Untitled

Anonymous
plain_text
02/13/2026 2:52 AM
18.0 KB
12
Indexable
/**
 * Memory Manager class that manages a byte array pool using
 * the buddy allocation method. Blocks are allocated in powers
 * of two and merged with their buddies when released.
 *
 * @author Leguejou Awunganyi
 * @author Ishita Punna
 * @version 2026-02-12
 */
public class MemManager {

    /** The byte array representing the memory pool. */
    private byte[] memoryPool;

    /** Array of free lists indexed by block size level. */
    private FreeList[] freeBlockLists;

    /** Stores the most recent memory expansion message. */
    private String lastExpansionMessage = "";

    /**
     * Node representing a free block address in a free list.
     */
    private static class FreeNode {

        /** Address of the free block. */
        int address;

        /** Reference to next node. */
        FreeNode next;

        /**
         * Creates a new free node.
         *
         * @param blockAddress starting address of block
         */
        FreeNode(int blockAddress) {
            // Store the starting address for this free block node
            address = blockAddress;
        }
    }

    /**
     * Linked list of free block addresses for a given block size.
     */
    private static class FreeList {

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

        /**
         * Adds a block address to the free list in sorted order.
         *
         * @param blockAddress address to insert
         */
        void add(int blockAddress) {

            // Create a new node for the block being inserted
            FreeNode newNode = new FreeNode(blockAddress);

            // If list is empty or block belongs at the front, insert at head
            if (head == null || head.address > blockAddress) {
                newNode.next = head;
                head = newNode;
                return;
            }

            // Traverse list until correct insertion point is found
            FreeNode currentNode = head;
            while (currentNode.next != null
                && currentNode.next.address < blockAddress) {
                currentNode = currentNode.next;
            }

            // Insert new node into the sorted list
            newNode.next = currentNode.next;
            currentNode.next = newNode;
        }

        /**
         * Removes and returns the first block address.
         *
         * @return removed block address
         */
        int removeFirst() {

            // Save the address at the head before removing it
            int blockAddress = head.address;

            // Advance head pointer to the next free node
            head = head.next;

            return blockAddress;
        }

        /**
         * Removes a specific block address if present.
         *
         * @param blockAddress address to remove
         * @return true if removed, false otherwise
         */
        boolean remove(int blockAddress) {

            // Track current node and previous node for removal
            FreeNode previousNode = null;
            FreeNode currentNode = head;

            // Walk through list searching for matching address
            while (currentNode != null) {
                if (currentNode.address == blockAddress) {

                    // If removing head, update head pointer
                    if (previousNode == null) {
                        head = currentNode.next;
                    }
                    else {
                        // Otherwise bypass the removed node
                        previousNode.next = currentNode.next;
                    }
                    return true;
                }

                // Move forward in the linked list
                previousNode = currentNode;
                currentNode = currentNode.next;
            }

            // Block was not found in this free list
            return false;
        }

        /**
         * Checks if free list is empty.
         *
         * @return true if empty
         */
        boolean isEmpty() {

            // List is empty when head is null
            return head == null;
        }

        /**
         * Returns formatted string of addresses.
         *
         * @return space-separated address list
         */
        String toStringList() {

            // Build a string containing all addresses in this free list
            StringBuilder builder = new StringBuilder();
            FreeNode currentNode = head;

            // Append each address until end of list is reached
            while (currentNode != null) {
                builder.append(" ").append(currentNode.address);
                currentNode = currentNode.next;
            }

            return builder.toString();
        }
    }

    /**
     * Constructs a new memory manager with given initial size.
     *
     * @param initialSize initial pool size (must be power of 2)
     */
    public MemManager(int initialSize) {

        // Allocate the initial byte array pool
        memoryPool = new byte[initialSize];

        // Determine number of free list levels based on pool size
        int levelCount = logBaseTwo(initialSize) + 1;
        freeBlockLists = new FreeList[levelCount];

        // Initialize each free list structure
        for (int i = 0; i < levelCount; i++) {
            freeBlockLists[i] = new FreeList();
        }

        // Entire pool begins as one free block at the largest level
        freeBlockLists[logBaseTwo(initialSize)].add(0);
    }

    /**
     * Inserts record data into memory pool.
     *
     * @param recordData byte array to insert
     * @return handle referencing stored data
     */
    public Handle insert(byte[] recordData) {

        // Compute required block size as next power of two
        int requiredSize = nextPowerOfTwo(recordData.length);
        int levelIndex = logBaseTwo(requiredSize);

        // Find the first available free block at or above needed size
        while (levelIndex < freeBlockLists.length
            && freeBlockLists[levelIndex].isEmpty()) {
            levelIndex++;
        }

        // If no block exists, expand memory pool and retry insertion
        if (levelIndex == freeBlockLists.length) {
            expandMemory();
            return insert(recordData);
        }

        // Remove a free block from the chosen free list level
        int blockAddress =
            freeBlockLists[levelIndex].removeFirst();

        // Split blocks until we reach the smallest sufficient size
        while ((1 << levelIndex) > requiredSize) {
            levelIndex--;
            int buddyAddress =
                blockAddress + (1 << levelIndex);

            // Add the unused buddy half back into the free list
            freeBlockLists[levelIndex].add(buddyAddress);
        }

        // Copy record bytes into allocated memory pool region
        System.arraycopy(
            recordData,
            0,
            memoryPool,
            blockAddress,
            recordData.length);

        // Return handle describing where record is stored
        return new Handle(
            blockAddress,
            requiredSize,
            recordData.length);
    }

