Skip to content

AllocatorFree

Description

Free memory through an allocator. Typed paths dispatch directly to the concrete *_allocator_deallocate, whose size return value is the freed-byte count (discarded here – stats are updated inline inside the typed body, not through the return). Type-erased Allocator * routes through AllocatorFree_dyn, which adds the outer ValidateAllocator before delegating to the same typed body, so stats counter movement is identical on both paths.

Parameters

Name Direction Description
self in,out Typed allocator pointer or Allocator *.
ptr in Pointer to the allocation, or NULL.

Success

Function returns. The allocation is reclaimed.

Failure

No-op on NULL. A ptr the allocator does not own / has already freed / does not point at an allocation’s base aborts via LOG_FATAL.

Usage example (Cross-references)

Usage examples (Cross-references)
        char **str = (char **)copy;
        if (str && *str) {
            AllocatorFree((Allocator *)alloc, *str);
            *str = NULL;
        }
    
        if (previous) {
            AllocatorFree(allocator_ptr, previous);
        }
            (void)IntToBytesBE(value, buffer, byte_len);
            if (!write_char_internal(o, fmt_info->flags, (Zstr)buffer, byte_len)) {
                AllocatorFree(StrAllocator(o), buffer);
                return false;
            }
                return false;
            }
            AllocatorFree(StrAllocator(o), buffer);
            return true;
        }
    
        if (*zs) {
            AllocatorFree((Allocator *)alloc, (void *)*zs);
            *zs = NULL;
        }
            }
    
            AllocatorFree(vec->allocator, vec->data);
        }
    
        if (vec->length == 0) {
            AllocatorFree(vec->allocator, vec->data);
            vec->data     = NULL;
            vec->capacity = 0;
        clear_map(map, entry_size, key_offset, key_size, value_offset, value_size, hash_offset);
    
        AllocatorFree(map->allocator, map->entries);
        AllocatorFree(map->allocator, map->states);
    
        AllocatorFree(map->allocator, map->entries);
        AllocatorFree(map->allocator, map->states);
    
        MemSet(map, 0, sizeof(*map));
    
        if ((map->length == 0) && (n == 0)) {
            AllocatorFree(map->allocator, map->entries);
            AllocatorFree(map->allocator, map->states);
            map->entries    = NULL;
        if ((map->length == 0) && (n == 0)) {
            AllocatorFree(map->allocator, map->entries);
            AllocatorFree(map->allocator, map->states);
            map->entries    = NULL;
            map->states     = NULL;
    
            if (!new_entries || !new_states) {
                AllocatorFree(map->allocator, new_entries);
                AllocatorFree(map->allocator, new_states);
                // Restore the original table so the map stays usable.
            if (!new_entries || !new_states) {
                AllocatorFree(map->allocator, new_entries);
                AllocatorFree(map->allocator, new_states);
                // Restore the original table so the map stays usable.
                map->entries  = old_entries;
            // Probe-budget exhausted. Free the failed new table, double
            // capacity, retry.
            AllocatorFree(map->allocator, new_entries);
            AllocatorFree(map->allocator, new_states);
            size next_cap = new_capacity * 2;
            // capacity, retry.
            AllocatorFree(map->allocator, new_entries);
            AllocatorFree(map->allocator, new_states);
            size next_cap = new_capacity * 2;
            if (next_cap <= new_capacity) {
        }
    
        AllocatorFree(map->allocator, old_entries);
        AllocatorFree(map->allocator, old_states);
    
        AllocatorFree(map->allocator, old_entries);
        AllocatorFree(map->allocator, old_states);
    
        (void)value_offset;
                hash
            )) {
            AllocatorFree(map->allocator, temp_entry);
            return false;
        }
                map->value_copy_deinit(temp_entry + value_offset, map->allocator);
            }
            AllocatorFree(map->allocator, temp_entry);
            return false;
        }
            return false;
        }
        AllocatorFree(map->allocator, temp_entry);
        return true;
    }
                    map->value_copy_deinit(temp_value, map->allocator);
                }
                AllocatorFree(map->allocator, temp_value);
                return false;
            }
        if (map->value_copy_init) {
            MemCopy(dst_value, temp_value, value_size);
            AllocatorFree(map->allocator, temp_value);
        } else {
            MemCopy(dst_value, value, value_size);
        }
    
        AllocatorFree(graph->allocator, data);
    }
        new_node->data = AllocatorAlloc(list->allocator, item_size, true);
        if (!new_node->data) {
            AllocatorFree(list->allocator, new_node);
            return false;
        }
        if (list->copy_init) {
            if (!list->copy_init(new_node->data, item_data, list->allocator)) {
                AllocatorFree(list->allocator, new_node->data);
                AllocatorFree(list->allocator, new_node);
                return false;
            if (!list->copy_init(new_node->data, item_data, list->allocator)) {
                AllocatorFree(list->allocator, new_node->data);
                AllocatorFree(list->allocator, new_node);
                return false;
            }
    
                MemSet(node->data, 0, item_size);
                AllocatorFree(list->allocator, node->data);
                node->data = NULL;
                }
    
                AllocatorFree(list->allocator, node->data);
                node->data = NULL;
                node       = node->next;
            node->prev = NULL;
    
            AllocatorFree(list->allocator, node);
            node = next;
        }
        }
    
        AllocatorFree(list->allocator, data);
        return true;
    }
        ValidateBitVec(bitvec);
        if (bitvec->data) {
            AllocatorFree(bitvec->allocator, bitvec->data);
        }
        MemSet(bitvec, 0, sizeof(*bitvec));
        ValidateBitVec(bv);
        if (bv->length == 0) {
            AllocatorFree(bv->allocator, bv->data);
            bv->data      = NULL;
            bv->capacity  = 0;
    
        if (!prev_row || !curr_row) {
            AllocatorFree(scratch, prev_row);
            AllocatorFree(scratch, curr_row);
            return false;
        if (!prev_row || !curr_row) {
            AllocatorFree(scratch, prev_row);
            AllocatorFree(scratch, curr_row);
            return false;
        }
    
        *out = prev_row[len2];
        AllocatorFree(scratch, prev_row);
        AllocatorFree(scratch, curr_row);
        *out = prev_row[len2];
        AllocatorFree(scratch, prev_row);
        AllocatorFree(scratch, curr_row);
    
        return true;
        if (!MapInsertR(&self->live, user_p, rec)) {
            if (self->config.force_page_backing)
                AllocatorFree(&self->page, user_p);
            else
                AllocatorFree(&self->heap, user_p);
                AllocatorFree(&self->page, user_p);
            else
                AllocatorFree(&self->heap, user_p);
            LOG_ERROR("DebugAllocator: failed to record allocation in live map");
    #if FEATURE_ALLOC_STATS
            // entries-table lookup.
            if (self->config.force_page_backing)
                AllocatorFree(&self->page, ptr);
            else
                AllocatorFree(&self->heap, ptr);
                AllocatorFree(&self->page, ptr);
            else
                AllocatorFree(&self->heap, ptr);
            return 0;
        }
            }
        } else {
            AllocatorFree(&self->heap, ptr);
        }
    #if FEATURE_ALLOC_STATS
    
        if (used_heap) {
            AllocatorFree(&halloc, pfds);
            HeapAllocatorDeinit(&halloc);
        }
        u8 *buf = AllocatorAlloc(BufAllocator(&self->data), sz, 0);
        if (!buf) {
            AllocatorFree(BufAllocator(&self->data), out);
            return NULL;
        }
        bool read_ok = stream_read(self, section_hdr_stream, 0, buf, sz);
        if (!read_ok) {
            AllocatorFree(BufAllocator(&self->data), buf);
            AllocatorFree(BufAllocator(&self->data), out);
            return NULL;
        if (!read_ok) {
            AllocatorFree(BufAllocator(&self->data), buf);
            AllocatorFree(BufAllocator(&self->data), out);
            return NULL;
        }
            (void)BufReadU32LE(&rec, &out[i].virtual_address);
        }
        AllocatorFree(BufAllocator(&self->data), buf);
        *out_count = n;
        return out;
            return false;
        if (!stream_read(self, symrec_stream, 0, buf, sz)) {
            AllocatorFree(BufAllocator(&self->data), buf);
            return false;
        }
                        pp.rva = rva;
                        if (!pool_append_cstr(pool, name, &pp.name_offset_in_pool)) {
                            AllocatorFree(BufAllocator(&self->data), buf);
                            return false;
                        }
                        }
                        if (!VecPushBackR(pending, pp)) {
                            AllocatorFree(BufAllocator(&self->data), buf);
                            return false;
                        }
        }
    
        AllocatorFree(BufAllocator(&self->data), buf);
        return true;
    }
        if (!sections || num_sections == 0) {
            if (sections)
                AllocatorFree(BufAllocator(&self->data), sections);
            return true; // can't compute RVAs without section table
        }
        PendingPubs pending = VecInitT(pending, BufAllocator(&self->data));
        bool        ok      = walk_publics(self, dbi.symrec_stream, sections, num_sections, &self->name_pool, &pending);
        AllocatorFree(BufAllocator(&self->data), sections);
    
        if (!ok) {
        if (alloc) {
            if (self->stream_dir)
                AllocatorFree(alloc, self->stream_dir);
            if (self->stream_sizes)
                AllocatorFree(alloc, self->stream_sizes);
                AllocatorFree(alloc, self->stream_dir);
            if (self->stream_sizes)
                AllocatorFree(alloc, self->stream_sizes);
            if (self->stream_blocks)
                AllocatorFree(alloc, self->stream_blocks);
                AllocatorFree(alloc, self->stream_sizes);
            if (self->stream_blocks)
                AllocatorFree(alloc, self->stream_blocks);
            if (self->stream_block_counts)
                AllocatorFree(alloc, self->stream_block_counts);
                AllocatorFree(alloc, self->stream_blocks);
            if (self->stream_block_counts)
                AllocatorFree(alloc, self->stream_block_counts);
        }
        StrDeinit(&self->name_pool);
        ok       = ok && PageProtect(region, page_bytes, PAGE_PROT_READ_WRITE);
    
        AllocatorFree(&page, region);
        PageAllocatorDeinit(&page);
        return ok;
            b->id = 2;
            ok    = (a->id == 1) && (b->id == 2);
            AllocatorFree(alloc_base, a);
            AllocatorFree(alloc_base, b);
        }
            ok    = (a->id == 1) && (b->id == 2);
            AllocatorFree(alloc_base, a);
            AllocatorFree(alloc_base, b);
        }
        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.
        ok      = ok && (b == a); // The free list returned the same slot.
    
        AllocatorFree(alloc_base, b);
        SlabAllocatorDeinit(&slab);
        return ok;
        for (size i = 0; i < 600; i++) {
            if (slots[i]) {
                AllocatorFree(alloc_base, slots[i]);
            }
        }
        // walk both the free list (recycling) and the slab on growth.
        for (size i = 0; ok && i < 200; i += 2) {
            AllocatorFree(alloc_base, slots[i]);
            slots[i] = NULL;
        }
    
        if (p) {
            AllocatorFree(alloc_base, p);
        }
        SlabAllocatorDeinit(&slab);
        Allocator    *a2 = ALLOCATOR_OF(&s2);
        Node         *p  = (Node *)AllocatorAlloc(a1, sizeof(Node), false);
        AllocatorFree(a2, p); // foreign to s2 -> LOG_FATAL
        return false;
    }
        Allocator    *alloc = ALLOCATOR_OF(&slab);
        u8           *p     = (u8 *)AllocatorAlloc(alloc, sizeof(Node), false);
        AllocatorFree(alloc, p + 1); // mis-aligned -> LOG_FATAL
        return false;
    }
        Allocator    *alloc = ALLOCATOR_OF(&slab);
        Node         *p     = (Node *)AllocatorAlloc(alloc, sizeof(Node), false);
        AllocatorFree(alloc, p);
        AllocatorFree(alloc, p); // bit already 0 -> LOG_FATAL
        return false;
        Node         *p     = (Node *)AllocatorAlloc(alloc, sizeof(Node), false);
        AllocatorFree(alloc, p);
        AllocatorFree(alloc, p); // bit already 0 -> LOG_FATAL
        return false;
    }
            b->id = 2;
            ok    = (a->id == 1) && (b->id == 2);
            AllocatorFree(alloc, a);
            AllocatorFree(alloc, b);
        }
            ok    = (a->id == 1) && (b->id == 2);
            AllocatorFree(alloc, a);
            AllocatorFree(alloc, b);
        }
        BudgetAllocator bp       = BudgetAllocatorInit(buf, sizeof(buf), sizeof(Node));
        Allocator      *alloc    = ALLOCATOR_OF(&bp);
        AllocatorFree(alloc, NULL);
        void *p  = AllocatorAlloc(alloc, sizeof(Node), false);
        bool  ok = (p != NULL);
        bool  ok = (p != NULL);
        if (p)
            AllocatorFree(alloc, p);
        BudgetAllocatorDeinit(&bp);
        return ok;
        Node *a  = (Node *)AllocatorAlloc(alloc, sizeof(Node), true);
        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);
    
        AllocatorFree(alloc, b);
        BudgetAllocatorDeinit(&bp);
        return ok;
    
        if (a)
            AllocatorFree(alloc, a);
        if (b)
            AllocatorFree(alloc, b);
            AllocatorFree(alloc, a);
        if (b)
            AllocatorFree(alloc, b);
        if (c)
            AllocatorFree(alloc, c);
            AllocatorFree(alloc, b);
        if (c)
            AllocatorFree(alloc, c);
        BudgetAllocatorDeinit(&bp);
        return ok;
    
        if (p1)
            AllocatorFree(alloc, p1);
        if (p2)
            AllocatorFree(alloc, p2);
            AllocatorFree(alloc, p1);
        if (p2)
            AllocatorFree(alloc, p2);
        BudgetAllocatorDeinit(&bp);
        return ok;
    
        Node *p = (Node *)AllocatorAlloc(alloc1, sizeof(Node), false);
        AllocatorFree(alloc2, p); // foreign to bp2 -> LOG_FATAL
        return false;
    }
        // exposing it would invite misuse); the test needs a non-NULL
        // pointer in the bitmap region to exercise the foreign-ptr check.
        AllocatorFree(alloc, bp.bitmap); // bitmap region -> LOG_FATAL
        return false;
    }
        Allocator      *alloc     = ALLOCATOR_OF(&bp);
        char           *p         = (char *)AllocatorAlloc(alloc, sizeof(Node), false);
        AllocatorFree(alloc, p + 1); // mis-aligned -> LOG_FATAL
        return false;
    }
        Allocator      *alloc     = ALLOCATOR_OF(&bp);
        Node           *p         = (Node *)AllocatorAlloc(alloc, sizeof(Node), false);
        AllocatorFree(alloc, p);
        AllocatorFree(alloc, p); // bit already 0 -> LOG_FATAL
        return false;
        Node           *p         = (Node *)AllocatorAlloc(alloc, sizeof(Node), false);
        AllocatorFree(alloc, p);
        AllocatorFree(alloc, p); // bit already 0 -> LOG_FATAL
        return false;
    }
            b[127] = 'B';
            ok     = (a[0] == 'A') && (a[31] == 'Z') && (b[0] == 'b') && (b[127] == 'B');
            AllocatorFree(alloc, a);
            AllocatorFree(alloc, b);
        }
            ok     = (a[0] == 'A') && (a[31] == 'Z') && (b[0] == 'b') && (b[127] == 'B');
            AllocatorFree(alloc, a);
            AllocatorFree(alloc, b);
        }
        }
        if (p)
            AllocatorFree(alloc, p);
        HeapAllocatorDeinit(&heap);
        return ok;
    
        // Must be silent + harmless.
        AllocatorFree(alloc, NULL);
    
        // Heap remains usable.
        bool  ok = (p != NULL);
        if (p)
            AllocatorFree(alloc, p);
        HeapAllocatorDeinit(&heap);
        return ok;
        bool  ok = (a != NULL) && (b != NULL) && (c != NULL) && (a != b) && (b != c) && (a != c);
    
        AllocatorFree(alloc, a);
        AllocatorFree(alloc, b);
        AllocatorFree(alloc, c);
    
        AllocatorFree(alloc, a);
        AllocatorFree(alloc, b);
        AllocatorFree(alloc, c);
        HeapAllocatorDeinit(&heap);
        AllocatorFree(alloc, a);
        AllocatorFree(alloc, b);
        AllocatorFree(alloc, c);
        HeapAllocatorDeinit(&heap);
        return ok;
    
        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.
    
        if (b)
            AllocatorFree(alloc, b);
        HeapAllocatorDeinit(&heap);
        return ok;
        for (u32 i = 0; i < N; i++) {
            if (ptrs[i])
                AllocatorFree(alloc, ptrs[i]);
        }
        AllocatorFree(alloc, ptrs);
                AllocatorFree(alloc, ptrs[i]);
        }
        AllocatorFree(alloc, ptrs);
        HeapAllocatorDeinit(&heap);
        return ok;
        for (u32 i = 0; i < 8; i++) {
            if (ptrs[i])
                AllocatorFree(alloc, ptrs[i]);
        }
        HeapAllocatorDeinit(&heap);
            p[n - 1] = 0xCD;
            ok       = (p[0] == 0xAB) && (p[n - 1] == 0xCD) && (HeapAllocatorXlCount(&heap) == 1);
            AllocatorFree(alloc, p);
            ok = ok && (HeapAllocatorXlCount(&heap) == 0);
        }
    
        if (p)
            AllocatorFree(alloc, p);
        HeapAllocatorDeinit(&heap);
        return ok;
            u8 *grown = (u8 *)AllocatorRealloc(alloc, p, 30);
            ok        = (grown == p) && (grown[0] == 'x');
            AllocatorFree(alloc, grown);
        }
        HeapAllocatorDeinit(&heap);
            ok = (grown != NULL) && (grown != p) && (grown[0] == 'h') && (grown[15] == '!');
            if (grown)
                AllocatorFree(alloc, grown);
        }
        HeapAllocatorDeinit(&heap);
                  (HeapAllocatorPageCount(&h2) > 0);
    
        AllocatorFree(alloc1, a);
        AllocatorFree(alloc2, b);
        HeapAllocatorDeinit(&h1);
    
        AllocatorFree(alloc1, a);
        AllocatorFree(alloc2, b);
        HeapAllocatorDeinit(&h1);
        HeapAllocatorDeinit(&h2);
        Allocator    *alloc2 = ALLOCATOR_OF(&h2);
        void         *p      = AllocatorAlloc(alloc1, 32, false);
        AllocatorFree(alloc2, p); // foreign to h2 -> LOG_FATAL
        return false;             // unreachable
    }
        Allocator    *alloc = ALLOCATOR_OF(&heap);
        void         *p     = AllocatorAlloc(alloc, 32, false);
        AllocatorFree(alloc, p);
        AllocatorFree(alloc, p); // bit already 0 -> LOG_FATAL
        return false;
        void         *p     = AllocatorAlloc(alloc, 32, false);
        AllocatorFree(alloc, p);
        AllocatorFree(alloc, p); // bit already 0 -> LOG_FATAL
        return false;
    }
        Allocator    *alloc = ALLOCATOR_OF(&heap);
        u8           *p     = (u8 *)AllocatorAlloc(alloc, 64, false);
        AllocatorFree(alloc, p + 1); // mis-aligned -> LOG_FATAL
        return false;
    }
        size          n     = 16 * 1024;
        u8           *p     = (u8 *)AllocatorAlloc(alloc, n, false);
        AllocatorFree(alloc, p + 128); // mid-allocation -> LOG_FATAL
        return false;
    }
        bool ok = (out != NULL) && s && (ZstrCompare(s, "hello world") == 0);
        if (s)
            AllocatorFree(&dbg.base, s);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
        DebugAllocatorDeinit(&dbg);
        Allocator     *alloc_base = ALLOCATOR_OF(&arena);
        char           stack_byte = 0;
        AllocatorFree(alloc_base, &stack_byte); // -> LOG_FATAL
        return false;                           // unreachable
    }
        ok      = ok && DebugAllocatorLiveBytes(&dbg) == (64 + 128);
    
        AllocatorFree(adbg, p1);
        ok = ok && DebugAllocatorLiveCount(&dbg) == 1;
        ok = ok && DebugAllocatorLiveBytes(&dbg) == 128;
        ok = ok && DebugAllocatorLiveBytes(&dbg) == 128;
    
        AllocatorFree(adbg, p2);
        ok = ok && DebugAllocatorLiveCount(&dbg) == 0;
        ok = ok && DebugAllocatorLiveBytes(&dbg) == 0;
        Allocator     *adbg = ALLOCATOR_OF(&dbg);
    
        AllocatorFree(adbg, NULL); // no-op, must not crash
        void *p  = AllocatorAlloc(adbg, 32, false);
        bool  ok = (p != NULL) && (DebugAllocatorLiveCount(&dbg) == 1);
        bool  ok = (p != NULL) && (DebugAllocatorLiveCount(&dbg) == 1);
        if (p)
            AllocatorFree(adbg, p);
    
        DebugAllocatorDeinit(&dbg);
        u8 *buf = (u8 *)AllocatorAlloc(adbg, 16, true);
        buf[16] = 0x55; // stomp first canary byte
        AllocatorFree(adbg, buf);
    
        bool ok = DebugAllocatorOverflows(&dbg) == 1;
        HeapAllocatorDeinit(&scratch);
        if (p1)
            AllocatorFree(adbg, p1);
        if (p2)
            AllocatorFree(adbg, p2);
            AllocatorFree(adbg, p1);
        if (p2)
            AllocatorFree(adbg, p2);
        DebugAllocatorDeinit(&dbg);
        return ok;
    
        if (p)
            AllocatorFree(adbg, p);
        DebugAllocatorDeinit(&dbg);
        return ok;
        ok       = ok && (DebugAllocatorFreedCount(&dbg) == 0); // alloc doesn't push
    
        AllocatorFree(adbg, p1);
        ok = ok && (DebugAllocatorFreedCount(&dbg) == 1);
        ok = ok && (DebugAllocatorFreedCount(&dbg) == 1);
    
        AllocatorFree(adbg, p2);
        AllocatorFree(adbg, p3);
        ok = ok && (DebugAllocatorFreedCount(&dbg) == 3);
    
        AllocatorFree(adbg, p2);
        AllocatorFree(adbg, p3);
        ok = ok && (DebugAllocatorFreedCount(&dbg) == 3);
                break;
            }
            AllocatorFree(adbg, p);
        }
        ok = ok && (DebugAllocatorFreedCount(&dbg) == 0);
    
        if (grown)
            AllocatorFree(adbg, grown);
        DebugAllocatorDeinit(&dbg);
        return ok;
        bool  ok = (p != NULL) && (DebugAllocatorLiveCount(&dbg) == 1);
        if (p)
            AllocatorFree(adbg, p);
    
        DebugAllocatorDeinit(&dbg);
        ok       = ok && (((u64)p & 0xfff) == 0); // page-aligned
    
        AllocatorFree(adbg, p);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
    
        void *p = AllocatorAlloc(adbg, 32, true);
        AllocatorFree(adbg, p);
        AllocatorFree(adbg, p); // -> Heap LOG_FATAL
        return false;           // unreachable
        void *p = AllocatorAlloc(adbg, 32, true);
        AllocatorFree(adbg, p);
        AllocatorFree(adbg, p); // -> Heap LOG_FATAL
        return false;           // unreachable
    }
    
        u8 junk[64];
        AllocatorFree(adbg, junk);
        return false; // unreachable
    }
        // Leave canary byte 0 (index 16) intact; stomp byte 5 of the canary.
        buf[16 + 5] = 0x77;
        AllocatorFree(adbg, buf);
    
        ok = ok && (DebugAllocatorOverflows(&dbg) == 1);
        ok                  = ok && (live_n > 0) && (live_n <= DEBUG_ALLOCATOR_MAX_TRACE);
    
        AllocatorFree(adbg, p);
        ok = ok && (DebugAllocatorFreedCount(&dbg) == 1);
        ok = ok && (VecPtrAt(&dbg.freed, 0)->alloc_trace_n == live_n);
            MemCopy(snapshot, rec->alloc_trace, (size)n * sizeof(StackFrame));
    
        AllocatorFree(adbg, p);
        ok = ok && (DebugAllocatorFreedCount(&dbg) == 1);
        void *p  = AllocatorAlloc(adbg, 32, false);
        bool  ok = (p != NULL);
        AllocatorFree(adbg, p);
    
        ok = ok && (DebugAllocatorFreedCount(&dbg) == 1);
        void *p  = AllocatorAlloc(adbg, 32, false);
        bool  ok = (p != NULL);
        AllocatorFree(adbg, p);
    
        ok = ok && (DebugAllocatorFreedCount(&dbg) == 1);
        void *p  = AllocatorAlloc(adbg, 32, false);
        bool  ok = (p != NULL);
        AllocatorFree(adbg, p);
    
        ok = ok && (DebugAllocatorFreedCount(&dbg) == 1);
        void *p  = AllocatorAlloc(adbg, 32, false);
        bool  ok = (p != NULL);
        AllocatorFree(adbg, p);
    
        ok = ok && (AllocatorDeallocations(adbg) == 1);
        ok       = ok && (AllocatorBytesInUse(adbg) == (64 + 128));
    
        AllocatorFree(adbg, a);
        ok = ok && (AllocatorBytesInUse(adbg) == 128);
        ok = ok && (AllocatorBytesInUse(adbg) == 128);
    
        AllocatorFree(adbg, b);
        ok = ok && (AllocatorBytesInUse(adbg) == 0);
        for (u32 i = 0; ok && i < 32; i++)
            ok = ok && (buf[i] == (u8)(i & 0xff));
        AllocatorFree(adbg, buf);
    
        ok = ok && (DebugAllocatorOverflows(&dbg) == 0);
    
        void *p = AllocatorAlloc(adbg, 32, true);
        AllocatorFree(adbg, p);
        AllocatorFree(adbg, p); // -> LOG_FATAL
        return false;           // unreachable
        void *p = AllocatorAlloc(adbg, 32, true);
        AllocatorFree(adbg, p);
        AllocatorFree(adbg, p); // -> LOG_FATAL
        return false;           // unreachable
    }
    
        u8 junk[64];
        AllocatorFree(adbg, junk); // -> LOG_FATAL (foreign ptr)
        return false;              // unreachable
    }
    
        if (p)
            AllocatorFree(adbg, p);
        ok = ok && (DebugAllocatorLiveBytes(&dbg) == 0);
        DebugAllocatorDeinit(&dbg);
    
        if (p1)
            AllocatorFree(adbg, p1);
        if (p2)
            AllocatorFree(adbg, p2);
            AllocatorFree(adbg, p1);
        if (p2)
            AllocatorFree(adbg, p2);
        DebugAllocatorDeinit(&dbg);
        return ok;
    
        if (p1)
            AllocatorFree(adbg, p1);
        if (p2)
            AllocatorFree(adbg, p2);
            AllocatorFree(adbg, p1);
        if (p2)
            AllocatorFree(adbg, p2);
        // still 100 after frees (cumulative)
        ok = ok && (AllocatorBytesRequested(adbg) == 100);
    
        if (p)
            AllocatorFree(adbg, p);
        ok = ok && (AllocatorBytesInUse(adbg) == 0);
        DebugAllocatorDeinit(&dbg);
    
        if (p)
            AllocatorFree(adbg, p);
        // peak does not shrink on free
        ok = ok && (AllocatorPeakBytesInUse(adbg) == 512);
    
        if (p)
            AllocatorFree(adbg, p);
        DebugAllocatorDeinit(&dbg);
        return ok;
        bool  ok  = (big != NULL) && (AllocatorPeakBytesInUse(adbg) == 1000);
        if (big)
            AllocatorFree(adbg, big);
    
        void *small = AllocatorAlloc(adbg, 8, false);
    
        if (small)
            AllocatorFree(adbg, small);
        DebugAllocatorDeinit(&dbg);
        return ok;
    
        if (p)
            AllocatorFree(adbg, p);
        DebugAllocatorDeinit(&dbg);
        return ok;
    
        if (p)
            AllocatorFree(adbg, p);
        DebugAllocatorDeinit(&dbg);
        return ok;
    
        if (p)
            AllocatorFree(adbg, p);
        DebugAllocatorDeinit(&dbg);
        return ok;
                buf[i] = (u8)(0xA0 + (i & 7)); // write the entire user region
        }
        AllocatorFree(adbg, buf);              // must be a clean free, no overflow
    
        ok = ok && (DebugAllocatorOverflows(&dbg) == 0);
                buf[i] = 0xFF;
        }
        AllocatorFree(adbg, buf);
        ok = ok && (DebugAllocatorOverflows(&dbg) == 0);
        DebugAllocatorDeinit(&dbg);
        // entry, no spurious bad-free / double-free.
        for (u32 i = 0; i < N; i += 2) {
            AllocatorFree(adbg, ps[i]);
            ps[i] = NULL;
        }
        }
        for (u32 i = 1; i < N; i += 2) {
            AllocatorFree(adbg, ps[i]);
            ps[i] = NULL;
        }
    
        if (p)
            AllocatorFree(adbg, p);
        DebugAllocatorDeinit(&dbg);
        return ok;
        if (ok)
            buf[n] = 0x55; // one past the user region -> stomps canary[0]
        AllocatorFree(adbg, buf);
    
        // The canary sits exactly at offset n; a one-past write corrupts it.
    
        if (grown)
            AllocatorFree(adbg, grown);
        DebugAllocatorDeinit(&dbg);
        return ok;
    
        if (shrunk)
            AllocatorFree(adbg, shrunk);
        // A max-copy mutant would have stomped the fresh canary; real code
        // leaves it pristine.
        HeapAllocatorDeinit(&scratch);
        if (p)
            AllocatorFree(adbg, p);
        DebugAllocatorDeinit(&dbg);
        return ok;
        void *p  = AllocatorAlloc(adbg, 32, true);
        bool  ok = (p != NULL);
        AllocatorFree(adbg, p);
    
        // Live is now empty; break the bytes_in_use/live consistency
        // the suite stay clean.
        if (q)
            AllocatorFree(adbg, q);
        dbg.bytes_in_use = 0;
        DebugAllocatorDeinit(&dbg);
        bool  ok = (p != NULL);
        if (p)
            AllocatorFree(adbg, p);
    
        DebugAllocatorDeinit(&dbg);
    
        if (q)
            AllocatorFree(adbg, q);
        if (p)
            AllocatorFree(adbg, p);
            AllocatorFree(adbg, q);
        if (p)
            AllocatorFree(adbg, p);
        DebugAllocatorDeinit(&dbg);
        return ok;
        }
        void *p = AllocatorAlloc(adbg, 32, true);
        AllocatorFree(adbg, p); // captures the free trace from this deep stack
    }
    static size fail_alloc_deallocate(Allocator *self, void *ptr) {
        FailAlloc *f = (FailAlloc *)(void *)self;
        AllocatorFree(f->inner, ptr);
        return 0;
    }
        // free takes the `requested > bytes_in_use` underflow-guard else arm.
        dbg.base.stats.bytes_in_use = 10;
        AllocatorFree(adbg, p);
    
        // Real: else arm sets stats.bytes_in_use = 0. Mutant: = 42.
        HeapAllocatorDeinit(&scratch);
        if (p)
            AllocatorFree(adbg, p);
        DebugAllocatorDeinit(&dbg);
        return ok;
        HeapAllocatorDeinit(&scratch);
        if (p)
            AllocatorFree(adbg, p);
        DebugAllocatorDeinit(&dbg);
        return ok;
    
    #define fixture_malloc(n) AllocatorAlloc(fixture_alloc(), (n), false)
    #define fixture_free(p)   AllocatorFree(fixture_alloc(), (p))
    
    // Cast off const: `zstr_dup` returns a `Zstr` because the project convention
            ((char *)ptr)[127] = 'y';
            ok                 = ok && (((char *)ptr)[0] == 'x') && (((char *)ptr)[127] == 'y');
            AllocatorFree(&alloc, ptr);
        }
                ok                  = ok && (shrunk != NULL) && (shrunk[0] == 'a');
                if (shrunk) {
                    AllocatorFree(&alloc, shrunk);
                }
            }
    
        if (ptr) {
            AllocatorFree(&alloc, ptr);
        }
    
        if (p1) {
            AllocatorFree(&alloc, p1);
        }
        // EntryCount drops to 1 (p1 moved to free_entries[]).
    
        if (p2) {
            AllocatorFree(&alloc, p2);
        }
        if (PageAllocatorEntryCount(&alloc) != 0) {
    static bool test_al_free_validates_magic(void) {
        Allocator a = mock_make_bad_magic();
        AllocatorFree(&a, mock_backing); // real -> Validate -> LOG_FATAL
        return false;
    }
               && (AllocatorBytesRequested(alloc) == 0u);
    
        AllocatorFree(alloc, p);
        HeapAllocatorDeinit(&heap);
        return ok;
Last updated on