More refactors in basebuffer
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user