    /**
     * Releases memory associated with a handle.
     *
     * @param handle handle to free
     */
    public void release(Handle handle) {

        // Get starting address and block size from handle
        int blockAddress = handle.getStart();
        int blockSize = handle.getSize();
        int levelIndex = logBaseTwo(blockSize);

        // Attempt to merge freed block with its buddy repeatedly
        while (true) {

            // Buddy address computed using XOR trick
            int buddyAddress =
                blockAddress ^ blockSize;

            // Stop merging if buddy is not currently free
            if (!freeBlockLists[levelIndex]
                .remove(buddyAddress)) {
                break;
            }

            // Update address to the smaller of the merged pair
            blockAddress =
                Math.min(blockAddress, buddyAddress);

            // Double block size and move up one level
            blockSize <<= 1;
            levelIndex++;
        }

        // Add the final merged block back into free list
        freeBlockLists[levelIndex].add(blockAddress);
    }

    /**
     * Retrieves stored record bytes.
     *
     * @param handle handle referencing record
     * @return byte array of record
     */
    public byte[] getRecord(Handle handle) {

        // Allocate an array large enough for the stored block
        byte[] recordBytes =
            new byte[handle.getSize()];

        // Copy only the real data length into the result array
        System.arraycopy(
            memoryPool,
            handle.getStart(),
            recordBytes,
            0,
            handle.getDataLength());

        return recordBytes;
    }

    /**
     * Expands memory pool to double its size.
     */
    private void expandMemory() {

        // Save current pool size before expansion
        int oldPoolSize = memoryPool.length;
        int newPoolSize = oldPoolSize * 2;

        // Create a new larger byte array pool
        byte[] expandedPool =
            new byte[newPoolSize];

        // Copy existing memory contents into expanded pool
        System.arraycopy(
            memoryPool,
            0,
            expandedPool,
            0,
            oldPoolSize);

        memoryPool = expandedPool;

        // Recompute free list structure for the larger pool
        int levelCount =
            logBaseTwo(newPoolSize) + 1;

        FreeList[] newFreeBlockLists =
            new FreeList[levelCount];

        // Initialize new free lists for expanded pool
        for (int i = 0; i < levelCount; i++) {
            newFreeBlockLists[i] = new FreeList();
        }

        // Copy existing free block addresses into new lists
        for (int i = 0; i < freeBlockLists.length; i++) {

            FreeNode currentNode =
                freeBlockLists[i].head;

            while (currentNode != null) {
                newFreeBlockLists[i]
                    .add(currentNode.address);
                currentNode = currentNode.next;
            }
        }

        freeBlockLists = newFreeBlockLists;

        // Create handle representing the new second half block
        Handle newHalfHandle =
            new Handle(oldPoolSize,
                oldPoolSize,
                oldPoolSize);

        // Release new half so it becomes available in free lists
        release(newHalfHandle);

        // Save message so DB can print expansion notice
        lastExpansionMessage =
            "Memory pool expanded to be "
                + newPoolSize
                + " bytes\r\n";
    }

    /**
     * Computes log base 2 of value.
     *
     * @param value input value
     * @return log2(value)
     */
    private int logBaseTwo(int value) {

        // Use bit operations to compute log base 2 efficiently
        return 31
            - Integer.numberOfLeadingZeros(value);
    }

    /**
     * Rounds value up to next power of two.
     *
     * @param value input size
     * @return next power of two
     */
    private int nextPowerOfTwo(int value) {

        // Start at smallest power of two and grow until big enough
        int power = 1;

        while (power < value) {
            power <<= 1;
        }

        return power;
    }

    /**
     * Prints all free blocks grouped by block size.
     *
     * @return formatted free block list
     */
    public String printBlocks() {

        // Build output showing all free blocks by size level
        StringBuilder outputBuilder =
            new StringBuilder();

        boolean hasFreeBlocks = false;

        // Loop through every level of free lists
        for (int levelIndex = 0;
            levelIndex < freeBlockLists.length;
            levelIndex++) {

            // Only print levels that contain at least one block
            if (!freeBlockLists[levelIndex]
                .isEmpty()) {

                hasFreeBlocks = true;

                int blockSize = 1 << levelIndex;

                // Append block size and its free addresses
                outputBuilder.append(blockSize)
                    .append(":")
                    .append(freeBlockLists[levelIndex]
                        .toStringList())
                    .append("\r\n");
            }
        }

        // If no blocks exist, return special message
        if (!hasFreeBlocks) {
            return "No free blocks are available.";
        }

        // Remove trailing newline characters from output
        while (outputBuilder.length() > 0
            && (outputBuilder.charAt(
                outputBuilder.length() - 1) == '\n'
            || outputBuilder.charAt(
                outputBuilder.length() - 1) == '\r')) {

            outputBuilder.setLength(
                outputBuilder.length() - 1);
        }

        return outputBuilder.toString();
    }

    /**
     * Returns last expansion message and clears it.
     *
     * @return expansion message
     */
    public String getExpandMessage() {

        // Save expansion message before resetting it
        String message = lastExpansionMessage;

        // Clear message so it only prints once
        lastExpansionMessage = "";

        return message;
    }
}
Editor is loading...
Leave a Comment