From 1f78ea1f37d920240c605b38cc714acbbfec6276 Mon Sep 17 00:00:00 2001 From: Gary Talent Date: Sun, 22 Oct 2023 16:37:08 -0500 Subject: [PATCH] [ox] Add Reader_c and make MetalClawReader use it --- deps/ox/src/ox/claw/CMakeLists.txt | 12 +- deps/ox/src/ox/claw/read.cpp | 3 +- deps/ox/src/ox/claw/read.hpp | 3 +- deps/ox/src/ox/mc/err.hpp | 6 +- deps/ox/src/ox/mc/intops.hpp | 90 ++++--- deps/ox/src/ox/mc/presenceindicator.cpp | 4 +- deps/ox/src/ox/mc/presenceindicator.hpp | 85 +++++- deps/ox/src/ox/mc/read.hpp | 338 ++++++++++-------------- deps/ox/src/ox/mc/test/tests.cpp | 39 +-- deps/ox/src/ox/mc/types.hpp | 4 +- deps/ox/src/ox/model/walk.hpp | 7 + deps/ox/src/ox/std/CMakeLists.txt | 1 + deps/ox/src/ox/std/buffer.cpp | 1 + deps/ox/src/ox/std/buffer.hpp | 70 ++++- deps/ox/src/ox/std/reader.cpp | 59 +++++ deps/ox/src/ox/std/reader.hpp | 96 +++++++ 16 files changed, 533 insertions(+), 285 deletions(-) create mode 100644 deps/ox/src/ox/std/reader.cpp create mode 100644 deps/ox/src/ox/std/reader.hpp diff --git a/deps/ox/src/ox/claw/CMakeLists.txt b/deps/ox/src/ox/claw/CMakeLists.txt index 099971d5..14d81e06 100644 --- a/deps/ox/src/ox/claw/CMakeLists.txt +++ b/deps/ox/src/ox/claw/CMakeLists.txt @@ -5,7 +5,6 @@ add_library( write.cpp ) - if(NOT MSVC) target_compile_options(OxClaw PRIVATE -Wsign-conversion) target_compile_options(OxClaw PRIVATE -Wconversion) @@ -17,6 +16,17 @@ target_link_libraries( $<$:OxOrganicClaw> ) +if(OX_USE_STDLIB) + add_executable( + readclaw + readclaw.cpp + ) + target_link_libraries( + readclaw PUBLIC + OxClaw + ) +endif() + install(TARGETS OxClaw LIBRARY DESTINATION lib ARCHIVE DESTINATION lib diff --git a/deps/ox/src/ox/claw/read.cpp b/deps/ox/src/ox/claw/read.cpp index 1542fc2c..01ad2b81 100644 --- a/deps/ox/src/ox/claw/read.cpp +++ b/deps/ox/src/ox/claw/read.cpp @@ -80,7 +80,8 @@ Result readClaw(TypeStore *ts, const char *buff, std::size_t buffSz switch (header.fmt) { case ClawFormat::Metal: { - MetalClawReader reader(reinterpret_cast(header.data), header.dataSize); + ox::BufferReader br(header.data, header.dataSize); + MetalClawReader reader(br); ModelHandlerInterface handler(&reader); oxReturnError(model(&handler, &obj)); return obj; diff --git a/deps/ox/src/ox/claw/read.hpp b/deps/ox/src/ox/claw/read.hpp index 1d6c7dbf..13b18164 100644 --- a/deps/ox/src/ox/claw/read.hpp +++ b/deps/ox/src/ox/claw/read.hpp @@ -51,7 +51,8 @@ Error readClaw(const char *buff, std::size_t buffLen, T *val) { switch (header.fmt) { case ClawFormat::Metal: { - MetalClawReader reader(reinterpret_cast(header.data), header.dataSize); + ox::BufferReader br(header.data, header.dataSize); + MetalClawReader reader(br); ModelHandlerInterface handler(&reader); return model(&handler, val); } diff --git a/deps/ox/src/ox/mc/err.hpp b/deps/ox/src/ox/mc/err.hpp index 623a5aba..29dcddc2 100644 --- a/deps/ox/src/ox/mc/err.hpp +++ b/deps/ox/src/ox/mc/err.hpp @@ -11,9 +11,9 @@ namespace ox { enum { - MC_PRESENCEMASKOUTBOUNDS = 1, - MC_BUFFENDED = 2, - MC_OUTBUFFENDED = 4 + McPresenceMapOverflow = 1, + McBuffEnded = 2, + McOutputBuffEnded = 4 }; } diff --git a/deps/ox/src/ox/mc/intops.hpp b/deps/ox/src/ox/mc/intops.hpp index 0740c478..e98e86cb 100644 --- a/deps/ox/src/ox/mc/intops.hpp +++ b/deps/ox/src/ox/mc/intops.hpp @@ -14,6 +14,7 @@ #include #include #include +#include namespace ox::mc { @@ -114,7 +115,7 @@ constexpr McInt encodeInteger(I input) noexcept { * length integer. */ [[nodiscard]] -static constexpr std::size_t countBytes(unsigned b) noexcept { +constexpr std::size_t countBytes(unsigned b) noexcept { std::size_t i = 0; while ((b >> i) & 1) ++i; return i + 1; @@ -131,55 +132,60 @@ static_assert(countBytes(0b0111'1111) == 8); static_assert(countBytes(0b1111'1111) == 9); template -constexpr Result decodeInteger(const uint8_t buff[9], std::size_t buffLen, std::size_t *bytesRead) noexcept { - const auto bytes = countBytes(buff[0]); +constexpr Result decodeInteger(Reader_c auto&rdr, std::size_t *bytesRead) noexcept { + uint8_t firstByte = 0; + oxReturnError(rdr.read(&firstByte, 1)); + oxReturnError(rdr.seekg(-1, ox::ios_base::cur)); + const auto bytes = countBytes(firstByte); if (bytes == 9) { *bytesRead = bytes; I out = 0; - ox_memcpy(&out, &buff[1], sizeof(I)); + oxReturnError(rdr.seekg(1, ox::ios_base::cur)); + oxReturnError(rdr.read(&out, sizeof(I))); return fromLittleEndian(out); - } else if (buffLen >= bytes) { - *bytesRead = bytes; - uint64_t decoded = 0; - ox_memcpy(&decoded, &buff[0], bytes); - decoded >>= bytes; - // move sign bit - if constexpr(is_signed_v) { - const auto negBit = bytes * 8 - bytes - 1; - // move sign - const auto negative = (decoded >> negBit) == 1; - if (negative) { - // fill in all bits between encoded sign and real sign with 1s - // split it up because the 32-bit ARM can't shift more than 32 bits - ox::Array d = {}; - ox_memcpy(d.data(), &decoded, sizeof(decoded)); - auto bit = negBit; - for (; bit < ox::min(Bits, 32); ++bit) { - d[0] |= 1 << bit; - } - bit -= 32; - for (; bit < Bits; ++bit) { - d[1] |= 1 << bit; - } - I out = 0; - if constexpr(ox::defines::BigEndian) { - const auto d0Tmp = d[0]; - d[0] = d[1]; - d[1] = d0Tmp; - } - ox_memcpy(&out, d.data(), sizeof(out)); - return out; - } - } - return static_cast(decoded); } - return OxError(1); + *bytesRead = bytes; + uint64_t decoded = 0; + oxReturnError(rdr.read(&decoded, bytes)); + decoded >>= bytes; + // move sign bit + if constexpr(is_signed_v) { + const auto negBit = bytes * 8 - bytes - 1; + // move sign + const auto negative = (decoded >> negBit) == 1; + if (negative) { + // fill in all bits between encoded sign and real sign with 1s + // split it up because the 32-bit ARM can't shift more than 32 bits + ox::Array d = {}; + //d[0] = decoded & 0xffff'ffff; + //d[1] = decoded >> 32; + ox_memcpy(d.data(), &decoded, sizeof(decoded)); + auto bit = negBit; + for (; bit < ox::min(Bits, 32); ++bit) { + d[0] |= 1 << bit; + } + bit -= 32; + for (; bit < Bits; ++bit) { + d[1] |= 1 << bit; + } + I out = 0; + if constexpr(ox::defines::BigEndian) { + const auto d0Tmp = d[0]; + d[0] = d[1]; + d[1] = d0Tmp; + } + ox_memcpy(&out, d.data(), sizeof(out)); + return out; + } + } + return static_cast(decoded); } template -constexpr Result decodeInteger(McInt m) noexcept { - std::size_t bytesRead; - return decodeInteger(m.data, 9, &bytesRead); +Result decodeInteger(McInt m) noexcept { + std::size_t bytesRead{}; + BufferReader br(reinterpret_cast(m.data), 9); + return decodeInteger(br, &bytesRead); } } diff --git a/deps/ox/src/ox/mc/presenceindicator.cpp b/deps/ox/src/ox/mc/presenceindicator.cpp index 16689908..7bc7a011 100644 --- a/deps/ox/src/ox/mc/presenceindicator.cpp +++ b/deps/ox/src/ox/mc/presenceindicator.cpp @@ -11,7 +11,7 @@ namespace ox { -template class FieldBitmapReader; -template class FieldBitmapReader; +template class FieldBitmapWriterBase; +template class FieldBitmapWriterBase; } diff --git a/deps/ox/src/ox/mc/presenceindicator.hpp b/deps/ox/src/ox/mc/presenceindicator.hpp index ca40debe..6d7ee97b 100644 --- a/deps/ox/src/ox/mc/presenceindicator.hpp +++ b/deps/ox/src/ox/mc/presenceindicator.hpp @@ -8,21 +8,77 @@ #pragma once +#include +#include #include #include +#include #include "err.hpp" namespace ox { -template +template class FieldBitmapReader { + protected: + mutable std::size_t m_mapBlockIdx = ~std::size_t{0}; + mutable uint64_t m_mapBlock = 0; + std::size_t m_mapStart = 0; + Reader &m_reader; + + public: + explicit constexpr FieldBitmapReader(Reader &reader) noexcept; + + constexpr Result get(std::size_t i) const noexcept; + + private: + constexpr ox::Error loadMapBlock(std::size_t id) const noexcept; + +}; + +template +constexpr FieldBitmapReader::FieldBitmapReader(Reader &reader) noexcept: + m_mapStart(reader.tellg()), + m_reader(reader) { +} + +template +constexpr Result FieldBitmapReader::get(std::size_t idx) const noexcept { + constexpr auto blockBits = sizeof(m_mapBlock); + auto const blockIdx = idx / blockBits; + if (m_mapBlockIdx != blockIdx) [[unlikely]] { + oxReturnError(loadMapBlock(blockIdx)); + } + idx %= blockBits; + return (m_mapBlock >> idx) & 1; +} + +template +constexpr ox::Error FieldBitmapReader::loadMapBlock(std::size_t idx) const noexcept { + oxRequire(g, m_reader.tellg()); + oxReturnError(m_reader.seekg(static_cast(m_mapStart + idx), ox::ios_base::beg)); + ox::Array mapBlock{}; + oxReturnError(m_reader.read(mapBlock.data(), sizeof(m_mapBlock))); + // Warning: narrow-conv + oxReturnError(m_reader.seekg(static_cast(g), ox::ios_base::beg)); + m_mapBlock = 0; + for (auto i = 0ull; auto b : mapBlock) { + m_mapBlock |= static_cast(std::bit_cast(b)) << i; + i += 8; + } + m_mapBlockIdx = idx; + return {}; +} + + +template +class FieldBitmapWriterBase { protected: T m_map = nullptr; std::size_t m_mapLen = 0; public: - constexpr FieldBitmapReader(T map, std::size_t maxLen) noexcept; + constexpr FieldBitmapWriterBase(T map, std::size_t maxLen) noexcept; constexpr auto setBuffer(T map, std::size_t maxLen) noexcept; @@ -38,45 +94,45 @@ class FieldBitmapReader { }; template -constexpr FieldBitmapReader::FieldBitmapReader(T map, std::size_t maxLen) noexcept { +constexpr FieldBitmapWriterBase::FieldBitmapWriterBase(T map, std::size_t maxLen) noexcept { m_map = map; m_mapLen = maxLen; } template -constexpr auto FieldBitmapReader::setBuffer(T map, std::size_t maxLen) noexcept { +constexpr auto FieldBitmapWriterBase::setBuffer(T map, std::size_t maxLen) noexcept { m_map = map; m_mapLen = maxLen; } template -constexpr Result FieldBitmapReader::get(std::size_t i) const noexcept { +constexpr Result FieldBitmapWriterBase::get(std::size_t i) const noexcept { if (i / 8 < m_mapLen) { return (m_map[i / 8] >> (i % 8)) & 1; } else { - return OxError(MC_PRESENCEMASKOUTBOUNDS); + return OxError(McPresenceMapOverflow); } } template -constexpr void FieldBitmapReader::setFields(int fields) noexcept { +constexpr void FieldBitmapWriterBase::setFields(int fields) noexcept { m_mapLen = static_cast((fields / 8 + 1) - (fields % 8 == 0)); } template -constexpr void FieldBitmapReader::setMaxLen(int maxLen) noexcept { +constexpr void FieldBitmapWriterBase::setMaxLen(int maxLen) noexcept { m_mapLen = static_cast(maxLen); } template -constexpr int64_t FieldBitmapReader::getMaxLen() const noexcept { +constexpr int64_t FieldBitmapWriterBase::getMaxLen() const noexcept { return static_cast(m_mapLen); } -extern template class FieldBitmapReader; -extern template class FieldBitmapReader; +extern template class FieldBitmapWriterBase; +extern template class FieldBitmapWriterBase; -class FieldBitmap: public FieldBitmapReader { +class FieldBitmap: public FieldBitmapWriterBase { public: constexpr FieldBitmap(uint8_t *map, std::size_t maxLen) noexcept; @@ -85,7 +141,8 @@ class FieldBitmap: public FieldBitmapReader { }; -constexpr FieldBitmap::FieldBitmap(uint8_t *map, std::size_t maxLen) noexcept: FieldBitmapReader(map, maxLen) { +constexpr FieldBitmap::FieldBitmap(uint8_t *map, std::size_t maxLen) noexcept: + FieldBitmapWriterBase(map, maxLen) { } constexpr Error FieldBitmap::set(std::size_t i, bool on) noexcept { @@ -97,7 +154,7 @@ constexpr Error FieldBitmap::set(std::size_t i, bool on) noexcept { } return {}; } else { - return OxError(MC_PRESENCEMASKOUTBOUNDS); + return OxError(McPresenceMapOverflow); } } diff --git a/deps/ox/src/ox/mc/read.hpp b/deps/ox/src/ox/mc/read.hpp index e0f7778f..bba31b01 100644 --- a/deps/ox/src/ox/mc/read.hpp +++ b/deps/ox/src/ox/mc/read.hpp @@ -26,22 +26,22 @@ namespace ox { -template -class MetalClawReaderTemplate { +template +class MetalClawReaderTemplate: public ModelHandlerBase> { private: - FieldBitmapReader m_fieldPresence; + FieldBitmapReader m_fieldPresence; std::size_t m_fields = 0; std::size_t m_field = 0; int m_unionIdx = -1; - std::size_t m_buffIt = 0; - std::size_t m_buffLen = 0; - const uint8_t *m_buff = nullptr; - MetalClawReaderTemplate *m_parent = nullptr; + Reader &m_reader; + MetalClawReaderTemplate *m_parent = nullptr; public: - constexpr MetalClawReaderTemplate(const uint8_t *buff, std::size_t buffLen, int unionIdx = -1, - MetalClawReaderTemplate *parent = nullptr) noexcept; + explicit constexpr MetalClawReaderTemplate( + Reader &reader, + int unionIdx = -1, + MetalClawReaderTemplate *parent = nullptr) noexcept; constexpr ~MetalClawReaderTemplate() noexcept; @@ -95,8 +95,7 @@ class MetalClawReaderTemplate { /** * Reads an string length from the current location in the buffer. */ - [[nodiscard]] - constexpr StringLength stringLength(const char *name) noexcept; + constexpr Result stringLength(const char *name) noexcept; template constexpr ox::Error setTypeInfo( @@ -109,7 +108,7 @@ class MetalClawReaderTemplate { * Returns a MetalClawReader to parse a child object. */ [[nodiscard]] - constexpr MetalClawReaderTemplate child(const char *name, int unionIdx = -1) noexcept; + constexpr MetalClawReaderTemplate child(const char *name, int unionIdx = -1) noexcept; /** * Indicates whether or not the next field to be read is present. @@ -139,127 +138,118 @@ class MetalClawReaderTemplate { }; -template -constexpr MetalClawReaderTemplate::MetalClawReaderTemplate(const uint8_t *buff, std::size_t buffLen, - int unionIdx, - MetalClawReaderTemplate *parent) noexcept: - m_fieldPresence(buff, buffLen), +template +constexpr MetalClawReaderTemplate::MetalClawReaderTemplate( + Reader &reader, + int unionIdx, + MetalClawReaderTemplate *parent) noexcept: + m_fieldPresence(reader), m_unionIdx(unionIdx), - m_buffLen(buffLen), - m_buff(buff), + m_reader(reader), m_parent(parent) { } -template -constexpr MetalClawReaderTemplate::~MetalClawReaderTemplate() noexcept { - if (m_parent) { - m_parent->m_buffIt += m_buffIt; - } +template +constexpr MetalClawReaderTemplate::~MetalClawReaderTemplate() noexcept { if (m_field != m_fields) { oxTrace("ox::mc::MetalClawReader::error") << "MetalClawReader: incorrect fields number given"; } } -template -constexpr Error MetalClawReaderTemplate::field(const char*, int8_t *val) noexcept { +template +constexpr Error MetalClawReaderTemplate::field(const char*, int8_t *val) noexcept { return readInteger(val); } -template -constexpr Error MetalClawReaderTemplate::field(const char*, int16_t *val) noexcept { +template +constexpr Error MetalClawReaderTemplate::field(const char*, int16_t *val) noexcept { return readInteger(val); } -template -constexpr Error MetalClawReaderTemplate::field(const char*, int32_t *val) noexcept { +template +constexpr Error MetalClawReaderTemplate::field(const char*, int32_t *val) noexcept { return readInteger(val); } -template -constexpr Error MetalClawReaderTemplate::field(const char*, int64_t *val) noexcept { +template +constexpr Error MetalClawReaderTemplate::field(const char*, int64_t *val) noexcept { return readInteger(val); } -template -constexpr Error MetalClawReaderTemplate::field(const char*, uint8_t *val) noexcept { +template +constexpr Error MetalClawReaderTemplate::field(const char*, uint8_t *val) noexcept { return readInteger(val); } -template -constexpr Error MetalClawReaderTemplate::field(const char*, uint16_t *val) noexcept { +template +constexpr Error MetalClawReaderTemplate::field(const char*, uint16_t *val) noexcept { return readInteger(val); } -template -constexpr Error MetalClawReaderTemplate::field(const char*, uint32_t *val) noexcept { +template +constexpr Error MetalClawReaderTemplate::field(const char*, uint32_t *val) noexcept { return readInteger(val); } -template -constexpr Error MetalClawReaderTemplate::field(const char*, uint64_t *val) noexcept { +template +constexpr Error MetalClawReaderTemplate::field(const char*, uint64_t *val) noexcept { return readInteger(val); } -template -constexpr Error MetalClawReaderTemplate::field(const char*, bool *val) noexcept { +template +constexpr Error MetalClawReaderTemplate::field(const char*, bool *val) noexcept { if (m_unionIdx == -1 || static_cast(m_unionIdx) == m_field) { - auto valErr = m_fieldPresence.get(static_cast(m_field)); - *val = valErr.value; - oxReturnError(valErr.error); + auto const result = m_fieldPresence.get(static_cast(m_field)); + *val = result.value; + oxReturnError(result); } ++m_field; return OxError(0); } // array handler -template -constexpr Error MetalClawReaderTemplate::field(const char *name, auto *val, std::size_t valLen) noexcept { +template +constexpr Error MetalClawReaderTemplate::field(const char *name, auto *val, std::size_t valLen) noexcept { if (m_unionIdx == -1 || static_cast(m_unionIdx) == m_field) { if (m_fieldPresence.get(static_cast(m_field))) { // read the length - if (m_buffIt >= m_buffLen) { - return OxError(MC_BUFFENDED); - } std::size_t bytesRead = 0; - oxRequire(len, mc::decodeInteger(&m_buff[m_buffIt], m_buffLen - m_buffIt, &bytesRead)); - m_buffIt += bytesRead; + oxRequire(len, mc::decodeInteger(m_reader, &bytesRead)); // read the list if (valLen >= len) { - auto reader = child(""); - auto handler = HandlerMaker(&reader); + auto reader = child({}); + auto &handler = *reader.interface(); oxReturnError(handler.setTypeInfo("List", 0, {}, static_cast(len))); for (std::size_t i = 0; i < len; ++i) { - oxReturnError(handler.field("", &val[i])); + oxReturnError(handler.field({}, &val[i])); } } else { - oxTrace("ox::mc::read::field(T)") << name << ", size:" << valLen; - return OxError(MC_OUTBUFFENDED); + oxTracef("ox::mc::read::field(T)", "{}, length: {}", name, valLen); + return OxError(McOutputBuffEnded); } } } ++m_field; - return OxError(0); + return {}; } -template +template template -constexpr Error MetalClawReaderTemplate::field(const char*, HashMap *val) noexcept { +constexpr Error MetalClawReaderTemplate::field(const char*, HashMap *val) noexcept { if (m_unionIdx == -1 || static_cast(m_unionIdx) == m_field) { if (m_fieldPresence.get(static_cast(m_field))) { // read the length - if (m_buffIt >= m_buffLen) { - return OxError(MC_BUFFENDED); - } + oxRequire(g, m_reader.tellg()); std::size_t bytesRead = 0; - oxRequire(len, mc::decodeInteger(&m_buff[m_buffIt], m_buffLen - m_buffIt, &bytesRead)); - m_buffIt += bytesRead; + oxRequire(len, mc::decodeInteger(m_reader, &bytesRead)); + oxReturnError(m_reader.seekg(g)); // read the list auto reader = child(""); - auto handler = HandlerMaker(&reader); + auto &handler = *reader.interface(); oxReturnError(handler.setTypeInfo("List", 0, {}, static_cast(len))); for (std::size_t i = 0; i < len; ++i) { - const auto keyLen = handler.stringLength(nullptr); + oxRequire(keyLen, handler.stringLength(nullptr)); auto wkey = ox_malloca(keyLen + 1, char, 0); auto wkeyPtr = wkey.get(); oxReturnError(handler.fieldCString("", &wkeyPtr, keyLen + 1)); @@ -271,18 +261,19 @@ constexpr Error MetalClawReaderTemplate::field(const char*, HashMa return OxError(0); } -template +template template -constexpr Error MetalClawReaderTemplate::field(const char *name, T *val) noexcept { +constexpr Error MetalClawReaderTemplate::field(const char *name, T *val) noexcept { if constexpr(isVector_v) { if (m_unionIdx == -1 || static_cast(m_unionIdx) == m_field) { // set size of val if the field is present, don't worry about it if not if (m_fieldPresence.get(static_cast(m_field))) { oxRequire(len, arrayLength(name, false)); val->resize(len); + return field(name, val->data(), val->size()); } - return field(name, val->data(), val->size()); } + val->resize(0); ++m_field; return {}; } else if constexpr(isArray_v) { @@ -302,8 +293,7 @@ constexpr Error MetalClawReaderTemplate::field(const char *name, T if ((m_unionIdx == -1 || static_cast(m_unionIdx) == m_field) && val) { if (m_fieldPresence.get(static_cast(m_field))) { auto reader = child(""); - auto handler = HandlerMaker(&reader); - oxReturnError(model(&handler, val)); + oxReturnError(model(reader.interface(), val)); } } ++m_field; @@ -311,44 +301,35 @@ constexpr Error MetalClawReaderTemplate::field(const char *name, T } } -template +template template -constexpr Error MetalClawReaderTemplate::field(const char*, UnionView val) noexcept { +constexpr Error MetalClawReaderTemplate::field(const char*, UnionView val) noexcept { if ((m_unionIdx == -1 || static_cast(m_unionIdx) == m_field) && val.get()) { if (m_fieldPresence.get(static_cast(m_field))) { auto reader = child("", val.idx()); - auto handler = HandlerMaker(&reader); - oxReturnError(model(&handler, val.get())); + oxReturnError(model(reader.interface(), val.get())); } } ++m_field; return OxError(0); } -template +template template -constexpr Error MetalClawReaderTemplate::field(const char*, BasicString *val) noexcept { +constexpr Error MetalClawReaderTemplate::field(const char*, BasicString *val) noexcept { if (m_unionIdx == -1 || static_cast(m_unionIdx) == m_field) { if (m_fieldPresence.get(static_cast(m_field))) { // read the length - if (m_buffIt >= m_buffLen) { - return OxError(MC_BUFFENDED); - } std::size_t bytesRead = 0; - oxRequire(size, mc::decodeInteger(&m_buff[m_buffIt], m_buffLen - m_buffIt, &bytesRead)); - m_buffIt += bytesRead; + oxRequire(size, mc::decodeInteger(m_reader, &bytesRead)); const auto cap = size; *val = BasicString(cap); auto data = val->data(); // read the string if (static_cast(cap) < size) { - return OxError(MC_OUTBUFFENDED); + return OxError(McOutputBuffEnded); } - if (m_buffIt + size > m_buffLen) { - return OxError(MC_BUFFENDED); - } - ox_strncpy(data, &m_buff[m_buffIt], size); - m_buffIt += size; + oxReturnError(m_reader.read(data, size)); } else { *val = ""; } @@ -357,81 +338,56 @@ constexpr Error MetalClawReaderTemplate::field(const char*, BasicS return OxError(0); } -template +template template -constexpr Error MetalClawReaderTemplate::field(const char *name, BString *val) noexcept { +constexpr Error MetalClawReaderTemplate::field(const char *name, BString *val) noexcept { return fieldCString(name, val->data(), val->cap()); } -template -constexpr Error MetalClawReaderTemplate::fieldCString(const char*, char *val, std::size_t buffLen) noexcept { +template +constexpr Error MetalClawReaderTemplate::fieldCString(const char*, char *val, std::size_t buffLen) noexcept { if (m_fieldPresence.get(static_cast(m_field))) { // read the length - if (m_buffIt >= m_buffLen) { - return OxError(MC_BUFFENDED); - } std::size_t bytesRead = 0; - auto [size, err] = mc::decodeInteger(&m_buff[m_buffIt], m_buffLen - m_buffIt, &bytesRead); + oxRequire(size, mc::decodeInteger(m_reader, &bytesRead)); if (size > buffLen) { - return OxError(MC_OUTBUFFENDED); + return OxError(McOutputBuffEnded); } - m_buffIt += bytesRead; - oxReturnError(err); // re-allocate in case too small auto data = val; // read the string - if (m_buffIt + size <= m_buffLen) { - ox_memcpy(data, &m_buff[m_buffIt], size); - data[size] = 0; - m_buffIt += size; - } else { - return OxError(MC_BUFFENDED); - } + oxReturnError(m_reader.read(data, size)); + data[size] = 0; } ++m_field; return OxError(0); } -template -constexpr Error MetalClawReaderTemplate::fieldCString(const char*, char **val) noexcept { +template +constexpr Error MetalClawReaderTemplate::fieldCString(const char*, char **val) noexcept { if (m_fieldPresence.get(static_cast(m_field))) { // read the length - if (m_buffIt >= m_buffLen) { - return OxError(MC_BUFFENDED); - } std::size_t bytesRead = 0; - auto [size, err] = mc::decodeInteger(&m_buff[m_buffIt], m_buffLen - m_buffIt, &bytesRead); - m_buffIt += bytesRead; - oxReturnError(err); + oxRequire(size, mc::decodeInteger(m_reader, &bytesRead)); // re-allocate in case too small safeDelete(*val); *val = new char[size + 1]; auto data = *val; // read the string - if (m_buffIt + size <= m_buffLen) { - ox_memcpy(data, &m_buff[m_buffIt], size); - data[size] = 0; - m_buffIt += size; - } else { - return OxError(MC_BUFFENDED); - } + oxReturnError(m_reader.read(data, size)); + data[size] = 0; } ++m_field; return OxError(0); } -template -constexpr Error MetalClawReaderTemplate::fieldCString(const char*, char **val, std::size_t buffLen) noexcept { +template +constexpr Error MetalClawReaderTemplate::fieldCString(const char*, char **val, std::size_t buffLen) noexcept { if (m_unionIdx == -1 || static_cast(m_unionIdx) == m_field) { if (m_fieldPresence.get(static_cast(m_field))) { // read the length - if (m_buffIt >= m_buffLen) { - return OxError(MC_BUFFENDED); - } std::size_t bytesRead = 0; - auto [size, err] = mc::decodeInteger(&m_buff[m_buffIt], m_buffLen - m_buffIt, &bytesRead); - m_buffIt += bytesRead; - oxReturnError(err); + oxRequire(size, mc::decodeInteger(m_reader, &bytesRead)); // re-allocate if too small if (buffLen < size + 1) { safeDelete(*val); @@ -440,13 +396,8 @@ constexpr Error MetalClawReaderTemplate::fieldCString(const char*, } auto data = *val; // read the string - if (m_buffIt + size <= m_buffLen) { - ox_memcpy(data, &m_buff[m_buffIt], size); - data[size] = 0; - m_buffIt += size; - } else { - return OxError(MC_BUFFENDED); - } + oxReturnError(m_reader.read(data, size)); + data[size] = 0; } else { auto data = *val; if (data) { @@ -458,18 +409,16 @@ constexpr Error MetalClawReaderTemplate::fieldCString(const char*, return OxError(0); } -template -constexpr Result MetalClawReaderTemplate::arrayLength(const char*, bool pass) noexcept { +template +constexpr Result MetalClawReaderTemplate::arrayLength(const char*, bool pass) noexcept { if (m_unionIdx == -1 || static_cast(m_unionIdx) == m_field) { if (m_fieldPresence.get(static_cast(m_field))) { // read the length - if (m_buffIt >= m_buffLen) { - return OxError(MC_BUFFENDED); - } std::size_t bytesRead = 0; - auto out = mc::decodeInteger(&m_buff[m_buffIt], m_buffLen - m_buffIt, &bytesRead).value; - if (pass) { - m_buffIt += bytesRead; + oxRequire(g, m_reader.tellg()); + oxRequire(out, mc::decodeInteger(m_reader, &bytesRead)); + if (!pass) { + oxReturnError(m_reader.seekg(g)); } return out; } @@ -477,20 +426,29 @@ constexpr Result MetalClawReaderTemplate::arrayLength return OxError(1); } -template +template +constexpr Result MetalClawReaderTemplate::stringLength(const char*) noexcept { + if (m_unionIdx == -1 || static_cast(m_unionIdx) == m_field) { + if (m_fieldPresence.get(static_cast(m_field))) { + // read the length + std::size_t bytesRead = 0; + auto len = mc::decodeInteger(m_reader, &bytesRead); + oxReturnError(m_reader.seekg(-static_cast(bytesRead), ox::ios_base::cur)); + return len; + } + } + return 0; +} + +template template -constexpr Error MetalClawReaderTemplate::readInteger(I *val) noexcept { +constexpr Error MetalClawReaderTemplate::readInteger(I *val) noexcept { if (m_unionIdx == -1 || static_cast(m_unionIdx) == m_field) { if (m_fieldPresence.get(static_cast(m_field))) { std::size_t bytesRead = 0; - if (m_buffIt >= m_buffLen) { - oxTrace("ox::MetalClaw::readInteger") << "Buffer ended"; - return OxError(MC_BUFFENDED); - } - auto valErr = mc::decodeInteger(&m_buff[m_buffIt], m_buffLen - m_buffIt, &bytesRead); - m_buffIt += bytesRead; - oxReturnError(valErr.error); - *val = valErr.value; + auto const result = mc::decodeInteger(m_reader, &bytesRead); + oxReturnError(result); + *val = result.value; } else { *val = 0; } @@ -499,22 +457,17 @@ constexpr Error MetalClawReaderTemplate::readInteger(I *val) noexc return OxError(0); } -template +template template -constexpr Error MetalClawReaderTemplate::field(const char*, CB cb) noexcept { +constexpr Error MetalClawReaderTemplate::field(const char*, CB cb) noexcept { if (m_unionIdx == -1 || static_cast(m_unionIdx) == m_field) { if (m_fieldPresence.get(static_cast(m_field))) { // read the length - if (m_buffIt >= m_buffLen) { - return OxError(MC_BUFFENDED); - } std::size_t bytesRead = 0; - oxRequire(len, mc::decodeInteger(&m_buff[m_buffIt], m_buffLen - m_buffIt, &bytesRead)); - m_buffIt += bytesRead; - + oxRequire(len, mc::decodeInteger(m_reader, &bytesRead)); // read the list auto reader = child(""); - auto handler = HandlerMaker(&reader); + auto &handler = *reader.interface(); oxReturnError(handler.setTypeInfo("List", 0, {}, static_cast(len))); for (std::size_t i = 0; i < len; ++i) { T val; @@ -527,50 +480,36 @@ constexpr Error MetalClawReaderTemplate::field(const char*, CB cb) return OxError(0); } -template -constexpr StringLength MetalClawReaderTemplate::stringLength(const char*) noexcept { - if (m_unionIdx == -1 || static_cast(m_unionIdx) == m_field) { - if (m_fieldPresence.get(static_cast(m_field))) { - // read the length - std::size_t bytesRead = 0; - auto len = mc::decodeInteger(&m_buff[m_buffIt], m_buffLen - m_buffIt, &bytesRead); - return len.value; - } - } - return 0; -} - -template +template template -constexpr ox::Error MetalClawReaderTemplate::setTypeInfo( +constexpr ox::Error MetalClawReaderTemplate::setTypeInfo( const char*, int, const Vector&, std::size_t fields) noexcept { m_fields = fields; - m_buffIt = static_cast((fields / 8 + 1) - (fields % 8 == 0)); - m_fieldPresence.setFields(static_cast(fields)); - m_fieldPresence.setMaxLen(static_cast(m_buffIt)); - return {}; + // Warning: narrow-conv + return m_reader.seekg( + static_cast((fields / 8 + 1) - (fields % 8 == 0)), + ox::ios_base::cur); } -template -constexpr MetalClawReaderTemplate MetalClawReaderTemplate::child(const char*, int unionIdx) noexcept { - return MetalClawReaderTemplate(m_buff + m_buffIt, m_buffLen - m_buffIt, unionIdx, this); +template +constexpr MetalClawReaderTemplate MetalClawReaderTemplate::child(const char*, int unionIdx) noexcept { + return MetalClawReaderTemplate(m_reader, unionIdx, this); } -template -constexpr bool MetalClawReaderTemplate::fieldPresent(const char*) const noexcept { +template +constexpr bool MetalClawReaderTemplate::fieldPresent(const char*) const noexcept { return m_fieldPresence.get(static_cast(m_field)).value; } -template -constexpr bool MetalClawReaderTemplate::fieldPresent(int fieldNo) const noexcept { +template +constexpr bool MetalClawReaderTemplate::fieldPresent(int fieldNo) const noexcept { return m_fieldPresence.get(static_cast(fieldNo)).value; } -template +template [[nodiscard]] -constexpr int MetalClawReaderTemplate::whichFieldPresent(const char*, const ModelUnion &u) const noexcept { - FieldBitmapReader p(m_buff + m_buffIt, m_buffLen - m_buffIt); - p.setFields(static_cast(u.fieldCount())); +constexpr int MetalClawReaderTemplate::whichFieldPresent(const char*, const ModelUnion &u) const noexcept { + FieldBitmapReader p(m_reader); for (auto i = 0u; i < u.fieldCount(); ++i) { if (p.get(i)) { return static_cast(i); @@ -579,18 +518,17 @@ constexpr int MetalClawReaderTemplate::whichFieldPresent(const cha return -1; } -template -constexpr void MetalClawReaderTemplate::nextField() noexcept { +template +constexpr void MetalClawReaderTemplate::nextField() noexcept { ++m_field; } -using MetalClawReader = MetalClawReaderTemplate<[](auto r) { - return ModelHandlerInterface{r}; -}>; +using MetalClawReader = MetalClawReaderTemplate; template Error readMC(const char *buff, std::size_t buffLen, T *val) noexcept { - MetalClawReader reader(reinterpret_cast(buff), buffLen); + BufferReader br(buff, buffLen); + MetalClawReader reader(br); ModelHandlerInterface handler(&reader); return model(&handler, val); } diff --git a/deps/ox/src/ox/mc/test/tests.cpp b/deps/ox/src/ox/mc/test/tests.cpp index 031ec630..26578a84 100644 --- a/deps/ox/src/ox/mc/test/tests.cpp +++ b/deps/ox/src/ox/mc/test/tests.cpp @@ -18,8 +18,8 @@ union TestUnion { static constexpr auto TypeName = "TestUnion"; static constexpr auto TypeVersion = 1; bool Bool; - uint32_t Int = 5; - char *CString; + uint32_t Int; + char *CString{}; }; struct TestStructNest { @@ -49,6 +49,7 @@ struct TestStruct { ox::BString<32> BString = ""; uint32_t List[4] = {0, 0, 0, 0}; ox::Vector Vector = {1, 2, 3, 4, 5}; + ox::Vector Vector2 = {1, 2, 3, 4, 5}; ox::HashMap Map; TestStructNest EmptyStruct; TestStructNest Struct; @@ -77,7 +78,6 @@ oxModelEnd() template constexpr ox::Error model(T *io, ox::CommonPtrWith auto *obj) noexcept { oxReturnError(io->template setTypeInfo()); - oxReturnError(io->field("Vector", &obj->Vector)); oxReturnError(io->field("Bool", &obj->Bool)); oxReturnError(io->field("Int", &obj->Int)); oxReturnError(io->field("Int1", &obj->Int1)); @@ -97,9 +97,10 @@ constexpr ox::Error model(T *io, ox::CommonPtrWith auto *obj) noexce oxReturnError(io->field("String", &obj->String)); oxReturnError(io->field("BString", &obj->BString)); oxReturnError(io->field("List", obj->List, 4)); + oxReturnError(io->field("Vector", &obj->Vector)); oxReturnError(io->field("Map", &obj->Map)); - oxReturnError(io->field("EmptyStruct", &obj->EmptyStruct)); oxReturnError(io->field("Struct", &obj->Struct)); + oxReturnError(io->field("EmptyStruct", &obj->EmptyStruct)); return OxError(0); } @@ -110,10 +111,9 @@ std::map tests = { [] { // This test doesn't confirm much, but it does show that the writer // doesn't segfault - static constexpr size_t buffLen = 1024; - char buff[buffLen]; + ox::Array buff; TestStruct ts; - oxReturnError(ox::writeMC(buff, buffLen, ts)); + oxReturnError(ox::writeMC(buff.data(), buff.size(), ts)); oxReturnError(ox::writeMC(ts)); return OxError(0); } @@ -126,18 +126,19 @@ std::map tests = { TestStruct testIn, testOut; testIn.Bool = true; testIn.Int = 42; - testIn.Union.Int = 42; - testIn.String = "Test String 0"; testIn.BString = "Test String 1"; + testIn.String = "Test String 2"; + testIn.Vector = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, }; + testIn.Vector2 = {}; testIn.List[0] = 1; testIn.List[1] = 2; testIn.List[2] = 3; testIn.List[3] = 4; - testIn.Map["asdf"] = 93; - testIn.Map["aoeu"] = 94; - testIn.Struct.Bool = false; + testIn.Struct.Bool = true; testIn.Struct.Int = 300; - testIn.Struct.BString = "Test String 2"; + testIn.Struct.BString = "Test String 3"; + testIn.unionIdx = 1; + testIn.Union.Int = 93; // run tests const auto [buff, err] = ox::writeMC(testIn); oxAssert(err, "writeMC failed"); @@ -160,10 +161,11 @@ std::map tests = { oxAssert(testIn.List[1] == testOut.List[1], "List[1] value mismatch"); oxAssert(testIn.List[2] == testOut.List[2], "List[2] value mismatch"); oxAssert(testIn.List[3] == testOut.List[3], "List[3] value mismatch"); - oxAssert(testIn.Vector[0] == testOut.Vector[0], "Vector[0] value mismatch"); - oxAssert(testIn.Vector[1] == testOut.Vector[1], "Vector[1] value mismatch"); - oxAssert(testIn.Vector[2] == testOut.Vector[2], "Vector[2] value mismatch"); - oxAssert(testIn.Vector[3] == testOut.Vector[3], "Vector[3] value mismatch"); + oxAssert(testIn.Vector.size() == testOut.Vector.size(), "Vector size mismatch"); + for (auto i = 0u; i < testIn.Vector.size(); ++i) { + oxAssert(testIn.Vector[i] == testOut.Vector[i], ox::sfmt("Vector[{}] value mismatch", i)); + } + oxAssert(testIn.Vector2.size() == testOut.Vector2.size(), "Vector2 size mismatch"); oxAssert(testIn.Map["asdf"] == testOut.Map["asdf"], "Map[\"asdf\"] value mismatch"); oxAssert(testIn.Map["aoeu"] == testOut.Map["aoeu"], "Map[\"aoeu\"] value mismatch"); oxAssert(testIn.EmptyStruct.Bool == testOut.EmptyStruct.Bool, "EmptyStruct.Bool value mismatch"); @@ -363,7 +365,8 @@ std::map tests = { ox::TypeStore typeStore; const auto [type, typeErr] = ox::buildTypeDef(&typeStore, &testIn); oxAssert(typeErr, "Descriptor write failed"); - oxReturnError(ox::walkModel(type, dataBuff, dataBuffLen, + ox::BufferReader br(dataBuff, dataBuffLen); + oxReturnError(ox::walkModel(type, br, [](const ox::Vector&, const ox::Vector&, const ox::DescriptorField &f, ox::MetalClawReader *rdr) -> ox::Error { //std::cout << f.fieldName.c_str() << '\n'; auto fieldName = f.fieldName.c_str(); diff --git a/deps/ox/src/ox/mc/types.hpp b/deps/ox/src/ox/mc/types.hpp index 8c4f7a6a..48f465db 100644 --- a/deps/ox/src/ox/mc/types.hpp +++ b/deps/ox/src/ox/mc/types.hpp @@ -14,7 +14,7 @@ namespace ox { -using StringLength = uint32_t; -using ArrayLength = uint32_t; +using StringLength = std::size_t; +using ArrayLength = std::size_t; } diff --git a/deps/ox/src/ox/model/walk.hpp b/deps/ox/src/ox/model/walk.hpp index 4fd8eed1..3939e8b0 100644 --- a/deps/ox/src/ox/model/walk.hpp +++ b/deps/ox/src/ox/model/walk.hpp @@ -144,6 +144,13 @@ constexpr Error model(Reader *rdr, DataWalker *walker) noexcept { return OxError(0); } +template +constexpr Error walkModel(DescriptorType *type, Reader_c auto &reader, Handler handler) noexcept { + DataWalker walker(type, handler); + Reader rdr(reader); + return model(&rdr, &walker); +} + template constexpr Error walkModel(DescriptorType *type, const char *data, std::size_t dataLen, Handler handler) noexcept { DataWalker walker(type, handler); diff --git a/deps/ox/src/ox/std/CMakeLists.txt b/deps/ox/src/ox/std/CMakeLists.txt index e87462f9..dcb947d7 100644 --- a/deps/ox/src/ox/std/CMakeLists.txt +++ b/deps/ox/src/ox/std/CMakeLists.txt @@ -30,6 +30,7 @@ add_library( math.cpp memops.cpp random.cpp + reader.cpp substitutes.cpp stacktrace.cpp string.cpp diff --git a/deps/ox/src/ox/std/buffer.cpp b/deps/ox/src/ox/std/buffer.cpp index 85c33666..75309fdf 100644 --- a/deps/ox/src/ox/std/buffer.cpp +++ b/deps/ox/src/ox/std/buffer.cpp @@ -13,6 +13,7 @@ namespace ox { template class Vector; +template class ReaderT; template class WriterT; template class WriterT; diff --git a/deps/ox/src/ox/std/buffer.hpp b/deps/ox/src/ox/std/buffer.hpp index 14f7f6b2..b6a9d8ed 100644 --- a/deps/ox/src/ox/std/buffer.hpp +++ b/deps/ox/src/ox/std/buffer.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2022 gary@drinkingtea.net + * Copyright 2015 - 2023 gary@drinkingtea.net * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -9,6 +9,7 @@ #pragma once #include "error.hpp" +#include "reader.hpp" #include "vector.hpp" #include "writer.hpp" @@ -166,6 +167,73 @@ class CharBuffWriter { } }; +class BufferReader { + private: + std::size_t m_it = 0; + std::size_t m_size = 0; + char const* m_buff = nullptr; + + public: + constexpr explicit BufferReader(char const*buff, std::size_t sz) noexcept: + m_size(sz), m_buff(buff) {} + + constexpr explicit BufferReader(ox::Buffer const&buffer) noexcept: + m_size(buffer.size()), m_buff(buffer.data()) {} + + constexpr ox::Result peek() const noexcept { + if (m_it >= m_size) [[unlikely]] { + return OxError(1, "Peek failed: buffer overrun"); + } + return m_buff[m_it]; + } + + constexpr ox::Result read(void *v, std::size_t sz) noexcept { + sz = ox::min(sz, m_size - m_it); + if (m_it + sz > m_size) [[unlikely]] { + return OxError(1, "Read failed: Buffer overrun"); + } + ox_memcpy(v, &m_buff[m_it], sz); + m_it += sz; + return sz; + } + + constexpr ox::Error seekg(std::size_t p) noexcept { + if (p > m_size) [[unlikely]] { + return OxError(1, "Seek failed: Buffer overrun"); + } + m_it = p; + return {}; + } + + constexpr ox::Error seekg(int64_t off, ios_base::seekdir dir) noexcept { + ox::Signed base = 0; + switch (dir) { + case ox::ios_base::beg: + base = 0; + break; + case ox::ios_base::end: + base = static_cast>(m_size); + break; + case ox::ios_base::cur: + base = static_cast>(m_it); + break; + default: + return OxError(1, "Invalid seekdir"); + } + auto const newIt = static_cast(base + off); + if (newIt > m_size) [[unlikely]] { + return OxError(1, "Seek failed: Buffer overrun"); + } + m_it = newIt; + return {}; + } + + constexpr ox::Result tellg() const noexcept { + return m_it; + } +}; + +extern template class ReaderT; extern template class WriterT; extern template class WriterT; diff --git a/deps/ox/src/ox/std/reader.cpp b/deps/ox/src/ox/std/reader.cpp new file mode 100644 index 00000000..0b4e78c2 --- /dev/null +++ b/deps/ox/src/ox/std/reader.cpp @@ -0,0 +1,59 @@ +/* + * Copyright 2015 - 2023 gary@drinkingtea.net + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + + +#ifdef OX_USE_STDLIB +#include + +#include "array.hpp" +#include "reader.hpp" + +namespace ox { + +[[nodiscard]] +constexpr int sdMap(ox::ios_base::seekdir in) noexcept { + switch (in) { + case ox::ios_base::beg: + return SEEK_SET; + case ox::ios_base::end: + return SEEK_END; + case ox::ios_base::cur: + return SEEK_CUR; + } + return -1; +} + +ox::Result FileReader::peek() const noexcept { + auto const c = fgetc(m_file); + auto const ok = c != EOF; + if (ok && ungetc(c, m_file)) [[unlikely]] { + return OxError(1, "Unable to unget character"); + } + return {static_cast(c), OxError(!ok, "File peek failed")}; +} + +ox::Result FileReader::read(char *v, std::size_t cnt) noexcept { + return fread(v, 1, cnt, m_file); +} + +ox::Error FileReader::seekg(std::size_t p) noexcept { + return OxError(fseek(m_file, static_cast(p), SEEK_CUR) != 0); +} + +ox::Error FileReader::seekg(int64_t p, ios_base::seekdir sd) noexcept { + return OxError(fseek(m_file, p, sdMap(sd)) != 0); +} + +ox::Result FileReader::tellg() noexcept { + const auto sz = ftell(m_file); + return {static_cast(sz), OxError(sz == -1)}; +} + +} + +#endif diff --git a/deps/ox/src/ox/std/reader.hpp b/deps/ox/src/ox/std/reader.hpp new file mode 100644 index 00000000..5915b28f --- /dev/null +++ b/deps/ox/src/ox/std/reader.hpp @@ -0,0 +1,96 @@ +/* + * Copyright 2015 - 2023 gary@drinkingtea.net + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#ifdef OX_USE_STDLIB +#include +#endif + +#include "concepts.hpp" +#include "error.hpp" +#include "types.hpp" +#include "writer.hpp" + +namespace ox { + +template +concept Reader_c = requires(T v) { + {v.peek()} -> ox::same_as>; + {v.read(static_cast(nullptr), static_cast(0))} -> ox::same_as>; + {v.seekg(static_cast(0))} -> ox::same_as; + {v.seekg(static_cast(0), ios_base::beg)} -> ox::same_as; + {v.tellg()} -> ox::same_as>; +}; + +class Reader_v { + public: + virtual constexpr ~Reader_v() noexcept = default; + [[nodiscard]] + virtual constexpr ox::Result peek() const noexcept = 0; + virtual constexpr ox::Result read(char*, std::size_t) noexcept = 0; + virtual constexpr ox::Result tellg() noexcept = 0; + virtual constexpr ox::Error seekg(std::size_t) noexcept = 0; + virtual constexpr ox::Error seekg(int64_t, ios_base::seekdir) = 0; +}; + +template +class ReaderT: public Reader_v { + private: + T m_reader{}; + public: + template + constexpr explicit ReaderT(Args&&... args) noexcept: m_reader(args...) { + } + constexpr ox::Result peek() const noexcept override { + return m_reader.peek(); + } + constexpr ox::Result read(char *v, std::size_t cnt) noexcept override { + return m_reader.read(v, cnt); + } + constexpr ox::Error seekg(std::size_t p) noexcept override { + return m_reader.seekg(p); + } + constexpr ox::Error seekg(int64_t p, ios_base::seekdir sd) noexcept override { + return m_reader.seekg(p, sd); + } + constexpr ox::Result tellg() noexcept override { + return m_reader.tellg(); + } +}; + +#ifdef OX_USE_STDLIB +class FileReader: public Reader_v { + private: + FILE *m_file = nullptr; + public: + constexpr explicit FileReader(FILE *file) noexcept: m_file(file) {} + ox::Result peek() const noexcept override; + ox::Result read(char *v, std::size_t cnt) noexcept override; + ox::Error seekg(std::size_t p) noexcept override; + ox::Error seekg(int64_t p, ios_base::seekdir sd) noexcept override; + ox::Result tellg() noexcept override; +}; +#endif + +/** + * Allocates the specified amount of data at the end of the current read stream. + * @param reader + * @param sz + * @return + */ +constexpr ox::Result allocate(Reader_c auto *reader, std::size_t sz) noexcept { + const auto p = reader->tellg(); + oxReturnError(reader->seekg(0, ios_base::end)); + const auto out = reader->tellg(); + oxReturnError(reader->read(nullptr, sz)); + oxReturnError(reader->seekg(p)); + return out; +} + +}