Merge commit '69fcd7ad1056940166a5d9524b4f03578f680834' as 'deps/oxlib'
This commit is contained in:
Vendored
+47
@@ -0,0 +1,47 @@
|
||||
add_library(
|
||||
OxMetalClaw
|
||||
src/read.cpp
|
||||
src/write.cpp
|
||||
)
|
||||
|
||||
if(NOT MSVC)
|
||||
target_compile_options(OxMetalClaw PRIVATE -Wsign-conversion)
|
||||
target_compile_options(OxMetalClaw PRIVATE -Wconversion)
|
||||
endif()
|
||||
|
||||
target_link_libraries(
|
||||
OxMetalClaw PUBLIC
|
||||
OxModel
|
||||
OxStd
|
||||
)
|
||||
|
||||
if(NOT OX_BARE_METAL)
|
||||
set_property(
|
||||
TARGET
|
||||
OxMetalClaw
|
||||
PROPERTY
|
||||
POSITION_INDEPENDENT_CODE ON
|
||||
)
|
||||
endif()
|
||||
|
||||
target_include_directories(
|
||||
OxMetalClaw PUBLIC
|
||||
include
|
||||
)
|
||||
|
||||
install(
|
||||
DIRECTORY
|
||||
include/ox
|
||||
DESTINATION
|
||||
include
|
||||
)
|
||||
|
||||
install(
|
||||
TARGETS OxMetalClaw
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
)
|
||||
|
||||
if(OX_RUN_TESTS)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2015 - 2025 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 https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace ox {
|
||||
|
||||
enum {
|
||||
McPresenceMapOverflow = 1,
|
||||
McBuffEnded = 2,
|
||||
McOutputBuffEnded = 4
|
||||
};
|
||||
|
||||
}
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright 2015 - 2025 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 https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/array.hpp>
|
||||
#include <ox/std/assert.hpp>
|
||||
#include <ox/std/bit.hpp>
|
||||
#include <ox/std/byteswap.hpp>
|
||||
#include <ox/std/math.hpp>
|
||||
#include <ox/std/memops.hpp>
|
||||
#include <ox/std/reader.hpp>
|
||||
|
||||
namespace ox::mc {
|
||||
|
||||
template<Integer_c T>
|
||||
static constexpr auto Bits = sizeof(T) << 3;
|
||||
|
||||
/**
|
||||
* Returns highest bit other than possible signed bit.
|
||||
* Bit numbering starts at 0.
|
||||
*/
|
||||
template<Integer_c I>
|
||||
[[nodiscard]]
|
||||
constexpr size_t highestBit(I const val) noexcept {
|
||||
unsigned shiftStart = sizeof(I) * 8 - 1;
|
||||
// find the most significant non-sign indicator bit
|
||||
size_t highestBit = 0;
|
||||
// start at one bit lower if signed
|
||||
if constexpr(is_signed_v<I>) {
|
||||
--shiftStart;
|
||||
}
|
||||
for (auto i = shiftStart; i > 0; --i) {
|
||||
auto const bitValue = (val >> i) & 1;
|
||||
if (bitValue) {
|
||||
highestBit = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return highestBit;
|
||||
}
|
||||
|
||||
static_assert(highestBit(static_cast<int8_t>(0b10000000)) == 0);
|
||||
static_assert(highestBit(~static_cast<int8_t>(-1)) == 0);
|
||||
static_assert(highestBit(~static_cast<int8_t>(-2)) == 0);
|
||||
static_assert(highestBit(~static_cast<int8_t>(-3)) == 1);
|
||||
static_assert(highestBit(1) == 0);
|
||||
static_assert(highestBit(2) == 1);
|
||||
static_assert(highestBit(4) == 2);
|
||||
static_assert(highestBit(8) == 3);
|
||||
static_assert(highestBit(static_cast<uint64_t>(1) << 31) == 31);
|
||||
static_assert(highestBit(static_cast<uint64_t>(1) << 63) == 63);
|
||||
|
||||
struct McInt {
|
||||
Array<uint8_t, 9> data{};
|
||||
// length of integer in bytes
|
||||
size_t length = 0;
|
||||
};
|
||||
|
||||
template<Integer_c I>
|
||||
[[nodiscard]]
|
||||
constexpr McInt encodeInteger(I const pInput) noexcept {
|
||||
auto const input = ResizedInt_t<I, 64>{pInput};
|
||||
McInt out;
|
||||
auto const inputNegative = is_signed_v<I> && input < 0;
|
||||
// move input to uint64_t to allow consistent bit manipulation and to avoid
|
||||
// overflow concerns
|
||||
auto const val = std::bit_cast<uint64_t>(input);
|
||||
if (val) {
|
||||
// bits needed to represent number factoring in space possibly
|
||||
// needed for signed bit
|
||||
auto const highBit = inputNegative ? highestBit(~val) : highestBit(val);
|
||||
auto const bits = highBit + 1 + (is_signed_v<I> ? 1 : 0);
|
||||
// bytes needed to store value
|
||||
size_t bytes = bits / 8 + (bits % 8 != 0);
|
||||
auto const bitsAvailable = bytes * 8; // bits available to integer value
|
||||
auto const bitsNeeded = bits + bytes;
|
||||
// factor in bits needed for bytesIndicator (does not affect bytesIndicator)
|
||||
// bits for integer + bits needed to represent bytes > bits available
|
||||
if (bitsNeeded > bitsAvailable && bytes != 9) {
|
||||
++bytes;
|
||||
}
|
||||
auto const bytesIndicator = onMask<uint8_t>(bytes - 1);
|
||||
// ensure we are copying from little endian representation
|
||||
LittleEndian<uint64_t> leVal = val;
|
||||
if (inputNegative) {
|
||||
leVal |= static_cast<uint64_t>(1 << (bitsNeeded - 1));
|
||||
}
|
||||
if (bytes == 9) {
|
||||
out.data[0] = bytesIndicator;
|
||||
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
||||
ox::memcpy(&out.data[1], &leVal, 8);
|
||||
OX_ALLOW_UNSAFE_BUFFERS_END
|
||||
if (inputNegative) {
|
||||
out.data[1] |= 0b1000'0000;
|
||||
}
|
||||
} else {
|
||||
auto const valBits = bytes * 8;
|
||||
uint64_t const negBit = inputNegative ? 1 : 0;
|
||||
auto const intermediate =
|
||||
static_cast<uint64_t>(leVal.raw() | (negBit << (valBits - 1))) << bytes |
|
||||
static_cast<uint64_t>(bytesIndicator);
|
||||
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
||||
ox::memcpy(&out.data[0], &intermediate, sizeof(intermediate));
|
||||
OX_ALLOW_UNSAFE_BUFFERS_END
|
||||
}
|
||||
out.length = bytes;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bytes indicated by the bytes indicator of a variable
|
||||
* length integer.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
constexpr size_t countBytes(unsigned const b) noexcept {
|
||||
size_t i = 0;
|
||||
while ((b >> i) & 1) ++i;
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
static_assert(countBytes(0b0000'0000) == 1);
|
||||
static_assert(countBytes(0b0000'0001) == 2);
|
||||
static_assert(countBytes(0b0000'0011) == 3);
|
||||
static_assert(countBytes(0b0000'0111) == 4);
|
||||
static_assert(countBytes(0b0000'1111) == 5);
|
||||
static_assert(countBytes(0b0001'1111) == 6);
|
||||
static_assert(countBytes(0b0011'1111) == 7);
|
||||
static_assert(countBytes(0b0111'1111) == 8);
|
||||
static_assert(countBytes(0b1111'1111) == 9);
|
||||
|
||||
template<Integer_c I>
|
||||
constexpr Result<I> decodeInteger(Reader_c auto &rdr, size_t &bytesRead) noexcept {
|
||||
uint8_t firstByte = 0;
|
||||
OX_RETURN_ERROR(rdr.read(&firstByte, 1));
|
||||
OX_RETURN_ERROR(rdr.seekg(-1, ox::ios_base::cur));
|
||||
auto const bytes = countBytes(firstByte);
|
||||
if (bytes == 9) {
|
||||
bytesRead = bytes;
|
||||
I out = 0;
|
||||
OX_RETURN_ERROR(rdr.seekg(1, ox::ios_base::cur));
|
||||
OX_RETURN_ERROR(rdr.read(&out, sizeof(I)));
|
||||
return fromLittleEndian<I>(out);
|
||||
}
|
||||
bytesRead = bytes;
|
||||
uint64_t decoded = 0;
|
||||
OX_RETURN_ERROR(rdr.read(&decoded, bytes));
|
||||
decoded >>= bytes;
|
||||
// move sign bit
|
||||
if constexpr(is_signed_v<I>) {
|
||||
auto const negBit = bytes * 8 - bytes - 1;
|
||||
// move sign
|
||||
auto const 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
|
||||
Array<uint32_t, 2> d = {};
|
||||
//d[0] = decoded & 0xffff'ffff;
|
||||
//d[1] = decoded >> 32;
|
||||
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
||||
ox::memcpy(&d[0], &decoded, sizeof(decoded));
|
||||
OX_ALLOW_UNSAFE_BUFFERS_END
|
||||
auto bit = negBit;
|
||||
for (; bit < ox::min<size_t>(Bits<I>, 32); ++bit) {
|
||||
d[0] |= 1 << bit;
|
||||
}
|
||||
bit -= 32;
|
||||
for (; bit < Bits<I>; ++bit) {
|
||||
d[1] |= 1 << bit;
|
||||
}
|
||||
I out = 0;
|
||||
if constexpr(ox::defines::BigEndian) {
|
||||
auto const d0Tmp = d[0];
|
||||
d[0] = d[1];
|
||||
d[1] = d0Tmp;
|
||||
}
|
||||
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
||||
ox::memcpy(&out, &d[0], sizeof(out));
|
||||
OX_ALLOW_UNSAFE_BUFFERS_END
|
||||
return out;
|
||||
}
|
||||
}
|
||||
return static_cast<I>(decoded);
|
||||
}
|
||||
|
||||
template<Integer_c I>
|
||||
Result<I> decodeInteger(McInt const &m) noexcept {
|
||||
size_t bytesRead{};
|
||||
BufferReader br({reinterpret_cast<const char*>(m.data.data()), 9});
|
||||
return decodeInteger<I>(br, bytesRead);
|
||||
}
|
||||
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright 2015 - 2025 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 https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "intops.hpp"
|
||||
#include "read.hpp"
|
||||
#include "types.hpp"
|
||||
#include "write.hpp"
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright 2015 - 2025 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 https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/array.hpp>
|
||||
#include <ox/std/bit.hpp>
|
||||
#include <ox/std/error.hpp>
|
||||
#include <ox/std/span.hpp>
|
||||
#include <ox/std/types.hpp>
|
||||
#include <ox/std/reader.hpp>
|
||||
|
||||
#include "err.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
template<Reader_c Reader>
|
||||
class FieldBitmapReader {
|
||||
protected:
|
||||
mutable size_t m_mapBlockIdx = ~size_t{};
|
||||
mutable uint64_t m_mapBlock{};
|
||||
size_t m_mapStart{};
|
||||
Reader &m_reader;
|
||||
|
||||
public:
|
||||
explicit constexpr FieldBitmapReader(Reader &reader) noexcept:
|
||||
m_mapStart(reader.tellg()),
|
||||
m_reader(reader) {
|
||||
}
|
||||
|
||||
constexpr Result<bool> get(size_t idx) const noexcept {
|
||||
constexpr auto blockBits = sizeof(m_mapBlock);
|
||||
auto const blockIdx = idx / blockBits;
|
||||
if (m_mapBlockIdx != blockIdx) [[unlikely]] {
|
||||
OX_RETURN_ERROR(loadMapBlock(blockIdx));
|
||||
}
|
||||
idx %= blockBits;
|
||||
return (m_mapBlock >> idx) & 1;
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr Error loadMapBlock(size_t const idx) const noexcept {
|
||||
OX_REQUIRE(g, m_reader.tellg());
|
||||
OX_RETURN_ERROR(m_reader.seekg(static_cast<int>(m_mapStart + idx), ox::ios_base::beg));
|
||||
Array<char, sizeof(m_mapBlock)> mapBlock{};
|
||||
OX_RETURN_ERROR(m_reader.read(mapBlock.data(), sizeof(m_mapBlock)));
|
||||
// Warning: narrow-conv
|
||||
OX_RETURN_ERROR(m_reader.seekg(static_cast<int>(g), ox::ios_base::beg));
|
||||
m_mapBlock = 0;
|
||||
for (uint64_t i{}; auto b : mapBlock) {
|
||||
m_mapBlock |= static_cast<uint64_t>(std::bit_cast<uint8_t>(b)) << i;
|
||||
i += 8;
|
||||
}
|
||||
m_mapBlockIdx = idx;
|
||||
return {};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
class FieldBitmapWriter {
|
||||
protected:
|
||||
Span<char> m_map;
|
||||
size_t m_mapLen{};
|
||||
|
||||
public:
|
||||
explicit constexpr FieldBitmapWriter(Span<char> const &map) noexcept:
|
||||
m_map(map),
|
||||
m_mapLen(m_map.size()) {
|
||||
}
|
||||
|
||||
constexpr auto setBuffer(Span<char> const &map) noexcept {
|
||||
m_map = map;
|
||||
m_mapLen = map.size();
|
||||
}
|
||||
|
||||
constexpr Result<bool> get(size_t const i) const noexcept {
|
||||
if (i / 8 < m_mapLen) {
|
||||
return (std::bit_cast<uint8_t>(m_map[i / 8]) >> (i % 8)) & 1;
|
||||
}
|
||||
return Error{McPresenceMapOverflow};
|
||||
}
|
||||
|
||||
constexpr Error setFields(int const fields) noexcept {
|
||||
m_mapLen = static_cast<size_t>((fields / 8 + 1) - (fields % 8 == 0));
|
||||
if (m_mapLen > m_map.size()) [[unlikely]] {
|
||||
return Error{McPresenceMapOverflow};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
constexpr void setMaxLen(int const maxLen) noexcept {
|
||||
m_mapLen = static_cast<size_t>(maxLen);
|
||||
}
|
||||
|
||||
constexpr int64_t getMaxLen() const noexcept {
|
||||
return static_cast<int64_t>(m_mapLen);
|
||||
}
|
||||
|
||||
constexpr Error set(size_t const i, bool const on) noexcept {
|
||||
if (i / 8 < m_mapLen) {
|
||||
char &actual = m_map[i / 8];
|
||||
uint8_t v = std::bit_cast<uint8_t>(actual);
|
||||
if (on) {
|
||||
v |= 1 << (i % 8);
|
||||
} else {
|
||||
v &= ~static_cast<uint8_t>(1 << (i % 8));
|
||||
}
|
||||
actual = std::bit_cast<char>(v);
|
||||
return {};
|
||||
}
|
||||
return Error{McPresenceMapOverflow};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
+558
@@ -0,0 +1,558 @@
|
||||
/*
|
||||
* Copyright 2015 - 2025 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 https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/model/fieldcounter.hpp>
|
||||
#include <ox/model/modelhandleradaptor.hpp>
|
||||
#include <ox/model/optype.hpp>
|
||||
#include <ox/model/typenamecatcher.hpp>
|
||||
#include <ox/model/types.hpp>
|
||||
#include <ox/std/buffer.hpp>
|
||||
#include <ox/std/byteswap.hpp>
|
||||
#include <ox/std/optional.hpp>
|
||||
#include <ox/std/string.hpp>
|
||||
#include <ox/std/trace.hpp>
|
||||
#include <ox/std/vector.hpp>
|
||||
|
||||
#include "err.hpp"
|
||||
#include "intops.hpp"
|
||||
#include "presenceindicator.hpp"
|
||||
#include "types.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
template<Reader_c Reader>
|
||||
class MetalClawReaderTemplate: public ModelHandlerBase<MetalClawReaderTemplate<Reader>, ox::OpType::Read> {
|
||||
|
||||
private:
|
||||
FieldBitmapReader<Reader> m_fieldPresence;
|
||||
size_t m_fields{};
|
||||
size_t m_field{};
|
||||
Optional<int> const m_unionIdx{};
|
||||
Reader &m_reader;
|
||||
|
||||
public:
|
||||
explicit constexpr MetalClawReaderTemplate(
|
||||
Reader &reader,
|
||||
Optional<int> const &unionIdx = {}) noexcept;
|
||||
|
||||
constexpr ~MetalClawReaderTemplate() noexcept;
|
||||
|
||||
constexpr Error field(CString, int8_t *val) noexcept;
|
||||
constexpr Error field(CString, int16_t *val) noexcept;
|
||||
constexpr Error field(CString, int32_t *val) noexcept;
|
||||
constexpr Error field(CString, int64_t *val) noexcept;
|
||||
|
||||
constexpr Error field(CString, uint8_t *val) noexcept;
|
||||
constexpr Error field(CString, uint16_t *val) noexcept;
|
||||
constexpr Error field(CString, uint32_t *val) noexcept;
|
||||
constexpr Error field(CString, uint64_t *val) noexcept;
|
||||
|
||||
constexpr Error field(CString, bool *val) noexcept;
|
||||
|
||||
// array handler
|
||||
constexpr Error field(CString, auto *val, size_t valLen) noexcept;
|
||||
|
||||
// map handler
|
||||
template<typename T>
|
||||
constexpr Error field(CString, HashMap<String, T> *val) noexcept;
|
||||
|
||||
// array handler, with callback to allow handling individual elements
|
||||
template<typename T, typename CB>
|
||||
constexpr Error field(CString, CB cb) noexcept;
|
||||
|
||||
template<typename T>
|
||||
constexpr Error field(CString, T *val) noexcept;
|
||||
|
||||
template<typename U, bool force>
|
||||
constexpr Error field(CString, UnionView<U, force> val) noexcept;
|
||||
|
||||
template<size_t SmallStringSize>
|
||||
constexpr Error field(CString, BasicString<SmallStringSize> *val) noexcept;
|
||||
|
||||
template<size_t L>
|
||||
constexpr Error field(CString, IString<L> *val) noexcept;
|
||||
|
||||
constexpr Error fieldCString(CString, char *val, size_t buffLen) noexcept;
|
||||
|
||||
constexpr Error fieldCString(CString, char **val) noexcept;
|
||||
|
||||
constexpr Error fieldCString(CString, char **val, size_t buffLen) noexcept;
|
||||
|
||||
/**
|
||||
* Reads an array length from the current location in the buffer.
|
||||
* @param pass indicates that the parsing should iterate past the array length
|
||||
*/
|
||||
constexpr Result<ArrayLength> arrayLength(const char *name, bool pass = true) noexcept;
|
||||
|
||||
/**
|
||||
* Reads an string length from the current location in the buffer.
|
||||
*/
|
||||
constexpr Result<StringLength> stringLength(const char *name) noexcept;
|
||||
|
||||
template<typename T = std::nullptr_t>
|
||||
constexpr ox::Error setTypeInfo(
|
||||
const char *name = T::TypeName,
|
||||
int version = T::TypeVersion,
|
||||
const Vector<String>& = {},
|
||||
size_t fields = ModelFieldCount_v<T>) noexcept;
|
||||
|
||||
/**
|
||||
* Returns a MetalClawReader to parse a child object.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
constexpr MetalClawReaderTemplate<Reader> child(const char *name, Optional<int> unionIdx = {}) noexcept;
|
||||
|
||||
/**
|
||||
* Indicates whether or not the next field to be read is present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
constexpr bool fieldPresent(const char *name) const noexcept;
|
||||
|
||||
/**
|
||||
* Indicates whether or not the given field is present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
constexpr bool fieldPresent(int fieldNo) const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr int whichFieldPresent(const char *name, ModelUnion const&) const noexcept;
|
||||
|
||||
constexpr void nextField() noexcept;
|
||||
|
||||
private:
|
||||
template<typename I>
|
||||
constexpr Error readInteger(I &val) noexcept;
|
||||
|
||||
};
|
||||
|
||||
template<Reader_c Reader>
|
||||
constexpr MetalClawReaderTemplate<Reader>::MetalClawReaderTemplate(
|
||||
Reader &reader,
|
||||
Optional<int> const &unionIdx) noexcept:
|
||||
m_fieldPresence(reader),
|
||||
m_unionIdx(unionIdx),
|
||||
m_reader(reader) {
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
constexpr MetalClawReaderTemplate<Reader>::~MetalClawReaderTemplate() noexcept {
|
||||
if (m_field != m_fields) {
|
||||
oxTrace("ox.mc.MetalClawReader.error") << "MetalClawReader: incorrect fields number given";
|
||||
}
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
constexpr Error MetalClawReaderTemplate<Reader>::field(CString, int8_t *val) noexcept {
|
||||
return readInteger(*val);
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
constexpr Error MetalClawReaderTemplate<Reader>::field(CString, int16_t *val) noexcept {
|
||||
return readInteger(*val);
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
constexpr Error MetalClawReaderTemplate<Reader>::field(CString, int32_t *val) noexcept {
|
||||
return readInteger(*val);
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
constexpr Error MetalClawReaderTemplate<Reader>::field(CString, int64_t *val) noexcept {
|
||||
return readInteger(*val);
|
||||
}
|
||||
|
||||
|
||||
template<Reader_c Reader>
|
||||
constexpr Error MetalClawReaderTemplate<Reader>::field(CString, uint8_t *val) noexcept {
|
||||
return readInteger(*val);
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
constexpr Error MetalClawReaderTemplate<Reader>::field(CString, uint16_t *val) noexcept {
|
||||
return readInteger(*val);
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
constexpr Error MetalClawReaderTemplate<Reader>::field(CString, uint32_t *val) noexcept {
|
||||
return readInteger(*val);
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
constexpr Error MetalClawReaderTemplate<Reader>::field(CString, uint64_t *val) noexcept {
|
||||
return readInteger(*val);
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
constexpr Error MetalClawReaderTemplate<Reader>::field(CString, bool *val) noexcept {
|
||||
if (!m_unionIdx.has_value() || static_cast<size_t>(*m_unionIdx) == m_field) {
|
||||
auto const result = m_fieldPresence.get(static_cast<size_t>(m_field));
|
||||
*val = result.value;
|
||||
OX_RETURN_ERROR(result);
|
||||
}
|
||||
++m_field;
|
||||
return {};
|
||||
}
|
||||
|
||||
// array handler
|
||||
template<Reader_c Reader>
|
||||
constexpr Error MetalClawReaderTemplate<Reader>::field(
|
||||
const char *name, auto *val, size_t const valLen) noexcept {
|
||||
if (!m_unionIdx.has_value() || static_cast<size_t>(*m_unionIdx) == m_field) {
|
||||
if (m_fieldPresence.get(static_cast<size_t>(m_field))) {
|
||||
// read the length
|
||||
size_t bytesRead = 0;
|
||||
OX_REQUIRE(len, mc::decodeInteger<ArrayLength>(m_reader, bytesRead));
|
||||
// read the list
|
||||
if (valLen >= len) {
|
||||
auto reader = child({});
|
||||
auto &handler = *reader.interface();
|
||||
OX_RETURN_ERROR(handler.setTypeInfo("List", 0, {}, static_cast<size_t>(len)));
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
||||
OX_RETURN_ERROR(handler.field({}, &val[i]));
|
||||
OX_ALLOW_UNSAFE_BUFFERS_END
|
||||
}
|
||||
} else {
|
||||
oxTracef("ox.mc.read.field(T)", "{}, length: {}", name, valLen);
|
||||
return ox::Error(McOutputBuffEnded);
|
||||
}
|
||||
}
|
||||
}
|
||||
++m_field;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
template<typename T>
|
||||
constexpr Error MetalClawReaderTemplate<Reader>::field(CString, HashMap<String, T> *val) noexcept {
|
||||
if (!m_unionIdx.has_value() || static_cast<size_t>(*m_unionIdx) == m_field) {
|
||||
if (m_fieldPresence.get(static_cast<size_t>(m_field))) {
|
||||
// read the length
|
||||
OX_REQUIRE(g, m_reader.tellg());
|
||||
size_t bytesRead = 0;
|
||||
OX_REQUIRE(len, mc::decodeInteger<ArrayLength>(m_reader, bytesRead));
|
||||
OX_RETURN_ERROR(m_reader.seekg(g));
|
||||
// read the list
|
||||
auto reader = child("");
|
||||
auto &handler = *reader.interface();
|
||||
OX_RETURN_ERROR(handler.setTypeInfo("List", 0, {}, static_cast<size_t>(len)));
|
||||
// this loop body needs to be in a lambda because of the potential alloca call
|
||||
constexpr auto loopBody = [](auto &handler, auto &val) {
|
||||
OX_REQUIRE(keyLen, handler.stringLength(nullptr));
|
||||
auto wkey = ox_malloca(keyLen + 1, char, 0);
|
||||
auto wkeyPtr = wkey.get();
|
||||
OX_RETURN_ERROR(handler.fieldCString("", &wkeyPtr, keyLen + 1));
|
||||
return handler.field("", &val[wkeyPtr]);
|
||||
};
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
OX_RETURN_ERROR(loopBody(handler, *val));
|
||||
}
|
||||
}
|
||||
}
|
||||
++m_field;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
template<typename T>
|
||||
constexpr Error MetalClawReaderTemplate<Reader>::field(const char *name, T *val) noexcept {
|
||||
if constexpr(isVector_v<T>) {
|
||||
if (!m_unionIdx.has_value() || static_cast<size_t>(*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<size_t>(m_field))) {
|
||||
OX_REQUIRE(len, arrayLength(name, false));
|
||||
OX_RETURN_ERROR(ox::resizeVector(*val, len));
|
||||
return field(name, val->data(), val->size());
|
||||
}
|
||||
OX_RETURN_ERROR(ox::resizeVector(*val, 0));
|
||||
}
|
||||
++m_field;
|
||||
return {};
|
||||
} else if constexpr(isArray_v<T>) {
|
||||
if (!m_unionIdx.has_value() || static_cast<size_t>(*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<size_t>(m_field))) {
|
||||
OX_REQUIRE(len, arrayLength(name, false));
|
||||
if (len > val->size()) {
|
||||
return ox::Error(1, "Input array is too long");
|
||||
}
|
||||
}
|
||||
return field(name, val->data(), val->size());
|
||||
}
|
||||
++m_field;
|
||||
return {};
|
||||
} else {
|
||||
if ((!m_unionIdx.has_value() || static_cast<size_t>(*m_unionIdx) == m_field) && val) {
|
||||
if (m_fieldPresence.get(static_cast<size_t>(m_field))) {
|
||||
auto reader = child("");
|
||||
OX_RETURN_ERROR(model(reader.interface(), val));
|
||||
}
|
||||
}
|
||||
++m_field;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
template<typename U, bool force>
|
||||
constexpr Error MetalClawReaderTemplate<Reader>::field(CString, UnionView<U, force> val) noexcept {
|
||||
if ((!m_unionIdx.has_value() || static_cast<size_t>(*m_unionIdx) == m_field) && val.get()) {
|
||||
if (m_fieldPresence.get(static_cast<size_t>(m_field))) {
|
||||
auto reader = child("", ox::Optional<int>(ox::in_place, val.idx()));
|
||||
OX_RETURN_ERROR(model(reader.interface(), val.get()));
|
||||
}
|
||||
}
|
||||
++m_field;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
template<size_t SmallStringSize>
|
||||
constexpr Error MetalClawReaderTemplate<Reader>::field(CString, BasicString<SmallStringSize> *val) noexcept {
|
||||
if (!m_unionIdx.has_value() || static_cast<size_t>(*m_unionIdx) == m_field) {
|
||||
if (m_fieldPresence.get(static_cast<size_t>(m_field))) {
|
||||
// read the length
|
||||
size_t bytesRead = 0;
|
||||
OX_REQUIRE(size, mc::decodeInteger<StringLength>(m_reader, bytesRead));
|
||||
const auto cap = size;
|
||||
*val = BasicString<SmallStringSize>(cap);
|
||||
auto data = val->data();
|
||||
// read the string
|
||||
OX_RETURN_ERROR(m_reader.read(data, size));
|
||||
} else {
|
||||
*val = "";
|
||||
}
|
||||
}
|
||||
++m_field;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
template<size_t L>
|
||||
constexpr Error MetalClawReaderTemplate<Reader>::field(CString, IString<L> *val) noexcept {
|
||||
if (!m_unionIdx.has_value() || static_cast<size_t>(*m_unionIdx) == m_field) {
|
||||
if (m_fieldPresence.get(static_cast<size_t>(m_field))) {
|
||||
// read the length
|
||||
size_t bytesRead = 0;
|
||||
OX_REQUIRE(size, mc::decodeInteger<StringLength>(m_reader, bytesRead));
|
||||
*val = IString<L>();
|
||||
OX_RETURN_ERROR(val->resize(size));
|
||||
auto const data = val->data();
|
||||
// read the string
|
||||
OX_RETURN_ERROR(m_reader.read(data, size));
|
||||
} else {
|
||||
*val = "";
|
||||
}
|
||||
}
|
||||
++m_field;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
constexpr Error MetalClawReaderTemplate<Reader>::fieldCString(
|
||||
CString, char *val, size_t const buffLen) noexcept {
|
||||
if (m_fieldPresence.get(static_cast<size_t>(m_field))) {
|
||||
// read the length
|
||||
size_t bytesRead = 0;
|
||||
OX_REQUIRE(size, mc::decodeInteger<StringLength>(m_reader, bytesRead));
|
||||
if (size > buffLen) {
|
||||
return ox::Error(McOutputBuffEnded);
|
||||
}
|
||||
// re-allocate in case too small
|
||||
auto data = val;
|
||||
// read the string
|
||||
OX_RETURN_ERROR(m_reader.read(data, size));
|
||||
data[size] = 0;
|
||||
}
|
||||
++m_field;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
constexpr Error MetalClawReaderTemplate<Reader>::fieldCString(CString, char **val) noexcept {
|
||||
if (m_fieldPresence.get(static_cast<size_t>(m_field))) {
|
||||
// read the length
|
||||
size_t bytesRead = 0;
|
||||
OX_REQUIRE(size, mc::decodeInteger<StringLength>(m_reader, bytesRead));
|
||||
// re-allocate in case too small
|
||||
safeDelete(*val);
|
||||
*val = new char[size + 1];
|
||||
auto data = ox::Span{*val, size + 1};
|
||||
// read the string
|
||||
OX_RETURN_ERROR(m_reader.read(data.data(), size));
|
||||
data[size] = 0;
|
||||
}
|
||||
++m_field;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
constexpr Error MetalClawReaderTemplate<Reader>::fieldCString(CString, char **val, size_t buffLen) noexcept {
|
||||
if (!m_unionIdx.has_value() || static_cast<size_t>(*m_unionIdx) == m_field) {
|
||||
if (m_fieldPresence.get(static_cast<size_t>(m_field))) {
|
||||
// read the length
|
||||
size_t bytesRead = 0;
|
||||
OX_REQUIRE(size, mc::decodeInteger<StringLength>(m_reader, bytesRead));
|
||||
// re-allocate if too small
|
||||
if (buffLen < size + 1) {
|
||||
safeDelete(*val);
|
||||
*val = new char[size + 1];
|
||||
buffLen = size + 1;
|
||||
}
|
||||
auto data = ox::Span{*val, size + 1};
|
||||
// read the string
|
||||
OX_RETURN_ERROR(m_reader.read(data.data(), size));
|
||||
data[size] = 0;
|
||||
} else {
|
||||
auto data = *val;
|
||||
if (data) {
|
||||
data[0] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
++m_field;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
constexpr Result<ArrayLength> MetalClawReaderTemplate<Reader>::arrayLength(CString, bool const pass) noexcept {
|
||||
if (!m_unionIdx.has_value() || static_cast<size_t>(*m_unionIdx) == m_field) {
|
||||
if (m_fieldPresence.get(static_cast<size_t>(m_field))) {
|
||||
// read the length
|
||||
size_t bytesRead = 0;
|
||||
OX_REQUIRE(g, m_reader.tellg());
|
||||
OX_REQUIRE(out, mc::decodeInteger<ArrayLength>(m_reader, bytesRead));
|
||||
if (!pass) {
|
||||
OX_RETURN_ERROR(m_reader.seekg(g));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
return ox::Error(1);
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
constexpr Result<StringLength> MetalClawReaderTemplate<Reader>::stringLength(CString) noexcept {
|
||||
if (!m_unionIdx.has_value() || static_cast<size_t>(*m_unionIdx) == m_field) {
|
||||
if (m_fieldPresence.get(static_cast<size_t>(m_field))) {
|
||||
// read the length
|
||||
size_t bytesRead = 0;
|
||||
auto len = mc::decodeInteger<StringLength>(m_reader, bytesRead);
|
||||
OX_RETURN_ERROR(m_reader.seekg(-static_cast<int64_t>(bytesRead), ox::ios_base::cur));
|
||||
return len;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
template<typename I>
|
||||
constexpr Error MetalClawReaderTemplate<Reader>::readInteger(I &val) noexcept {
|
||||
if (!m_unionIdx.has_value() || static_cast<size_t>(*m_unionIdx) == m_field) {
|
||||
if (m_fieldPresence.get(static_cast<size_t>(m_field))) {
|
||||
size_t bytesRead = 0;
|
||||
auto const result = mc::decodeInteger<I>(m_reader, bytesRead);
|
||||
OX_RETURN_ERROR(result);
|
||||
val = result.value;
|
||||
} else {
|
||||
val = 0;
|
||||
}
|
||||
}
|
||||
++m_field;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
template<typename T, typename CB>
|
||||
constexpr Error MetalClawReaderTemplate<Reader>::field(CString, CB cb) noexcept {
|
||||
if (!m_unionIdx.has_value() || static_cast<size_t>(*m_unionIdx) == m_field) {
|
||||
if (m_fieldPresence.get(static_cast<size_t>(m_field))) {
|
||||
// read the length
|
||||
size_t bytesRead = 0;
|
||||
OX_REQUIRE(len, mc::decodeInteger<ArrayLength>(m_reader, bytesRead));
|
||||
// read the list
|
||||
auto reader = child("");
|
||||
auto &handler = *reader.interface();
|
||||
OX_RETURN_ERROR(handler.setTypeInfo("List", 0, {}, static_cast<size_t>(len)));
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
T val;
|
||||
OX_RETURN_ERROR(handler.field("", &val));
|
||||
OX_RETURN_ERROR(cb(i, &val));
|
||||
}
|
||||
}
|
||||
}
|
||||
++m_field;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
template<typename T>
|
||||
constexpr ox::Error MetalClawReaderTemplate<Reader>::setTypeInfo(
|
||||
CString, int, const Vector<String>&, size_t const fields) noexcept {
|
||||
m_fields = fields;
|
||||
// Warning: narrow-conv
|
||||
return m_reader.seekg(
|
||||
static_cast<int>((fields / 8 + 1) - (fields % 8 == 0)),
|
||||
ox::ios_base::cur);
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
constexpr MetalClawReaderTemplate<Reader> MetalClawReaderTemplate<Reader>::child(
|
||||
CString,
|
||||
Optional<int> const unionIdx) noexcept {
|
||||
return MetalClawReaderTemplate<Reader>(m_reader, unionIdx);
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
constexpr bool MetalClawReaderTemplate<Reader>::fieldPresent(CString) const noexcept {
|
||||
return m_fieldPresence.get(static_cast<size_t>(m_field)).value;
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
constexpr bool MetalClawReaderTemplate<Reader>::fieldPresent(int const fieldNo) const noexcept {
|
||||
return m_fieldPresence.get(static_cast<size_t>(fieldNo)).value;
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
[[nodiscard]]
|
||||
constexpr int MetalClawReaderTemplate<Reader>::whichFieldPresent(CString, ModelUnion const &u) const noexcept {
|
||||
FieldBitmapReader<Reader> p(m_reader);
|
||||
for (auto i = 0u; i < u.fieldCount(); ++i) {
|
||||
if (p.get(i)) {
|
||||
return static_cast<int>(i);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
template<Reader_c Reader>
|
||||
constexpr void MetalClawReaderTemplate<Reader>::nextField() noexcept {
|
||||
++m_field;
|
||||
}
|
||||
|
||||
using MetalClawReader = MetalClawReaderTemplate<ox::BufferReader>;
|
||||
|
||||
template<typename T>
|
||||
Error readMC(ox::BufferView const buff, T &val) noexcept {
|
||||
BufferReader br(buff);
|
||||
MetalClawReader reader(br);
|
||||
return model(reader.interface(), &val);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Result<T> readMC(ox::BufferView buff) noexcept {
|
||||
Result<T> val;
|
||||
OX_RETURN_ERROR(readMC(buff, val.value));
|
||||
return val;
|
||||
}
|
||||
|
||||
extern template class ModelHandlerInterface<MetalClawReaderTemplate<BufferReader>>;
|
||||
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2015 - 2025 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 https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/string.hpp>
|
||||
#include <ox/std/strops.hpp>
|
||||
#include <ox/std/types.hpp>
|
||||
|
||||
namespace ox {
|
||||
|
||||
using StringLength = std::size_t;
|
||||
using ArrayLength = std::size_t;
|
||||
|
||||
}
|
||||
+401
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
* Copyright 2015 - 2025 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 https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/model/fieldcounter.hpp>
|
||||
#include <ox/model/modelhandleradaptor.hpp>
|
||||
#include <ox/model/optype.hpp>
|
||||
#include <ox/model/types.hpp>
|
||||
#include <ox/std/bit.hpp>
|
||||
#include <ox/std/buffer.hpp>
|
||||
#include <ox/std/byteswap.hpp>
|
||||
#include <ox/std/hashmap.hpp>
|
||||
#include <ox/std/optional.hpp>
|
||||
#include <ox/std/string.hpp>
|
||||
#include <ox/std/types.hpp>
|
||||
#include <ox/std/units.hpp>
|
||||
|
||||
#include "intops.hpp"
|
||||
#include "err.hpp"
|
||||
#include "presenceindicator.hpp"
|
||||
#include "types.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
template<Writer_c Writer>
|
||||
class MetalClawWriter: public ModelHandlerBase<MetalClawWriter<Writer>, OpType::Write> {
|
||||
|
||||
private:
|
||||
Vector<char, 16> m_presenceMapBuff{};
|
||||
FieldBitmapWriter m_fieldPresence{m_presenceMapBuff};
|
||||
int m_field{};
|
||||
Optional<int> m_unionIdx;
|
||||
size_t m_writerBeginP{};
|
||||
Writer &m_writer;
|
||||
|
||||
public:
|
||||
constexpr explicit MetalClawWriter(Writer &writer, Optional<int> const &unionIdx = {}) noexcept;
|
||||
|
||||
constexpr ~MetalClawWriter() noexcept = default;
|
||||
|
||||
constexpr Error field(CString, int8_t const *val) noexcept;
|
||||
constexpr Error field(CString, int16_t const *val) noexcept;
|
||||
constexpr Error field(CString, int32_t const *val) noexcept;
|
||||
constexpr Error field(CString, int64_t const *val) noexcept;
|
||||
|
||||
constexpr Error field(CString, uint8_t const *val) noexcept;
|
||||
constexpr Error field(CString, uint16_t const *val) noexcept;
|
||||
constexpr Error field(CString, uint32_t const *val) noexcept;
|
||||
constexpr Error field(CString, uint64_t const *val) noexcept;
|
||||
|
||||
constexpr Error field(CString, bool const *val) noexcept;
|
||||
|
||||
template<typename T>
|
||||
constexpr Error field(CString, T const *val, size_t len) noexcept;
|
||||
|
||||
template<typename T>
|
||||
constexpr Error field(CString name, HashMap<String, T> const *val) noexcept;
|
||||
|
||||
template<size_t SmallStringSize>
|
||||
constexpr Error field(CString, BasicString<SmallStringSize> const *val) noexcept;
|
||||
|
||||
template<size_t L>
|
||||
constexpr Error field(CString, IString<L> const *val) noexcept;
|
||||
|
||||
constexpr Error fieldCString(CString name, CString const*val, size_t buffLen) noexcept;
|
||||
|
||||
constexpr Error fieldCString(CString name, CString *val) noexcept;
|
||||
|
||||
constexpr Error fieldCString(CString name, CString const*val) noexcept;
|
||||
|
||||
constexpr Error fieldCString(CString name, CString val, size_t strLen) noexcept;
|
||||
|
||||
template<typename T>
|
||||
constexpr Error field(CString, T const *val) noexcept;
|
||||
|
||||
template<typename U, bool force = false>
|
||||
constexpr Error field(CString, UnionView<U, force> val) noexcept;
|
||||
|
||||
template<typename T = std::nullptr_t>
|
||||
constexpr Error setTypeInfo(
|
||||
CString name = T::TypeName,
|
||||
int version = T::TypeVersion,
|
||||
Vector<String> const& = {},
|
||||
size_t fields = ModelFieldCount_v<T>) noexcept;
|
||||
|
||||
/**
|
||||
* stringLength is not implemented in MetalClawWriter
|
||||
*/
|
||||
[[nodiscard]]
|
||||
constexpr auto stringLength(CString) noexcept {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* stringLength is not implemented in MetalClawWriter
|
||||
*/
|
||||
[[nodiscard]]
|
||||
constexpr auto arrayLength(CString, bool = true) noexcept {
|
||||
return 0;
|
||||
}
|
||||
|
||||
constexpr Error finalize() noexcept;
|
||||
|
||||
private:
|
||||
constexpr Error appendInteger(Integer_c auto val) noexcept {
|
||||
bool fieldSet = false;
|
||||
if (val && (!m_unionIdx.has_value() || *m_unionIdx == m_field)) {
|
||||
auto mi = mc::encodeInteger(val);
|
||||
OX_RETURN_ERROR(m_writer.write(reinterpret_cast<CString>(mi.data.data()), mi.length));
|
||||
fieldSet = true;
|
||||
}
|
||||
OX_RETURN_ERROR(m_fieldPresence.set(static_cast<size_t>(m_field), fieldSet));
|
||||
++m_field;
|
||||
return {};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
extern template class ModelHandlerInterface<MetalClawWriter<BufferWriter>>;
|
||||
extern template class ModelHandlerInterface<MetalClawWriter<CharBuffWriter>>;
|
||||
|
||||
template<Writer_c Writer>
|
||||
constexpr MetalClawWriter<Writer>::MetalClawWriter(Writer &writer, Optional<int> const &unionIdx) noexcept:
|
||||
m_unionIdx(unionIdx),
|
||||
m_writerBeginP(writer.tellp()),
|
||||
m_writer(writer) {
|
||||
}
|
||||
|
||||
template<Writer_c Writer>
|
||||
constexpr Error MetalClawWriter<Writer>::field(CString, int8_t const *val) noexcept {
|
||||
return appendInteger(*val);
|
||||
}
|
||||
|
||||
template<Writer_c Writer>
|
||||
constexpr Error MetalClawWriter<Writer>::field(CString, int16_t const *val) noexcept {
|
||||
return appendInteger(*val);
|
||||
}
|
||||
|
||||
template<Writer_c Writer>
|
||||
constexpr Error MetalClawWriter<Writer>::field(CString, int32_t const *val) noexcept {
|
||||
return appendInteger(*val);
|
||||
}
|
||||
|
||||
template<Writer_c Writer>
|
||||
constexpr Error MetalClawWriter<Writer>::field(CString, int64_t const *val) noexcept {
|
||||
return appendInteger(*val);
|
||||
}
|
||||
|
||||
template<Writer_c Writer>
|
||||
constexpr Error MetalClawWriter<Writer>::field(CString, uint8_t const *val) noexcept {
|
||||
return appendInteger(*val);
|
||||
}
|
||||
|
||||
template<Writer_c Writer>
|
||||
constexpr Error MetalClawWriter<Writer>::field(CString, uint16_t const *val) noexcept {
|
||||
return appendInteger(*val);
|
||||
}
|
||||
|
||||
template<Writer_c Writer>
|
||||
constexpr Error MetalClawWriter<Writer>::field(CString, uint32_t const *val) noexcept {
|
||||
return appendInteger(*val);
|
||||
}
|
||||
|
||||
template<Writer_c Writer>
|
||||
constexpr Error MetalClawWriter<Writer>::field(CString, uint64_t const *val) noexcept {
|
||||
return appendInteger(*val);
|
||||
}
|
||||
|
||||
template<Writer_c Writer>
|
||||
constexpr Error MetalClawWriter<Writer>::field(CString, bool const *val) noexcept {
|
||||
if (!m_unionIdx.has_value() || *m_unionIdx == m_field) {
|
||||
OX_RETURN_ERROR(m_fieldPresence.set(static_cast<size_t>(m_field), *val));
|
||||
}
|
||||
++m_field;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Writer_c Writer>
|
||||
template<size_t SmallStringSize>
|
||||
constexpr Error MetalClawWriter<Writer>::field(CString, BasicString<SmallStringSize> const *val) noexcept {
|
||||
bool fieldSet = false;
|
||||
if (val->size() && (!m_unionIdx.has_value() || *m_unionIdx == m_field)) {
|
||||
// write the length
|
||||
auto const strLen = mc::encodeInteger(val->size());
|
||||
OX_RETURN_ERROR(m_writer.write(reinterpret_cast<CString>(strLen.data.data()), strLen.length));
|
||||
// write the string
|
||||
OX_RETURN_ERROR(m_writer.write(val->c_str(), static_cast<size_t>(val->size())));
|
||||
fieldSet = true;
|
||||
}
|
||||
OX_RETURN_ERROR(m_fieldPresence.set(static_cast<size_t>(m_field), fieldSet));
|
||||
++m_field;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Writer_c Writer>
|
||||
template<size_t L>
|
||||
constexpr Error MetalClawWriter<Writer>::field(CString name, IString<L> const *val) noexcept {
|
||||
return fieldCString(name, val->data(), val->size());
|
||||
}
|
||||
|
||||
template<Writer_c Writer>
|
||||
constexpr Error MetalClawWriter<Writer>::fieldCString(CString, CString const *val, size_t) noexcept {
|
||||
bool fieldSet = false;
|
||||
if (!m_unionIdx.has_value() || *m_unionIdx == m_field) {
|
||||
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
||||
// this strlen is tolerated because sometimes 0 gets passed to
|
||||
// the size param, which is a lie
|
||||
// this code should be cleaned up at some point...
|
||||
auto const strLen = *val ? ox::strlen(*val) : 0;
|
||||
OX_ALLOW_UNSAFE_BUFFERS_END
|
||||
// write the length
|
||||
auto const strLenBuff = mc::encodeInteger(strLen);
|
||||
OX_RETURN_ERROR(m_writer.write(reinterpret_cast<CString>(strLenBuff.data.data()), strLenBuff.length));
|
||||
// write the string
|
||||
OX_RETURN_ERROR(m_writer.write(*val, static_cast<size_t>(strLen)));
|
||||
fieldSet = true;
|
||||
}
|
||||
OX_RETURN_ERROR(m_fieldPresence.set(static_cast<size_t>(m_field), fieldSet));
|
||||
++m_field;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Writer_c Writer>
|
||||
constexpr Error MetalClawWriter<Writer>::fieldCString(CString const name, CString *val) noexcept {
|
||||
return fieldCString(name, val, {});
|
||||
}
|
||||
|
||||
template<Writer_c Writer>
|
||||
constexpr Error MetalClawWriter<Writer>::fieldCString(CString const name, CString const *val) noexcept {
|
||||
return fieldCString(name, val, {});
|
||||
}
|
||||
|
||||
template<Writer_c Writer>
|
||||
constexpr Error MetalClawWriter<Writer>::fieldCString(CString, CString const val, size_t const strLen) noexcept {
|
||||
bool fieldSet = false;
|
||||
if (strLen && (!m_unionIdx.has_value() || *m_unionIdx == m_field)) {
|
||||
// write the length
|
||||
auto const strLenBuff = mc::encodeInteger(strLen);
|
||||
OX_RETURN_ERROR(m_writer.write(reinterpret_cast<CString>(strLenBuff.data.data()), strLenBuff.length));
|
||||
// write the string
|
||||
OX_RETURN_ERROR(m_writer.write(val, static_cast<size_t>(strLen)));
|
||||
fieldSet = true;
|
||||
}
|
||||
OX_RETURN_ERROR(m_fieldPresence.set(static_cast<size_t>(m_field), fieldSet));
|
||||
++m_field;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Writer_c Writer>
|
||||
template<typename T>
|
||||
constexpr Error MetalClawWriter<Writer>::field(CString, T const *val) noexcept {
|
||||
if constexpr(isVector_v<T> || isArray_v<T>) {
|
||||
return field(nullptr, val->data(), val->size());
|
||||
} else {
|
||||
bool fieldSet = false;
|
||||
if (val && (!m_unionIdx.has_value() || *m_unionIdx == m_field)) {
|
||||
auto const writeIdx = m_writer.tellp();
|
||||
MetalClawWriter writer(m_writer);
|
||||
OX_RETURN_ERROR(model(writer.interface(), val));
|
||||
OX_RETURN_ERROR(writer.finalize());
|
||||
fieldSet = writeIdx != m_writer.tellp();
|
||||
}
|
||||
OX_RETURN_ERROR(m_fieldPresence.set(static_cast<size_t>(m_field), fieldSet));
|
||||
++m_field;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
template<Writer_c Writer>
|
||||
template<typename U, bool force>
|
||||
constexpr Error MetalClawWriter<Writer>::field(CString, UnionView<U, force> val) noexcept {
|
||||
bool fieldSet = false;
|
||||
if (val.get() && (!m_unionIdx.has_value() || *m_unionIdx == m_field)) {
|
||||
auto const writeIdx = m_writer.tellp();
|
||||
MetalClawWriter writer(m_writer, Optional<int>(in_place, val.idx()));
|
||||
OX_RETURN_ERROR(model(writer.interface(), val.get()));
|
||||
OX_RETURN_ERROR(writer.finalize());
|
||||
fieldSet = writeIdx != m_writer.tellp();
|
||||
}
|
||||
OX_RETURN_ERROR(m_fieldPresence.set(static_cast<size_t>(m_field), fieldSet));
|
||||
++m_field;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Writer_c Writer>
|
||||
template<typename T>
|
||||
constexpr Error MetalClawWriter<Writer>::field(CString, T const *val, size_t const len) noexcept {
|
||||
bool fieldSet = false;
|
||||
if (len && (!m_unionIdx.has_value() || *m_unionIdx == m_field)) {
|
||||
// write the length
|
||||
auto const arrLen = mc::encodeInteger(len);
|
||||
OX_RETURN_ERROR(m_writer.write(reinterpret_cast<CString>(arrLen.data.data()), arrLen.length));
|
||||
auto const writeIdx = m_writer.tellp();
|
||||
MetalClawWriter writer(m_writer);
|
||||
OX_RETURN_ERROR(writer.interface()->template setTypeInfo<T>("List", 0, {}, static_cast<size_t>(len)));
|
||||
// write the array
|
||||
for (size_t i{}; i < len; ++i) {
|
||||
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
||||
OX_RETURN_ERROR(writer.interface()->field("", &val[i]));
|
||||
OX_ALLOW_UNSAFE_BUFFERS_END
|
||||
}
|
||||
OX_RETURN_ERROR(writer.finalize());
|
||||
fieldSet = writeIdx != m_writer.tellp();
|
||||
}
|
||||
OX_RETURN_ERROR(m_fieldPresence.set(static_cast<size_t>(m_field), fieldSet));
|
||||
++m_field;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Writer_c Writer>
|
||||
template<typename T>
|
||||
constexpr Error MetalClawWriter<Writer>::field(CString, HashMap<String, T> const *val) noexcept {
|
||||
auto const &keys = val->keys();
|
||||
auto const len = keys.size();
|
||||
bool fieldSet = false;
|
||||
if (len && (!m_unionIdx.has_value() || *m_unionIdx == m_field)) {
|
||||
// write the length
|
||||
auto const arrLen = mc::encodeInteger(len);
|
||||
OX_RETURN_ERROR(m_writer.write(reinterpret_cast<CString>(arrLen.data.data()), arrLen.length));
|
||||
// write map
|
||||
MetalClawWriter writer(m_writer);
|
||||
// double len for both key and value
|
||||
OX_RETURN_ERROR(writer.interface()->setTypeInfo("Map", 0, {}, len * 2));
|
||||
// this loop body needs to be in a lambda because of the potential alloca call
|
||||
constexpr auto loopBody = [](auto &handler, auto const &key, auto const &val) -> Error {
|
||||
auto const keyLen = key.size();
|
||||
auto wkey = ox_malloca(keyLen + 1, char, 0);
|
||||
memcpy(wkey.get(), key.c_str(), keyLen + 1);
|
||||
OX_RETURN_ERROR(handler.fieldCString("", wkey.get(), keyLen));
|
||||
OX_REQUIRE_M(value, val.at(key));
|
||||
return handler.field("", value);
|
||||
};
|
||||
// write the array
|
||||
for (size_t i{}; i < len; ++i) {
|
||||
auto const &key = keys[i];
|
||||
OX_RETURN_ERROR(loopBody(*writer.interface(), key, *val));
|
||||
}
|
||||
OX_RETURN_ERROR(writer.finalize());
|
||||
fieldSet = true;
|
||||
}
|
||||
OX_RETURN_ERROR(m_fieldPresence.set(static_cast<size_t>(m_field), fieldSet));
|
||||
++m_field;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Writer_c Writer>
|
||||
template<typename T>
|
||||
constexpr Error MetalClawWriter<Writer>::setTypeInfo(
|
||||
CString,
|
||||
int,
|
||||
Vector<String> const&,
|
||||
size_t const fields) noexcept {
|
||||
auto const fieldPresenceLen = (fields - 1) / 8 + 1;
|
||||
OX_RETURN_ERROR(m_writer.write(nullptr, fieldPresenceLen));
|
||||
m_presenceMapBuff.resize(fieldPresenceLen);
|
||||
m_fieldPresence.setBuffer(m_presenceMapBuff);
|
||||
return m_fieldPresence.setFields(static_cast<int>(fields));
|
||||
}
|
||||
|
||||
template<Writer_c Writer>
|
||||
constexpr Error MetalClawWriter<Writer>::finalize() noexcept {
|
||||
auto const end = m_writer.tellp();
|
||||
OX_RETURN_ERROR(m_writer.seekp(m_writerBeginP));
|
||||
OX_RETURN_ERROR(m_writer.write(
|
||||
m_presenceMapBuff.data(),
|
||||
m_presenceMapBuff.size()));
|
||||
OX_RETURN_ERROR(m_writer.seekp(end));
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<Buffer> writeMC(Writer_c auto &writer, auto const &val) noexcept {
|
||||
MetalClawWriter mcWriter(writer);
|
||||
OX_RETURN_ERROR(model(mcWriter.interface(), &val));
|
||||
OX_RETURN_ERROR(mcWriter.finalize());
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<Buffer> writeMC(auto const &val, size_t const buffReserveSz = 2 * units::KB) noexcept {
|
||||
Buffer buff(buffReserveSz);
|
||||
BufferWriter bw(&buff, 0);
|
||||
OX_RETURN_ERROR(writeMC(bw, val));
|
||||
buff.resize(bw.tellp());
|
||||
return buff;
|
||||
}
|
||||
|
||||
Error writeMC(char *buff, size_t const buffLen, auto const &val, size_t *sizeOut = nullptr) noexcept {
|
||||
CharBuffWriter bw{{buff, buffLen}};
|
||||
OX_RETURN_ERROR(writeMC(bw, val));
|
||||
if (sizeOut) {
|
||||
*sizeOut = bw.tellp();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2015 - 2025 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 https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include <ox/model/modelhandleradaptor.hpp>
|
||||
#include <ox/std/buffer.hpp>
|
||||
#include <ox/std/reader.hpp>
|
||||
|
||||
#include <ox/mc/read.hpp>
|
||||
|
||||
namespace ox {
|
||||
|
||||
template class ModelHandlerInterface<MetalClawReaderTemplate<BufferReader>>;
|
||||
|
||||
}
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2015 - 2025 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 https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include <ox/std/assert.hpp>
|
||||
#include <ox/std/byteswap.hpp>
|
||||
#include <ox/std/memops.hpp>
|
||||
#include <ox/std/trace.hpp>
|
||||
|
||||
#include <ox/mc/write.hpp>
|
||||
|
||||
namespace ox {
|
||||
|
||||
template class ModelHandlerInterface<MetalClawWriter<BufferWriter>>;
|
||||
template class ModelHandlerInterface<MetalClawWriter<CharBuffWriter>>;
|
||||
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
add_executable(
|
||||
McTest
|
||||
tests.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
McTest
|
||||
OxMetalClaw
|
||||
)
|
||||
|
||||
add_test("[ox/mc] Writer" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/McTest MetalClawWriter)
|
||||
add_test("[ox/mc] Reader" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/McTest MetalClawReader)
|
||||
#add_test("[ox/mc] MetalClawDef" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/McTest MetalClawDef)
|
||||
add_test("[ox/mc] MetalClawModelValue" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/McTest MetalClawModelValue)
|
||||
add_test("[ox/mc] encodeInteger" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/McTest encodeInteger)
|
||||
add_test("[ox/mc] decodeInteger" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/McTest decodeInteger)
|
||||
Vendored
+476
@@ -0,0 +1,476 @@
|
||||
/*
|
||||
* Copyright 2015 - 2025 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 https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#undef NDEBUG
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <ox/mc/mc.hpp>
|
||||
#include <ox/model/model.hpp>
|
||||
#include <ox/std/std.hpp>
|
||||
|
||||
union TestUnion {
|
||||
static constexpr auto TypeName = "TestUnion";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
bool Bool;
|
||||
uint32_t Int;
|
||||
char *CString{};
|
||||
};
|
||||
|
||||
struct TestStructNest {
|
||||
static constexpr auto TypeName = "TestStructNest";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
bool Bool = false;
|
||||
uint32_t Int = 0;
|
||||
ox::IString<32> IString = "";
|
||||
};
|
||||
|
||||
struct TestStruct {
|
||||
static constexpr auto TypeName = "TestStruct";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
bool Bool = false;
|
||||
int32_t Int = 0;
|
||||
int32_t Int1 = 0;
|
||||
int32_t Int2 = 0;
|
||||
int32_t Int3 = 0;
|
||||
int32_t Int4 = 0;
|
||||
int32_t Int5 = 0;
|
||||
int32_t Int6 = 0;
|
||||
int32_t Int7 = 0;
|
||||
int32_t Int8 = 0;
|
||||
int unionIdx = 1;
|
||||
TestUnion Union;
|
||||
ox::String String;
|
||||
ox::IString<32> IString = "";
|
||||
uint32_t List[4] = {0, 0, 0, 0};
|
||||
ox::Vector<uint32_t> Vector = {1, 2, 3, 4, 5};
|
||||
ox::Vector<uint32_t> Vector2 = {1, 2, 3, 4, 5};
|
||||
ox::HashMap<ox::String, int> Map;
|
||||
TestStructNest EmptyStruct;
|
||||
TestStructNest Struct;
|
||||
constexpr ~TestStruct() noexcept {
|
||||
if (unionIdx == 2) {
|
||||
ox::safeDelete(Union.CString);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
constexpr ox::Error model(T *io, ox::CommonPtrWith<TestUnion> auto *obj) noexcept {
|
||||
OX_RETURN_ERROR(io->template setTypeInfo<TestUnion>());
|
||||
OX_RETURN_ERROR(io->field("Bool", &obj->Bool));
|
||||
OX_RETURN_ERROR(io->field("Int", &obj->Int));
|
||||
OX_RETURN_ERROR(io->fieldCString("CString", &obj->CString));
|
||||
return ox::Error(0);
|
||||
}
|
||||
|
||||
OX_MODEL_BEGIN(TestStructNest)
|
||||
OX_MODEL_FIELD(Bool)
|
||||
OX_MODEL_FIELD(Int)
|
||||
OX_MODEL_FIELD(IString)
|
||||
OX_MODEL_END()
|
||||
|
||||
template<typename T>
|
||||
constexpr ox::Error model(T *io, ox::CommonPtrWith<TestStruct> auto *obj) noexcept {
|
||||
OX_RETURN_ERROR(io->template setTypeInfo<TestStruct>());
|
||||
OX_RETURN_ERROR(io->field("Bool", &obj->Bool));
|
||||
OX_RETURN_ERROR(io->field("Int", &obj->Int));
|
||||
OX_RETURN_ERROR(io->field("Int1", &obj->Int1));
|
||||
OX_RETURN_ERROR(io->field("Int2", &obj->Int2));
|
||||
OX_RETURN_ERROR(io->field("Int3", &obj->Int3));
|
||||
OX_RETURN_ERROR(io->field("Int4", &obj->Int4));
|
||||
OX_RETURN_ERROR(io->field("Int5", &obj->Int5));
|
||||
OX_RETURN_ERROR(io->field("Int6", &obj->Int6));
|
||||
OX_RETURN_ERROR(io->field("Int7", &obj->Int7));
|
||||
OX_RETURN_ERROR(io->field("Int8", &obj->Int8));
|
||||
OX_RETURN_ERROR(io->field("unionIdx", &obj->unionIdx));
|
||||
if constexpr(T::opType() == ox::OpType::Reflect) {
|
||||
OX_RETURN_ERROR(io->field("Union", ox::UnionView{&obj->Union, 0}));
|
||||
} else {
|
||||
OX_RETURN_ERROR(io->field("Union", ox::UnionView{&obj->Union, obj->unionIdx}));
|
||||
}
|
||||
OX_RETURN_ERROR(io->field("String", &obj->String));
|
||||
OX_RETURN_ERROR(io->field("IString", &obj->IString));
|
||||
OX_RETURN_ERROR(io->field("List", obj->List, 4));
|
||||
OX_RETURN_ERROR(io->field("Vector", &obj->Vector));
|
||||
OX_RETURN_ERROR(io->field("Vector2", &obj->Vector2));
|
||||
OX_RETURN_ERROR(io->field("Map", &obj->Map));
|
||||
OX_RETURN_ERROR(io->field("Struct", &obj->Struct));
|
||||
OX_RETURN_ERROR(io->field("EmptyStruct", &obj->EmptyStruct));
|
||||
return ox::Error(0);
|
||||
}
|
||||
|
||||
std::map<ox::StringView, ox::Error(*)()> tests = {
|
||||
{
|
||||
{
|
||||
"MetalClawWriter",
|
||||
[] {
|
||||
// This test doesn't confirm much, but it does show that the writer
|
||||
// doesn't segfault
|
||||
ox::Array<char, 1024> buff;
|
||||
TestStruct ts;
|
||||
OX_RETURN_ERROR(ox::writeMC(buff.data(), buff.size(), ts));
|
||||
OX_RETURN_ERROR(ox::writeMC(ts));
|
||||
return ox::Error(0);
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"MetalClawReader",
|
||||
[] {
|
||||
// setup for tests
|
||||
TestStruct testIn, testOut;
|
||||
testIn.Bool = true;
|
||||
testIn.Int = 42;
|
||||
testIn.IString = "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.Struct.Bool = true;
|
||||
testIn.Struct.Int = 300;
|
||||
testIn.Struct.IString = "Test String 3";
|
||||
testIn.unionIdx = 1;
|
||||
testIn.Union.Int = 93;
|
||||
// run tests
|
||||
const auto [buff, err] = ox::writeMC(testIn);
|
||||
oxAssert(err, "writeMC failed");
|
||||
oxAssert(ox::readMC(buff, testOut), "readMC failed");
|
||||
//std::cout << testIn.Union.Int << "|" << testOut.Union.Int << "|\n";
|
||||
oxAssert(testIn.Bool == testOut.Bool, "Bool value mismatch");
|
||||
oxAssert(testIn.Int == testOut.Int, "Int value mismatch");
|
||||
oxAssert(testIn.Int1 == testOut.Int1, "Int1 value mismatch");
|
||||
oxAssert(testIn.Int2 == testOut.Int2, "Int2 value mismatch");
|
||||
oxAssert(testIn.Int3 == testOut.Int3, "Int3 value mismatch");
|
||||
oxAssert(testIn.Int4 == testOut.Int4, "Int4 value mismatch");
|
||||
oxAssert(testIn.Int5 == testOut.Int5, "Int5 value mismatch");
|
||||
oxAssert(testIn.Int6 == testOut.Int6, "Int6 value mismatch");
|
||||
oxAssert(testIn.Int7 == testOut.Int7, "Int7 value mismatch");
|
||||
oxAssert(testIn.Int8 == testOut.Int8, "Int8 value mismatch");
|
||||
oxAssert(testIn.Union.Int == testOut.Union.Int, "Union.Int value mismatch");
|
||||
oxAssert(testIn.String == testOut.String, "String value mismatch");
|
||||
ox::expect(testIn.IString, testOut.IString);
|
||||
oxAssert(testIn.List[0] == testOut.List[0], "List[0] value mismatch");
|
||||
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.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");
|
||||
oxAssert(testIn.EmptyStruct.Int == testOut.EmptyStruct.Int, "EmptyStruct.Int value mismatch");
|
||||
oxAssert(testIn.EmptyStruct.IString == testOut.EmptyStruct.IString, "EmptyStruct.IString value mismatch");
|
||||
oxAssert(testIn.Struct.Int == testOut.Struct.Int, "Struct.Int value mismatch");
|
||||
oxAssert(testIn.Struct.IString == testOut.Struct.IString, "Struct.IString value mismatch");
|
||||
oxAssert(testIn.Struct.Bool == testOut.Struct.Bool, "Struct.Bool value mismatch");
|
||||
return ox::Error(0);
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"encodeInteger",
|
||||
[] {
|
||||
using ox::MaxValue;
|
||||
using ox::mc::McInt;
|
||||
using ox::mc::encodeInteger;
|
||||
static constexpr auto check = [](McInt val, const ox::Vector<uint8_t, 9> &expected) {
|
||||
if (val.length != expected.size()) {
|
||||
std::cout << "val.length: " << val.length << ", expected: " << expected.size() << '\n';
|
||||
return ox::Error(1);
|
||||
}
|
||||
for (std::size_t i = 0; i < expected.size(); i++) {
|
||||
if (expected[i] != val.data[i]) {
|
||||
std::cout << "decoded: " << static_cast<uint32_t>(val.data[i]) << ", expected: " << static_cast<uint32_t>(expected[i]) << '\n';
|
||||
std::cout << "decoded: " << i << ": " << static_cast<uint32_t>(val.data[i]) << '\n';
|
||||
return ox::Error(1);
|
||||
}
|
||||
}
|
||||
return ox::Error(0);
|
||||
};
|
||||
constexpr auto check64 = [](McInt val, auto expected) {
|
||||
if (val.length != 9) {
|
||||
std::cout << "val.length: " << val.length << '\n';
|
||||
return ox::Error(1);
|
||||
}
|
||||
ox::LittleEndian<decltype(expected)> decoded = *reinterpret_cast<decltype(expected)*>(&val.data[1]);
|
||||
if (expected != decoded) {
|
||||
std::cout << "decoded: " << decoded << ", expected: " << expected << '\n';
|
||||
return ox::Error(1);
|
||||
}
|
||||
return ox::Error(0);
|
||||
};
|
||||
// signed positive
|
||||
oxAssert(check(encodeInteger(int64_t(1)), {0b000'0001'0}), "Encode 1 fail");
|
||||
oxAssert(check(encodeInteger(int64_t(2)), {0b000'0010'0}), "Encode 2 fail");
|
||||
oxAssert(check(encodeInteger(int64_t(3)), {0b000'0011'0}), "Encode 3 fail");
|
||||
oxAssert(check(encodeInteger(int64_t(4)), {0b000'0100'0}), "Encode 4 fail");
|
||||
oxAssert(check(encodeInteger(int64_t(64)), {0b00'0000'01, 0b1}), "Encode 64 fail");
|
||||
oxAssert(check(encodeInteger(int64_t(128)), {0b00'0000'01, 0b10}), "Encode 128 fail");
|
||||
oxAssert(check(encodeInteger(int64_t(129)), {0b00'0001'01, 0b10}), "Encode 129 fail");
|
||||
oxAssert(check(encodeInteger(int64_t(130)), {0b00'0010'01, 0b10}), "Encode 130 fail");
|
||||
oxAssert(check(encodeInteger(int64_t(131)), {0b00'0011'01, 0b10}), "Encode 131 fail");
|
||||
// signed negative
|
||||
oxAssert(check(encodeInteger( int64_t(-1)), {0b111'1111'0}), "Encode -1 fail");
|
||||
oxAssert(check(encodeInteger( int64_t(-2)), {0b111'1110'0}), "Encode -2 fail");
|
||||
oxAssert(check(encodeInteger( int64_t(-3)), {0b111'1101'0}), "Encode -3 fail");
|
||||
oxAssert(check(encodeInteger( int64_t(-4)), {0b111'1100'0}), "Encode -4 fail");
|
||||
oxAssert(check(encodeInteger( int64_t(-64)), {0b100'0000'0}), "Encode -64 fail");
|
||||
oxAssert(check(encodeInteger(int64_t(-128)), {0b00'0000'01, 0b11'1111'10}), "Encode -128 fail");
|
||||
oxAssert(check(encodeInteger(int64_t(-129)), {0b11'1111'01, 0b11'1111'01}), "Encode -129 fail");
|
||||
oxAssert(check(encodeInteger(int64_t(-130)), {0b11'1110'01, 0b11'1111'01}), "Encode -130 fail");
|
||||
oxAssert(check(encodeInteger(int64_t(-131)), {0b11'1101'01, 0b11'1111'01}), "Encode -131 fail");
|
||||
// unsigned
|
||||
oxAssert(check(encodeInteger(uint32_t(0xffffffff)), {0b11101111, 255, 255, 255, 0b00011111}), "Encode 0xffffffff fail");
|
||||
oxAssert(check(encodeInteger(uint64_t(1)), {0b0010}), "Encode 1 fail");
|
||||
oxAssert(check(encodeInteger(uint64_t(2)), {0b0100}), "Encode 2 fail");
|
||||
oxAssert(check(encodeInteger(uint64_t(3)), {0b0110}), "Encode 3 fail");
|
||||
oxAssert(check(encodeInteger(uint64_t(4)), {0b1000}), "Encode 4 fail");
|
||||
oxAssert(check(encodeInteger(uint64_t(64)), {0b1000'000'0}), "Encode 4 fail");
|
||||
oxAssert(check(encodeInteger(uint64_t(128)), {0b0001, 0b10}), "Encode 128 fail");
|
||||
oxAssert(check(encodeInteger(uint64_t(129)), {0b0101, 0b10}), "Encode 129 fail");
|
||||
oxAssert(check(encodeInteger(uint64_t(130)), {0b1001, 0b10}), "Encode 130 fail");
|
||||
oxAssert(check(encodeInteger(uint64_t(131)), {0b1101, 0b10}), "Encode 131 fail");
|
||||
// Signed check needs lambda templates to run correctly without
|
||||
// code deduplication
|
||||
oxAssert(check64(encodeInteger(MaxValue<int64_t>), MaxValue<int64_t>), "Encode MaxValue<int64_t> fail");
|
||||
oxAssert(check64(encodeInteger(MaxValue<uint64_t>), MaxValue<uint64_t>), "Encode MaxValue<uint64_t> fail");
|
||||
return ox::Error(0);
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"decodeInteger",
|
||||
[] {
|
||||
using ox::MaxValue;
|
||||
using ox::mc::McInt;
|
||||
using ox::mc::encodeInteger;
|
||||
using ox::mc::decodeInteger;
|
||||
static constexpr auto check = [](auto val) {
|
||||
auto result = decodeInteger<decltype(val)>(encodeInteger(val));
|
||||
OX_RETURN_ERROR(result.error);
|
||||
if (result.value != val) {
|
||||
std::cout << "Bad value: " << result.value << ", expected: " << val << '\n';
|
||||
return ox::Error(1);
|
||||
}
|
||||
return ox::Error(0);
|
||||
};
|
||||
oxAssert(check(uint32_t(14)), "Decode of 14 failed.");
|
||||
oxAssert(check(int8_t(-1)), "Decode of -1 failed.");
|
||||
oxAssert(check(int16_t(-1)), "Decode of -1 failed.");
|
||||
oxAssert(check(int32_t(-1)), "Decode of -1 failed.");
|
||||
oxAssert(check(int64_t(-1)), "Decode of -1 failed.");
|
||||
oxAssert(check(int64_t(-2)), "Decode of -2 failed.");
|
||||
oxAssert(check(int64_t(-127)), "Decode of -127 failed.");
|
||||
oxAssert(check(int64_t(-128)), "Decode of -128 failed.");
|
||||
oxAssert(check(int64_t(-129)), "Decode of -129 failed.");
|
||||
oxAssert(check(int64_t(-129000)), "Decode of -129000 failed.");
|
||||
oxAssert(check(int64_t(1)), "Decode of 1 failed.");
|
||||
oxAssert(check(int64_t(2)), "Decode of 2 failed.");
|
||||
oxAssert(check(int64_t(42)), "Decode of 42 failed.");
|
||||
oxAssert(check(int64_t(130)), "Decode of 130 failed.");
|
||||
oxAssert(check(int64_t(131)), "Decode of 131 failed.");
|
||||
oxAssert(check(int64_t(131000)), "Decode of 131000 failed.");
|
||||
oxAssert(check(uint64_t(1)), "Decode of 1 failed.");
|
||||
oxAssert(check(uint64_t(2)), "Decode of 2 failed.");
|
||||
oxAssert(check(uint64_t(42)), "Decode of 42 failed.");
|
||||
oxAssert(check(uint64_t(130)), "Decode of 130 failed.");
|
||||
oxAssert(check(uint64_t(131)), "Decode of 131 failed.");
|
||||
oxAssert(check(0xffffffff), "Decode of 0xffffffff failed.");
|
||||
oxAssert(check(0xffffffffffff), "Decode of 0xffffffffffff failed.");
|
||||
oxAssert(check(0xffffffffffffffff), "Decode of U64 max failed.");
|
||||
return ox::Error(0);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
"MetalClawModelValue",
|
||||
[] {
|
||||
static constexpr size_t dataBuffLen = ox::units::MB;
|
||||
ox::Buffer dataBuff(dataBuffLen);
|
||||
TestStruct testIn;
|
||||
testIn.Bool = true;
|
||||
testIn.Int = 42;
|
||||
testIn.IString = "Test String 1";
|
||||
testIn.List[0] = 1;
|
||||
testIn.List[1] = 2;
|
||||
testIn.List[2] = 3;
|
||||
testIn.List[3] = 4;
|
||||
testIn.Struct.Bool = true;
|
||||
testIn.Struct.Int = 300;
|
||||
testIn.Struct.IString = "Test String 2";
|
||||
testIn.unionIdx = 1;
|
||||
testIn.Union.Int = 93;
|
||||
oxAssert(ox::writeMC(dataBuff.data(), dataBuff.size(), testIn), "Data generation failed");
|
||||
ox::TypeStore typeStore;
|
||||
const auto [type, typeErr] = ox::buildTypeDef(typeStore, testIn);
|
||||
oxAssert(typeErr, "Descriptor write failed");
|
||||
ox::ModelObject testOut;
|
||||
OX_RETURN_ERROR(testOut.setType(type));
|
||||
oxAssert(ox::readMC(dataBuff, testOut), "Data read failed");
|
||||
oxAssert(testOut.at("Int").unwrap()->get<int>() == testIn.Int, "testOut.Int failed");
|
||||
oxAssert(testOut.at("Bool").unwrap()->get<bool>() == testIn.Bool, "testOut.Bool failed");
|
||||
oxAssert(testOut.at("IString").unwrap()->get<ox::String>() == testIn.IString.c_str(), "testOut.String failed");
|
||||
oxAssert(testOut.at("String").unwrap()->get<ox::String>() == testIn.String, "testOut.String failed");
|
||||
auto &testOutStruct = testOut.at("Struct").unwrap()->get<ox::ModelObject>();
|
||||
auto &testOutUnion = testOut.at("Union").unwrap()->get<ox::ModelUnion>();
|
||||
auto &testOutList = testOut.at("List").unwrap()->get<ox::ModelValueVector>();
|
||||
auto testOutStructCopy = testOut.at("Struct").unwrap()->get<ox::ModelObject>();
|
||||
auto testOutUnionCopy = testOut.at("Union").unwrap()->get<ox::ModelUnion>();
|
||||
auto testOutListCopy = testOut.at("List").unwrap()->get<ox::ModelValueVector>();
|
||||
oxAssert(testOutStruct.typeName() == TestStructNest::TypeName, "ModelObject TypeName failed");
|
||||
oxAssert(testOutStruct.typeVersion() == TestStructNest::TypeVersion, "ModelObject TypeVersion failed");
|
||||
oxAssert(testOutStruct.at("Bool").unwrap()->get<bool>() == testIn.Struct.Bool, "testOut.Struct.Bool failed");
|
||||
oxAssert(testOutStruct.at("IString").unwrap()->get<ox::String>() == testIn.Struct.IString.c_str(), "testOut.Struct.IString failed");
|
||||
oxAssert(testOut.at("unionIdx").unwrap()->get<int>() == testIn.unionIdx, "testOut.unionIdx failed");
|
||||
oxAssert(testOutUnion.unionIdx() == testIn.unionIdx, "testOut.Union idx wrong");
|
||||
oxAssert(testOutUnion.at("Int").unwrap()->get<uint32_t>() == testIn.Union.Int, "testOut.Union.Int failed");
|
||||
oxAssert(testOutList[0].get<uint32_t>() == testIn.List[0], "testOut.List[0] failed");
|
||||
oxAssert(testOutList[1].get<uint32_t>() == testIn.List[1], "testOut.Struct.List[1] failed");
|
||||
oxAssert(testOutStructCopy.at("Bool").unwrap()->get<bool>() == testIn.Struct.Bool, "testOut.Struct.Bool (copy) failed");
|
||||
oxAssert(testOutStructCopy.at("IString").unwrap()->get<ox::String>() == testIn.Struct.IString.c_str(), "testOut.Struct.IString (copy) failed");
|
||||
oxAssert(testOutListCopy[0].get<uint32_t>() == testIn.List[0], "testOut.Struct.List[0] (copy) failed");
|
||||
oxAssert(testOutListCopy[1].get<uint32_t>() == testIn.List[1], "testOut.Struct.List[1] (copy) failed");
|
||||
return ox::Error(0);
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"MetalClawDef",
|
||||
[] {
|
||||
//constexpr size_t descBuffLen = 1024;
|
||||
//uint8_t descBuff[descBuffLen];
|
||||
static constexpr size_t dataBuffLen = ox::units::MB;
|
||||
char dataBuff[dataBuffLen];
|
||||
TestStruct testIn, testOut;
|
||||
testIn.Bool = true;
|
||||
testIn.Int = 42;
|
||||
testIn.IString = "Test String 1";
|
||||
testIn.List[0] = 1;
|
||||
testIn.List[1] = 2;
|
||||
testIn.List[2] = 3;
|
||||
testIn.List[3] = 4;
|
||||
testIn.Struct.Bool = false;
|
||||
testIn.Struct.Int = 300;
|
||||
testIn.Struct.IString = "Test String 2";
|
||||
oxAssert(ox::writeMC(dataBuff, dataBuffLen, testIn), "Data generation failed");
|
||||
ox::TypeStore typeStore;
|
||||
const auto [type, typeErr] = ox::buildTypeDef(typeStore, testIn);
|
||||
oxAssert(typeErr, "Descriptor write failed");
|
||||
ox::BufferReader br({dataBuff, dataBuffLen});
|
||||
OX_RETURN_ERROR(ox::walkModel<ox::MetalClawReader>(type, br,
|
||||
[](const ox::Vector<ox::FieldName>&, const ox::Vector<ox::String>&, const ox::DescriptorField &f, ox::MetalClawReader *rdr) -> ox::Error {
|
||||
//std::cout << f.fieldName.c_str() << '\n';
|
||||
auto fieldName = f.fieldName.c_str();
|
||||
switch (f.type->primitiveType) {
|
||||
case ox::PrimitiveType::UnsignedInteger:
|
||||
std::cout << fieldName << ":\tuint" << f.type->length * 8 << "_t:\t";
|
||||
switch (f.type->length) {
|
||||
case 1: {
|
||||
uint8_t i = {};
|
||||
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
|
||||
std::cout << i;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
uint16_t i = {};
|
||||
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
|
||||
std::cout << i;
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
uint32_t i = {};
|
||||
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
|
||||
std::cout << i;
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
uint64_t i = {};
|
||||
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
|
||||
std::cout << i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::cout << '\n';
|
||||
break;
|
||||
case ox::PrimitiveType::SignedInteger:
|
||||
std::cout << fieldName << ":\tint" << f.type->length * 8 << "_t:\t";
|
||||
switch (f.type->length) {
|
||||
case 1: {
|
||||
int8_t i = {};
|
||||
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
|
||||
std::cout << i;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
int16_t i = {};
|
||||
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
|
||||
std::cout << i;
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
int32_t i = {};
|
||||
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
|
||||
std::cout << i;
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
int64_t i = {};
|
||||
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
|
||||
std::cout << i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::cout << '\n';
|
||||
break;
|
||||
case ox::PrimitiveType::Bool: {
|
||||
bool i = {};
|
||||
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
|
||||
std::cout << fieldName << ":\t" << "bool:\t\t" << (i ? "true" : "false") << '\n';
|
||||
break;
|
||||
}
|
||||
case ox::PrimitiveType::String: {
|
||||
ox::String s;
|
||||
//std::cout << rdr->stringLength() << '\n';
|
||||
oxAssert(rdr->field(fieldName, &s), "Walking model failed.");
|
||||
oxOutf("{}:\tstring:\t\t{}\n", fieldName, s);
|
||||
break;
|
||||
}
|
||||
case ox::PrimitiveType::Struct:
|
||||
break;
|
||||
case ox::PrimitiveType::Union:
|
||||
break;
|
||||
}
|
||||
return ox::Error(0);
|
||||
}
|
||||
));
|
||||
return ox::Error(0);
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, const char **args) {
|
||||
if (argc < 2) {
|
||||
oxError("Must specify test to run");
|
||||
}
|
||||
auto const testName = args[1];
|
||||
auto const func = tests.find(testName);
|
||||
if (func != tests.end()) {
|
||||
oxAssert(func->second(), "Test returned Error");
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
Reference in New Issue
Block a user