From cca153b66b32704c2636c09b310fdc49d3731537 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 27 Dec 2024 08:33:52 +0300 Subject: [PATCH] add byteutil library --- src/coders/byte_utils.cpp | 50 +++-- src/coders/byte_utils.hpp | 24 ++- src/logic/scripting/lua/libs/api_lua.hpp | 1 + src/logic/scripting/lua/libs/libbyteutil.cpp | 194 +++++++++++++++++++ src/logic/scripting/lua/lua_engine.cpp | 1 + src/logic/scripting/lua/lua_util.hpp | 11 ++ 6 files changed, 253 insertions(+), 28 deletions(-) create mode 100644 src/logic/scripting/lua/libs/libbyteutil.cpp diff --git a/src/coders/byte_utils.cpp b/src/coders/byte_utils.cpp index e30c14f2e..bd4a00909 100644 --- a/src/coders/byte_utils.cpp +++ b/src/coders/byte_utils.cpp @@ -6,6 +6,10 @@ #include "util/data_io.hpp" +ByteBuilder::ByteBuilder(size_t size) { + buffer.reserve(size); +} + void ByteBuilder::put(ubyte b) { buffer.push_back(b); } @@ -31,37 +35,37 @@ void ByteBuilder::put(const ubyte* arr, size_t size) { } } -void ByteBuilder::putInt16(int16_t val) { +void ByteBuilder::putInt16(int16_t val, bool bigEndian) { size_t size = buffer.size(); buffer.resize(buffer.size() + sizeof(int16_t)); - val = dataio::h2le(val); + val = bigEndian ? dataio::h2be(val) : dataio::h2le(val); std::memcpy(buffer.data()+size, &val, sizeof(int16_t)); } -void ByteBuilder::putInt32(int32_t val) { +void ByteBuilder::putInt32(int32_t val, bool bigEndian) { size_t size = buffer.size(); buffer.resize(buffer.size() + sizeof(int32_t)); - val = dataio::h2le(val); + val = bigEndian ? dataio::h2be(val) : dataio::h2le(val); std::memcpy(buffer.data()+size, &val, sizeof(int32_t)); } -void ByteBuilder::putInt64(int64_t val) { +void ByteBuilder::putInt64(int64_t val, bool bigEndian) { size_t size = buffer.size(); buffer.resize(buffer.size() + sizeof(int64_t)); - val = dataio::h2le(val); + val = bigEndian ? dataio::h2be(val) : dataio::h2le(val); std::memcpy(buffer.data()+size, &val, sizeof(int64_t)); } -void ByteBuilder::putFloat32(float val) { +void ByteBuilder::putFloat32(float val, bool bigEndian) { int32_t i32_val; std::memcpy(&i32_val, &val, sizeof(int32_t)); - putInt32(i32_val); + putInt32(i32_val, bigEndian); } -void ByteBuilder::putFloat64(double val) { +void ByteBuilder::putFloat64(double val, bool bigEndian) { int64_t i64_val; std::memcpy(&i64_val, &val, sizeof(int64_t)); - putInt64(i64_val); + putInt64(i64_val, bigEndian); } void ByteBuilder::set(size_t position, ubyte val) { @@ -95,6 +99,10 @@ ByteReader::ByteReader(const ubyte* data) : data(data), size(4), pos(0) { size = getInt32(); } +ByteReader::ByteReader(const std::vector& data) + : data(data.data()), size(data.size()), pos(0) { +} + void ByteReader::checkMagic(const char* data, size_t size) { if (pos + size >= this->size) { throw std::runtime_error("invalid magic number"); @@ -129,45 +137,51 @@ ubyte ByteReader::peek() { return data[pos]; } -int16_t ByteReader::getInt16() { +int16_t ByteReader::getInt16(bool bigEndian) { if (pos + sizeof(int16_t) > size) { throw std::runtime_error("buffer underflow"); } int16_t value; std::memcpy(&value, data + pos, sizeof(int16_t)); pos += sizeof(int16_t); - return dataio::le2h(value); + return bigEndian ? dataio::be2h(value) : dataio::le2h(value); } -int32_t ByteReader::getInt32() { +int32_t ByteReader::getInt32(bool bigEndian) { if (pos + sizeof(int32_t) > size) { throw std::runtime_error("buffer underflow"); } int32_t value; std::memcpy(&value, data + pos, sizeof(int32_t)); pos += sizeof(int32_t); - return dataio::le2h(value); + return bigEndian ? dataio::be2h(value) : dataio::le2h(value); } -int64_t ByteReader::getInt64() { +int64_t ByteReader::getInt64(bool bigEndian) { if (pos + sizeof(int64_t) > size) { throw std::runtime_error("buffer underflow"); } int64_t value; std::memcpy(&value, data + pos, sizeof(int64_t)); pos += sizeof(int64_t); - return dataio::le2h(value); + return bigEndian ? dataio::be2h(value) : dataio::le2h(value); } -float ByteReader::getFloat32() { +float ByteReader::getFloat32(bool bigEndian) { int32_t i32_val = getInt32(); + if (bigEndian) { + i32_val = dataio::be2h(i32_val); + } float val; std::memcpy(&val, &i32_val, sizeof(float)); return val; } -double ByteReader::getFloat64() { +double ByteReader::getFloat64(bool bigEndian) { int64_t i64_val = getInt64(); + if (bigEndian) { + i64_val = dataio::be2h(i64_val); + } double val; std::memcpy(&val, &i64_val, sizeof(double)); return val; diff --git a/src/coders/byte_utils.hpp b/src/coders/byte_utils.hpp index 0532fe43a..8d98585a3 100644 --- a/src/coders/byte_utils.hpp +++ b/src/coders/byte_utils.hpp @@ -8,20 +8,23 @@ class ByteBuilder { std::vector buffer; public: + ByteBuilder() = default; + ByteBuilder(size_t size); + /// @brief Write one byte (8 bit unsigned integer) void put(ubyte b); /// @brief Write c-string (bytes array terminated with '\00') void putCStr(const char* str); /// @brief Write signed 16 bit little-endian integer - void putInt16(int16_t val); + void putInt16(int16_t val, bool bigEndian = false); /// @brief Write signed 32 bit integer - void putInt32(int32_t val); + void putInt32(int32_t val, bool bigEndian = false); /// @brief Write signed 64 bit integer - void putInt64(int64_t val); + void putInt64(int64_t val, bool bigEndian = false); /// @brief Write 32 bit floating-point number - void putFloat32(float val); + void putFloat32(float val, bool bigEndian = false); /// @brief Write 64 bit floating-point number - void putFloat64(double val); + void putFloat64(double val, bool bigEndian = false); /// @brief Write string (uint32 length + bytes) void put(const std::string& s); @@ -50,6 +53,7 @@ class ByteReader { public: ByteReader(const ubyte* data, size_t size); ByteReader(const ubyte* data); + ByteReader(const std::vector& data); void checkMagic(const char* data, size_t size); /// @brief Get N bytes @@ -59,15 +63,15 @@ class ByteReader { /// @brief Read one byte (unsigned 8 bit integer) without pointer move ubyte peek(); /// @brief Read signed 16 bit little-endian integer - int16_t getInt16(); + int16_t getInt16(bool bigEndian = false); /// @brief Read signed 32 bit little-endian integer - int32_t getInt32(); + int32_t getInt32(bool bigEndian = false); /// @brief Read signed 64 bit little-endian integer - int64_t getInt64(); + int64_t getInt64(bool bigEndian = false); /// @brief Read 32 bit floating-point number - float getFloat32(); + float getFloat32(bool bigEndian = false); /// @brief Read 64 bit floating-point number - double getFloat64(); + double getFloat64(bool bigEndian = false); /// @brief Read C-String const char* getCString(); /// @brief Read string with unsigned 32 bit number before (length) diff --git a/src/logic/scripting/lua/libs/api_lua.hpp b/src/logic/scripting/lua/libs/api_lua.hpp index 48ea6da1b..1b7d42d6b 100644 --- a/src/logic/scripting/lua/libs/api_lua.hpp +++ b/src/logic/scripting/lua/libs/api_lua.hpp @@ -19,6 +19,7 @@ extern const luaL_Reg base64lib[]; extern const luaL_Reg bjsonlib[]; extern const luaL_Reg blocklib[]; extern const luaL_Reg blockwrapslib[]; // gfx.blockwraps +extern const luaL_Reg byteutillib[]; extern const luaL_Reg cameralib[]; extern const luaL_Reg consolelib[]; extern const luaL_Reg corelib[]; diff --git a/src/logic/scripting/lua/libs/libbyteutil.cpp b/src/logic/scripting/lua/libs/libbyteutil.cpp new file mode 100644 index 000000000..e4b20d8cf --- /dev/null +++ b/src/logic/scripting/lua/libs/libbyteutil.cpp @@ -0,0 +1,194 @@ +#include "api_lua.hpp" + +#include + +#include "coders/byte_utils.hpp" +#include "util/data_io.hpp" + +static size_t calc_size(const char* format) { + size_t outSize = 0; + for (size_t i = 0; format[i]; i++) { + switch (format[i]) { + case 'b': + case 'B': + outSize += 1; + break; + case 'h': + case 'H': + outSize += 2; + break; + case 'i': + case 'I': + case 'f': + outSize += 4; + break; + case 'd': + case 'l': + case 'L': + outSize += 8; + break; + default: + break; + } + } + return outSize; +} + +static int pack(lua::State* L, const char* format, bool usetable) { + size_t outSize = calc_size(format); + bool bigEndian = false; + + ByteBuilder builder(outSize); + int index = 2; + for (int i = 0; format[i]; i++) { + switch (format[i]) { + case 'b': + builder.put(lua::tointeger(L, index) & 0xFF); + break; + case 'h': + builder.putInt16(lua::tointeger(L, index), bigEndian); + break; + case 'H': + builder.putInt16(lua::tointeger(L, index) & 0xFFFFU, bigEndian); + break; + case 'i': + builder.putInt32(lua::tointeger(L, index), bigEndian); + break; + case 'I': + builder.putInt32(lua::tointeger(L, index) & 0xFFFFFFFFULL, bigEndian); + break; + case 'l': + case 'L': + builder.putInt64(lua::tointeger(L, index), bigEndian); + break; + case 'f': + builder.putFloat32(lua::tonumber(L, index), bigEndian); + break; + case 'd': + builder.putFloat64(lua::tonumber(L, index), bigEndian); + break; + case '!': + case '>': + bigEndian = true; + continue; + case '<': + bigEndian = false; + continue; + case '@': + case '=': + bigEndian = dataio::is_big_endian(); + continue; + default: + continue; + } + index++; + } + if (usetable) { + lua::createtable(L, outSize, 0); + const ubyte* data = builder.data(); + for (size_t i = 0; i < outSize; i++) { + lua::pushinteger(L, data[i]); + lua::rawseti(L, i + 1); + } + return 1; + } else { + return lua::newuserdata(L, builder.build()); + } +} + +static int count_elements(const char* format) { + int count = 0; + for (size_t i = 0; format[i]; i++) { + switch (format[i]) { + case 'b': + case 'B': + case 'h': + case 'H': + case 'i': + case 'I': + case 'l': + case 'L': + case 'f': + case 'd': + count++; + break; + default: + break; + } + } + return count; +} + +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); + bool bigEndian = false; + + int index = 1; + lua::createtable(L, count, 0); + for (size_t i = 0; format[i]; i++) { + switch (format[i]) { + case 'b': + lua::pushinteger(L, reader.get()); + break; + case 'h': + lua::pushinteger(L, reader.getInt16(bigEndian)); + break; + case 'H': + lua::pushinteger(L, reader.getInt16(bigEndian) & 0xFFFF); + break; + case 'i': + lua::pushinteger(L, reader.getInt32(bigEndian)); + break; + case 'I': + lua::pushinteger(L, reader.getInt32(bigEndian) & 0xFFFFFFFF); + break; + case 'l': + lua::pushinteger(L, reader.getInt64(bigEndian)); + break; + case 'L': + lua::pushinteger(L, reader.getInt64(bigEndian)); + break; + case 'f': + lua::pushnumber(L, reader.getFloat32(bigEndian)); + break; + case 'd': + lua::pushnumber(L, reader.getFloat64(bigEndian)); + break; + case '!': + case '>': + bigEndian = true; + continue; + case '<': + bigEndian = false; + continue; + case '@': + case '=': + bigEndian = dataio::is_big_endian(); + continue; + default: + continue; + } + lua::rawseti(L, index++); + } + return 1; +} + +static int l_pack(lua::State* L) { + const char* format = lua::require_string(L, 1); + return pack(L, format, false); +} + +static int l_tpack(lua::State* L) { + const char* format = lua::require_string(L, 1); + return pack(L, format, true); +} + +const luaL_Reg byteutillib[] = { + {"pack", l_pack}, + {"tpack", l_tpack}, + {"unpack", l_unpack}, + {NULL, NULL} +}; diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index e9b28ff6a..8d40221cc 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -43,6 +43,7 @@ static void create_libs(State* L, StateType stateType) { openlib(L, "base64", base64lib); openlib(L, "bjson", bjsonlib); openlib(L, "block", blocklib); + openlib(L, "byteutil", byteutillib); openlib(L, "core", corelib); openlib(L, "file", filelib); openlib(L, "generation", generationlib); diff --git a/src/logic/scripting/lua/lua_util.hpp b/src/logic/scripting/lua/lua_util.hpp index f9da3b219..057c706d7 100644 --- a/src/logic/scripting/lua/lua_util.hpp +++ b/src/logic/scripting/lua/lua_util.hpp @@ -720,4 +720,15 @@ namespace lua { } } } + + inline std::vector require_bytearray(lua::State* L, int idx) { + if (auto* bytearray = lua::touserdata(L, idx)) { + return bytearray->data(); + } else if (lua::istable(L, idx)) { + std::vector bytes; + read_bytes_from_table(L, idx, bytes); + return bytes; + } + throw std::runtime_error("bytearray expected"); + } }