core/mem/tlsf
mem_tlsf
Types
4Allocator
Allocator :: struct {
// Empty lists point at this block to indicate they are free.
block_null: Block_Header,
// Bitmaps for free lists.
fl_bitmap: u32,
sl_bitmap: [FL_INDEX_COUNT]u32,
// Head of free lists.
blocks: [FL_INDEX_COUNT][SL_INDEX_COUNT]^Block_Header,
// Keep track of pools so we can deallocate them.
// If `pool.allocator` is blank, we don't do anything.
// We also use this linked list of pools to report
// statistics like how much memory is still available,
// fragmentation, etc.
pool: Pool,
// If we're expected to grow when we run out of memory,
// how much should we ask the backing allocator for?
new_pool_size: uint,
}SourceBlock_Header
Block_Header :: struct {
prev_phys_block: ^Block_Header,
size: uint,
// Next and previous free blocks.
next_free: ^Block_Header,
prev_free: ^Block_Header,
}SourceBlock header structure.
There are several implementation subtleties involved:
- The
prev_phys_blockfield is only valid if the previous block is free. - The
prev_phys_blockfield is actually stored at the end of the
previous block. It appears at the beginning of this structure only to
simplify the implementation.
- The `next_free` / `prev_free` fields are only valid if the block is free.Error
Error :: enum u8 {
None = 0,
Invalid_Backing_Allocator = 1,
Invalid_Alignment = 2,
Backing_Buffer_Too_Small = 3,
Backing_Buffer_Too_Large = 4,
Backing_Allocator_Error = 5,
}SourcePool
Pool :: struct {
data: []u8,
allocator: runtime.Allocator,
next: ^Pool,
}SourceConstants
14ALIGN_SIZE
ALIGN_SIZE :: _ = 1 << ALIGN_SIZE_LOG2SourceBLOCK_HEADER_FREE
BLOCK_HEADER_FREE :: uint = uint(1 << 0)SourceSince block sizes are always at least a multiple of 4, the two least significant bits of the size field are used to store the block status:
- bit 0: whether block is busy or free
- bit 1: whether previous block is busy or free
BLOCK_HEADER_OVERHEAD
BLOCK_HEADER_OVERHEAD :: uint = uint(size_of(uint))SourceThe size of the block header exposed to used blocks is the size field. The prev_phys_block field is stored inside the previous free block.
BLOCK_HEADER_PREV_FREE
BLOCK_HEADER_PREV_FREE :: uint = uint(1 << 1)SourceBLOCK_SIZE_MAX
BLOCK_SIZE_MAX :: uint = uint(1) << FL_INDEX_MAXSourceBLOCK_SIZE_MIN
BLOCK_SIZE_MIN :: uint = uint(size_of(Block_Header) - size_of(^Block_Header))SourceA free block must be large enough to store its header minus the size of the prev_phys_block field, and no larger than the number of addressable bits for FL_INDEX.
BLOCK_START_OFFSET
BLOCK_START_OFFSET :: offset_of = offset_of(Block_Header, size) + size_of(Block_Header{}.size)SourceUser data starts directly after the size field in a used block.
FL_INDEX_COUNT
FL_INDEX_COUNT :: _ = FL_INDEX_MAX - FL_INDEX_SHIFT + 1SourceFL_INDEX_SHIFT
FL_INDEX_SHIFT :: _ = TLSF_SL_INDEX_COUNT_LOG2 + ALIGN_SIZE_LOG2SourceINITIAL_POOL_OVERHEAD
INITIAL_POOL_OVERHEAD :: 48SourcePOOL_OVERHEAD
POOL_OVERHEAD :: _ = 2 * BLOCK_HEADER_OVERHEADSourceSL_INDEX_COUNT
SL_INDEX_COUNT :: 1 << TLSF_SL_INDEX_COUNT_LOG2SourceSMALL_BLOCK_SIZE
SMALL_BLOCK_SIZE :: _ = 1 << FL_INDEX_SHIFTSourceTLSF_SL_INDEX_COUNT_LOG2
TLSF_SL_INDEX_COUNT_LOG2 :: #config(TLSF_SL_INDEX_COUNT_LOG2, 5)Sourcelog2 of number of linear subdivisions of block sizes. Larger values require more memory in the control structure. Values of 4 or 5 are typical.
Procedures
11allocator
allocator :: proc(t: ^Allocator) -> (runtime.Allocator)Sourceallocator_proc
allocator_proc :: proc(
allocator_data: rawptr,
mode: runtime.Allocator_Mode,
size: int,
alignment: int,
old_memory: rawptr,
old_size: int,
location: _ = #caller_location,
) -> ([]u8, runtime.Allocator_Error)Sourcedestroy
destroy :: proc(control: ^Allocator)Sourceestimate_pool_from_size_alignment
estimate_pool_from_size_alignment :: proc(count: int, size: int, alignment: int) -> (pool_size: int)SourceTries to estimate a pool size sufficient for count allocations, each of size and with alignment.
estimate_pool_from_typeid
estimate_pool_from_typeid :: proc(count: int, type: typeid) -> (pool_size: int)SourceTries to estimate a pool size sufficient for count allocations of type.
ffs
ffs :: proc(word: u32) -> (bit: i32)SourceExported solely to facilitate testing
fls
fls :: proc(word: u32) -> (bit: i32)SourceExported solely to facilitate testing
fls_uint
fls_uint :: proc(size: uint) -> (bit: i32)SourceExported solely to facilitate testing
free_with_size
free_with_size :: proc(control: ^Allocator, ptr: rawptr, size: uint)Sourceinit_from_allocator
init_from_allocator :: proc(control: ^Allocator, backing: runtime.Allocator, initial_pool_size: int, new_pool_size: untyped integer = 0) -> (Error)Sourceinit_from_buffer
init_from_buffer :: proc(control: ^Allocator, buf: []u8) -> (Error)Source