Some checks failed
Build / build (push) Has been cancelled
Remove UTF-8 parsing. It is a rare enough need that it should have a specialized call when needed. Better to have a more optimal length fetch for typical case.
556 lines
17 KiB
C++
556 lines
17 KiB
C++
/*
|
|
* Copyright 2015 - 2024 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;
|
|
std::size_t m_fields = 0;
|
|
std::size_t m_field = 0;
|
|
ox::Optional<int> m_unionIdx;
|
|
Reader &m_reader;
|
|
|
|
public:
|
|
explicit constexpr MetalClawReaderTemplate(
|
|
Reader &reader,
|
|
ox::Optional<int> const&unionIdx = {}) noexcept;
|
|
|
|
constexpr ~MetalClawReaderTemplate() noexcept;
|
|
|
|
constexpr Error field(const char*, int8_t *val) noexcept;
|
|
constexpr Error field(const char*, int16_t *val) noexcept;
|
|
constexpr Error field(const char*, int32_t *val) noexcept;
|
|
constexpr Error field(const char*, int64_t *val) noexcept;
|
|
|
|
constexpr Error field(const char*, uint8_t *val) noexcept;
|
|
constexpr Error field(const char*, uint16_t *val) noexcept;
|
|
constexpr Error field(const char*, uint32_t *val) noexcept;
|
|
constexpr Error field(const char*, uint64_t *val) noexcept;
|
|
|
|
constexpr Error field(const char*, bool *val) noexcept;
|
|
|
|
// array handler
|
|
constexpr Error field(const char*, auto *val, std::size_t len) noexcept;
|
|
|
|
// map handler
|
|
template<typename T>
|
|
constexpr Error field(const char*, HashMap<String, T> *val) noexcept;
|
|
|
|
// array handler, with callback to allow handling individual elements
|
|
template<typename T, typename CB>
|
|
constexpr Error field(const char*, CB cb) noexcept;
|
|
|
|
template<typename T>
|
|
constexpr Error field(const char*, T *val) noexcept;
|
|
|
|
template<typename U, bool force>
|
|
constexpr Error field(const char*, UnionView<U, force> val) noexcept;
|
|
|
|
template<std::size_t SmallStringSize>
|
|
constexpr Error field(const char*, BasicString<SmallStringSize> *val) noexcept;
|
|
|
|
template<std::size_t L>
|
|
constexpr Error field(const char*, IString<L> *val) noexcept;
|
|
|
|
constexpr Error fieldCString(const char*, char *val, std::size_t buffLen) noexcept;
|
|
|
|
constexpr Error fieldCString(const char*, char **val) noexcept;
|
|
|
|
constexpr Error fieldCString(const char*, char **val, std::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>& = {},
|
|
std::size_t fields = ModelFieldCount_v<T>) noexcept;
|
|
|
|
/**
|
|
* Returns a MetalClawReader to parse a child object.
|
|
*/
|
|
[[nodiscard]]
|
|
constexpr MetalClawReaderTemplate<Reader> child(const char *name, ox::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, const ModelUnion&) 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,
|
|
ox::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(const char*, int8_t *val) noexcept {
|
|
return readInteger(val);
|
|
}
|
|
|
|
template<Reader_c Reader>
|
|
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, int16_t *val) noexcept {
|
|
return readInteger(val);
|
|
}
|
|
|
|
template<Reader_c Reader>
|
|
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, int32_t *val) noexcept {
|
|
return readInteger(val);
|
|
}
|
|
|
|
template<Reader_c Reader>
|
|
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, int64_t *val) noexcept {
|
|
return readInteger(val);
|
|
}
|
|
|
|
|
|
template<Reader_c Reader>
|
|
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, uint8_t *val) noexcept {
|
|
return readInteger(val);
|
|
}
|
|
|
|
template<Reader_c Reader>
|
|
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, uint16_t *val) noexcept {
|
|
return readInteger(val);
|
|
}
|
|
|
|
template<Reader_c Reader>
|
|
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, uint32_t *val) noexcept {
|
|
return readInteger(val);
|
|
}
|
|
|
|
template<Reader_c Reader>
|
|
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, uint64_t *val) noexcept {
|
|
return readInteger(val);
|
|
}
|
|
|
|
template<Reader_c Reader>
|
|
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, bool *val) noexcept {
|
|
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
|
|
auto const result = m_fieldPresence.get(static_cast<std::size_t>(m_field));
|
|
*val = result.value;
|
|
oxReturnError(result);
|
|
}
|
|
++m_field;
|
|
return OxError(0);
|
|
}
|
|
|
|
// array handler
|
|
template<Reader_c Reader>
|
|
constexpr Error MetalClawReaderTemplate<Reader>::field(const char *name, auto *val, std::size_t valLen) noexcept {
|
|
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
|
|
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
|
|
// read the length
|
|
std::size_t bytesRead = 0;
|
|
oxRequire(len, mc::decodeInteger<ArrayLength>(m_reader, &bytesRead));
|
|
// read the list
|
|
if (valLen >= len) {
|
|
auto reader = child({});
|
|
auto &handler = *reader.interface();
|
|
oxReturnError(handler.setTypeInfo("List", 0, {}, static_cast<std::size_t>(len)));
|
|
for (std::size_t i = 0; i < len; ++i) {
|
|
oxReturnError(handler.field({}, &val[i]));
|
|
}
|
|
} else {
|
|
oxTracef("ox.mc.read.field(T)", "{}, length: {}", name, valLen);
|
|
return OxError(McOutputBuffEnded);
|
|
}
|
|
}
|
|
}
|
|
++m_field;
|
|
return {};
|
|
}
|
|
|
|
template<Reader_c Reader>
|
|
template<typename T>
|
|
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, HashMap<String, T> *val) noexcept {
|
|
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
|
|
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
|
|
// read the length
|
|
oxRequire(g, m_reader.tellg());
|
|
std::size_t bytesRead = 0;
|
|
oxRequire(len, mc::decodeInteger<ArrayLength>(m_reader, &bytesRead));
|
|
oxReturnError(m_reader.seekg(g));
|
|
// read the list
|
|
auto reader = child("");
|
|
auto &handler = *reader.interface();
|
|
oxReturnError(handler.setTypeInfo("List", 0, {}, static_cast<std::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) {
|
|
oxRequire(keyLen, handler.stringLength(nullptr));
|
|
auto wkey = ox_malloca(keyLen + 1, char, 0);
|
|
auto wkeyPtr = wkey.get();
|
|
oxReturnError(handler.fieldCString("", &wkeyPtr, keyLen + 1));
|
|
return handler.field("", &val[wkeyPtr]);
|
|
};
|
|
for (std::size_t i = 0; i < len; ++i) {
|
|
oxReturnError(loopBody(handler, *val));
|
|
}
|
|
}
|
|
}
|
|
++m_field;
|
|
return OxError(0);
|
|
}
|
|
|
|
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<std::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<std::size_t>(m_field))) {
|
|
oxRequire(len, arrayLength(name, false));
|
|
oxReturnError(ox::resizeVector(*val, len));
|
|
return field(name, val->data(), val->size());
|
|
}
|
|
oxReturnError(ox::resizeVector(*val, 0));
|
|
}
|
|
++m_field;
|
|
return {};
|
|
} else if constexpr(isArray_v<T>) {
|
|
if (!m_unionIdx.has_value() || static_cast<std::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<std::size_t>(m_field))) {
|
|
oxRequire(len, arrayLength(name, false));
|
|
if (len > val->size()) {
|
|
return OxError(1, "Input array is too long");
|
|
}
|
|
}
|
|
return field(name, val->data(), val->size());
|
|
}
|
|
++m_field;
|
|
return {};
|
|
} else {
|
|
if ((!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) && val) {
|
|
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
|
|
auto reader = child("");
|
|
oxReturnError(model(reader.interface(), val));
|
|
}
|
|
}
|
|
++m_field;
|
|
return {};
|
|
}
|
|
}
|
|
|
|
template<Reader_c Reader>
|
|
template<typename U, bool force>
|
|
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, UnionView<U, force> val) noexcept {
|
|
if ((!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) && val.get()) {
|
|
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
|
|
auto reader = child("", ox::Optional<int>(ox::in_place, val.idx()));
|
|
oxReturnError(model(reader.interface(), val.get()));
|
|
}
|
|
}
|
|
++m_field;
|
|
return {};
|
|
}
|
|
|
|
template<Reader_c Reader>
|
|
template<std::size_t SmallStringSize>
|
|
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, BasicString<SmallStringSize> *val) noexcept {
|
|
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
|
|
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
|
|
// read the length
|
|
std::size_t bytesRead = 0;
|
|
oxRequire(size, mc::decodeInteger<StringLength>(m_reader, &bytesRead));
|
|
const auto cap = size;
|
|
*val = BasicString<SmallStringSize>(cap);
|
|
auto data = val->data();
|
|
// read the string
|
|
oxReturnError(m_reader.read(data, size));
|
|
} else {
|
|
*val = "";
|
|
}
|
|
}
|
|
++m_field;
|
|
return OxError(0);
|
|
}
|
|
|
|
template<Reader_c Reader>
|
|
template<std::size_t L>
|
|
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, IString<L> *val) noexcept {
|
|
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
|
|
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
|
|
// read the length
|
|
std::size_t bytesRead = 0;
|
|
oxRequire(size, mc::decodeInteger<StringLength>(m_reader, &bytesRead));
|
|
*val = IString<L>();
|
|
oxReturnError(val->resize(size));
|
|
auto const data = val->data();
|
|
// read the string
|
|
oxReturnError(m_reader.read(data, size));
|
|
} else {
|
|
*val = "";
|
|
}
|
|
}
|
|
++m_field;
|
|
return {};
|
|
}
|
|
|
|
template<Reader_c Reader>
|
|
constexpr Error MetalClawReaderTemplate<Reader>::fieldCString(const char*, char *val, std::size_t buffLen) noexcept {
|
|
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
|
|
// read the length
|
|
std::size_t bytesRead = 0;
|
|
oxRequire(size, mc::decodeInteger<StringLength>(m_reader, &bytesRead));
|
|
if (size > buffLen) {
|
|
return OxError(McOutputBuffEnded);
|
|
}
|
|
// re-allocate in case too small
|
|
auto data = val;
|
|
// read the string
|
|
oxReturnError(m_reader.read(data, size));
|
|
data[size] = 0;
|
|
}
|
|
++m_field;
|
|
return OxError(0);
|
|
}
|
|
|
|
template<Reader_c Reader>
|
|
constexpr Error MetalClawReaderTemplate<Reader>::fieldCString(const char*, char **val) noexcept {
|
|
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
|
|
// read the length
|
|
std::size_t bytesRead = 0;
|
|
oxRequire(size, mc::decodeInteger<StringLength>(m_reader, &bytesRead));
|
|
// re-allocate in case too small
|
|
safeDelete(*val);
|
|
*val = new char[size + 1];
|
|
auto data = *val;
|
|
// read the string
|
|
oxReturnError(m_reader.read(data, size));
|
|
data[size] = 0;
|
|
}
|
|
++m_field;
|
|
return OxError(0);
|
|
}
|
|
|
|
template<Reader_c Reader>
|
|
constexpr Error MetalClawReaderTemplate<Reader>::fieldCString(const char*, char **val, std::size_t buffLen) noexcept {
|
|
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
|
|
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
|
|
// read the length
|
|
std::size_t bytesRead = 0;
|
|
oxRequire(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 = *val;
|
|
// read the string
|
|
oxReturnError(m_reader.read(data, size));
|
|
data[size] = 0;
|
|
} else {
|
|
auto data = *val;
|
|
if (data) {
|
|
data[0] = 0;
|
|
}
|
|
}
|
|
}
|
|
++m_field;
|
|
return OxError(0);
|
|
}
|
|
|
|
template<Reader_c Reader>
|
|
constexpr Result<ArrayLength> MetalClawReaderTemplate<Reader>::arrayLength(const char*, bool pass) noexcept {
|
|
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
|
|
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
|
|
// read the length
|
|
std::size_t bytesRead = 0;
|
|
oxRequire(g, m_reader.tellg());
|
|
oxRequire(out, mc::decodeInteger<ArrayLength>(m_reader, &bytesRead));
|
|
if (!pass) {
|
|
oxReturnError(m_reader.seekg(g));
|
|
}
|
|
return out;
|
|
}
|
|
}
|
|
return OxError(1);
|
|
}
|
|
|
|
template<Reader_c Reader>
|
|
constexpr Result<StringLength> MetalClawReaderTemplate<Reader>::stringLength(const char*) noexcept {
|
|
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
|
|
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
|
|
// read the length
|
|
std::size_t bytesRead = 0;
|
|
auto len = mc::decodeInteger<StringLength>(m_reader, &bytesRead);
|
|
oxReturnError(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<std::size_t>(*m_unionIdx) == m_field) {
|
|
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
|
|
std::size_t bytesRead = 0;
|
|
auto const result = mc::decodeInteger<I>(m_reader, &bytesRead);
|
|
oxReturnError(result);
|
|
*val = result.value;
|
|
} else {
|
|
*val = 0;
|
|
}
|
|
}
|
|
++m_field;
|
|
return OxError(0);
|
|
}
|
|
|
|
template<Reader_c Reader>
|
|
template<typename T, typename CB>
|
|
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, CB cb) noexcept {
|
|
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
|
|
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
|
|
// read the length
|
|
std::size_t bytesRead = 0;
|
|
oxRequire(len, mc::decodeInteger<ArrayLength>(m_reader, &bytesRead));
|
|
// read the list
|
|
auto reader = child("");
|
|
auto &handler = *reader.interface();
|
|
oxReturnError(handler.setTypeInfo("List", 0, {}, static_cast<std::size_t>(len)));
|
|
for (std::size_t i = 0; i < len; ++i) {
|
|
T val;
|
|
oxReturnError(handler.field("", &val));
|
|
oxReturnError(cb(i, &val));
|
|
}
|
|
}
|
|
}
|
|
++m_field;
|
|
return OxError(0);
|
|
}
|
|
|
|
template<Reader_c Reader>
|
|
template<typename T>
|
|
constexpr ox::Error MetalClawReaderTemplate<Reader>::setTypeInfo(
|
|
const char*, int, const Vector<String>&, std::size_t 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(
|
|
const char*,
|
|
ox::Optional<int> unionIdx) noexcept {
|
|
return MetalClawReaderTemplate<Reader>(m_reader, unionIdx);
|
|
}
|
|
|
|
template<Reader_c Reader>
|
|
constexpr bool MetalClawReaderTemplate<Reader>::fieldPresent(const char*) const noexcept {
|
|
return m_fieldPresence.get(static_cast<std::size_t>(m_field)).value;
|
|
}
|
|
|
|
template<Reader_c Reader>
|
|
constexpr bool MetalClawReaderTemplate<Reader>::fieldPresent(int fieldNo) const noexcept {
|
|
return m_fieldPresence.get(static_cast<std::size_t>(fieldNo)).value;
|
|
}
|
|
|
|
template<Reader_c Reader>
|
|
[[nodiscard]]
|
|
constexpr int MetalClawReaderTemplate<Reader>::whichFieldPresent(const char*, const ModelUnion &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 buff, T &val) noexcept {
|
|
BufferReader br(buff);
|
|
MetalClawReader reader(br);
|
|
ModelHandlerInterface<MetalClawReader, ox::OpType::Read> handler(&reader);
|
|
return model(&handler, &val);
|
|
}
|
|
|
|
template<typename T>
|
|
Result<T> readMC(ox::BufferView buff) noexcept {
|
|
Result<T> val;
|
|
oxReturnError(readMC(buff, val.value));
|
|
return val;
|
|
}
|
|
|
|
extern template class ModelHandlerInterface<MetalClawReaderTemplate<BufferReader>>;
|
|
|
|
}
|