Heap Allocator

C0 Heap Allocator

Implemented in ./utility/memory.ts

interface C0HeapAllocator {
    // Basic Operations
    malloc(size: number): C0Pointer
    free(ptr: C0Pointer): void
    // Clear the whole heap memory, called when C0VM restarts
    clear(): void

    // Return the memory pool as ArrayBuffer - used for debug only
    // Should not change the memory by altering the ArrayBuffer here
    debug_getMemPool(): ArrayBuffer | undefined
    // Operations on Heap Memory
    cmstore(ptr: C0Pointer, value: DataView): void
    cmload(ptr: C0Pointer): DataView

    imstore(ptr: C0Pointer, value: DataView): void
    imload(ptr: C0Pointer): DataView

    amstore(ptr: C0Pointer, stored_ptr: C0Pointer): void
    amload(ptr: C0Pointer): DataView

    deref(ptr: C0Pointer, block_size: number): DataView
}

// Factory Function for HeapAllocator
export function createHeap(
    allocator: C0HeapAllocatorConstructor,
    size ?: number
): C0HeapAllocator

In C0VM.ts, we use a ArrayBuffer to simulate the heap memory of a C program.

Since we are following the C's convention (0x0000000000000000 is a NULLpointer), the memory address at 0x00 is reserved and never touched.

malloc & free Functions

A C0HeapAllocator is able to manage its own memory pool and encapsulate the details with malloc and free interface.

malloc will allocate a block of memory and return a C0Pointer that referes to the starting position of the allocated memory segment.

free will free/release a block of memory. When the pointer given to free does not points to the start of memory segment, c0_memory_error should be throw to caller. (since free a memory with result of pointer arithmetic will lead to undefined behavior in C)

deref Function

The deref function is a simulation of the * operator in C.

Given a C0Pointer, the deref function will return a DataView that is an alias of the ArrayBuffer of the memory block that pointer is pointing to.

Example

For some pointer 0x0000_0000_0070_00FF, passing it to deref will return you a DataView of the segment [0x0000_0070, 0x0000_00FF).

0x0000_0000                            0x0000_00FF
|=====================^================|
^- addr = 0x0000_0000 ^- offset = 0x0070

It is always recommended to use this function to access heap memory.

Naïve Implementation of C0HeapAllocator

A naïve implementation of C0HeapAllocator is the VM_Memory class. Defined as follow:

export class VM_Memory implements C0HeapAllocator {
    private memory_pool: ArrayBuffer;
    private heap_top_address: number;
    private memory_size: number;

    constructor(size?: number);

    malloc(size: number): C0Pointer;
    free(ptr: C0Pointer): void;
    clear(): void;

    debug_getMemPool(): ArrayBuffer;

    cmstore(ptr: C0Pointer, value: DataView): void;
    cmload(ptr: C0Pointer): DataView;
    imstore(ptr: C0Pointer, value: DataView): void;
    imload(ptr: C0Pointer): DataView;
    amstore(ptr: C0Pointer, stored_ptr: DataView): void;
    amload(ptr: C0Pointer): DataView;

    deref(ptr: DataView, block_size: number): DataView
}

Written in ./utility/memory.ts, the VM_Memory class is a naïve implementation to the C0HeapAllocator interface.

The private property heap_top_address will keep track of the boundary between allocated and unallocated parts of the memory pool. Whenever a malloc is called to allocate n bytes of heap memory, the allocator will give the memory segment [heap_top, heap_top + n) to the caller and make heap_top += n.

Since 0x00 is reserved for NULL, allocator will start to allocate memory from 0x01.

Last updated