147 lines
3.4 KiB
Odin
147 lines
3.4 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)
|
|
}
|
|
|
|
resolve_slice :: proc(s: ^$T/Span_Pool($E), handle: Handle, loc := #caller_location) -> []E {
|
|
assert(int(handle.first + handle.len) <= len(s.elems), "invalid spanpool handle", loc)
|
|
assert(s.generations[handle.first] == handle.gen, "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[:])
|
|
}
|