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 "lpx-basebuffer.hpp"
class FlxStreamBufferCore {
class FlxStreamBufferConfig {
private:
bool err_eof_on_read_;
bool err_string_too_long_;
bool err_integer_truncated_;
protected:
using string_type = 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; }
@@ -24,7 +24,7 @@ public:
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:
using BaseBuffer::BaseBuffer;
using BaseBuffer::write_string;

View File

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

View File

@@ -103,7 +103,10 @@ struct BaseLuaValue {
//
// 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_uint16(uint64_t data)
@@ -119,15 +122,21 @@ struct BaseLuaValue {
// void write_length(size_t 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)
// 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>
@@ -181,7 +190,10 @@ public:
//
// 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();
// uint16_t read_uint16();
@@ -196,38 +208,40 @@ public:
// float read_float();
// double read_double();
// size_t read_length();
// String read_string_limit(uint64_t size);
// String read_string();
// string read_string_limit(uint64_t size);
// 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 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
// to report errors using exceptions or flags.
//
// If read_bytes_into discovers there's not enough bytes,
// there are two valid options: throw an exception, OR,
// set an error flag and fill the buffer with zeros.
//
// 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.
// If you call read_string_limit(100), and this finds
// a string of length 200 in the incoming data, then
// read_string_limit will call raise_string_too_long.
// 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>
class BaseReadMethods {
protected:
using string_type = typename Derived::string_type;
template<class T>
T read_value_core() {
T result;
@@ -254,50 +268,48 @@ public:
size_t read_length() {
uint64_t len = read_uint8();
if (len == 255) {
len = read_uint64();
}
if (len == 255) len = read_uint64();
return len;
}
auto read_string_limit(uint64_t limit) {
string_type read_string_limit(uint64_t limit) {
size_t len = read_length();
Derived *dthis = static_cast<Derived*>(this);
if (len > limit) {
dthis->raise_string_too_long();
len = 0;
}
typename Derived::read_string_type result(len, ' ');
string_type result(len, ' ');
dthis->read_bytes_into(&(result[0]), len);
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
//
// You must supply a CoreHandler which must define these
// methods:
// You must supply a BaseBufferConfig which must define these:
//
// using string_type = std::string; // or compatible
// void *basebuffer_malloc(size_t size);
// void basebuffer_free(void *data);
// void raise_eof_on_read();
// 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>
class BaseBuffer : public CoreHandler {
template<class BaseBufferConfig>
class BaseBuffer : public BaseBufferConfig {
private:
// Used for all strings in BaseBuffer.
using string_type = typename BaseBufferConfig::string_type;
// True if we own this buffer.
bool owned_;
@@ -317,7 +329,7 @@ private:
private:
void init(bool fixed, bool owned, char *buf, int64_t size) {
CoreHandler::clear_error_flags();
BaseBufferConfig::clear_error_flags();
owned_ = owned;
fixed_size_ = fixed;
buf_lo_ = buf;
@@ -328,8 +340,6 @@ private:
}
public:
using string_type = StringType;
// Construct an empty buffer.
//
BaseBuffer() {
@@ -340,7 +350,7 @@ public:
//
BaseBuffer(int64_t size, bool fixed) {
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.
@@ -353,7 +363,7 @@ public:
// Modify an existing streambuffer to read from an external block of bytes.
//
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());
write_cursor_ = buf_hi_;
}
@@ -361,7 +371,7 @@ public:
// Destructor. Frees the buffer, if any.
//
~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.
@@ -435,7 +445,7 @@ public:
open("");
} else {
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_hi_ = nullptr;
}
@@ -529,7 +539,7 @@ public:
const char *read_bytes(int64_t bytes) {
int64_t avail = write_cursor_ - read_cursor_;
if (avail < bytes) {
CoreHandler::raise_eof_on_read();
BaseBufferConfig::raise_eof_on_read();
return nullptr;
}
char *data = read_cursor_;
@@ -576,12 +586,12 @@ public:
std::string_view read_string_view_limit(uint64_t limit) {
size_t length = read_length();
if (length > limit) {
CoreHandler::raise_string_too_long();
BaseBufferConfig::raise_string_too_long();
return std::string_view();
}
int64_t avail = write_cursor_ - read_cursor_;
if (avail < int64_t(length)) {
CoreHandler::raise_eof_on_read();
BaseBufferConfig::raise_eof_on_read();
return std::string_view();
}
std::string_view result(read_cursor_, length);
@@ -600,12 +610,12 @@ public:
string_type read_string_limit(uint64_t limit) {
size_t len = read_length();
if (len > limit) {
CoreHandler::raise_string_too_long();
BaseBufferConfig::raise_string_too_long();
return string_type();
}
int64_t avail = write_cursor_ - read_cursor_;
if (avail < int64_t(len)) {
CoreHandler::raise_eof_on_read();
BaseBufferConfig::raise_eof_on_read();
return string_type();
}
string_type result(len, ' ');
@@ -706,9 +716,9 @@ private:
} else if (existing_size >= desired_size) {
if (data_size > 0) memcpy(buf_lo_, read_cursor_, data_size);
} 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 (buf_lo_ != nullptr) CoreHandler::basebuffer_free(buf_lo_);
if (buf_lo_ != nullptr) BaseBufferConfig::basebuffer_free(buf_lo_);
buf_lo_ = nbuf;
buf_hi_ = nbuf + desired_size;
}
@@ -728,7 +738,7 @@ private:
template<class T, class XT>
void write_int_core(XT arg) {
T reduced = arg;
if (XT(reduced) != arg) CoreHandler::raise_integer_truncated();
if (XT(reduced) != arg) BaseBufferConfig::raise_integer_truncated();
write_value_core(reduced);
}
@@ -737,7 +747,7 @@ private:
T result;
int64_t avail = write_cursor_ - read_cursor_;
if (avail < int64_t(sizeof(result))) {
CoreHandler::raise_eof_on_read();
BaseBufferConfig::raise_eof_on_read();
return 0;
}
memcpy(&result, read_cursor_, sizeof(result));