package xarr import "base:builtin" import "base:intrinsics" BASE_CHUNK_SIZE :: uint(64) BASE_CHUNK_SIZE_LOG2 :: intrinsics.constant_log2(BASE_CHUNK_SIZE) BASE_CHUNK_SHIFT :: BASE_CHUNK_SIZE_LOG2 - 1 NUM_CHUNKS :: 30 Xarr :: struct($T: typeid, $SOA := false) { chunks: ([NUM_CHUNKS]#soa[]T when SOA else [NUM_CHUNKS][^]T), len: int, allocated_chunks_mask: u32, } UINT_BITS :: size_of(uint) * 8 msb :: #force_inline proc "contextless" (#any_int idx: uint) -> i8 { return i8(UINT_BITS - intrinsics.count_leading_zeros(idx)) - 1 } chunk_by_index :: #force_inline proc "contextless" (#any_int idx: uint) -> (chunk: i8) { return max(msb(idx) - BASE_CHUNK_SHIFT, 0) } chunk_size :: #force_inline proc "contextless" (chunk_idx: i8) -> uint { return BASE_CHUNK_SIZE << u32(max(chunk_idx - 1, 0)) } get_chunk_slice_scalar :: #force_inline proc "contextless" ( a: $T/Xarr($E, false), chunk_idx: i8, ) -> []E { return a.chunks[chunk_idx][:chunk_size(chunk_idx)] } get_chunk_slice_soa :: #force_inline proc "contextless" ( a: $T/Xarr($E, true), chunk_idx: i8, ) -> #soa[]E { return a.chunks[chunk_idx] } get_chunk_slice :: proc { get_chunk_slice_scalar, get_chunk_slice_soa, } capacity_from_allocated_mask :: #force_inline proc(allocated_mask: uint) -> uint { return( (allocated_mask >> 1) << BASE_CHUNK_SIZE_LOG2 + (allocated_mask & 1) << BASE_CHUNK_SIZE_LOG2 \ ) } capacity :: #force_inline proc(a: $T/Xarr($E, $SOA)) -> u32 { allocated_mask := a.allocated_chunks_mask return capacity_from_allocated_mask(allocated_mask) } reserve :: proc(a: $T/^Xarr($E, $SOA), cap: int, allocator := context.allocator) { allocated_mask := a.allocated_chunks_mask current_chunk := msb(allocated_mask) required_chunks := chunk_by_index(max(cap - 1, 0)) + 1 for i := current_chunk + 1; i < required_chunks; i += 1 { when SOA { chunk_slice := make_soa_slice(#soa[]E, chunk_size(i), allocator) a.chunks[i] = chunk_slice } else { chunk_slice := make([]E, chunk_size(i), allocator) a.chunks[i] = raw_data(chunk_slice) } a.allocated_chunks_mask |= u32(1) << u8(i) } } append :: proc(a: $T/^Xarr($E, $SOA), elems: ..E, allocator := context.allocator) { if len(elems) == 0 { return } reserve(a, a.len + len(elems)) set_elems_assume_allocated(a, elems) a.len += len(elems) } translate_index :: #force_inline proc( #any_int idx: int, ) -> ( chunk_idx: i8, idx_within_chunk: uint, ) { assert(idx >= 0) chunk_idx = chunk_by_index(idx) idx_within_chunk = uint(idx) & (chunk_size(chunk_idx) - 1) return } @(private = "file") set_elems_assume_allocated :: proc(a: $T/^Xarr($E, $SOA), elems: []E) { for &e, i in elems { idx := a.len + i chunk_idx, idx_within_chunk := translate_index(idx) assert(a.chunks[chunk_idx] != nil) a.chunks[chunk_idx][idx_within_chunk] = e } } set :: proc(a: $T/Xarr($E, $SOA), #any_int idx: int, val: E) { assert(idx >= 0 && idx < a.len) chunk_idx, idx_within_chunk := translate_index(idx) return get_chunk_slice(a, chunk_idx)[idx_within_chunk] } get :: proc(a: $T/Xarr($E, $SOA), #any_int idx: int) -> E { assert(idx >= 0 && idx < a.len) chunk_idx, idx_within_chunk := translate_index(idx) return get_chunk_slice(a, chunk_idx)[idx_within_chunk] } get_ptr :: proc(a: $T/Xarr($E, $SOA), #any_int idx: int) -> ^E { assert(idx >= 0 && idx < a.len) chunk_idx, idx_within_chunk := translate_index(idx) return &get_chunk_slice(a, chunk_idx)[idx_within_chunk] } unordered_remove :: proc(a: $T/^Xarr($E, $SOA), #any_int idx: int) { assert(idx >= 0 && idx < a.len) get_ptr(a^, idx)^ = get(a^, a.len - 1) a.len -= 1 } clear :: proc "contextless" (a: $T/^Xarr($E, $SOA)) { a.len = 0 } delete :: proc(a: $T/^Xarr($E, $SOA), allocator := context.allocator) { for i in 0 ..< len(a.chunks) { builtin.delete(get_chunk_slice(a^, i8(i)), allocator) } a^ = Xarr(E, SOA){} } Iterator :: struct($E: typeid, $SOA: bool) { xarr: ^Xarr(E, SOA), idx: int, } iterator :: proc(a: $T/^Xarr($E, $SOA), start_idx := 0) -> Iterator(E, SOA) { return Iterator(E, SOA){xarr = a, idx = start_idx} } iterator_next :: proc(it: ^Iterator($E, $SOA)) -> (e: ^E, idx: int, ok: bool) { if it.idx >= it.xarr.len { return nil, it.idx, false } e = get_ptr(it.xarr^, it.idx) idx = it.idx ok = true it.idx += 1 return }