core/container/xar
container_xar
Types
4Array
Array :: struct {}SourceAn Exponential Array with stable element addresses.
Unlike `[dynamic]T` which reallocates and moves elements when growing, `Array`
allocates separate chunks of exponentially increasing size. This guarantees
that pointers to elements remain valid for the lifetime of the container.
Fields:
- `chunks`: Fixed array of multi-pointers to allocated chunks
- `len`: Number of elements currently stored
- `allocator`: Allocator used for chunk allocations
Type Parameters:
- `T`: The element type
- `SHIFT`: Controls initial chunk size (1 << SHIFT). Must be in range (0, MAX_SHIFT].
Larger values mean fewer, bigger chunks. Recommended: 4-8.
Chunk sizes grow as:
- `chunks[0]`: 1 << SHIFT elements
- `chunks[1]`: 1 << SHIFT elements
- `chunks[2]`: 1 << (SHIFT + 1) elements
- `chunks[3]`: 1 << (SHIFT + 2) elements
- `chunks[4]`: 1 << (SHIFT + 3) elements
- ...and so on
Example:
import "core:container/xar"
example :: proc() {
// Xar with initial chunk size of 16 (1 << 4)
x: xar.Array(My_Struct, 4)
defer xar.destroy(&x)
}Array_Iterator
Array_Iterator :: struct {}SourceIterator state for traversing a Xar.
Fields:
xar: Pointer to the exponential array being iteratedidx: Current iteration index
Freelist_Array
Freelist_Array :: struct {}SourceFreelist_Iterator
Freelist_Iterator :: struct {}SourceConstants
3MAX_SHIFT
MAX_SHIFT :: PLATFORM_BITS>>1SourcePLATFORM_BITS
PLATFORM_BITS :: 8*size_of(uint)Source_LOG2_PLATFORM_BITS
_LOG2_PLATFORM_BITS :: _ = intrinsics.constant_log2(PLATFORM_BITS)SourceProcedures
39_meta_get
_meta_get :: proc(SHIFT: uint, index: uint) -> (chunk_idx: uint, elem_idx: uint, chunk_cap: uint)SourceInternal: computes chunk index, element index within chunk, and chunk capacity for a given index.
append_and_get_ptr
append_and_get_ptr :: proc(x: ^X, value: T, loc = #caller_location) -> (ptr: ^T, err: runtime.Allocator_Error)Sourcearray_append_and_get_ptr
array_append_and_get_ptr :: proc(x: ^X, value: T, loc = #caller_location) -> (ptr: ^T, err: runtime.Allocator_Error)Sourcearray_cap
array_cap :: proc(x: X) -> (int)SourceReturns the number of allocated elements
array_clear
array_clear :: proc(x: ^X)SourceResets the array's length to zero without freeing memory. Allocated chunks are retained for reuse.
array_destroy
array_destroy :: proc(x: ^X)SourceFrees all allocated chunks and resets the exponential array.
Inputs
x: Pointer to the exponential array to destroy
array_get
array_get :: proc(x: ^X, index: int, loc = #caller_location) -> (val: T)SourceGet a copy of the element at the specified index.
Inputs
x: Pointer to the exponential arrayindex: Position of the element (0-indexed)
Returns
- a copy of the element
array_get_ptr
array_get_ptr :: proc(x: ^X, index: int, loc = #caller_location) -> (val: ^T)SourceGet a pointer to the element at the specified index.
The returned pointer remains valid even after additional elements are added, as long as the element is not removed and the array is not destroyed.
Inputs
x: Pointer to the exponential arrayindex: Position of the element (0-indexed)
Returns
- a stable pointer to the element
Example:
import "core:container/xar"
get_ptr_example :: proc() {
x: xar.Array(int, 4)
defer xar.destroy(&x)
xar.push_back(&x, 100)
ptr := xar.get_ptr(&x, 0)
// Pointer remains valid after growing
for i in 0..<1000 {
xar.push_back(&x, i)
}
fmt.println(ptr^) // Still prints 100
}array_get_ptr_unsafe
array_get_ptr_unsafe :: proc(x: ^X, index: int) -> (val: ^T)SourceNo bounds checking
array_init
array_init :: proc(x: ^X, allocator: mem.Allocator = context.allocator)SourceInitializes an exponential array with the given allocator.
Inputs
x: Pointer to the exponential array to initializeallocator: Allocator to use for chunk allocations (defaults to context.allocator)
array_iterate_by_ptr
array_iterate_by_ptr :: proc(it: ^Array_Iterator($T, $SHIFT)) -> (val: ^T, idx: int, ok: bool)SourceAdvance the iterator and returns a pointer to the next element.
Inputs
it: Pointer to the iterator
Returns
- pointer to the current element
trueif an element was returned,falseif iteration is complete
array_iterate_by_val
array_iterate_by_val :: proc(it: ^Array_Iterator($T, $SHIFT)) -> (val: T, idx: int, ok: bool)SourceAdvance the iterator and returns the next element.
Inputs
it: Pointer to the iterator
Returns
- current element
trueif an element was returned,falseif iteration is complete
array_iterator
array_iterator :: proc(xar: ^X) -> (Array_Iterator(T, SHIFT))SourceCreate an iterator for traversing the exponential array.
Inputs
xar: Pointer to the exponential array
Returns
- an iterator positioned at the start
Example:
import "core:container/xar"
import "core:fmt"
iterator_example :: proc() {
x: xar.Array(int, 4)
defer xar.destroy(&x)
xar.push_back(&x, 10)
xar.push_back(&x, 20)
xar.push_back(&x, 30)
it := xar.iterator(&x)
for val in xar.iterate_by_ptr(&it) {
fmt.println(val^)
}
}Output:
10
20
30array_len
array_len :: proc(x: X) -> (int)SourceReturns the length of the exponential-array
array_linear_search
array_linear_search :: proc(x: ^X, elem: T) -> (index: int, found: bool)Sourcearray_pop
array_pop :: proc(x: ^X, loc = #caller_location) -> (val: T)Sourcepop will remove and return the end value of an exponential array x and reduces the length of the array by 1.
Note: If the exponential array has no elements (xar.len(x) == 0), this procedure will panic.
array_pop_safe
array_pop_safe :: proc(x: ^X) -> (val: T, ok: bool)Sourcepop_safe trys to remove and return the end value of dynamic array x and reduces the length of the array by 1. If the operation is not possible, it will return false.
array_push_back_elem
array_push_back_elem :: proc(x: ^X, value: T, loc = #caller_location) -> (n: int, err: runtime.Allocator_Error)SourceAppend an element to the end of the exponential array. Allocates a new chunk if necessary. Existing elements aren't moved, and their pointers remain stable.
Inputs
x: Pointer to the exponential arrayvalue: The element to append
Returns
- number of elements added (always 1 on success)
- allocation error if chunk allocation failed
Example:
import "core:container/xar"
push_back_example :: proc() {
x: xar.Array(string, 4)
defer xar.destroy(&x)
xar.push_back(&x, "hello")
xar.push_back(&x, "world")
fmt.println(xar.get(&x, 0)) // hello
fmt.println(xar.get(&x, 1)) // world
}array_push_back_elem_and_get_ptr
array_push_back_elem_and_get_ptr :: proc(x: ^X, value: T, loc = #caller_location) -> (ptr: ^T, err: runtime.Allocator_Error)SourceAppend an element and return a stable pointer to it. This is useful when you need to initialize a complex struct in-place or retain a reference to the newly added element.
Inputs
x: Pointer to the exponential arrayvalue: The element to append
Returns
- a stable pointer to the newly added element
- allocation error if chunk allocation failed
Example:
import "core:container/xar"
push_back_and_get_ptr_example :: proc() {
x: xar.Array(My_Struct, 4)
defer xar.destroy(&x)
ptr := xar.push_back_elem_and_get_ptr(&x, My_Struct{}) or_else panic("alloc failed")
ptr.field = 42 // Initialize in-place
}array_push_back_elems
array_push_back_elems :: proc(x: ^X, values, loc = #caller_location) -> (n: int, err: runtime.Allocator_Error)SourceAppend multiple elements to the end of the exponential array.
Inputs
x: Pointer to the exponential arrayvalues: The elements to append
Returns
- number of elements successfully added
- allocation error if chunk allocation failed (partial append possible)
array_set
array_set :: proc(x: ^X, index: int, value: T, loc = #caller_location)SourceSet the element at the specified index to the given value.
Inputs
x: Pointer to the exponential arrayindex: Position of the element (0-indexed)value: The value to set
array_unordered_remove
array_unordered_remove :: proc(x: ^X, index: int, loc = #caller_location)Sourceunordered_remove removed the element at the specified index. It does so by replacing the current end value
with the old value, and reducing the length of the exponential array by 1.
Note: This is an O(1) operation.
Note: This is currently no procedure that is the equivalent of an "ordered_remove"
Note: If the index is out of bounds, this procedure will panic.
Note: Pointers to the last element become invalid (it gets moved). Pointers to other elements remain valid.
Example:
import "core:container/xar"
unordered_remove_example :: proc() {
x: xar.Array(int, 4)
defer xar.destroy(&x)
xar.push_back(&x, 10)
xar.push_back(&x, 20)
xar.push_back(&x, 30)
xar.unordered_remove(&x, 0) // Removes 10, replaces with 30
// Array now contains [30, 20]
fmt.println(xar.get(&x, 0)) // 30
fmt.println(xar.get(&x, 1)) // 20
}freelist_cap
freelist_cap :: proc(x: X) -> (int)Sourcefreelist_clear
freelist_clear :: proc(x: ^X)Sourcefreelist_destroy
freelist_destroy :: proc(x: ^X)Sourcefreelist_get
freelist_get :: proc(x: ^X, index: int, loc = #caller_location) -> (T)Sourcefreelist_get_ptr
freelist_get_ptr :: proc(x: ^X, index: int, loc = #caller_location) -> (^T)Sourcefreelist_init
freelist_init :: proc(x: ^X, allocator: mem.Allocator = context.allocator)Sourcefreelist_is_freed
freelist_is_freed :: proc(x: ^X, index: int) -> (bool)Sourcefreelist_iterate_by_ptr
freelist_iterate_by_ptr :: proc(it: ^Freelist_Iterator($T, $SHIFT)) -> (val: ^T, idx: int, ok: bool)Sourcefreelist_iterate_by_val
freelist_iterate_by_val :: proc(it: ^Freelist_Iterator($T, $SHIFT)) -> (val: T, idx: int, ok: bool)Sourcefreelist_iterator
freelist_iterator :: proc(x: ^X) -> (Freelist_Iterator(T, SHIFT))Sourcefreelist_len
freelist_len :: proc(x: X) -> (int)Sourcefreelist_linear_search
freelist_linear_search :: proc(x: ^X, ptr: ^T) -> (index: int, found: bool)Sourcefreelist_pop
freelist_pop :: proc(x: ^X, index: int, loc = #caller_location) -> (T)Sourcefreelist_push
freelist_push :: proc(x: ^X, value: T, loc = #caller_location) -> (ptr: ^T, err: runtime.Allocator_Error)Sourcefreelist_push_with_index
freelist_push_with_index :: proc(x: ^X, value: T, loc = #caller_location) -> (ptr: ^T, index: int, err: runtime.Allocator_Error)Sourcefreelist_release
freelist_release :: proc(x: ^X, index: int, loc = #caller_location)Sourcefreelist_set
freelist_set :: proc(x: ^X, index: int, value: T, loc = #caller_location)SourceProcedure Groups
27append
append :: proc{array_push_back_elem, array_push_back_elems}Sourcearray_append
array_append :: proc{array_push_back_elem, array_push_back_elems}Sourcearray_push_back
array_push_back :: proc{array_push_back_elem, array_push_back_elems}Sourcecap
cap :: proc{array_cap, freelist_cap}Sourceclear
clear :: proc{array_clear, freelist_clear}Sourcedestroy
destroy :: proc{array_destroy, freelist_destroy}Sourceget
get :: proc{array_get, freelist_get}Sourceget_ptr
get_ptr :: proc{array_get_ptr, freelist_get_ptr}Sourceget_ptr_unsafe
get_ptr_unsafe :: proc{array_get_ptr_unsafe}Sourceinit
init :: proc{array_init, freelist_init}Sourceis_freed
is_freed :: proc{freelist_is_freed}Sourceiterate_by_ptr
iterate_by_ptr :: proc{array_iterate_by_ptr, freelist_iterate_by_ptr}Sourceiterate_by_val
iterate_by_val :: proc{array_iterate_by_val, freelist_iterate_by_val}Sourceiterator
iterator :: proc{array_iterator, freelist_iterator}Sourcelen
len :: proc{array_len, freelist_len}Sourcelinear_search
linear_search :: proc{array_linear_search, freelist_linear_search}Sourcepop
pop :: proc{array_pop, freelist_pop}Sourcepop_safe
pop_safe :: proc{array_pop_safe}Sourcepush
push :: proc{freelist_push}Sourcepush_back
push_back :: proc{array_push_back_elem, array_push_back_elems}Sourcepush_back_elem
push_back_elem :: proc{array_push_back_elem}Sourcepush_back_elem_and_get_ptr
push_back_elem_and_get_ptr :: proc{array_push_back_elem_and_get_ptr}Sourcepush_back_elems
push_back_elems :: proc{array_push_back_elems}Sourcepush_with_index
push_with_index :: proc{freelist_push_with_index}Sourcerelease
release :: proc{freelist_release}Sourceset
set :: proc{array_set, freelist_set}Sourceunordered_remove
unordered_remove :: proc{array_unordered_remove}Source