290 lines
7.6 KiB
C++
290 lines
7.6 KiB
C++
/*
|
|
* Copyright 2015 - 2021 gary@drinkingtea.net
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <ox/model/fieldcounter.hpp>
|
|
#include <ox/model/optype.hpp>
|
|
#include <ox/model/types.hpp>
|
|
#include <ox/std/byteswap.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 {
|
|
|
|
class MetalClawReader {
|
|
|
|
private:
|
|
FieldBitmapReader<const uint8_t*> m_fieldPresence;
|
|
int m_fields = 0;
|
|
int m_field = 0;
|
|
int m_unionIdx = -1;
|
|
std::size_t m_buffIt = 0;
|
|
std::size_t m_buffLen = 0;
|
|
const uint8_t *m_buff = nullptr;
|
|
MetalClawReader *m_parent = nullptr;
|
|
|
|
public:
|
|
MetalClawReader(const uint8_t *buff, std::size_t buffLen, int unionIdx = -1, MetalClawReader *parent = nullptr) noexcept;
|
|
|
|
~MetalClawReader() noexcept;
|
|
|
|
Error field(const char*, int8_t *val);
|
|
Error field(const char*, int16_t *val);
|
|
Error field(const char*, int32_t *val);
|
|
Error field(const char*, int64_t *val);
|
|
|
|
Error field(const char*, uint8_t *val);
|
|
Error field(const char*, uint16_t *val);
|
|
Error field(const char*, uint32_t *val);
|
|
Error field(const char*, uint64_t *val);
|
|
|
|
Error field(const char*, bool *val);
|
|
|
|
// array handler
|
|
template<typename T>
|
|
Error field(const char*, T *val, std::size_t len);
|
|
|
|
// map handler
|
|
template<typename T>
|
|
Error field(const char*, HashMap<String, T> *val);
|
|
|
|
// array handler, with callback to allow handling individual elements
|
|
template<typename T, typename Handler>
|
|
Error field(const char*, Handler handler);
|
|
|
|
template<typename T>
|
|
Error field(const char*, T *val);
|
|
|
|
template<typename U>
|
|
Error field(const char*, UnionView<U> val);
|
|
|
|
template<std::size_t L>
|
|
Error field(const char*, BString<L> *val);
|
|
|
|
Error field(const char*, SerStr val);
|
|
|
|
/**
|
|
* Reads an array length from the current location in the buffer.
|
|
* @param pass indicates that the parsing should iterate past the array length
|
|
*/
|
|
Result<ArrayLength> arrayLength(const char *name, bool pass = true);
|
|
|
|
/**
|
|
* Reads an string length from the current location in the buffer.
|
|
*/
|
|
[[nodiscard]]
|
|
StringLength stringLength(const char *name);
|
|
|
|
template<typename T = std::nullptr_t>
|
|
void setTypeInfo(const char *name = T::TypeName, int fields = countFields<T>());
|
|
|
|
/**
|
|
* Returns a MetalClawReader to parse a child object.
|
|
*/
|
|
[[nodiscard]]
|
|
MetalClawReader child(const char *name, int unionIdx = -1);
|
|
|
|
/**
|
|
* Indicates whether or not the next field to be read is present.
|
|
*/
|
|
bool fieldPresent(const char *name) const;
|
|
|
|
/**
|
|
* Indicates whether or not the given field is present.
|
|
*/
|
|
bool fieldPresent(int fieldNo) const;
|
|
|
|
void nextField() noexcept;
|
|
|
|
static constexpr auto opType() {
|
|
return OpType::Read;
|
|
}
|
|
|
|
private:
|
|
template<typename I>
|
|
Error readInteger(I *val);
|
|
|
|
};
|
|
|
|
template<typename T>
|
|
Error MetalClawReader::field(const char *name, T *val) {
|
|
if constexpr(isVector_v<T>) {
|
|
if (m_unionIdx == -1 || m_unionIdx == m_field) {
|
|
// set size of val if the field is present, don't worry about it if not
|
|
if (m_fieldPresence.get(m_field)) {
|
|
oxRequire(len, arrayLength(name, false));
|
|
val->resize(len);
|
|
}
|
|
return field(name, val->data(), val->size());
|
|
}
|
|
++m_field;
|
|
return OxError(0);
|
|
} else {
|
|
if ((m_unionIdx == -1 || m_unionIdx == m_field) && val && m_fieldPresence.get(m_field)) {
|
|
auto reader = child("");
|
|
oxReturnError(model(&reader, val));
|
|
}
|
|
++m_field;
|
|
return OxError(0);
|
|
}
|
|
}
|
|
|
|
template<typename U>
|
|
Error MetalClawReader::field(const char*, UnionView<U> val) {
|
|
if ((m_unionIdx == -1 || m_unionIdx == m_field) && val.get() && m_fieldPresence.get(m_field)) {
|
|
auto reader = child("", val.idx());
|
|
oxReturnError(model(&reader, val.get()));
|
|
}
|
|
++m_field;
|
|
return OxError(0);
|
|
}
|
|
|
|
template<std::size_t L>
|
|
Error MetalClawReader::field(const char *name, BString<L> *val) {
|
|
return field(name, SerStr(val->data(), val->cap()));
|
|
}
|
|
|
|
template<typename I>
|
|
Error MetalClawReader::readInteger(I *val) {
|
|
if (m_unionIdx == -1 || m_unionIdx == m_field) {
|
|
if (m_fieldPresence.get(m_field)) {
|
|
std::size_t bytesRead = 0;
|
|
if (m_buffIt >= m_buffLen) {
|
|
oxTrace("ox::MetalClaw::readInteger") << "Buffer ended";
|
|
return OxError(MC_BUFFENDED);
|
|
}
|
|
auto valErr = mc::decodeInteger<I>(&m_buff[m_buffIt], m_buffLen - m_buffIt, &bytesRead);
|
|
m_buffIt += bytesRead;
|
|
oxReturnError(valErr.error);
|
|
*val = valErr.value;
|
|
} else {
|
|
*val = 0;
|
|
}
|
|
}
|
|
++m_field;
|
|
return OxError(0);
|
|
}
|
|
|
|
// array handler
|
|
template<typename T>
|
|
Error MetalClawReader::field(const char *name, T *val, std::size_t valLen) {
|
|
if (m_unionIdx == -1 || m_unionIdx == m_field) {
|
|
if (m_fieldPresence.get(m_field)) {
|
|
// read the length
|
|
if (m_buffIt >= m_buffLen) {
|
|
return OxError(MC_BUFFENDED);
|
|
}
|
|
std::size_t bytesRead = 0;
|
|
oxRequire(len, mc::decodeInteger<ArrayLength>(&m_buff[m_buffIt], m_buffLen - m_buffIt, &bytesRead));
|
|
m_buffIt += bytesRead;
|
|
|
|
// read the list
|
|
if (valLen >= len) {
|
|
auto reader = child("");
|
|
reader.setTypeInfo("List", len);
|
|
for (std::size_t i = 0; i < len; i++) {
|
|
oxReturnError(reader.field("", &val[i]));
|
|
}
|
|
} else {
|
|
oxTrace("ox::mc::read::field(T)") << name << ", size:" << valLen;
|
|
return OxError(MC_OUTBUFFENDED);
|
|
}
|
|
}
|
|
}
|
|
++m_field;
|
|
return OxError(0);
|
|
}
|
|
|
|
template<typename T>
|
|
Error MetalClawReader::field(const char*, HashMap<String, T> *val) {
|
|
if (m_unionIdx == -1 || m_unionIdx == m_field) {
|
|
if (m_fieldPresence.get(m_field)) {
|
|
// read the length
|
|
if (m_buffIt >= m_buffLen) {
|
|
return OxError(MC_BUFFENDED);
|
|
}
|
|
std::size_t bytesRead = 0;
|
|
oxRequire(len, mc::decodeInteger<ArrayLength>(&m_buff[m_buffIt], m_buffLen - m_buffIt, &bytesRead));
|
|
m_buffIt += bytesRead;
|
|
|
|
// read the list
|
|
auto reader = child("");
|
|
reader.setTypeInfo("List", len);
|
|
for (std::size_t i = 0; i < len; i++) {
|
|
const auto keyLen = reader.stringLength(nullptr);
|
|
auto wkey = ox_malloca(keyLen + 1, char, 0);
|
|
oxReturnError(reader.field("", SerStr(wkey.get(), keyLen)));
|
|
oxReturnError(reader.field("", &val->operator[](wkey.get())));
|
|
}
|
|
}
|
|
}
|
|
++m_field;
|
|
return OxError(0);
|
|
}
|
|
|
|
template<typename T, typename Handler>
|
|
Error MetalClawReader::field(const char*, Handler handler) {
|
|
if (m_unionIdx == -1 || m_unionIdx == m_field) {
|
|
if (m_fieldPresence.get(m_field)) {
|
|
// read the length
|
|
if (m_buffIt >= m_buffLen) {
|
|
return OxError(MC_BUFFENDED);
|
|
}
|
|
std::size_t bytesRead = 0;
|
|
oxRequire(len, mc::decodeInteger<ArrayLength>(&m_buff[m_buffIt], m_buffLen - m_buffIt, &bytesRead));
|
|
m_buffIt += bytesRead;
|
|
|
|
// read the list
|
|
auto reader = child("");
|
|
reader.setTypeInfo("List", len);
|
|
for (std::size_t i = 0; i < len; i++) {
|
|
T val;
|
|
oxReturnError(reader.field("", &val));
|
|
oxReturnError(handler(i, &val));
|
|
}
|
|
}
|
|
}
|
|
++m_field;
|
|
return OxError(0);
|
|
}
|
|
|
|
template<typename T>
|
|
void MetalClawReader::setTypeInfo(const char*, int fields) {
|
|
m_fields = fields;
|
|
m_buffIt = (fields / 8 + 1) - (fields % 8 == 0);
|
|
m_fieldPresence.setFields(fields);
|
|
m_fieldPresence.setMaxLen(m_buffIt);
|
|
}
|
|
|
|
template<typename T>
|
|
Error readMC(const char *buff, std::size_t buffLen, T *val) {
|
|
MetalClawReader reader(bit_cast<uint8_t*>(buff), buffLen);
|
|
return model(&reader, val);
|
|
}
|
|
|
|
template<typename T>
|
|
Result<T> readMC(const char *buff, std::size_t buffLen) {
|
|
T val;
|
|
oxReturnError(readMC(buff, buffLen, &val));
|
|
return move(val);
|
|
}
|
|
|
|
template<typename T>
|
|
Result<T> readMC(const Vector<char> &buff) {
|
|
return readMC<T>(buff.data(), buff.size());
|
|
}
|
|
|
|
}
|