Improved consistency with naming of init / create / destroy and when to propagate allocation errors and #18
+34
-28
@@ -2,6 +2,7 @@ package many_bits
|
||||
|
||||
import "base:builtin"
|
||||
import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
import "core:fmt"
|
||||
import "core:slice"
|
||||
|
||||
@@ -25,15 +26,20 @@ Bits :: struct {
|
||||
length: int, // Total number of bits being stored
|
||||
}
|
||||
|
||||
delete :: proc(bits: Bits, allocator := context.allocator) {
|
||||
delete_slice(bits.int_array, allocator)
|
||||
destroy :: proc(bits: Bits, allocator := context.allocator) -> runtime.Allocator_Error {
|
||||
return delete_slice(bits.int_array, allocator)
|
||||
}
|
||||
|
||||
make :: proc(#any_int length: int, allocator := context.allocator) -> Bits {
|
||||
return Bits {
|
||||
int_array = make_slice([]Int_Bits, ((length - 1) >> INDEX_SHIFT) + 1, allocator),
|
||||
length = length,
|
||||
}
|
||||
create :: proc(
|
||||
#any_int length: int,
|
||||
allocator := context.allocator,
|
||||
) -> (
|
||||
bits: Bits,
|
||||
err: runtime.Allocator_Error,
|
||||
) #optional_allocator_error {
|
||||
bits.int_array, err = make_slice([]Int_Bits, ((length - 1) >> INDEX_SHIFT) + 1, allocator)
|
||||
bits.length = length
|
||||
return bits, err
|
||||
}
|
||||
|
||||
// Sets all bits to 0 (false)
|
||||
@@ -507,8 +513,8 @@ import "core:testing"
|
||||
|
||||
@(test)
|
||||
test_set :: proc(t: ^testing.T) {
|
||||
bits := make(128)
|
||||
defer delete(bits)
|
||||
bits := create(128)
|
||||
defer destroy(bits)
|
||||
|
||||
set(bits, 0, true)
|
||||
testing.expect_value(t, bits.int_array[0], Int_Bits{0})
|
||||
@@ -524,8 +530,8 @@ test_set :: proc(t: ^testing.T) {
|
||||
|
||||
@(test)
|
||||
test_get :: proc(t: ^testing.T) {
|
||||
bits := make(128)
|
||||
defer delete(bits)
|
||||
bits := create(128)
|
||||
defer destroy(bits)
|
||||
|
||||
// Default is false
|
||||
testing.expect(t, !get(bits, 0))
|
||||
@@ -560,8 +566,8 @@ test_get :: proc(t: ^testing.T) {
|
||||
|
||||
@(test)
|
||||
test_set_true_set_false :: proc(t: ^testing.T) {
|
||||
bits := make(128)
|
||||
defer delete(bits)
|
||||
bits := create(128)
|
||||
defer destroy(bits)
|
||||
|
||||
// set_true within first uint
|
||||
set_true(bits, 0)
|
||||
@@ -605,8 +611,8 @@ all_true_test :: proc(t: ^testing.T) {
|
||||
uint_max := UINT_MAX
|
||||
all_ones := transmute(Int_Bits)uint_max
|
||||
|
||||
bits := make(132)
|
||||
defer delete(bits)
|
||||
bits := create(132)
|
||||
defer destroy(bits)
|
||||
|
||||
bits.int_array[0] = all_ones
|
||||
bits.int_array[1] = all_ones
|
||||
@@ -616,8 +622,8 @@ all_true_test :: proc(t: ^testing.T) {
|
||||
bits.int_array[2] = {0, 1, 2}
|
||||
testing.expect(t, !all_true(bits))
|
||||
|
||||
bits2 := make(1)
|
||||
defer delete(bits2)
|
||||
bits2 := create(1)
|
||||
defer destroy(bits2)
|
||||
|
||||
bits2.int_array[0] = {0}
|
||||
testing.expect(t, all_true(bits2))
|
||||
@@ -628,8 +634,8 @@ test_range_true :: proc(t: ^testing.T) {
|
||||
uint_max := UINT_MAX
|
||||
all_ones := transmute(Int_Bits)uint_max
|
||||
|
||||
bits := make(192)
|
||||
defer delete(bits)
|
||||
bits := create(192)
|
||||
defer destroy(bits)
|
||||
|
||||
// Empty range is vacuously true
|
||||
testing.expect(t, range_true(bits, 0, 0))
|
||||
@@ -676,7 +682,7 @@ test_range_true :: proc(t: ^testing.T) {
|
||||
|
||||
@(test)
|
||||
nearest_true_handles_same_word_and_boundaries :: proc(t: ^testing.T) {
|
||||
bits := make(128, context.temp_allocator)
|
||||
bits := create(128, context.temp_allocator)
|
||||
|
||||
set_true(bits, 0)
|
||||
set_true(bits, 10)
|
||||
@@ -710,7 +716,7 @@ nearest_true_handles_same_word_and_boundaries :: proc(t: ^testing.T) {
|
||||
|
||||
@(test)
|
||||
nearest_false_handles_same_word_and_boundaries :: proc(t: ^testing.T) {
|
||||
bits := make(128, context.temp_allocator)
|
||||
bits := create(128, context.temp_allocator)
|
||||
|
||||
// Start with all bits true, then clear a few to false.
|
||||
for i := 0; i < bits.length; i += 1 {
|
||||
@@ -749,7 +755,7 @@ nearest_false_handles_same_word_and_boundaries :: proc(t: ^testing.T) {
|
||||
|
||||
@(test)
|
||||
nearest_false_scans_across_words_and_returns_false_when_all_true :: proc(t: ^testing.T) {
|
||||
bits := make(192, context.temp_allocator)
|
||||
bits := create(192, context.temp_allocator)
|
||||
|
||||
// Start with all bits true, then clear a couple far apart.
|
||||
for i := 0; i < bits.length; i += 1 {
|
||||
@@ -773,7 +779,7 @@ nearest_false_scans_across_words_and_returns_false_when_all_true :: proc(t: ^tes
|
||||
|
||||
@(test)
|
||||
nearest_true_scans_across_words_and_returns_false_when_empty :: proc(t: ^testing.T) {
|
||||
bits := make(192, context.temp_allocator)
|
||||
bits := create(192, context.temp_allocator)
|
||||
|
||||
set_true(bits, 5)
|
||||
set_true(bits, 130)
|
||||
@@ -790,7 +796,7 @@ nearest_true_scans_across_words_and_returns_false_when_empty :: proc(t: ^testing
|
||||
|
||||
@(test)
|
||||
nearest_false_handles_last_word_partial_length :: proc(t: ^testing.T) {
|
||||
bits := make(130, context.temp_allocator)
|
||||
bits := create(130, context.temp_allocator)
|
||||
|
||||
// Start with all bits true, then clear the first and last valid bits.
|
||||
for i := 0; i < bits.length; i += 1 {
|
||||
@@ -811,7 +817,7 @@ nearest_false_handles_last_word_partial_length :: proc(t: ^testing.T) {
|
||||
|
||||
@(test)
|
||||
nearest_true_handles_last_word_partial_length :: proc(t: ^testing.T) {
|
||||
bits := make(130, context.temp_allocator)
|
||||
bits := create(130, context.temp_allocator)
|
||||
|
||||
set_true(bits, 0)
|
||||
set_true(bits, 129)
|
||||
@@ -828,7 +834,7 @@ nearest_true_handles_last_word_partial_length :: proc(t: ^testing.T) {
|
||||
@(test)
|
||||
iterator_basic_mixed_bits :: proc(t: ^testing.T) {
|
||||
// Use non-word-aligned length to test partial last word handling
|
||||
bits := make(100, context.temp_allocator)
|
||||
bits := create(100, context.temp_allocator)
|
||||
|
||||
// Set specific bits: 0, 3, 64, 99 (last valid index)
|
||||
set_true(bits, 0)
|
||||
@@ -903,7 +909,7 @@ iterator_basic_mixed_bits :: proc(t: ^testing.T) {
|
||||
@(test)
|
||||
iterator_all_false_bits :: proc(t: ^testing.T) {
|
||||
// Use non-word-aligned length
|
||||
bits := make(100, context.temp_allocator)
|
||||
bits := create(100, context.temp_allocator)
|
||||
// All bits default to false, no need to set anything
|
||||
|
||||
// Test iterate - should return all 100 bits as false
|
||||
@@ -944,7 +950,7 @@ iterator_all_false_bits :: proc(t: ^testing.T) {
|
||||
@(test)
|
||||
iterator_all_true_bits :: proc(t: ^testing.T) {
|
||||
// Use non-word-aligned length
|
||||
bits := make(100, context.temp_allocator)
|
||||
bits := create(100, context.temp_allocator)
|
||||
// Set all bits to true
|
||||
for i := 0; i < bits.length; i += 1 {
|
||||
set_true(bits, i)
|
||||
|
||||
+97
-57
@@ -1,35 +1,81 @@
|
||||
package ring
|
||||
|
||||
import "base:runtime"
|
||||
import "core:fmt"
|
||||
|
||||
@(private)
|
||||
ODIN_BOUNDS_CHECK :: !ODIN_NO_BOUNDS_CHECK
|
||||
|
||||
Ring :: struct($T: typeid) {
|
||||
data: []T,
|
||||
Ring :: struct($E: typeid) {
|
||||
data: []E,
|
||||
_end_index, len: int,
|
||||
}
|
||||
|
||||
Ring_Soa :: struct($T: typeid) {
|
||||
data: #soa[]T,
|
||||
Ring_Soa :: struct($E: typeid) {
|
||||
data: #soa[]E,
|
||||
_end_index, len: int,
|
||||
}
|
||||
|
||||
from_slice_raos :: #force_inline proc(data: $T/[]$E) -> Ring(E) {
|
||||
return {data = data, _end_index = -1}
|
||||
destroy_aos :: #force_inline proc(ring: ^Ring($E)) -> runtime.Allocator_Error {
|
||||
return delete(ring.data)
|
||||
}
|
||||
|
||||
from_slice_rsoa :: #force_inline proc(data: $T/#soa[]$E) -> Ring_Soa(E) {
|
||||
return {data = data, _end_index = -1}
|
||||
destroy_soa :: #force_inline proc(ring: ^Ring_Soa($E)) -> runtime.Allocator_Error {
|
||||
return delete(ring.data)
|
||||
}
|
||||
|
||||
from_slice :: proc {
|
||||
from_slice_raos,
|
||||
from_slice_rsoa,
|
||||
destroy :: proc {
|
||||
destroy_aos,
|
||||
destroy_soa,
|
||||
}
|
||||
|
||||
create_aos :: #force_inline proc(
|
||||
$E: typeid,
|
||||
capacity: int,
|
||||
allocator := context.allocator,
|
||||
) -> (
|
||||
ring: Ring(E),
|
||||
err: runtime.Allocator_Error,
|
||||
) #optional_allocator_error {
|
||||
ring.data, err = make([]E, capacity, allocator)
|
||||
ring._end_index = -1
|
||||
return ring, err
|
||||
}
|
||||
|
||||
create_soa :: #force_inline proc(
|
||||
$E: typeid,
|
||||
capacity: int,
|
||||
allocator := context.allocator,
|
||||
) -> (
|
||||
ring: Ring_Soa(E),
|
||||
err: runtime.Allocator_Error,
|
||||
) #optional_allocator_error {
|
||||
ring.data, err = make(#soa[]E, capacity, allocator)
|
||||
ring._end_index = -1
|
||||
return ring, err
|
||||
}
|
||||
|
||||
init_from_slice_aos :: #force_inline proc(ring: ^Ring($E), data: $T/[]E) {
|
||||
ring.data = data
|
||||
ring.len = 0
|
||||
ring._end_index = -1
|
||||
return
|
||||
}
|
||||
|
||||
init_from_slice_soa :: #force_inline proc(ring: ^Ring_Soa($E), data: $T/#soa[]E) {
|
||||
ring.data = data
|
||||
ring.len = 0
|
||||
ring._end_index = -1
|
||||
return
|
||||
}
|
||||
|
||||
init_from_slice :: proc {
|
||||
init_from_slice_aos,
|
||||
init_from_slice_soa,
|
||||
}
|
||||
|
||||
// Index in the backing array where the ring starts
|
||||
_start_index_raos :: proc(ring: Ring($T)) -> int {
|
||||
_start_index_aos :: #force_inline proc(ring: Ring($E)) -> int {
|
||||
if ring.len < len(ring.data) {
|
||||
return 0
|
||||
} else {
|
||||
@@ -39,7 +85,7 @@ _start_index_raos :: proc(ring: Ring($T)) -> int {
|
||||
}
|
||||
|
||||
// Index in the backing array where the ring starts
|
||||
_start_index_rsoa :: proc(ring: Ring_Soa($T)) -> int {
|
||||
_start_index_soa :: #force_inline proc(ring: Ring_Soa($E)) -> int {
|
||||
if ring.len < len(ring.data) {
|
||||
return 0
|
||||
} else {
|
||||
@@ -48,7 +94,7 @@ _start_index_rsoa :: proc(ring: Ring_Soa($T)) -> int {
|
||||
}
|
||||
}
|
||||
|
||||
advance_raos :: proc(ring: ^Ring($T)) {
|
||||
advance_aos :: #force_inline proc(ring: ^Ring($E)) {
|
||||
// Length
|
||||
if ring.len != len(ring.data) do ring.len += 1
|
||||
// End index
|
||||
@@ -59,7 +105,7 @@ advance_raos :: proc(ring: ^Ring($T)) {
|
||||
}
|
||||
}
|
||||
|
||||
advance_rsoa :: proc(ring: ^Ring_Soa($T)) {
|
||||
advance_soa :: #force_inline proc(ring: ^Ring_Soa($E)) {
|
||||
// Length
|
||||
if ring.len != len(ring.data) do ring.len += 1
|
||||
// End index
|
||||
@@ -71,33 +117,31 @@ advance_rsoa :: proc(ring: ^Ring_Soa($T)) {
|
||||
}
|
||||
|
||||
advance :: proc {
|
||||
advance_raos,
|
||||
advance_rsoa,
|
||||
advance_aos,
|
||||
advance_soa,
|
||||
}
|
||||
|
||||
append_raos :: proc(ring: ^Ring($T), element: T) {
|
||||
append_aos :: #force_inline proc(ring: ^Ring($E), element: E) {
|
||||
advance(ring)
|
||||
ring.data[ring._end_index] = element
|
||||
}
|
||||
|
||||
append_rsoa :: proc(ring: ^Ring_Soa($T), element: T) {
|
||||
append_soa :: #force_inline proc(ring: ^Ring_Soa($E), element: E) {
|
||||
advance(ring)
|
||||
ring.data[ring._end_index] = element
|
||||
}
|
||||
|
||||
append :: proc {
|
||||
append_raos,
|
||||
append_rsoa,
|
||||
append_aos,
|
||||
append_soa,
|
||||
}
|
||||
|
||||
get_raos :: proc(ring: Ring($T), index: int) -> ^T {
|
||||
get_aos :: #force_inline proc(ring: Ring($E), index: int) -> ^E {
|
||||
when ODIN_BOUNDS_CHECK {
|
||||
if index >= ring.len {
|
||||
panic(fmt.tprintf("Ring index %i out of bounds for length %i", index, ring.len))
|
||||
}
|
||||
fmt.assertf(index < ring.len, "Ring index %i out of bounds for length %i", index, ring.len)
|
||||
}
|
||||
|
||||
array_index := _start_index_raos(ring) + index
|
||||
array_index := _start_index_aos(ring) + index
|
||||
if array_index < len(ring.data) {
|
||||
return &ring.data[array_index]
|
||||
} else {
|
||||
@@ -107,14 +151,12 @@ get_raos :: proc(ring: Ring($T), index: int) -> ^T {
|
||||
}
|
||||
|
||||
// SOA can't return soa pointer to parapoly T.
|
||||
get_rsoa :: proc(ring: Ring_Soa($T), index: int) -> T {
|
||||
get_soa :: #force_inline proc(ring: Ring_Soa($E), index: int) -> E {
|
||||
when ODIN_BOUNDS_CHECK {
|
||||
if index >= ring.len {
|
||||
panic(fmt.tprintf("Ring index %i out of bounds for length %i", index, ring.len))
|
||||
}
|
||||
fmt.assertf(index < ring.len, "Ring index %i out of bounds for length %i", index, ring.len)
|
||||
}
|
||||
|
||||
array_index := _start_index_rsoa(ring) + index
|
||||
array_index := _start_index_soa(ring) + index
|
||||
if array_index < len(ring.data) {
|
||||
return ring.data[array_index]
|
||||
} else {
|
||||
@@ -124,36 +166,36 @@ get_rsoa :: proc(ring: Ring_Soa($T), index: int) -> T {
|
||||
}
|
||||
|
||||
get :: proc {
|
||||
get_raos,
|
||||
get_rsoa,
|
||||
get_aos,
|
||||
get_soa,
|
||||
}
|
||||
|
||||
get_last_raos :: #force_inline proc(ring: Ring($T)) -> ^T {
|
||||
get_last_aos :: #force_inline proc(ring: Ring($E)) -> ^E {
|
||||
return get(ring, ring.len - 1)
|
||||
}
|
||||
|
||||
get_last_rsoa :: #force_inline proc(ring: Ring_Soa($T)) -> T {
|
||||
get_last_soa :: #force_inline proc(ring: Ring_Soa($E)) -> E {
|
||||
return get(ring, ring.len - 1)
|
||||
}
|
||||
|
||||
get_last :: proc {
|
||||
get_last_raos,
|
||||
get_last_rsoa,
|
||||
get_last_aos,
|
||||
get_last_soa,
|
||||
}
|
||||
|
||||
clear_raos :: #force_inline proc "contextless" (ring: ^Ring($T)) {
|
||||
clear_aos :: #force_inline proc "contextless" (ring: ^Ring($E)) {
|
||||
ring.len = 0
|
||||
ring._end_index = -1
|
||||
}
|
||||
|
||||
clear_rsoa :: #force_inline proc "contextless" (ring: ^Ring_Soa($T)) {
|
||||
clear_soa :: #force_inline proc "contextless" (ring: ^Ring_Soa($E)) {
|
||||
ring.len = 0
|
||||
ring._end_index = -1
|
||||
}
|
||||
|
||||
clear :: proc {
|
||||
clear_raos,
|
||||
clear_rsoa,
|
||||
clear_aos,
|
||||
clear_soa,
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
@@ -164,14 +206,13 @@ import "core:testing"
|
||||
|
||||
@(test)
|
||||
test_ring_aos :: proc(t: ^testing.T) {
|
||||
data := make_slice([]int, 10)
|
||||
ring := from_slice(data)
|
||||
defer delete(ring.data)
|
||||
ring := create_aos(int, 10)
|
||||
defer destroy(&ring)
|
||||
|
||||
for i in 1 ..= 5 {
|
||||
append(&ring, i)
|
||||
log.debug("Length:", ring.len)
|
||||
log.debug("Start index:", _start_index_raos(ring))
|
||||
log.debug("Start index:", _start_index_aos(ring))
|
||||
log.debug("End index:", ring._end_index)
|
||||
log.debug(ring.data)
|
||||
}
|
||||
@@ -179,12 +220,12 @@ test_ring_aos :: proc(t: ^testing.T) {
|
||||
testing.expect_value(t, get(ring, 4)^, 5)
|
||||
testing.expect_value(t, ring.len, 5)
|
||||
testing.expect_value(t, ring._end_index, 4)
|
||||
testing.expect_value(t, _start_index_raos(ring), 0)
|
||||
testing.expect_value(t, _start_index_aos(ring), 0)
|
||||
|
||||
for i in 6 ..= 15 {
|
||||
append(&ring, i)
|
||||
log.debug("Length:", ring.len)
|
||||
log.debug("Start index:", _start_index_raos(ring))
|
||||
log.debug("Start index:", _start_index_aos(ring))
|
||||
log.debug("End index:", ring._end_index)
|
||||
log.debug(ring.data)
|
||||
}
|
||||
@@ -194,12 +235,12 @@ test_ring_aos :: proc(t: ^testing.T) {
|
||||
testing.expect_value(t, get_last(ring)^, 15)
|
||||
testing.expect_value(t, ring.len, 10)
|
||||
testing.expect_value(t, ring._end_index, 4)
|
||||
testing.expect_value(t, _start_index_raos(ring), 5)
|
||||
testing.expect_value(t, _start_index_aos(ring), 5)
|
||||
|
||||
for i in 15 ..= 25 {
|
||||
append(&ring, i)
|
||||
log.debug("Length:", ring.len)
|
||||
log.debug("Start index:", _start_index_raos(ring))
|
||||
log.debug("Start index:", _start_index_aos(ring))
|
||||
log.debug("End index:", ring._end_index)
|
||||
log.debug(ring.data)
|
||||
}
|
||||
@@ -219,14 +260,13 @@ test_ring_soa :: proc(t: ^testing.T) {
|
||||
x, y: int,
|
||||
}
|
||||
|
||||
data := make_soa_slice(#soa[]Ints, 10)
|
||||
ring := from_slice(data)
|
||||
defer delete(ring.data)
|
||||
ring := create_soa(Ints, 10)
|
||||
defer destroy(&ring)
|
||||
|
||||
for i in 1 ..= 5 {
|
||||
append(&ring, Ints{i, i})
|
||||
log.debug("Length:", ring.len)
|
||||
log.debug("Start index:", _start_index_rsoa(ring))
|
||||
log.debug("Start index:", _start_index_soa(ring))
|
||||
log.debug("End index:", ring._end_index)
|
||||
log.debug(ring.data)
|
||||
}
|
||||
@@ -234,12 +274,12 @@ test_ring_soa :: proc(t: ^testing.T) {
|
||||
testing.expect_value(t, get(ring, 4), Ints{5, 5})
|
||||
testing.expect_value(t, ring.len, 5)
|
||||
testing.expect_value(t, ring._end_index, 4)
|
||||
testing.expect_value(t, _start_index_rsoa(ring), 0)
|
||||
testing.expect_value(t, _start_index_soa(ring), 0)
|
||||
|
||||
for i in 6 ..= 15 {
|
||||
append(&ring, Ints{i, i})
|
||||
log.debug("Length:", ring.len)
|
||||
log.debug("Start index:", _start_index_rsoa(ring))
|
||||
log.debug("Start index:", _start_index_soa(ring))
|
||||
log.debug("End index:", ring._end_index)
|
||||
log.debug(ring.data)
|
||||
}
|
||||
@@ -249,12 +289,12 @@ test_ring_soa :: proc(t: ^testing.T) {
|
||||
testing.expect_value(t, get_last(ring), Ints{15, 15})
|
||||
testing.expect_value(t, ring.len, 10)
|
||||
testing.expect_value(t, ring._end_index, 4)
|
||||
testing.expect_value(t, _start_index_rsoa(ring), 5)
|
||||
testing.expect_value(t, _start_index_soa(ring), 5)
|
||||
|
||||
for i in 15 ..= 25 {
|
||||
append(&ring, Ints{i, i})
|
||||
log.debug("Length:", ring.len)
|
||||
log.debug("Start index:", _start_index_rsoa(ring))
|
||||
log.debug("Start index:", _start_index_soa(ring))
|
||||
log.debug("End index:", ring._end_index)
|
||||
log.debug(ring.data)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user