mirror of
https://github.com/MihailRis/voxelcore.git
synced 2026-10-04 18:41:51 +00:00
367 lines
9.9 KiB
Lua
367 lines
9.9 KiB
Lua
local MIN_CAPACITY = 8
|
|
local _type = type
|
|
local FFI = ffi
|
|
|
|
FFI.cdef[[
|
|
void* malloc(size_t);
|
|
void* realloc(void*, size_t);
|
|
void free(void*);
|
|
typedef struct {
|
|
unsigned char* bytes;
|
|
int size;
|
|
int capacity;
|
|
} bytearray_t;
|
|
void* memmove(void*, const void*, size_t);
|
|
]]
|
|
|
|
local free = FFI.C.free
|
|
|
|
local FFIBytearray
|
|
local bytearray_type
|
|
|
|
local allocated_bytes = 0
|
|
local GC_THRESHOLD = 50 * 1024 * 1024
|
|
|
|
local function malloc(size)
|
|
local raw = FFI.C.malloc(size)
|
|
if not raw then
|
|
error("failed memory allocation of " .. size .. " bytes")
|
|
end
|
|
|
|
allocated_bytes = allocated_bytes + size
|
|
if allocated_bytes >= GC_THRESHOLD then
|
|
collectgarbage("step", 200)
|
|
allocated_bytes = 0
|
|
end
|
|
|
|
return raw
|
|
end
|
|
|
|
local function realloc(buff, size, throwerror)
|
|
if throwerror == nil then throwerror = true end
|
|
|
|
if size <= 0 then
|
|
free(buff)
|
|
return
|
|
end
|
|
|
|
local raw = FFI.C.realloc(buff, size)
|
|
if not raw and throwerror then
|
|
error("failed memory reallocation of block " .. buff .. " to new size " .. size)
|
|
end
|
|
|
|
allocated_bytes = allocated_bytes + size
|
|
if allocated_bytes >= GC_THRESHOLD then
|
|
collectgarbage("step", 200)
|
|
allocated_bytes = 0
|
|
end
|
|
|
|
return raw
|
|
end
|
|
|
|
local function grow_buffer(self, elems)
|
|
local new_capacity = math.ceil(self.capacity / 0.75 + elems)
|
|
self.bytes = realloc(self.bytes, new_capacity)
|
|
self.capacity = new_capacity
|
|
end
|
|
|
|
local function trim_buffer(self)
|
|
if self.size == self.capacity then return end
|
|
local new_bytes = realloc(self.bytes, self.size, false)
|
|
if new_bytes then
|
|
self.bytes = new_bytes
|
|
self.capacity = self.size
|
|
end
|
|
end
|
|
|
|
local function count_elements(b)
|
|
local elems = 1
|
|
if _type(b) ~= "number" then
|
|
elems = #b
|
|
end
|
|
return elems
|
|
end
|
|
|
|
local function append(self, b)
|
|
local elems = count_elements(b)
|
|
if self.size + elems > self.capacity then
|
|
grow_buffer(self, elems)
|
|
end
|
|
if _type(b) == "number" then
|
|
self.bytes[self.size] = b
|
|
else
|
|
for i=1, #b do
|
|
self.bytes[self.size + i - 1] = b[i]
|
|
end
|
|
end
|
|
self.size = self.size + elems
|
|
end
|
|
|
|
local function insert(self, index, b)
|
|
if b == nil then
|
|
b = index
|
|
index = self.size + 1
|
|
end
|
|
if index <= 0 or index > self.size + 1 then
|
|
return
|
|
end
|
|
local elems = count_elements(b)
|
|
if self.size + elems > self.capacity then
|
|
grow_buffer(self, elems)
|
|
end
|
|
for i = self.size - 1, index - 1, -1 do
|
|
self.bytes[i + elems] = self.bytes[i]
|
|
end
|
|
if _type(b) == "number" then
|
|
self.bytes[index - 1] = b
|
|
else
|
|
for i=1, #b do
|
|
self.bytes[index + i - 2] = b[i]
|
|
end
|
|
end
|
|
self.size = self.size + elems
|
|
end
|
|
|
|
local function remove(self, index, elems)
|
|
if index <= 0 or index > self.size then
|
|
return
|
|
end
|
|
if elems == nil then
|
|
elems = 1
|
|
end
|
|
if index + elems > self.size then
|
|
elems = self.size - index + 1
|
|
end
|
|
for i=index - 1, self.size - elems - 1 do
|
|
self.bytes[i] = self.bytes[i + elems]
|
|
end
|
|
self.size = self.size - elems
|
|
end
|
|
|
|
local function clear(self)
|
|
self.size = 0
|
|
end
|
|
|
|
local function fill(self, index, size, byte)
|
|
if index ~= nil and size ~= nil and size > 0 then
|
|
if index < 1 or index + size - 1 > self.size then
|
|
error("selected fill range is out of array bounds")
|
|
end
|
|
FFI.fill(self.bytes + index - 1, size, byte)
|
|
else
|
|
FFI.fill(self.bytes, self.size, byte)
|
|
end
|
|
end
|
|
|
|
local function reserve(self, new_capacity)
|
|
if new_capacity <= self.capacity then return end
|
|
self.bytes = realloc(self.bytes, new_capacity)
|
|
self.capacity = new_capacity
|
|
end
|
|
|
|
local function get_capacity(self)
|
|
return self.capacity
|
|
end
|
|
|
|
local function slice(self, offset, length)
|
|
offset = offset or 1
|
|
length = length or (self.size - offset + 1)
|
|
if offset < 1 or offset > self.size then
|
|
return FFIBytearray(0)
|
|
end
|
|
if offset + length - 1 > self.size then
|
|
length = self.size - offset + 1
|
|
end
|
|
local buffer = malloc(length)
|
|
FFI.copy(buffer, self.bytes + (offset - 1), length)
|
|
return bytearray_type(buffer, length, length)
|
|
end
|
|
|
|
local function copy(self, srcindex, dst, dstindex, size)
|
|
if size <= 0 then error("size of byte range must be positive non-zero integer") end
|
|
if srcindex < 1 or srcindex + size - 1 > self.size then
|
|
error("specified source byte range is out of source array bounds")
|
|
end
|
|
if dstindex < 1 or dstindex + size - 1 > dst.size then
|
|
error("specified destination byte range is out of destination array bounds")
|
|
end
|
|
|
|
FFI.copy(dst.bytes + dstindex - 1, self.bytes + srcindex - 1, size)
|
|
end
|
|
|
|
local function move(self, fromindex, toindex, size)
|
|
if size <= 0 then error("size of byte range must be positive non-zero integer") end
|
|
if fromindex < 1 or fromindex + size - 1 > self.size then
|
|
error("specified source byte range is out of array bounds")
|
|
end
|
|
if toindex < 1 or toindex + size - 1 > self.size then
|
|
error("specified destination byte range is out of array bounds")
|
|
end
|
|
|
|
FFI.C.memmove(self.bytes + self.toindex - 1, self.bytes + self.fromindex - 1, size)
|
|
end
|
|
|
|
local bytearray_methods = {
|
|
append = append,
|
|
insert = insert,
|
|
remove = remove,
|
|
trim = trim_buffer,
|
|
clear = clear,
|
|
reserve = reserve,
|
|
get_capacity = get_capacity,
|
|
slice = slice,
|
|
fill = fill,
|
|
copy = copy,
|
|
move = move
|
|
}
|
|
|
|
local bytearray_mt = {
|
|
__index = function(self, key)
|
|
if _type(key) == "string" then
|
|
return bytearray_methods[key]
|
|
end
|
|
if key <= 0 or key > self.size then
|
|
return
|
|
end
|
|
return self.bytes[key - 1]
|
|
end,
|
|
__newindex = function(self, key, value)
|
|
if key == self.size + 1 then
|
|
return append(self, value)
|
|
elseif key <= 0 or key > self.size then
|
|
return
|
|
end
|
|
self.bytes[key - 1] = value
|
|
end,
|
|
__tostring = function(self)
|
|
return string.format("FFIBytearray[%s]{...}", tonumber(self.size))
|
|
end,
|
|
__len = function(self)
|
|
return tonumber(self.size)
|
|
end,
|
|
__gc = function(self)
|
|
free(self.bytes)
|
|
end,
|
|
__ipairs = function(self)
|
|
local i = 0
|
|
return function()
|
|
i = i + 1
|
|
if i <= self.size then
|
|
return i, self.bytes[i - 1]
|
|
end
|
|
end
|
|
end
|
|
}
|
|
bytearray_mt.__pairs = bytearray_mt.__ipairs
|
|
|
|
bytearray_type = FFI.metatype("bytearray_t", bytearray_mt)
|
|
|
|
FFIBytearray = {
|
|
__call = function (self, n)
|
|
local t = type(n)
|
|
if t == "string" then
|
|
local buffer = malloc(#n)
|
|
FFI.copy(buffer, n, #n)
|
|
return bytearray_type(buffer, #n, #n)
|
|
elseif t == "table" then
|
|
local capacity = math.max(#n, MIN_CAPACITY)
|
|
local buffer = FFI.cast("unsigned char*", malloc(capacity))
|
|
for i=1,#n do
|
|
buffer[i - 1] = n[i]
|
|
end
|
|
return bytearray_type(buffer, #n, capacity)
|
|
end
|
|
n = n or 0
|
|
if n < MIN_CAPACITY then
|
|
return bytearray_type(malloc(MIN_CAPACITY), n, MIN_CAPACITY)
|
|
else
|
|
return bytearray_type(malloc(n), n, n)
|
|
end
|
|
end,
|
|
}
|
|
table.merge(FFIBytearray, bytearray_methods)
|
|
|
|
local function FFIBytearray_as_ptr(bytes)
|
|
if type(bytes) == "cdata" then
|
|
return tostring(bytes.bytes):sub(27), bytes.size
|
|
end
|
|
return "0"
|
|
end
|
|
|
|
local function FFIBytearray_as_string(bytes)
|
|
local t = type(bytes)
|
|
if t == "cdata" then
|
|
return FFI.string(bytes.bytes, bytes.size)
|
|
elseif t == "table" then
|
|
local buffer = FFI.new("unsigned char[?]", #bytes)
|
|
for i=1, #bytes do
|
|
buffer[i - 1] = bytes[i]
|
|
end
|
|
return FFI.string(buffer, #bytes)
|
|
else
|
|
error("Bytearray expected, got "..type(bytes))
|
|
end
|
|
end
|
|
|
|
local function create_FFIview_class(name, typename)
|
|
local typesize = FFI.sizeof(typename)
|
|
local ptrtype = typename .. "*"
|
|
local FFIview_mt = {
|
|
__index = function(self, key)
|
|
if key == 'size' then
|
|
return self.bytes.size / typesize
|
|
end
|
|
if key <= 0 or key > self.bytes.size / typesize then
|
|
return
|
|
end
|
|
local ptr = FFI.cast(ptrtype, self.bytes.bytes)
|
|
return ptr[key - 1]
|
|
end,
|
|
__newindex = function(self, key, value)
|
|
if key <= 0 or key > self.bytes.size / typesize then
|
|
return
|
|
end
|
|
local ptr = FFI.cast(ptrtype, self.bytes.bytes)
|
|
ptr[key - 1] = value
|
|
end,
|
|
__len = function(self)
|
|
return self.bytes.size / typesize
|
|
end,
|
|
__tostring = function(self)
|
|
return string.format(name .. "[%s]{...}", tonumber(self.bytes.size / typesize))
|
|
end,
|
|
__ipairs = function(self)
|
|
local i = 0
|
|
return function()
|
|
i = i + 1
|
|
if i <= self.bytes.size / typesize then
|
|
local ptr = FFI.cast(ptrtype, self.bytes.bytes)
|
|
return i, ptr[i - 1]
|
|
end
|
|
end
|
|
end
|
|
}
|
|
return function (bytes)
|
|
local x = setmetatable({
|
|
bytes=bytes,
|
|
}, FFIview_mt)
|
|
return x
|
|
end
|
|
end
|
|
|
|
local FFII16view = create_FFIview_class("FFII16view", "int16_t")
|
|
local FFIU16view = create_FFIview_class("FFIU16view", "uint16_t")
|
|
local FFII32view = create_FFIview_class("FFII32view", "int32_t")
|
|
local FFIU32view = create_FFIview_class("FFIU32view", "uint32_t")
|
|
local FFIF32view = create_FFIview_class("FFIF32view", "float")
|
|
|
|
return {
|
|
FFIBytearray = setmetatable(FFIBytearray, FFIBytearray),
|
|
FFIBytearray_as_string = FFIBytearray_as_string,
|
|
FFIBytearray_as_ptr = FFIBytearray_as_ptr,
|
|
FFIU16view = FFIU16view,
|
|
FFII16view = FFII16view,
|
|
FFIU32view = FFIU32view,
|
|
FFII32view = FFII32view,
|
|
FFIF32view = FFIF32view,
|
|
}
|