Final refactor of basebuffer

This commit is contained in:
2026-02-22 23:56:48 -05:00
parent cbbe475f65
commit 8889a36ba3
8 changed files with 162 additions and 128 deletions

View File

@@ -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;

View File

@@ -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<std::string>;
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; }

View File

@@ -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;

View File

@@ -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

View File

@@ -255,6 +255,7 @@ class ReplayLogfile : public std::ifstream {
using std::ifstream::ifstream;
using DD = DataDeserializer<ReplayLogfile>;
public:
using string_type = std::string;
void read_bytes_into(char *n, size_t size) {
read(n, size);
if (!good()) {

View File

@@ -248,6 +248,9 @@ using LuaValue = BaseLuaValue<eng::string>;
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<StreamBufferConfig>
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.

View File

@@ -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<util::DXYZ> 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);
}
}

View File

@@ -14,6 +14,7 @@
#include <cassert>
#include <cstring>
#include <string_view>
#include <type_traits>
///////////////////////////////////////////////////////////////
//
@@ -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<class FV>
void write_xyz(const FV &v) {
static_assert(std::is_same_v<FV, typename OutputDevice::fvector_type>);
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<class DV>
void write_dxyz(const DV &v) {
static_assert(std::is_same_v<DV, typename OutputDevice::dvector_type>);
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<class LV>
void write_lua_value(const LV &sd) {
static_assert(std::is_same_v<LV, typename OutputDevice::luavalue_type>);
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<class InputDevice, class StringType = std::string>
template<class InputDevice>
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<class LV>
void read_lua_value(LV *result) {
static_assert(std::is_same_v<LV, typename InputDevice::luavalue_type>);
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<BaseBuffer<BaseBufferConfig>>;
using DD = DataDeserializer<BaseBuffer<BaseBufferConfig>, string_type>;
using DD = DataDeserializer<BaseBuffer<BaseBufferConfig>>;
void init(bool fixed, bool owned, char *buf, int64_t size) {
BaseBufferConfig::clear_error_flags();
@@ -442,45 +533,16 @@ public:
write_bytes(s.data(), s.size());
}
// Write methods — delegate to DataSerializer.
// Copy the contents of this buffer into another buffer.
//
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.
//
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<class STRING>
void write_simple_dynamic(const BaseLuaValue<STRING> &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.
//
@@ -562,33 +650,6 @@ public:
return read_string_view_limit(0x1000000);
}
// Read a LuaValueType
//
LuaValueType read_simple_dynamic_tag() {
return LuaValueType(read_uint8());
}
// Read a BaseLuaValue
//
template<class STRING>
void read_simple_dynamic(BaseLuaValue<STRING> *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,
// returns a block of text that ends in newline.