// MUSCLE SmartCard Development // Authors: Tommaso Cucinotta <cucinotta@sssup.it> // David Corcoran <corcoran@linuxnet.com> // Ludovic Rousseau <ludovic.rousseau@free.fr> // Jamie Nicolson <nicolson@netscape.com> // Package: CardEdgeApplet // Description: CardEdge implementation with JavaCard // Protocol Authors: Tommaso Cucinotta <cucinotta@sssup.it> // David Corcoran <corcoran@linuxnet.com> // Modified: // Eirik Herskedal <ehersked@cs.purdue.edu> // // BEGIN LICENSE BLOCK // Copyright (c) 1999-2002 David Corcoran <corcoran@linuxnet.com> // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // 1. Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // 3. The name of the author may not be used to endorse or promote products // derived from this software without specific prior written permission. // // Changes to this license can be made only by the copyright author with // explicit written consent. // // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. // IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT // NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Alternatively, the contents of this file may be used under the terms of // the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which // case the provisions of the LGPL are applicable instead of those above. If // you wish to allow use of your version of this file only under the terms // of the LGPL, and not to allow others to use your version of this file // under the terms of the BSD license, indicate your decision by deleting // the provisions above and replace them with the notice and other // provisions required by the LGPL. If you do not delete the provisions // above, a recipient may use your version of this file under the terms of // either the BSD license or the LGPL. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // END LICENSE_BLOCK package com.redhat.ckey.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> * * <p>The Memory Manager allocates or frees memory chunks in the * preallocated byte array on demand.</p> * * <p>No defragmentation is done, actually.</p> * * <p>Consecutive freed memory chunks are recompacted.</p> * * <p>Every allocation takes 2 more bytes to store the allocated block * size, just before the allocated offset.</p> * * <p>A free memory block starts with a node (NODE_SIZE bytes):</p> * * <pre> * short size; * short next; * </pre> * * @author Tommaso Cucinotta * @author David Corcoran * @author Ludovic Rousseau * @version 0.9.9 */ public class MemoryManager { /** * Special offset value used as invalid offset */ public static final short NULL_OFFSET = -1; private static final byte NODE_SIZE = 4; private byte ptr[]; private short free_head; /** * Constructor for the MemoryManager class * * @param mem_size Size of the memory are to be allocated */ public MemoryManager(short mem_size) { ptr = null; free_head = NULL_OFFSET; Init(mem_size); } private void Init(short mem_size) { if(ptr != null) { return; } else { ptr = new byte[mem_size]; Util.setShort(ptr, (short)0, mem_size); Util.setShort(ptr, (short)2, (short)NULL_OFFSET); free_head = 0; return; } } /** * Allocate memory * * 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 #alloc(short) * @see #freemem() */ public short alloc(short size) { short offset = free_head; short prev = NULL_OFFSET; size += 2; if(size < NODE_SIZE) size = NODE_SIZE; short next_offset; for(; offset != NULL_OFFSET; offset = next_offset) { short free_size = Util.getShort(ptr, offset); next_offset = Util.getShort(ptr, (short)(offset + 2)); if(free_size >= size) { short remain = (short)(free_size - size); if(remain >= NODE_SIZE) { Util.setShort(ptr, offset, remain); } else { size = free_size; remain = 0; if(prev == NULL_OFFSET) free_head = next_offset; else Util.setShort(ptr, (short)(prev + 2), next_offset); } Util.setShort(ptr, (short)(offset + remain), size); return (short)(offset + remain + 2); } prev = offset; } return NULL_OFFSET; } /** * 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.</p> * * @param offset The offset at which the memory block starts; it was * returned from a previous call to {@link #alloc(short)} * * @see #alloc(short) * @see #freemem() */ public void free(short offset) { offset -= 2; short size = Util.getShort(ptr, offset); short prev = NULL_OFFSET; short base = free_head; boolean found = false; short node_next = 0; for(; base != NULL_OFFSET; base = node_next) { node_next = Util.getShort(ptr, (short)(base + 2)); if(offset < base) { found = true; break; } prev = base; } if(found && (short)(offset + size) == base) { size += Util.getShort(ptr, base); 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; } if(prev != NULL_OFFSET) { short prev_size = Util.getShort(ptr, prev); if((short)(prev + prev_size) == offset) { Util.setShort(ptr, prev, (short)(prev_size + size)); } else { Util.setShort(ptr, (short)(offset + 2), base); Util.setShort(ptr, (short)(prev + 2), offset); } } else { Util.setShort(ptr, (short)(offset + 2), base); free_head = offset; } } /** * Get available free memory * * @return The total amount of available free memory, equal to the * sum of all free fragments' sizes. * * @see #free(short) * @see #alloc(short) */ public short freemem() { short offset = free_head; short total = 0; for(; offset != NULL_OFFSET; offset = Util.getShort(ptr, (short)(offset + 2))) total = (short)((total + Util.getShort(ptr, offset)) - 2); return total; } /** * 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); } /** * Retrieve the Java byte array containing all the memory contents. * * <p>To optimize, we don't use external buffers, but we directly * copy from the memory array.</p> * * <p><b>Use this function only if really required.</b></p> * * @return The Java byte array containing all memory contents */ public byte[] getBuffer() { return ptr; } /** * 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]; } /** * 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)]; } /** * 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); } /** * 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; for(short base = free_head; base != NULL_OFFSET; base = Util.getShort(ptr, (short)(base + 2))) { short size = Util.getShort(ptr, base); if(size > max_size) max_size = size; } return (short)(max_size - 2); } /** * 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); } /** * 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)); } /** * Resize (only clamping is supported) a previously allocated memory * chunk * * @param offset Memory offset as returned by alloc() * @param new_size ew size of the memory block * @return True if it was possible to realloc(), False otherwise * * @see #alloc(short) * @see #free(short) * @see #freemem() */ public boolean realloc(short offset, short new_size) { short actual_size = Util.getShort(ptr, (short)(offset - 2)); new_size += 2; if(new_size < 3 || (short)(actual_size - new_size) < NODE_SIZE) { return false; } else { Util.setShort(ptr, (short)(offset - 2), new_size); Util.setShort(ptr, (short)((offset + new_size) - 2), (short)(actual_size - new_size)); free((short)(offset + new_size)); return true; } } /** * 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; } /** * 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; } /** * 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); } /** * 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); } /** * Set a short value into memory */ public void setShort(short base, short offset, short b) { Util.setShort(ptr, (short)(base + offset), b); } }