More refactors in basebuffer

This commit is contained in:
2026-02-22 20:59:02 -05:00
parent bd2f927d6f
commit 5c258be507
3 changed files with 75 additions and 64 deletions

View File

@@ -3,13 +3,13 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "lpx-basebuffer.hpp" #include "lpx-basebuffer.hpp"
class FlxStreamBufferCore { class FlxStreamBufferConfig {
private: private:
bool err_eof_on_read_; bool err_eof_on_read_;
bool err_string_too_long_; bool err_string_too_long_;
bool err_integer_truncated_; bool err_integer_truncated_;
protected: protected:
using string_type = std::string;
void *basebuffer_malloc(size_t size) { return malloc(size); } void *basebuffer_malloc(size_t size) { return malloc(size); }
void basebuffer_free(void *p) { free(p); } void basebuffer_free(void *p) { free(p); }
void clear_error_flags() { err_eof_on_read_ = err_string_too_long_ = err_integer_truncated_ = false; } void clear_error_flags() { err_eof_on_read_ = err_string_too_long_ = err_integer_truncated_ = false; }
@@ -24,7 +24,7 @@ public:
bool any_error() const { return err_eof_on_read_ || err_string_too_long_ || err_integer_truncated_; } bool any_error() const { return err_eof_on_read_ || err_string_too_long_ || err_integer_truncated_; }
}; };
class FlxStreamBuffer : public BaseBuffer<FlxStreamBufferCore, std::string> { class FlxStreamBuffer : public BaseBuffer<FlxStreamBufferConfig> {
public: public:
using BaseBuffer::BaseBuffer; using BaseBuffer::BaseBuffer;
using BaseBuffer::write_string; using BaseBuffer::write_string;

View File

@@ -245,8 +245,9 @@ public:
using LuaValue = BaseLuaValue<eng::string>; using LuaValue = BaseLuaValue<eng::string>;
class StreamBufferCore { class StreamBufferConfig {
protected: protected:
using string_type = eng::string;
void *basebuffer_malloc(size_t size) { return eng::malloc(size); } void *basebuffer_malloc(size_t size) { return eng::malloc(size); }
void basebuffer_free(void *p) { eng::free(p); } void basebuffer_free(void *p) { eng::free(p); }
void clear_error_flags() { } void clear_error_flags() { }
@@ -255,7 +256,7 @@ protected:
void raise_integer_truncated() { throw StreamIntegerTruncated(); } void raise_integer_truncated() { throw StreamIntegerTruncated(); }
}; };
class StreamBuffer : public eng::nevernew, public BaseBuffer<StreamBufferCore, eng::string> { class StreamBuffer : public eng::nevernew, public BaseBuffer<StreamBufferConfig> {
public: public:
using BaseBuffer::BaseBuffer; using BaseBuffer::BaseBuffer;

View File

@@ -103,7 +103,10 @@ struct BaseLuaValue {
// //
// BaseWriteMethods // BaseWriteMethods
// //
// This base class provides the following methods: // Suppose you've written a class MyWriter that contains a
// write_bytes method. Now you want to add write_int,
// write_string, and so forth. By deriving from
// BaseWriteMethods, you add all of the following methods:
// //
// void write_uint8(uint64_t data) // void write_uint8(uint64_t data)
// void write_uint16(uint64_t data) // void write_uint16(uint64_t data)
@@ -119,15 +122,21 @@ struct BaseLuaValue {
// void write_length(size_t data) // void write_length(size_t data)
// void write_string(std::string_view data) // void write_string(std::string_view data)
// //
// You should derive from BaseWriteMethods using the CRTP pattern: // Here is how you should derive from BaseWriteMethods:
// //
// class DerivedWriter : public BaseWriteMethods<DerivedWriter> // class MyWriter : public BaseWriteMethods<MyWriter>
// //
// You must provide two methods in the derived class: // Your class MyWriter must implement these two methods:
// //
// write_bytes(const char *n, size_t size) // write_bytes(const char *n, size_t size)
// raise_integer_truncated() // raise_integer_truncated()
// //
// If you call write_uint8(1000), that is an error, because
// 1000 doesn't fit in a uint8. So, write_uint8 will call
// raise_integer_truncated. That function can throw an
// exception, set a flag, or otherwise handle the error -
// you can implement it to do whatever you wish.
//
/////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////
template<class Derived> template<class Derived>
@@ -181,7 +190,10 @@ public:
// //
// BaseReadMethods // BaseReadMethods
// //
// This base class provides the following methods: // Suppose you've written a class MyReader that contains a
// read_bytes method. Now you want to add read_int,
// read_string, and so forth. By deriving from
// BaseReadMethods, you add all of the following methods:
// //
// uint8_t read_uint8(); // uint8_t read_uint8();
// uint16_t read_uint16(); // uint16_t read_uint16();
@@ -196,38 +208,40 @@ public:
// float read_float(); // float read_float();
// double read_double(); // double read_double();
// size_t read_length(); // size_t read_length();
// String read_string_limit(uint64_t size); // string read_string_limit(uint64_t size);
// String read_string(); // string read_string();
// //
// You should derive from BaseReadMethods using the CRTP pattern: // Here is how you should derive from BaseReadMethods:
// //
// class DerivedReader : public BaseReadMethods<DerivedReader> // class MyReader : public BaseReadMethods<MyReader>
// //
// The derived class must provide: // Your class MyReader must implement these:
// //
// using read_string_type = std::string; // or compatible // using string_type = std::string; // or compatible
// void read_bytes_into(char *n, size_t size) // void read_bytes_into(char *n, size_t size)
// void raise_string_too_long(); // void raise_string_too_long();
// //
// Error Handling: // The read_string function will return a string.
// Usually, you want it to return std::string, but
// you might be using some other string type. Supply
// string_type = std::string or any other type
// with a (bytes, len) constructor, and read_string
// will return that type.
// //
// It is up to the derived class whether it wants // If you call read_string_limit(100), and this finds
// to report errors using exceptions or flags. // a string of length 200 in the incoming data, then
// // read_string_limit will call raise_string_too_long.
// If read_bytes_into discovers there's not enough bytes, // That function can throw an exception, set a flag,
// there are two valid options: throw an exception, OR, // or otherwise handle the error - you can implement it
// set an error flag and fill the buffer with zeros. // to do whatever you wish.
//
// If read_string discovers that the string is longer than
// the allowed limit, it will call raise_string_too_long.
// This function may either throw an exception, or set an
// error flag.
// //
/////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////
template<class Derived> template<class Derived>
class BaseReadMethods { class BaseReadMethods {
protected: protected:
using string_type = typename Derived::string_type;
template<class T> template<class T>
T read_value_core() { T read_value_core() {
T result; T result;
@@ -254,50 +268,48 @@ public:
size_t read_length() { size_t read_length() {
uint64_t len = read_uint8(); uint64_t len = read_uint8();
if (len == 255) { if (len == 255) len = read_uint64();
len = read_uint64();
}
return len; return len;
} }
auto read_string_limit(uint64_t limit) { string_type read_string_limit(uint64_t limit) {
size_t len = read_length(); size_t len = read_length();
Derived *dthis = static_cast<Derived*>(this); Derived *dthis = static_cast<Derived*>(this);
if (len > limit) { if (len > limit) {
dthis->raise_string_too_long(); dthis->raise_string_too_long();
len = 0; len = 0;
} }
typename Derived::read_string_type result(len, ' '); string_type result(len, ' ');
dthis->read_bytes_into(&(result[0]), len); dthis->read_bytes_into(&(result[0]), len);
return result; return result;
} }
auto read_string() { return read_string_limit(0x1000000); } // 16MB limit default string_type read_string() {
return read_string_limit(0x1000000); // 16MB limit default
}
}; };
/////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////
// //
// Class BaseBuffer // Class BaseBuffer
// //
// You must supply a CoreHandler which must define these // You must supply a BaseBufferConfig which must define these:
// methods:
// //
// void *basebuffer_malloc(size_t size); // using string_type = std::string; // or compatible
// void basebuffer_free(void *data); // void *basebuffer_malloc(size_t size);
// void raise_eof_on_read(); // void basebuffer_free(void *data);
// void raise_string_too_long(); // void raise_eof_on_read();
// void raise_integer_truncated(); // void raise_string_too_long();
// // void raise_integer_truncated();
// You must also select a StringType. Typically this would
// be std::string. This only affects the return value of
// read_string. You can always use read_string_view to read
// strings into other string types.
// //
/////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////
template<class CoreHandler, class StringType> template<class BaseBufferConfig>
class BaseBuffer : public CoreHandler { class BaseBuffer : public BaseBufferConfig {
private: private:
// Used for all strings in BaseBuffer.
using string_type = typename BaseBufferConfig::string_type;
// True if we own this buffer. // True if we own this buffer.
bool owned_; bool owned_;
@@ -317,7 +329,7 @@ private:
private: private:
void init(bool fixed, bool owned, char *buf, int64_t size) { void init(bool fixed, bool owned, char *buf, int64_t size) {
CoreHandler::clear_error_flags(); BaseBufferConfig::clear_error_flags();
owned_ = owned; owned_ = owned;
fixed_size_ = fixed; fixed_size_ = fixed;
buf_lo_ = buf; buf_lo_ = buf;
@@ -328,8 +340,6 @@ private:
} }
public: public:
using string_type = StringType;
// Construct an empty buffer. // Construct an empty buffer.
// //
BaseBuffer() { BaseBuffer() {
@@ -340,7 +350,7 @@ public:
// //
BaseBuffer(int64_t size, bool fixed) { BaseBuffer(int64_t size, bool fixed) {
assert(size >= 0); assert(size >= 0);
init(fixed, true, (char *)CoreHandler::basebuffer_malloc(size), size); init(fixed, true, (char *)BaseBufferConfig::basebuffer_malloc(size), size);
} }
// Construct a streambuffer that reads from an external block of bytes. // Construct a streambuffer that reads from an external block of bytes.
@@ -353,7 +363,7 @@ public:
// Modify an existing streambuffer to read from an external block of bytes. // Modify an existing streambuffer to read from an external block of bytes.
// //
void open(std::string_view data) { void open(std::string_view data) {
if (owned_ && (buf_lo_ != 0)) CoreHandler::basebuffer_free(buf_lo_); if (owned_ && (buf_lo_ != 0)) BaseBufferConfig::basebuffer_free(buf_lo_);
init(true, false, const_cast<char *>(data.data()), data.size()); init(true, false, const_cast<char *>(data.data()), data.size());
write_cursor_ = buf_hi_; write_cursor_ = buf_hi_;
} }
@@ -361,7 +371,7 @@ public:
// Destructor. Frees the buffer, if any. // Destructor. Frees the buffer, if any.
// //
~BaseBuffer() { ~BaseBuffer() {
if (owned_ && (buf_lo_ != 0)) CoreHandler::basebuffer_free(buf_lo_); if (owned_ && (buf_lo_ != 0)) BaseBufferConfig::basebuffer_free(buf_lo_);
} }
// Return the total number of bytes ever read. // Return the total number of bytes ever read.
@@ -435,7 +445,7 @@ public:
open(""); open("");
} else { } else {
if ((!fixed_size_) && (buf_lo_ != nullptr) && ((buf_hi_ - buf_lo_) > 100000)) { if ((!fixed_size_) && (buf_lo_ != nullptr) && ((buf_hi_ - buf_lo_) > 100000)) {
CoreHandler::basebuffer_free(buf_lo_); BaseBufferConfig::basebuffer_free(buf_lo_);
buf_lo_ = nullptr; buf_lo_ = nullptr;
buf_hi_ = nullptr; buf_hi_ = nullptr;
} }
@@ -529,7 +539,7 @@ public:
const char *read_bytes(int64_t bytes) { const char *read_bytes(int64_t bytes) {
int64_t avail = write_cursor_ - read_cursor_; int64_t avail = write_cursor_ - read_cursor_;
if (avail < bytes) { if (avail < bytes) {
CoreHandler::raise_eof_on_read(); BaseBufferConfig::raise_eof_on_read();
return nullptr; return nullptr;
} }
char *data = read_cursor_; char *data = read_cursor_;
@@ -576,12 +586,12 @@ public:
std::string_view read_string_view_limit(uint64_t limit) { std::string_view read_string_view_limit(uint64_t limit) {
size_t length = read_length(); size_t length = read_length();
if (length > limit) { if (length > limit) {
CoreHandler::raise_string_too_long(); BaseBufferConfig::raise_string_too_long();
return std::string_view(); return std::string_view();
} }
int64_t avail = write_cursor_ - read_cursor_; int64_t avail = write_cursor_ - read_cursor_;
if (avail < int64_t(length)) { if (avail < int64_t(length)) {
CoreHandler::raise_eof_on_read(); BaseBufferConfig::raise_eof_on_read();
return std::string_view(); return std::string_view();
} }
std::string_view result(read_cursor_, length); std::string_view result(read_cursor_, length);
@@ -600,12 +610,12 @@ public:
string_type read_string_limit(uint64_t limit) { string_type read_string_limit(uint64_t limit) {
size_t len = read_length(); size_t len = read_length();
if (len > limit) { if (len > limit) {
CoreHandler::raise_string_too_long(); BaseBufferConfig::raise_string_too_long();
return string_type(); return string_type();
} }
int64_t avail = write_cursor_ - read_cursor_; int64_t avail = write_cursor_ - read_cursor_;
if (avail < int64_t(len)) { if (avail < int64_t(len)) {
CoreHandler::raise_eof_on_read(); BaseBufferConfig::raise_eof_on_read();
return string_type(); return string_type();
} }
string_type result(len, ' '); string_type result(len, ' ');
@@ -706,9 +716,9 @@ private:
} else if (existing_size >= desired_size) { } else if (existing_size >= desired_size) {
if (data_size > 0) memcpy(buf_lo_, read_cursor_, data_size); if (data_size > 0) memcpy(buf_lo_, read_cursor_, data_size);
} else { } else {
char *nbuf = (char *)CoreHandler::basebuffer_malloc(desired_size); char *nbuf = (char *)BaseBufferConfig::basebuffer_malloc(desired_size);
if (data_size > 0) memcpy(nbuf, read_cursor_, data_size); if (data_size > 0) memcpy(nbuf, read_cursor_, data_size);
if (buf_lo_ != nullptr) CoreHandler::basebuffer_free(buf_lo_); if (buf_lo_ != nullptr) BaseBufferConfig::basebuffer_free(buf_lo_);
buf_lo_ = nbuf; buf_lo_ = nbuf;
buf_hi_ = nbuf + desired_size; buf_hi_ = nbuf + desired_size;
} }
@@ -728,7 +738,7 @@ private:
template<class T, class XT> template<class T, class XT>
void write_int_core(XT arg) { void write_int_core(XT arg) {
T reduced = arg; T reduced = arg;
if (XT(reduced) != arg) CoreHandler::raise_integer_truncated(); if (XT(reduced) != arg) BaseBufferConfig::raise_integer_truncated();
write_value_core(reduced); write_value_core(reduced);
} }
@@ -737,7 +747,7 @@ private:
T result; T result;
int64_t avail = write_cursor_ - read_cursor_; int64_t avail = write_cursor_ - read_cursor_;
if (avail < int64_t(sizeof(result))) { if (avail < int64_t(sizeof(result))) {
CoreHandler::raise_eof_on_read(); BaseBufferConfig::raise_eof_on_read();
return 0; return 0;
} }
memcpy(&result, read_cursor_, sizeof(result)); memcpy(&result, read_cursor_, sizeof(result));