Added new ostd::Serial class

This commit is contained in:
OmniaX-Dev 2026-05-17 14:36:33 +02:00
parent 5f87625d23
commit ddcc74f176
3 changed files with 319 additions and 1020 deletions

View file

@ -27,7 +27,7 @@ namespace ostd
{ {
class OutputHandlerBase; class OutputHandlerBase;
struct Signal; struct Signal;
class BaseObject : public I_stringeable, public I_serializable class BaseObject : public I_stringeable, public serial::I_serializable
{ {
public: using SignalCallback = std::function<void(Signal&)>; public: using SignalCallback = std::function<void(Signal&)>;
public: public:

File diff suppressed because it is too large Load diff

View file

@ -25,11 +25,91 @@
namespace ostd namespace ostd
{ {
class I_serializable; namespace serial
{
enum class Endianness : u8 { LittleEndian = 0, BigEndian };
// ============================================================
// Byte-packing utilities
// ============================================================
// Map a type to its unsigned-integer "bit container" of the same size.
namespace detail
{
template<typename T> struct bit_container;
template<> struct bit_container<i8> { using type = u8; };
template<> struct bit_container<u8> { using type = u8; };
template<> struct bit_container<i16> { using type = u16; };
template<> struct bit_container<u16> { using type = u16; };
template<> struct bit_container<i32> { using type = u32; };
template<> struct bit_container<u32> { using type = u32; };
template<> struct bit_container<i64> { using type = u64; };
template<> struct bit_container<u64> { using type = u64; };
template<> struct bit_container<f32> { using type = u32; };
template<> struct bit_container<f64> { using type = u64; };
template<typename T>
using bit_container_t = typename bit_container<T>::type;
}
// Pack any 1/2/4/8-byte scalar into an array of bytes.
// Works for signed and unsigned integers, and for floats.
template<typename T>
std::array<i8, sizeof(T)> to_bytes(T value, Endianness endianness = Endianness::LittleEndian)
{
static_assert(std::is_trivially_copyable_v<T>, "to_bytes: T must be trivially copyable");
static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8, "to_bytes: T must be 1, 2, 4, or 8 bytes");
using U = detail::bit_container_t<T>;
U u;
std::memcpy(&u, &value, sizeof(T)); // bit-cast value -> unsigned
std::array<i8, sizeof(T)> out{};
if (endianness == Endianness::LittleEndian)
{
for (std::size_t i = 0; i < sizeof(T); ++i)
out[i] = static_cast<i8>((u >> (i * 8)) & 0xFF);
}
else // BigEndian
{
for (std::size_t i = 0; i < sizeof(T); ++i)
out[i] = static_cast<i8>((u >> ((sizeof(T) - 1 - i) * 8)) & 0xFF);
}
return out;
}
// Unpack bytes back into any 1/2/4/8-byte scalar.
template<typename T>
T from_bytes(std::span<const i8> bytes, Endianness endianness = Endianness::LittleEndian)
{
static_assert(std::is_trivially_copyable_v<T>, "from_bytes: T must be trivially copyable");
static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8, "from_bytes: T must be 1, 2, 4, or 8 bytes");
using U = detail::bit_container_t<T>;
U u = 0;
if (endianness == Endianness::LittleEndian)
{
for (std::size_t i = 0; i < sizeof(T); ++i)
u |= static_cast<U>(static_cast<u8>(bytes[i])) << (i * 8);
}
else // BigEndian
{
for (std::size_t i = 0; i < sizeof(T); ++i)
u |= static_cast<U>(static_cast<u8>(bytes[i])) << ((sizeof(T) - 1 - i) * 8);
}
T value;
std::memcpy(&value, &u, sizeof(T)); // bit-cast unsigned -> T
return value;
}
// Forward declaration
class I_serializable;
}
class Serial class Serial
{ {
public: template<typename T> struct ReadResult { T value; bool error { false }; }; public: template<typename T> struct ReadResult { T value; bool error { false }; };
public: enum class Endianness : u8 { LittleEndian = 0, BigEndian };
public: using StreamEdit = std::span<int8_t>; public: using StreamEdit = std::span<int8_t>;
public: using StreamView = std::span<const int8_t>; public: using StreamView = std::span<const int8_t>;
public: class Stream public: class Stream
@ -38,7 +118,7 @@ namespace ostd
Stream(const Stream&) = delete; Stream(const Stream&) = delete;
Stream& operator=(const Stream&) = delete; Stream& operator=(const Stream&) = delete;
inline Stream(void) { } // Uninitialized Stream inline Stream(void) { } // Uninitialized Stream
inline Stream(u64 size, Endianness endianness = Endianness::LittleEndian, bool preInitialize = false, i8 initialValue = 0) { inline Stream(u64 size, serial::Endianness endianness = serial::Endianness::LittleEndian, bool preInitialize = false, i8 initialValue = 0) {
construct(size, endianness, preInitialize, initialValue); construct(size, endianness, preInitialize, initialValue);
} }
inline Stream(Stream&& other) noexcept : m_data(other.m_data), m_size(other.m_size), m_endianness(other.m_endianness) { inline Stream(Stream&& other) noexcept : m_data(other.m_data), m_size(other.m_size), m_endianness(other.m_endianness) {
@ -60,7 +140,7 @@ namespace ostd
delete[] m_data; delete[] m_data;
m_data = nullptr; m_data = nullptr;
} }
inline bool construct(u64 size, Endianness endianness = Endianness::LittleEndian, bool preInitialize = false, i8 initialValue = 0) { inline bool construct(u64 size, serial::Endianness endianness = serial::Endianness::LittleEndian, bool preInitialize = false, i8 initialValue = 0) {
if (isValid()) return false; if (isValid()) return false;
if (size < 1) return false; if (size < 1) return false;
if constexpr (sizeof(size_t) < sizeof(uint64_t)) if constexpr (sizeof(size_t) < sizeof(uint64_t))
@ -80,10 +160,10 @@ namespace ostd
} }
inline bool isValid(void) const { return m_size > 0 && m_data != nullptr; } inline bool isValid(void) const { return m_size > 0 && m_data != nullptr; }
inline u64 size(void) const { return isValid() ? m_size : 0; } inline u64 size(void) const { return isValid() ? m_size : 0; }
inline bool isLittleEndian(void) const { return m_endianness == Endianness::LittleEndian; } inline bool isLittleEndian(void) const { return m_endianness == serial::Endianness::LittleEndian; }
inline bool isBigEndian(void) const { return m_endianness == Endianness::BigEndian; } inline bool isBigEndian(void) const { return m_endianness == serial::Endianness::BigEndian; }
inline Endianness getEndianness(void) const { return m_endianness; } inline serial::Endianness getEndianness(void) const { return m_endianness; }
inline void setEndianness(Endianness endianness) { m_endianness = endianness; } inline void setEndianness(serial::Endianness endianness) { m_endianness = endianness; }
inline StreamEdit data() { inline StreamEdit data() {
if (isValid()) if (isValid())
return { m_data, cast<std::size_t>(m_size) }; return { m_data, cast<std::size_t>(m_size) };
@ -98,204 +178,158 @@ namespace ostd
private: private:
i8* m_data { nullptr }; i8* m_data { nullptr };
u64 m_size { 0 }; u64 m_size { 0 };
Endianness m_endianness { Endianness::LittleEndian }; serial::Endianness m_endianness { serial::Endianness::LittleEndian };
}; };
private: struct Reader public: struct StreamAccessor
{ {
inline Reader(Serial& _parent) : parent(_parent) { data = parent.m_data.data(); } public:
bool resetAddress(u64 newAddr = 1);
ReadResult<i8> int8(u64 addr); protected:
inline ReadResult<i8> int8(void) { return int8(0); } inline StreamAccessor(Serial& _parent) : parent(_parent) { }
ReadResult<u8> uint8(u64 addr);
inline ReadResult<u8> uint8(void) { return uint8(0); }
ReadResult<i16> int16(u64 addr); u64& resolve_address(u64& addr);
inline ReadResult<i16> int16(void) { return int16(0); } bool restore_address_and_return(bool passthrough);
ReadResult<u16> uint16(u64 addr); void clear_address_backup(void);
inline ReadResult<u16> uint16(void) { return uint16(0); } inline void disable_address_restore(void) { m_addressRestoreEnabled = false; }
inline void enable_address_restore(void) { m_addressRestoreEnabled = true; }
ReadResult<i32> int32(u64 addr); // Convenience wrappers
inline ReadResult<i32> int32(void) { return int32(0); }
ReadResult<u32> uint32(u64 addr);
inline ReadResult<u32> uint32(void) { return uint32(0); }
ReadResult<i64> int64(u64 addr);
inline ReadResult<i64> int64(void) { return int64(0); }
ReadResult<u64> uint64(u64 addr);
inline ReadResult<u64> uint64(void) { return uint64(0); }
ReadResult<f32> float32(u64 addr);
inline ReadResult<f32> float32(void) { return float32(0); }
ReadResult<f64> float64(u64 addr);
inline ReadResult<f64> float64(void) { return float64(0); }
bool stream(u64 addr, Stream& outStream, bool sizeStored = true, u32 size_if_not_stored = 0);
inline bool stream(Stream& outStream, bool sizeStored = true, u32 size_if_not_stored = 0) { return stream(0, outStream, sizeStored, size_if_not_stored); }
bool string(u64 addr, String& outString, bool sizeStored = true, u32 size_if_not_stored = 0, bool null_terminated = false);
inline bool string(String& outString, bool sizeStored = true, u32 size_if_not_stored = 0, bool null_terminated = false) { return string(0, outString, sizeStored, size_if_not_stored, null_terminated); }
bool object(u64 addr, I_serializable& outObject);
inline bool object(I_serializable& outObject) { return object(0, outObject); }
private: // Convenience wrappers
inline bool isValid(void) const { return parent.isValid(); } inline bool isValid(void) const { return parent.isValid(); }
inline bool isInvalid(void) const { return parent.isInvalid(); } inline bool isInvalid(void) const { return parent.isInvalid(); }
inline bool isLittleEndian(void) const { return parent.isLittleEndian(); } inline bool isLittleEndian(void) const { return parent.isLittleEndian(); }
inline bool isBigEndian(void) const { return parent.isBigEndian(); } inline bool isBigEndian(void) const { return parent.isBigEndian(); }
inline u64& resolve_address(u64& addr) { return parent.resolve_address(addr); } inline bool isValidAddress(u64 addr, u32 size = 1) const { return parent.isValidAddress(addr, size); }
inline bool isValidAddress(u64 addr, u32 size = 1) const { return parent.isValidAddress(addr); }
private: protected:
Serial& parent;
StreamView data;
};
private: struct Writer
{
inline Writer(Serial& _parent) : parent(_parent) { data = parent.m_data.data(); }
bool int8(u64 addr, i8 value);
inline bool int8(i8 value) { return int8(0, value); }
bool uint8(u64 addr, u8 value);
inline bool uint8(u8 value) { return uint8(0, value); }
bool int16(u64 addr, i16 value);
inline bool int16(i16 value) { return int16(0, value); }
bool uint16(u64 addr, u16 value);
inline bool uint16(u16 value) { return uint16(0, value); }
bool int32(u64 addr, i32 value);
inline bool int32(i32 value) { return int32(0, value); }
bool uint32(u64 addr, u32 value);
inline bool uint32(u32 value) { return uint32(0, value); }
bool int64(u64 addr, i64 value);
inline bool int64(i64 value) { return int64(0, value); }
bool uint64(u64 addr, u64 value);
inline bool uint64(u64 value) { return uint64(0, value); }
bool float32(u64 addr, f32 value);
inline bool float32(f32 value) { return float32(0, value); }
bool float64(u64 addr, f64 value);
inline bool float64(f64 value) { return float64(0, value); }
bool stream(u64 addr, const Stream& value, bool storeSize = true);
inline bool stream(const Stream& value, bool storeSize = true) { return stream(0, value, storeSize); }
bool string(u64 addr, const String& value, bool storeSize = true, bool null_terminated = false); // if sizeStored, null_terminated will be ignored
inline bool string(const String& value, bool storeSize = true, bool null_terminated = false) { return string(0, value, storeSize, null_terminated); }
bool object(u64 addr, const I_serializable& value);
inline bool object(const I_serializable& value) { return object(0, value); }
private: // Convenience wrappers
inline bool isValid(void) const { return parent.isValid(); }
inline bool isInvalid(void) const { return parent.isInvalid(); }
inline bool isLittleEndian(void) const { return parent.isLittleEndian(); }
inline bool isBigEndian(void) const { return parent.isBigEndian(); }
inline u64& resolve_address(u64& addr) { return parent.resolve_address(addr); }
inline bool isValidAddress(u64 addr, u32 size = 1) const { return parent.isValidAddress(addr); }
private:
Serial& parent; Serial& parent;
StreamEdit data; StreamEdit data;
private:
u64 m_address { 1 };
u64 m_backupAddress { 1 };
bool m_addressBackupStored { false };
bool m_addressRestoreEnabled { true };
friend class Serial;
};
public: struct Reader : public StreamAccessor
{
inline Reader(Serial& _parent) : StreamAccessor(_parent) { }
template<typename T>
Serial::ReadResult<T> read_scalar(u64 _addr)
{
if (isInvalid()) return { T{}, true };
u64& addr = resolve_address(_addr);
if (!isValidAddress(addr, sizeof(T)))
return { T{}, restore_address_and_return(true) };
T val = from_bytes<T>(data.subspan(addr, sizeof(T)), parent.getEndianness());
addr += sizeof(T);
clear_address_backup();
return { val, false };
}
inline ReadResult<i8> int8(u64 _addr) { return read_scalar<i8>(_addr); }
inline ReadResult<i8> int8(void) { return int8(0); }
inline ReadResult<u8> uint8(u64 _addr) { return read_scalar<u8>(_addr); }
inline ReadResult<u8> uint8(void) { return uint8(0); }
inline ReadResult<i16> int16(u64 _addr) { return read_scalar<i16>(_addr); }
inline ReadResult<i16> int16(void) { return int16(0); }
inline ReadResult<u16> uint16(u64 _addr) { return read_scalar<u16>(_addr); }
inline ReadResult<u16> uint16(void) { return uint16(0); }
inline ReadResult<i32> int32(u64 _addr) { return read_scalar<i32>(_addr); }
inline ReadResult<i32> int32(void) { return int32(0); }
inline ReadResult<u32> uint32(u64 _addr) { return read_scalar<u32>(_addr); }
inline ReadResult<u32> uint32(void) { return uint32(0); }
inline ReadResult<i64> int64(u64 _addr) { return read_scalar<i64>(_addr); }
inline ReadResult<i64> int64(void) { return int64(0); }
inline ReadResult<u64> uint64(u64 _addr) { return read_scalar<u64>(_addr); }
inline ReadResult<u64> uint64(void) { return uint64(0); }
inline ReadResult<f32> float32(u64 _addr) { return read_scalar<f32>(_addr); }
inline ReadResult<f32> float32(void) { return float32(0); }
inline ReadResult<f64> float64(u64 _addr) { return read_scalar<f64>(_addr); }
inline ReadResult<f64> float64(void) { return float64(0); }
bool stream(u64 _addr, Stream& outStream, bool sizeStored = true, u32 size_if_not_stored = 0);
inline bool stream(Stream& outStream, bool sizeStored = true, u32 size_if_not_stored = 0) { return stream(0, outStream, sizeStored, size_if_not_stored); }
bool string(u64 _addr, String& outString, bool sizeStored = true, u32 size_if_not_stored = 0, bool null_terminated = false);
inline bool string(String& outString, bool sizeStored = true, u32 size_if_not_stored = 0, bool null_terminated = false) { return string(0, outString, sizeStored, size_if_not_stored, null_terminated); }
bool object(u64 _addr, serial::I_serializable& outObject);
inline bool object(serial::I_serializable& outObject) { return object(0, outObject); }
};
public: struct Writer : public StreamAccessor
{
inline Writer(Serial& _parent) : StreamAccessor(_parent) { }
template<typename T>
bool write_scalar(u64 _addr, T value)
{
if (isInvalid()) return false;
u64& addr = resolve_address(_addr);
if (!isValidAddress(addr, sizeof(T)))
return restore_address_and_return(false);
auto bytes = to_bytes(value, parent.getEndianness());
std::copy(bytes.begin(), bytes.end(), data.begin() + addr);
addr += sizeof(T);
clear_address_backup();
return true;
}
inline bool int8(u64 _addr, i8 value) { return write_scalar<i8> (_addr, value); }
inline bool int8(i8 value) { return int8(0, value); }
inline bool uint8(u64 _addr, u8 value) { return write_scalar<u8> (_addr, value); }
inline bool uint8(u8 value) { return uint8(0, value); }
inline bool int16(u64 _addr, i16 value) { return write_scalar<i16>(_addr, value); }
inline bool int16(i16 value) { return int16(0, value); }
inline bool uint16(u64 _addr, u16 value) { return write_scalar<u16>(_addr, value); }
inline bool uint16(u16 value) { return uint16(0, value); }
inline bool int32(u64 _addr, i32 value) { return write_scalar<i32>(_addr, value); }
inline bool int32(i32 value) { return int32(0, value); }
inline bool uint32(u64 _addr, u32 value) { return write_scalar<u32>(_addr, value); }
inline bool uint32(u32 value) { return uint32(0, value); }
inline bool int64(u64 _addr, i64 value) { return write_scalar<i64>(_addr, value); }
inline bool int64(i64 value) { return int64(0, value); }
inline bool uint64(u64 _addr, u64 value) { return write_scalar<u64>(_addr, value); }
inline bool uint64(u64 value) { return uint64(0, value); }
inline bool float32(u64 _addr, f32 value) { return write_scalar<f32>(_addr, value); }
inline bool float32(f32 value) { return float32(0, value); }
inline bool float64(u64 _addr, f64 value) { return write_scalar<f64>(_addr, value); }
inline bool float64(f64 value) { return float64(0, value); }
bool stream(u64 _addr, const Stream& value, bool storeSize = true);
inline bool stream(const Stream& value, bool storeSize = true) { return stream(0, value, storeSize); }
bool string(u64 _addr, const String& value, bool storeSize = true, bool null_terminated = false); // if sizeStored, null_terminated will be ignored
inline bool string(const String& value, bool storeSize = true, bool null_terminated = false) { return string(0, value, storeSize, null_terminated); }
bool object(u64 _addr, const serial::I_serializable& value);
inline bool object(const serial::I_serializable& value) { return object(0, value); }
}; };
public: public:
inline Serial(void) : read(*this), write(*this) { } inline Serial(void) : read(*this), write(*this) { }
inline Serial(u64 size, Endianness endianness = Endianness::LittleEndian, bool preInitialize = false, i8 initialValue = 0) : read(*this), write(*this) { construct(size, endianness, preInitialize, initialValue); } inline Serial(u64 size, serial::Endianness endianness = serial::Endianness::LittleEndian, bool preInitialize = false, i8 initialValue = 0) : read(*this), write(*this) { construct(size, endianness, preInitialize, initialValue); }
bool construct(u64 size, Endianness endianness = Endianness::LittleEndian, bool preInitialize = false, i8 initialValue = 0); bool construct(u64 size, serial::Endianness endianness = serial::Endianness::LittleEndian, bool preInitialize = false, i8 initialValue = 0);
bool resetAddress(u64 newAddr);
bool isValidAddress(u64 addr, u32 size = 1) const; bool isValidAddress(u64 addr, u32 size = 1) const;
inline bool isValid(void) const { return m_data.isValid(); } inline bool isValid(void) const { return m_data.isValid(); }
inline bool isInvalid(void) const { return !isValid(); } inline bool isInvalid(void) const { return !isValid(); }
inline bool isLittleEndian(void) const { return m_data.isLittleEndian(); } inline bool isLittleEndian(void) const { return m_data.isLittleEndian(); }
inline bool isBigEndian(void) const { return m_data.isBigEndian(); } inline bool isBigEndian(void) const { return m_data.isBigEndian(); }
inline Endianness getEndianness(void) const { return m_data.getEndianness(); } inline serial::Endianness getEndianness(void) const { return m_data.getEndianness(); }
private:
inline u64& resolve_address(u64& addr) { if (addr == 0) return m_address; return addr; }
inline bool restore_address(void) { resetAddress(m_backupAddress); return false; } // Always returns false so it can be used as false on error in return statements
private: private:
Stream m_data; Stream m_data;
u64 m_address { 0 };
u64 m_backupAddress { 0 };
bool m_backupStored { false };
public: public:
Writer write; Writer write;
Reader read; Reader read;
}; };
class I_serializable
{
public:
virtual Serial::Stream serialize(void) const = 0;
virtual bool deserialize(const Serial::Stream& data) = 0;
};
// =============================================== OLD VERSION ===============================================
class OutputHandlerBase;
namespace serial namespace serial
{ {
class SerialIO class I_serializable
{ {
public: struct tEndianness {
inline static constexpr u8 LittleEndian = 0x00;
inline static constexpr u8 BigEndian = 0x01;
};
public: public:
inline SerialIO(void) { init(); } virtual Serial::Stream serialize(void) const = 0;
inline SerialIO(u64 size, u8 endianness = tEndianness::BigEndian) { init(size, endianness); } virtual bool deserialize(const Serial::Stream& data) = 0;
inline SerialIO(ByteStream& data, u8 endianness = tEndianness::BigEndian) { m_data = data; m_endianness = endianness; }
bool init(u64 size = SerialIO::DefaultMaxSize, u8 endianness = tEndianness::BigEndian);
bool r_QWord(StreamIndex addr, QWord& outVal);
bool r_DWord(StreamIndex addr, DWord& outVal);
bool r_Word(StreamIndex addr, Word& outVal);
bool r_Byte(StreamIndex addr, Byte& outVal);
bool r_Addr(StreamIndex addr, StreamIndex& outVal);
bool r_Float(StreamIndex addr, f32& outVal);
bool r_Double(StreamIndex addr, f64& outVal);
bool r_ByteStream(StreamIndex addr, ByteStream& outStream);
bool r_ByteStream(StreamIndex addr, ByteStream& outStream, u32 size);
bool r_String(StreamIndex addr, String& outString);
bool r_String(StreamIndex addr, String& outString, u32 size);
bool r_NullTerminatedString(StreamIndex addr, String& outString);
bool w_QWord(StreamIndex addr, QWord val);
bool w_DWord(StreamIndex addr, DWord val);
bool w_Word(StreamIndex addr, Word val);
bool w_Byte(StreamIndex addr, Byte val);
bool w_Addr(StreamIndex addr, StreamIndex val);
bool w_Float(StreamIndex addr, f32 val);
bool w_Double(StreamIndex addr, f64 val);
bool w_ByteStream(StreamIndex addr, const ByteStream& stream, bool store_size = true);
bool w_String(StreamIndex addr, const String& str, bool store_size = false, bool null_terminate = true);
bool is_validAddr(StreamIndex addr, StreamIndex offset = 1);
inline u64 size(void) { return m_data.size(); }
inline ByteStream& getData(void) { return m_data; }
void print(StreamIndex start, OutputHandlerBase& out);
bool saveToFile(const String& filePath);
inline u8 getEndianness(void) const { return m_endianness; }
inline bool isLittleEndian(void) const { return m_endianness == tEndianness::LittleEndian; }
inline bool isBigEndian(void) const { return m_endianness == tEndianness::BigEndian; }
inline bool isAutoResizeEnabled(void) const { return m_autoResize; }
inline void enableAutoResize(bool val = true, i32 resizeAmount = 1024) { m_autoResize = val; if (!val) return; m_resizeAmount = resizeAmount; }
private:
ByteStream m_data;
u8 m_endianness { tEndianness::BigEndian };
bool m_autoResize { true };
i32 m_resizeAmount { 1024 };
bool m_statuWriting { false };
public:
inline static constexpr u64 DefaultMaxSize = 512;
}; };
} }
} }