diff --git a/code/grime_arena.odin b/code/grime_arena.odin new file mode 100644 index 0000000..dbf1bf8 --- /dev/null +++ b/code/grime_arena.odin @@ -0,0 +1,45 @@ +/* +The default arena allocator Odin provides does fragmented resizes even for the last most allocated block getting resized. +This is an alternative to alleviates that. +*/ +package sectr + +ArenaFixedHeader :: struct { + data : []byte, + offset : uint, + peak_used : uint, +} + +ArenaFixed :: struct { + using header : ^ArenaFixedHeader, +} + +arena_fixed_init :: proc( backing : []byte ) -> (arena : ArenaFixed) { + header_size := size_of(ArenaFixedHeader) + + verify(len(backing) >= (header_size + Kilobyte), "Attempted to init an arena with less than kilobyte of memory...") + + arena.header = cast(^ArenaFixedHeader) raw_data(backing) + using arena.header + data_ptr := cast([^]byte) (cast( [^]ArenaFixedHeader) arena.header)[ 1:] + data = slice_ptr( data_ptr, len(backing) - header_size ) + offset = 0 + peak_used = 0 + return +} + +arena_fixed_allocator_proc :: proc( + allocator_data : rawptr, + mode : AllocatorMode, + size : int, + alignment : int, + old_memory : rawptr, + old_size : int, + location := #caller_location +) -> ([]byte, AllocatorError) +{ + + + return nil, .Out_Of_Memory +} + diff --git a/code/grime_pool_allocator.odin b/code/grime_pool_allocator.odin new file mode 100644 index 0000000..f9261c8 --- /dev/null +++ b/code/grime_pool_allocator.odin @@ -0,0 +1,45 @@ +/* +This is a pool allocator setup to grow incrementally via buckets. +Buckets are stored in singly-linked lists so that they be allocated non-contiguous +*/ +package sectr + +Pool :: struct { + using header : ^PoolHeader, +} + +PoolHeader :: struct { + backing : Allocator, + + block_size : uint, + bucket_capacity : uint, + out_band_size : uint, + alignment : uint, + + free_list : LL_Node( [^]byte), + buckets : LL_Node( [^]byte), + current_bucket : ^byte, +} + +PoolBucket :: struct ( $ Type : typeid) { + blocks : [^]Type, +} + +pool_allocator :: proc ( using self : Pool ) -> (allocator : Allocator) { + allocator.procedure = pool_allocator_proc + allocator.data = self.header + return +} + +pool_allocator_proc :: proc( + allocator_data : rawptr, + mode : AllocatorMode, + size : int, + alignment : int, + old_memory : rawptr, + old_size : int, + loc := #caller_location +) -> ([]byte, AllocatorError) +{ + return nil, AllocatorError.Mode_Not_Implemented +} diff --git a/code/grime_slab_allocator.odin b/code/grime_slab_allocator.odin index 5b859ca..a5ba9ef 100644 --- a/code/grime_slab_allocator.odin +++ b/code/grime_slab_allocator.odin @@ -1,5 +1,63 @@ /* Slab Allocator +These are a collection of pool allocators serving as a general way +to allocate a large amount of dynamic sized data. +The usual use case for this is an arena, stack, +or dedicated pool allocator fail to be enough to handle a data structure +that either is too random with its size (ex: strings) +or is intended to grow an abitrary degree with an unknown upper bound (dynamic arrays, and hashtables). + +The protototype will use slab allocators for two purposes: +* String interning +* General purpose set for handling large arrays & hash tables within some underlying arena or stack. + +Technically speaking the general purpose situations can instead be grown on demand +with a dedicated segement of vmem, however this might be overkill +if the worst case buckets allocated are < 500 mb for most app usage. + +The slab allocators are expected to hold growable pool allocators, +where each pool stores a 'bucket' of fixed-sized blocks of memory. +When a pools bucket is full it will request another bucket from its arena +for permanent usage within the arena's lifetime. + +A freelist is tracked for free-blocks for each pool (provided by the underlying pool allocator) + +A slab starts out with pools initialized with no buckets and grows as needed. +When a slab is initialized the slab policy is provided to know how many size-classes there should be +which each contain the ratio of bucket to block size. + +Strings Slab pool size-classes (bucket:block ratio) are as follows: +16 mb : 64 b = 262,144 blocks +8 mb : 128 b = 8192 blocks +8 mb : 256 b = 8192 blocks +8 mb : 1 kb = 8192 blocks +16 mb : 4 kb = 4096 blocks +32 mb : 16 kb = 4096 blocks +32 mb : 32 kb = 4096 blocks +256 mb : 64 kb = +512 mb : 128 kb = */ package sectr +SlabSizeClass :: struct { + bucket : uint, + block : uint, +} + +Slab_Max_Size_Classes :: 32 + +SlabPolicy :: [Slab_Max_Size_Classes]SlabSizeClass + +SlabHeader :: struct { + policy : SlabPolicy, + pools : [Slab_Max_Size_Classes]Pool, +} + +Slab :: struct { + using header : SlabHeader, +} + +slab_init_reserve :: proc( ) -> ( Slab ) +{ + return {} +} diff --git a/code/grime_string_interning.odin b/code/grime_string_interning.odin new file mode 100644 index 0000000..2432dbc --- /dev/null +++ b/code/grime_string_interning.odin @@ -0,0 +1 @@ +package sectr