[ox] Move each library's include to its own include dir
Build / build (push) Failing after 21s

This commit is contained in:
2026-05-06 01:06:34 -05:00
parent bd792a0d25
commit d4807cd2a0
191 changed files with 248 additions and 208 deletions
+50
View File
@@ -0,0 +1,50 @@
add_library(
OxOrganicClaw
src/read.cpp
src/write.cpp
)
if(NOT MSVC)
target_compile_options(OxOrganicClaw PRIVATE -Wsign-conversion)
target_compile_options(OxOrganicClaw PRIVATE -Wconversion)
endif()
target_link_libraries(
OxOrganicClaw PUBLIC
OxModel
jsoncpp_object
)
target_include_directories(
OxOrganicClaw PUBLIC
${JSONCPP_INCLUDE_DIR}
)
set_property(
TARGET
OxOrganicClaw
PROPERTY
POSITION_INDEPENDENT_CODE ON
)
target_include_directories(
OxOrganicClaw PUBLIC
include
)
install(
DIRECTORY
include/ox
DESTINATION
include
)
install(
TARGETS OxOrganicClaw
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
if(OX_RUN_TESTS)
add_subdirectory(test)
endif()
+12
View File
@@ -0,0 +1,12 @@
/*
* 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 "read.hpp"
#include "write.hpp"
+313
View File
@@ -0,0 +1,313 @@
/*
* 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/def.hpp>
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
#include <json/json.h>
OX_ALLOW_UNSAFE_BUFFERS_END
#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 const m_unionIdx = -1;
public:
OrganicClawReader() noexcept = default;
OrganicClawReader(uint8_t const *buff, size_t buffSize);
OrganicClawReader(CString json, size_t buffSize);
explicit OrganicClawReader(Json::Value json, int unionIdx = -1) noexcept;
Error field(CString key, bool *val) noexcept;
// array handler
template<typename T>
Error field(CString key, T *val, size_t len) noexcept;
template<typename T>
Error field(CString, HashMap<String, T> *val) noexcept;
template<typename T>
Error field(CString key, T *val) noexcept;
template<typename U, bool force = false>
Error field(CString key, UnionView<U, force> val) noexcept;
template<size_t L>
Error field(CString key, BasicString<L> *val) noexcept;
template<size_t L>
Error field(CString key, IString<L> *val) noexcept;
Error fieldCString(CString key, char *val, size_t buffLen) noexcept;
Error fieldCString(CString key, char **val) noexcept;
Error fieldCString(CString key, char **val, size_t buffLen) noexcept;
Error field(CString key, UUID *val) noexcept;
/**
* Reads an array length from the current location in the buffer.
* @param key
* @param pass indicates that the parsing should iterate past the array length
*/
Result<size_t> arrayLength(CString key, bool pass = true) noexcept;
/**
* Reads an string length from the current location in the buffer.
*/
[[nodiscard]]
size_t stringLength(CString key) noexcept;
template<typename T = void>
constexpr Error setTypeInfo() noexcept {
return {};
}
template<typename T = void>
constexpr Error setTypeInfo(CString) noexcept {
return {};
}
template<typename T = void>
constexpr Error setTypeInfo(CString, int, const Vector<String>& = {}) noexcept {
return {};
}
template<typename T = void>
constexpr Error setTypeInfo(CString, int, const Vector<String>& = {}, size_t = {}) noexcept {
return {};
}
/**
* Returns a OrganicClawReader to parse a child object.
*/
[[nodiscard]]
OrganicClawReader child(CString key, int unionIdx = -1) noexcept;
// compatibility stub
constexpr void nextField() noexcept {}
[[nodiscard]]
bool fieldPresent(CString key) noexcept;
[[nodiscard]]
int whichFieldPresent(CString name, const ModelUnion &u) const noexcept;
[[nodiscard]]
static constexpr auto opType() noexcept {
return OpType::Read;
}
private:
[[nodiscard]]
Json::Value &value(CString key) noexcept;
[[nodiscard]]
bool targetValid() const noexcept;
};
template<typename T>
Error OrganicClawReader::field(CString key, T *val) noexcept {
Error err{};
try {
if constexpr (is_integer_v<T>) {
if (targetValid()) {
auto const &jv = value(key);
auto const rightType = sizeof(T) == 8 ?
(is_signed_v<T> ? jv.isInt64() : jv.isUInt64()) :
(is_signed_v<T> ? jv.isInt() : jv.isUInt());
if (jv.empty()) {
*val = 0;
} else if (rightType) {
if constexpr(is_signed_v<T>) {
*val = static_cast<T>(jv.asInt64());
} else {
*val = static_cast<T>(jv.asUInt64());
}
} else {
err = Error{1, "Type mismatch"};
}
}
} else if constexpr (isVector_v<T>) {
auto const &srcVal = value(key);
auto const srcSize = srcVal.size();
OX_RETURN_ERROR(resizeVector(*val, srcSize));
err = field(key, val->data(), val->size());
} else if constexpr (isArray_v<T>) {
auto const &srcVal = value(key);
auto const srcSize = srcVal.size();
if (srcSize > val->size()) {
err = Error{1, "Input array is too long"};
} else {
err = field(key, val->data(), val->size());
}
} else if (targetValid()) {
auto const &jv = value(key);
if (jv.empty() || jv.isObject()) {
auto reader = child(key);
ModelHandlerInterface handler(reader);
err = model(&handler, val);
} else {
err = Error{1, "Type mismatch"};
}
}
} catch (Json::LogicError const &e) {
err = Error{1, "error reading JSON data"};
}
++m_fieldIt;
return err;
}
template<typename U, bool force>
Error OrganicClawReader::field(CString const key, UnionView<U, force> val) noexcept {
Error err{};
if (targetValid()) {
auto const &jv = value(key);
if (jv.empty() || jv.isObject()) {
auto reader = child(key, val.idx());
ModelHandlerInterface handler(reader);
err = model(&handler, val.get());
} else {
err = Error{1, "Type mismatch"};
}
}
++m_fieldIt;
return err;
}
template<size_t L>
Error OrganicClawReader::field(CString const key, BasicString<L> *val) noexcept {
Error err{};
if (targetValid()) {
auto const &jv = value(key);
if (jv.empty()) {
*val = BasicString<L>{};
} else if (jv.isString()) {
*val = jv.asString().c_str();
} else {
err = Error{1, "Type mismatch"};
}
}
++m_fieldIt;
return err;
}
template<size_t L>
Error OrganicClawReader::field(CString const key, IString<L> *val) noexcept {
Error err{};
if (targetValid()) {
auto const &jv = value(key);
if (jv.empty()) {
*val = IString<L>{};
} else if (jv.isString()) {
*val = jv.asString().c_str();
} else {
err = Error{1, "Type mismatch"};
}
}
++m_fieldIt;
return err;
}
// array handler
template<typename T>
Error OrganicClawReader::field(CString const key, T *val, size_t valLen) noexcept {
auto const &srcVal = value(key);
if (!srcVal.isNull() && !srcVal.isArray()) {
return Error{1, "Type mismatch"};
}
auto srcSize = srcVal.size();
if (srcSize > valLen) {
return 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 {};
}
template<typename T>
Error OrganicClawReader::field(CString const key, HashMap<String, T> *val) noexcept {
auto const &srcVal = value(key);
if (!srcVal.isObject()) {
return 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) {
auto const k = keys[i].c_str();
OX_RETURN_ERROR(handler.field(k, &val->operator[](k)));
}
return {};
}
Error readOC(BufferView const buff, auto &val) noexcept {
// OrganicClawReader constructor can throw, but readOC should return its errors.
try {
Json::Value doc;
Json::CharReaderBuilder parserBuilder;
auto parser = UPtr<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 Error{1, "Could not parse JSON"};
}
OrganicClawReader reader(buff.data(), buff.size());
ModelHandlerInterface handler(reader);
return model(&handler, &val);
} catch (Error const &err) {
return err;
} catch (...) {
return 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(StringViewCR json) noexcept {
return readOC<T>(BufferView{json.data(), json.size()});
}
}
+285
View File
@@ -0,0 +1,285 @@
/*
* 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/def.hpp>
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
#include <json/json.h>
OX_ALLOW_UNSAFE_BUFFERS_END
#include <ox/model/fieldcounter.hpp>
#include <ox/model/modelhandleradaptor.hpp>
#include <ox/model/optype.hpp>
#include <ox/model/types.hpp>
#include <ox/model/typenamecatcher.hpp>
#include <ox/std/buffer.hpp>
#include <ox/std/hashmap.hpp>
#include <ox/std/string.hpp>
#include <ox/std/uuid.hpp>
namespace ox {
class OrganicClawWriter {
friend Result<Buffer> writeOC(const auto &val) noexcept;
friend Result<String> writeOCString(const auto &val) noexcept;
protected:
Json::Value m_json{Json::Value(Json::objectValue)};
Json::ArrayIndex m_fieldIt = 0;
int const m_unionIdx = -1;
public:
explicit OrganicClawWriter(int unionIdx = -1) noexcept;
explicit OrganicClawWriter(Json::Value json, int unionIdx = -1) noexcept;
Error field(CString const key, int8_t const *val) noexcept {
if (targetValid() && (*val || m_json.isArray())) {
value(key) = *val;
}
++m_fieldIt;
return {};
}
Error field(CString const key, int16_t const *val) noexcept {
if (targetValid() && (*val || m_json.isArray())) {
value(key) = *val;
}
++m_fieldIt;
return {};
}
Error field(CString const key, int32_t const *val) noexcept {
if (targetValid() && (*val || m_json.isArray())) {
value(key) = *val;
}
++m_fieldIt;
return {};
}
Error field(CString const key, int64_t const *val) noexcept {
if (targetValid() && (*val || m_json.isArray())) {
value(key) = *val;
}
++m_fieldIt;
return {};
}
Error field(CString const key, uint8_t const *val) noexcept {
if (targetValid() && (*val || m_json.isArray())) {
value(key) = *val;
}
++m_fieldIt;
return {};
}
Error field(CString const key, uint16_t const *val) noexcept {
if (targetValid() && (*val || m_json.isArray())) {
value(key) = *val;
}
++m_fieldIt;
return {};
}
Error field(CString const key, uint32_t const *val) noexcept {
if (targetValid() && (*val || m_json.isArray())) {
value(key) = *val;
}
++m_fieldIt;
return {};
}
Error field(CString const key, uint64_t const *val) noexcept {
if (targetValid() && (*val || m_json.isArray())) {
value(key) = *val;
}
++m_fieldIt;
return {};
}
Error field(char const*key, bool const *val) noexcept {
if (targetValid() && (*val || m_json.isArray())) {
value(key) = *val;
}
++m_fieldIt;
return {};
}
template<typename U, bool force = true>
Error field(char const*, UnionView<U, force> val) noexcept;
template<typename T>
Error field(char const*key, HashMap<String, T> const *val) noexcept {
if (targetValid()) {
const auto &keys = val->keys();
OrganicClawWriter w;
ModelHandlerInterface<OrganicClawWriter, OpType::Write> handler{w};
for (size_t i = 0; i < keys.size(); ++i) {
const auto k = keys[i].c_str();
if (k) [[likely]] {
OX_REQUIRE_M(value, val->at(k));
OX_RETURN_ERROR(handler.field(k, value));
}
}
value(key) = w.m_json;
}
++m_fieldIt;
return {};
}
template<size_t L>
Error field(char const*key, IString<L> const *val) noexcept {
if (targetValid() && val->size()) {
value(key) = val->c_str();
}
++m_fieldIt;
return {};
}
template<size_t L>
Error field(char const*key, BasicString<L> const *val) noexcept {
if (targetValid() && val->size()) {
value(key) = val->c_str();
}
++m_fieldIt;
return {};
}
Error fieldCString(CString, CString const *val, int len) noexcept;
Error fieldCString(CString name, CString const*val) noexcept;
Error field(CString key, UUID const *uuid) noexcept;
template<typename T>
Error field(CString, T const *val, size_t len) noexcept;
template<typename T>
Error field(CString, T const *val) noexcept;
template<typename T>
constexpr Error setTypeInfo(
const char* = T::TypeName,
int = T::TypeVersion) noexcept {
return {};
}
template<typename T>
constexpr Error setTypeInfo(
const char*,
int,
Vector<String> const&,
size_t) noexcept {
return {};
}
[[nodiscard]]
static constexpr auto opType() noexcept {
return OpType::Write;
}
private:
[[nodiscard]]
constexpr bool targetValid() const noexcept {
return static_cast<int>(m_fieldIt) == m_unionIdx || m_unionIdx == -1;
}
[[nodiscard]]
Json::Value &value(CString key) noexcept;
};
template<typename T>
Error OrganicClawWriter::field(CString key, T const *val, size_t const len) noexcept {
if (targetValid() && len) {
OrganicClawWriter w((Json::Value(Json::arrayValue)));
ModelHandlerInterface<OrganicClawWriter, OpType::Write> handler{w};
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
}
value(key) = w.m_json;
}
++m_fieldIt;
return {};
}
template<typename T>
Error OrganicClawWriter::field(CString key, T const *val) noexcept {
if constexpr(is_integer_v<T>) {
if (targetValid() && (*val || m_json.isArray())) {
// the int type needs to be normalized because jsoncpp doesn't
// factor in every permutation unsigned long, etc.
if constexpr(is_signed_v<T>) {
value(key) = static_cast<Int<8 * sizeof(*val)>>(*val);
} else {
value(key) = static_cast<Uint<8 * sizeof(*val)>>(*val);
}
}
} else if constexpr(isVector_v<T> || isArray_v<T>) {
return field(key, val->data(), val->size());
} else if (val && targetValid()) {
OrganicClawWriter w;
ModelHandlerInterface<OrganicClawWriter, OpType::Write> handler{w};
OX_RETURN_ERROR(model(&handler, val));
if (!w.m_json.empty() || m_json.isArray()) {
value(key) = w.m_json;
}
}
++m_fieldIt;
return {};
}
template<typename U, bool force>
Error OrganicClawWriter::field(CString key, UnionView<U, force> val) noexcept {
if (targetValid()) {
OrganicClawWriter w(val.idx());
ModelHandlerInterface<OrganicClawWriter, OpType::Write> handler{w};
OX_RETURN_ERROR(model(&handler, val.get()));
if (!w.m_json.isNull()) {
value(key) = w.m_json;
}
}
++m_fieldIt;
return {};
}
Result<Buffer> writeOC(auto const &val) noexcept {
OrganicClawWriter writer;
ModelHandlerInterface<OrganicClawWriter, OpType::Write> handler(writer);
OX_RETURN_ERROR(model(&handler, &val));
Json::StreamWriterBuilder const jsonBuilder;
auto const str = Json::writeString(jsonBuilder, writer.m_json);
Result<Buffer> buff;
buff.value.resize(str.size() + 1);
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
memcpy(buff.value.data(), str.data(), str.size() + 1);
OX_ALLOW_UNSAFE_BUFFERS_END
return buff;
}
Result<String> writeOCString(auto const &val) noexcept {
OrganicClawWriter writer;
ModelHandlerInterface<OrganicClawWriter, OpType::Write> handler(writer);
OX_RETURN_ERROR(model(&handler, &val));
Json::StreamWriterBuilder const jsonBuilder;
auto const str = Json::writeString(jsonBuilder, writer.m_json);
Result<String> buff;
buff.value.resize(str.size());
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
memcpy(buff.value.data(), str.data(), str.size() + 1);
OX_ALLOW_UNSAFE_BUFFERS_END
return buff;
}
}
+199
View File
@@ -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/.
*/
#include <ox/std/bit.hpp>
#include <utility>
#include <ox/oc/read.hpp>
namespace ox {
OrganicClawReader::OrganicClawReader(const uint8_t *buff, size_t const buffSize) {
auto json = reinterpret_cast<const char*>(buff);
auto jsonLen = ox::strnlen_s(json, buffSize);
Json::CharReaderBuilder parserBuilder;
auto parser = std::unique_ptr<Json::CharReader>(parserBuilder.newCharReader());
if (!parser->parse(json, json + jsonLen, &m_json, nullptr)) {
throw Exception(1, "Could not parse JSON");
}
}
OrganicClawReader::OrganicClawReader(CString const json, size_t const jsonLen) {
Json::CharReaderBuilder parserBuilder;
auto parser = std::unique_ptr<Json::CharReader>(parserBuilder.newCharReader());
if (!parser->parse(json, json + jsonLen, &m_json, nullptr)) {
throw Exception(1, "Could not parse JSON");
}
}
OrganicClawReader::OrganicClawReader(Json::Value json, int const unionIdx) noexcept:
m_json(std::move(json)),
m_unionIdx(unionIdx) {
}
Error OrganicClawReader::field(CString const key, bool *val) noexcept {
Error err{};
if (targetValid()) {
const auto &jv = value(key);
if (jv.empty()) {
*val = false;
} else if (jv.isBool()) {
*val = jv.asBool();
} else {
err = Error(1, "Type mismatch");
}
}
++m_fieldIt;
return err;
}
Error OrganicClawReader::fieldCString(CString const key, char *val, size_t const buffLen) noexcept {
Error err{};
CString begin = nullptr, end = nullptr;
const auto &jv = value(key);
if (targetValid()) {
if (jv.empty()) {
auto data = val;
if (data) {
data[0] = 0;
}
} else if (jv.isString()) {
jv.getString(&begin, &end);
const auto strSize = static_cast<size_t>(end - begin);
auto data = val;
if (strSize >= buffLen) {
err = Error(2, "String size exceeds capacity of destination");
} else {
memcpy(data, begin, static_cast<size_t>(strSize));
data[strSize] = 0;
}
} else {
err = Error(1, "Type mismatch");
}
}
++m_fieldIt;
return err;
}
Error OrganicClawReader::fieldCString(CString const key, char **val) noexcept {
Error err{};
CString begin = nullptr, end = nullptr;
const auto &jv = value(key);
auto &data = *val;
if (targetValid()) {
if (jv.empty()) {
if (data) {
data[0] = 0;
}
} else if (jv.isString()) {
jv.getString(&begin, &end);
const auto strSize = static_cast<size_t>(end - begin);
safeDelete(*val);
*val = new char[strSize + 1];
memcpy(data, begin, static_cast<size_t>(strSize));
data[strSize] = 0;
} else {
err = Error(1, "Type mismatch");
}
}
++m_fieldIt;
return err;
}
Error OrganicClawReader::fieldCString(CString const key, char **val, size_t const buffLen) noexcept {
Error err{};
CString begin = nullptr, end = nullptr;
const auto &jv = value(key);
if (targetValid()) {
if (jv.empty()) {
auto data = val;
if (data) {
data[0] = nullptr;
}
} else if (jv.isString()) {
jv.getString(&begin, &end);
const auto strSize = static_cast<size_t>(end - begin);
auto data = val;
if (strSize >= buffLen) {
safeDelete(*val);
*val = new char[strSize + 1];
}
memcpy(data, begin, static_cast<size_t>(strSize));
data[strSize] = nullptr;
} else {
err = Error(1, "Type mismatch");
}
}
++m_fieldIt;
return err;
}
Error OrganicClawReader::field(CString const key, UUID *val) noexcept {
UUIDStr str;
OX_RETURN_ERROR(field(key, &str));
return UUID::fromString(str).moveTo(*val);
}
Result<size_t> OrganicClawReader::arrayLength(CString const key, bool) noexcept {
const auto &jv = value(key);
if (jv.empty()) {
return 0;
}
if (jv.isArray()) {
return jv.size();
}
return Error(1, "Type mismatch");
}
[[nodiscard]]
size_t OrganicClawReader::stringLength(CString const key) noexcept {
CString begin = nullptr, end = nullptr;
const auto &jv = value(key);
if (jv.empty()) {
return 0;
}
if (jv.isString()) {
jv.getString(&begin, &end);
return static_cast<size_t>(end - begin);
}
return Error(1, "Type mismatch");
}
OrganicClawReader OrganicClawReader::child(CString const key, int const unionIdx) noexcept {
return OrganicClawReader(value(key), unionIdx);
}
bool OrganicClawReader::fieldPresent(CString const key) noexcept {
return !m_json[key].empty();
}
int OrganicClawReader::whichFieldPresent(CString const name, ModelUnion const &u) const noexcept {
const auto &obj = m_json[name];
if (!obj.isObject()) {
return -1;
}
const auto &keys = obj.getMemberNames();
if (keys.size() != 1) {
return -1;
}
return u.getKeyIdx(keys.front().c_str());
}
Json::Value &OrganicClawReader::value(CString const key) noexcept {
if (m_json.isArray()) {
return m_json[m_fieldIt];
} else {
return m_json[key];
}
}
bool OrganicClawReader::targetValid() const noexcept {
return static_cast<int>(m_fieldIt) == m_unionIdx || m_unionIdx == -1;
}
}
+50
View File
@@ -0,0 +1,50 @@
/*
* 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/oc/write.hpp>
namespace ox {
OrganicClawWriter::OrganicClawWriter(int const unionIdx) noexcept: m_unionIdx(unionIdx) {
}
OrganicClawWriter::OrganicClawWriter(Json::Value json, int const unionIdx) noexcept:
m_json(std::move(json)),
m_unionIdx(unionIdx) {
}
Error OrganicClawWriter::fieldCString(const char *key, const char *const *val, int const len) noexcept {
if (targetValid() && len) {
value(key) = *val;
}
++m_fieldIt;
return {};
}
Error OrganicClawWriter::fieldCString(const char *key, const char *const *val) noexcept {
return fieldCString(key, const_cast<const char**>(val), static_cast<int>(ox::strlen(val)));
}
Error OrganicClawWriter::field(const char *key, UUID const *uuid) noexcept {
const auto uuidStr = uuid->toString();
if (targetValid() && uuidStr.size()) {
value(key) = uuidStr.c_str();
}
++m_fieldIt;
return {};
}
Json::Value &OrganicClawWriter::value(const char *key) noexcept {
if (m_json.isArray()) {
return m_json[m_fieldIt];
} else {
return m_json[key];
}
}
}
+14
View File
@@ -0,0 +1,14 @@
add_executable(
OcTest
tests.cpp
)
target_link_libraries(
OcTest
OxOrganicClaw
)
add_test("[ox/oc] Writer" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/OcTest OrganicClawWriter)
add_test("[ox/oc] Reader" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/OcTest OrganicClawReader)
add_test("[ox/oc] OrganicClawModelValue" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/OcTest OrganicClawModelValue)
add_test("[ox/oc] OrganicClawDef" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/OcTest OrganicClawDef)
+364
View File
@@ -0,0 +1,364 @@
/*
* 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 <map>
#include <ox/model/model.hpp>
#include <ox/oc/oc.hpp>
#include <ox/std/std.hpp>
template<typename T>
union U {
T t;
int i;
};
U<short> u;
union TestUnion {
static constexpr auto TypeName = "TestUnion";
static constexpr auto TypeVersion = 1;
bool Bool;
uint32_t Int = 5;
char *String;
};
struct TestStructNest {
static constexpr auto TypeName = "TestStructNest";
static constexpr auto TypeVersion = 1;
bool Bool = false;
uint32_t Int = 0;
ox::IString<32> String = "";
};
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{""};
uint32_t List[4] = {0, 0, 0, 0};
ox::HashMap<ox::String, int> Map;
TestStructNest EmptyStruct;
TestStructNest Struct;
TestStruct() noexcept = default;
TestStruct(TestStruct &&other) noexcept;
constexpr ~TestStruct() noexcept {
if (unionIdx == 2) {
ox::safeDelete(Union.String);
}
}
constexpr TestStruct &operator=(TestStruct&&) noexcept;
};
constexpr ox::Error model(auto *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("String", &obj->String));
return ox::Error(0);
}
constexpr ox::Error model(auto *io, ox::CommonPtrWith<TestStructNest> auto *obj) noexcept {
OX_RETURN_ERROR(io->template setTypeInfo<TestStructNest>());
OX_RETURN_ERROR(io->field("Bool", &obj->Bool));
OX_RETURN_ERROR(io->field("Int", &obj->Int));
OX_RETURN_ERROR(io->field("String", &obj->String));
return ox::Error(0);
}
constexpr ox::Error model(auto *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 (io->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("List", obj->List, 4));
OX_RETURN_ERROR(io->field("Map", &obj->Map));
OX_RETURN_ERROR(io->field("EmptyStruct", &obj->EmptyStruct));
OX_RETURN_ERROR(io->field("Struct", &obj->Struct));
return ox::Error(0);
}
TestStruct::TestStruct(TestStruct &&other) noexcept {
ox::moveModel(this, &other);
}
constexpr TestStruct &TestStruct::operator=(TestStruct &&other) noexcept {
ox::moveModel(this, &other);
return *this;
}
const std::map<ox::StringView, ox::Error(*)()> tests = {
{
{
"OrganicClawWriter",
[] {
// This test doesn't confirm much, but it does show that the writer
// doesn't segfault
TestStruct ts;
return ox::writeOC(ts).error;
}
},
{
"OrganicClawReader",
[] {
TestStruct testIn;
testIn.Bool = true;
testIn.Int = 42;
testIn.Union.Int = 52;
testIn.String = "Test String 1";
testIn.List[0] = 1;
testIn.List[1] = 2;
testIn.List[2] = 3;
testIn.List[3] = 4;
testIn.Map["asdf"] = 93;
testIn.Map["aoeu"] = 94;
testIn.Struct.Bool = false;
testIn.Struct.Int = 300;
testIn.Struct.String = "Test String 2";
auto [oc, writeErr] = ox::writeOC(testIn);
oxAssert(writeErr, "writeOC failed");
oxOutf("{}\n", oc.data());
auto [testOut, readErr] = ox::readOC<TestStruct>(oc.data());
oxAssert(readErr, "readOC failed");
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");
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.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.String == testOut.EmptyStruct.String, "EmptyStruct.String value mismatch");
oxAssert(testIn.Struct.Int == testOut.Struct.Int, "Struct.Int value mismatch");
oxAssert(testIn.Struct.String == testOut.Struct.String, "Struct.String value mismatch");
oxAssert(testIn.Struct.Bool == testOut.Struct.Bool, "Struct.Bool value mismatch");
return ox::Error(0);
}
},
{
"OrganicClawModelValue",
[] {
ox::Buffer dataBuff;
TestStruct testIn;
testIn.Bool = true;
testIn.Int = 42;
testIn.String = "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.String = "Test String 2";
testIn.unionIdx = 1;
testIn.Union.Int = 93;
oxAssert(ox::writeOC(testIn).moveTo(dataBuff), "Data generation failed");
ox::TypeStore typeStore;
auto type = ox::buildTypeDef(typeStore, testIn);
oxAssert(type.error, "Descriptor write failed");
ox::ModelObject testOut;
OX_RETURN_ERROR(testOut.setType(type.value));
oxAssert(ox::readOC(dataBuff, testOut), "Data read failed");
oxAssert(testOut.get("Int").unwrap()->get<int>() == testIn.Int, "testOut.Int failed");
oxAssert(testOut.get("Bool").unwrap()->get<bool>() == testIn.Bool, "testOut.Bool failed");
oxAssert(testOut.get("String").unwrap()->get<ox::String>() == testIn.String, "testOut.String failed");
auto &testOutStruct = testOut.get("Struct").unwrap()->get<ox::ModelObject>();
auto &testOutUnion = testOut.get("Union").unwrap()->get<ox::ModelUnion>();
auto &testOutList = testOut.get("List").unwrap()->get<ox::ModelValueVector>();
auto testOutStructCopy = testOut.get("Struct").unwrap()->get<ox::ModelObject>();
auto testOutUnionCopy = testOut.get("Union").unwrap()->get<ox::ModelUnion>();
auto testOutListCopy = testOut.get("List").unwrap()->get<ox::ModelValueVector>();
oxAssert(testOutStruct.typeName() == TestStructNest::TypeName, "ModelObject TypeName failed");
oxAssert(testOutStruct.typeVersion() == TestStructNest::TypeVersion, "ModelObject TypeVersion failed");
oxAssert(testOutStruct.get("Bool").unwrap()->get<bool>() == testIn.Struct.Bool, "testOut.Struct.Bool failed");
oxAssert(testOutStruct.get("String").unwrap()->get<ox::String>() == testIn.Struct.String.c_str(), "testOut.Struct.String failed");
oxAssert(testOut.get("unionIdx").unwrap()->get<int>() == testIn.unionIdx, "testOut.unionIdx failed");
oxAssert(testOutUnion.unionIdx() == testIn.unionIdx, "testOut.Union idx wrong");
oxAssert(testOutUnion.get("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.get("Bool").unwrap()->get<bool>() == testIn.Struct.Bool, "testOut.Struct.Bool (copy) failed");
oxAssert(testOutStructCopy.get("String").unwrap()->get<ox::String>() == testIn.Struct.String.c_str(), "testOut.Struct.String (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);
}
},
{
"OrganicClawDef",
[] {
TestStruct testIn, testOut;
testIn.Bool = true;
testIn.Int = 42;
testIn.String = "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.String = "Test String 2";
auto [oc, ocErr] = ox::writeOC(testIn);
oxAssert(ocErr, "Data generation failed");
ox::TypeStore typeStore;
auto type = ox::buildTypeDef(typeStore, testIn);
oxAssert(type.error, "Descriptor write failed");
OX_RETURN_ERROR(ox::walkModel<ox::OrganicClawReader>(type.value, oc.data(), oc.size(),
[](const ox::Vector<ox::FieldName>&, const ox::Vector<ox::String>&, const ox::DescriptorField &f,
ox::OrganicClawReader *rdr) -> ox::Error {
auto fieldName = f.fieldName.c_str();
switch (f.type->primitiveType) {
case ox::PrimitiveType::UnsignedInteger:
oxOutf("{}:\tuint{}_t:\t", fieldName, f.type->length * 8);
switch (f.type->length) {
case 1: {
uint8_t i = {};
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
oxOutf("{}", i);
break;
}
case 2: {
uint16_t i = {};
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
oxOutf("{}", i);
break;
}
case 4: {
uint32_t i = {};
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
oxOutf("{}", i);
break;
}
case 8: {
uint64_t i = {};
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
oxOutf("{}", i);
break;
}
}
oxOut("\n");
break;
case ox::PrimitiveType::SignedInteger:
oxOutf("{}:\tint{}_t:\t", fieldName, f.type->length * 8);
switch (f.type->length) {
case 1: {
int8_t i = {};
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
oxOutf("{}", i);
break;
}
case 2: {
int16_t i = {};
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
oxOutf("{}", i);
break;
}
case 4: {
int32_t i = {};
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
oxOutf("{}", i);
break;
}
case 8: {
int64_t i = {};
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
oxOutf("{}", i);
break;
}
}
oxOut("\n");
break;
case ox::PrimitiveType::Bool: {
bool i = {};
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
oxOutf("{}:\tbool:\t{}\n", fieldName, i ? "true" : "false");
break;
}
case ox::PrimitiveType::String: {
ox::Vector<char> v(rdr->stringLength(fieldName) + 1);
oxAssert(rdr->fieldCString(fieldName, v.data(), v.size()), "Walking model failed.");
oxOutf("{}:\tstring:\t{}\n", fieldName, v.data());
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;
}