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: [-------------|------------------]