Files
nostalgia/deps/ox/src/ox/oc/read.hpp
Gary Talent 9f338a7429
All checks were successful
Build / build (push) Successful in 3m18s
[ox] Run liccor
2025-01-08 23:03:05 -06:00

305 lines
8.0 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 <json/json.h>
#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/hashmap.hpp>
#include <ox/std/memops.hpp>
#include <ox/std/memory.hpp>
#include <ox/std/string.hpp>
#include <ox/std/uuid.hpp>
namespace ox {
class OrganicClawReader {
private:
Json::Value m_json;
Json::ArrayIndex m_fieldIt = 0;
int m_unionIdx = -1;
public:
OrganicClawReader() noexcept = default;
OrganicClawReader(const uint8_t *buff, std::size_t buffSize);
OrganicClawReader(const char *json, std::size_t buffSize);
explicit OrganicClawReader(Json::Value json, int unionIdx = -1) noexcept;
Error field(const char *key, bool *val) noexcept;
// array handler
template<typename T>
Error field(const char *key, T *val, std::size_t len) noexcept;
template<typename T>
Error field(const char*, HashMap<String, T> *val) noexcept;
template<typename T>
Error field(const char *key, T *val) noexcept;
template<typename U, bool force = false>
Error field(const char *key, UnionView<U, force> val) noexcept;
template<std::size_t L>
Error field(const char *key, BasicString<L> *val) noexcept;
template<std::size_t L>
Error field(const char *key, IString<L> *val) noexcept;
Error fieldCString(const char *key, char *val, std::size_t buffLen) noexcept;
Error fieldCString(const char *key, char **val) noexcept;
Error fieldCString(const char *key, char **val, std::size_t buffLen) noexcept;
Error field(const char *key, UUID *val) noexcept;
/**
* Reads an array length from the current location in the buffer.
* @param pass indicates that the parsing should iterate past the array length
*/
Result<std::size_t> arrayLength(const char *key, bool pass = true) noexcept;
/**
* Reads an string length from the current location in the buffer.
*/
[[nodiscard]]
std::size_t stringLength(const char *name) noexcept;
template<typename T = void>
constexpr ox::Error setTypeInfo() noexcept {
return {};
}
template<typename T = void>
constexpr ox::Error setTypeInfo(const char*) noexcept {
return {};
}
template<typename T = void>
constexpr ox::Error setTypeInfo(const char*, int, const Vector<String>& = {}) noexcept {
return {};
}
template<typename T = void>
constexpr ox::Error setTypeInfo(const char*, int, const Vector<String>& = {}, std::size_t = {}) noexcept {
return {};
}
/**
* Returns a OrganicClawReader to parse a child object.
*/
[[nodiscard]]
OrganicClawReader child(const char *key, int unionIdx = -1) noexcept;
// compatibility stub
constexpr void nextField() noexcept {}
[[nodiscard]]
bool fieldPresent(const char *key) noexcept;
[[nodiscard]]
int whichFieldPresent(const char *name, const ModelUnion &u) const noexcept;
[[nodiscard]]
static constexpr auto opType() noexcept {
return OpType::Read;
}
private:
[[nodiscard]]
Json::Value &value(const char *key) noexcept;
[[nodiscard]]
bool targetValid() const noexcept;
};
template<typename T>
Error OrganicClawReader::field(const char *key, T *val) noexcept {
auto err = ox::Error(0);
try {
if constexpr (is_integer_v<T>) {
if (targetValid()) {
auto const&jv = value(key);
auto const rightType = sizeof(T) == 8 ?
(ox::is_signed_v<T> ? jv.isInt64() : jv.isUInt64()) :
(ox::is_signed_v<T> ? jv.isInt() : jv.isUInt());
if (jv.empty()) {
*val = 0;
} else if (rightType) {
*val = static_cast<T>(jv.asUInt());
} else {
err = ox::Error(1, "Type mismatch");
}
}
} else if constexpr (isVector_v<T>) {
const auto&srcVal = value(key);
const auto srcSize = srcVal.size();
OX_RETURN_ERROR(ox::resizeVector(*val, srcSize));
err = field(key, val->data(), val->size());
} else if constexpr (isArray_v<T>) {
const auto&srcVal = value(key);
const auto srcSize = srcVal.size();
if (srcSize > val->size()) {
err = ox::Error(1, "Input array is too long");
} else {
err = field(key, val->data(), val->size());
}
} else if (targetValid()) {
const auto&jv = value(key);
if (jv.empty() || jv.isObject()) {
auto reader = child(key);
ModelHandlerInterface handler(&reader);
err = model(&handler, val);
} else {
err = ox::Error(1, "Type mismatch");
}
}
} catch (Json::LogicError const&) {
err = ox::Error(1, "error reading JSON data");
}
++m_fieldIt;
return err;
}
template<typename U, bool force>
Error OrganicClawReader::field(const char *key, UnionView<U, force> val) noexcept {
auto err = ox::Error(0);
if (targetValid()) {
const auto &jv = value(key);
if (jv.empty() || jv.isObject()) {
auto reader = child(key, val.idx());
ModelHandlerInterface handler(&reader);
err = model(&handler, val.get());
} else {
err = ox::Error(1, "Type mismatch");
}
}
++m_fieldIt;
return err;
}
template<std::size_t L>
Error OrganicClawReader::field(const char *key, BasicString<L> *val) noexcept {
auto err = ox::Error(0);
if (targetValid()) {
const auto &jv = value(key);
if (jv.empty()) {
*val = BasicString<L>{};
} else if (jv.isString()) {
*val = jv.asString().c_str();
} else {
err = ox::Error(1, "Type mismatch");
}
}
++m_fieldIt;
return err;
}
template<std::size_t L>
Error OrganicClawReader::field(const char *key, IString<L> *val) noexcept {
auto err = ox::Error(0);
if (targetValid()) {
const auto &jv = value(key);
if (jv.empty()) {
*val = IString<L>{};
} else if (jv.isString()) {
*val = jv.asString().c_str();
} else {
err = ox::Error(1, "Type mismatch");
}
}
++m_fieldIt;
return err;
}
// array handler
template<typename T>
Error OrganicClawReader::field(const char *key, T *val, std::size_t valLen) noexcept {
const auto &srcVal = value(key);
if (!srcVal.isNull() && !srcVal.isArray()) {
return ox::Error(1, "Type mismatch");
}
auto srcSize = srcVal.size();
if (srcSize > valLen) {
return ox::Error(1);
}
OrganicClawReader r(srcVal);
ModelHandlerInterface handler{&r};
for (decltype(srcSize) i = 0; i < srcSize; ++i) {
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
OX_RETURN_ERROR(handler.field("", &val[i]));
OX_ALLOW_UNSAFE_BUFFERS_END
}
return ox::Error(0);
}
template<typename T>
Error OrganicClawReader::field(const char *key, HashMap<String, T> *val) noexcept {
const auto &srcVal = value(key);
if (!srcVal.isObject()) {
return ox::Error(1, "Type mismatch");
}
auto keys = srcVal.getMemberNames();
auto srcSize = srcVal.size();
OrganicClawReader r(srcVal);
ModelHandlerInterface handler{&r};
for (decltype(srcSize) i = 0; i < srcSize; ++i) {
const auto k = keys[i].c_str();
OX_RETURN_ERROR(handler.field(k, &val->operator[](k)));
}
return ox::Error(0);
}
Error readOC(BufferView buff, auto &val) noexcept {
// OrganicClawReader constructor can throw, but readOC should return its errors.
try {
Json::Value doc;
Json::CharReaderBuilder parserBuilder;
auto parser = UniquePtr<Json::CharReader>(parserBuilder.newCharReader());
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
if (!parser->parse(buff.data(), buff.data() + buff.size(), &doc, nullptr)) {
OX_ALLOW_UNSAFE_BUFFERS_END
return ox::Error(1, "Could not parse JSON");
}
OrganicClawReader reader(buff.data(), buff.size());
ModelHandlerInterface handler(&reader);
return model(&handler, &val);
} catch (const Error &err) {
return err;
} catch (...) {
return ox::Error(1, "Unknown Error");
}
}
template<typename T>
Result<T> readOC(BufferView buff) noexcept {
Result<T> val;
OX_RETURN_ERROR(readOC(buff, val.value));
return val;
}
template<typename T>
Result<T> readOC(ox::StringView json) noexcept {
return readOC<T>(ox::BufferView{json.data(), json.len()});
}
}