package org.satochip.applet; import javacard.framework.Util; /** * Memory Manager class. * <p> * * An instance of this class is capable of handling allocation and deallocation * of chunks in a large Java byte array that is allocated once during the object * instantiation. * <p> * The Memory Manager allocates or frees memory chunks in the preallocated byte * array on demand. * <p> * * No defragmentation is done, actually. * <p> * Consecutive freed memory chunks are recompacted. * <p> * * Every allocation takes 2 more bytes to store the allocated block size, just * before the allocated offset. * <p> * * A free memory block starts with a node (NODE_SIZE bytes): * * <pre> * short size; * short next; * </pre> */ public class MemoryManager { /** Special offset value used as invalid offset */ public final static short NULL_OFFSET = (short) 0xFFFF; // Also used as End // Of List private final static byte NODE_SIZE = (byte) 4; // All the available memory as a byte array private byte ptr[] = null; // Free memory list private short free_head = NULL_OFFSET; /** * Constructor for the MemoryManager class * * @param mem_size * Size of the memory are to be allocated */ public MemoryManager(short mem_size) { Init(mem_size); } private void Init(short mem_size) { if (ptr != null) return; // Allocate the memory ptr = new byte[mem_size]; // Setup the free memory list // set the size Util.setShort(ptr, (short) 0, (short) mem_size); // set the pointer to EndOfList Util.setShort(ptr, (short) 2, (short) NULL_OFFSET); // set the pointer to the head node free_head = (short) 0; } /** * Allocate memory * <p> * Each allocation takes actually a 2 bytes overhead. * * @param size * Size of the memory block * @return The offset at which allocated memory starts or NULL_OFFSET if an * error occurred. * @see #free * @see #freemem */ public short alloc(short size) { short offset = free_head; short prev = NULL_OFFSET; size = (short) (size + 2); // We need a 2 bytes more for block size // Forbid allocation of single bytes: when freeing, // they could remain isolated and a free node would not fit ! if (size < NODE_SIZE) size = NODE_SIZE; // Search the free mem list for a suitable location // (no special memory management policies, at the moment) while (offset != NULL_OFFSET) { // System.out.println(offset); short free_size = Util.getShort(ptr, offset); short next_offset = Util.getShort(ptr, (short) (offset + 2)); // System.out.println(free_size); // System.out.println(next_offset); if (free_size >= size) { // We've got it short remain = (short) (free_size - size); if (remain >= NODE_SIZE) { /* * There's enough space for a new free mem node; * - just * clamp this node (it won't move) * - previous node doesn't * change at all */ Util.setShort(ptr, offset, remain); } else { /* * Not enough space for a new free mem node; * - just * allocate all the node's space * - previous node must skip * to the next one */ size = free_size; remain = (short) 0; if (prev == NULL_OFFSET) { // No previous: it was the 1st free_head = next_offset; } else { // Previous: set it's next offset field Util.setShort(ptr, (short) (prev + 2), next_offset); } } /* * Write the memory block size and skip it * while returning * allocated offset (from * the tail of the free space) */ Util.setShort(ptr, (short) (offset + remain), size); return (short) (offset + remain + 2); } else { // Go to next list node prev = offset; offset = next_offset; } } /* No memory found ! */ return NULL_OFFSET; } /** * Gets the size of the greatest chunk of available memory * * @return The size of the greatest free memory chunk, or zero if there is * no free mem left */ public short getMaxSize() { short max_size = 2; short base = free_head; while (base != NULL_OFFSET) { short size = Util.getShort(ptr, base); if (size > max_size) max_size = size; base = Util.getShort(ptr, (short) (base + 2)); } return (short) (max_size - 2); } /** * Free a memory block * <p> * Consecutive free blocks are recompacted. Recompaction happens on free(). * 4 cases are considered: don't recompact, recompact with next only, with * previous only and with both of them. * * @param offset * The offset at which the memory block starts; it was returned * from a previous call to {@link #alloc} * @see #alloc * @see #freemem */ public void free(short offset) { offset -= 2; short size = Util.getShort(ptr, offset); /* Search for the right insertion point */ short prev = NULL_OFFSET; short base = free_head; boolean found = false; short node_next = (short) 0; // Compiler warning... while (base != NULL_OFFSET) { node_next = Util.getShort(ptr, (short) (base + 2)); if (offset < base) { found = true; break; } prev = base; base = node_next; } /* Check if can recompact with next */ if (found && ((short) (offset + size) == base)) { /* * Recompact with next: extract next from list * so we handle a * single case, after compacting * next with new node to be inserted */ size += Util.getShort(ptr, base); /* * We have to rewrite down the right size, in case it becomes a new * node */ Util.setShort(ptr, offset, size); if (prev != NULL_OFFSET) Util.setShort(ptr, (short) (prev + 2), node_next); else free_head = node_next; base = node_next; } /* Check if can recompact with previous */ if (prev != NULL_OFFSET) { short prev_size = Util.getShort(ptr, prev); if ((short) (prev + prev_size) == offset) { /* Recompact with previous and don't insert a new node */ Util.setShort(ptr, prev, (short) (prev_size + size)); } else { /* Couldn't recompact: insert node after previous */ // Write node next pointer only (size is already in place) Util.setShort(ptr, (short) (offset + 2), base); Util.setShort(ptr, (short) (prev + 2), offset); } } else { /* Couldn't recompact with prev; head-insert new node */ // Write node next pointer only (size is already in place) Util.setShort(ptr, (short) (offset + 2), base); free_head = offset; } } /** * Get the size of a memory block * * @param offset * The offset at which the memory block starts */ public short getBlockSize(short offset) { return (short) (Util.getShort(ptr, (short) (offset - 2)) - 2); } /** * Get available free memory * * @return The total amount of available free memory, equal to the sum of * all free fragments' sizes. * @see free * @see alloc */ public short freemem() { short offset = free_head; short total = (short) 0; // Scan free mem list while (offset != NULL_OFFSET) { // Return free memory in case that every single free block // is entirely allocated at once (best case) // (every allocation keeps 2 bytes for block size) total = (short) (total + Util.getShort(ptr, offset) - 2); offset = Util.getShort(ptr, (short) (offset + 2)); } return total; } /** * Resize (only clamping is supported) a previously allocated memory chunk * <p> * * @param offset * Memory offset as returned by alloc() * @param size * New size of the memory block * @return True if it was possible to realloc(), False otherwise * @see #alloc * @see #free * @see #freemem */ public boolean realloc(short offset, short new_size) { short actual_size = Util.getShort(ptr, (short) (offset - 2)); new_size += (short) 2; if ((new_size < (short) (1 + 2)) || ((short) (actual_size - new_size) < NODE_SIZE)) // Cannot free any memory (really here there are issues...) return false; // Clamp this node Util.setShort(ptr, (short) (offset - 2), new_size); // Create a fake allocated node Util.setShort(ptr, (short) (offset + new_size - 2), (short) (actual_size - new_size)); // Deallocate the freed memory free((short) (offset + new_size)); return true; } /** * Set a byte value into memory * * @param base * The base memory location (offset) of the byte to set * @param offset * The offset of the byte (is added to the base parameter) * @param b * The new byte value */ public void setByte(short base, short offset, byte b) { ptr[(short) (base + offset)] = b; } /** * Set a byte value into memory * * @param base * The complete memory location (offset) of the byte to set * @param b * The new byte value */ public void setByte(short base, byte b) { ptr[base] = b; } /** * Read a byte value from memory * * @param base * The base memory location (offset) of the byte to read * @param offset * The offset of the byte (is added to the base parameter) * @return The byte value */ public byte getByte(short base, short offset) { return ptr[(short) (base + offset)]; } /** * Read a byte value from memory * * @param base * The complete memory location (offset) of the byte to read * @return The byte value */ public byte getByte(short base) { return ptr[base]; } /** * Set a short value into memory * * @param base * The base memory location (offset) of the short to set * @param offset * The offset of the short (is added to the base parameter) * @param b * The short value */ public void setShort(short base, short offset, short b) { Util.setShort(ptr, (short) (base + offset), b); } /** * Set a short value into memory * * @param base * The complete memory location (offset) of the short to set * @param b * The short value */ public void setShort(short base, short b) { Util.setShort(ptr, base, b); } /** * Read a short value from memory * * @param base * The base memory location (offset) of the short to read * @param offset * The offset of the short (is added to the base parameter) * @return The short value */ public short getShort(short base, short offset) { return Util.getShort(ptr, (short) (base + offset)); } /** * Read a short value from memory * * @param base * The base memory location (offset) of the short to read * @return The short value */ public short getShort(short base) { return Util.getShort(ptr, base); } /** * Copy a byte sequence into memory * * @param dst_base * The base memory location (offset) of the destination byte * sequence * @param dst_offset * The offset of the destination byte sequence (is added to the * dst_base parameter) * @param src_bytes * The source byte array * @param src_offset * The offset at which the source sequence starts in src_bytes[] * @param size * The number of bytes to be copied */ public void setBytes(short dst_base, short dst_offset, byte[] src_bytes, short src_offset, short size) { Util.arrayCopy(src_bytes, src_offset, ptr, (short) (dst_base + dst_offset), size); } /** * Copy a byte sequence from memory * * @param dst_bytes * The destination byte array * @param dst_offset * The offset at which the sequence will be copied in dst_bytes[] * @param src_base * The base memory location (offset) of the source byte sequence * @param src_offset * The offset of the source byte sequence (is added to the * src_base parameter) * @param size * The number of bytes to be copied */ public void getBytes(byte[] dst_bytes, short dst_offset, short src_base, short src_offset, short size) { Util.arrayCopy(ptr, (short) (src_base + src_offset), dst_bytes, dst_offset, size); } /** * Retrieve the Java byte array containing all the memory contents. To * optimize, we don't use external buffers, * but we directly copy from the * memory array * Use this function only if really required. * * * @return The Java byte array containing all memory contents */ public byte[] getBuffer() { return ptr; } } // class MemoryManager