aboutsummaryrefslogtreecommitdiff
path: root/core/container
diff options
context:
space:
mode:
authorgingerBill <gingerBill@users.noreply.github.com>2025-12-12 01:44:31 +0000
committergingerBill <gingerBill@users.noreply.github.com>2025-12-12 01:44:31 +0000
commitd43b00bb10f070a66fff6578cb6abd296e50acaa (patch)
treeddec5b3629b465641a5a026cdbc53bc4fafd13c1 /core/container
parent23ddb8dd3f7fb1ed1cd6b1cd11aacdc9cdaccd7d (diff)
Add basic docs
Diffstat (limited to 'core/container')
-rw-r--r--core/container/xar/xar.odin87
1 files changed, 67 insertions, 20 deletions
diff --git a/core/container/xar/xar.odin b/core/container/xar/xar.odin
index 29a8c3e00..e10033d3e 100644
--- a/core/container/xar/xar.odin
+++ b/core/container/xar/xar.odin
@@ -1,17 +1,8 @@
/*
Exponential Array (Xar).
- A fixed inline array of multi-pointers to exponentially growing chunks.
-
- Xar(T, SHIFT)
-
-
- Size of chunks:
- len(chunks[0]) == 1<<(SHIFT+0)
- len(chunks[1]) == 1<<(SHIFT+0)
- len(chunks[2]) == 1<<(SHIFT+1)
- len(chunks[3]) == 1<<(SHIFT+2)
-
+ A fixed inline array of multi-pointers to exponentially growing chunks,
+ allowing for stable memory addresses for elements.
For more information: https://azmr.uk/dyn/#exponential-arrayxar
*/
@@ -26,6 +17,18 @@ _LOG2_PLATFORM_BITS :: intrinsics.constant_log2(PLATFORM_BITS)
MAX_SHIFT :: PLATFORM_BITS>>1
+/*
+ An Exponential Array (Xar) is a fixed inline array of multi-pointers to exponentially growing chunks,
+ allowing for stable memory addresses for elements.
+
+ The chunk length uses as many chunks as much as addressable memory the machine provides.
+
+ Size of chunks:
+ len(chunks[0]) == 1<<(SHIFT+0)
+ len(chunks[1]) == 1<<(SHIFT+0)
+ len(chunks[2]) == 1<<(SHIFT+1)
+ len(chunks[3]) == 1<<(SHIFT+2)
+*/
Xar :: struct($T: typeid, $SHIFT: uint) where 0 < SHIFT, SHIFT <= MAX_SHIFT {
chunks: [(1 << (_LOG2_PLATFORM_BITS - intrinsics.constant_log2(SHIFT))) + 1][^]T,
len: int,
@@ -47,12 +50,30 @@ destroy :: proc(x: ^$X/Xar($T, $SHIFT)) {
x^ = {}
}
+// Resets the array's length to zero.
clear :: proc(x: $X/Xar($T, $SHIFT)) {
x.len = 0
}
+// Returns the length of the exponential-array
+@(require_results)
+len :: proc(x: $X/Xar($T, $SHIFT)) -> int {
+ return x.len
+}
+
+// Returns the number of allocated elements
+@(require_results)
+cap :: proc(x: $X/Xar($T, $SHIFT)) -> int {
+ #reverse for c, i in x.chunks {
+ if c != nil {
+ return 1 << (SHIFT + uint(i if i > 0 else 1))
+ }
+ }
+ return 0
+}
+
@(require_results)
-meta_get :: #force_inline proc($SHIFT: uint, index: uint) -> (chunk_idx, elem_idx, chunk_cap: uint) {
+_meta_get :: #force_inline proc($SHIFT: uint, index: uint) -> (chunk_idx, elem_idx, chunk_cap: uint) {
elem_idx = index
chunk_cap = uint(1) << SHIFT
chunk_idx = 0
@@ -71,31 +92,40 @@ meta_get :: #force_inline proc($SHIFT: uint, index: uint) -> (chunk_idx, elem_id
return
}
+// Gets the element at the index
@(require_results)
get :: proc(x: ^$X/Xar($T, $SHIFT), #any_int index: int, loc := #caller_location) -> (val: T) #no_bounds_check {
runtime.bounds_check_error_loc(loc, index, x.len)
- chunk_idx, elem_idx, _ := meta_get(SHIFT, uint(index))
+ chunk_idx, elem_idx, _ := _meta_get(SHIFT, uint(index))
return x.chunks[chunk_idx][elem_idx]
}
+// Gets the pointer of the element at the index
@(require_results)
get_ptr :: proc(x: ^$X/Xar($T, $SHIFT), #any_int index: int, loc := #caller_location) -> (val: ^T) #no_bounds_check {
runtime.bounds_check_error_loc(loc, index, x.len)
- chunk_idx, elem_idx, _ := meta_get(SHIFT, uint(index))
+ chunk_idx, elem_idx, _ := _meta_get(SHIFT, uint(index))
return &x.chunks[chunk_idx][elem_idx]
}
+// Sets the value at the index
set :: proc(x: ^$X/Xar($T, $SHIFT), #any_int index: int, value: T, loc := #caller_location) #no_bounds_check {
runtime.bounds_check_error_loc(loc, index, x.len)
- chunk_idx, elem_idx, _ := meta_get(SHIFT, uint(index))
+ chunk_idx, elem_idx, _ := _meta_get(SHIFT, uint(index))
x.chunks[chunk_idx][elem_idx] = value
}
append :: proc{push_back_elem, push_back_elems}
push_back :: proc{push_back_elem, push_back_elems}
+// `push_back_elem` pushes back (appends) an element to an exponential array
push_back_elem :: proc(x: ^$X/Xar($T, $SHIFT), value: T, loc := #caller_location) -> (n: int, err: mem.Allocator_Error) {
- chunk_idx, elem_idx, chunk_cap := meta_get(SHIFT, uint(x.len))
+ if x.allocator.procedure == nil {
+ // to minic `[dynamic]T` behaviour
+ x.allocator = context.allocator
+ }
+
+ chunk_idx, elem_idx, chunk_cap := _meta_get(SHIFT, uint(x.len))
if x.chunks[chunk_idx] == nil {
x.chunks[chunk_idx] = make([^]T, chunk_cap, x.allocator) or_return
}
@@ -105,6 +135,7 @@ push_back_elem :: proc(x: ^$X/Xar($T, $SHIFT), value: T, loc := #caller_location
return
}
+// `push_back_elems` pushes back (appends) multiple elements to an exponential array
push_back_elems :: proc(x: ^$X/Xar($T, $SHIFT), values: ..T, loc := #caller_location) -> (n: int, err: mem.Allocator_Error) {
for value in values {
n += push_back_elem(x, value, loc) or_return
@@ -114,9 +145,15 @@ push_back_elems :: proc(x: ^$X/Xar($T, $SHIFT), values: ..T, loc := #caller_loca
append_and_get_ptr :: push_back_elem_and_get_ptr
+// `push_back_elem` pushes back (appends) an element to an exponential array and returns its pointer
@(require_results)
push_back_elem_and_get_ptr :: proc(x: ^$X/Xar($T, $SHIFT), value: T, loc := #caller_location) -> (ptr: ^T, err: mem.Allocator_Error) {
- chunk_idx, elem_idx, chunk_cap := meta_get(SHIFT, uint(x.len))
+ if x.allocator.procedure == nil {
+ // to minic `[dynamic]T` behaviour
+ x.allocator = context.allocator
+ }
+
+ chunk_idx, elem_idx, chunk_cap := _meta_get(SHIFT, uint(x.len))
if x.chunks[chunk_idx] == nil {
x.chunks[chunk_idx] = make([^]T, chunk_cap, x.allocator) or_return
}
@@ -127,22 +164,26 @@ push_back_elem_and_get_ptr :: proc(x: ^$X/Xar($T, $SHIFT), value: T, loc := #cal
return
}
-
+// `pop` 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.
pop :: proc(x: ^$X/Xar($T, $SHIFT), loc := #caller_location) -> (val: T) {
assert(x.len > 0, loc=loc)
index := uint(x.len-1)
- chunk_idx, elem_idx, _ := meta_get(SHIFT, index)
+ chunk_idx, elem_idx, _ := _meta_get(SHIFT, index)
x.len -= 1
return x.chunks[chunk_idx][elem_idx]
}
+// `pop_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.
@(require_results)
pop_safe :: proc(x: ^$X/Xar($T, $SHIFT)) -> (val: T, ok: bool) {
if x.len == 0 {
return
}
index := uint(x.len-1)
- chunk_idx, elem_idx, _ := meta_get(SHIFT, index)
+ chunk_idx, elem_idx, _ := _meta_get(SHIFT, index)
x.len -= 1
val = x.chunks[chunk_idx][elem_idx]
@@ -150,6 +191,12 @@ pop_safe :: proc(x: ^$X/Xar($T, $SHIFT)) -> (val: T, ok: bool) {
return
}
+// `unordered_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.
unordered_remove :: proc(x: ^$X/Xar($T, $SHIFT), #any_int index: int, loc := #caller_location) {
runtime.bounds_check_error_loc(loc, index, x.len)
n := x.len-1