package core:mem/tlsf
Overview
package mem_tlsf implements a Two Level Segregated Fit memory allocator.
Copyright 2024 Jeroen van Rijn <nom@duclavier.com>. Made available under Odin's BSD-3 license. List of contributors: Matt Conte: Original C implementation, see LICENSE file in this package Jeroen van Rijn: Source port
Index
Types (4)
Variables (0)
This section is empty.
Procedure Groups (2)
Types
Allocator ¶
Allocator :: struct { // Empty lists point at this block to indicate they are free. block_null: Block_Header, // Bitmaps for free lists. fl_bitmap: u32 `fmt:"-"`, sl_bitmap: [25]u32 `fmt:"-"`, // Head of free lists. blocks: [25][32]^Block_Header `fmt:"-"`, // 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, }
Related Procedures With Parameters
- allocator
- destroy
- free_with_size
- init_from_allocator
- init_from_buffer
- init (procedure groups)
Block_Header ¶
Block_Header :: struct { prev_phys_block: ^Block_Header, size: uint, // Next and previous free blocks. next_free: ^Block_Header, prev_free: ^Block_Header, }
Block header structure.
There are several implementation subtleties involved:
The prev_phys_block
field is only valid if the previous block is free.
The prev_phys_block
field 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, }
Related Procedures With Returns
- init_from_allocator
- init_from_buffer
- init (procedure groups)
Constants
ALIGN_SIZE ¶
ALIGN_SIZE: int : 1 << ALIGN_SIZE_LOG2
ALIGN_SIZE_LOG2 ¶
ALIGN_SIZE_LOG2: int : 3 when size_of(uintptr) == 8 else 2
All allocation sizes and addresses are aligned to 4/8 bytes
BLOCK_HEADER_FREE ¶
BLOCK_HEADER_FREE :: uint(1 << 0)
Since 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(size_of(uint))
The 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(1 << 1)
BLOCK_SIZE_MAX ¶
BLOCK_SIZE_MAX :: uint(1) << FL_INDEX_MAX
BLOCK_SIZE_MIN ¶
BLOCK_SIZE_MIN :: uint(size_of(Block_Header) - size_of(^Block_Header))
A 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: uintptr : offset_of(Block_Header, size) + size_of(Block_Header{}.size)
User data starts directly after the size field in a used block.
FL_INDEX_COUNT ¶
FL_INDEX_COUNT: int : FL_INDEX_MAX - FL_INDEX_SHIFT + 1
FL_INDEX_MAX ¶
FL_INDEX_MAX: int : 32 when size_of(uintptr) == 8 else 30
We can increase this to support larger allocation sizes, at the expense of more overhead in the TLSF structure
FL_INDEX_SHIFT ¶
FL_INDEX_SHIFT: int : TLSF_SL_INDEX_COUNT_LOG2 + ALIGN_SIZE_LOG2
INITIAL_POOL_OVERHEAD ¶
INITIAL_POOL_OVERHEAD: int : 48
POOL_OVERHEAD ¶
POOL_OVERHEAD: uint : 2 * BLOCK_HEADER_OVERHEAD
SL_INDEX_COUNT ¶
SL_INDEX_COUNT: int : 1 << TLSF_SL_INDEX_COUNT_LOG2
SMALL_BLOCK_SIZE ¶
SMALL_BLOCK_SIZE: int : 1 << FL_INDEX_SHIFT
TLSF_SL_INDEX_COUNT_LOG2 ¶
TLSF_SL_INDEX_COUNT_LOG2: int : #config(TLSF_SL_INDEX_COUNT_LOG2, 5)
log2 of number of linear subdivisions of block sizes. Larger values require more memory in the control structure. Values of 4 or 5 are typical.
Variables
This section is empty.
Procedures
allocator_proc ¶
allocator_proc :: proc( allocator_data: rawptr, mode: runtime.Allocator_Mode, size, alignment: int, old_memory: rawptr, old_size: int, location := #caller_location, ) -> ([]u8, runtime.Allocator_Error) {…}
estimate_pool_from_size_alignment ¶
estimate_pool_from_size_alignment :: proc(count: int, size: int, alignment: int) -> (pool_size: int) {…}
Tries to estimate a pool size sufficient for count
allocations, each of size
and with alignment
.
estimate_pool_from_typeid ¶
Tries to estimate a pool size sufficient for count
allocations of type
.
ffs ¶
Exported solely to facilitate testing
fls ¶
Exported solely to facilitate testing
fls_uint ¶
Exported solely to facilitate testing
Procedure Groups
estimate_pool_size ¶
estimate_pool_size :: proc{ estimate_pool_from_size_alignment, estimate_pool_from_typeid, }
init ¶
init :: proc{ init_from_buffer, init_from_allocator, }
Source Files
Generation Information
Generated with odin version dev-2025-04 (vendor "odin") Windows_amd64 @ 2025-04-15 11:45:54.798719800 +0000 UTC