Merge pull request #508 from MihailRis/bytearray-upgrade

Bytearray upgrade
This commit is contained in:
MihailRis 2025-04-12 18:47:05 +03:00 committed by GitHub
commit 7a19d99913
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 345 additions and 329 deletions

27
dev/tests/bytearray.lua Normal file
View File

@ -0,0 +1,27 @@
local arr = Bytearray()
assert(#arr == 0)
for i=1,10 do
arr[i] = 10 - i
assert(#arr == i)
assert(arr[i] == 10 - i)
end
for i, v in ipairs(arr) do
assert(v == 10 - i)
end
Bytearray.remove(arr, 2)
assert(#arr == 9)
Bytearray.insert(arr, {5, 3, 6})
assert(#arr == 12)
Bytearray.insert(arr, 2, 8)
assert(#arr == 13)
for i=1,10 do
assert(arr[i] == 10 - i)
end
print(#arr, arr:get_capacity())
arr:trim()
assert(#arr == arr:get_capacity())

View File

@ -0,0 +1,4 @@
local data_buffer = require "core:data_buffer"
local buffer = data_buffer({}, 'LE', true)
buffer:put_int16(2025)
debug.print(buffer)

View File

@ -0,0 +1,215 @@
local MIN_CAPACITY = 8
local _type = type
local FFI = ffi
FFI.cdef[[
void* malloc(size_t);
void free(void*);
typedef struct {
unsigned char* bytes;
int size;
int capacity;
} bytearray_t;
]]
local malloc = FFI.C.malloc
local free = FFI.C.free
local function grow_buffer(self, elems)
local new_capacity = math.ceil(self.capacity / 0.75 + elems)
local prev = self.bytes
self.bytes = malloc(new_capacity)
FFI.copy(self.bytes, prev, self.size)
self.capacity = new_capacity
free(prev)
end
local function trim_buffer(self)
if self.size == self.capacity then
return
end
local size = self.size
local prev = self.bytes
self.bytes = malloc(size)
FFI.copy(self.bytes, prev, self.size)
self.capacity = size
free(prev)
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, 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 reserve(self, new_capacity)
if new_capacity <= self.capacity then
return
end
local prev = self.bytes
self.bytes = malloc(new_capacity)
FFI.copy(self.bytes, prev, self.size)
self.capacity = new_capacity
free(prev)
end
local function get_capacity(self)
return self.capacity
end
local bytearray_methods = {
append=append,
insert=insert,
remove=remove,
trim=trim_buffer,
clear=clear,
reserve=reserve,
get_capacity=get_capacity,
}
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
}
local bytearray_type = FFI.metatype("bytearray_t", bytearray_mt)
local FFIBytearray = {
__call = function (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 = 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_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
return {
FFIBytearray = setmetatable(FFIBytearray, FFIBytearray),
FFIBytearray_as_string = FFIBytearray_as_string
}

View File

@ -263,6 +263,11 @@ entities.get_all = function(uids)
return stdcomp.get_all(uids)
end
end
local bytearray = require "core:internal/bytearray"
Bytearray = bytearray.FFIBytearray
Bytearray_as_string = bytearray.FFIBytearray_as_string
Bytearray_construct = Bytearray.__call
ffi = nil
math.randomseed(time.uptime() * 1536227939)

View File

@ -1,6 +1,3 @@
local _ffi = ffi
ffi = nil
-- Lua has no parallelizm, also _set_data does not call any lua functions so
-- may be reused one global ffi buffer per lua_State
local canvas_ffi_buffer
@ -8,14 +5,14 @@ local canvas_ffi_buffer_size = 0
function __vc_Canvas_set_data(self, data)
if type(data) == "cdata" then
self:_set_data(tostring(_ffi.cast("uintptr_t", data)))
self:_set_data(tostring(ffi.cast("uintptr_t", data)))
end
local width = self.width
local height = self.height
local size = width * height * 4
if size > canvas_ffi_buffer_size then
canvas_ffi_buffer = _ffi.new(
canvas_ffi_buffer = ffi.new(
string.format("unsigned char[%s]", size)
)
canvas_ffi_buffer_size = size
@ -23,7 +20,7 @@ function __vc_Canvas_set_data(self, data)
for i=0, size - 1 do
canvas_ffi_buffer[i] = data[i + 1]
end
self:_set_data(tostring(_ffi.cast("uintptr_t", canvas_ffi_buffer)))
self:_set_data(tostring(ffi.cast("uintptr_t", canvas_ffi_buffer)))
end
function crc32(bytes, chksum)
@ -31,14 +28,14 @@ function crc32(bytes, chksum)
local length = #bytes
if type(bytes) == "table" then
local buffer_len = _ffi.new('int[1]', length)
local buffer = _ffi.new(
local buffer_len = ffi.new('int[1]', length)
local buffer = ffi.new(
string.format("char[%s]", length)
)
for i=1, length do
buffer[i - 1] = bytes[i]
end
bytes = _ffi.string(buffer, buffer_len[0])
bytes = ffi.string(buffer, buffer_len[0])
end
return _crc32(bytes, chksum)
end

View File

@ -16,14 +16,14 @@ static int l_encode(lua::State* L) {
return lua::pushstring(L, util::base64_encode(
reinterpret_cast<const ubyte*>(buffer.data()), buffer.size()
));
} else if (auto bytes = lua::touserdata<lua::LuaBytearray>(L, 1)) {
return lua::pushstring(
L,
util::base64_encode(
bytes->data().data(),
bytes->data().size()
)
} else {
auto string = lua::bytearray_as_string(L, 1);
auto out = util::base64_encode(
reinterpret_cast<const ubyte*>(string.data()),
string.size()
);
lua::pop(L);
return lua::pushstring(L, std::move(out));
}
throw std::runtime_error("array or ByteArray expected");
}
@ -36,13 +36,10 @@ static int l_decode(lua::State* L) {
lua::pushinteger(L, buffer[i] & 0xFF);
lua::rawseti(L, i+1);
}
return 1;
} else {
lua::newuserdata<lua::LuaBytearray>(L, buffer.size());
auto bytearray = lua::touserdata<lua::LuaBytearray>(L, -1);
bytearray->data().reserve(buffer.size());
std::memcpy(bytearray->data().data(), buffer.data(), buffer.size());
return lua::create_bytearray(L, buffer.data(), buffer.size());
}
return 1;
}
const luaL_Reg base64lib[] = {

View File

@ -9,9 +9,7 @@ static int l_tobytes(lua::State* L) {
if (lua::gettop(L) >= 2) {
compress = lua::toboolean(L, 2);
}
return lua::newuserdata<lua::LuaBytearray>(
L, json::to_binary(value, compress)
);
return lua::create_bytearray(L, json::to_binary(value, compress));
}
static int l_frombytes(lua::State* L) {
@ -24,17 +22,18 @@ static int l_frombytes(lua::State* L) {
lua::pop(L);
}
return lua::pushvalue(L, json::from_binary(buffer.data(), len));
} else if (auto bytes = lua::touserdata<lua::LuaBytearray>(L, -1)) {
const auto& buffer = bytes->data();
return lua::pushvalue(
L, json::from_binary(buffer.data(), buffer.size())
);
} else {
throw std::runtime_error("table or Bytearray expected");
auto string = lua::bytearray_as_string(L, 1);
auto out = json::from_binary(
reinterpret_cast<const ubyte*>(string.data()), string.size()
);
lua::pop(L);
return lua::pushvalue(L, std::move(out));
}
}
const luaL_Reg bjsonlib[] = {
{"tobytes", lua::wrap<l_tobytes>},
{"frombytes", lua::wrap<l_frombytes>},
{NULL, NULL}};
{NULL, NULL}
};

View File

@ -99,7 +99,7 @@ static int pack(lua::State* L, const char* format, bool usetable) {
}
return 1;
} else {
return lua::newuserdata<lua::LuaBytearray>(L, builder.build());
return lua::create_bytearray(L, builder.build());
}
}
@ -130,8 +130,8 @@ static int count_elements(const char* format) {
static int l_unpack(lua::State* L) {
const char* format = lua::require_string(L, 1);
int count = count_elements(format);
auto bytes = lua::require_bytearray(L, 2);
ByteReader reader(bytes);
auto bytes = lua::bytearray_as_string(L, 2);
ByteReader reader(reinterpret_cast<const ubyte*>(bytes.data()), bytes.size());
bool bigEndian = false;
for (size_t i = 0; format[i]; i++) {

View File

@ -248,12 +248,14 @@ static int l_load_texture(lua::State* L) {
}
lua::pop(L);
load_texture(buffer.data(), buffer.size(), lua::require_string(L, 2));
} else if (auto bytes = lua::touserdata<lua::LuaBytearray>(L, 1)) {
} else {
auto string = lua::bytearray_as_string(L, 1);
load_texture(
bytes->data().data(),
bytes->data().size(),
reinterpret_cast<const ubyte*>(string.data()),
string.size(),
lua::require_string(L, 2)
);
lua::pop(L);
}
return 0;
}

View File

@ -128,7 +128,7 @@ static int l_read_bytes(lua::State* L) {
auto bytes = io::read_bytes(path);
if (lua::gettop(L) < 2 || !lua::toboolean(L, 2)) {
lua::newuserdata<lua::LuaBytearray>(L, std::move(bytes));
lua::create_bytearray(L, std::move(bytes));
} else {
lua::createtable(L, length, 0);
int newTable = lua::gettop(L);
@ -148,18 +148,12 @@ static int l_read_bytes(lua::State* L) {
static int l_write_bytes(lua::State* L) {
io::path path = get_writeable_path(L);
if (auto bytearray = lua::touserdata<lua::LuaBytearray>(L, 2)) {
auto& bytes = bytearray->data();
return lua::pushboolean(
L, io::write_bytes(path, bytes.data(), bytes.size())
);
}
std::vector<ubyte> bytes;
lua::read_bytes_from_table(L, 2, bytes);
return lua::pushboolean(
L, io::write_bytes(path, bytes.data(), bytes.size())
auto string = lua::bytearray_as_string(L, 2);
bool res = io::write_bytes(
path, reinterpret_cast<const ubyte*>(string.data()), string.size()
);
lua::pop(L);
return lua::pushboolean(L, res);
}
static int l_list_all_res(lua::State* L, const std::string& path) {

View File

@ -109,13 +109,13 @@ static int l_send(lua::State* L, network::Network& network) {
}
lua::pop(L);
connection->send(buffer.data(), size);
} else if (auto bytes = lua::touserdata<lua::LuaBytearray>(L, 2)) {
connection->send(
reinterpret_cast<char*>(bytes->data().data()), bytes->data().size()
);
} else if (lua::isstring(L, 2)) {
auto string = lua::tolstring(L, 2);
connection->send(string.data(), string.length());
} else {
auto string = lua::bytearray_as_string(L, 2);
connection->send(string.data(), string.length());
lua::pop(L);
}
return 0;
}
@ -140,13 +140,10 @@ static int l_recv(lua::State* L, network::Network& network) {
lua::pushinteger(L, buffer[i] & 0xFF);
lua::rawseti(L, i+1);
}
return 1;
} else {
lua::newuserdata<lua::LuaBytearray>(L, size);
auto bytearray = lua::touserdata<lua::LuaBytearray>(L, -1);
bytearray->data().reserve(size);
std::memcpy(bytearray->data().data(), buffer.data(), size);
return lua::create_bytearray(L, buffer.data(), size);
}
return 1;
}
static int l_available(lua::State* L, network::Network& network) {

View File

@ -14,13 +14,10 @@ static int l_tobytes(lua::State* L) {
lua::pushinteger(L, string[i] & 0xFF);
lua::rawseti(L, i+1);
}
return 1;
} else {
lua::newuserdata<lua::LuaBytearray>(L, string.length());
auto bytearray = lua::touserdata<lua::LuaBytearray>(L, -1);
bytearray->data().reserve(string.length());
std::memcpy(bytearray->data().data(), string.data(), string.length());
return lua::create_bytearray(L, string.data(), string.length());
}
return 1;
}
static int l_tostring(lua::State* L) {
@ -35,16 +32,10 @@ static int l_tostring(lua::State* L) {
}
lua::pop(L);
return lua::pushlstring(L, buffer.data(), size);
} else if (auto bytes = lua::touserdata<lua::LuaBytearray>(L, 1)) {
return lua::pushstring(
L,
std::string(
reinterpret_cast<char*>(bytes->data().data()),
bytes->data().size()
)
);
} else {
lua::bytearray_as_string(L, 1);
return 1;
}
return 1;
}
static int l_length(lua::State* L) {

View File

@ -144,7 +144,7 @@ static int l_get_chunk_data(lua::State* L) {
} else {
chunkData = compressed_chunks::encode(*chunk);
}
return lua::newuserdata<lua::LuaBytearray>(L, std::move(chunkData));
return lua::create_bytearray(L, std::move(chunkData));
}
static void integrate_chunk_client(Chunk& chunk) {
@ -175,14 +175,17 @@ static int l_set_chunk_data(lua::State* L) {
int x = static_cast<int>(lua::tointeger(L, 1));
int z = static_cast<int>(lua::tointeger(L, 2));
auto buffer = lua::require_bytearray(L, 3);
auto buffer = lua::bytearray_as_string(L, 3);
auto chunk = level->chunks->getChunk(x, z);
if (chunk == nullptr) {
return lua::pushboolean(L, false);
}
compressed_chunks::decode(
*chunk, buffer.data(), buffer.size(), *content->getIndices()
*chunk,
reinterpret_cast<const ubyte*>(buffer.data()),
buffer.size(),
*content->getIndices()
);
if (controller->getChunksController()->lighting == nullptr) {
return lua::pushboolean(L, true);
@ -198,10 +201,16 @@ static int l_save_chunk_data(lua::State* L) {
int x = static_cast<int>(lua::tointeger(L, 1));
int z = static_cast<int>(lua::tointeger(L, 2));
auto buffer = lua::require_bytearray(L, 3);
auto buffer = lua::bytearray_as_string(L, 3);
compressed_chunks::save(
x, z, std::move(buffer), level->getWorld()->wfile->getRegions()
x,
z,
std::vector(
reinterpret_cast<const ubyte*>(buffer.data()),
reinterpret_cast<const ubyte*>(buffer.data()) + buffer.size()
),
level->getWorld()->wfile->getRegions()
);
return 0;
}

View File

@ -18,26 +18,6 @@ namespace lua {
virtual const std::string& getTypeName() const = 0;
};
class LuaBytearray : public Userdata {
std::vector<ubyte> buffer;
public:
LuaBytearray(size_t capacity);
LuaBytearray(std::vector<ubyte> buffer);
LuaBytearray(const ubyte* data, size_t size);
virtual ~LuaBytearray();
const std::string& getTypeName() const override {
return TYPENAME;
}
inline std::vector<ubyte>& data() {
return buffer;
}
static int createMetatable(lua::State*);
inline static std::string TYPENAME = "Bytearray";
};
static_assert(!std::is_abstract<LuaBytearray>());
class LuaHeightmap : public Userdata {
std::shared_ptr<Heightmap> map;
std::unique_ptr<fnl_state> noise;

View File

@ -117,7 +117,6 @@ void lua::init_state(State* L, StateType stateType) {
initialize_libs_extends(L);
newusertype<LuaBytearray>(L);
newusertype<LuaHeightmap>(L);
newusertype<LuaVoxelFragment>(L);
newusertype<LuaCanvas>(L);

View File

@ -39,14 +39,16 @@ int l_crc32(lua::State* L) {
string.length()
)
);
} else if (auto bytearray = lua::touserdata<lua::LuaBytearray>(L, 1)) {
auto& bytes = bytearray->data();
return lua::pushinteger(L, crc32(value, bytes.data(), bytes.size()));
} else if (lua::istable(L, 1)) {
std::vector<ubyte> bytes;
lua::read_bytes_from_table(L, 1, bytes);
return lua::pushinteger(L, crc32(value, bytes.data(), bytes.size()));
} else {
throw std::runtime_error("invalid argument 1");
auto string = lua::bytearray_as_string(L, 1);
auto res = crc32(
value, reinterpret_cast<const ubyte*>(string.data()), string.size()
);
lua::pop(L);
return lua::pushinteger(L, res);
}
}

View File

@ -61,7 +61,7 @@ int lua::pushvalue(State* L, const dv::value& value) {
break;
case value_type::bytes: {
const auto& bytes = value.asBytes();
newuserdata<LuaBytearray>(L, bytes.data(), bytes.size());
create_bytearray(L, bytes.data(), bytes.size());
break;
}
}
@ -128,18 +128,14 @@ dv::value lua::tovalue(State* L, int idx) {
return map;
}
}
case LUA_TUSERDATA: {
if (auto bytes = touserdata<LuaBytearray>(L, idx)) {
const auto& data = bytes->data();
return std::make_shared<dv::objects::Bytes>(data.data(), data.size());
}
[[fallthrough]];
}
default:
throw std::runtime_error(
"lua type " + std::string(lua_typename(L, type)) +
" is not supported"
default: {
auto data = bytearray_as_string(L, idx);
auto bytes = std::make_shared<dv::objects::Bytes>(
reinterpret_cast<const ubyte*>(data.data()), data.size()
);
pop(L);
return bytes;
}
}
}

View File

@ -746,14 +746,22 @@ namespace lua {
}
}
inline std::vector<ubyte> require_bytearray(lua::State* L, int idx) {
if (auto* bytearray = lua::touserdata<LuaBytearray>(L, idx)) {
return bytearray->data();
} else if (lua::istable(L, idx)) {
std::vector<ubyte> bytes;
read_bytes_from_table(L, idx, bytes);
return bytes;
}
throw std::runtime_error("bytearray expected");
inline int create_bytearray(lua::State* L, const void* bytes, size_t size) {
lua::requireglobal(L, "Bytearray_construct");
lua::pushlstring(
L, std::string_view(reinterpret_cast<const char*>(bytes), size)
);
return lua::call(L, 1, 1);
}
inline int create_bytearray(lua::State* L, const std::vector<ubyte>& bytes) {
return create_bytearray(L, bytes.data(), bytes.size());
}
inline std::string_view bytearray_as_string(lua::State* L, int idx) {
lua::requireglobal(L, "Bytearray_as_string");
lua::pushvalue(L, idx);
lua::call(L, 1, 1);
return lua::tolstring(L, -1);
}
}

View File

@ -1,206 +0,0 @@
#include "../lua_custom_types.hpp"
#include <sstream>
#include "util/listutil.hpp"
#include "../lua_util.hpp"
using namespace lua;
LuaBytearray::LuaBytearray(size_t capacity) : buffer(capacity) {
buffer.resize(capacity);
}
LuaBytearray::LuaBytearray(std::vector<ubyte> buffer) : buffer(std::move(buffer)) {
}
LuaBytearray::LuaBytearray(const ubyte* data, size_t size) : buffer(data, data + size) {
}
LuaBytearray::~LuaBytearray() {
}
static int l_append(lua::State* L) {
if (auto buffer = touserdata<LuaBytearray>(L, 1)) {
if (lua::isnumber(L, 2)) {
auto value = tointeger(L, 2);
buffer->data().push_back(static_cast<ubyte>(value));
} else if (lua::istable(L, 2)) {
lua::read_bytes_from_table(L, 2, buffer->data());
} else if (auto extension = lua::touserdata<LuaBytearray>(L, 2)) {
util::concat(buffer->data(), extension->data());
} else {
throw std::runtime_error("integer/table/Bytearray expected");
}
}
return 0;
}
static int l_insert(lua::State* L) {
auto buffer = touserdata<LuaBytearray>(L, 1);
if (buffer == nullptr) {
return 0;
}
auto& data = buffer->data();
auto index = tointeger(L, 2) - 1;
if (static_cast<size_t>(index) > data.size()) {
return 0;
}
if (lua::isnumber(L, 3)) {
auto value = tointeger(L, 3);
data.insert(data.begin() + index, static_cast<ubyte>(value));
} else if (lua::istable(L, 3)) {
std::vector<ubyte> temp;
lua::read_bytes_from_table(L, 3, temp);
data.insert(data.begin() + index, temp.begin(), temp.end());
} else if (auto extension = lua::touserdata<LuaBytearray>(L, 3)) {
const std::vector<ubyte>& src = extension->data();
data.insert(data.begin() + index, src.begin(), src.end());
} else {
throw std::runtime_error("integer/table/Bytearray expected");
}
return 0;
}
static int l_remove(lua::State* L) {
auto buffer = touserdata<LuaBytearray>(L, 1);
if (buffer == nullptr) {
return 0;
}
auto& data = buffer->data();
auto index = tointeger(L, 2) - 1;
if (static_cast<size_t>(index) > data.size()) {
return 0;
}
data.erase(data.begin() + index);
return 0;
}
static std::unordered_map<std::string, lua_CFunction> methods {
{"append", lua::wrap<l_append>},
{"insert", lua::wrap<l_insert>},
{"remove", lua::wrap<l_remove>},
};
static int l_meta_meta_call(lua::State* L) {
if (lua_istable(L, 2)) {
size_t len = objlen(L, 2);
std::vector<ubyte> buffer(len);
buffer.resize(len);
for (size_t i = 0; i < len; i++) {
rawgeti(L, i + 1);
buffer[i] = static_cast<ubyte>(tointeger(L, -1));
pop(L);
}
return newuserdata<LuaBytearray>(L, std::move(buffer));
}
auto size = tointeger(L, 2);
if (size < 0) {
throw std::runtime_error("size can not be less than 0");
}
return newuserdata<LuaBytearray>(L, static_cast<size_t>(size));
}
static int l_meta_index(lua::State* L) {
auto buffer = touserdata<LuaBytearray>(L, 1);
if (buffer == nullptr) {
return 0;
}
auto& data = buffer->data();
if (isstring(L, 2)) {
auto found = methods.find(tostring(L, 2));
if (found != methods.end()) {
return pushcfunction(L, found->second);
}
}
auto index = tointeger(L, 2) - 1;
if (static_cast<size_t>(index) > data.size()) {
return 0;
}
return pushinteger(L, data.at(index));
}
static int l_meta_newindex(lua::State* L) {
auto buffer = touserdata<LuaBytearray>(L, 1);
if (buffer == nullptr) {
return 0;
}
auto& data = buffer->data();
auto index = static_cast<size_t>(tointeger(L, 2) - 1);
auto value = tointeger(L, 3);
if (index >= data.size()) {
if (index == data.size()) {
data.push_back(static_cast<ubyte>(value));
}
return 0;
}
data[index] = static_cast<ubyte>(value);
return 0;
}
static int l_meta_len(lua::State* L) {
if (auto buffer = touserdata<LuaBytearray>(L, 1)) {
return pushinteger(L, buffer->data().size());
}
return 0;
}
static int l_meta_tostring(lua::State* L) {
auto buffer = touserdata<LuaBytearray>(L, 1);
if (buffer == nullptr) {
return 0;
}
auto& data = buffer->data();
if (data.size() > 512) {
return pushstring(
L, "bytearray[" + std::to_string(data.size()) + "]{...}"
);
} else {
std::stringstream ss;
ss << "bytearray[" << std::to_string(data.size()) << "]{";
for (size_t i = 0; i < data.size(); i++) {
if (i > 0) {
ss << " ";
}
ss << static_cast<uint>(data[i]);
}
ss << "}";
return pushstring(L, ss.str());
}
}
static int l_meta_add(lua::State* L) {
auto bufferA = touserdata<LuaBytearray>(L, 1);
auto bufferB = touserdata<LuaBytearray>(L, 2);
if (bufferA == nullptr || bufferB == nullptr) {
return 0;
}
auto& dataA = bufferA->data();
auto& dataB = bufferB->data();
std::vector<ubyte> ab;
ab.reserve(dataA.size() + dataB.size());
ab.insert(ab.end(), dataA.begin(), dataA.end());
ab.insert(ab.end(), dataB.begin(), dataB.end());
return newuserdata<LuaBytearray>(L, std::move(ab));
}
int LuaBytearray::createMetatable(lua::State* L) {
createtable(L, 0, 6);
pushcfunction(L, lua::wrap<l_meta_index>);
setfield(L, "__index");
pushcfunction(L, lua::wrap<l_meta_newindex>);
setfield(L, "__newindex");
pushcfunction(L, lua::wrap<l_meta_len>);
setfield(L, "__len");
pushcfunction(L, lua::wrap<l_meta_tostring>);
setfield(L, "__tostring");
pushcfunction(L, lua::wrap<l_meta_add>);
setfield(L, "__add");
createtable(L, 0, 1);
pushcfunction(L, lua::wrap<l_meta_meta_call>);
setfield(L, "__call");
setmetatable(L);
return 1;
}