270 lines
6.7 KiB
Odin
270 lines
6.7 KiB
Odin
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})
|
|
}
|