Skip to content

HeapPageXL

Description

Per-XL-region descriptor. One per allocation >2 KiB. XL allocations are page-aligned standalone mmaps with no bitmap and no sub-slotting; the descriptor’s presence in xl_in_use[] IS the in-use bit, presence in xl_freed[] IS the retained bit. Both arrays are grown by doubling; live / retained lookup is a linear scan over the relevant array.

Fields

Name Description
page Base of the user mmap (4 KiB-aligned).
os_pages Mmap size in OS-page units (each unit is HEAP_PAGE_SIZE * HEAP_PAGES_PER_OS_PAGE bytes). Equal to the value passed to the kernel; free unmaps exactly that many bytes.

Usage example (Cross-references)

Usage examples (Cross-references)
    // =============================================================================
    
    static bool heap_xl_array_reserve(HeapAllocator *heap, HeapPageXL **arr, u32 *cap_inout, u32 len) {
        if (len < *cap_inout)
            return true;
            return true;
        u32         want_cap   = *cap_inout ? *cap_inout * 2u : 0u;
        size        want_bytes = (size)want_cap * sizeof(HeapPageXL);
        size        new_bytes  = os_page_round_up(want_bytes ? want_bytes : sizeof(HeapPageXL));
        u32         new_cap    = (u32)(new_bytes / sizeof(HeapPageXL));
        u32         want_cap   = *cap_inout ? *cap_inout * 2u : 0u;
        size        want_bytes = (size)want_cap * sizeof(HeapPageXL);
        size        new_bytes  = os_page_round_up(want_bytes ? want_bytes : sizeof(HeapPageXL));
        u32         new_cap    = (u32)(new_bytes / sizeof(HeapPageXL));
        HeapPageXL *fresh      = (HeapPageXL *)os_page_map(&heap->base, new_bytes);
        size        want_bytes = (size)want_cap * sizeof(HeapPageXL);
        size        new_bytes  = os_page_round_up(want_bytes ? want_bytes : sizeof(HeapPageXL));
        u32         new_cap    = (u32)(new_bytes / sizeof(HeapPageXL));
        HeapPageXL *fresh      = (HeapPageXL *)os_page_map(&heap->base, new_bytes);
        if (!fresh)
        size        new_bytes  = os_page_round_up(want_bytes ? want_bytes : sizeof(HeapPageXL));
        u32         new_cap    = (u32)(new_bytes / sizeof(HeapPageXL));
        HeapPageXL *fresh      = (HeapPageXL *)os_page_map(&heap->base, new_bytes);
        if (!fresh)
            return false;
            return false;
        if (*arr && len) {
            MemCopy(fresh, *arr, (size)len * sizeof(HeapPageXL));
        }
        if (*arr) {
        }
        if (*arr) {
            os_page_unmap(&heap->base, *arr, os_page_round_up((size)(*cap_inout) * sizeof(HeapPageXL)));
        }
        *arr       = fresh;
    // already the last) and shrink the length. Order is not preserved --
    // fine because both arrays are pure sets of descriptors.
    static FORCE_INLINE HeapPageXL heap_xl_in_use_swap_remove(HeapAllocator *heap, u32 idx) {
        HeapPageXL out       = heap->xl_in_use[idx];
        heap->xl_in_use_len -= 1u;
    // fine because both arrays are pure sets of descriptors.
    static FORCE_INLINE HeapPageXL heap_xl_in_use_swap_remove(HeapAllocator *heap, u32 idx) {
        HeapPageXL out       = heap->xl_in_use[idx];
        heap->xl_in_use_len -= 1u;
        if (idx != heap->xl_in_use_len) {
    }
    
    static FORCE_INLINE HeapPageXL heap_xl_freed_swap_remove(HeapAllocator *heap, u32 idx) {
        HeapPageXL out      = heap->xl_freed[idx];
        heap->xl_freed_len -= 1u;
    
    static FORCE_INLINE HeapPageXL heap_xl_freed_swap_remove(HeapAllocator *heap, u32 idx) {
        HeapPageXL out      = heap->xl_freed[idx];
        heap->xl_freed_len -= 1u;
        if (idx != heap->xl_freed_len) {
        u64 target    = footprint - footprint / 4u; // 75% of current
        while (heap->base.footprint_bytes > target && heap->xl_freed_len > 0u) {
            HeapPageXL ent         = heap_xl_freed_swap_remove(heap, heap->xl_freed_len - 1u);
            size       bytes       = (size)ent.os_pages * HEAP_OS_PAGE_SIZE;
            heap->retention_bytes -= (u64)bytes;
        u32 fr_idx = heap_xl_freed_find_match(heap, (u32)os_pages);
        if (fr_idx != HEAP_BUCKET_NONE) {
            HeapPageXL ent         = heap_xl_freed_swap_remove(heap, fr_idx);
            ptr                    = ent.page;
            heap->retention_bytes -= (u64)total;
        }
    
        HeapPageXL desc                        = {.page = ptr, .os_pages = (u32)os_pages};
        heap->xl_in_use[heap->xl_in_use_len++] = desc;
        HEAP_MARK_DIRTY(heap);
    
    static void heap_free_xl(HeapAllocator *heap, u32 idx) {
        HeapPageXL ent   = heap_xl_in_use_swap_remove(heap, idx);
        size       total = (size)ent.os_pages * HEAP_OS_PAGE_SIZE;
        HEAP_MARK_DIRTY(heap);
            os_page_unmap(&self->base, self->pages, os_page_round_up((size)self->pages_cap * sizeof(HeapPage)));
        if (self->xl_in_use)
            os_page_unmap(&self->base, self->xl_in_use, os_page_round_up((size)self->xl_in_use_cap * sizeof(HeapPageXL)));
        if (self->xl_freed)
            os_page_unmap(&self->base, self->xl_freed, os_page_round_up((size)self->xl_freed_cap * sizeof(HeapPageXL)));
            os_page_unmap(&self->base, self->xl_in_use, os_page_round_up((size)self->xl_in_use_cap * sizeof(HeapPageXL)));
        if (self->xl_freed)
            os_page_unmap(&self->base, self->xl_freed, os_page_round_up((size)self->xl_freed_cap * sizeof(HeapPageXL)));
        if (self->recycle)
            os_page_unmap(&self->base, self->recycle, os_page_round_up((size)self->recycle_cap * sizeof(void *)));
            void *page;
            u32   os_pages;
        } HeapPageXL;
    
        /// HeapAllocator owns:
            /// Storage for both arrays is itself page-mapped and grown by
            /// doubling.
            HeapPageXL *xl_in_use;
            u32         xl_in_use_len;
            u32         xl_in_use_cap;
            u32         xl_in_use_len;
            u32         xl_in_use_cap;
            HeapPageXL *xl_freed;
            u32         xl_freed_len;
            u32         xl_freed_cap;
Last updated on