From 8889a36ba3caef9fd1fa261125046f1fbbf77f8f Mon Sep 17 00:00:00 2001 From: jyelon Date: Sun, 22 Feb 2026 23:56:48 -0500 Subject: [PATCH] Final refactor of basebuffer --- Source/Integration/LuaCall.cpp | 16 +-- Source/Integration/StringDecoder.h | 3 + luprex/cpp/core/animqueue.cpp | 10 +- luprex/cpp/core/animqueue.hpp | 2 +- luprex/cpp/core/drivenengine.cpp | 1 + luprex/cpp/core/streambuffer.hpp | 37 +----- luprex/cpp/core/world-core.cpp | 16 +-- luprex/ext/base-buffer.hpp | 205 +++++++++++++++++++---------- 8 files changed, 162 insertions(+), 128 deletions(-) diff --git a/Source/Integration/LuaCall.cpp b/Source/Integration/LuaCall.cpp index acfd1e59..e84d667a 100644 --- a/Source/Integration/LuaCall.cpp +++ b/Source/Integration/LuaCall.cpp @@ -288,7 +288,7 @@ void UlxLuaCallLibrary::LuaCallArgument_string(UObject *context, const FString & ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); FlxStreamBuffer &sb = mode->GetLuaCallBuffer(); if (NotInitialized(sb)) return; - sb.write_simple_dynamic_tag(LuaValueType::STRING); + sb.write_lua_value_type(LuaValueType::STRING); sb.write_string(pstring); } @@ -303,7 +303,7 @@ void UlxLuaCallLibrary::LuaCallArgument_name(UObject *context, const FName &pnam ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); FlxStreamBuffer &sb = mode->GetLuaCallBuffer(); if (NotInitialized(sb)) return; - sb.write_simple_dynamic_tag(LuaValueType::TOKEN); + sb.write_lua_value_type(LuaValueType::TOKEN); sb.write_string(namestr); } @@ -311,7 +311,7 @@ void UlxLuaCallLibrary::LuaCallArgument_float(UObject *context, double pfloat) { ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); FlxStreamBuffer &sb = mode->GetLuaCallBuffer(); if (NotInitialized(sb)) return; - sb.write_simple_dynamic_tag(LuaValueType::NUMBER); + sb.write_lua_value_type(LuaValueType::NUMBER); sb.write_double(pfloat); } @@ -319,7 +319,7 @@ void UlxLuaCallLibrary::LuaCallArgument_int(UObject *context, int value) { ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); FlxStreamBuffer &sb = mode->GetLuaCallBuffer(); if (NotInitialized(sb)) return; - sb.write_simple_dynamic_tag(LuaValueType::NUMBER); + sb.write_lua_value_type(LuaValueType::NUMBER); sb.write_double(value); } @@ -327,7 +327,7 @@ void UlxLuaCallLibrary::LuaCallArgument_vector(UObject *context, const FVector & ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); FlxStreamBuffer &sb = mode->GetLuaCallBuffer(); if (NotInitialized(sb)) return; - sb.write_simple_dynamic_tag(LuaValueType::VECTOR); + sb.write_lua_value_type(LuaValueType::VECTOR); sb.write_fvector(pvector); } @@ -335,7 +335,7 @@ void UlxLuaCallLibrary::LuaCallArgument_vector2d(UObject *context, const FVector ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); FlxStreamBuffer &sb = mode->GetLuaCallBuffer(); if (NotInitialized(sb)) return; - sb.write_simple_dynamic_tag(LuaValueType::VECTOR); + sb.write_lua_value_type(LuaValueType::VECTOR); sb.write_double(pvector.X); sb.write_double(pvector.Y); sb.write_double(0.0); @@ -345,7 +345,7 @@ void UlxLuaCallLibrary::LuaCallArgument_boolean(UObject *context, bool pbool) { ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); FlxStreamBuffer &sb = mode->GetLuaCallBuffer(); if (NotInitialized(sb)) return; - sb.write_simple_dynamic_tag(LuaValueType::BOOLEAN); + sb.write_lua_value_type(LuaValueType::BOOLEAN); sb.write_bool(pbool); } @@ -375,7 +375,7 @@ bool UlxLuaValues::Initialize(std::string_view data) while (!Decoder.empty()) { - LuaValueType Tag = Decoder.read_simple_dynamic_tag(); + LuaValueType Tag = Decoder.read_lua_value_type(); int64 Pos = Decoder.total_reads(); ElxLuaValueType Type; diff --git a/Source/Integration/StringDecoder.h b/Source/Integration/StringDecoder.h index 6f660a02..c7398b67 100644 --- a/Source/Integration/StringDecoder.h +++ b/Source/Integration/StringDecoder.h @@ -10,6 +10,9 @@ private: bool err_integer_truncated_; public: using string_type = std::string; + using fvector_type = FVector; + using dvector_type = FVector; + using luavalue_type = BaseLuaValue; void *basebuffer_malloc(size_t size) { return malloc(size); } void basebuffer_free(void *p) { free(p); } void clear_error_flags() { err_eof_on_read_ = err_string_too_long_ = err_integer_truncated_ = false; } diff --git a/luprex/cpp/core/animqueue.cpp b/luprex/cpp/core/animqueue.cpp index 8ba83f5d..c0ef72ec 100644 --- a/luprex/cpp/core/animqueue.cpp +++ b/luprex/cpp/core/animqueue.cpp @@ -130,7 +130,7 @@ eng::string AnimStepEditor::encode() const { const AnimValue &value = pair.second; sb.write_string(name); sb.write_bool(value.persistent); - sb.write_simple_dynamic(value); + sb.write_lua_value(value); } return eng::string(sb.view()); } @@ -142,7 +142,7 @@ void AnimStepEditor::decode(std::string_view s) { eng::string name = sb.read_string(); AnimValue &value = map_[name]; value.persistent = sb.read_bool(); - sb.read_simple_dynamic(&value); + sb.read_lua_value(&value); } } @@ -156,9 +156,9 @@ void AnimStepEditor::decode_persistent(std::string_view s) { if (persistent) { AnimValue &value = map_[name]; value.persistent = persistent; - sb.read_simple_dynamic(&value); + sb.read_lua_value(&value); } else { - sb.read_simple_dynamic(&dummy); + sb.read_lua_value(&dummy); } } } @@ -349,7 +349,7 @@ void AnimCoreState::decode(std::string_view s) { while (!sb.empty()) { eng::string name = sb.read_string(); bool persistent = sb.read_bool(); - sb.read_simple_dynamic(&value); + sb.read_lua_value(&value); if (persistent) { if ((name == "xyz") && (value.type == LuaValueType::VECTOR)) xyz = util::DXYZ(value.x, value.y, value.z); if ((name == "plane") && (value.type == LuaValueType::STRING)) plane = value.s; diff --git a/luprex/cpp/core/animqueue.hpp b/luprex/cpp/core/animqueue.hpp index 2d58c02b..e6c14dc6 100644 --- a/luprex/cpp/core/animqueue.hpp +++ b/luprex/cpp/core/animqueue.hpp @@ -79,7 +79,7 @@ // for all key-value pairs do: // write_string(key) // write_bool(persistent) -// write_simple_dynamic(value) +// write_lua_value(value) // // The encoded string produced by the loop above is called an "encstep". // That's short for "encoded animation step". The encstep has a hash diff --git a/luprex/cpp/core/drivenengine.cpp b/luprex/cpp/core/drivenengine.cpp index f4832267..f8b0099b 100644 --- a/luprex/cpp/core/drivenengine.cpp +++ b/luprex/cpp/core/drivenengine.cpp @@ -255,6 +255,7 @@ class ReplayLogfile : public std::ifstream { using std::ifstream::ifstream; using DD = DataDeserializer; public: + using string_type = std::string; void read_bytes_into(char *n, size_t size) { read(n, size); if (!good()) { diff --git a/luprex/cpp/core/streambuffer.hpp b/luprex/cpp/core/streambuffer.hpp index 71271dd7..cad543ad 100644 --- a/luprex/cpp/core/streambuffer.hpp +++ b/luprex/cpp/core/streambuffer.hpp @@ -248,6 +248,9 @@ using LuaValue = BaseLuaValue; class StreamBufferConfig { public: using string_type = eng::string; + using fvector_type = util::XYZ; + using dvector_type = util::DXYZ; + using luavalue_type = LuaValue; void *basebuffer_malloc(size_t size) { return eng::malloc(size); } void basebuffer_free(void *p) { eng::free(p); } void clear_error_flags() { } @@ -260,50 +263,16 @@ class StreamBuffer : public eng::nevernew, public BaseBuffer public: using BaseBuffer::BaseBuffer; - void write_xyz(const util::XYZ &xyz) { - write_float(xyz.x); - write_float(xyz.y); - write_float(xyz.z); - } - - void write_dxyz(const util::DXYZ &xyz) { - write_double(xyz.x); - write_double(xyz.y); - write_double(xyz.z); - } - void write_hashvalue(const util::HashValue &h) { write_uint64(h.first); write_uint64(h.second); } - util::XYZ read_xyz() { - float x = read_float(); - float y = read_float(); - float z = read_float(); - return util::XYZ(x, y, z); - } - - util::DXYZ read_dxyz() { - double x = read_double(); - double y = read_double(); - double z = read_double(); - return util::DXYZ(x, y, z); - } - util::HashValue read_hashvalue() { uint64_t f = read_uint64(); uint64_t s = read_uint64(); return util::HashValue(f, s); } - - bool contents_equal(const StreamBuffer *sb) { - return view() == sb->view(); - } - - void copy_into(StreamBuffer *sb) { - sb->write_bytes(view()); - } }; // Use a streambuffer as a lua_writer. diff --git a/luprex/cpp/core/world-core.cpp b/luprex/cpp/core/world-core.cpp index 3b3b2cf3..b0b693c8 100644 --- a/luprex/cpp/core/world-core.cpp +++ b/luprex/cpp/core/world-core.cpp @@ -11,7 +11,7 @@ // Read a serialized LuaValue value from the // streambuffer and push it onto the lua stack. void push_simple_dynamic(lua_State *L, StreamBuffer *sb) { - LuaValueType type = sb->read_simple_dynamic_tag(); + LuaValueType type = sb->read_lua_value_type(); switch (type) { case LuaValueType::STRING: { std::string_view s = sb->read_string_view(); @@ -56,25 +56,25 @@ void push_simple_dynamic(lua_State *L, StreamBuffer *sb) { bool encode_simple_dynamic(LuaCoreStack &LS, LuaSlot &slot, StreamBuffer *sb) { switch (LS.type(slot)) { case LUA_TSTRING: - sb->write_simple_dynamic_tag(LuaValueType::STRING); + sb->write_lua_value_type(LuaValueType::STRING); sb->write_string(LS.ckstringview(slot)); return true; case LUA_TLIGHTUSERDATA: - sb->write_simple_dynamic_tag(LuaValueType::TOKEN); + sb->write_lua_value_type(LuaValueType::TOKEN); sb->write_string(LS.cktoken(slot).str()); return true; case LUA_TNUMBER: - sb->write_simple_dynamic_tag(LuaValueType::NUMBER); + sb->write_lua_value_type(LuaValueType::NUMBER); sb->write_double(LS.cknumber(slot)); return true; case LUA_TBOOLEAN: - sb->write_simple_dynamic_tag(LuaValueType::BOOLEAN); + sb->write_lua_value_type(LuaValueType::BOOLEAN); sb->write_bool(LS.ckboolean(slot)); return true; case LUA_TTABLE: { std::optional xyz = LS.tryxyz(slot); if (!xyz.has_value()) return false; - sb->write_simple_dynamic_tag(LuaValueType::VECTOR); + sb->write_lua_value_type(LuaValueType::VECTOR); sb->write_dxyz(xyz.value()); return true; } @@ -480,7 +480,7 @@ void World::probe_lua_call(int64_t actor_id, int64_t place_id, std::string_view // return value of the function. // int64_t rv_base = retvals->total_writes(); - retvals->write_simple_dynamic_tag(LuaValueType::STRING); + retvals->write_lua_value_type(LuaValueType::STRING); retvals->write_string(msg); if (msg.empty()) { @@ -509,7 +509,7 @@ void World::probe_lua_call(int64_t actor_id, int64_t place_id, std::string_view if (!ok) { msg = util::ss("Lua function ",classname,".",funcname," returned a non-serializable value"); retvals->unwrite_to(rv_base); - retvals->write_simple_dynamic_tag(LuaValueType::STRING); + retvals->write_lua_value_type(LuaValueType::STRING); retvals->write_string(msg); } } diff --git a/luprex/ext/base-buffer.hpp b/luprex/ext/base-buffer.hpp index 4dfeaea2..6f935011 100644 --- a/luprex/ext/base-buffer.hpp +++ b/luprex/ext/base-buffer.hpp @@ -14,6 +14,7 @@ #include #include #include +#include /////////////////////////////////////////////////////////////// // @@ -109,12 +110,15 @@ struct BaseLuaValue { // // To serialize, first construct a DataSerializer, passing // in a pointer to an output device. An output device is -// any class that has these methods: -// -// void write_bytes(char *data, size_t len); +// any class that has these methods and type aliases: // +// void write_bytes(const char *data, size_t len); // void raise_integer_truncated(); // +// using fvector_type = ...; // needed for write_xyz +// using dvector_type = ...; // needed for write_dxyz +// using luavalue_type = ...; // needed for write_lua_value +// // After constructing the DataSerializer, call write_int, // write_float, write_string, or the like. The data will be // written to the output device using write_bytes. If @@ -176,6 +180,44 @@ public: write_length(s.size()); output_->write_bytes(s.data(), s.size()); } + + void write_lua_value_type(LuaValueType tag) { + write_uint8(uint8_t(tag)); + } + + template + void write_xyz(const FV &v) { + static_assert(std::is_same_v); + if constexpr (requires { v.x; }) { + write_float(v.x); write_float(v.y); write_float(v.z); + } else { + write_float(v.X); write_float(v.Y); write_float(v.Z); + } + } + + template + void write_dxyz(const DV &v) { + static_assert(std::is_same_v); + if constexpr (requires { v.x; }) { + write_double(v.x); write_double(v.y); write_double(v.z); + } else { + write_double(v.X); write_double(v.Y); write_double(v.Z); + } + } + + template + void write_lua_value(const LV &sd) { + static_assert(std::is_same_v); + write_lua_value_type(sd.type); + switch(sd.type) { + case LuaValueType::STRING: write_string(sd.s); break; + case LuaValueType::TOKEN: write_string(sd.s); break; + case LuaValueType::NUMBER: write_double(sd.x); break; + case LuaValueType::BOOLEAN: write_bool(sd.x == 1.0); break; + case LuaValueType::VECTOR: write_double(sd.x); write_double(sd.y); write_double(sd.z); break; + default: assert(false); + } + } }; @@ -189,11 +231,16 @@ public: // // To deserialize, first construct a DataDeserializer, passing // in a pointer to an input device. An input device is -// any class that has these methods: +// any class that has these methods and type aliases: // // void read_bytes_into(char *data, size_t len); // void raise_string_too_long(); // +// using string_type = ...; // needed for read_string +// using fvector_type = ...; // needed for read_xyz +// using dvector_type = ...; // needed for read_dxyz +// using luavalue_type = ...; // needed for read_lua_value +// // After constructing the DataDeserializer, call read_int, // read_float, read_string, or the like. The data will be // read from the input device using read_bytes_into. If @@ -208,7 +255,7 @@ public: // /////////////////////////////////////////////////////////////// -template +template class DataDeserializer { private: InputDevice *input_; @@ -243,20 +290,57 @@ public: return len; } - StringType read_string_limit(uint64_t limit) { + auto read_string_limit(uint64_t limit) { + using ST = typename InputDevice::string_type; size_t len = read_length(); if (len > limit) { input_->raise_string_too_long(); len = 0; } - StringType result(len, ' '); + ST result(len, ' '); input_->read_bytes_into(&(result[0]), len); return result; } - StringType read_string() { + auto read_string() { return read_string_limit(0x1000000); // 16MB limit default } + + auto read_xyz() { + using FV = typename InputDevice::fvector_type; + float x = read_float(), y = read_float(), z = read_float(); + return FV(x, y, z); + } + + auto read_dxyz() { + using DV = typename InputDevice::dvector_type; + double x = read_double(), y = read_double(), z = read_double(); + return DV(x, y, z); + } + + LuaValueType read_lua_value_type() { + return LuaValueType(read_uint8()); + } + + template + void read_lua_value(LV *result) { + static_assert(std::is_same_v); + LuaValueType type = read_lua_value_type(); + switch (type) { + case LuaValueType::STRING: result->set_string(read_string()); break; + case LuaValueType::TOKEN: result->set_token(read_string()); break; + case LuaValueType::NUMBER: result->set_number(read_double()); break; + case LuaValueType::BOOLEAN: result->set_boolean(read_bool()); break; + case LuaValueType::VECTOR: { + double x=read_double(); + double y=read_double(); + double z=read_double(); + result->set_vector(x,y,z); + break; + } + default: result->set_uninitialized(); break; + } + } }; /////////////////////////////////////////////////////////////// @@ -265,9 +349,13 @@ public: // // You must supply a BaseBufferConfig which must define these: // -// using string_type = std::string; // or compatible +// using string_type = ...; // string type for read_string +// using fvector_type = ...; // vector type for read/write_xyz +// using dvector_type = ...; // vector type for read/write_dxyz +// using luavalue_type = ...; // lua value type for read/write_lua_value // void *basebuffer_malloc(size_t size); // void basebuffer_free(void *data); +// void clear_error_flags(); // void raise_eof_on_read(); // void raise_string_too_long(); // void raise_integer_truncated(); @@ -297,10 +385,13 @@ private: public: using string_type = typename BaseBufferConfig::string_type; + using fvector_type = typename BaseBufferConfig::fvector_type; + using dvector_type = typename BaseBufferConfig::dvector_type; + using luavalue_type = typename BaseBufferConfig::luavalue_type; private: using DS = DataSerializer>; - using DD = DataDeserializer, string_type>; + using DD = DataDeserializer>; void init(bool fixed, bool owned, char *buf, int64_t size) { BaseBufferConfig::clear_error_flags(); @@ -441,46 +532,17 @@ public: void write_bytes(std::string_view s) { write_bytes(s.data(), s.size()); } - - // Write methods — delegate to DataSerializer. - // - void write_uint8(uint64_t data) { DS(this).write_uint8(data); } - void write_uint16(uint64_t data) { DS(this).write_uint16(data); } - void write_uint32(uint64_t data) { DS(this).write_uint32(data); } - void write_uint64(uint64_t data) { DS(this).write_uint64(data); } - void write_int8(int64_t data) { DS(this).write_int8(data); } - void write_int16(int64_t data) { DS(this).write_int16(data); } - void write_int32(int64_t data) { DS(this).write_int32(data); } - void write_int64(int64_t data) { DS(this).write_int64(data); } - void write_bool(bool b) { DS(this).write_bool(b); } - void write_char(char c) { DS(this).write_char(c); } - void write_float(float arg) { DS(this).write_float(arg); } - void write_double(double arg) { DS(this).write_double(arg); } - void write_length(size_t len) { DS(this).write_length(len); } - void write_string(std::string_view s) { DS(this).write_string(s); } - // Write a LuaValueType. + // Copy the contents of this buffer into another buffer. // - void write_simple_dynamic_tag(LuaValueType tag) { - write_uint8(uint8_t(tag)); + void copy_into(BaseBuffer *sb) { + sb->write_bytes(view()); } - // Write a BaseLuaValue value. + // Check if the contents of this buffer are equal to another. // - // This works regardless of what kind of data is present in the - // BaseLuaValue. - // - template - void write_simple_dynamic(const BaseLuaValue &sd) { - write_simple_dynamic_tag(sd.type); - switch(sd.type) { - case LuaValueType::STRING: write_string(sd.s); break; - case LuaValueType::TOKEN: write_string(sd.s); break; - case LuaValueType::NUMBER: write_double(sd.x); break; - case LuaValueType::BOOLEAN: write_bool(sd.x == 1.0); break; - case LuaValueType::VECTOR: write_double(sd.x); write_double(sd.y); write_double(sd.z); break; - default: assert(false); - } + bool contents_equal(const BaseBuffer *sb) const { + return view() == sb->view(); } // Read bytes into a caller-supplied buffer. @@ -517,6 +579,28 @@ public: return data; } + + // Write methods — delegate to DataSerializer. + // + void write_uint8(uint64_t data) { DS(this).write_uint8(data); } + void write_uint16(uint64_t data) { DS(this).write_uint16(data); } + void write_uint32(uint64_t data) { DS(this).write_uint32(data); } + void write_uint64(uint64_t data) { DS(this).write_uint64(data); } + void write_int8(int64_t data) { DS(this).write_int8(data); } + void write_int16(int64_t data) { DS(this).write_int16(data); } + void write_int32(int64_t data) { DS(this).write_int32(data); } + void write_int64(int64_t data) { DS(this).write_int64(data); } + void write_bool(bool b) { DS(this).write_bool(b); } + void write_char(char c) { DS(this).write_char(c); } + void write_float(float arg) { DS(this).write_float(arg); } + void write_double(double arg) { DS(this).write_double(arg); } + void write_length(size_t len) { DS(this).write_length(len); } + void write_string(std::string_view s) { DS(this).write_string(s); } + void write_lua_value_type(LuaValueType tag) { DS(this).write_lua_value_type(tag); } + void write_xyz(const fvector_type &v) { DS(this).write_xyz(v); } + void write_dxyz(const dvector_type &v) { DS(this).write_dxyz(v); } + void write_lua_value(const luavalue_type &sd) { DS(this).write_lua_value(sd); } + // Read methods — delegate to DataDeserializer. // uint8_t read_uint8() { return DD(this).read_uint8(); } @@ -534,6 +618,10 @@ public: size_t read_length() { return DD(this).read_length(); } string_type read_string_limit(uint64_t limit) { return DD(this).read_string_limit(limit); } string_type read_string() { return DD(this).read_string(); } + LuaValueType read_lua_value_type() { return DD(this).read_lua_value_type(); } + fvector_type read_xyz() { return DD(this).read_xyz(); } + dvector_type read_dxyz() { return DD(this).read_dxyz(); } + void read_lua_value(luavalue_type *result) { DD(this).read_lua_value(result); } // Read a string as a string_view. // @@ -561,33 +649,6 @@ public: std::string_view read_string_view() { return read_string_view_limit(0x1000000); } - - // Read a LuaValueType - // - LuaValueType read_simple_dynamic_tag() { - return LuaValueType(read_uint8()); - } - - // Read a BaseLuaValue - // - template - void read_simple_dynamic(BaseLuaValue *result) { - LuaValueType type = read_simple_dynamic_tag(); - switch (type) { - case LuaValueType::STRING: result->set_string(read_string()); break; - case LuaValueType::TOKEN: result->set_token(read_string()); break; - case LuaValueType::NUMBER: result->set_number(read_double()); break; - case LuaValueType::BOOLEAN: result->set_boolean(read_bool()); break; - case LuaValueType::VECTOR: { - double x=read_double(); - double y=read_double(); - double z=read_double(); - result->set_vector(x,y,z); - break; - } - default: result->set_uninitialized(); break; - } - } // Attempt to do a "readline". If there is no newline in // the buffer, returns empty string. If there is a newline,