170 lines
4.3 KiB
Odin
170 lines
4.3 KiB
Odin
package spanpool
|
|
|
|
import "base:runtime"
|
|
import "core:c/libc"
|
|
import "core:log"
|
|
import "core:slice"
|
|
import "core:testing"
|
|
|
|
expect_assert :: proc(
|
|
t: ^testing.T,
|
|
data: $T,
|
|
client_proc: proc(_: ^testing.T, data: T),
|
|
expr := #caller_expression,
|
|
loc := #caller_location,
|
|
) {
|
|
@(thread_local)
|
|
jmp: libc.jmp_buf
|
|
|
|
context.assertion_failure_proc =
|
|
proc(prefix, message: string, loc: runtime.Source_Code_Location) -> ! {
|
|
log.infof("expected assertion %s: %s ", prefix, message, location = loc)
|
|
libc.longjmp(&jmp, 1)
|
|
}
|
|
|
|
jmp_res := libc.setjmp(&jmp)
|
|
|
|
if jmp_res == 0 {
|
|
client_proc(t, data)
|
|
libc.longjmp(&jmp, 2)
|
|
}
|
|
|
|
if jmp_res != 1 {
|
|
log.errorf("%v DID NOT assert", expr, location = loc)
|
|
}
|
|
}
|
|
|
|
@(test)
|
|
test_basic_alloc :: proc(t: ^testing.T) {
|
|
pool := Span_Pool(u32){}
|
|
defer destroy_spanpool(&pool)
|
|
|
|
handle := allocate_elems(&pool, 1, 2, 3, 4)
|
|
|
|
testing.expect_value(t, handle.first, 0)
|
|
testing.expect_value(t, handle.len, 4)
|
|
testing.expect_value(t, handle.gen, 1)
|
|
}
|
|
|
|
@(test)
|
|
test_basic_free :: proc(t: ^testing.T) {
|
|
pool := Span_Pool(u32){}
|
|
defer destroy_spanpool(&pool)
|
|
|
|
handle := allocate_elems(&pool, 1, 2, 3, 4)
|
|
|
|
free(&pool, handle)
|
|
|
|
testing.expect_value(t, len(pool.free_spans), 1)
|
|
testing.expect_value(t, pool.free_spans[0], Span{first = 0, len = 4})
|
|
}
|
|
|
|
@(test)
|
|
test_double_free :: proc(t: ^testing.T) {
|
|
pool := Span_Pool(u32){}
|
|
defer destroy_spanpool(&pool)
|
|
|
|
expect_assert(t, &pool, proc(t: ^testing.T, pool: ^Span_Pool(u32)) {
|
|
handle := allocate_elems(pool, 1, 2, 3, 4)
|
|
|
|
free(pool, handle)
|
|
free(pool, handle)
|
|
})
|
|
}
|
|
|
|
@(test)
|
|
test_resolve :: proc(t: ^testing.T) {
|
|
pool := Span_Pool(u32){}
|
|
defer destroy_spanpool(&pool)
|
|
|
|
handle := allocate_elems(&pool, 1, 2, 3, 4)
|
|
handle2 := allocate_elems(&pool, 4, 3, 2, 1)
|
|
|
|
testing.expect(t, slice.equal(resolve_slice(&pool, handle), []u32{1, 2, 3, 4}))
|
|
testing.expect(t, slice.equal(resolve_slice(&pool, handle2), []u32{4, 3, 2, 1}))
|
|
}
|
|
|
|
@(test)
|
|
test_multiple_spans :: proc(t: ^testing.T) {
|
|
pool := Span_Pool(u32){}
|
|
defer destroy_spanpool(&pool)
|
|
|
|
handle := allocate_elems(&pool, 1, 2, 3, 4)
|
|
handle2 := allocate_elems(&pool, 4, 3, 2, 1)
|
|
|
|
free(&pool, handle)
|
|
free(&pool, handle2)
|
|
|
|
testing.expect_value(t, len(pool.free_spans), 2)
|
|
testing.expect_value(t, pool.free_spans[0], Span{first = 0, len = 4})
|
|
testing.expect_value(t, pool.free_spans[1], Span{first = 4, len = 4})
|
|
}
|
|
|
|
@(test)
|
|
test_reconcile :: proc(t: ^testing.T) {
|
|
pool := Span_Pool(u32){}
|
|
defer destroy_spanpool(&pool)
|
|
|
|
handle := allocate_elems(&pool, 1, 2, 3, 4)
|
|
handle2 := allocate_elems(&pool, 4, 3, 2, 1)
|
|
|
|
free(&pool, handle)
|
|
free(&pool, handle2)
|
|
|
|
reconcile(&pool)
|
|
|
|
testing.expect_value(t, len(pool.free_spans), 1)
|
|
testing.expect_value(t, pool.free_spans[0], Span{first = 0, len = 8})
|
|
}
|
|
|
|
@(test)
|
|
test_reconcile_with_gaps :: proc(t: ^testing.T) {
|
|
pool := Span_Pool(u32){}
|
|
defer destroy_spanpool(&pool)
|
|
|
|
handle := allocate_elems(&pool, 1, 2, 3, 4)
|
|
handle2 := allocate_elems(&pool, 1, 2, 3, 4)
|
|
handle3 := allocate_elems(&pool, 1, 2, 3, 4)
|
|
handle4 := allocate_elems(&pool, 1, 2, 3, 4)
|
|
handle5 := allocate_elems(&pool, 1, 2, 3, 4)
|
|
handle6 := allocate_elems(&pool, 1, 2, 3, 4)
|
|
handle7 := allocate_elems(&pool, 1, 2, 3, 4)
|
|
|
|
free(&pool, handle)
|
|
free(&pool, handle3)
|
|
free(&pool, handle4)
|
|
free(&pool, handle6)
|
|
free(&pool, handle7)
|
|
|
|
_, _ = handle2, handle5
|
|
|
|
reconcile(&pool)
|
|
|
|
testing.expect_value(t, len(pool.free_spans), 3)
|
|
testing.expect_value(t, pool.free_spans[0], Span{first = 0, len = 4})
|
|
testing.expect_value(t, pool.free_spans[1], Span{first = 8, len = 8})
|
|
testing.expect_value(t, pool.free_spans[2], Span{first = 20, len = 8})
|
|
}
|
|
|
|
@(test)
|
|
test_free_span_reuse :: proc(t: ^testing.T) {
|
|
pool := Span_Pool(u32){}
|
|
defer destroy_spanpool(&pool)
|
|
|
|
handle := allocate_elems(&pool, 1, 2, 3, 4)
|
|
_ = allocate_elems(&pool, 1, 2, 3, 4)
|
|
|
|
free(&pool, handle)
|
|
|
|
handle3 := allocate_elems(&pool, 5, 6)
|
|
handle4 := allocate_elems(&pool, 7, 8)
|
|
handle5 := allocate_elems(&pool, 9, 10)
|
|
|
|
testing.expect_value(t, handle3, Handle{first = 0, len = 2, gen = 3})
|
|
testing.expect_value(t, handle4, Handle{first = 2, len = 2, gen = 3})
|
|
testing.expect_value(t, handle5, Handle{first = 8, len = 2, gen = 1})
|
|
testing.expect(t, slice.equal(resolve_slice(&pool, handle3), []u32{5, 6}))
|
|
testing.expect(t, slice.equal(resolve_slice(&pool, handle4), []u32{7, 8}))
|
|
testing.expect(t, slice.equal(resolve_slice(&pool, handle5), []u32{9, 10}))
|
|
}
|