diff --git a/ring/ring.odin b/ring/ring.odin index 382e408..e897d96 100644 --- a/ring/ring.odin +++ b/ring/ring.odin @@ -16,11 +16,17 @@ Ring_Soa :: struct($E: typeid) { next_write_index, len: int, } -destroy_aos :: #force_inline proc(ring: ^Ring($E)) -> runtime.Allocator_Error { +destroy_aos :: #force_inline proc( + ring: ^Ring($E), + allocator := context.allocator, +) -> runtime.Allocator_Error { return delete(ring.data) } -destroy_soa :: #force_inline proc(ring: ^Ring_Soa($E)) -> runtime.Allocator_Error { +destroy_soa :: #force_inline proc( + ring: ^Ring_Soa($E), + allocator := context.allocator, +) -> runtime.Allocator_Error { return delete(ring.data) } @@ -53,6 +59,7 @@ create_soa :: #force_inline proc( return ring, err } +// All contents of `data` will be completely ignored, `data` is treated as an empty slice. init_from_slice_aos :: #force_inline proc(ring: ^Ring($E), data: $T/[]E) { ring.data = data ring.len = 0 @@ -60,6 +67,7 @@ init_from_slice_aos :: #force_inline proc(ring: ^Ring($E), data: $T/[]E) { return } +// All contents of `data` will be completely ignored, `data` is treated as an empty slice. init_from_slice_soa :: #force_inline proc(ring: ^Ring_Soa($E), data: $T/#soa[]E) { ring.data = data ring.len = 0 @@ -291,3 +299,141 @@ test_ring_soa :: proc(t: ^testing.T) { testing.expect_value(t, ring.len, 1) testing.expect_value(t, get(ring, 0), Ints{1, 1}) } + +@(test) +test_ring_aos_init_from_slice :: proc(t: ^testing.T) { + // Stack-allocated backing with pre-existing garbage and odd capacity. + backing: [7]int = {99, 99, 99, 99, 99, 99, 99} + + ring: Ring(int) + init_from_slice(&ring, backing[:]) + + // Empty ring invariants after init_from_slice. + testing.expect_value(t, ring.len, 0) + testing.expect_value(t, ring.next_write_index, 0) + testing.expect_value(t, start_index_aos(ring), 0) + + // Partial fill (3 / 7). + for i in 1 ..= 3 do append(&ring, i) + testing.expect_value(t, ring.len, 3) + testing.expect_value(t, ring.next_write_index, 3) + testing.expect_value(t, start_index_aos(ring), 0) + testing.expect_value(t, get(ring, 0)^, 1) + testing.expect_value(t, get(ring, 2)^, 3) + testing.expect_value(t, get_last(ring)^, 3) + + // Fill exactly to capacity. Pushing element 7 must make len == cap + // AND wrap next_write_index from 6 back to 0 in the same step. + for i in 4 ..= 7 do append(&ring, i) + testing.expect_value(t, ring.len, 7) + testing.expect_value(t, ring.next_write_index, 0) + testing.expect_value(t, start_index_aos(ring), 0) + testing.expect_value(t, get(ring, 0)^, 1) + testing.expect_value(t, get(ring, 6)^, 7) + testing.expect_value(t, get_last(ring)^, 7) + + // First overwrite — oldest element shifts by one. + append(&ring, 8) + testing.expect_value(t, ring.len, 7) + testing.expect_value(t, ring.next_write_index, 1) + testing.expect_value(t, start_index_aos(ring), 1) + testing.expect_value(t, get(ring, 0)^, 2) + testing.expect_value(t, get(ring, 6)^, 8) + testing.expect_value(t, get_last(ring)^, 8) + + // Stress: 3 more complete wrap cycles (21 more pushes). + // After 29 total pushes, ring contains the last 7 (23..=29), + // and next_write_index = 29 mod 7 = 1. + for i in 9 ..= 29 do append(&ring, i) + testing.expect_value(t, ring.len, 7) + testing.expect_value(t, ring.next_write_index, 1) + testing.expect_value(t, start_index_aos(ring), 1) + testing.expect_value(t, get(ring, 0)^, 23) + testing.expect_value(t, get(ring, 3)^, 26) + testing.expect_value(t, get(ring, 6)^, 29) + testing.expect_value(t, get_last(ring)^, 29) + + // Clear returns ring to empty-equivalent state. + clear(&ring) + testing.expect_value(t, ring.len, 0) + testing.expect_value(t, ring.next_write_index, 0) + testing.expect_value(t, start_index_aos(ring), 0) + + // Single-element edge case: get_last(len==1) routes through get(ring, 0). + append(&ring, 42) + testing.expect_value(t, ring.len, 1) + testing.expect_value(t, ring.next_write_index, 1) + testing.expect_value(t, get(ring, 0)^, 42) + testing.expect_value(t, get_last(ring)^, 42) +} + +@(test) +test_ring_soa_init_from_slice :: proc(t: ^testing.T) { + Ints :: struct { + x, y: int, + } + + // Stack-allocated backing with pre-existing garbage and odd capacity. + backing: #soa[7]Ints = {{99, 99}, {99, 99}, {99, 99}, {99, 99}, {99, 99}, {99, 99}, {99, 99}} + + ring: Ring_Soa(Ints) + init_from_slice(&ring, backing[:]) + + // Empty ring invariants after init_from_slice. + testing.expect_value(t, ring.len, 0) + testing.expect_value(t, ring.next_write_index, 0) + testing.expect_value(t, start_index_soa(ring), 0) + + // Partial fill (3 / 7). + for i in 1 ..= 3 do append(&ring, Ints{i, i}) + testing.expect_value(t, ring.len, 3) + testing.expect_value(t, ring.next_write_index, 3) + testing.expect_value(t, start_index_soa(ring), 0) + testing.expect_value(t, get(ring, 0), Ints{1, 1}) + testing.expect_value(t, get(ring, 2), Ints{3, 3}) + testing.expect_value(t, get_last(ring), Ints{3, 3}) + + // Fill exactly to capacity. Pushing element 7 must make len == cap + // AND wrap next_write_index from 6 back to 0 in the same step. + for i in 4 ..= 7 do append(&ring, Ints{i, i}) + testing.expect_value(t, ring.len, 7) + testing.expect_value(t, ring.next_write_index, 0) + testing.expect_value(t, start_index_soa(ring), 0) + testing.expect_value(t, get(ring, 0), Ints{1, 1}) + testing.expect_value(t, get(ring, 6), Ints{7, 7}) + testing.expect_value(t, get_last(ring), Ints{7, 7}) + + // First overwrite — oldest element shifts by one. + append(&ring, Ints{8, 8}) + testing.expect_value(t, ring.len, 7) + testing.expect_value(t, ring.next_write_index, 1) + testing.expect_value(t, start_index_soa(ring), 1) + testing.expect_value(t, get(ring, 0), Ints{2, 2}) + testing.expect_value(t, get(ring, 6), Ints{8, 8}) + testing.expect_value(t, get_last(ring), Ints{8, 8}) + + // Stress: 3 more complete wrap cycles (21 more pushes). + // After 29 total pushes, ring contains the last 7 (23..=29), + // and next_write_index = 29 mod 7 = 1. + for i in 9 ..= 29 do append(&ring, Ints{i, i}) + testing.expect_value(t, ring.len, 7) + testing.expect_value(t, ring.next_write_index, 1) + testing.expect_value(t, start_index_soa(ring), 1) + testing.expect_value(t, get(ring, 0), Ints{23, 23}) + testing.expect_value(t, get(ring, 3), Ints{26, 26}) + testing.expect_value(t, get(ring, 6), Ints{29, 29}) + testing.expect_value(t, get_last(ring), Ints{29, 29}) + + // Clear returns ring to empty-equivalent state. + clear(&ring) + testing.expect_value(t, ring.len, 0) + testing.expect_value(t, ring.next_write_index, 0) + testing.expect_value(t, start_index_soa(ring), 0) + + // Single-element edge case: get_last(len==1) routes through get(ring, 0). + append(&ring, Ints{42, 42}) + testing.expect_value(t, ring.len, 1) + testing.expect_value(t, ring.next_write_index, 1) + testing.expect_value(t, get(ring, 0), Ints{42, 42}) + testing.expect_value(t, get_last(ring), Ints{42, 42}) +}