Merge commit '69fcd7ad1056940166a5d9524b4f03578f680834' as 'deps/oxlib'

This commit is contained in:
2026-05-06 01:38:00 -05:00
457 changed files with 53647 additions and 0 deletions
+45
View File
@@ -0,0 +1,45 @@
add_library(
OxModel
src/desctypes.cpp
src/descwrite.cpp
src/modelvalue.cpp
)
if(NOT MSVC)
target_compile_options(OxModel PRIVATE -Wconversion)
target_compile_options(OxModel PRIVATE -Wsign-conversion)
endif()
target_link_libraries(
OxModel PUBLIC
OxStd
)
if(NOT OX_BARE_METAL)
set_property(
TARGET
OxModel
PROPERTY
POSITION_INDEPENDENT_CODE ON
)
endif()
target_link_libraries(
OxModel PUBLIC
OxStd
)
target_include_directories(
OxModel PUBLIC
include
)
install(
TARGETS OxModel
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
if(OX_RUN_TESTS)
add_subdirectory(test)
endif()
+7
View File
@@ -0,0 +1,7 @@
<Type> : <TypeName><FieldList>
<FieldList> : <FieldList> | <FieldList><Field>
<Field> : <FieldType><TypeID><FieldName>
<TypeID> : <TypeName> | <TypeName><Type>
<TypeName> : <string>
<FieldType> : <0: single> | <1: list>
<FieldName> : <string>
+19
View File
@@ -0,0 +1,19 @@
/*
* 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/concepts.hpp>
// oxModelFwdDecl is necessary because Apple-Clang is broken...
#define OX_MODEL_FWD_DECL(modelName) constexpr ox::Error model(auto *io, ox::CommonPtrWith<modelName> auto *o) noexcept
#define OX_MODEL_BEGIN(modelName) constexpr ox::Error model(auto *io, [[maybe_unused]] ox::CommonPtrWith<modelName> auto *o) noexcept { OX_RETURN_ERROR(io->template setTypeInfo<modelName>());
#define OX_MODEL_END() return {}; }
#define OX_MODEL_FIELD(fieldName) OX_RETURN_ERROR(io->field(#fieldName, &o->fieldName));
#define OX_MODEL_FIELD_RENAME(objFieldName, serFieldName) OX_RETURN_ERROR(io->field(#serFieldName, &o->objFieldName));
#define OX_MODEL_FRIEND(modelName) friend constexpr ox::Error model(auto *io, ox::CommonPtrWith<modelName> auto *o) noexcept
@@ -0,0 +1,7 @@
<Type> : <TypeName><FieldList>
<FieldList> : <FieldList> | <FieldList><Field>
<Field> : <FieldType><TypeID><FieldName>
<TypeID> : <TypeName> | <TypeName><Type>
<TypeName> : <string>
<FieldType> : <0: single> | <1: list>
<FieldName> : <string>
+45
View File
@@ -0,0 +1,45 @@
/*
* 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 "typestore.hpp"
#include "desctypes.hpp"
namespace ox {
template<typename ReaderBase>
class TypeDescReader: public ReaderBase {
private:
TypeStore m_typeStore;
public:
constexpr TypeDescReader(const uint8_t *buff, std::size_t buffLen) noexcept;
[[nodiscard]]
constexpr const auto &typeStore() const noexcept;
};
template<typename ReaderBase>
constexpr TypeDescReader<ReaderBase>::TypeDescReader(const uint8_t *buff, std::size_t buffLen) noexcept:
ReaderBase(buff, buffLen) {
}
template<typename ReaderBase>
constexpr const auto &TypeDescReader<ReaderBase>::typeStore() const noexcept {
return m_typeStore;
}
template<typename ReaderBase, typename T>
constexpr int readMCDef(const uint8_t *buff, std::size_t buffLen, T *val) noexcept {
TypeDescReader<ReaderBase> reader(buff, buffLen);
return model(&reader, val);
}
}
+251
View File
@@ -0,0 +1,251 @@
/*
* 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/bit.hpp>
#include <ox/std/error.hpp>
#include <ox/std/fmt.hpp>
#include <ox/std/hashmap.hpp>
#include <ox/std/memory.hpp>
#include <ox/std/string.hpp>
#include <ox/std/typetraits.hpp>
#include <ox/std/vector.hpp>
#include "optype.hpp"
#include "types.hpp"
#include "typenamecatcher.hpp"
namespace ox {
using FieldName = String;
using TypeParamPack = Vector<String>;
template<typename T>
constexpr auto buildTypeId() noexcept {
constexpr auto name = requireModelTypeName<T>();
constexpr auto version = requireModelTypeVersion<T>();
return ox::sfmt("{};{}", name, version);
}
static constexpr auto buildTypeId(
StringViewCR name, int version,
const TypeParamPack &typeParams = {}) noexcept {
String tp;
if (!typeParams.empty()) {
tp = "#";
for (const auto &p : typeParams) {
tp += p + ",";
}
tp.resize(tp.size() - 1);
tp += "#";
}
return ox::sfmt("{}{};{}", name, tp, version);
}
enum class PrimitiveType: uint8_t {
UnsignedInteger = 0,
SignedInteger = 1,
Bool = 2,
// Float = 3, reserved, but not implemented
String = 4,
Struct = 5,
Union = 6,
};
struct Subscript {
static constexpr auto TypeName = "net.drinkingtea.ox.Subscript";
static constexpr auto TypeVersion = 1;
enum class SubscriptType: uint32_t {
None = 0,
Ptr = 1,
PtrArray = 2,
InlineArray = 3,
Vector = 4,
};
SubscriptType subscriptType = SubscriptType::None;
uint64_t length = 0;
uint64_t smallSzLen = 0;
};
template<typename T>
constexpr Error model(T *io, CommonPtrWith<Subscript> auto *type) noexcept {
OX_RETURN_ERROR(io->template setTypeInfo<Subscript>());
if constexpr(T::opType() == OpType::Reflect) {
uint32_t st = 0;
OX_RETURN_ERROR(io->field("subscriptType", &st));
} else if constexpr(T::opType() == OpType::Write) {
auto pt = type ? static_cast<uint8_t>(type->subscriptType) : 0;
OX_RETURN_ERROR(io->field("subscriptType", &pt));
} else {
auto pt = type ? static_cast<uint32_t>(type->subscriptType) : 0;
OX_RETURN_ERROR(io->field("subscriptType", &pt));
type->subscriptType = static_cast<Subscript::SubscriptType>(pt);
}
OX_RETURN_ERROR(io->field("length", &type->length));
OX_RETURN_ERROR(io->field("smallSzLen", &type->smallSzLen));
return {};
}
using SubscriptStack = Vector<Subscript, 3>;
struct DescriptorField {
// order of fields matters
static constexpr auto TypeName = "net.drinkingtea.ox.DescriptorField";
static constexpr auto TypeVersion = 3;
// do not serialize type
const struct DescriptorType *type = nullptr;
String fieldName;
int subscriptLevels = 0;
SubscriptStack subscriptStack;
String typeId; // gives reference to type for lookup if type is null
constexpr DescriptorField() noexcept = default;
constexpr DescriptorField(const DescriptorType *pType, String pFieldName,
int pSubscriptLevels,
SubscriptStack pSubscriptType,
String pTypeId) noexcept:
type(pType),
fieldName(std::move(pFieldName)),
subscriptLevels(pSubscriptLevels),
subscriptStack(std::move(pSubscriptType)),
typeId(std::move(pTypeId)) {
oxAssert(subscriptLevels <= static_cast<int>(subscriptStack.size()), "Subscript level mismatch");
}
constexpr DescriptorField(const DescriptorField &other) noexcept:
type(other.type),
fieldName(other.fieldName),
subscriptLevels(other.subscriptLevels),
subscriptStack(other.subscriptStack),
typeId(other.typeId) {
}
constexpr DescriptorField(DescriptorField &&other) noexcept:
type(other.type),
fieldName(std::move(other.fieldName)),
subscriptLevels(other.subscriptLevels),
subscriptStack(std::move(other.subscriptStack)),
typeId(std::move(other.typeId)) {
other.type = {};
other.subscriptLevels = {};
other.subscriptStack = {};
}
constexpr ~DescriptorField() noexcept = default;
constexpr DescriptorField &operator=(const DescriptorField &other) noexcept = delete;
constexpr DescriptorField &operator=(DescriptorField &&other) noexcept = delete;
};
using FieldList = Vector<DescriptorField>;
struct DescriptorType {
static constexpr auto TypeName = "net.drinkingtea.ox.TypeDescriptor";
static constexpr auto TypeVersion = 1;
String typeName;
int typeVersion = 0;
PrimitiveType primitiveType = PrimitiveType::UnsignedInteger;
TypeParamPack typeParams;
// fieldList only applies to structs and unions
FieldList fieldList;
// - number of bytes for integer and float types
// - number of fields for structs and lists
int64_t length = 0;
bool preloadable = false;
constexpr DescriptorType() noexcept = default;
constexpr explicit DescriptorType(String tn, int typeVersion, PrimitiveType t, TypeParamPack pTypeParams) noexcept:
typeName(std::move(tn)),
typeVersion(typeVersion),
primitiveType(t),
typeParams(std::move(pTypeParams)) {
}
};
[[nodiscard]]
constexpr auto buildTypeId(const DescriptorType &t) noexcept {
return buildTypeId(t.typeName, t.typeVersion, t.typeParams);
}
template<typename T>
constexpr Error model(T *io, CommonPtrWith<DescriptorType> auto *type) noexcept {
OX_RETURN_ERROR(io->template setTypeInfo<DescriptorType>());
OX_RETURN_ERROR(io->field("typeName", &type->typeName));
OX_RETURN_ERROR(io->field("typeVersion", &type->typeVersion));
if constexpr(T::opType() == OpType::Reflect) {
uint8_t pt = 0;
OX_RETURN_ERROR(io->field("primitiveType", &pt));
} else if constexpr(T::opType() == OpType::Write) {
auto pt = type ? static_cast<uint8_t>(type->primitiveType) : 0;
OX_RETURN_ERROR(io->field("primitiveType", &pt));
} else {
auto pt = type ? static_cast<uint8_t>(type->primitiveType) : 0;
OX_RETURN_ERROR(io->field("primitiveType", &pt));
type->primitiveType = static_cast<PrimitiveType>(pt);
}
OX_RETURN_ERROR(io->field("typeParams", &type->typeParams));
OX_RETURN_ERROR(io->field("fieldList", &type->fieldList));
OX_RETURN_ERROR(io->field("length", &type->length));
OX_RETURN_ERROR(io->field("preloadable", &type->preloadable));
return {};
}
template<typename T>
constexpr Error model(T *io, CommonPtrWith<DescriptorField> auto *field) noexcept {
OX_RETURN_ERROR(io->template setTypeInfo<DescriptorField>());
OX_RETURN_ERROR(io->field("typeId", &field->typeId));
OX_RETURN_ERROR(io->field("fieldName", &field->fieldName));
OX_RETURN_ERROR(io->field("subscriptLevels", &field->subscriptLevels));
OX_RETURN_ERROR(io->field("subscriptStack", &field->subscriptStack));
// defaultValue is unused now, but leave placeholder for backwards compatibility
int defaultValue = 0;
OX_RETURN_ERROR(io->field("defaultValue", &defaultValue));
return {};
}
template<typename ReaderBase>
class TypeDescReader;
#if 0 // unused right now
template<typename T>
constexpr Error model(TypeDescReader<T> *io, CommonPtrWith<DescriptorField> auto *field) noexcept {
io->template setTypeInfo<DescriptorField>();
oxReturnError(io->field("typeName", &field->typeName));
oxReturnError(io->field("typeVersion", &field->typeVersion));
auto &typeStore = io->typeStore();
auto &[type, err] = typeStore->at(field->typeName).value;
oxReturnError(err);
field->type = type.get();
oxReturnError(io->field("fieldName", &field->fieldName));
oxReturnError(io->field("subscriptLevels", &field->subscriptLevels));
if constexpr(T::opType() != ox::OpType::Reflect) {
auto subscriptStack = static_cast<uint32_t>(field->subscriptStack);
oxReturnError(io->field("subscriptStack", &subscriptStack));
field->subscriptStack = static_cast<DescriptorField::SubscriptType>(subscriptStack);
} else {
oxReturnError(io->field("subscriptStack", &field->subscriptStack));
}
// defaultValue is unused now, but placeholder for backwards compatibility
int defaultValue = 0;
oxReturnError(io->field("defaultValue", &defaultValue));
return {};
}
#endif
}
+404
View File
@@ -0,0 +1,404 @@
/*
* 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/byteswap.hpp>
#include <ox/std/istring.hpp>
#include <ox/std/memory.hpp>
#include <ox/std/string.hpp>
#include <ox/std/trace.hpp>
#include <ox/std/types.hpp>
#include <ox/std/vector.hpp>
#include "desctypes.hpp"
#include "fieldcounter.hpp"
#include "metadata.hpp"
#include "modelhandleradaptor.hpp"
#include "optype.hpp"
#include "typenamecatcher.hpp"
#include "types.hpp"
#include "typestore.hpp"
namespace ox {
namespace detail {
template<typename T>
constexpr int indirectionLevels_v = 0;
template<typename T>
constexpr int indirectionLevels_v<T*> = 1 + indirectionLevels_v<T>;
template<typename T, std::size_t sz>
constexpr int indirectionLevels_v<T[sz]> = 1 + indirectionLevels_v<T>;
template<typename T, std::size_t SmallVecSz>
constexpr int indirectionLevels_v<::ox::Vector<T, SmallVecSz>> = 1 + indirectionLevels_v<T>;
template<typename T>
constexpr auto buildSubscriptStack(const T*, SubscriptStack*) noexcept {
}
template<typename T>
constexpr auto buildSubscriptStack(const T**, SubscriptStack *s) noexcept {
s->push_back({.subscriptType = Subscript::SubscriptType::Ptr});
}
template<typename T>
constexpr auto buildSubscriptStack(const UPtr<T>*, SubscriptStack *s) noexcept {
s->push_back({.subscriptType = Subscript::SubscriptType::Ptr});
}
template<typename T, std::size_t sz>
constexpr auto buildSubscriptStack(const Array<T, sz>*, SubscriptStack *s) noexcept {
s->push_back({.subscriptType = Subscript::SubscriptType::InlineArray, .length = sz});
buildSubscriptStack(static_cast<T*>(nullptr), s);
}
template<typename T, std::size_t SmallVecSz>
constexpr auto buildSubscriptStack(const Vector<T, SmallVecSz>*, SubscriptStack *s) noexcept {
s->push_back({
.subscriptType = Subscript::SubscriptType::Vector,
.smallSzLen = SmallVecSz,
});
buildSubscriptStack(static_cast<T*>(nullptr), s);
}
template<typename T>
constexpr auto buildSubscriptStack(const T*) {
SubscriptStack s;
buildSubscriptStack(static_cast<const T*>(nullptr), &s);
return s;
}
}
class TypeDescWriter {
private:
TypeStore *m_typeStore = nullptr;
DescriptorType *m_type = nullptr;
public:
explicit constexpr TypeDescWriter(TypeStore *typeStore = nullptr) noexcept;
constexpr ~TypeDescWriter() noexcept = default;
template<typename T = std::nullptr_t>
constexpr ox::Error setTypeInfo(StringViewCR name = T::TypeName,
int version = T::TypeVersion,
const TypeParamPack &typeParams = {},
std::size_t fields = ModelFieldCount_v<T>) noexcept;
template<typename T>
constexpr Error field(
StringViewCR name,
T const*val,
std::size_t valLen,
SubscriptStack const&subscriptStack) noexcept;
template<typename T>
constexpr Error field(StringViewCR name, T const*val, std::size_t valLen) noexcept;
template<typename T, bool force>
constexpr Error field(StringViewCR name, UnionView<T, force> val) noexcept;
template<typename T>
constexpr Error field(StringViewCR name, const T *val) noexcept;
template<typename ...Args>
constexpr Error fieldCString(StringViewCR name, Args&&...) noexcept;
[[nodiscard]]
constexpr DescriptorType *definition() noexcept {
return m_type;
}
static constexpr auto opType() noexcept {
return OpType::Reflect;
}
private:
[[nodiscard]]
constexpr const DescriptorType *type(const int8_t *val) const noexcept;
[[nodiscard]]
constexpr const DescriptorType *type(const int16_t *val) const noexcept;
[[nodiscard]]
constexpr const DescriptorType *type(const int32_t *val) const noexcept;
[[nodiscard]]
constexpr const DescriptorType *type(const int64_t *val) const noexcept;
[[nodiscard]]
constexpr const DescriptorType *type(const uint8_t *val) const noexcept;
[[nodiscard]]
constexpr const DescriptorType *type(const uint16_t *val) const noexcept;
[[nodiscard]]
constexpr const DescriptorType *type(const uint32_t *val) const noexcept;
[[nodiscard]]
constexpr const DescriptorType *type(const uint64_t *val) const noexcept;
[[nodiscard]]
constexpr const DescriptorType *type(const bool *val) const noexcept;
[[nodiscard]]
constexpr const DescriptorType *type(const char *val) const noexcept;
template<std::size_t SmallStrSz>
[[nodiscard]]
constexpr const DescriptorType *type(const BasicString<SmallStrSz>*) const noexcept {
constexpr auto PT = PrimitiveType::String;
return getType(types::BasicString, 1, PT, 0, {sfmt("{}", SmallStrSz)});
}
template<std::size_t sz>
[[nodiscard]]
constexpr const DescriptorType *type(const IString<sz> *val) const noexcept;
template<typename T>
[[nodiscard]]
constexpr const DescriptorType *type(const T *val) const noexcept;
template<typename T>
[[nodiscard]]
constexpr const DescriptorType *type(const HashMap<String, T> *val) const noexcept;
template<typename U>
[[nodiscard]]
constexpr const DescriptorType *type(UnionView<U> val) const noexcept;
[[nodiscard]]
constexpr const DescriptorType *getType(StringViewCR tn, int typeVersion, PrimitiveType t, int b,
const TypeParamPack &typeParams = {}) const noexcept;
};
constexpr TypeDescWriter::TypeDescWriter(TypeStore *typeStore) noexcept: m_typeStore(typeStore) {}
template<typename T>
constexpr ox::Error TypeDescWriter::setTypeInfo(
StringViewCR typeName, int typeVersion,
const TypeParamPack &typeParams, std::size_t) noexcept {
PrimitiveType pt;
if constexpr(is_union_v<T>) {
pt = PrimitiveType::Union;
} else if constexpr(isBasicString_v<T> || isIString_v<T>) {
pt = PrimitiveType::String;
} else {
pt = PrimitiveType::Struct;
}
m_type = m_typeStore->getInit(typeName, typeVersion, pt, typeParams);
m_type->preloadable = preloadable<T>::value;
return {};
}
// array handler
template<typename T>
constexpr Error TypeDescWriter::field(StringViewCR name, T const*, std::size_t, SubscriptStack const&subscriptStack) noexcept {
if (m_type) {
constexpr typename remove_pointer<T>::type *p = nullptr;
const auto t = type(p);
oxAssert(t != nullptr, "field(const char *name, T *val, std::size_t): Type not found or generated");
m_type->fieldList.emplace_back(t, String(name), detail::indirectionLevels_v<T> + 1, subscriptStack, buildTypeId(*t));
return {};
}
return ox::Error(1);
}
// array handler
template<typename T>
constexpr Error TypeDescWriter::field(StringViewCR name, T const*, std::size_t) noexcept {
if (m_type) {
constexpr typename remove_pointer<T>::type *p = nullptr;
const auto t = type(p);
oxAssert(t != nullptr, "field(const char *name, T *val, std::size_t): Type not found or generated");
auto const lvls = detail::indirectionLevels_v<T> + 1;
SubscriptStack subscriptStack{lvls};
m_type->fieldList.emplace_back(t, String(name), lvls, subscriptStack, buildTypeId(*t));
return {};
}
return ox::Error(1);
}
template<typename T, bool force>
constexpr Error TypeDescWriter::field(StringViewCR name, UnionView<T, force> val) noexcept {
if (m_type) {
const auto t = type(val);
oxAssert(t != nullptr, "field(const char *name, T val): Type not found or generated");
m_type->fieldList.emplace_back(t, String(name), 0, SubscriptStack{}, ox::String(t->typeName));
return {};
}
return ox::Error(1);
}
template<typename T>
constexpr Error TypeDescWriter::field(StringViewCR name, const T *val) noexcept {
if (m_type) {
if constexpr(isVector_v<T> || isArray_v<T>) {
typename T::value_type *data = nullptr;
return field(name, data, 0, detail::buildSubscriptStack(val));
} else if constexpr(isSmartPtr_v<T>) {
typename T::value_type *data = nullptr;
return field(name, data, 0, detail::buildSubscriptStack(val));
} else if constexpr(is_pointer_v<T>) {
return field(name, val, 0, detail::buildSubscriptStack(val));
} else {
const auto t = type(val);
oxAssert(t != nullptr, "field(const char *name, T *val): Type not found or generated");
m_type->fieldList.emplace_back(t, String(name), 0, SubscriptStack{}, buildTypeId(*t));
return {};
}
}
return ox::Error(1);
}
template<typename ...Args>
constexpr Error TypeDescWriter::fieldCString(StringViewCR name, Args&&...) noexcept {
constexpr auto s = "";
const auto t = type(s);
m_type->fieldList.emplace_back(t, String(name), 0, SubscriptStack{}, ox::String(t->typeName));
return {};
}
template<typename T>
constexpr const DescriptorType *TypeDescWriter::type(const T *val) const noexcept {
if constexpr(isVector_v<T>) {
return type(static_cast<typename T::value_type*>(nullptr));
} else {
auto [t, err] = m_typeStore->template get<T>();
if (!err) {
return t;
} else {
TypeDescWriter dw(m_typeStore);
const auto reflectErr = model(&dw, val);
oxLogError(reflectErr);
oxAssert(reflectErr, "field(const char *name, T val): Type info could not be generated");
return dw.m_type;
}
}
}
template<typename T>
constexpr const DescriptorType *TypeDescWriter::type(const HashMap<String, T>*) const noexcept {
return type(static_cast<T*>(nullptr));
}
template<typename U>
constexpr const DescriptorType *TypeDescWriter::type(UnionView<U> val) const noexcept {
const auto t = type(val.get());
oxAssert(t != nullptr, "field(const char *name, T val): Type not found or generated");
return t;
}
constexpr const DescriptorType *TypeDescWriter::type(const int8_t*) const noexcept {
constexpr auto PT = PrimitiveType::SignedInteger;
constexpr auto Bytes = 1;
return getType(types::Int8, 0, PT, Bytes);
}
constexpr const DescriptorType *TypeDescWriter::type(const int16_t*) const noexcept {
constexpr auto PT = PrimitiveType::SignedInteger;
constexpr auto Bytes = 2;
return getType(types::Int16, 0, PT, Bytes);
}
constexpr const DescriptorType *TypeDescWriter::type(const int32_t*) const noexcept {
constexpr auto PT = PrimitiveType::SignedInteger;
constexpr auto Bytes = 4;
return getType(types::Int32, 0, PT, Bytes);
}
constexpr const DescriptorType *TypeDescWriter::type(const int64_t*) const noexcept {
constexpr auto PT = PrimitiveType::SignedInteger;
constexpr auto Bytes = 8;
return getType(types::Int64, 0, PT, Bytes);
}
constexpr const DescriptorType *TypeDescWriter::type(const uint8_t*) const noexcept {
constexpr auto PT = PrimitiveType::UnsignedInteger;
constexpr auto Bytes = 1;
return getType(types::Uint8, 0, PT, Bytes);
}
constexpr const DescriptorType *TypeDescWriter::type(const uint16_t*) const noexcept {
constexpr auto PT = PrimitiveType::UnsignedInteger;
constexpr auto Bytes = 2;
return getType(types::Uint16, 0, PT, Bytes);
}
constexpr const DescriptorType *TypeDescWriter::type(const uint32_t*) const noexcept {
constexpr auto PT = PrimitiveType::UnsignedInteger;
constexpr auto Bytes = 4;
return getType(types::Uint32, 0, PT, Bytes);
}
constexpr const DescriptorType *TypeDescWriter::type(const uint64_t*) const noexcept {
constexpr auto PT = PrimitiveType::UnsignedInteger;
constexpr auto Bytes = 8;
return getType(types::Uint64, 0, PT, Bytes);
}
constexpr const DescriptorType *TypeDescWriter::type(const bool*) const noexcept {
constexpr auto PT = PrimitiveType::Bool;
constexpr auto Bytes = 0;
return getType(types::Bool, 0, PT, Bytes);
}
constexpr const DescriptorType *TypeDescWriter::type(const char*) const noexcept {
constexpr auto PT = PrimitiveType::String;
return getType(types::String, 0, PT, 0);
}
template<std::size_t sz>
constexpr const DescriptorType *TypeDescWriter::type(const IString<sz>*) const noexcept {
constexpr auto PT = PrimitiveType::String;
return getType(types::IString, 0, PT, 0);
}
constexpr const DescriptorType *TypeDescWriter::getType(StringViewCR tn, int typeVersion, PrimitiveType pt, int b,
const TypeParamPack &typeParams) const noexcept {
auto t = m_typeStore->get(tn, typeVersion, typeParams);
if (!t.error) {
auto type = t.value;
oxAssert(type != nullptr, "TypeDescWriter::getType returning null DescriptorType");
return type;
} else {
auto dt = ox::make_unique<DescriptorType>(String(tn), typeVersion, pt, typeParams);
dt->length = b;
const auto out = dt.get();
const auto typeId = buildTypeId(tn, typeVersion, typeParams);
m_typeStore->set(typeId, std::move(dt));
return out;
}
}
template<typename T>
constexpr Result<DescriptorType*> buildTypeDef(TypeStore &typeStore) noexcept {
TypeDescWriter writer(&typeStore);
ModelHandlerInterface<TypeDescWriter, ox::OpType::Reflect> handler(writer);
if (std::is_constant_evaluated()) {
std::allocator<T> a;
T *t = a.allocate(1);
OX_RETURN_ERROR(model(&handler, t));
a.deallocate(t, 1);
} else {
auto t = ox_malloca(sizeof(T), T);
OX_RETURN_ERROR(model(&handler, t.get()));
}
return writer.definition();
}
template<typename T>
constexpr Result<DescriptorType*> buildTypeDef(TypeStore &typeStore, T &val) noexcept {
TypeDescWriter writer(&typeStore);
ModelHandlerInterface<TypeDescWriter, ox::OpType::Reflect> handler(writer);
OX_RETURN_ERROR(model(&handler, &val));
return writer.definition();
}
}
+79
View File
@@ -0,0 +1,79 @@
/*
* 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/assert.hpp>
#include <ox/std/bit.hpp>
#include <ox/std/error.hpp>
#include "optype.hpp"
namespace ox {
namespace detail {
template<typename T>
class FieldCounter {
public:
std::size_t fields = 0;
template<typename U = std::nullptr_t>
constexpr ox::Error setTypeInfo(StringViewCR = "", int = 0, const Vector<String>& = {}, std::size_t = 0) {
return {};
}
template<typename U>
constexpr ox::Error field(StringViewCR, U) noexcept {
++fields;
return {};
}
template<typename U>
constexpr ox::Error field(StringViewCR, U, std::size_t) noexcept {
++fields;
return {};
}
template<typename U, typename Handler>
constexpr Error field(StringViewCR, Handler) {
++fields;
return {};
}
template<typename ...Args>
constexpr Error fieldCString(Args&&...) noexcept {
++fields;
return {};
}
static constexpr auto opType() noexcept {
return OpType::Reflect;
}
};
}
namespace detail {
template<typename T>
[[nodiscard]]
consteval auto modelFieldCount() noexcept {
auto a = std::allocator<T>();
auto t = a.allocate(1);
detail::FieldCounter<T> c;
const auto err = model(&c, t);
oxAssert(err, "Count failed");
a.deallocate(t, 1);
return c.fields;
}
}
template<typename T>
constexpr auto ModelFieldCount_v = detail::modelFieldCount<T>();
}
+66
View File
@@ -0,0 +1,66 @@
/*
* 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/byteswap.hpp>
#include <ox/std/istring.hpp>
#include <ox/std/memory.hpp>
#include <ox/std/string.hpp>
#include <ox/std/trace.hpp>
#include <ox/std/types.hpp>
#include <ox/std/vector.hpp>
#include "desctypes.hpp"
#include "fieldcounter.hpp"
#include "optype.hpp"
#include "types.hpp"
namespace ox {
namespace detail {
template<bool>
struct BoolWrapper {
};
template<int>
struct IntWrapper {
};
}
template<typename T, typename = detail::BoolWrapper<true>>
struct preloadable: false_type {
};
template<typename T>
struct preloadable<T, detail::BoolWrapper<T::Preloadable>> {
static constexpr bool value = T::Preloadable;
};
// cannot be done until C++20
//struct PseudoString {
// constexpr PseudoString(const char* = "") noexcept {}
//};
//
//template<PseudoString>
//struct StringWrapper {
//};
//
//template<typename T, typename = StringWrapper<"">>
//struct ModelTypeName {
// static constexpr const char *value = "";
//};
//
//template<typename T>
//struct ModelTypeName<T, detail::StringWrapper<T::TypeName>> {
// static constexpr const char *value = T::TypeName;
//};
}
+22
View File
@@ -0,0 +1,22 @@
/*
* 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 "def.hpp"
#include "descread.hpp"
#include "desctypes.hpp"
#include "descwrite.hpp"
#include "fieldcounter.hpp"
#include "modelhandleradaptor.hpp"
#include "modelops.hpp"
#include "modelvalue.hpp"
#include "typenamecatcher.hpp"
#include "types.hpp"
#include "typestore.hpp"
#include "walk.hpp"
@@ -0,0 +1,243 @@
/*
* 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/utility.hpp>
#include "modelvalue.hpp"
namespace ox {
template<typename Handler, OpType opType_v = Handler::opType()>
class ModelHandlerInterface {
private:
Handler &m_handler;
public:
constexpr explicit ModelHandlerInterface(Handler &handler) noexcept: m_handler(handler) {
}
template<typename T = std::nullptr_t>
constexpr ox::Error setTypeInfo(
CString name = T::TypeName,
int version = T::TypeVersion,
Vector<String> const &typeParams = {}) noexcept {
return m_handler.template setTypeInfo<T>(name, version, typeParams, ModelFieldCount_v<T>);
}
template<typename T = std::nullptr_t>
constexpr ox::Error setTypeInfo(
CString name,
int version,
Vector<String> const &typeParams,
size_t fields) noexcept {
return m_handler.template setTypeInfo<T>(name, version, typeParams, fields);
}
template<size_t len>
constexpr Error fieldCString(CString name, char val[len]) noexcept {
return m_handler.fieldCString(name, &val[0], len);
}
template<size_t len>
constexpr Error fieldCString(CString name, char const val[len]) noexcept requires(opType_v != OpType::Read) {
if constexpr(opType_v != OpType::Read) {
return m_handler.fieldCString(name, &val[0], len);
} else {
return {};
}
}
constexpr Error fieldCString(CString name, char **val) noexcept {
return m_handler.fieldCString(name, val);
}
constexpr Error fieldCString(CString name, char const *const*val) noexcept requires(opType_v != OpType::Read) {
// this check looks pointless, but it's to address a Clang bug
if constexpr(opType_v != OpType::Read) {
return m_handler.fieldCString(name, val);
} else {
return {};
}
}
constexpr Error fieldCString(CString name, char const **val) noexcept requires(opType_v != OpType::Read) {
// this check looks pointless, but it's to address a Clang bug
if constexpr(opType_v != OpType::Read) {
return m_handler.fieldCString(name, val);
} else {
return {};
}
}
constexpr Error fieldCString(CString name, char **val, size_t buffLen) noexcept {
return m_handler.fieldCString(name, val, buffLen);
}
constexpr Error fieldCString(CString name, char const **val, size_t buffLen) noexcept requires(opType_v != OpType::Read) {
// this check looks pointless, but it's to address a Clang bug
if constexpr(opType_v != OpType::Read) {
return m_handler.fieldCString(name, val, buffLen);
} else {
return {};
}
}
constexpr Error fieldCString(CString name, char *val, size_t buffLen) noexcept {
return m_handler.fieldCString(name, val, buffLen);
}
constexpr Error fieldModelValue(char const *name, CommonPtrWith<ModelValue> auto *v) noexcept {
switch (v->type()) {
case ModelValue::Type::Undefined:
break;
case ModelValue::Type::Bool:
return m_handler.field(name, &v->template get<bool>());
case ModelValue::Type::UnsignedInteger8:
return m_handler.field(name, &v->template get<uint8_t>());
case ModelValue::Type::UnsignedInteger16:
return m_handler.field(name, &v->template get<uint16_t>());
case ModelValue::Type::UnsignedInteger32:
return m_handler.field(name, &v->template get<uint32_t>());
case ModelValue::Type::UnsignedInteger64:
return m_handler.field(name, &v->template get<uint64_t>());
case ModelValue::Type::SignedInteger8:
return m_handler.field(name, &v->template get<int8_t>());
case ModelValue::Type::SignedInteger16:
return m_handler.field(name, &v->template get<int16_t>());
case ModelValue::Type::SignedInteger32:
return m_handler.field(name, &v->template get<int32_t>());
case ModelValue::Type::SignedInteger64:
return m_handler.field(name, &v->template get<int64_t>());
case ModelValue::Type::String:
return m_handler.field(name, &v->template get<String>());
case ModelValue::Type::Object:
return m_handler.field(name, &v->template get<ModelObject>());
case ModelValue::Type::Union:
{
auto &u = v->template get<ModelUnion>();
if constexpr(opType_v == OpType::Read) {
u.setActiveField(whichFieldPresent(m_handler, name, u));
return m_handler.field(name, UnionView<ModelUnion, true>(&u, u.unionIdx()));
} else {
return m_handler.field(name, UnionView<ModelUnion const, true>(&u, u.unionIdx()));
}
}
case ModelValue::Type::Vector:
return m_handler.field(name, &v->template get<ModelValueVector>());
case ModelValue::Type::InlineArray:
return m_handler.field(name, &v->template get<ModelValueArray>());
}
oxErrf("invalid type: {}: {}\n", name, static_cast<int>(v->type()));
ox::panic("invalid type");
return ox::Error(1, "invalid type");
}
// array handler, with callback to allow handling individual elements
template<typename T, typename Callback>
constexpr Error field(CString name, Callback cb) noexcept {
return m_handler.template field<T, Callback>(name, cb);
}
template<typename T>
constexpr Error field(CString name, const T *v) noexcept {
if constexpr(ox::is_same_v<T, ModelValue>) {
return fieldModelValue(name, v);
} else {
return m_handler.field(name, v);
}
}
template<typename T>
constexpr Error field(CString name, T *v) noexcept {
if constexpr(ox::is_same_v<T, ModelValue>) {
return fieldModelValue(name, v);
} else {
return m_handler.field(name, v);
}
}
template<typename U, bool force = false>
constexpr Error field(CString name, UnionView<U, force> val) noexcept {
return m_handler.field(name, val);
}
constexpr Error field(CString name, auto *val, size_t len) noexcept {
return m_handler.field(name, val, len);
}
/**
* Reads an array length from the current location in the buffer.
* @param name
* @param pass indicates that the parsing should iterate past the array length
*/
[[nodiscard]]
constexpr auto arrayLength(CString name, bool pass = true) noexcept {
return m_handler.arrayLength(name, pass);
}
/**
* Reads an string length from the current location in the buffer.
*/
[[nodiscard]]
constexpr auto stringLength(CString name) noexcept {
return m_handler.stringLength(name);
}
[[nodiscard]]
static constexpr auto opType() noexcept {
return Handler::opType();
}
[[nodiscard]]
constexpr auto handler() noexcept {
return m_handler;
}
private:
template<typename H>
static constexpr int whichFieldPresent(H &h, CString name, ModelUnion const &u) noexcept
requires(H::opType() == OpType::Read) {
return h.whichFieldPresent(name, u);
}
template<typename H>
static constexpr int whichFieldPresent(H&, CString, ModelUnion const&) noexcept
requires(H::opType() != OpType::Read) {
return 0;
}
};
template<typename Handler, OpType opType_v = Handler::opType()>
class ModelHandlerBase {
private:
ModelHandlerInterface<Handler, opType_v> m_interface{*static_cast<Handler*>(this)};
public:
[[nodiscard]]
constexpr auto interface() noexcept {
return &m_interface;
}
[[nodiscard]]
static constexpr OpType opType() noexcept {
return opType_v;
}
};
constexpr ox::Error resizeVector(auto &vec, size_t sz) {
if constexpr(ox::is_same_v<decltype(vec.resize(0)), ox::Error>) {
return vec.resize(sz);
} else {
vec.resize(sz);
return {};
}
}
}
+303
View File
@@ -0,0 +1,303 @@
/*
* 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/bit.hpp>
#include <ox/std/error.hpp>
#include <ox/std/types.hpp>
#include <ox/std/utility.hpp>
#include "fieldcounter.hpp"
#include "modelvalue.hpp"
#include "optype.hpp"
#include "types.hpp"
namespace ox {
namespace detail {
class Wrap {
public:
virtual ~Wrap() = default;
};
template<typename T>
class WrapT: public Wrap {
private:
T *m_obj = nullptr;
public:
constexpr WrapT(T *obj) noexcept: m_obj(obj) {
}
constexpr WrapT() = default;
[[nodiscard]]
constexpr auto obj() noexcept {
return m_obj;
}
};
template<std::size_t size>
class MemberList {
private:
std::size_t m_i = 0;
public:
Array<void*, size> vars;
template<typename T>
constexpr Error field(const char*, T *v) noexcept {
vars[m_i++] = static_cast<void*>(v);
return {};
}
template<typename T>
constexpr Error field(const char*, T *v, int) noexcept {
vars[m_i++] = static_cast<void*>(v);
return {};
}
template<typename U, bool force = false>
constexpr Error field(const char*, UnionView<U, force> u) noexcept {
vars[m_i++] = static_cast<void*>(u.get());
return {};
}
template<typename T>
constexpr ox::Error setTypeInfo(
const char* = T::TypeName,
int = T::TypeVersion,
const Vector<String>& = {},
std::size_t = ModelFieldCount_v<T>) noexcept {
return {};
}
[[nodiscard]]
static constexpr auto opType() noexcept {
return OpType::Reflect;
}
};
template<std::size_t size>
class Copier {
private:
std::size_t m_i = 0;
MemberList<size> *m_dst = nullptr;
public:
constexpr explicit Copier(MemberList<size> *dst) noexcept: m_dst(dst) {
}
template<typename FT>
constexpr Error field(const char *name, const FT *v) noexcept {
if constexpr(isVector_v<FT>) {
return field(name, v->data(), v->size());
} else {
auto &src = *v;
auto &dst = *cbit_cast<FT*>(m_dst->vars[m_i]);
dst = src;
++m_i;
return {};
}
}
template<typename FT>
constexpr Error field(const char*, const FT *list, int elements) {
for (auto i = 0l; i < elements; ++i) {
auto &src = list[i];
auto &dst = cbit_cast<FT*>(m_dst->vars[m_i])[i];
dst = src;
}
++m_i;
return {};
}
template<typename U, bool force = false>
constexpr Error field(const char*, UnionView<U, force> u) {
auto &dst = *cbit_cast<U*>(m_dst->vars[m_i]);
auto &src = *u.get();
dst = src;
++m_i;
return {};
}
template<typename T = void>
constexpr ox::Error setTypeInfo(
const char* = T::TypeName,
int = T::TypeVersion,
const Vector<String>& = {},
int = ModelFieldCount_v<T>) noexcept {
return {};
}
[[nodiscard]]
static constexpr auto opType() noexcept {
return OpType::Read;
}
};
template<std::size_t size>
class Mover {
private:
std::size_t m_i = 0;
MemberList<size> *m_dst = nullptr;
public:
constexpr explicit Mover(MemberList<size> *dst) noexcept: m_dst(dst) {
}
template<typename FT>
constexpr Error field(const char *name, FT *v) noexcept {
if constexpr(isVector_v<FT>) {
return field(name, v->data(), v->size());
} else {
auto &src = *v;
auto &dst = *cbit_cast<FT*>(m_dst->vars[m_i]);
dst = std::move(src);
src = FT{};
++m_i;
return {};
}
}
template<typename FT>
constexpr Error field(const char*, FT *list, int elements) noexcept {
for (auto i = 0l; i < elements; ++i) {
auto &src = list[i];
auto &dst = cbit_cast<FT*>(m_dst->vars[m_i])[i];
dst = std::move(src);
src = FT{};
}
++m_i;
return {};
}
template<typename U, bool force = false>
constexpr Error field(const char*, UnionView<U, force> u) noexcept {
auto &dst = *cbit_cast<U*>(m_dst->vars[m_i]);
auto &src = *u.get();
dst = std::move(src);
++m_i;
return {};
}
template<typename T = void>
constexpr ox::Error setTypeInfo(
const char* = T::TypeName,
int = T::TypeVersion,
const Vector<String>& = {},
int = ModelFieldCount_v<T>) noexcept {
return {};
}
[[nodiscard]]
static constexpr auto opType() noexcept {
return OpType::Read;
}
};
template<std::size_t size>
class Equals {
private:
std::size_t m_i = 0;
MemberList<size> *m_other = nullptr;
public:
bool value = false;
constexpr Equals(MemberList<size> *other) noexcept: m_other(other) {
}
template<typename FT>
constexpr Error field(const char*, const FT *v) noexcept {
const auto &src = *v;
const auto &dst = std::bit_cast<FT>(*m_other->vars[m_i]);
++m_i;
if (dst == src) {
return {};
} else {
this->value = false;
return ox::Error(1);
}
}
template<typename FT>
constexpr Error field(const char*, const FT *list, int elements) noexcept {
for (auto i = 0l; i < elements; ++i) {
const auto &src = list[i];
const auto &dst = cbit_cast<FT*>(m_other->vars[m_i])[i];
if (!(dst == src)) {
this->value = false;
return ox::Error(1);
}
}
++m_i;
return {};
}
template<typename U, bool force = false>
constexpr Error field(const char*, UnionView<U, force> u) noexcept {
const auto &dst = *cbit_cast<U*>(m_other->vars[m_i]);
const auto &src = *u.get();
++m_i;
if (dst == src) {
return {};
} else {
this->value = false;
return ox::Error(1);
}
}
[[nodiscard]]
static constexpr auto opType() noexcept {
return OpType::Read;
}
};
}
template<typename T>
constexpr void moveModel(T *dst, T *src) noexcept {
constexpr auto size = ModelFieldCount_v<T>;
detail::MemberList<size> dstFields;
detail::Mover<size> mover(&dstFields);
std::ignore = model(&dstFields, dst);
std::ignore = model(&mover, src);
}
template<typename T>
constexpr void copyModel(T *dst, const T *src) noexcept {
constexpr auto size = ModelFieldCount_v<T>;
detail::MemberList<size> dstFields;
detail::Copier<size> copier(&dstFields);
std::ignore = model(&dstFields, dst);
std::ignore = model(&copier, src);
}
template<typename T>
[[nodiscard]]
constexpr bool equalsModel(T *a, T *b) noexcept {
constexpr auto size = T::Fields;
detail::MemberList<size> aFields;
detail::Equals<size> equals(&aFields);
std::ignore = model(&aFields, a);
std::ignore = model(&equals, b);
return equals.value;
}
}
File diff suppressed because it is too large Load Diff
+21
View File
@@ -0,0 +1,21 @@
/*
* 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/stringview.hpp>
namespace ox {
enum class OpType {
Read = 1,
Write,
Reflect,
};
}
@@ -0,0 +1,123 @@
/*
* 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/memory.hpp>
#include <ox/std/string.hpp>
#include <ox/std/types.hpp>
#include "fieldcounter.hpp"
#include "optype.hpp"
namespace ox {
struct TypeInfoCatcher {
CString name = "";
int version = 0;
template<typename T = std::nullptr_t>
constexpr Error setTypeInfo(
CString const n = T::TypeName,
int const v = T::TypeVersion,
Vector<String> const& = {},
size_t = 0) noexcept {
this->name = n;
this->version = v;
return {};
}
template<typename T>
constexpr Error field(CString, T*, size_t) noexcept {
return {};
}
template<typename T>
constexpr Error field(CString, T const&) noexcept {
return {};
}
template<typename T>
constexpr Error fieldCString(CString, T const&) noexcept {
return {};
}
static constexpr auto opType() noexcept {
return OpType::Reflect;
}
};
template<typename T>
[[nodiscard]]
constexpr int getModelTypeVersion(T *val) noexcept {
TypeInfoCatcher nc;
std::ignore = model(&nc, val);
return nc.version;
}
template<typename T>
[[nodiscard]]
constexpr int getModelTypeVersion() noexcept {
std::allocator<T> a;
const auto t = a.allocate(1);
const auto out = getModelTypeVersion(t);
a.deallocate(t, 1);
return out;
}
template<typename T>
[[nodiscard]]
consteval int requireModelTypeVersion() noexcept {
constexpr auto version = getModelTypeVersion<T>();
static_assert(version != 0, "TypeVersion is required");
return version;
}
template<typename T, typename Str = const char*>
[[nodiscard]]
constexpr Str getModelTypeName(T *val) noexcept {
TypeInfoCatcher nc;
std::ignore = model(&nc, val);
return nc.name;
}
template<typename T, typename Str = const char*>
[[nodiscard]]
consteval Str getModelTypeName() noexcept {
std::allocator<T> a;
auto t = a.allocate(1);
auto out = getModelTypeName(t);
a.deallocate(t, 1);
return out;
}
template<typename T, typename Str = StringLiteral>
[[nodiscard]]
consteval auto requireModelTypeName() noexcept {
constexpr auto name = getModelTypeName<T, Str>();
static_assert(StringView{name}.size(), "Type lacks required TypeName");
return name;
}
template<typename T, typename Str = StringLiteral>
constexpr auto ModelTypeName_v = requireModelTypeName<T, Str>();
template<typename T, typename Str = const char*>
constexpr auto ModelTypeVersion_v = requireModelTypeVersion<T>();
template<typename T>
constexpr auto ModelTypeId_v = [] {
constexpr auto name = ModelTypeName_v<T, StringView>;
constexpr auto version = ModelTypeVersion_v<T>;
constexpr auto versionStr = ox::sfmt<IString<19>>("{}", version);
return ox::sfmt<IString<name.size() + versionStr.size() + 1>>("{};{}", name, versionStr);
}();
}
+200
View File
@@ -0,0 +1,200 @@
/*
* 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
#if __has_include(<vector>)
#include <vector>
#endif
#if __has_include(<array>)
#include <array>
#endif
#if __has_include(<QVector>)
#include <QVector>
#endif
#include <ox/std/array.hpp>
#include <ox/std/istring.hpp>
#include <ox/std/string.hpp>
#include <ox/std/strops.hpp>
#include <ox/std/types.hpp>
#include <ox/std/typetraits.hpp>
#include <ox/std/vector.hpp>
namespace ox {
namespace types {
constexpr StringLiteral BasicString = "net.drinkingtea.ox.BasicString";
constexpr StringLiteral IString = "net.drinkingtea.ox.IString";
constexpr StringLiteral String = "B.string";
constexpr StringLiteral Bool = "B.bool";
constexpr StringLiteral Uint8 = "B.uint8";
constexpr StringLiteral Uint16 = "B.uint16";
constexpr StringLiteral Uint32 = "B.uint32";
constexpr StringLiteral Uint64 = "B.uint64";
constexpr StringLiteral Int8 = "B.int8";
constexpr StringLiteral Int16 = "B.int16";
constexpr StringLiteral Int32 = "B.int32";
constexpr StringLiteral Int64 = "B.int64";
}
template<typename T>
consteval bool isBasicString(const T*) noexcept {
return false;
}
template<std::size_t SmallVecSize>
consteval bool isBasicString(const BasicString<SmallVecSize>*) noexcept {
return true;
}
template<typename T>
constexpr bool isBasicString_v = isBasicString(static_cast<const T*>(nullptr));
static_assert(isBasicString_v<ox::BasicString<0ul>>);
static_assert(isBasicString_v<ox::BasicString<8ul>>);
static_assert(isBasicString_v<ox::String>);
template<typename T>
consteval bool isIString(const T*) noexcept {
return false;
}
template<std::size_t SmallVecSize>
consteval bool isIString(const BasicString<SmallVecSize>*) noexcept {
return true;
}
template<typename T>
constexpr bool isIString_v = isIString(static_cast<const T*>(nullptr));
static_assert(isBasicString_v<ox::BasicString<0ul>>);
static_assert(isBasicString_v<ox::BasicString<8ul>>);
static_assert(isBasicString_v<ox::String>);
template<typename T>
consteval bool isOxVector(const T*) noexcept {
return false;
}
template<typename T, std::size_t SmallVecSize>
consteval bool isOxVector(const Vector<T, SmallVecSize>*) noexcept {
return true;
}
template<typename T>
constexpr bool isOxVector_v = isVector(static_cast<const T*>(nullptr));
template<typename T>
consteval bool isVector(const T*) noexcept {
return false;
}
template<typename T, std::size_t SmallVecSize>
consteval bool isVector(const Vector<T, SmallVecSize>*) noexcept {
return true;
}
#if __has_include(<vector>)
template<typename T>
consteval bool isVector(const std::vector<T>*) noexcept {
return true;
}
#endif
#if __has_include(<QVector>)
template<typename T>
constexpr bool isVector(const QVector<T>*) noexcept {
return true;
}
#endif
template<typename T>
constexpr bool isVector_v = isVector(static_cast<const T*>(nullptr));
static_assert(isVector_v<ox::Vector<unsigned int, 0ul>>);
template<typename T>
constexpr bool isBareArray_v = false;
template<typename T>
constexpr bool isBareArray_v<T[]> = true;
template<typename T, std::size_t sz>
constexpr bool isBareArray_v<T[sz]> = true;
template<typename T>
consteval bool isArray(T*) noexcept {
return false;
}
template<typename T>
constexpr bool isArray_v = isArray(static_cast<T*>(nullptr));
template<typename T>
constexpr bool isArray_v<T[]> = true;
template<typename T, std::size_t sz>
constexpr bool isArray_v<T[sz]> = true;
template<typename T, std::size_t sz>
constexpr bool isArray_v<Array<T, sz>> = true;
template<typename T, std::size_t sz>
consteval bool isArray(T[sz]) noexcept {
return true;
}
template<typename T, std::size_t sz>
consteval bool isArray(Array<T, sz>) noexcept {
return true;
}
template<typename T>
constexpr bool isSmartPtr_v = false;
template<typename T>
constexpr bool isSmartPtr_v<::ox::UPtr<T>> = true;
#if __has_include(<array>)
template<typename T>
constexpr bool isSmartPtr_v<::std::unique_ptr<T>> = true;
#endif
template<typename Union, bool force = false> requires(force || is_union_v<Union>)
class UnionView {
protected:
int m_idx = -1;
Union *m_union = nullptr;
public:
constexpr UnionView(Union *u, int idx) noexcept: m_idx(idx), m_union(u) {
}
[[nodiscard]]
constexpr auto idx() const noexcept {
return m_idx;
}
[[nodiscard]]
constexpr const Union *get() const noexcept {
return m_union;
}
[[nodiscard]]
constexpr Union *get() noexcept {
return m_union;
}
};
}
+120
View File
@@ -0,0 +1,120 @@
/*
* 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/fmt.hpp>
#include <ox/std/hashmap.hpp>
#include <ox/std/memory.hpp>
#include <ox/std/string.hpp>
#include <ox/std/typetraits.hpp>
#include "typenamecatcher.hpp"
#include "desctypes.hpp"
namespace ox {
class TypeStore {
private:
HashMap<String, UPtr<DescriptorType>> m_cache;
public:
constexpr TypeStore() noexcept = default;
constexpr virtual ~TypeStore() noexcept = default;
constexpr Result<const DescriptorType*> get(const auto &name, int typeVersion,
const TypeParamPack &typeParams) const noexcept {
const auto typeId = buildTypeId(name, typeVersion, typeParams);
OX_REQUIRE(out, m_cache.at(typeId));
return out->get();
}
template<typename T>
constexpr Result<const DescriptorType*> get() const noexcept {
constexpr auto typeName = ModelTypeName_v<T>;
constexpr auto typeVersion = ModelTypeVersion_v<T>;
const auto typeId = buildTypeId(typeName, typeVersion, {});
OX_REQUIRE(out, m_cache.at(typeId));
return out->get();
}
constexpr DescriptorType *getInit(StringViewCR typeName, int typeVersion, PrimitiveType pt,
const TypeParamPack &typeParams) noexcept {
const auto typeId = buildTypeId(typeName, typeVersion, typeParams);
auto &out = m_cache[typeId];
out = ox::make_unique<DescriptorType>(String(typeName), typeVersion, pt, typeParams);
return out.get();
}
constexpr Result<const DescriptorType*> getLoad(const auto &typeId) noexcept {
auto [val, err] = m_cache.at(typeId);
if (err) {
if (!std::is_constant_evaluated()) {
OX_REQUIRE_M(dt, loadDescriptor(typeId));
for (auto &f : dt->fieldList) {
if (typeId == f.typeId) {
f.type = dt.get();
} else {
OX_RETURN_ERROR(this->getLoad(f.typeId).moveTo(f.type));
}
}
auto &out = m_cache[typeId];
out = std::move(dt);
return out.get();
} else {
return ox::Error(1, "Type not available");
}
}
return val->get();
}
constexpr Result<const DescriptorType*> getLoad(const auto &typeName, auto typeVersion,
const TypeParamPack &typeParams = {}) noexcept {
return getLoad(buildTypeId(typeName, typeVersion, typeParams));
}
template<typename T>
constexpr Result<const DescriptorType*> getLoad() noexcept {
constexpr auto typeName = requireModelTypeName<T>();
constexpr auto typeVersion = requireModelTypeVersion<T>();
return getLoad(typeName, typeVersion);
}
constexpr void set(const auto &typeId, UPtr<DescriptorType> dt) noexcept {
m_cache[typeId] = std::move(dt);
}
constexpr void set(const auto &typeId, DescriptorType *dt) noexcept {
m_cache[typeId] = UPtr<DescriptorType>(dt);
}
[[nodiscard]]
constexpr auto typeList() const noexcept {
const auto &keys = m_cache.keys();
ox::Vector<DescriptorType*> descs;
for (const auto &k : keys) {
descs.emplace_back(m_cache.at(k).unwrap()->get());
}
return descs;
}
protected:
virtual Result<UPtr<DescriptorType>> loadDescriptor(ox::StringView) noexcept {
return ox::Error(1);
}
Result<UPtr<DescriptorType>> loadDescriptor(ox::StringViewCR name, int version,
const ox::TypeParamPack &typeParams) noexcept {
const auto typeId = buildTypeId(name, version, typeParams);
return loadDescriptor(typeId);
}
};
}
+161
View File
@@ -0,0 +1,161 @@
/*
* 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 {};
}
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 {};
}
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);
}
}
+13
View File
@@ -0,0 +1,13 @@
/*
* 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/model/desctypes.hpp>
namespace ox {
}
+49
View File
@@ -0,0 +1,49 @@
/*
* 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/model/descwrite.hpp>
namespace ox {
namespace detail {
struct preloadable_type {
static constexpr auto Preloadable = true;
};
struct non_preloadable_type {
static constexpr auto Preloadable = false;
};
struct non_preloadable_type2 {
};
static_assert(preloadable<preloadable_type>::value);
static_assert(!preloadable<non_preloadable_type>::value);
static_assert(!preloadable<non_preloadable_type2>::value);
}
static_assert([] {
return detail::indirectionLevels_v<int> == 0;
}(), "indirectionLevels broken: indirectionLevels(int)");
static_assert([] {
return detail::indirectionLevels_v<int*> == 1;
}(), "indirectionLevels broken: indirectionLevels(int*)");
static_assert([] {
return detail::indirectionLevels_v<int[2]> == 1;
}(), "indirectionLevels broken: indirectionLevels(int[])");
static_assert([] {
return detail::indirectionLevels_v<int**> == 2;
}(), "indirectionLevels broken: indirectionLevels(int[])");
}
+28
View File
@@ -0,0 +1,28 @@
/*
* 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/hashmap.hpp>
#include <ox/model/modelvalue.hpp>
namespace ox {
static_assert([]() -> Error {
ModelValue v;
OX_RETURN_ERROR(v.setType<int32_t>());
if (v.type() != ModelValue::Type::SignedInteger32) {
return Error(1, "type is wrong");
}
//oxReturnError(v.set<int32_t>(5));
return {};
}() == Error{});
// a dummy function to prevent linker errors in a library that has no other symbols
void modelDummyFunc() noexcept {}
}
+13
View File
@@ -0,0 +1,13 @@
add_executable(
ModelTest
tests.cpp
)
target_link_libraries(
ModelTest
OxModel
)
add_test("[ox/model] ModelValue" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ModelTest ModelValue)
add_test("[ox/model] getModelTypeName" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ModelTest getModelTypeName)
add_test("[ox/model] getModelTypeVersion" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ModelTest getModelTypeVersion)
+82
View File
@@ -0,0 +1,82 @@
/*
* 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/std/std.hpp>
struct TestType {
static constexpr auto TypeName = "net.drinkingtea.model.test.TestType";
static constexpr auto TypeVersion = 1;
};
OX_MODEL_BEGIN(TestType)
OX_MODEL_END()
struct TestType2 {
};
template<typename Str = ox::StringView>
constexpr auto getModelTypeName(TestType2*) noexcept {
return "net.drinkingtea.model.test.TestType2";
}
constexpr auto getModelTypeVersion(TestType2*) noexcept {
return 2;
}
std::map<ox::StringView, ox::Error(*)()> tests = {
{
{
"ModelValue",
[] {
ox::ModelValue v;
OX_RETURN_ERROR(v.setType<int32_t>());
//v.m_type = ox::ModelValue::getType<int32_t>();
if (v.type() != ox::ModelValue::Type::SignedInteger32) {
return ox::Error(1, "type is wrong");
}
OX_RETURN_ERROR(v.set<int32_t>(5));
return ox::Error{};
}
},
{
"getModelTypeName",
[] {
oxAssert(ox::getModelTypeName<TestType>() == TestType::TypeName, "getModelTypeName call failed");
oxAssert(ox::getModelTypeName<TestType2>() == ox::StringView("net.drinkingtea.model.test.TestType2"), "getModelTypeName call failed");
return ox::Error{};
}
},
{
"getModelTypeVersion",
[] {
oxAssert(ox::getModelTypeVersion<TestType>() == TestType::TypeVersion, "getModelTypeVersion call failed");
oxAssert(ox::getModelTypeVersion<TestType2>() == 2, "getModelTypeVersion call failed");
return ox::Error{};
}
},
}
};
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;
}