package ring import "core:fmt" @(private) ODIN_BOUNDS_CHECK :: !ODIN_NO_BOUNDS_CHECK Ring :: struct($T: typeid) { data: []T, _end_index, len: int, } Ring_Soa :: struct($T: typeid) { data: #soa[]T, _end_index, len: int, } from_slice_raos :: #force_inline proc(data: $T/[]$E) -> Ring(E) { return {data = data, _end_index = -1} } from_slice_rsoa :: #force_inline proc(data: $T/#soa[]$E) -> Ring_Soa(E) { return {data = data, _end_index = -1} } from_slice :: proc { from_slice_raos, from_slice_rsoa, } // Index in the backing array where the ring starts _start_index_raos :: proc(ring: Ring($T)) -> int { if ring.len < len(ring.data) { return 0 } else { start_index := ring._end_index + 1 return 0 if start_index == len(ring.data) else start_index } } // Index in the backing array where the ring starts _start_index_rsoa :: proc(ring: Ring_Soa($T)) -> int { if ring.len < len(ring.data) { return 0 } else { start_index := ring._end_index + 1 return 0 if start_index == len(ring.data) else start_index } } advance_raos :: proc(ring: ^Ring($T)) { // Length if ring.len != len(ring.data) do ring.len += 1 // End index if ring._end_index == len(ring.data) - 1 { // If we are at the end of the backing array ring._end_index = 0 // Overflow end to 0 } else { ring._end_index += 1 } } advance_rsoa :: proc(ring: ^Ring_Soa($T)) { // Length if ring.len != len(ring.data) do ring.len += 1 // End index if ring._end_index == len(ring.data) - 1 { // If we are at the end of the backing array ring._end_index = 0 // Overflow end to 0 } else { ring._end_index += 1 } } advance :: proc { advance_raos, advance_rsoa, } append_raos :: proc(ring: ^Ring($T), element: T) { advance(ring) ring.data[ring._end_index] = element } append_rsoa :: proc(ring: ^Ring_Soa($T), element: T) { advance(ring) ring.data[ring._end_index] = element } append :: proc { append_raos, append_rsoa, } get_raos :: proc(ring: Ring($T), index: int) -> ^T { when ODIN_BOUNDS_CHECK { if index >= ring.len { panic(fmt.tprintf("Ring index %i out of bounds for length %i", index, ring.len)) } } array_index := _start_index_raos(ring) + index if array_index < len(ring.data) { return &ring.data[array_index] } else { array_index = array_index - len(ring.data) return &ring.data[array_index] } } // SOA can't return soa pointer to parapoly T. get_rsoa :: proc(ring: Ring_Soa($T), index: int) -> T { when ODIN_BOUNDS_CHECK { if index >= ring.len { panic(fmt.tprintf("Ring index %i out of bounds for length %i", index, ring.len)) } } array_index := _start_index_rsoa(ring) + index if array_index < len(ring.data) { return ring.data[array_index] } else { array_index = array_index - len(ring.data) return ring.data[array_index] } } get :: proc { get_raos, get_rsoa, } get_last_raos :: #force_inline proc(ring: Ring($T)) -> ^T { return get(ring, ring.len - 1) } get_last_rsoa :: #force_inline proc(ring: Ring_Soa($T)) -> T { return get(ring, ring.len - 1) } get_last :: proc { get_last_raos, get_last_rsoa, } clear_raos :: #force_inline proc "contextless" (ring: ^Ring($T)) { ring.len = 0 ring._end_index = -1 } clear_rsoa :: #force_inline proc "contextless" (ring: ^Ring_Soa($T)) { ring.len = 0 ring._end_index = -1 } clear :: proc { clear_raos, clear_rsoa, } // --------------------------------------------------------------------------------------------------------------------- // ----- Tests ------------------------ // --------------------------------------------------------------------------------------------------------------------- import "core:log" import "core:testing" @(test) test_ring_aos :: proc(t: ^testing.T) { data := make_slice([]int, 10) ring := from_slice(data) defer delete(ring.data) for i in 1 ..= 5 { append(&ring, i) log.debug("Length:", ring.len) log.debug("Start index:", _start_index_raos(ring)) log.debug("End index:", ring._end_index) log.debug(ring.data) } testing.expect_value(t, get(ring, 0)^, 1) 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) for i in 6 ..= 15 { append(&ring, i) log.debug("Length:", ring.len) log.debug("Start index:", _start_index_raos(ring)) log.debug("End index:", ring._end_index) log.debug(ring.data) } testing.expect_value(t, get(ring, 0)^, 6) testing.expect_value(t, get(ring, 4)^, 10) testing.expect_value(t, get(ring, 9)^, 15) 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) for i in 15 ..= 25 { append(&ring, i) log.debug("Length:", ring.len) log.debug("Start index:", _start_index_raos(ring)) log.debug("End index:", ring._end_index) log.debug(ring.data) } testing.expect_value(t, get(ring, 0)^, 16) testing.expect_value(t, ring._end_index, 5) testing.expect_value(t, get_last(ring)^, 25) clear(&ring) append(&ring, 1) testing.expect_value(t, ring.len, 1) testing.expect_value(t, get(ring, 0)^, 1) } @(test) test_ring_soa :: proc(t: ^testing.T) { Ints :: struct { x, y: int, } data := make_soa_slice(#soa[]Ints, 10) ring := from_slice(data) defer delete(ring.data) 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("End index:", ring._end_index) log.debug(ring.data) } testing.expect_value(t, get(ring, 0), Ints{1, 1}) 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) 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("End index:", ring._end_index) log.debug(ring.data) } testing.expect_value(t, get(ring, 0), Ints{6, 6}) testing.expect_value(t, get(ring, 4), Ints{10, 10}) testing.expect_value(t, get(ring, 9), Ints{15, 15}) 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) 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("End index:", ring._end_index) log.debug(ring.data) } testing.expect_value(t, get(ring, 0), Ints{16, 16}) testing.expect_value(t, ring._end_index, 5) testing.expect_value(t, get_last(ring), Ints{25, 25}) clear(&ring) append(&ring, Ints{1, 1}) testing.expect_value(t, ring.len, 1) testing.expect_value(t, get(ring, 0), Ints{1, 1}) }