std1.0.0-beta-7 SafeC docs

Source alloc/slab.h

View in the repository

// SafeC Standard Library — Slab Allocator
// Fixed-size object pool. O(1) alloc/free via embedded free-list.
// Conforms to Allocator and ArenaAllocator (allocator.h): typed allocation
// is alloc_t / dealloc_t there.
#pragma once
#include <std/alloc/allocator.h>

namespace std {

// A free slot's first word: the link to the next free slot.
struct SlabLink_ {
    ?&SlabLink_ next;
};

struct SlabAllocator {
    &heap unsigned char base;       // heap-backed backing buffer
    int           owned;   // 1 if SlabAllocator::new allocated base (destroy() frees it)
    ?&SlabLink_   free_head;  // head of the embedded free list (walked inside unsafe)
    unsigned long obj_size;   // slot size: the requested size rounded up to a multiple of 8
    unsigned long cap;        // max number of objects
    unsigned long used;       // currently allocated count
    // 1 if each slot has an extra 8-byte double-free-detection tag ahead
    // of it (SlabAllocator::new, where this allocator sized+owns 'base' itself, so
    // growing the per-slot stride is safe) — 0 if not (SlabAllocator::over, where
    // 'base' is caller-provided and sized to the documented obj_size*count
    // contract, which adding a tag would silently overrun).
    int           has_tags;

    // A slab over a caller-provided buffer. Slots are obj_size rounded up to
    // a multiple of 8 (at least 8, to hold the free-list link) and start
    // 8-byte aligned, so 'cap' can come out below buffer.len / obj_size when
    // that rounding doesn't fit. No double-free detection (see has_tags) —
    // the buffer size contract can't grow to fit a tag.
    static struct SlabAllocator over([]unsigned char buffer, unsigned long obj_size);

    // A heap-backed slab of `count` objects (double-free-detecting).
    static struct SlabAllocator new(unsigned long obj_size, unsigned long count);

    // Allocate one object (all `obj_size` bytes of it) for `size` bytes
    // aligned to `align`. null if the slab is full, size > obj_size, or
    // `align` is above max_align() or not a power of two (0 is taken as 1).
    ?&heap unsigned char alloc(unsigned long size, unsigned long align);

    // Return an object to the pool (null: no-op). Aborts with a diagnostic
    // on double-free (SlabAllocator::new-created allocators only — see has_tags above).
    void          dealloc(?&heap unsigned char ptr);

    // Free every slot at once (slots handed out before become invalid).
    void          reset();

    // Return the number of free slots remaining.
    unsigned long available() const;

    // The largest alignment alloc honours: the largest power of two dividing
    // obj_size, at most 8 (given an 8-aligned buffer: always for SlabAllocator::new).
    unsigned long max_align() const;

    // Free the backing buffer (only valid for heap-backed allocators from SlabAllocator::new).
    void          drop();
    void          destroy() consume;   // = drop()

    // Internal: build embedded free-list through the buffer.
    void          build_freelist_();

    // Internal: bytes between consecutive slots' payload pointers.
    unsigned long slot_stride_() const;
};

} // namespace std