Untitled

Anonymous
plain_text
02/08/2026 10:15 PM
11.6 KB
14
Indexable
/**
 * Memory manager class that stores records in a byte array.
 * This version uses a buddy-system allocator on top of an array.
 *
 * @author Leguejou Awunganyi
 * @author Ishita Punna
 * @version 2026-02-08
 */
public class MemManager {

    /**
     * Raw byte array used as the memory pool.
     */
    private byte[] pool;

    /**
     * Array of free lists, one per block size level.
     */
    private FreeList[] freeLists;

    /**
     * Last expansion message, cleared after it is read.
     */
    private String lastExpandMessage = "";

    /**
     * Node in a singly linked free list.
     */
    private static class FreeNode {

        /**
         * Starting address of this free block.
         */
        private int addr;

        /**
         * Next node in the free list.
         */
        private FreeNode next;

        /**
         * Create a new free-node with the given address.
         *
         * @param a starting address for this block
         */
        FreeNode(int a) {
            addr = a;
        }
    }

    /**
     * Simple linked list of free blocks for a single block size.
     */
    private static class FreeList {

        /**
         * Head of the free list.
         */
        private FreeNode head;

        /**
         * Add a free block at the front of the list.
         *
         * @param a starting address of the free block
         */
        void add(int a) {
            FreeNode n = new FreeNode(a);
            n.next = head;
            head = n;
        }

        /**
         * Remove and return the first free block.
         *
         * @return starting address of the removed block
         */
        int removeFirst() {
            int a = head.addr;
            head = head.next;
            return a;
        }

        /**
         * Remove the first node whose address matches a.
         *
         * @param a address to remove
         * @return true if a block was removed, false otherwise
         */
        boolean remove(int a) {
            FreeNode prev = null;
            FreeNode cur = head;
            while (cur != null) {
                if (cur.addr == a) {
                    if (prev == null) {
                        head = cur.next;
                    }
                    else {
                        prev.next = cur.next;
                    }
                    return true;
                }
                prev = cur;
                cur = cur.next;
            }
            return false;
        }

        /**
         * Test whether this free list is empty.
         *
         * @return true if the list has no nodes
         */
        boolean isEmpty() {
            return head == null;
        }

        /**
         * Build a string of all block addresses in this list.
         *
         * @return addresses separated by a leading space
         */
        String toStringList() {
            String s = "";
            FreeNode c = head;
            while (c != null) {
                s += " " + c.addr;
                c = c.next;
            }
            return s;
        }
    }

    /**
     * Create a new MemManager object.
     *
     * @param startSize initial requested size of the memory pool
     */
    public MemManager(int startSize) {
        int actualSize = nextPowerOfTwo(startSize);
        pool = new byte[actualSize];

        int levels = log2(actualSize) + 1;

        freeLists = new FreeList[levels];
        for (int i = 0; i < levels; i++) {
            freeLists[i] = new FreeList();
        }
        // Start with one free block at address 0 of full size.
        freeLists[log2(actualSize)].add(0);
    }

    /**
     * Insert a record into the pool and return a handle.
     * Uses the buddy system to allocate a block whose size
     * is the next power of two at least as large as data.length.
     *
     * @param data byte array containing the record
     * @return handle representing the stored record
     */
    public Handle insert(byte[] data) {
        int needed = nextPowerOfTwo(data.length);
        int level = log2(needed);

        // Find the first non-empty free list at or above this level.
        while (level < freeLists.length && freeLists[level].isEmpty()) {
            level++;
        }

        if (level == freeLists.length) {
            // No blocks big enough; expand and try again.
            expand();
            return insert(data);
        }

        // Remove a free block from this level.
        int addr = freeLists[level].removeFirst();

        // Split blocks until the block size matches the needed size.
        while ((1 << level) > needed) {
            level--;
            int buddy = addr + (1 << level);
            freeLists[level].add(buddy);
        }

        // Copy the record into the pool.
        System.arraycopy(data, 0, pool, addr, data.length);

        // Handle carries block size and logical data length.
        return new Handle(addr, needed, data.length);
    }

    /**
     * Release a block given its handle back to the free lists.
     * Merges with its buddy whenever possible.
     *
     * @param h handle to the memory being freed
     */
    public void release(Handle h) {
        int addr = h.getStart();
        int size = h.getSize();
        int level = log2(size);

        while (true) {
            // Buddy is the block whose address differs in the bit for "size".
            int buddy = addr ^ size;

            if (!freeLists[level].remove(buddy)) {
                // Buddy not free; stop merging.
                break;
            }

            // Merge with buddy into a larger block.
            addr = Math.min(addr, buddy);
            size <<= 1;
            level++;
        }

        freeLists[level].add(addr);
    }

    /**
     * Retrieve the record stored at a given handle.
     *
     * @param h handle to the data
     * @return copy of the stored bytes
     */
    public byte[] getRecord(Handle h) {
        byte[] out = new byte[h.getDataLength()];
        System.arraycopy(pool, h.getStart(), out, 0, h.getDataLength());
        return out;
    }

    /**
     * Expand the memory pool by doubling its size.
     * Copies existing contents and adds the new half as a free block.
     */
    private void expand() {
        int oldSize = pool.length;
        int newSize = oldSize * 2;

        byte[] newPool = new byte[newSize];
        System.arraycopy(pool, 0, newPool, 0, oldSize);
        pool = newPool;

        FreeList[] newLists = new FreeList[freeLists.length + 1];
        for (int i = 0; i < freeLists.length; i++) {
            newLists[i] = freeLists[i];
        }
        newLists[freeLists.length] = new FreeList();
        freeLists = newLists;

        // Add new free block for the second half of the pool.
        freeLists[log2(oldSize)].add(oldSize);

        lastExpandMessage =
            "Memory pool expanded to be " + newSize + " bytes\r\n";
    }

    /**
     * Compute floor(log2(n)) using bit operations.
     *
     * @param n positive integer
     * @return exponent e such that 2^e is the highest power of two ≤ n
     */
    private int log2(int n) {
        return 31 - Integer.numberOfLeadingZeros(n);
    }

    /**
     * Find the smallest power of two that is at least n.
     *
     * @param n requested size
     * @return power-of-two size ≥ n
     */
    private int nextPowerOfTwo(int n) {
        int p = 1;
        while (p < n) {
            p <<= 1;
        }
        return p;
    }

    /**
     * Print all free blocks grouped by size.
     *
     * @return formatted string of free blocks, or a message if none are free
     */
    public String printBlocks() {
        StringBuilder sb = new StringBuilder();
        boolean any = false;

        for (int level = 0; level < freeLists.length; level++) {
            if (!freeLists[level].isEmpty()) {
                any = true;
                int size = 1 << level;
                sb.append(size)
                    .append(":");
                sb.append(freeLists[level].toStringList());
                sb.append("\r\n");
            }
        }

        if (!any) {
            return "No free blocks are available.";
        }

        // Remove final "\r\n".
        sb.setLength(sb.length() - 2);
        return sb.toString();
    }

    /**
     * Get the last expansion message and clear it.
     *
     * @return last expansion message, or empty string if none
     */
    public String getExpandMessage() {
        String m = lastExpandMessage;
        lastExpandMessage = "";
        return m;
    }
}
Editor is loading...
Leave a Comment