Skip to content
AllocatorAlloc

AllocatorAlloc

Description

Allocate memory through an allocator. The macro dispatches at compile time on the static type of self: typed allocator pointers (HeapAllocator *, …) route straight to the concrete *_allocator_allocate, skipping the Allocator * indirect call; Allocator * falls through to the dynamic wrapper for the type-erased path. Wrong types fail at the _Generic mismatch.

The typed direct path skips the dynamic wrapper’s outer ValidateAllocator and retry loop. Stats accounting happens inline inside each typed *_allocator_* body (and the typed body also self-validates via magic check etc.), so both the typed direct call and the dyn dispatch produce identical counter movement on the same workload.

Parameters

Name Direction Description
self in,out Typed allocator pointer or Allocator *.
bytes in Number of bytes to allocate.
zeroed in Whether the allocated region must be zero-initialized.

Success

Returns a writable pointer to allocated memory.

Failure

Returns NULL when allocation fails or self is invalid.

Usage example (Cross-references)

Usage examples (Cross-references)
            }
    
            u8 *buffer = (u8 *)AllocatorAlloc(StrAllocator(o), byte_len * sizeof(u8), true);
    
            if (!buffer) {
            len++;
    
        char *new_str = (char *)AllocatorAlloc(alloc, len + 1, false);
        if (!new_str) {
            // Allocator failures are not errno-bearing (allocators are
        // and is the standard fix.
        for (;;) {
            new_entries = AllocatorAlloc(map->allocator, new_capacity * entry_size, true);
            new_states  = AllocatorAlloc(map->allocator, new_capacity * sizeof(u8), true);
        for (;;) {
            new_entries = AllocatorAlloc(map->allocator, new_capacity * entry_size, true);
            new_states  = AllocatorAlloc(map->allocator, new_capacity * sizeof(u8), true);
    
            if (!new_entries || !new_states) {
    
        hash       = map_hash_key(map, key, key_size);
        temp_entry = AllocatorAlloc(map->allocator, entry_size, true);
        if (!temp_entry) {
            return false;
    
        if (map->value_copy_init) {
            temp_value = AllocatorAlloc(map->allocator, value_size, true);
            if (!temp_value) {
                return false;
    static void *graph_alloc_node_data(GenericGraph *graph, size item_size) {
        graph_validate_alignment(graph);
        return AllocatorAlloc(graph->allocator, item_size, true);
    }
        }
    
        new_node = AllocatorAlloc(list->allocator, sizeof(GenericListNode), true);
        if (!new_node) {
            return false;
        }
    
        new_node->data = AllocatorAlloc(list->allocator, item_size, true);
        if (!new_node->data) {
            AllocatorFree(list->allocator, new_node);
            LOG_FATAL("list_sort: item_size * item_count overflows u64");
        }
        data = AllocatorAlloc(list->allocator, item_size * item_count, false);
        if (!data) {
            return false;
        }
    
        result.data = (u8 *)AllocatorAlloc(result.allocator, BYTES_FOR_BITS(cap), true);
        if (!result.data) {
            return result;
    
        // Dynamic programming matrix
        u64 *prev_row = AllocatorAlloc(scratch, (len2 + 1) * sizeof(u64), false);
        u64 *curr_row = AllocatorAlloc(scratch, (len2 + 1) * sizeof(u64), false);
        // Dynamic programming matrix
        u64 *prev_row = AllocatorAlloc(scratch, (len2 + 1) * sizeof(u64), false);
        u64 *curr_row = AllocatorAlloc(scratch, (len2 + 1) * sizeof(u64), false);
    
        if (!prev_row || !curr_row) {
        // typed call directly keeps both arms on the typed-dispatch path
        // (no upcast to `Allocator *`, no AllocatorAlloc_dyn).
        void *user_p = self->config.force_page_backing ? AllocatorAlloc(&self->page, padded, zeroed) :
                                                         AllocatorAlloc(&self->heap, padded, zeroed);
        if (!user_p) {
        // (no upcast to `Allocator *`, no AllocatorAlloc_dyn).
        void *user_p = self->config.force_page_backing ? AllocatorAlloc(&self->page, padded, zeroed) :
                                                         AllocatorAlloc(&self->heap, padded, zeroed);
        if (!user_p) {
    #if FEATURE_ALLOC_STATS
        if (count > STACK_MAX) {
            halloc = HeapAllocatorInit();
            pfds   = (plat_pollfd_t *)AllocatorAlloc(&halloc, sizeof(plat_pollfd_t) * count, true);
            if (!pfds) {
                HeapAllocatorDeinit(&halloc);
        }
    
        self->stream_sizes        = AllocatorAlloc(BufAllocator(&self->data), (size)sizes_bytes, 0);
        self->stream_blocks       = AllocatorAlloc(BufAllocator(&self->data), (size)ptrs_bytes, 0);
        self->stream_block_counts = AllocatorAlloc(BufAllocator(&self->data), (size)counts_bytes, 0);
    
        self->stream_sizes        = AllocatorAlloc(BufAllocator(&self->data), (size)sizes_bytes, 0);
        self->stream_blocks       = AllocatorAlloc(BufAllocator(&self->data), (size)ptrs_bytes, 0);
        self->stream_block_counts = AllocatorAlloc(BufAllocator(&self->data), (size)counts_bytes, 0);
        if (!self->stream_sizes || !self->stream_blocks || !self->stream_block_counts)
        self->stream_sizes        = AllocatorAlloc(BufAllocator(&self->data), (size)sizes_bytes, 0);
        self->stream_blocks       = AllocatorAlloc(BufAllocator(&self->data), (size)ptrs_bytes, 0);
        self->stream_block_counts = AllocatorAlloc(BufAllocator(&self->data), (size)counts_bytes, 0);
        if (!self->stream_sizes || !self->stream_blocks || !self->stream_block_counts)
            return false;
        if (out_bytes > (u64)((size)-1))
            return NULL;
        SectionRva *out = AllocatorAlloc(BufAllocator(&self->data), (size)out_bytes, 0);
        if (!out)
            return NULL;
            return NULL;
    
        u8 *buf = AllocatorAlloc(BufAllocator(&self->data), sz, 0);
        if (!buf) {
            AllocatorFree(BufAllocator(&self->data), out);
        // Stream into a flat buffer; the record stream is typically large
        // but not unbounded.
        u8 *buf = AllocatorAlloc(BufAllocator(&self->data), sz, 0);
        if (!buf)
            return false;
    
        size  page_bytes = PageAllocatorPageSize(&page);
        void *region     = AllocatorAlloc(base, page_bytes, true);
        if (!region) {
            return false;
        SlabAllocator slab       = SlabAllocatorInit(sizeof(Node));
        Allocator    *alloc_base = ALLOCATOR_OF(&slab);
        Node         *a          = (Node *)AllocatorAlloc(alloc_base, sizeof(Node), true);
        Node         *b          = (Node *)AllocatorAlloc(alloc_base, sizeof(Node), true);
        bool          ok         = (a != NULL) && (b != NULL) && (a != b);
        Allocator    *alloc_base = ALLOCATOR_OF(&slab);
        Node         *a          = (Node *)AllocatorAlloc(alloc_base, sizeof(Node), true);
        Node         *b          = (Node *)AllocatorAlloc(alloc_base, sizeof(Node), true);
        bool          ok         = (a != NULL) && (b != NULL) && (a != b);
        SlabAllocator slab       = SlabAllocatorInit(sizeof(Node));
        Allocator    *alloc_base = ALLOCATOR_OF(&slab);
        Node         *a          = (Node *)AllocatorAlloc(alloc_base, sizeof(Node), true);
        bool          ok         = (a != NULL);
    
        AllocatorFree(alloc_base, a);
        Node *b = (Node *)AllocatorAlloc(alloc_base, sizeof(Node), true);
        ok      = ok && (b == a); // The free list returned the same slot.
    
        for (size i = 0; i < 600; i++) {
            slots[i] = (Node *)AllocatorAlloc(alloc_base, sizeof(Node), true);
            if (!slots[i]) {
                ok = false;
        SlabAllocator slab       = SlabAllocatorInit(sizeof(int));
        Allocator    *alloc_base = ALLOCATOR_OF(&slab);
        void         *big        = AllocatorAlloc(alloc_base, 4096, true);
        bool          ok         = (big == NULL);
    
        for (size i = 0; i < 200; i++) {
            slots[i] = (Node *)AllocatorAlloc(alloc_base, sizeof(Node), true);
            if (!slots[i]) {
                ok = false;
        Node *fresh[100];
        for (size i = 0; ok && i < 100; i++) {
            fresh[i] = (Node *)AllocatorAlloc(alloc_base, sizeof(Node), true);
            if (!fresh[i]) {
                ok = false;
        SlabAllocator slab       = SlabAllocatorInitAligned(sizeof(int), 64);
        Allocator    *alloc_base = ALLOCATOR_OF(&slab);
        int          *p          = (int *)AllocatorAlloc(alloc_base, sizeof(int), true);
        bool          ok         = (p != NULL) && (((u64)p & 63u) == 0);
        Allocator    *a1 = ALLOCATOR_OF(&s1);
        Allocator    *a2 = ALLOCATOR_OF(&s2);
        Node         *p  = (Node *)AllocatorAlloc(a1, sizeof(Node), false);
        AllocatorFree(a2, p); // foreign to s2 -> LOG_FATAL
        return false;
        SlabAllocator slab  = SlabAllocatorInit(sizeof(Node));
        Allocator    *alloc = ALLOCATOR_OF(&slab);
        u8           *p     = (u8 *)AllocatorAlloc(alloc, sizeof(Node), false);
        AllocatorFree(alloc, p + 1); // mis-aligned -> LOG_FATAL
        return false;
        SlabAllocator slab  = SlabAllocatorInit(sizeof(Node));
        Allocator    *alloc = ALLOCATOR_OF(&slab);
        Node         *p     = (Node *)AllocatorAlloc(alloc, sizeof(Node), false);
        AllocatorFree(alloc, p);
        AllocatorFree(alloc, p); // bit already 0 -> LOG_FATAL
        BudgetAllocator bp        = BudgetAllocatorInit(buf, sizeof(buf), sizeof(Node));
        Allocator      *alloc     = ALLOCATOR_OF(&bp);
        Node           *a         = (Node *)AllocatorAlloc(alloc, sizeof(Node), true);
        Node           *b         = (Node *)AllocatorAlloc(alloc, sizeof(Node), true);
        bool            ok        = (a != NULL) && (b != NULL) && (a != b);
        Allocator      *alloc     = ALLOCATOR_OF(&bp);
        Node           *a         = (Node *)AllocatorAlloc(alloc, sizeof(Node), true);
        Node           *b         = (Node *)AllocatorAlloc(alloc, sizeof(Node), true);
        bool            ok        = (a != NULL) && (b != NULL) && (a != b);
        BudgetAllocator bp       = BudgetAllocatorInit(buf, sizeof(buf), sizeof(Node));
        Allocator      *alloc    = ALLOCATOR_OF(&bp);
        void           *p        = AllocatorAlloc(alloc, 0, false);
        BudgetAllocatorDeinit(&bp);
        return p == NULL;
        Allocator      *alloc    = ALLOCATOR_OF(&bp);
        AllocatorFree(alloc, NULL);
        void *p  = AllocatorAlloc(alloc, sizeof(Node), false);
        bool  ok = (p != NULL);
        if (p)
        bool ok = BudgetAllocatorSlotCount(&bp) > 0;
        for (size i = 0; ok && i < BudgetAllocatorSlotCount(&bp); i++) {
            if (!AllocatorAlloc(alloc, sizeof(Node), false))
                ok = false;
        }
        }
        // One more must fail -- no growth path.
        void *overflow = AllocatorAlloc(alloc, sizeof(Node), false);
        ok             = ok && (overflow == NULL);
        Allocator      *alloc     = ALLOCATOR_OF(&bp);
    
        Node *a  = (Node *)AllocatorAlloc(alloc, sizeof(Node), true);
        bool  ok = (a != NULL);
        AllocatorFree(alloc, a);
        bool  ok = (a != NULL);
        AllocatorFree(alloc, a);
        Node *b = (Node *)AllocatorAlloc(alloc, sizeof(Node), true);
        // ctz finds the lowest clear bit, which is the one we just freed.
        ok = ok && (b == a);
        Allocator      *alloc     = ALLOCATOR_OF(&bp);
    
        Node *a  = (Node *)AllocatorAlloc(alloc, sizeof(Node), false);
        Node *b  = (Node *)AllocatorAlloc(alloc, sizeof(Node), false);
        Node *c  = (Node *)AllocatorAlloc(alloc, sizeof(Node), false);
    
        Node *a  = (Node *)AllocatorAlloc(alloc, sizeof(Node), false);
        Node *b  = (Node *)AllocatorAlloc(alloc, sizeof(Node), false);
        Node *c  = (Node *)AllocatorAlloc(alloc, sizeof(Node), false);
        bool  ok = (a && b && c && a != b && b != c && a != c);
        Node *a  = (Node *)AllocatorAlloc(alloc, sizeof(Node), false);
        Node *b  = (Node *)AllocatorAlloc(alloc, sizeof(Node), false);
        Node *c  = (Node *)AllocatorAlloc(alloc, sizeof(Node), false);
        bool  ok = (a && b && c && a != b && b != c && a != c);
        BudgetAllocator bp       = BudgetAllocatorInit(buf, sizeof(buf), sizeof(int));
        Allocator      *alloc    = ALLOCATOR_OF(&bp);
        void           *big      = AllocatorAlloc(alloc, 4096, true);
        bool            ok       = (big == NULL);
        BudgetAllocatorDeinit(&bp);
        BudgetAllocator bp    = BudgetAllocatorInitAligned(buf, sizeof(buf), sizeof(int), 64);
        Allocator      *alloc = ALLOCATOR_OF(&bp);
        int            *p1    = (int *)AllocatorAlloc(alloc, sizeof(int), true);
        int            *p2    = (int *)AllocatorAlloc(alloc, sizeof(int), true);
        Allocator      *alloc = ALLOCATOR_OF(&bp);
        int            *p1    = (int *)AllocatorAlloc(alloc, sizeof(int), true);
        int            *p2    = (int *)AllocatorAlloc(alloc, sizeof(int), true);
    
        bool ok = (p1 != NULL) && (p2 != NULL);
        Allocator      *alloc2     = ALLOCATOR_OF(&bp2);
    
        Node *p = (Node *)AllocatorAlloc(alloc1, sizeof(Node), false);
        AllocatorFree(alloc2, p); // foreign to bp2 -> LOG_FATAL
        return false;
        BudgetAllocator bp        = BudgetAllocatorInit(buf, sizeof(buf), sizeof(Node));
        Allocator      *alloc     = ALLOCATOR_OF(&bp);
        char           *p         = (char *)AllocatorAlloc(alloc, sizeof(Node), false);
        AllocatorFree(alloc, p + 1); // mis-aligned -> LOG_FATAL
        return false;
        BudgetAllocator bp        = BudgetAllocatorInit(buf, sizeof(buf), sizeof(Node));
        Allocator      *alloc     = ALLOCATOR_OF(&bp);
        Node           *p         = (Node *)AllocatorAlloc(alloc, sizeof(Node), false);
        AllocatorFree(alloc, p);
        AllocatorFree(alloc, p); // bit already 0 -> LOG_FATAL
        Allocator    *alloc = ALLOCATOR_OF(&heap);
    
        u8  *a  = (u8 *)AllocatorAlloc(alloc, 32, true);
        u8  *b  = (u8 *)AllocatorAlloc(alloc, 128, true);
        bool ok = (a != NULL) && (b != NULL) && (a != b);
    
        u8  *a  = (u8 *)AllocatorAlloc(alloc, 32, true);
        u8  *b  = (u8 *)AllocatorAlloc(alloc, 128, true);
        bool ok = (a != NULL) && (b != NULL) && (a != b);
        Allocator    *alloc = ALLOCATOR_OF(&heap);
    
        u8  *p  = (u8 *)AllocatorAlloc(alloc, 64, true);
        bool ok = (p != NULL);
        for (size i = 0; ok && i < 64; i++) {
        Allocator    *alloc = ALLOCATOR_OF(&heap);
    
        void *p = AllocatorAlloc(alloc, 0, false);
    
        HeapAllocatorDeinit(&heap);
    
        // Heap remains usable.
        void *p  = AllocatorAlloc(alloc, 32, false);
        bool  ok = (p != NULL);
        if (p)
        Allocator    *alloc = ALLOCATOR_OF(&heap);
    
        void *a  = AllocatorAlloc(alloc, 32, false);
        void *b  = AllocatorAlloc(alloc, 32, false);
        void *c  = AllocatorAlloc(alloc, 32, false);
    
        void *a  = AllocatorAlloc(alloc, 32, false);
        void *b  = AllocatorAlloc(alloc, 32, false);
        void *c  = AllocatorAlloc(alloc, 32, false);
        bool  ok = (a != NULL) && (b != NULL) && (c != NULL) && (a != b) && (b != c) && (a != c);
        void *a  = AllocatorAlloc(alloc, 32, false);
        void *b  = AllocatorAlloc(alloc, 32, false);
        void *c  = AllocatorAlloc(alloc, 32, false);
        bool  ok = (a != NULL) && (b != NULL) && (c != NULL) && (a != b) && (b != c) && (a != c);
        Allocator    *alloc = ALLOCATOR_OF(&heap);
    
        void *a = AllocatorAlloc(alloc, 32, false);
        AllocatorFree(alloc, a);
        void *b = AllocatorAlloc(alloc, 32, false);
        void *a = AllocatorAlloc(alloc, 32, false);
        AllocatorFree(alloc, a);
        void *b = AllocatorAlloc(alloc, 32, false);
        // ctz finds the LOWEST clear bit, which is the one we just freed.
        bool ok = (a != NULL) && (b == a);
        Allocator    *alloc = ALLOCATOR_OF(&heap);
    
        void **ptrs = (void **)AllocatorAlloc(alloc, (size)(N * sizeof(void *)), true);
        if (!ptrs) {
            HeapAllocatorDeinit(&heap);
        bool ok = true;
        for (u32 i = 0; i < N; i++) {
            ptrs[i] = AllocatorAlloc(alloc, 32, false);
            if (!ptrs[i])
                ok = false;
        bool  ok = true;
        for (u32 i = 0; i < 8; i++) {
            ptrs[i] = AllocatorAlloc(alloc, sizes[i], true);
            if (!ptrs[i])
                ok = false;
    
        size n  = 64 * 1024;
        u8  *p  = (u8 *)AllocatorAlloc(alloc, n, true);
        bool ok = (p != NULL);
        if (ok) {
        Allocator    *alloc = ALLOCATOR_OF(&heap);
    
        void *p  = AllocatorAlloc(alloc, 256, true);
        bool  ok = (p != NULL) && (((u64)p & 63u) == 0);
        // 28 and 30 both round up to the 32-byte class in the current
        // bin layout, so realloc must succeed in place.
        u8  *p  = (u8 *)AllocatorAlloc(alloc, 28, true);
        bool ok = (p != NULL);
        if (ok) {
        Allocator    *alloc = ALLOCATOR_OF(&heap);
    
        u8  *p  = (u8 *)AllocatorAlloc(alloc, 16, true);
        bool ok = (p != NULL);
        if (ok) {
        Allocator    *alloc2 = ALLOCATOR_OF(&h2);
    
        void *a = AllocatorAlloc(alloc1, 32, true);
        void *b = AllocatorAlloc(alloc2, 32, true);
        // intentional bypass: no public accessor exposes the bucket-array
    
        void *a = AllocatorAlloc(alloc1, 32, true);
        void *b = AllocatorAlloc(alloc2, 32, true);
        // intentional bypass: no public accessor exposes the bucket-array
        // pointer (rightly so -- it's internal hash-table storage), but
        Allocator    *alloc1 = ALLOCATOR_OF(&h1);
        Allocator    *alloc2 = ALLOCATOR_OF(&h2);
        void         *p      = AllocatorAlloc(alloc1, 32, false);
        AllocatorFree(alloc2, p); // foreign to h2 -> LOG_FATAL
        return false;             // unreachable
        HeapAllocator heap  = HeapAllocatorInit();
        Allocator    *alloc = ALLOCATOR_OF(&heap);
        void         *p     = AllocatorAlloc(alloc, 32, false);
        AllocatorFree(alloc, p);
        AllocatorFree(alloc, p); // bit already 0 -> LOG_FATAL
        HeapAllocator heap  = HeapAllocatorInit();
        Allocator    *alloc = ALLOCATOR_OF(&heap);
        u8           *p     = (u8 *)AllocatorAlloc(alloc, 64, false);
        AllocatorFree(alloc, p + 1); // mis-aligned -> LOG_FATAL
        return false;
        Allocator    *alloc = ALLOCATOR_OF(&heap);
        size          n     = 16 * 1024;
        u8           *p     = (u8 *)AllocatorAlloc(alloc, n, false);
        AllocatorFree(alloc, p + 128); // mid-allocation -> LOG_FATAL
        return false;
        ArenaAllocator arena      = ArenaAllocatorInit();
        Allocator     *alloc_base = ALLOCATOR_OF(&arena);
        u8            *a          = (u8 *)AllocatorAlloc(alloc_base, 16, true);
        u8            *b          = (u8 *)AllocatorAlloc(alloc_base, 32, true);
        bool           ok         = (a != NULL) && (b != NULL) && (b > a);
        Allocator     *alloc_base = ALLOCATOR_OF(&arena);
        u8            *a          = (u8 *)AllocatorAlloc(alloc_base, 16, true);
        u8            *b          = (u8 *)AllocatorAlloc(alloc_base, 32, true);
        bool           ok         = (a != NULL) && (b != NULL) && (b > a);
        ArenaAllocator arena      = ArenaAllocatorInit();
        Allocator     *alloc_base = ALLOCATOR_OF(&arena);
        u8            *p          = (u8 *)AllocatorAlloc(alloc_base, 16, true);
        bool           ok         = (p != NULL);
        ArenaAllocator arena      = ArenaAllocatorInit();
        Allocator     *alloc_base = ALLOCATOR_OF(&arena);
        u8            *a          = (u8 *)AllocatorAlloc(alloc_base, 16, true);
        u8            *b          = (u8 *)AllocatorAlloc(alloc_base, 16, true);
        (void)b;
        Allocator     *alloc_base = ALLOCATOR_OF(&arena);
        u8            *a          = (u8 *)AllocatorAlloc(alloc_base, 16, true);
        u8            *b          = (u8 *)AllocatorAlloc(alloc_base, 16, true);
        (void)b;
        (void)AllocatorRealloc(alloc_base, a, 64); // -> LOG_FATAL
        ArenaAllocator arena      = ArenaAllocatorInit();
        Allocator     *alloc_base = ALLOCATOR_OF(&arena);
        u8            *a          = (u8 *)AllocatorAlloc(alloc_base, 4096, true);
        u8            *b          = (u8 *)AllocatorAlloc(alloc_base, 4096, true);
        bool           ok         = (a != NULL) && (b != NULL);
        Allocator     *alloc_base = ALLOCATOR_OF(&arena);
        u8            *a          = (u8 *)AllocatorAlloc(alloc_base, 4096, true);
        u8            *b          = (u8 *)AllocatorAlloc(alloc_base, 4096, true);
        bool           ok         = (a != NULL) && (b != NULL);
    
        ArenaAllocatorReset(&arena);
        u8 *c = (u8 *)AllocatorAlloc(alloc_base, 4096, true);
        ok    = ok && (c != NULL) && (c == a); // Reset reuses the first chunk.
        ArenaAllocator arena      = ArenaAllocatorInitAligned(64);
        Allocator     *alloc_base = ALLOCATOR_OF(&arena);
        void          *a          = AllocatorAlloc(alloc_base, 1, true);
        void          *b          = AllocatorAlloc(alloc_base, 1, true);
        bool           ok         = (a != NULL) && (b != NULL);
        Allocator     *alloc_base = ALLOCATOR_OF(&arena);
        void          *a          = AllocatorAlloc(alloc_base, 1, true);
        void          *b          = AllocatorAlloc(alloc_base, 1, true);
        bool           ok         = (a != NULL) && (b != NULL);
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        void *p1 = AllocatorAlloc(adbg, 64, true);
        void *p2 = AllocatorAlloc(adbg, 128, true);
    
        void *p1 = AllocatorAlloc(adbg, 64, true);
        void *p2 = AllocatorAlloc(adbg, 128, true);
    
        bool ok = p1 && p2;
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        void *p  = AllocatorAlloc(adbg, 0, false);
        bool  ok = (p == NULL) && (DebugAllocatorLiveCount(&dbg) == 0);
    
        AllocatorFree(adbg, NULL); // no-op, must not crash
        void *p  = AllocatorAlloc(adbg, 32, false);
        bool  ok = (p != NULL) && (DebugAllocatorLiveCount(&dbg) == 1);
        if (p)
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        u8 *buf = (u8 *)AllocatorAlloc(adbg, 16, true);
        buf[16] = 0x55; // stomp first canary byte
        AllocatorFree(adbg, buf);
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        (void)AllocatorAlloc(adbg, 32, true);
        (void)AllocatorAlloc(adbg, 48, true);
    
        (void)AllocatorAlloc(adbg, 32, true);
        (void)AllocatorAlloc(adbg, 48, true);
    
        bool ok = DebugAllocatorLiveCount(&dbg) == 2;
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        void *p1 = AllocatorAlloc(adbg, 24, true);
        void *p2 = AllocatorAlloc(adbg, 40, true);
        (void)p1;
    
        void *p1 = AllocatorAlloc(adbg, 24, true);
        void *p2 = AllocatorAlloc(adbg, 40, true);
        (void)p1;
        (void)p2;
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        void *p  = AllocatorAlloc(adbg, 64, false);
        bool  ok = (p != NULL);
    
        bool  ok = (DebugAllocatorFreedCount(&dbg) == 0);
        void *p1 = AllocatorAlloc(adbg, 16, false);
        void *p2 = AllocatorAlloc(adbg, 32, false);
        void *p3 = AllocatorAlloc(adbg, 64, false);
        bool  ok = (DebugAllocatorFreedCount(&dbg) == 0);
        void *p1 = AllocatorAlloc(adbg, 16, false);
        void *p2 = AllocatorAlloc(adbg, 32, false);
        void *p3 = AllocatorAlloc(adbg, 64, false);
        ok       = ok && (DebugAllocatorFreedCount(&dbg) == 0); // alloc doesn't push
        void *p1 = AllocatorAlloc(adbg, 16, false);
        void *p2 = AllocatorAlloc(adbg, 32, false);
        void *p3 = AllocatorAlloc(adbg, 64, false);
        ok       = ok && (DebugAllocatorFreedCount(&dbg) == 0); // alloc doesn't push
        bool ok = true;
        for (u32 i = 0; i < 100; i++) {
            void *p = AllocatorAlloc(adbg, 32, false);
            if (!p) {
                ok = false;
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        u8  *p  = (u8 *)AllocatorAlloc(adbg, 16, true);
        bool ok = (p != NULL);
        if (ok) {
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        void *p   = AllocatorAlloc(adbg, 24, false);
        bool  ok  = (p != NULL) && (DebugAllocatorLiveCount(&dbg) == 1);
        void *nil = AllocatorRealloc(adbg, p, 0);
        Allocator     *adbg      = ALLOCATOR_OF(&dbg);
    
        void *p  = AllocatorAlloc(adbg, 64, true);
        bool  ok = (p != NULL) && (DebugAllocatorLiveCount(&dbg) == 1);
        ok       = ok && (DebugAllocatorLiveBytes(&dbg) == 64);
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        void *p = AllocatorAlloc(adbg, 32, true);
        AllocatorFree(adbg, p);
        AllocatorFree(adbg, p); // -> Heap LOG_FATAL
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        u8  *buf = (u8 *)AllocatorAlloc(adbg, 16, true);
        bool ok  = (buf != NULL);
        // Leave canary byte 0 (index 16) intact; stomp byte 5 of the canary.
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        void *p  = AllocatorAlloc(adbg, 48, false);
        bool  ok = (p != NULL);
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        void *p  = AllocatorAlloc(adbg, 48, false);
        bool  ok = (p != NULL);
        Allocator     *adbg      = ALLOCATOR_OF(&dbg);
    
        void *p  = AllocatorAlloc(adbg, 32, false);
        bool  ok = (p != NULL);
        AllocatorFree(adbg, p);
        Allocator     *adbg      = ALLOCATOR_OF(&dbg);
    
        void *p  = AllocatorAlloc(adbg, 32, false);
        bool  ok = (p != NULL);
        AllocatorFree(adbg, p);
        Allocator     *adbg      = ALLOCATOR_OF(&dbg);
    
        void *p  = AllocatorAlloc(adbg, 32, false);
        bool  ok = (p != NULL);
        AllocatorFree(adbg, p);
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        void *p  = AllocatorAlloc(adbg, 32, false);
        bool  ok = (p != NULL);
        AllocatorFree(adbg, p);
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        void *a  = AllocatorAlloc(adbg, 64, false);
        void *b  = AllocatorAlloc(adbg, 128, false);
        bool  ok = (a != NULL) && (b != NULL);
    
        void *a  = AllocatorAlloc(adbg, 64, false);
        void *b  = AllocatorAlloc(adbg, 128, false);
        bool  ok = (a != NULL) && (b != NULL);
        ok       = ok && (AllocatorBytesInUse(adbg) == (64 + 128));
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        u8  *buf = (u8 *)AllocatorAlloc(adbg, 32, false);
        bool ok  = (buf != NULL);
        for (u32 i = 0; ok && i < 32; i++)
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        void *p = AllocatorAlloc(adbg, 32, true);
        AllocatorFree(adbg, p);
        AllocatorFree(adbg, p); // -> LOG_FATAL
        if (levels > 0)
            return deep_alloc(a, bytes, zeroed, levels - 1);
        return AllocatorAlloc(a, bytes, zeroed);
    }
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        void *p  = AllocatorAlloc(adbg, 100, false);
        bool  ok = (p != NULL);
        // self->bytes_in_use is surfaced by DebugAllocatorLiveBytes. A
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        void *p1 = AllocatorAlloc(adbg, 16, false);
        void *p2 = AllocatorAlloc(adbg, 16, false);
        bool  ok = (p1 != NULL) && (p2 != NULL);
    
        void *p1 = AllocatorAlloc(adbg, 16, false);
        void *p2 = AllocatorAlloc(adbg, 16, false);
        bool  ok = (p1 != NULL) && (p2 != NULL);
        ok       = ok && (AllocatorAllocations(adbg) == 2);
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        void *p1 = AllocatorAlloc(adbg, 30, false);
        void *p2 = AllocatorAlloc(adbg, 70, false);
        bool  ok = (p1 != NULL) && (p2 != NULL);
    
        void *p1 = AllocatorAlloc(adbg, 30, false);
        void *p2 = AllocatorAlloc(adbg, 70, false);
        bool  ok = (p1 != NULL) && (p2 != NULL);
        // bytes_requested is cumulative and does NOT decrease on free.
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        void *p  = AllocatorAlloc(adbg, 256, false);
        bool  ok = (p != NULL);
        ok       = ok && (AllocatorBytesInUse(adbg) == 256);
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        void *p  = AllocatorAlloc(adbg, 512, false);
        bool  ok = (p != NULL);
        // peak equals current in-use after a single alloc. A const-mutated
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        void *p  = AllocatorAlloc(adbg, 333, false);
        bool  ok = (p != NULL);
        // Real `if (in_use > peak) peak = in_use` advances 0 -> 333. A
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        void *big = AllocatorAlloc(adbg, 1000, false);
        bool  ok  = (big != NULL) && (AllocatorPeakBytesInUse(adbg) == 1000);
        if (big)
            AllocatorFree(adbg, big);
    
        void *small = AllocatorAlloc(adbg, 8, false);
        ok          = ok && (small != NULL);
        // in_use (8) < peak (1000): peak must stay 1000.
    
        // Absurd size: the heap cannot serve it and returns NULL.
        void *p  = AllocatorAlloc(adbg, (size)-1 - 4096, false);
        bool  ok = (p == NULL);
        ok       = ok && (AllocatorFailedAllocations(adbg) == 1);
    
        size n   = 48;
        u8  *buf = (u8 *)AllocatorAlloc(adbg, n, false);
        bool ok  = (buf != NULL);
        if (ok) {
    
        size n   = 200;
        u8  *buf = (u8 *)AllocatorAlloc(adbg, n, true);
        bool ok  = (buf != NULL);
        if (ok) {
    
        for (u32 i = 0; i < N; i++) {
            ps[i] = AllocatorAlloc(adbg, 8 + (i & 31), false);
            ok    = ok && (ps[i] != NULL);
        }
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        void *p  = AllocatorAlloc(adbg, 64, false);
        bool  ok = (p != NULL);
        // debug_allocator_resize unconditionally returns 0 (refuses).
    
        size n   = 32;
        u8  *buf = (u8 *)AllocatorAlloc(adbg, n, true);
        bool ok  = (buf != NULL);
        if (ok)
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        void *p = AllocatorAlloc(adbg, 16, false);
        (void)p;
        // Corrupt the magic so debug_validate_self trips type-confusion.
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        u8 *p = (u8 *)AllocatorAlloc(adbg, 64, true);
        if (!p)
            return false;
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        u8 *p = (u8 *)AllocatorAlloc(adbg, 64, true);
        if (!p)
            return false;
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        void *p = AllocatorAlloc(adbg, 24, true);
        (void)p;
        dbg.heap.base.__magic = 0xdeadbeefULL;
        Allocator *adbg       = ALLOCATOR_OF(&dbg);
        (void)AllocatorAlloc(adbg, 32, true); // structural -> bad heap magic LOG_FATAL
        return false;                         // unreachable
    }
    
        // First op clears the validated bit.
        void *p  = AllocatorAlloc(adbg, 32, true);
        bool  ok = (p != NULL);
        AllocatorFree(adbg, p);
        dbg.bytes_in_use = 4096;
    
        void *q = AllocatorAlloc(adbg, 16, true); // real: no structural recheck
        ok      = ok && (q != NULL);
        Allocator *adbg    = ALLOCATOR_OF(&dbg);
    
        void *p  = AllocatorAlloc(adbg, 32, true); // real: 4 is pow2 -> accepted
        bool  ok = (p != NULL);
        if (p)
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        void *p  = AllocatorAlloc(adbg, 48, true); // count=1, bytes_in_use=48
        bool  ok = (p != NULL);
        dbg.base.__magic |= MAGIC_VALIDATED_BIT;
    
        void *q = AllocatorAlloc(adbg, 16, true); // real: live + bytes -> no abort
        ok      = ok && (q != NULL);
        dbg.page.base.__magic = 0xdeadbeefULL;
        Allocator *adbg       = ALLOCATOR_OF(&dbg);
        (void)AllocatorAlloc(adbg, 32, true); // structural -> bad page magic LOG_FATAL
        return false;                         // unreachable
    }
            return;
        }
        void *p = AllocatorAlloc(adbg, 32, true);
        AllocatorFree(adbg, p); // captures the free trace from this deep stack
    }
        if (f->fail_now)
            return NULL;
        return AllocatorAlloc(f->inner, bytes, zeroed);
    }
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        void *p  = AllocatorAlloc(adbg, 64, false);
        bool  ok = (p != NULL);
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        void *p  = AllocatorAlloc(adbg, 24, true);
        bool  ok = (p != NULL);
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        void *p  = AllocatorAlloc(adbg, 24, true);
        bool  ok = (p != NULL);
    }
    
    #define fixture_malloc(n) AllocatorAlloc(fixture_alloc(), (n), false)
    #define fixture_free(p)   AllocatorFree(fixture_alloc(), (p))
        PageAllocator alloc      = PageAllocatorInit();
        Allocator    *alloc_base = ALLOCATOR_OF(&alloc);
        void         *ptr        = AllocatorAlloc(alloc_base, 128, true);
        bool          ok         = (ptr != NULL);
        Allocator    *alloc_base = ALLOCATOR_OF(&alloc);
        size          page       = PageAllocatorPageSize(&alloc);
        u8           *ptr        = (u8 *)AllocatorAlloc(alloc_base, 64, true);
        bool          ok         = (ptr != NULL);
        Allocator    *alloc_base = ALLOCATOR_OF(&alloc);
        size          page       = PageAllocatorPageSize(&alloc);
        void         *ptr        = AllocatorAlloc(alloc_base, 1024, true);
        bool          ok         = (ptr != NULL) && (((u64)ptr & (page - 1u)) == 0);
        }
    
        void *p1 = AllocatorAlloc(alloc_base, page, true);
        void *p2 = AllocatorAlloc(alloc_base, page * 2, true);
        if (!p1 || !p2) {
    
        void *p1 = AllocatorAlloc(alloc_base, page, true);
        void *p2 = AllocatorAlloc(alloc_base, page * 2, true);
        if (!p1 || !p2) {
            WriteFmt("alloc failed\n");
        Allocator a = mock_make(ALLOCATOR_EFFORT_RETRY, 2);
        mock_reset(2); // fail twice, succeed on the 3rd
        void *p = AllocatorAlloc(&a, 16, false);
        return p != NULL && mock_alloc_calls == 3;
    }
        Allocator a = mock_make(ALLOCATOR_EFFORT_RETRY, 2);
        mock_reset(1000); // always fail across any plausible attempt count
        void *p = AllocatorAlloc(&a, 16, false);
        return p == NULL && mock_alloc_calls == 3;
    }
        Allocator a = mock_make(ALLOCATOR_EFFORT_RETRY, 3); // 4 attempts
        mock_reset(1000);
        void *p = AllocatorAlloc(&a, 16, false);
        return p == NULL && mock_alloc_calls == 4;
    }
        Allocator a = mock_make(ALLOCATOR_EFFORT_RETRY, 3); // 4 attempts
        mock_reset(1);                                      // fail once, then succeed
        void *p = AllocatorAlloc(&a, 16, false);
        return p != NULL && mock_alloc_calls == 2;
    }
        Allocator a = mock_make_bad_magic();
        mock_reset(0);
        (void)AllocatorAlloc(&a, 16, false); // real -> LOG_FATAL; mutant returns
        return false;
    }
        a.alignment = 3u;                    // not a power of two
        mock_reset(0);
        (void)AllocatorAlloc(&a, 16, false); // real -> LOG_FATAL via Validate
        return false;
    }
        Allocator    *alloc = ALLOCATOR_OF(&heap);
    
        void *p = AllocatorAlloc(alloc, 64, false);
        if (!p) {
            HeapAllocatorDeinit(&heap);
Last updated on