155 lines
3.5 KiB
Odin

package spanpool
import "base:builtin"
import "core:fmt"
import "core:slice"
Span :: struct {
first, len: i32,
}
Generation_Index :: i32
Span_Pool :: struct($E: typeid) {
elems: [dynamic]E,
generations: [dynamic]Generation_Index,
free_spans: [dynamic]Span,
}
Handle :: struct {
first: i32,
len: i32,
gen: i32,
}
destroy_spanpool :: proc(s: ^$T/Span_Pool($E)) {
delete(s.elems)
delete(s.generations)
delete(s.free_spans)
}
// TODO: use everywhere
is_handle_valid :: proc(s: ^$T/Span_Pool($E), handle: Handle) -> bool {
return(
handle.gen != 0 &&
int(handle.first + handle.len) <= len(s.elems) &&
s.generations[handle.first] == handle.gen \
)
}
resolve_slice :: proc(s: ^$T/Span_Pool($E), handle: Handle, loc := #caller_location) -> []E {
assert(is_handle_valid(s, handle), "invalid spanpool handle", loc)
return s.elems[handle.first:handle.first + handle.len]
}
resolve_single :: proc(s: ^$T/Span_Pool($E), handle: Handle) -> ^E {
assert(int(handle.first + handle.len) <= len(s.elems))
assert(handle.len == 1)
assert(s.generations[handle.first] == handle.gen)
return &s.elems[handle.first]
}
allocate_elems :: proc(s: ^$T/Span_Pool($E), elems: ..E) -> (handle: Handle) {
handle = allocate_num(s, i32(len(elems)))
for i in 0 ..< handle.len {
s.elems[i + handle.first] = elems[i]
}
return
}
allocate_num :: proc(s: ^$T/Span_Pool($E), count: i32) -> (handle: Handle) {
handle.len = count
maybe_existing_span: Maybe(i32)
for span, i in s.free_spans {
if span.len >= count {
maybe_existing_span = i32(i)
break
}
}
existing_span, ok := maybe_existing_span.?
if ok {
span := s.free_spans[existing_span]
handle.first = span.first
new_len := span.len - count
if new_len == 0 {
ordered_remove(&s.free_spans, existing_span)
} else {
s.free_spans[existing_span].first += count
s.free_spans[existing_span].len = new_len
}
} else {
handle.first = i32(len(s.elems))
new_len := len(s.elems) + int(count)
resize(&s.elems, new_len)
resize(&s.generations, new_len)
}
// Now figure out the generation index
max_gen := i32(0)
for i in handle.first ..< handle.first + handle.len {
max_gen = max(max_gen, s.generations[i])
}
handle.gen = max_gen + 1
for i in handle.first ..< handle.first + handle.len {
s.generations[i] = handle.gen
}
return
}
free :: proc(s: ^$T/Span_Pool($E), handle: Handle, loc := #caller_location) {
fmt.assertf(
int(handle.first + handle.len) <= len(s.elems),
"invalid handle %v",
handle,
loc = loc,
)
for i in 0 ..< handle.len {
fmt.assertf(
s.generations[handle.first + i] == handle.gen,
"wrong generation number %v at index %v for handle %v",
s.generations[handle.first + i],
i,
handle,
loc = loc,
)
s.generations[handle.first + i] += 1
}
append(&s.free_spans, Span{first = handle.first, len = handle.len})
}
reconcile :: proc(s: ^$T/Span_Pool($E)) {
slice.sort_by_key(s.free_spans[:], proc(span: Span) -> i32 {
return span.first
})
for i := len(s.free_spans) - 2; i >= 0; i -= 1 {
cur_span, next_span := &s.free_spans[i], &s.free_spans[i + 1]
if (cur_span.first + cur_span.len == next_span.first) {
// Merge
cur_span.len += next_span.len
unordered_remove(&s.free_spans, i + 1)
}
}
}
copy :: proc(dst: ^Span_Pool($E), src: Span_Pool(E)) {
resize(&dst.elems, len(src.elems))
resize(&dst.generations, len(src.generations))
resize(&dst.free_spans, len(src.free_spans))
builtin.copy(dst.elems[:], src.elems[:])
builtin.copy(dst.generations[:], src.generations[:])
builtin.copy(dst.free_spans[:], src.free_spans[:])
}