162 lines
4.9 KiB
C++
162 lines
4.9 KiB
C++
/*
|
|
* 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/error.hpp>
|
|
|
|
#include "desctypes.hpp"
|
|
|
|
namespace ox {
|
|
|
|
template<typename Reader, typename T>
|
|
class DataWalker {
|
|
template<typename ReaderBase, typename FH>
|
|
friend constexpr Error parseField(const DescriptorField &field, ReaderBase *rdr, DataWalker<ReaderBase, FH> *walker) noexcept;
|
|
|
|
private:
|
|
Vector<const DescriptorType*> m_typeStack;
|
|
T m_fieldHandler;
|
|
Vector<FieldName> m_path;
|
|
Vector<String> m_typePath;
|
|
|
|
public:
|
|
constexpr DataWalker(DescriptorType *type, T fieldHandler) noexcept;
|
|
|
|
constexpr Result<const DescriptorType*> type() const noexcept;
|
|
|
|
constexpr Error read(const DescriptorField&, Reader *rdr) noexcept;
|
|
|
|
protected:
|
|
constexpr void pushNamePath(const FieldName &fn) noexcept;
|
|
|
|
constexpr void popNamePath() noexcept;
|
|
|
|
constexpr void pushType(const DescriptorType *type) noexcept;
|
|
|
|
constexpr void popType() noexcept;
|
|
|
|
};
|
|
|
|
template<typename Reader, typename T>
|
|
constexpr DataWalker<Reader, T>::DataWalker(DescriptorType *type, T fieldHandler) noexcept: m_fieldHandler(fieldHandler) {
|
|
m_typeStack.push_back(type);
|
|
}
|
|
|
|
template<typename Reader, typename T>
|
|
constexpr Result<const DescriptorType*> DataWalker<Reader, T>::type() const noexcept {
|
|
OX_REQUIRE(out, m_typeStack.back());
|
|
return *out;
|
|
}
|
|
|
|
template<typename Reader, typename T>
|
|
constexpr Error DataWalker<Reader, T>::read(const DescriptorField &f, Reader *rdr) noexcept {
|
|
// get const ref of paths
|
|
const auto &pathCr = m_path;
|
|
const auto &typePathCr = m_typePath;
|
|
return m_fieldHandler(pathCr, typePathCr, f, rdr);
|
|
}
|
|
|
|
template<typename Reader, typename T>
|
|
constexpr void DataWalker<Reader, T>::pushNamePath(const FieldName &fn) noexcept {
|
|
m_path.emplace_back(fn);
|
|
}
|
|
|
|
template<typename Reader, typename T>
|
|
constexpr void DataWalker<Reader, T>::popNamePath() noexcept {
|
|
m_path.pop_back();
|
|
}
|
|
|
|
template<typename Reader, typename T>
|
|
constexpr void DataWalker<Reader, T>::pushType(const DescriptorType *type) noexcept {
|
|
m_typeStack.push_back(type);
|
|
}
|
|
|
|
template<typename Reader, typename T>
|
|
constexpr void DataWalker<Reader, T>::popType() noexcept {
|
|
m_typeStack.pop_back();
|
|
}
|
|
|
|
template<typename Reader, typename FH>
|
|
static constexpr Error parseField(const DescriptorField &field, Reader *rdr, DataWalker<Reader, FH> *walker) noexcept {
|
|
walker->pushNamePath(field.fieldName);
|
|
if (field.subscriptLevels) {
|
|
// add array handling
|
|
OX_REQUIRE(arrayLen, rdr->arrayLength(field.fieldName.c_str(), true));
|
|
auto child = rdr->child(field.fieldName.c_str());
|
|
OX_RETURN_ERROR(child.setTypeInfo(field.type->typeName.c_str(), field.type->typeVersion, field.type->typeParams, arrayLen));
|
|
DescriptorField f(field); // create mutable copy
|
|
--f.subscriptLevels;
|
|
String subscript;
|
|
for (std::size_t i = 0; i < arrayLen; i++) {
|
|
subscript = "[";
|
|
subscript += static_cast<uint64_t>(i);
|
|
subscript += "]";
|
|
walker->pushNamePath(subscript);
|
|
OX_RETURN_ERROR(parseField(f, &child, walker));
|
|
walker->popNamePath();
|
|
}
|
|
rdr->nextField();
|
|
} else {
|
|
switch (field.type->primitiveType) {
|
|
case PrimitiveType::UnsignedInteger:
|
|
case PrimitiveType::SignedInteger:
|
|
case PrimitiveType::Bool:
|
|
case PrimitiveType::String:
|
|
OX_RETURN_ERROR(walker->read(field, rdr));
|
|
break;
|
|
case PrimitiveType::Struct:
|
|
case PrimitiveType::Union:
|
|
if (rdr->fieldPresent(field.fieldName.c_str())) {
|
|
auto child = rdr->child(field.fieldName.c_str());
|
|
walker->pushType(field.type);
|
|
OX_RETURN_ERROR(model(&child, walker));
|
|
walker->popType();
|
|
rdr->nextField();
|
|
} else {
|
|
// skip and discard absent field
|
|
int discard;
|
|
OX_RETURN_ERROR(rdr->field(field.fieldName.c_str(), &discard));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
walker->popNamePath();
|
|
return ox::Error(0);
|
|
}
|
|
|
|
template<typename Reader, typename FH>
|
|
constexpr Error model(Reader *rdr, DataWalker<Reader, FH> *walker) noexcept {
|
|
OX_REQUIRE(type, walker->type());
|
|
auto typeName = type->typeName.c_str();
|
|
auto typeVersion = type->typeVersion;
|
|
auto typeParams = type->typeParams;
|
|
auto &fields = type->fieldList;
|
|
OX_RETURN_ERROR(rdr->setTypeInfo(typeName, typeVersion, typeParams, fields.size()));
|
|
for (const auto &field : fields) {
|
|
OX_RETURN_ERROR(parseField(field, rdr, walker));
|
|
}
|
|
return ox::Error(0);
|
|
}
|
|
|
|
template<typename Reader, typename Handler>
|
|
constexpr Error walkModel(DescriptorType *type, Reader_c auto &reader, Handler handler) noexcept {
|
|
DataWalker<Reader, Handler> walker(type, handler);
|
|
Reader rdr(reader);
|
|
return model(&rdr, &walker);
|
|
}
|
|
|
|
template<typename Reader, typename Handler>
|
|
constexpr Error walkModel(DescriptorType *type, const char *data, std::size_t dataLen, Handler handler) noexcept {
|
|
DataWalker<Reader, Handler> walker(type, handler);
|
|
Reader rdr(reinterpret_cast<const uint8_t*>(data), dataLen);
|
|
return model(&rdr, &walker);
|
|
}
|
|
|
|
}
|