mcap/src/base/circle_buffer.jai

210 lines
4.8 KiB
Plaintext

Circle_Buffer :: struct (T: Type) {
allocated: s64;
data: *T;
allocator: Allocator;
offset: s64;
total_count: s64;
}
resize :: (cb: *Circle_Buffer, item_count: s64) {
buffer_has_memory := cb.data;
cb_copy := cb.*;
remember_allocators(cb);
array := NewArray(item_count, cb.T,, cb.allocator);
cb.allocated = array.count;
cb.data = array.data;
cb.offset = 0;
cb.total_count = 0;
// add back original and delete
if buffer_has_memory {
for item: cb_copy {
insert(cb, item);
}
delete(*cb_copy);
}
}
initialized :: (cb: *Circle_Buffer) -> bool {
return (cb.data && cb.allocated);
}
reset :: (using cb: *Circle_Buffer, $overwrite:=false) {
offset = 0;
total_count = 0;
if overwrite {
memset(cb.data, 0, cb.allocated * size_of(cb.T));
}
}
delete :: (cb: *Circle_Buffer) {
free(cb.data,, cb.allocator);
cb.allocated = 0;
cb.data = null;
cb.offset = 0;
cb.total_count = 0;
}
// This version of insert returns the original item from the slot to
// be inserted into. This is useful for when resources need to be freed before
// a slot should be overwritten
insert :: (cb: *Circle_Buffer, item: cb.T) -> cb.T {
original_item := cb.data[cb.offset];
cb.data[cb.offset] = item;
cb.offset = (cb.offset + 1) % cb.allocated;
cb.total_count += 1;
return original_item;
}
insert :: (cb: *Circle_Buffer, items: []cb.T) {
for item: items { insert(cb, item); }
}
// The newest get_count items, oldest first. Clamped to the items actually stored, since the slots
// past them hold nothing yet.
get :: (cb: *Circle_Buffer, get_count: s64) -> []cb.T {
get_count = min(get_count, count(cb.*));
return flatten_circle_buffer(cb, get_count);
}
flatten_circle_buffer :: (src: *Circle_Buffer, count: s64) -> []src.T {
assert(count <= src.allocated);
assert(src.offset < src.allocated);
// View buffer as Array_View:
src_array : []src.T = {src.allocated, src.data};
return flatten_circle_buffer(src_array, src.offset, count);
}
flatten_circle_buffer :: inline (src: []$T, offset: s64, count: s64) -> []T {
start := offset - count;
result := NewArray(count, T);
if start >= 0 { // just copy buffer:
for 0..count-1 {
result[it] = src[it + start];
}
return result;
}
remainder := -start;
src_start := src.count - remainder;
// copy from end-remainder to src_buffer end
copy1_count := remainder;
for 0..copy1_count-1 {
result[it] = src[it + src_start];
}
// copy from 0 -> offset (the end bit)
copy2_count := offset;
for 0..copy2_count-1 {
result[it + copy1_count] = src[it];
}
assert(copy1_count + copy2_count == count);
return result;
}
operator *[] :: (cb: Circle_Buffer, index: int) -> *cb.T {
cb_index : int;
if cb.total_count > cb.allocated {
assert(index < cb.allocated, "Array index out of bounds!");
cb_index = (cb.offset + index) % cb.allocated;
} else {
assert(index < cb.total_count, "Array index out of bounds!");
cb_index = (index);
}
result := *cb.data[cb_index];
return result;
}
count :: (cb: Circle_Buffer) -> int {
if cb.total_count <= cb.allocated {
return cb.total_count;
}
return cb.allocated;
}
// This needs a for_expansion & operator *[]
for_expansion :: (src: *$T/Circle_Buffer, body: Code, flags: For_Flags) #expand {
REVERSE :: cast(bool) (flags & .REVERSE);
DO_POINTER :: cast(bool) (flags & .POINTER);
`it_index: int;
#if DO_POINTER { `it: *src.T; }
else { `it: src.T; }
#if REVERSE it_index = count(src.*);
else it_index = -1;
case1 := src.total_count <= src.allocated;
if case1 {
for <=REVERSE i: 0..src.total_count-1 {
#if REVERSE it_index -= 1;
else it_index += 1;
#if DO_POINTER it = *src.data[i];
else it = src.data[i];
#insert body;
}
return;
}
// Section 1 | Section 2
// mem_block: [-------------|------------------]<src.allocated
// offset: ^
#if REVERSE {
// Section 1
for < i: 0..src.offset-1 {
it_index -= 1;
#if DO_POINTER it = *src.data[i];
else it = src.data[i];
#insert body;
}
// Section 2
for < i: src.offset..src.allocated-1 {
it_index -= 1;
#if DO_POINTER it = *src.data[i];
else it = src.data[i];
#insert body;
}
}
#if !REVERSE {
// Section 2 (pointers after offset)
for i: src.offset..src.allocated-1 {
it_index += 1;
#if DO_POINTER it = *src.data[i];
else it = src.data[i];
#insert body;
}
// Section 1
for i: 0..src.offset-1 {
it_index += 1;
#if DO_POINTER it = *src.data[i];
else it = src.data[i];
#insert body;
}
}
}