Files
evolved.lua/develop/testing/realloc_tests.lua
2026-01-16 04:12:17 +07:00

589 lines
18 KiB
Lua

local evo = require 'evolved'
---@type ffilib?
local ffi = (function()
local ffi_loader = package and package.preload and package.preload['ffi']
local ffi = ffi_loader and ffi_loader()
return ffi
end)()
if not ffi then
return
end
local FLOAT_TYPEOF = ffi.typeof('float')
local FLOAT_SIZEOF = ffi.sizeof(FLOAT_TYPEOF)
local FLOAT_STORAGE_TYPEOF = ffi.typeof('$[?]', FLOAT_TYPEOF)
local DOUBLE_TYPEOF = ffi.typeof('double')
local DOUBLE_SIZEOF = ffi.sizeof(DOUBLE_TYPEOF)
local DOUBLE_STORAGE_TYPEOF = ffi.typeof('$[?]', DOUBLE_TYPEOF)
local STORAGE_SIZES = {}
---@type evolved.realloc
local function float_realloc(src, src_size, dst_size)
if dst_size == 0 then
assert(src and src_size > 0)
local expected_src_size = STORAGE_SIZES[src]
assert(expected_src_size == src_size)
STORAGE_SIZES[src] = nil
return
else
if src then
assert(src_size > 0)
local expected_src_size = STORAGE_SIZES[src]
assert(expected_src_size == src_size)
else
assert(src_size == 0)
end
local dst = ffi.new(FLOAT_STORAGE_TYPEOF, dst_size + 1)
STORAGE_SIZES[dst] = dst_size
if src then
ffi.copy(dst + 1, src + 1, math.min(src_size, dst_size) * FLOAT_SIZEOF)
end
return dst
end
end
---@type evolved.realloc
local function double_realloc(src, src_size, dst_size)
if dst_size == 0 then
assert(src and src_size > 0)
local expected_src_size = STORAGE_SIZES[src]
assert(expected_src_size == src_size)
STORAGE_SIZES[src] = nil
return
else
if src then
assert(src_size > 0)
local expected_src_size = STORAGE_SIZES[src]
assert(expected_src_size == src_size)
else
assert(src_size == 0)
end
local dst = ffi.new(DOUBLE_STORAGE_TYPEOF, dst_size + 1)
STORAGE_SIZES[dst] = dst_size
if src then
ffi.copy(dst + 1, src + 1, math.min(src_size, dst_size) * DOUBLE_SIZEOF)
end
return dst
end
end
---@type evolved.compmove
local function double_compmove(src, f, e, t, dst)
ffi.copy(dst + t, src + f, (e - f + 1) * DOUBLE_SIZEOF)
end
do
local f1 = evo.builder():realloc(double_realloc):build()
local e1 = evo.builder():set(f1, 21):build()
assert(evo.has(e1, f1) and evo.get(e1, f1) == 21)
local e2 = evo.builder():set(f1, 42):build()
assert(evo.has(e1, f1) and evo.get(e1, f1) == 21)
assert(evo.has(e2, f1) and evo.get(e2, f1) == 42)
local e3 = evo.builder():set(f1, 84):build()
assert(evo.has(e1, f1) and evo.get(e1, f1) == 21)
assert(evo.has(e2, f1) and evo.get(e2, f1) == 42)
assert(evo.has(e3, f1) and evo.get(e3, f1) == 84)
evo.destroy(e1)
assert(not evo.has(e1, f1))
assert(evo.has(e2, f1) and evo.get(e2, f1) == 42)
assert(evo.has(e3, f1) and evo.get(e3, f1) == 84)
evo.destroy(e3)
assert(not evo.has(e1, f1))
assert(evo.has(e2, f1) and evo.get(e2, f1) == 42)
assert(not evo.has(e3, f1))
evo.destroy(e2)
assert(not evo.has(e1, f1))
assert(not evo.has(e2, f1))
assert(not evo.has(e3, f1))
end
do
local f1 = evo.builder():realloc(double_realloc):build()
local q1 = evo.builder():include(f1):build()
do
local es, ec = {}, 10
for i = 1, ec do es[i] = evo.spawn({ [f1] = i }) end
for i = 1, ec do assert(evo.has(es[i], f1) and evo.get(es[i], f1) == i) end
end
do
local p = evo.builder():set(f1, 42):build()
local es, ec = {}, 10
for i = 1, ec do es[i] = evo.clone(p) end
for i = 1, ec do assert(evo.has(es[i], f1) and evo.get(es[i], f1) == 42) end
end
do
local es1, ec1 = evo.multi_spawn(10, { [f1] = 42 })
for i = 1, ec1 do assert(evo.has(es1[i], f1) and evo.get(es1[i], f1) == 42) end
local es2, ec2 = evo.multi_spawn(20, { [f1] = 84 })
for i = 1, ec1 do assert(evo.has(es1[i], f1) and evo.get(es1[i], f1) == 42) end
for i = 1, ec2 do assert(evo.has(es2[i], f1) and evo.get(es2[i], f1) == 84) end
end
do
local p = evo.builder():set(f1, 21):build()
local es1, ec1 = evo.multi_clone(10, p)
for i = 1, ec1 do assert(evo.has(es1[i], f1) and evo.get(es1[i], f1) == 21) end
local es2, ec2 = evo.multi_clone(20, p)
for i = 1, ec1 do assert(evo.has(es1[i], f1) and evo.get(es1[i], f1) == 21) end
for i = 1, ec2 do assert(evo.has(es2[i], f1) and evo.get(es2[i], f1) == 21) end
end
evo.batch_destroy(q1)
end
do
local f1 = evo.builder():realloc(double_realloc):build()
local f2 = evo.builder():realloc(double_realloc):build()
local q1 = evo.builder():include(f1):build()
local q2 = evo.builder():include(f2):build()
do
local e = evo.builder():set(f1, 21):set(f2, 42):build()
assert(evo.has(e, f1) and evo.get(e, f1) == 21)
assert(evo.has(e, f2) and evo.get(e, f2) == 42)
evo.remove(e, f1)
assert(not evo.has(e, f1))
assert(evo.has(e, f2) and evo.get(e, f2) == 42)
end
do
local e = evo.builder():set(f1, 21):set(f2, 42):build()
assert(evo.has(e, f1) and evo.get(e, f1) == 21)
assert(evo.has(e, f2) and evo.get(e, f2) == 42)
evo.clear(e)
assert(not evo.has(e, f1))
assert(not evo.has(e, f2))
end
do
local es, ec = evo.multi_spawn(10, { [f1] = 21, [f2] = 42 })
for i = 1, ec do
assert(evo.has(es[i], f1) and evo.get(es[i], f1) == 21)
assert(evo.has(es[i], f2) and evo.get(es[i], f2) == 42)
end
evo.batch_remove(q1, f1)
local e12 = evo.builder():set(f1, 1):set(f2, 2):build()
assert(evo.has(e12, f1) and evo.get(e12, f1) == 1)
assert(evo.has(e12, f2) and evo.get(e12, f2) == 2)
for i = 1, ec do
assert(not evo.has(es[i], f1))
assert(evo.has(es[i], f2) and evo.get(es[i], f2) == 42)
end
evo.batch_set(q2, f1, 84)
assert(evo.has(e12, f1) and evo.get(e12, f1) == 84)
assert(evo.has(e12, f2) and evo.get(e12, f2) == 2)
for i = 1, ec do
assert(evo.has(es[i], f1) and evo.get(es[i], f1) == 84)
assert(evo.has(es[i], f2) and evo.get(es[i], f2) == 42)
end
evo.batch_set(q2, f1, 21)
assert(evo.has(e12, f1) and evo.get(e12, f1) == 21)
assert(evo.has(e12, f2) and evo.get(e12, f2) == 2)
for i = 1, ec do
assert(evo.has(es[i], f1) and evo.get(es[i], f1) == 21)
assert(evo.has(es[i], f2) and evo.get(es[i], f2) == 42)
end
end
end
do
local f1 = evo.builder():realloc(double_realloc):compmove(double_compmove):build()
local f2 = evo.builder():realloc(double_realloc):compmove(double_compmove):build()
local q1 = evo.builder():include(f1):build()
local q2 = evo.builder():include(f2):build()
do
local es1, ec1 = evo.multi_spawn(10, { [f1] = 1, [f2] = 2 })
for i = 1, ec1 do
assert(evo.has(es1[i], f1) and evo.get(es1[i], f1) == 1)
assert(evo.has(es1[i], f2) and evo.get(es1[i], f2) == 2)
end
local es2, ec2 = evo.multi_spawn(20, { [f1] = 3, [f2] = 4 })
for i = 1, ec1 do
assert(evo.has(es1[i], f1) and evo.get(es1[i], f1) == 1)
assert(evo.has(es1[i], f2) and evo.get(es1[i], f2) == 2)
end
for i = 1, ec2 do
assert(evo.has(es2[i], f1) and evo.get(es2[i], f1) == 3)
assert(evo.has(es2[i], f2) and evo.get(es2[i], f2) == 4)
end
local e2 = evo.builder():set(f2, 42):build()
assert(evo.has(e2, f2) and evo.get(e2, f2) == 42)
evo.batch_remove(q1, f1)
assert(evo.has(e2, f2) and evo.get(e2, f2) == 42)
for i = 1, ec1 do
assert(not evo.has(es1[i], f1))
assert(evo.has(es1[i], f2) and evo.get(es1[i], f2) == 2)
end
for i = 1, ec2 do
assert(not evo.has(es2[i], f1))
assert(evo.has(es2[i], f2) and evo.get(es2[i], f2) == 4)
end
local e12 = evo.builder():set(f1, 21):set(f2, 42):build()
assert(evo.has(e2, f2) and evo.get(e2, f2) == 42)
assert(evo.has(e12, f1) and evo.get(e12, f1) == 21)
assert(evo.has(e12, f2) and evo.get(e12, f2) == 42)
evo.batch_set(q2, f1, 84)
assert(evo.has(e2, f2) and evo.get(e2, f2) == 42)
assert(evo.has(e12, f1) and evo.get(e12, f1) == 84)
assert(evo.has(e12, f2) and evo.get(e12, f2) == 42)
for i = 1, ec1 do
assert(evo.has(es1[i], f1) and evo.get(es1[i], f1) == 84)
assert(evo.has(es1[i], f2) and evo.get(es1[i], f2) == 2)
end
for i = 1, ec2 do
assert(evo.has(es2[i], f1) and evo.get(es2[i], f1) == 84)
assert(evo.has(es2[i], f2) and evo.get(es2[i], f2) == 4)
end
end
end
do
local f1 = evo.builder():default(42):build()
local es, ec = evo.multi_spawn(10, { [f1] = 21 })
for i = 1, ec do assert(evo.has(es[i], f1) and evo.get(es[i], f1) == 21) end
evo.set(f1, evo.TAG)
for i = 1, ec do assert(evo.has(es[i], f1) and evo.get(es[i], f1) == nil) end
evo.remove(f1, evo.TAG)
for i = 1, ec do assert(evo.has(es[i], f1) and evo.get(es[i], f1) == 42) end
end
do
local f1 = evo.builder():realloc(float_realloc):build()
local e1 = evo.builder():set(f1, 3):build()
assert(evo.has(e1, f1) and evo.get(e1, f1) == 3)
evo.set(f1, evo.REALLOC, double_realloc)
assert(evo.has(e1, f1) and evo.get(e1, f1) == 3)
evo.remove(f1, evo.REALLOC)
assert(evo.has(e1, f1) and evo.get(e1, f1) == 3)
evo.set(f1, evo.REALLOC, double_realloc)
assert(evo.has(e1, f1) and evo.get(e1, f1) == 3)
end
do
local f1 = evo.builder():realloc(double_realloc):build()
local es, ec = evo.multi_spawn(20, { [f1] = 42 })
for i = 1, ec / 2 do
evo.destroy(es[ec - i + 1])
end
evo.collect_garbage()
for i = 1, ec / 2 do
assert(evo.has(es[i], f1) and evo.get(es[i], f1) == 42)
end
end
do
evo.collect_garbage()
local f1 = evo.builder():name('f1'):realloc(double_realloc):compmove(double_compmove):build()
local f2 = evo.builder():name('f2'):realloc(double_realloc):compmove(double_compmove):build()
local q1 = evo.builder():include(f1):build()
local q2 = evo.builder():include(f2):build()
do
local es, ec = evo.multi_spawn(40, { [f2] = 2 })
for i = 1, ec do
assert(not evo.has(es[i], f1))
assert(evo.has(es[i], f2) and evo.get(es[i], f2) == 2)
end
evo.batch_destroy(q2)
end
do
local es, ec = evo.multi_spawn(50, { [f1] = 1, [f2] = 2 })
for i = 1, ec do
assert(evo.has(es[i], f1) and evo.get(es[i], f1) == 1)
assert(evo.has(es[i], f2) and evo.get(es[i], f2) == 2)
end
evo.batch_remove(q1, f1)
for i = 1, ec do
assert(not evo.has(es[i], f1))
assert(evo.has(es[i], f2) and evo.get(es[i], f2) == 2)
end
evo.batch_destroy(q1, q2)
end
do
evo.spawn({ [f1] = 1 })
evo.spawn({ [f2] = 2 })
evo.spawn({ [f1] = 1, [f2] = 2 })
end
evo.collect_garbage()
end
do
evo.collect_garbage()
local f1 = evo.builder():name('f1'):realloc(double_realloc):compmove(double_compmove):build()
local f2 = evo.builder():name('f2'):realloc(double_realloc):compmove(double_compmove):build()
local q1 = evo.builder():include(f1):build()
local q2 = evo.builder():include(f2):build()
do
local es, ec = evo.multi_spawn(40, { [f1] = 1, [f2] = 2 })
for i = 1, ec do
assert(evo.has(es[i], f1) and evo.get(es[i], f1) == 1)
assert(evo.has(es[i], f2) and evo.get(es[i], f2) == 2)
end
evo.batch_destroy(q2)
end
do
local es, ec = evo.multi_spawn(50, { [f1] = 1 })
for i = 1, ec do
assert(evo.has(es[i], f1) and evo.get(es[i], f1) == 1)
assert(not evo.has(es[i], f2))
end
evo.batch_set(q1, f2, 2)
for i = 1, ec do
assert(evo.has(es[i], f1) and evo.get(es[i], f1) == 1)
assert(evo.has(es[i], f2) and evo.get(es[i], f2) == 2)
end
evo.batch_destroy(q1, q2)
end
do
evo.spawn({ [f1] = 1 })
evo.spawn({ [f2] = 2 })
evo.spawn({ [f1] = 1, [f2] = 2 })
end
evo.collect_garbage()
end
do
evo.collect_garbage()
local alloc_call_count = 0
local free_call_count = 0
local resize_call_count = 0
local function ctor_realloc()
---@type evolved.realloc
return function(src, src_size, dst_size)
if dst_size == 0 then
assert(src and src_size > 0)
free_call_count = free_call_count + 1
return
else
if src then
assert(src_size > 0)
resize_call_count = resize_call_count + 1
else
assert(src_size == 0)
alloc_call_count = alloc_call_count + 1
end
local dst = {}
if src then
for i = 1, math.min(src_size, dst_size) do
dst[i] = src[i]
end
end
return dst
end
end
end
do
local realloc1 = ctor_realloc()
local realloc2 = ctor_realloc()
local f1 = evo.builder():default(44):realloc(realloc1):build()
alloc_call_count, free_call_count, resize_call_count = 0, 0, 0
do
local e1 = evo.builder():set(f1, 21):build()
assert(evo.has(e1, f1) and evo.get(e1, f1) == 21)
assert(alloc_call_count == 1 and free_call_count == 0)
local e2 = evo.builder():set(f1, 42):build()
assert(evo.has(e1, f1) and evo.get(e1, f1) == 21)
assert(evo.has(e2, f1) and evo.get(e2, f1) == 42)
assert(alloc_call_count == 1 and free_call_count == 0)
evo.collect_garbage()
assert(alloc_call_count == 1 and free_call_count == 0 and resize_call_count == 0)
evo.destroy(e1)
assert(alloc_call_count == 1 and free_call_count == 0 and resize_call_count == 0)
evo.collect_garbage()
assert(alloc_call_count == 1 and free_call_count == 0 and resize_call_count == 0)
evo.destroy(e2)
assert(alloc_call_count == 1 and free_call_count == 0 and resize_call_count == 0)
evo.collect_garbage()
assert(alloc_call_count == 1 and free_call_count == 1 and resize_call_count == 0)
end
alloc_call_count, free_call_count, resize_call_count = 0, 0, 0
do
local es, ec = evo.multi_spawn(10, { [f1] = 84 })
assert(alloc_call_count == 1 and free_call_count == 0 and resize_call_count == 0)
for i = 1, ec / 2 do evo.destroy(es[i]) end
assert(alloc_call_count == 1 and free_call_count == 0 and resize_call_count == 0)
evo.collect_garbage()
assert(alloc_call_count == 1 and free_call_count == 0 and resize_call_count == 1)
evo.set(f1, evo.REALLOC, realloc2)
assert(alloc_call_count == 2 and free_call_count == 1 and resize_call_count == 1)
for i = 1, ec do evo.destroy(es[i]) end
evo.collect_garbage()
assert(alloc_call_count == 2 and free_call_count == 2 and resize_call_count == 1)
end
alloc_call_count, free_call_count, resize_call_count = 0, 0, 0
do
local e1 = evo.builder():set(f1, 24):build()
assert(evo.has(e1, f1) and evo.get(e1, f1) == 24)
assert(alloc_call_count == 1 and free_call_count == 0 and resize_call_count == 0)
evo.set(f1, evo.TAG)
assert(evo.has(e1, f1) and evo.get(e1, f1) == nil)
assert(alloc_call_count == 1 and free_call_count == 1 and resize_call_count == 0)
local es, ec = evo.multi_spawn(20, { [f1] = 48 })
for i = 1, ec do assert(evo.has(es[i], f1) and evo.get(es[i], f1) == nil) end
assert(alloc_call_count == 1 and free_call_count == 1 and resize_call_count == 0)
evo.remove(f1, evo.TAG)
assert(evo.has(e1, f1) and evo.get(e1, f1) == 44)
for i = 1, ec do assert(evo.has(es[i], f1) and evo.get(es[i], f1) == 44) end
assert(alloc_call_count == 2 and free_call_count == 1 and resize_call_count == 0)
evo.destroy(e1)
for i = 1, ec do evo.destroy(es[i]) end
assert(alloc_call_count == 2 and free_call_count == 1 and resize_call_count == 0)
evo.collect_garbage()
assert(alloc_call_count == 2 and free_call_count == 2 and resize_call_count == 0)
end
alloc_call_count, free_call_count, resize_call_count = 0, 0, 0
do
local e1 = evo.builder():set(f1, 100):build()
assert(evo.has(e1, f1) and evo.get(e1, f1) == 100)
assert(alloc_call_count == 1 and free_call_count == 0 and resize_call_count == 0)
evo.set(f1, evo.TAG)
assert(evo.has(e1, f1) and evo.get(e1, f1) == nil)
assert(alloc_call_count == 1 and free_call_count == 1 and resize_call_count == 0)
local es, ec = evo.multi_spawn(20, { [f1] = 48 })
for i = 1, ec do assert(evo.has(es[i], f1) and evo.get(es[i], f1) == nil) end
assert(alloc_call_count == 1 and free_call_count == 1 and resize_call_count == 0)
evo.destroy(e1)
for i = 1, ec do evo.destroy(es[i]) end
assert(alloc_call_count == 1 and free_call_count == 1 and resize_call_count == 0)
evo.collect_garbage()
assert(alloc_call_count == 1 and free_call_count == 1 and resize_call_count == 0)
end
end
do
local realloc = ctor_realloc()
local f1 = evo.builder():realloc(realloc):build()
alloc_call_count, free_call_count, resize_call_count = 0, 0, 0
do
local e1 = evo.builder():set(f1, 42):build()
assert(evo.has(e1, f1) and evo.get(e1, f1) == 42)
assert(alloc_call_count == 1 and free_call_count == 0 and resize_call_count == 0)
evo.destroy(e1)
assert(not evo.has(e1, f1) and evo.get(e1, f1) == nil)
assert(alloc_call_count == 1 and free_call_count == 0 and resize_call_count == 0)
evo.set(f1, evo.TAG)
assert(alloc_call_count == 1 and free_call_count == 1 and resize_call_count == 0)
end
end
end