Squashed 'deps/nostalgia/' content from commit 9cb6bd4a

git-subtree-dir: deps/nostalgia
git-subtree-split: 9cb6bd4a32e9f39a858f72443ff5c6d40489fe22
This commit is contained in:
2023-12-23 14:17:05 -06:00
commit 587dd92414
1112 changed files with 283489 additions and 0 deletions

1
deps/ox/src/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1 @@
add_subdirectory(ox)

27
deps/ox/src/ox/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,27 @@
if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
set(OX_OS_WINDOWS TRUE)
endif()
if(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
set(OX_OS_FREEBSD TRUE)
else()
set(OX_OS_FREEBSD FALSE)
endif()
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(OX_OS_LINUX TRUE)
else()
set(OX_OS_LINUX FALSE)
endif()
if(OX_USE_STDLIB)
add_subdirectory(oc)
endif()
add_subdirectory(clargs)
add_subdirectory(claw)
add_subdirectory(event)
add_subdirectory(fs)
add_subdirectory(logconn)
add_subdirectory(mc)
add_subdirectory(model)
add_subdirectory(preloader)
add_subdirectory(std)

37
deps/ox/src/ox/clargs/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,37 @@
cmake_minimum_required(VERSION 3.10)
add_library(
OxClArgs
clargs.cpp
)
set_property(
TARGET
OxClArgs
PROPERTY
POSITION_INDEPENDENT_CODE ON
)
if(NOT MSVC)
target_compile_options(OxClArgs PRIVATE -Wsign-conversion)
target_compile_options(OxClArgs PRIVATE -Wconversion)
endif()
target_link_libraries(
OxClArgs PUBLIC
OxStd
)
install(
FILES
clargs.hpp
DESTINATION
include/ox/clargs
)
install(
TARGETS
OxClArgs
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)

70
deps/ox/src/ox/clargs/clargs.cpp vendored Normal file
View File

@@ -0,0 +1,70 @@
/*
* Copyright 2015 - 2023 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <ox/std/string.hpp>
#include "clargs.hpp"
namespace ox {
ClArgs::ClArgs(int argc, const char **args) noexcept {
for (auto i = 0u; i < static_cast<unsigned>(argc); ++i) {
auto arg = String(args[i]);
if (arg[0] == '-') {
while (arg[0] == '-' && arg.len()) {
arg = arg.substr(1);
}
m_bools[arg] = true;
// parse additional arguments
if (i < static_cast<unsigned>(argc) && args[i + 1]) {
auto val = String(args[i + 1]);
if (val.len() && val[i] != '-') {
if (val == "false") {
m_bools[arg] = false;
}
m_strings[arg] = val;
if (auto r = ox_atoi(val.c_str()); r.error == 0) {
m_ints[arg] = r.value;
}
++i;
}
}
}
}
}
bool ClArgs::getBool(ox::CRStringView arg, bool defaultValue) const noexcept {
auto [value, err] = m_ints.at(arg);
return !err ? *value : defaultValue;
}
String ClArgs::getString(ox::CRStringView arg, const char *defaultValue) const noexcept {
auto [value, err] = m_strings.at(arg);
return !err ? ox::String(std::move(*value)) : ox::String(defaultValue);
}
int ClArgs::getInt(ox::CRStringView arg, int defaultValue) const noexcept {
auto [value, err] = m_ints.at(arg);
return !err ? *value : defaultValue;
}
Result<bool> ClArgs::getBool(ox::CRStringView arg) const noexcept {
oxRequire(out, m_bools.at(arg));
return *out;
}
Result<String> ClArgs::getString(ox::CRStringView argName) const noexcept {
oxRequire(out, m_strings.at(argName));
return *out;
}
Result<int> ClArgs::getInt(ox::CRStringView arg) const noexcept {
oxRequire(out, m_ints.at(arg));
return *out;
}
}

44
deps/ox/src/ox/clargs/clargs.hpp vendored Normal file
View File

@@ -0,0 +1,44 @@
/*
* Copyright 2015 - 2023 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <ox/std/hashmap.hpp>
#include <ox/std/string.hpp>
namespace ox {
class ClArgs {
private:
HashMap<String, bool> m_bools;
HashMap<String, String> m_strings;
HashMap<String, int> m_ints;
public:
ClArgs(int argc, const char **args) noexcept;
[[nodiscard]]
bool getBool(ox::CRStringView arg, bool defaultValue) const noexcept;
[[nodiscard]]
String getString(ox::CRStringView argName, const char *defaultValue) const noexcept;
[[nodiscard]]
int getInt(ox::CRStringView arg, int defaultValue) const noexcept;
[[nodiscard]]
Result<bool> getBool(ox::CRStringView arg) const noexcept;
[[nodiscard]]
Result<String> getString(ox::CRStringView argName) const noexcept;
Result<int> getInt(ox::CRStringView arg) const noexcept;
};
}

37
deps/ox/src/ox/claw/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,37 @@
add_library(
OxClaw
read.cpp
write.cpp
)
if(NOT MSVC)
target_compile_options(OxClaw PRIVATE -Wsign-conversion)
target_compile_options(OxClaw PRIVATE -Wconversion)
endif()
target_link_libraries(
OxClaw PUBLIC
OxMetalClaw
$<$<BOOL:${OX_USE_STDLIB}>:OxOrganicClaw>
)
#if(OX_USE_STDLIB)
# add_executable(
# readclaw
# readclaw.cpp
# )
# target_link_libraries(
# readclaw PUBLIC
# OxClaw
# )
#endif()
install(TARGETS OxClaw
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
if(OX_RUN_TESTS)
add_subdirectory(test)
endif()

12
deps/ox/src/ox/claw/claw.hpp vendored Normal file
View File

@@ -0,0 +1,12 @@
/*
* Copyright 2015 - 2022 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "read.hpp"
#include "write.hpp"

19
deps/ox/src/ox/claw/format.hpp vendored Normal file
View File

@@ -0,0 +1,19 @@
/*
* Copyright 2015 - 2022 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
namespace ox {
enum class ClawFormat: int {
None,
Metal,
Organic,
};
}

110
deps/ox/src/ox/claw/read.cpp vendored Normal file
View File

@@ -0,0 +1,110 @@
/*
* Copyright 2015 - 2023 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/buffer.hpp>
#include "read.hpp"
namespace ox {
Result<ClawHeader> readClawHeader(const char *buff, std::size_t buffLen) noexcept {
const auto s1End = ox_strchr(buff, ';', buffLen);
if (!s1End) {
return OxError(1, "Could not read Claw header");
}
const auto s1Size = static_cast<std::size_t>(s1End - buff);
const String fmt(buff, s1Size);
buff += s1Size + 1;
buffLen -= s1Size + 1;
const auto s2End = ox_strchr(buff, ';', buffLen);
if (!s2End) {
return OxError(2, "Could not read Claw header");
}
const auto s2Size = static_cast<std::size_t>(s2End - buff);
const String typeName(buff, s2Size);
buff += s2Size + 1;
buffLen -= s2Size + 1;
const auto s3End = ox_strchr(buff, ';', buffLen);
if (!s3End) {
return OxError(3, "Could not read Claw header");
}
const auto s3Size = static_cast<std::size_t>(s3End - buff);
const String versionStr(buff, s3Size);
buff += s3Size + 1;
buffLen -= s3Size + 1;
ClawHeader hdr;
if (fmt == "M2") {
hdr.fmt = ClawFormat::Metal;
} else if (fmt == "O1") {
hdr.fmt = ClawFormat::Organic;
} else {
return OxError(4, "Claw format does not match any supported format/version combo");
}
hdr.typeName = typeName;
if (auto r = ox_atoi(versionStr.c_str()); r.error == 0) {
hdr.typeVersion = r.value;
}
hdr.data = buff;
hdr.dataSize = buffLen;
return hdr;
}
Result<ClawHeader> readClawHeader(const ox::Buffer &buff) noexcept {
return readClawHeader(buff.data(), buff.size());
}
Result<Buffer> stripClawHeader(const char *buff, std::size_t buffLen) noexcept {
oxRequire(header, readClawHeader(buff, buffLen));
Buffer out(header.dataSize);
ox_memcpy(out.data(), header.data, out.size());
return out;
}
Result<Buffer> stripClawHeader(const ox::Buffer &buff) noexcept {
return stripClawHeader(buff.data(), buff.size());
}
Result<ModelObject> readClaw(TypeStore &ts, const char *buff, std::size_t buffSz) noexcept {
oxRequire(header, readClawHeader(buff, buffSz));
oxRequire(t, ts.getLoad(header.typeName, header.typeVersion, header.typeParams));
ModelObject obj;
oxReturnError(obj.setType(t));
switch (header.fmt) {
case ClawFormat::Metal:
{
ox::BufferReader br(header.data, header.dataSize);
MetalClawReader reader(br);
ModelHandlerInterface handler(&reader);
oxReturnError(model(&handler, &obj));
return obj;
}
case ClawFormat::Organic:
{
#ifdef OX_USE_STDLIB
OrganicClawReader reader(header.data, header.dataSize);
ModelHandlerInterface handler(&reader);
oxReturnError(model(&handler, &obj));
return obj;
#else
break;
#endif
}
case ClawFormat::None:
return OxError(1);
}
return OxError(1);
}
Result<ModelObject> readClaw(TypeStore &ts, const Buffer &buff) noexcept {
return readClaw(ts, buff.data(), buff.size());
}
}

95
deps/ox/src/ox/claw/read.hpp vendored Normal file
View File

@@ -0,0 +1,95 @@
/*
* Copyright 2015 - 2022 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/mc/read.hpp>
#ifdef OX_USE_STDLIB
#include <ox/oc/read.hpp>
#endif
#include <ox/std/buffer.hpp>
#include <ox/std/string.hpp>
#include "format.hpp"
namespace ox {
constexpr auto Error_ClawTypeMismatch = 200;
constexpr auto Error_ClawTypeVersionMismatch = 201;
struct ClawHeader {
String typeName;
int typeVersion = -1;
TypeParamPack typeParams;
ClawFormat fmt = ClawFormat::None;
const char *data = nullptr;
std::size_t dataSize = 0;
};
Result<ClawHeader> readClawHeader(const char *buff, std::size_t buffLen) noexcept;
Result<ClawHeader> readClawHeader(const ox::Buffer &buff) noexcept;
Result<Buffer> stripClawHeader(const char *buff, std::size_t buffLen) noexcept;
Result<Buffer> stripClawHeader(const ox::Buffer &buff) noexcept;
template<typename T>
Error readClaw(const char *buff, std::size_t buffLen, T *val) {
oxRequire(header, readClawHeader(buff, buffLen));
if (header.typeName != getModelTypeName<T>()) {
return OxError(Error_ClawTypeMismatch, "Claw Read: Type mismatch");
}
if (header.typeVersion != getModelTypeVersion<T>()) {
return OxError(Error_ClawTypeVersionMismatch, "Claw Read: Type Version mismatch");
}
switch (header.fmt) {
case ClawFormat::Metal:
{
ox::BufferReader br(header.data, header.dataSize);
MetalClawReader reader(br);
ModelHandlerInterface handler(&reader);
return model(&handler, val);
}
case ClawFormat::Organic:
{
#ifdef OX_USE_STDLIB
OrganicClawReader reader(header.data, header.dataSize);
return model(&reader, val);
#else
break;
#endif
}
case ClawFormat::None:
return OxError(1);
}
return OxError(1);
}
template<typename T>
Result<T> readClaw(const char *buff, std::size_t buffLen) {
T val;
oxReturnError(readClaw(buff, buffLen, &val));
return val;
}
template<typename T>
Error readClaw(const Buffer &buff, T *val) {
return readClaw<T>(buff.data(), buff.size(), val);
}
template<typename T>
Result<T> readClaw(const Buffer &buff) {
return readClaw<T>(buff.data(), buff.size());
}
Result<ModelObject> readClaw(TypeStore &ts, const char *buff, std::size_t buffSz) noexcept;
Result<ModelObject> readClaw(TypeStore &ts, const Buffer &buff) noexcept;
}

30
deps/ox/src/ox/claw/readclaw.cpp vendored Normal file
View File

@@ -0,0 +1,30 @@
/*
* Copyright 2015 - 2023 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <cstdio>
#include <ox/std/reader.hpp>
#include <ox/std/trace.hpp>
#include "claw.hpp"
int main(int argc, char **args) {
if (argc > 2) {
oxErr("Too many arguments");
return -1;
}
auto const file = argc == 1 ? stdin : fopen(args[1], "r");
if (fseek(file, 0, SEEK_END)) {
oxErr("Could not get file size\n");
return -2;
}
auto const size = static_cast<std::size_t>(ftell(file));
oxDebugf("{}", size);
return 0;
}

14
deps/ox/src/ox/claw/test/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,14 @@
add_executable(
ClawTest
tests.cpp
)
target_link_libraries(
ClawTest
OxClaw
)
add_test("[ox/claw] ClawHeaderReader" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ClawTest ClawHeaderReader)
add_test("[ox/claw] ClawHeaderReader2" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ClawTest ClawHeaderReader2)
add_test("[ox/claw] ClawWriter" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ClawTest ClawWriter)
add_test("[ox/claw] ClawReader" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ClawTest ClawReader)

201
deps/ox/src/ox/claw/test/tests.cpp vendored Normal file
View File

@@ -0,0 +1,201 @@
/*
* Copyright 2015 - 2023 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/claw/format.hpp>
#include <ox/claw/read.hpp>
#include <ox/claw/write.hpp>
#include <ox/mc/mc.hpp>
#include <ox/model/model.hpp>
#include <ox/std/std.hpp>
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::BString<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::BString<32> String = "";
uint32_t List[4] = {0, 0, 0, 0};
TestStructNest EmptyStruct;
TestStructNest Struct;
~TestStruct() {
if (unionIdx == 2) {
ox::safeDelete(Union.String);
}
}
};
template<typename T>
constexpr ox::Error model(T *io, ox::CommonPtrWith<TestUnion> auto *obj) {
oxReturnError(io->template setTypeInfo<TestUnion>());
oxReturnError(io->field("Bool", &obj->Bool));
oxReturnError(io->field("Int", &obj->Int));
oxReturnError(io->fieldCString("String", &obj->String));
return OxError(0);
}
template<typename T>
constexpr ox::Error model(T *io, ox::CommonPtrWith<TestStructNest> auto *obj) {
oxReturnError(io->template setTypeInfo<TestStructNest>());
oxReturnError(io->field("Bool", &obj->Bool));
oxReturnError(io->field("Int", &obj->Int));
oxReturnError(io->field("String", &obj->String));
return OxError(0);
}
template<typename T>
constexpr ox::Error model(T *io, ox::CommonPtrWith<TestStruct> auto *obj) {
oxReturnError(io->template setTypeInfo<TestStruct>());
oxReturnError(io->field("Bool", &obj->Bool));
oxReturnError(io->field("Int", &obj->Int));
oxReturnError(io->field("Int1", &obj->Int1));
oxReturnError(io->field("Int2", &obj->Int2));
oxReturnError(io->field("Int3", &obj->Int3));
oxReturnError(io->field("Int4", &obj->Int4));
oxReturnError(io->field("Int5", &obj->Int5));
oxReturnError(io->field("Int6", &obj->Int6));
oxReturnError(io->field("Int7", &obj->Int7));
oxReturnError(io->field("Int8", &obj->Int8));
int unionIdx = 0;
if constexpr(T::opType() != ox::OpType::Reflect) {
unionIdx = obj->unionIdx;
}
oxReturnError(io->field("Union", ox::UnionView{&obj->Union, unionIdx}));
oxReturnError(io->field("String", &obj->String));
oxReturnError(io->field("List", obj->List, 4));
oxReturnError(io->field("EmptyStruct", &obj->EmptyStruct));
oxReturnError(io->field("Struct", &obj->Struct));
return OxError(0);
}
static std::map<ox::StringView, ox::Error(*)()> tests = {
{
{
"ClawHeaderReader",
[] {
constexpr auto hdr = ox::StringLiteral("O1;com.drinkingtea.ox.claw.test.Header;2;");
auto [ch, err] = ox::readClawHeader(hdr.c_str(), hdr.len() + 1);
oxAssert(err, "Error parsing header");
oxAssert(ch.fmt == ox::ClawFormat::Organic, "Format wrong");
oxAssert(ch.typeName == "com.drinkingtea.ox.claw.test.Header", "Type name wrong");
oxAssert(ch.typeVersion == 2, "Type version wrong");
return OxError(0);
}
},
{
"ClawHeaderReader2",
[] {
constexpr auto hdr = ox::StringLiteral("M2;com.drinkingtea.ox.claw.test.Header2;3;");
auto [ch, err] = ox::readClawHeader(hdr.c_str(), hdr.len() + 1);
oxAssert(err, "Error parsing header");
oxAssert(ch.fmt == ox::ClawFormat::Metal, "Format wrong");
oxAssert(ch.typeName == "com.drinkingtea.ox.claw.test.Header2", "Type name wrong");
oxAssert(ch.typeVersion == 3, "Type version wrong");
return OxError(0);
}
},
{
"ClawWriter",
[] {
// This test doesn't confirm much, but it does show that the writer
// doesn't segfault
TestStruct ts;
oxReturnError(ox::writeClaw(ts, ox::ClawFormat::Metal));
return OxError(0);
}
},
{
"ClawReader",
[] {
TestStruct testIn, testOut;
testIn.Bool = true;
testIn.Int = 42;
testIn.Union.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";
const auto [buff, err] = ox::writeMC(testIn);
oxAssert(err, "writeClaw failed");
oxAssert(ox::readMC(buff.data(), buff.size(), &testOut), "readClaw failed");
//std::cout << testIn.Union.Int << "|" << testOut.Union.Int << "|\n";
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.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 OxError(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;
}

23
deps/ox/src/ox/claw/write.cpp vendored Normal file
View File

@@ -0,0 +1,23 @@
/*
* Copyright 2015 - 2022 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include "write.hpp"
namespace ox::detail {
struct versioned_type {
static constexpr int TypeVersion = 4;
};
struct unversioned_type {
};
static_assert(type_version<versioned_type>::value == 4);
static_assert(type_version<unversioned_type>::value == -1);
}

123
deps/ox/src/ox/claw/write.hpp vendored Normal file
View File

@@ -0,0 +1,123 @@
/*
* Copyright 2015 - 2023 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <ox/mc/write.hpp>
#ifdef OX_USE_STDLIB
#include <ox/oc/write.hpp>
#endif
#include <ox/std/buffer.hpp>
#include <ox/std/string.hpp>
#include <ox/std/stringview.hpp>
#include "format.hpp"
namespace ox {
namespace detail {
struct TypeInfoCatcher {
const char *name = nullptr;
int version = 0;
template<typename T>
constexpr ox::Error setTypeInfo(
const char *pName = T::TypeName,
int pVersion = T::TypeVersion,
const Vector<String>& = {},
std::size_t = 0) noexcept {
this->name = pName;
this->version = pVersion;
return {};
}
constexpr Error field(...) noexcept {
return OxError(0);
}
static constexpr auto opType() {
return OpType::Write;
}
};
template<typename T, typename = int>
struct type_version {
static constexpr auto value = -1;
};
template<typename T>
struct type_version<T, decltype((void) T::TypeVersion, -1)> {
static constexpr auto value = T::TypeVersion;
};
template<typename T>
constexpr const char *getTypeName(const T *t) noexcept {
TypeInfoCatcher tnc;
oxIgnoreError(model(&tnc, t));
return tnc.name;
}
template<typename T>
constexpr int getTypeVersion(const T *t) noexcept {
TypeInfoCatcher tnc;
oxIgnoreError(model(&tnc, t));
return tnc.version;
}
template<typename T>
ox::Error writeClawHeader(Writer_c auto &writer, const T *t, ClawFormat fmt) noexcept {
switch (fmt) {
case ClawFormat::Metal:
oxReturnError(write(writer, "M2;"));
break;
case ClawFormat::Organic:
oxReturnError(write(writer, "O1;"));
break;
default:
return OxError(1);
}
oxReturnError(write(writer, detail::getTypeName(t)));
oxReturnError(writer.put(';'));
const auto tn = detail::getTypeVersion(t);
if (tn > -1) {
oxReturnError(ox::itoa(tn, writer));
}
oxReturnError(writer.put(';'));
return {};
}
}
Result<Buffer> writeClaw(
const auto &t,
ClawFormat fmt = ClawFormat::Metal,
std::size_t buffReserveSz = 2 * units::KB) noexcept {
Buffer out(buffReserveSz);
BufferWriter bw(&out, 0);
oxReturnError(detail::writeClawHeader(bw, &t, fmt));
#ifdef OX_USE_STDLIB
if (fmt == ClawFormat::Metal) {
oxReturnError(writeMC(bw, t));
} else if (fmt == ClawFormat::Organic) {
oxRequire(data, writeOC(t));
oxReturnError(bw.write(data.data(), data.size()));
}
#else
if (fmt != ClawFormat::Metal) {
return OxError(1, "OC is not supported in this build");
}
oxReturnError(writeMC(bw, t));
#endif
out.resize(bw.tellp());
return out;
}
}

46
deps/ox/src/ox/event/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,46 @@
add_library(
OxEvent
signal.cpp
)
if(NOT MSVC)
target_compile_options(OxEvent PRIVATE -Wsign-conversion)
target_compile_options(OxEvent PRIVATE -Wconversion)
endif()
if(NOT OX_BARE_METAL)
set_property(
TARGET
OxEvent
PROPERTY
POSITION_INDEPENDENT_CODE ON
)
endif()
target_compile_definitions(
OxEvent PUBLIC
$<$<BOOL:${OX_USE_STDLIB}>:OX_USE_STDLIB>
$<$<BOOL:${OX_NODEBUG}>:OX_NODEBUG>
)
target_link_libraries(
OxEvent PUBLIC
OxStd
)
install(
FILES
event.hpp
signal.hpp
DESTINATION
include/ox/event
)
install(TARGETS OxEvent
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
if(OX_RUN_TESTS)
add_subdirectory(test)
endif()

11
deps/ox/src/ox/event/event.hpp vendored Normal file
View File

@@ -0,0 +1,11 @@
/*
* Copyright 2015 - 2022 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "signal.hpp"

28
deps/ox/src/ox/event/signal.cpp vendored Normal file
View File

@@ -0,0 +1,28 @@
/*
* Copyright 2015 - 2022 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include "signal.hpp"
namespace ox {
#ifndef OX_OS_BareMetal
template class Signal<const SignalHandler*>;
#endif
template class Signal<Error(const SignalHandler*)>;
SignalHandler::~SignalHandler() noexcept {
destruction.emit(this);
}
}
/*
* 1. ProjectExplorer::fileOpened cannot notify StudioUI that it is being destroyed.
* 2. StudioUI tries to unsubscribe from ProjectExplorer::fileOpened upon its destruction.
* 3. Segfault
*/

406
deps/ox/src/ox/event/signal.hpp vendored Normal file
View File

@@ -0,0 +1,406 @@
/*
* Copyright 2015 - 2022 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <ox/std/assert.hpp>
#include <ox/std/def.hpp>
#include <ox/std/defines.hpp>
#include <ox/std/error.hpp>
#include <ox/std/memory.hpp>
#include <ox/std/trace.hpp>
#include <ox/std/vector.hpp>
namespace ox {
class SignalHandler;
#ifndef OX_OS_BareMetal
namespace detail {
template<typename T>
struct isError {
static constexpr bool value = false;
};
template<>
struct isError<Error> {
static constexpr bool value = true;
};
}
template<class... Args>
class Signal {
protected:
struct BaseSlot {
virtual ~BaseSlot() = default;
virtual void call(Args...) = 0;
virtual void cleanup(Signal*) noexcept {}
[[nodiscard]]
virtual const void *receiver() const noexcept { return nullptr; }
};
template<typename F>
struct FunctionSlot: public BaseSlot {
F f;
explicit FunctionSlot(F f) {
this->f = f;
}
void call(Args... args) final {
if constexpr(detail::isError<decltype(f(args...))>::value) {
oxThrowError(f(args...));
} else {
f(args...);
}
}
};
template<typename T, typename Method>
struct MethodSlot: public BaseSlot {
T m_receiver = nullptr;
Method m_methodPtr;
MethodSlot(T receiver, Method methodPtr) {
m_receiver = receiver;
m_methodPtr = methodPtr;
}
void call(Args... args) final {
if constexpr(detail::isError<decltype((m_receiver->*(m_methodPtr))(args...))>::value) {
oxThrowError((m_receiver->*(m_methodPtr))(args...));
} else {
f(args...);
}
}
void cleanup(Signal *signal) noexcept final {
auto err = m_receiver->destruction.disconnectSignal(signal);
if (err) {
oxErrorf("Signal could not notify receiver that it is being destroyed. Destruction of receiver will cause use-after-free. ({})", toStr(err));
}
}
[[nodiscard]]
const void *receiver() const noexcept final {
return m_receiver;
}
};
template<typename SignalT, typename Method>
struct SignalMethodSlot: public BaseSlot {
const SignalT *m_receiver = nullptr;
Method m_methodPtr;
SignalMethodSlot(const SignalT *receiver, Method methodPtr) {
m_receiver = receiver;
m_methodPtr = methodPtr;
}
void call(Args... args) final {
if constexpr(detail::isError<decltype((m_receiver->*(m_methodPtr))(args...))>::value) {
oxThrowError((m_receiver->*(m_methodPtr))(args...));
} else {
(m_receiver->*(m_methodPtr))(args...);
}
}
void cleanup(Signal*) noexcept final {
}
[[nodiscard]]
const void *receiver() const noexcept final {
return m_receiver;
}
};
mutable Vector<UniquePtr<BaseSlot>> m_slots;
public:
~Signal() noexcept;
void connect(Error(*f)(Args...)) const noexcept;
template<typename T, typename Method>
void connect(const T *receiver, Method methodPtr) const noexcept;
template<typename T, typename Method>
void connect(T *receiver, Method methodPtr) const noexcept;
template<class... SubArgs, typename Method>
void connect(const Signal<SubArgs...> *receiver, Method methodPtr) const noexcept;
template<class... SubArgs>
Error disconnectSignal(const Signal<SubArgs...> *receiver) const noexcept;
Error disconnectObject(const void *receiver) const noexcept;
void emit(Args... args) const;
Error emitCheckError(Args... args) const noexcept;
};
extern template class Signal<const SignalHandler*>;
template<class... Args>
Signal<Args...>::~Signal() noexcept {
for (auto &slot : m_slots) {
slot->cleanup(this);
}
}
template<class... Args>
void Signal<Args...>::connect(Error(*f)(Args...)) const noexcept {
m_slots.emplace_back(new FunctionSlot<decltype(f)>(f));
}
template<class... Args>
template<typename T, typename Method>
void Signal<Args...>::connect(const T *receiver, Method methodPtr) const noexcept {
receiver->destruction.connect(this, &Signal<Error(Args...)>::disconnectObject);
m_slots.emplace_back(new MethodSlot<const T*, Method>(receiver, methodPtr));
}
template<class... Args>
template<typename T, typename Method>
void Signal<Args...>::connect(T *receiver, Method methodPtr) const noexcept {
receiver->destruction.connect(this, &Signal<Error(Args...)>::disconnectObject);
m_slots.emplace_back(new MethodSlot<T*, Method>(receiver, methodPtr));
}
template<class... Args>
template<class... SubArgs, typename Method>
void Signal<Args...>::connect(const Signal<SubArgs...> *receiver, Method methodPtr) const noexcept {
m_slots.emplace_back(new SignalMethodSlot<Signal<SubArgs...>, Method>(receiver, methodPtr));
}
template<class... Args>
template<class... SubArgs>
Error Signal<Args...>::disconnectSignal(const Signal<SubArgs...> *receiver) const noexcept {
return disconnectObject(receiver);
}
template<class... Args>
Error Signal<Args...>::disconnectObject(const void *receiver) const noexcept {
for (auto i = 0u; i < m_slots.size(); ++i) {
const auto &slot = m_slots[i];
if (slot->receiver() == receiver) {
oxReturnError(m_slots.erase(i));
--i;
}
}
return OxError(1, "Signal::disconnectObject: Receiver was not found among this Signal's slots");
}
template<class... Args>
void Signal<Args...>::emit(Args... args) const {
for (auto &f : m_slots) {
f->call(args...);
}
}
template<class... Args>
Error Signal<Args...>::emitCheckError(Args... args) const noexcept {
try {
for (auto &f : m_slots) {
f->call(args...);
}
return OxError(0);
} catch (const ox::Exception &ex) {
return ox::Error(ex.file, ex.line, ex.errCode, ex.msg);
}
}
#else
template<typename T>
class Signal;
#endif
template<class... Args>
class Signal<Error(Args...)> {
protected:
struct BaseSlot {
virtual ~BaseSlot() = default;
virtual Error call(Args...) noexcept = 0;
virtual void cleanup(Signal*) noexcept {}
[[nodiscard]]
virtual const void *receiver() const noexcept { return nullptr; }
};
struct FunctionSlot: public BaseSlot {
Error (*f)(Args...);
explicit FunctionSlot(Error (*f)(Args...)) noexcept {
this->f = f;
}
Error call(Args... args) noexcept final {
return f(ox::forward<Args>(args)...);
}
};
template<typename T, typename Method>
struct MethodSlot: public BaseSlot {
T m_receiver = nullptr;
Method m_methodPtr;
MethodSlot(T receiver, Method methodPtr) {
m_receiver = receiver;
m_methodPtr = methodPtr;
}
Error call(Args... args) noexcept final {
return (m_receiver->*(m_methodPtr))(ox::forward<Args>(args)...);
}
void cleanup(Signal *signal) noexcept final {
auto err = m_receiver->destruction.disconnectSignal(signal);
oxErrorf("{}", toStr(err));
//oxAssert(err, "Signal could not notify receiver that it is being destroyed. Destruction of receiver will cause use-after-free.");
}
[[nodiscard]]
const void *receiver() const noexcept final {
return m_receiver;
}
};
template<typename SignalT, typename Method>
struct SignalMethodSlot: public BaseSlot {
const SignalT *m_receiver = nullptr;
Method m_methodPtr;
SignalMethodSlot(const SignalT *receiver, Method methodPtr) {
m_receiver = receiver;
m_methodPtr = methodPtr;
}
Error call(Args... args) noexcept final {
return (m_receiver->*(m_methodPtr))(ox::forward<Args>(args)...);
}
void cleanup(Signal*) noexcept final {
}
[[nodiscard]]
const void *receiver() const noexcept final {
return m_receiver;
}
};
mutable Vector<UniquePtr<BaseSlot>> m_slots;
public:
~Signal() noexcept;
void connect(Error(*f)(Args...)) const noexcept;
template<typename T, typename Method>
void connect(const T *receiver, Method methodPtr) const noexcept;
template<typename T, typename Method>
void connect(T *receiver, Method methodPtr) const noexcept;
template<class... SubArgs, typename Method>
void connect(const Signal<SubArgs...> *receiver, Method methodPtr) const noexcept;
template<class... SubArgs>
Error disconnectSignal(const Signal<SubArgs...> *receiver) const noexcept;
Error disconnectObject(const void *receiver) const noexcept;
void emit(Args... args) const noexcept;
Error emitCheckError(Args... args) const noexcept;
};
extern template class Signal<Error(const SignalHandler*)>;
class SignalHandler {
public:
Signal<Error(const SignalHandler*)> destruction;
constexpr SignalHandler() noexcept = default;
SignalHandler(const SignalHandler&) = delete;
SignalHandler(SignalHandler&) = delete;
SignalHandler(SignalHandler&&) = delete;
virtual ~SignalHandler() noexcept;
};
template<class... Args>
Signal<Error(Args...)>::~Signal() noexcept {
for (auto &slot : m_slots) {
slot->cleanup(this);
}
}
template<class... Args>
void Signal<Error(Args...)>::connect(Error(*f)(Args...)) const noexcept {
m_slots.emplace_back(new FunctionSlot(f));
}
template<class... Args>
template<typename T, typename Method>
void Signal<Error(Args...)>::connect(const T *receiver, Method methodPtr) const noexcept {
receiver->destruction.connect(this, &Signal<Error(Args...)>::disconnectObject);
m_slots.emplace_back(new MethodSlot<const T*, Method>(receiver, methodPtr));
}
template<class... Args>
template<typename T, typename Method>
void Signal<Error(Args...)>::connect(T *receiver, Method methodPtr) const noexcept {
receiver->destruction.connect(this, &Signal<Error(Args...)>::disconnectObject);
m_slots.emplace_back(new MethodSlot<T*, Method>(receiver, methodPtr));
}
template<class... Args>
template<class... SubArgs, typename Method>
void Signal<Error(Args...)>::connect(const Signal<SubArgs...> *receiver, Method methodPtr) const noexcept {
m_slots.emplace_back(new SignalMethodSlot<Signal<SubArgs...>, Method>(receiver, methodPtr));
}
template<class... Args>
template<class... SubArgs>
Error Signal<Error(Args...)>::disconnectSignal(const Signal<SubArgs...> *receiver) const noexcept {
return disconnectObject(receiver);
}
template<class... Args>
Error Signal<Error(Args...)>::disconnectObject(const void *receiver) const noexcept {
for (auto i = 0u; i < m_slots.size(); ++i) {
const auto &slot = m_slots[i];
if (slot->receiver() == receiver) {
oxReturnError(m_slots.erase(i));
--i;
}
}
return OxError(1, "Signal::disconnectObject: Receiver was not found among this Signal's slots");
}
template<class... Args>
void Signal<Error(Args...)>::emit(Args... args) const noexcept {
for (auto &f : m_slots) {
oxIgnoreError(f->call(ox::forward<Args>(args)...));
}
}
template<class... Args>
Error Signal<Error(Args...)>::emitCheckError(Args... args) const noexcept {
for (auto &f : m_slots) {
oxReturnError(f->call(ox::forward<Args>(args)...));
}
return OxError(0);
}
}

View File

@@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 3.10)
add_executable(
EventTest
tests.cpp
)
target_link_libraries(EventTest OxEvent)
add_test("[ox/event] Test 1" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/EventTest "test1")

52
deps/ox/src/ox/event/test/tests.cpp vendored Normal file
View File

@@ -0,0 +1,52 @@
/*
* Copyright 2015 - 2022 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#undef NDEBUG
#include <map>
#include <functional>
#include <ox/event/signal.hpp>
#include <ox/std/std.hpp>
struct TestStruct: public ox::SignalHandler {
int value = 0;
ox::Error method(int i) noexcept {
value = i;
return OxError(0);
}
};
std::map<ox::StringView, std::function<ox::Error()>> tests = {
{
"test1",
[] {
ox::Signal<ox::Error(int)> signal;
signal.connect([](int i) -> ox::Error {
return OxError(i != 5);
});
TestStruct ts;
signal.connect(&ts, &TestStruct::method);
oxReturnError(signal.emitCheckError(5));
oxReturnError(OxError(ts.value != 5));
return OxError(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;
}

89
deps/ox/src/ox/fs/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,89 @@
add_library(
OxFS
ptrarith/nodebuffer.hpp
ptrarith/ptr.hpp
filestore/filestoretemplate.cpp
filesystem/filelocation.cpp
filesystem/pathiterator.cpp
filesystem/directory.cpp
filesystem/filesystem.cpp
filesystem/passthroughfs.cpp
)
if(NOT MSVC)
target_compile_options(OxFS PRIVATE -Wsign-conversion)
endif()
if(NOT OX_BARE_METAL)
if(NOT APPLE AND NOT MSVC AND NOT ${OX_OS_FREEBSD})
target_link_libraries(
OxFS PUBLIC
stdc++fs
)
endif()
set_property(
TARGET
OxFS
PROPERTY
POSITION_INDEPENDENT_CODE ON
)
endif()
target_link_libraries(
OxFS PUBLIC
OxMetalClaw
)
if(NOT OX_BARE_METAL)
add_executable(
oxfs-tool
tool.cpp
)
target_link_libraries(
oxfs-tool
OxFS
)
install(
TARGETS
oxfs-tool
DESTINATION
bin
)
endif()
install(
FILES
filestore/filestoretemplate.hpp
DESTINATION
include/ox/fs/filestore
)
install(
FILES
filesystem/filesystem.hpp
filesystem/pathiterator.hpp
DESTINATION
include/ox/fs/filesystem
)
install(
FILES
ptrarith/nodebuffer.hpp
ptrarith/ptr.hpp
DESTINATION
include/ox/fs/ptrarith
)
install(
TARGETS
OxFS
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
if(OX_RUN_TESTS)
add_subdirectory(test)
endif()

View File

@@ -0,0 +1,16 @@
/*
* Copyright 2015 - 2022 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include "filestoretemplate.hpp"
namespace ox {
template class FileStoreTemplate<uint16_t>;
template class FileStoreTemplate<uint32_t>;
}

View File

@@ -0,0 +1,773 @@
/*
* Copyright 2015 - 2023 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <ox/fs/ptrarith/nodebuffer.hpp>
namespace ox {
using InodeId_t = uint64_t;
using FsSize_t = std::size_t;
struct StatInfo {
InodeId_t inode = 0;
InodeId_t links = 0;
FsSize_t size = 0;
uint8_t fileType = 0;
};
template<typename size_t>
struct OX_PACKED FileStoreItem: public ptrarith::Item<size_t> {
LittleEndian<size_t> id = 0;
LittleEndian<uint8_t> fileType = 0;
LittleEndian<size_t> links = 0;
LittleEndian<size_t> left = 0;
LittleEndian<size_t> right = 0;
FileStoreItem() = default;
explicit FileStoreItem(size_t size) {
this->setSize(size);
}
/**
* @return the size of the data + the size of the Item type
*/
[[nodiscard]]
size_t fullSize() const {
return sizeof(*this) + this->size();
}
ptrarith::Ptr<uint8_t, std::size_t> data() {
return ptrarith::Ptr<uint8_t, std::size_t>(this, this->fullSize(), sizeof(*this), this->size());
}
};
template<typename size_t>
class FileStoreTemplate {
public:
using InodeId_t = size_t;
private:
using Item = FileStoreItem<size_t>;
using ItemPtr = typename ptrarith::NodeBuffer<size_t, FileStoreItem<size_t>>::ItemPtr;
using Buffer = ptrarith::NodeBuffer<size_t, FileStoreItem<size_t>>;
static constexpr InodeId_t ReservedInodeEnd = 100;
struct OX_PACKED FileStoreData {
LittleEndian<size_t> rootNode = 0;
Random random;
};
size_t m_buffSize = 0;
mutable Buffer *m_buffer = nullptr;
public:
FileStoreTemplate() = default;
FileStoreTemplate(void *buff, std::size_t buffSize);
static Error format(void *buffer, std::size_t bufferSize);
Error setSize(std::size_t buffSize);
Error incLinks(uint64_t id);
Error decLinks(uint64_t id);
Error write(uint64_t id64, const void *data, FsSize_t dataSize, uint8_t fileType = 0);
Error remove(uint64_t id);
Error read(uint64_t id, void *out, FsSize_t outSize, FsSize_t *size = nullptr) const;
Error read(uint64_t id, FsSize_t readStart, FsSize_t readSize, void *data, FsSize_t *size = nullptr) const;
ptrarith::Ptr<uint8_t, std::size_t> read(uint64_t id) const;
/**
* Reads the "file" at the given id. You are responsible for freeing
* the data when done with it.
* @param id id of the "file"
* @param readStart where in the data to start reading
* @param readSize how much data to read
* @param data pointer to the pointer where the data is stored
* @param size pointer to a value that will be assigned the size of data
* @return 0 if read is a success
*/
template<typename T>
Error read(uint64_t id, FsSize_t readStart,
FsSize_t readSize, T *data,
FsSize_t *size) const;
Result<StatInfo> stat(uint64_t id) const;
Error resize();
Error resize(std::size_t size, void *newBuff = nullptr);
[[nodiscard]]
InodeId_t spaceNeeded(FsSize_t size) const;
[[nodiscard]]
InodeId_t size() const;
[[nodiscard]]
InodeId_t available() const;
[[nodiscard]]
char *buff();
Error walk(Error(*cb)(uint8_t, uint64_t, uint64_t));
Result<InodeId_t> generateInodeId();
bool valid() const;
Error compact();
private:
FileStoreData *fileStoreData() const;
/**
* Places the given Item at the given ID. If it already exists, the
* existing value will be overwritten.
*/
Error placeItem(ItemPtr item);
/**
* Places the given Item at the given ID. If it already exists, the
* existing value will be overwritten.
*/
Error placeItem(ItemPtr root, ItemPtr item, int depth = 0);
/**
* Removes the given Item at the given ID. If it already exists, the
* existing value will be overwritten.
*/
Error unplaceItem(ItemPtr item);
/**
* Removes the given Item at the given ID. If it already exists, the
* existing value will be overwritten.
*/
Error unplaceItem(ItemPtr root, ItemPtr item, int depth = 0);
Error remove(ItemPtr item);
/**
* Finds the parent an inode by its ID.
*/
ItemPtr findParent(ItemPtr item, size_t id, size_t oldAddr) const;
/**
* Finds an inode by its ID.
*/
ItemPtr find(ItemPtr item, InodeId_t id, int depth = 0) const;
/**
* Finds an inode by its ID.
*/
ItemPtr find(InodeId_t id) const;
/**
* Gets the root inode.
*/
ItemPtr rootInode();
bool canWrite(ItemPtr existing, std::size_t size);
};
template<typename size_t>
FileStoreTemplate<size_t>::FileStoreTemplate(void *buff, std::size_t buffSize) {
m_buffSize = static_cast<size_t>(buffSize);
m_buffer = reinterpret_cast<ptrarith::NodeBuffer<size_t, FileStoreItem<size_t>>*>(buff);
if (!m_buffer->valid(m_buffSize)) {
m_buffSize = 0;
m_buffer = nullptr;
}
}
template<typename size_t>
Error FileStoreTemplate<size_t>::format(void *buffer, std::size_t bufferSize) {
auto nb = new (buffer) Buffer(static_cast<size_t>(bufferSize));
auto fsData = nb->malloc(sizeof(FileStoreData)).value;
if (!fsData.valid()) {
oxTrace("ox.fs.FileStoreTemplate.format.fail", "Could not read data section of FileStoreData");
return OxError(1, "Could not read data section of FileStoreData");
}
auto data = nb->template dataOf<FileStoreData>(fsData);
if (!data.valid()) {
oxTrace("ox.fs.FileStoreTemplate.format.fail", "Could not read data section of FileStoreData");
return OxError(1, "Could not read data section of FileStoreData");
}
new (data) FileStoreData;
return {};
}
template<typename size_t>
Error FileStoreTemplate<size_t>::setSize(std::size_t size) {
if (m_buffSize >= size) {
return m_buffer->setSize(static_cast<size_t>(size));
}
return OxError(1);
}
template<typename size_t>
Error FileStoreTemplate<size_t>::incLinks(uint64_t id) {
oxRequireM(item, find(static_cast<size_t>(id)).validate());
++item->links;
return OxError(0);
}
template<typename size_t>
Error FileStoreTemplate<size_t>::decLinks(uint64_t id) {
oxRequireM(item, find(static_cast<size_t>(id)).validate());
--item->links;
if (item->links == 0) {
oxReturnError(remove(item));
}
return OxError(0);
}
template<typename size_t>
Error FileStoreTemplate<size_t>::write(uint64_t id64, const void *data, FsSize_t dataSize, uint8_t fileType) {
const auto id = static_cast<size_t>(id64);
oxTracef("ox.fs.FileStoreTemplate.write", "Attempting to write to inode {}", id);
auto existing = find(id);
if (!canWrite(existing, dataSize)) {
oxReturnError(compact());
existing = find(id);
}
if (canWrite(existing, dataSize)) {
// delete the old node if it exists
if (existing.valid()) {
oxTracef("ox.fs.FileStoreTemplate.write", "Freeing old version of inode found at offset: {}", existing.offset());
auto err = m_buffer->free(existing);
if (err) {
oxTrace("ox.fs.FileStoreTemplate.write.fail", "Free of old version of inode failed");
return err;
}
existing = nullptr;
}
// write the given data
auto dest = m_buffer->malloc(dataSize).value;
// if first malloc failed, compact and try again
if (!dest.valid()) {
oxTrace("ox.fs.FileStoreTemplate.write", "Allocation failed, compacting");
oxReturnError(compact());
dest = m_buffer->malloc(dataSize).value;
}
if (dest.valid()) {
oxTrace("ox.fs.FileStoreTemplate.write", "Memory allocated");
dest->id = id;
dest->fileType = fileType;
auto destData = m_buffer->template dataOf<uint8_t>(dest);
if (destData.valid()) {
oxAssert(destData.size() == dataSize, "Allocation size does not match data.");
// write data if any was provided
if (data != nullptr) {
ox_memcpy(destData, data, dest->size());
oxTrace("ox.fs.FileStoreTemplate.write", "Data written");
}
auto fsData = fileStoreData();
if (fsData) {
oxTracef("ox.fs.FileStoreTemplate.write", "Searching for root node at {}", fsData->rootNode.get());
auto root = m_buffer->ptr(fsData->rootNode);
if (root.valid()) {
oxTracef("ox.fs.FileStoreTemplate.write",
"Placing {} on {} at {}", dest->id.get(), root->id.get(), destData.offset());
return placeItem(dest);
} else {
oxTracef("ox.fs.FileStoreTemplate.write",
"Initializing root inode: {} (offset: {}, data size: {})",
dest->id.get(), dest.offset(), destData.size());
fsData->rootNode = dest.offset();
oxTracef("ox.fs.FileStoreTemplate.write", "Root inode: {}", dest->id.get());
return OxError(0);
}
} else {
oxTrace("ox.fs.FileStoreTemplate.write.fail", "Could not place item due to absence of FileStore header.");
}
}
}
oxReturnError(m_buffer->free(dest));
}
return OxError(1);
}
template<typename size_t>
Error FileStoreTemplate<size_t>::remove(uint64_t id) {
return remove(find(static_cast<size_t>(id)));
}
template<typename size_t>
Error FileStoreTemplate<size_t>::read(uint64_t id, void *out, FsSize_t outSize, FsSize_t *size) const {
oxTracef("ox.fs.FileStoreTemplate.read", "Attempting to read from inode {}", id);
auto src = find(static_cast<size_t>(id));
// error check
if (!src.valid()) {
oxTracef("ox.fs.FileStoreTemplate.read.fail", "Could not find requested item: {}", id);
return OxError(1, "Could not find requested item");
}
auto srcData = m_buffer->template dataOf<uint8_t>(src);
oxTracef("ox.fs.FileStoreTemplate.read.found", "{} found at {} with data section at {}",
id, src.offset(), srcData.offset());
oxTracef("ox.fs.FileStoreTemplate.read.outSize", "{} {} {}", srcData.offset(), srcData.size(), outSize);
// error check
if (!(srcData.valid() && srcData.size() <= outSize)) {
oxTracef("ox.fs.FileStoreTemplate.read.fail", "Could not read data section of item: {}", id);
oxTracef("ox.fs.FileStoreTemplate.read.fail",
"Item data section size: {}, Expected size: {}", srcData.size(), outSize);
return OxError(1);
}
ox_memcpy(out, srcData, srcData.size());
if (size) {
*size = src.size();
}
return OxError(0);
}
template<typename size_t>
Error FileStoreTemplate<size_t>::read(uint64_t id, FsSize_t readStart, FsSize_t readSize, void *out, FsSize_t *size) const {
oxTracef("ox.fs.FileStoreTemplate.read", "Attempting to read from inode {}", id);
auto src = find(static_cast<size_t>(id));
// error check
if (!src.valid()) {
oxTracef("ox.fs.FileStoreTemplate.read.fail", "Could not find requested item: {}", id);
return OxError(1);
}
auto srcData = m_buffer->template dataOf<uint8_t>(src);
oxTracef("ox.fs.FileStoreTemplate.read.found", "{} found at {} with data section at {}",
id, src.offset(), srcData.offset());
oxTracef("ox.fs.FileStoreTemplate.read.readSize", "{} {} {}", srcData.offset(), srcData.size(), readSize);
// error check
if (!(srcData.valid() && srcData.size() - readStart <= readSize)) {
oxTracef("ox.fs.FileStoreTemplate.read.fail", "Could not read data section of item: {}", id);
oxTracef("ox.fs.FileStoreTemplate.read.fail",
"Item data section size: {}, Expected size: {}", srcData.size(), readSize);
return OxError(1);
}
ox_memcpy(out, srcData.get() + readStart, readSize);
if (size) {
*size = src.size();
}
return {};
}
template<typename size_t>
template<typename T>
Error FileStoreTemplate<size_t>::read(uint64_t id, FsSize_t readStart,
FsSize_t readSize, T *out, FsSize_t *size) const {
oxTracef("ox.fs.FileStoreTemplate.read", "Attempting to read from inode {}", id);
auto src = find(static_cast<size_t>(id));
// error check
if (!src.valid()) {
oxTracef("ox.fs.FileStoreTemplate.read.fail", "Could not find requested item: {}", id);
return OxError(1);
}
auto srcData = m_buffer->template dataOf<uint8_t>(src);
oxTracef("ox.fs.FileStoreTemplate.read.found", "{} found at {} with data section at {}",
id, src.offset(), srcData.offset());
oxTracef("ox.fs.FileStoreTemplate.read.readSize", "{} {} {}", srcData.offset(), srcData.size(), readSize);
// error check
if (!(srcData.valid() && srcData.size() - readStart <= readSize)) {
oxTracef("ox.fs.FileStoreTemplate.read.fail", "Could not read data section of item: {}", id);
oxTracef("ox.fs.FileStoreTemplate.read.fail",
"Item data section size: {}, Expected size: {}", srcData.size(), readSize);
return OxError(1);
}
ox_memcpy(out, srcData.get() + readStart, readSize);
if (size) {
*size = src.size();
}
return {};
}
template<typename size_t>
ptrarith::Ptr<uint8_t, std::size_t> FileStoreTemplate<size_t>::read(uint64_t id) const {
auto item = find(static_cast<size_t>(id));
if (item.valid()) {
return item->data();
} else {
return nullptr;
}
}
template<typename size_t>
Error FileStoreTemplate<size_t>::resize() {
oxReturnError(compact());
const auto newSize = static_cast<std::size_t>(size() - available());
oxTracef("ox.fs.FileStoreTemplate.resize", "resize to: {}", newSize);
oxReturnError(m_buffer->setSize(newSize));
oxTracef("ox.fs.FileStoreTemplate.resize", "resized to: {}", m_buffer->size());
return OxError(0);
}
template<typename size_t>
Error FileStoreTemplate<size_t>::resize(std::size_t size, void *newBuff) {
if (m_buffer->size() > size) {
return OxError(1);
}
m_buffSize = static_cast<size_t>(size);
if (newBuff) {
m_buffer = reinterpret_cast<Buffer*>(newBuff);
oxReturnError(m_buffer->setSize(static_cast<size_t>(size)));
}
return OxError(0);
}
template<typename size_t>
Result<StatInfo> FileStoreTemplate<size_t>::stat(uint64_t id) const {
oxRequire(inode, find(static_cast<size_t>(id)).validate());
return StatInfo {
id,
inode->links,
inode->size(),
inode->fileType,
};
}
template<typename size_t>
typename FileStoreTemplate<size_t>::InodeId_t FileStoreTemplate<size_t>::spaceNeeded(FsSize_t size) const {
return m_buffer->spaceNeeded(size);
}
template<typename size_t>
typename FileStoreTemplate<size_t>::InodeId_t FileStoreTemplate<size_t>::size() const {
return m_buffer->size();
}
template<typename size_t>
typename FileStoreTemplate<size_t>::InodeId_t FileStoreTemplate<size_t>::available() const {
return m_buffer->available();
}
template<typename size_t>
char *FileStoreTemplate<size_t>::buff() {
return reinterpret_cast<char*>(m_buffer);
}
template<typename size_t>
Error FileStoreTemplate<size_t>::walk(Error(*cb)(uint8_t, uint64_t, uint64_t)) {
for (auto i = m_buffer->iterator(); i.valid(); i.next()) {
oxReturnError(cb(i->fileType, i.ptr().offset(), i.ptr().end()));
}
return OxError(0);
}
template<typename size_t>
Result<typename FileStoreTemplate<size_t>::InodeId_t> FileStoreTemplate<size_t>::generateInodeId() {
auto fsData = fileStoreData();
if (!fsData) {
return OxError(1);
}
for (auto i = 0; i < 100; i++) {
auto inode = static_cast<typename FileStoreTemplate<size_t>::InodeId_t>(fsData->random.gen() % MaxValue<InodeId_t>);
if (inode > ReservedInodeEnd && !find(static_cast<size_t>(inode)).valid()) {
return inode;
}
}
return OxError(2);
}
template<typename size_t>
Error FileStoreTemplate<size_t>::compact() {
auto isFirstItem = true;
return m_buffer->compact([this, &isFirstItem](uint64_t oldAddr, ItemPtr item) -> Error {
if (isFirstItem) {
isFirstItem = false;
return OxError(0);
}
if (!item.valid()) {
return OxError(1);
}
oxTracef("ox.fs.FileStoreTemplate.compact.moveItem", "Moving Item: {} from {} to {}", item->id.get(), oldAddr, item.offset());
// update rootInode if this is it
auto fsData = fileStoreData();
if (fsData && oldAddr == fsData->rootNode) {
fsData->rootNode = item.offset();
}
auto parent = findParent(rootInode(), item->id, static_cast<size_t>(oldAddr));
oxAssert(parent.valid() || rootInode() == item.offset(),
"Parent inode not found for item that should have parent.");
if (parent.valid()) {
if (parent->left == oldAddr) {
parent->left = item;
} else if (parent->right == oldAddr) {
parent->right = item;
}
}
return OxError(0);
});
}
template<typename size_t>
typename FileStoreTemplate<size_t>::FileStoreData *FileStoreTemplate<size_t>::fileStoreData() const {
auto first = m_buffer->firstItem();
if (first.valid()) {
auto data = first->data();
if (data.valid()) {
return reinterpret_cast<FileStoreData*>(data.get());
}
}
return nullptr;
}
template<typename size_t>
Error FileStoreTemplate<size_t>::placeItem(ItemPtr item) {
auto fsData = fileStoreData();
if (!fsData) {
return OxError(1);
}
oxRequireM(root, m_buffer->ptr(fsData->rootNode).validate());
if (root->id == item->id) {
fsData->rootNode = item;
item->left = root->left;
item->right = root->right;
oxTracef("ox.fs.FileStoreTemplate.placeItem", "Overwrote Root Item: {}", item->id.get());
return OxError(0);
} else {
return placeItem(root, item);
}
}
template<typename size_t>
Error FileStoreTemplate<size_t>::placeItem(ItemPtr root, ItemPtr item, int depth) {
if (depth > 5000) {
oxTrace("ox.fs.FileStoreTemplate.placeItem.fail", "Excessive recursion depth, stopping before stack overflow.");
return OxError(2);
}
if (item->id > root->id) {
auto right = m_buffer->ptr(root->right);
if (!right.valid() || right->id == item->id) {
root->right = item.offset();
if (right.valid()) {
item->left = right->left;
item->right = right->right;
}
oxTracef("ox.fs.FileStoreTemplate.placeItem", "Placed Item: {}", item->id.get());
return OxError(0);
} else {
return placeItem(right, item, depth + 1);
}
} else if (item->id < root->id) {
auto left = m_buffer->ptr(root->left);
if (!left.valid() || left->id == item->id) {
root->left = item.offset();
if (left.valid()) {
item->left = left->left;
item->right = left->right;
}
oxTracef("ox.fs.FileStoreTemplate.placeItem", "Placed Item: {}", item->id.get());
return OxError(0);
} else {
return placeItem(left, item, depth + 1);
}
} else {
oxTrace("ox.fs.FileStoreTemplate.placeItem.fail", "Cannot insert an item on itself.");
return OxError(1, "Cannot insert an item on itself.");
}
}
template<typename size_t>
Error FileStoreTemplate<size_t>::unplaceItem(ItemPtr item) {
auto fsData = fileStoreData();
if (!fsData) {
return OxError(1);
}
oxRequireM(root, m_buffer->ptr(fsData->rootNode).validate());
if (root->id == item->id) {
item->left = root->left;
item->right = root->right;
auto left = m_buffer->ptr(item->left);
auto right = m_buffer->ptr(item->right);
if (right.valid()) {
auto oldRoot = fsData->rootNode;
fsData->rootNode = item->right;
if (left.valid()) {
auto err = placeItem(left);
// undo if unable to place the left side of the tree
if (err) {
fsData->rootNode = oldRoot;
return err;
}
}
} else if (left.valid()) {
fsData->rootNode = item->left;
} else {
fsData->rootNode = 0;
}
return OxError(0);
} else {
return unplaceItem(root, item);
}
}
template<typename size_t>
Error FileStoreTemplate<size_t>::unplaceItem(ItemPtr root, ItemPtr item, int depth) {
if (depth >= 5000) {
oxTrace("ox.fs.FileStoreTemplate.unplaceItem.fail", "Excessive recursion depth, stopping before stack overflow.");
return OxError(1, "Excessive recursion depth, stopping before stack overflow.");
}
if (item->id > root->id) {
auto right = m_buffer->ptr(root->right);
if (right->id == item->id) {
root->right = 0;
oxTracef("ox.fs.FileStoreTemplate.unplaceItem", "Unplaced Item: {}", item->id.get());
} else {
return unplaceItem(right, item, depth + 1);
}
} else if (item->id < root->id) {
auto left = m_buffer->ptr(root->left);
if (left->id == item->id) {
root->left = 0;
oxTracef("ox.fs.FileStoreTemplate.unplaceItem", "Unplaced Item: {}", item->id.get());
} else {
return unplaceItem(left, item, depth + 1);
}
} else {
return OxError(1);
}
if (item->right) {
oxReturnError(placeItem(m_buffer->ptr(item->right)));
}
if (item->left) {
oxReturnError(placeItem(m_buffer->ptr(item->left)));
}
return OxError(0);
}
template<typename size_t>
Error FileStoreTemplate<size_t>::remove(ItemPtr item) {
if (item.valid()) {
oxReturnError(unplaceItem(item));
oxReturnError(m_buffer->free(item));
return OxError(0);
}
return OxError(1);
}
template<typename size_t>
typename FileStoreTemplate<size_t>::ItemPtr FileStoreTemplate<size_t>::findParent(ItemPtr item, size_t id, size_t oldAddr) const {
// This is a little bit confusing. findParent uses the inode ID to find
// where the target ID should be, but the actual address of that item is
// currently invalid, so we check it against what is known to be the old
// address of the item to confirm that we have the right item.
if (item.valid()) {
if (id > item->id) {
if (item->right == oldAddr) {
return item;
} else {
auto right = m_buffer->ptr(item->right);
return findParent(right, id, oldAddr);
}
} else if (id < item->id) {
if (item->left == oldAddr) {
return item;
} else {
auto left = m_buffer->ptr(item->left);
return findParent(left, id, oldAddr);
}
}
}
return nullptr;
}
template<typename size_t>
typename FileStoreTemplate<size_t>::ItemPtr FileStoreTemplate<size_t>::find(ItemPtr item, InodeId_t id, int depth) const {
if (depth > 5000) {
oxTracef("ox.fs.FileStoreTemplate.find.fail", "Excessive recursion depth, stopping before stack overflow. Search for: {}", id);
return nullptr;
}
if (!item.valid()) {
oxTrace("ox.fs.FileStoreTemplate.find.fail", "item invalid");
return nullptr;
}
// do search
if (id > item->id) {
oxTracef("ox.fs.FileStoreTemplate.find", "Not a match, searching on {}", item->right.get());
return find(m_buffer->ptr(item->right), id, depth + 1);
} else if (id < item->id) {
oxTracef("ox.fs.FileStoreTemplate.find", "Not a match, searching on {}", item->left.get());
return find(m_buffer->ptr(item->left), id, depth + 1);
} else if (id == item->id) {
oxTracef("ox.fs.FileStoreTemplate.find", "Found {} at {}", id, item.offset());
return item;
}
return nullptr;
}
template<typename size_t>
typename FileStoreTemplate<size_t>::ItemPtr FileStoreTemplate<size_t>::find(InodeId_t id) const {
oxTracef("ox.fs.FileStoreTemplate.find", "Searching for inode: {}", id);
auto fsData = fileStoreData();
if (fsData) {
auto root = m_buffer->ptr(fsData->rootNode);
if (root.valid()) {
auto item = find(root, id);
return item;
} else {
oxTrace("ox.fs.FileStoreTemplate.find.fail", "No root node");
}
} else {
oxTrace("ox.fs.FileStoreTemplate.find.fail", "No FileStore Data");
}
return nullptr;
}
/**
* Gets the root inode.
*/
template<typename size_t>
typename FileStoreTemplate<size_t>::ItemPtr FileStoreTemplate<size_t>::rootInode() {
auto fsData = fileStoreData();
if (fsData) {
return m_buffer->ptr(fsData->rootNode);
} else {
return nullptr;
}
}
template<typename size_t>
bool FileStoreTemplate<size_t>::canWrite(ItemPtr existing, std::size_t size) {
const auto sz = static_cast<size_t>(size);
return existing.size() >= sz || m_buffer->spaceNeeded(sz) <= m_buffer->available();
}
template<typename size_t>
bool FileStoreTemplate<size_t>::valid() const {
return m_buffer;
}
extern template class FileStoreTemplate<uint16_t>;
extern template class FileStoreTemplate<uint32_t>;
using FileStore16 = FileStoreTemplate<uint16_t>;
using FileStore32 = FileStoreTemplate<uint32_t>;
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright 2015 - 2022 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include "directory.hpp"
namespace ox {
template class Directory<FileStore16, uint16_t>;
template class Directory<FileStore32, uint32_t>;
template struct DirectoryEntry<uint16_t>;
template struct DirectoryEntry<uint32_t>;
}

View File

@@ -0,0 +1,368 @@
/*
* Copyright 2015 - 2023 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <ox/fs/filesystem/pathiterator.hpp>
#include <ox/fs/filestore/filestoretemplate.hpp>
#include <ox/fs/ptrarith/nodebuffer.hpp>
#include <ox/std/byteswap.hpp>
#include "types.hpp"
namespace ox {
template<typename InodeId_t>
struct OX_PACKED DirectoryEntry {
public:
struct OX_PACKED DirectoryEntryData {
// DirectoryEntry fields
LittleEndian<InodeId_t> inode = 0;
char name[MaxFileNameLength];
static constexpr std::size_t spaceNeeded(std::size_t chars) {
return offsetof(DirectoryEntryData, name) + chars;
}
};
// NodeBuffer fields
LittleEndian<InodeId_t> prev = 0;
LittleEndian<InodeId_t> next = 0;
private:
LittleEndian<InodeId_t> m_bufferSize = sizeof(DirectoryEntry);
public:
constexpr DirectoryEntry() noexcept = default;
Error init(InodeId_t inode, const char *name, InodeId_t bufferSize) noexcept {
m_bufferSize = bufferSize;
auto d = data();
if (d.valid()) {
d->inode = inode;
ox_strncpy(d->name, name, ox::min(bufferSize, static_cast<InodeId_t>(MaxFileNameLength)));
return OxError(0);
}
return OxError(1);
}
ptrarith::Ptr<DirectoryEntryData, InodeId_t> data() noexcept {
oxTracef("ox.fs.DirectoryEntry.data", "{} {} {}", this->fullSize(), sizeof(*this), this->size());
return ptrarith::Ptr<DirectoryEntryData, InodeId_t>(this, this->fullSize(), sizeof(*this), this->size(), this->size());
}
/**
* @return the size of the data + the size of the Item type
*/
[[nodiscard]]
InodeId_t fullSize() const {
return m_bufferSize;
}
[[nodiscard]]
InodeId_t size() const {
return fullSize() - static_cast<InodeId_t>(sizeof(*this));
}
void setSize(std::size_t) {
// ignore set value
}
static constexpr std::size_t spaceNeeded(std::size_t chars) {
return sizeof(DirectoryEntry) + offsetof(DirectoryEntryData, name) + chars;
}
};
template<typename FileStore, typename InodeId_t>
class Directory {
private:
using Buffer = ptrarith::NodeBuffer<InodeId_t, DirectoryEntry<InodeId_t>>;
InodeId_t m_inodeId = 0;
std::size_t m_size = 0;
FileStore m_fs;
public:
Directory() noexcept = default;
Directory(FileStore fs, uint64_t inode) noexcept;
/**
* Initializes Directory.
*/
Error init() noexcept;
Error mkdir(PathIterator path, bool parents, FileName *nameBuff = nullptr);
/**
* @param parents indicates the operation should create non-existent directories in the path, like mkdir -p
*/
Error write(PathIterator path, uint64_t inode64, FileName *nameBuff = nullptr) noexcept;
Error remove(PathIterator path, FileName *nameBuff = nullptr) noexcept;
template<typename F>
Error ls(F cb) noexcept;
[[nodiscard]]
Result<typename FileStore::InodeId_t> findEntry(const FileName &name) const noexcept;
[[nodiscard]]
Result<typename FileStore::InodeId_t> find(PathIterator name, FileName *nameBuff = nullptr) const noexcept;
};
template<typename FileStore, typename InodeId_t>
Directory<FileStore, InodeId_t>::Directory(FileStore fs, uint64_t inodeId) noexcept {
m_fs = fs;
m_inodeId = static_cast<InodeId_t>(inodeId);
auto buff = m_fs.read(static_cast<InodeId_t>(inodeId)).template to<Buffer>();
if (buff.valid()) {
m_size = buff.size();
}
}
template<typename FileStore, typename InodeId_t>
Error Directory<FileStore, InodeId_t>::init() noexcept {
constexpr auto Size = sizeof(Buffer);
oxTracef("ox.fs.Directory.init", "Initializing Directory with Inode ID: {}", m_inodeId);
oxReturnError(m_fs.write(m_inodeId, nullptr, Size, static_cast<uint8_t>(FileType::Directory)));
auto buff = m_fs.read(m_inodeId).template to<Buffer>();
if (!buff.valid()) {
m_size = 0;
return OxError(1);
}
new (buff) Buffer(Size);
m_size = Size;
return OxError(0);
}
template<typename FileStore, typename InodeId_t>
Error Directory<FileStore, InodeId_t>::mkdir(PathIterator path, bool parents, FileName *nameBuff) {
if (path.valid()) {
oxTrace("ox.fs.Directory.mkdir", path.fullPath());
// reuse nameBuff if it has already been allocated, as it is a rather large variable
if (nameBuff == nullptr) {
nameBuff = new (ox_alloca(sizeof(FileName))) FileName;
}
// determine if already exists
auto name = nameBuff;
oxReturnError(path.get(name));
auto childInode = find(PathIterator(*name));
if (!childInode.ok()) {
// if this is not the last item in the path and parents is disabled,
// return an error
if (!parents && path.hasNext()) {
return OxError(1);
}
childInode = m_fs.generateInodeId();
oxTracef("ox.fs.Directory.mkdir", "Generated Inode ID: {}", childInode.value);
oxLogError(childInode.error);
oxReturnError(childInode.error);
// initialize the directory
Directory<FileStore, InodeId_t> child(m_fs, childInode.value);
oxReturnError(child.init());
auto err = write(PathIterator(*name), childInode.value);
if (err) {
oxLogError(err);
// could not index the directory, delete it
oxLogError(m_fs.remove(childInode.value));
return err;
}
}
Directory<FileStore, InodeId_t> child(m_fs, childInode.value);
if (path.hasNext()) {
oxReturnError(child.mkdir(path.next(), parents, nameBuff));
}
}
return OxError(0);
}
template<typename FileStore, typename InodeId_t>
Error Directory<FileStore, InodeId_t>::write(PathIterator path, uint64_t inode64, FileName *nameBuff) noexcept {
const auto inode = static_cast<InodeId_t>(inode64);
// reuse nameBuff if it has already been allocated, as it is a rather large variable
if (nameBuff == nullptr) {
nameBuff = new (ox_alloca(sizeof(FileName))) FileName;
}
auto name = nameBuff;
if (path.next().hasNext()) { // not yet at target directory, recurse to next one
oxReturnError(path.get(name));
oxTracef("ox.fs.Directory.write", "Attempting to write to next sub-Directory: {} of {}",
*name, path.fullPath());
oxRequire(nextChild, findEntry(*name));
oxTracef("ox.fs.Directory.write", "{}: {}", *name, nextChild);
if (nextChild) {
// reuse name because it is a rather large variable and will not be used again
// be attentive that this remains true
name = nullptr;
return Directory(m_fs, nextChild).write(path.next(), inode, nameBuff);
} else {
oxTracef("ox.fs.Directory.write", "{} not found and not allowed to create it.", *name);
return OxError(1, "File not found and not allowed to create it.");
}
} else {
oxTrace("ox.fs.Directory.write", path.fullPath());
// insert the new entry on this directory
// get the name
oxReturnError(path.next(name));
// find existing version of directory
oxTracef("ox.fs.Directory.write", "Searching for directory inode {}", m_inodeId);
oxRequire(oldStat, m_fs.stat(m_inodeId));
oxTracef("ox.fs.Directory.write", "Found existing directory of size {}", oldStat.size);
auto old = m_fs.read(m_inodeId).template to<Buffer>();
if (!old.valid()) {
oxTrace("ox.fs.Directory.write.fail", "Could not read existing version of Directory");
return OxError(1, "Could not read existing version of Directory");
}
const auto pathSize = name->len() + 1;
const auto entryDataSize = DirectoryEntry<InodeId_t>::DirectoryEntryData::spaceNeeded(pathSize);
const auto newSize = oldStat.size + Buffer::spaceNeeded(entryDataSize);
auto cpy = ox_malloca(newSize, Buffer, *old, oldStat.size);
if (cpy == nullptr) {
oxTrace("ox.fs.Directory.write.fail", "Could not allocate memory for copy of Directory");
return OxError(1, "Could not allocate memory for copy of Directory");
}
oxReturnError(cpy->setSize(newSize));
auto val = cpy->malloc(entryDataSize).value;
if (!val.valid()) {
oxTrace("ox.fs.Directory.write.fail", "Could not allocate memory for new directory entry");
return OxError(1, "Could not allocate memory for new directory entry");
}
oxTracef("ox.fs.Directory.write", "Attempting to write Directory entry: {}", name->data());
oxReturnError(val->init(inode, name->data(), val.size()));
return m_fs.write(m_inodeId, cpy.get(), cpy->size(), static_cast<uint8_t>(FileType::Directory));
}
}
template<typename FileStore, typename InodeId_t>
Error Directory<FileStore, InodeId_t>::remove(PathIterator path, FileName *nameBuff) noexcept {
// reuse nameBuff if it has already been allocated, as it is a rather large variable
if (nameBuff == nullptr) {
nameBuff = new (ox_alloca(sizeof(FileName))) FileName;
}
auto &name = *nameBuff;
oxReturnError(path.get(&name));
oxTrace("ox.fs.Directory.remove", name);
auto buff = m_fs.read(m_inodeId).template to<Buffer>();
if (buff.valid()) {
oxTrace("ox.fs.Directory.remove", "Found directory buffer.");
for (auto i = buff->iterator(); i.valid(); i.next()) {
auto data = i->data();
if (data.valid()) {
if (name == data->name) {
oxReturnError(buff->free(i));
}
} else {
oxTrace("ox.fs.Directory.remove", "INVALID DIRECTORY ENTRY");
}
}
} else {
oxTrace("ox.fs.Directory.remove.fail", "Could not find directory buffer");
return OxError(1, "Could not find directory buffer");
}
return OxError(0);
}
template<typename FileStore, typename InodeId_t>
template<typename F>
Error Directory<FileStore, InodeId_t>::ls(F cb) noexcept {
oxTrace("ox.fs.Directory.ls");
auto buff = m_fs.read(m_inodeId).template to<Buffer>();
if (!buff.valid()) {
oxTrace("ox.fs.Directory.ls.fail", "Could not directory buffer");
return OxError(1, "Could not directory buffer");
}
oxTrace("ox.fs.Directory.ls", "Found directory buffer.");
for (auto i = buff->iterator(); i.valid(); i.next()) {
auto data = i->data();
if (data.valid()) {
oxReturnError(cb(data->name, data->inode));
} else {
oxTrace("ox.fs.Directory.ls", "INVALID DIRECTORY ENTRY");
}
}
return OxError(0);
}
template<typename FileStore, typename InodeId_t>
Result<typename FileStore::InodeId_t> Directory<FileStore, InodeId_t>::findEntry(const FileName &name) const noexcept {
oxTrace("ox.fs.Directory.findEntry", name);
auto buff = m_fs.read(m_inodeId).template to<Buffer>();
if (!buff.valid()) {
oxTrace("ox.fs.Directory.findEntry.fail", "Could not findEntry directory buffer");
return OxError(2, "Could not findEntry directory buffer");
}
oxTracef("ox.fs.Directory.findEntry", "Found directory buffer, size: {}", buff.size());
for (auto i = buff->iterator(); i.valid(); i.next()) {
auto data = i->data();
if (data.valid()) {
oxTracef("ox.fs.Directory.findEntry", "Comparing \"{}\" to \"{}\"", name, data->name);
if (name == data->name) {
oxTracef("ox.fs.Directory.findEntry", "\"{}\" match found.", name);
return static_cast<InodeId_t>(data->inode);
}
} else {
oxTrace("ox.fs.Directory.findEntry") << "INVALID DIRECTORY ENTRY";
}
}
oxTrace("ox.fs.Directory.findEntry.fail", "Entry not present");
return OxError(1, "Entry not present");
}
template<typename FileStore, typename InodeId_t>
Result<typename FileStore::InodeId_t> Directory<FileStore, InodeId_t>::find(PathIterator path, FileName *nameBuff) const noexcept {
// reuse nameBuff if it has already been allocated, as it is a rather large variable
if (nameBuff == nullptr) {
nameBuff = new (ox_alloca(sizeof(FileName))) FileName;
}
// determine if already exists
auto name = nameBuff;
oxReturnError(path.get(name));
oxRequire(v, findEntry(*name));
// recurse if not at end of path
if (auto p = path.next(); p.valid()) {
Directory dir(m_fs, v);
name = nullptr;
return dir.find(p, nameBuff);
}
return v;
}
extern template class Directory<FileStore16, uint16_t>;
extern template class Directory<FileStore32, uint32_t>;
extern template struct DirectoryEntry<uint16_t>;
extern template struct DirectoryEntry<uint32_t>;
using Directory16 = Directory<FileStore16, uint16_t>;
using Directory32 = Directory<FileStore32, uint32_t>;
}

View File

@@ -0,0 +1,99 @@
/*
* Copyright 2015 - 2022 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <ox/model/modelops.hpp>
#include "filelocation.hpp"
namespace ox {
FileAddress::FileAddress(const FileAddress &other) noexcept {
operator=(other);
}
FileAddress::FileAddress(FileAddress &&other) noexcept {
operator=(std::move(other));
}
FileAddress::FileAddress(std::nullptr_t) noexcept {
}
FileAddress::FileAddress(uint64_t inode) noexcept {
m_data.inode = inode;
m_type = FileAddressType::Inode;
}
FileAddress::FileAddress(ox::CRStringView path) noexcept {
auto pathSize = path.bytes();
m_data.path = new char[pathSize + 1];
memcpy(m_data.path, path.data(), pathSize);
m_data.path[pathSize] = 0;
m_type = FileAddressType::Path;
}
FileAddress &FileAddress::operator=(const FileAddress &other) noexcept {
if (this == &other) {
return *this;
}
cleanup();
m_type = other.m_type;
switch (m_type) {
case FileAddressType::Path:
{
if (other.m_data.path) {
auto strSize = ox_strlen(other.m_data.path) + 1;
m_data.path = new char[strSize];
ox_memcpy(m_data.path, other.m_data.path, strSize);
} else {
m_data.constPath = "";
m_type = FileAddressType::ConstPath;
}
break;
}
case FileAddressType::ConstPath:
case FileAddressType::Inode:
m_data = other.m_data;
break;
case FileAddressType::None:
break;
}
return *this;
}
FileAddress &FileAddress::operator=(FileAddress &&other) noexcept {
if (this == &other) {
return *this;
}
cleanup();
m_type = other.m_type;
switch (m_type) {
case FileAddressType::Path:
{
m_data.path = other.m_data.path;
break;
}
case FileAddressType::ConstPath:
case FileAddressType::Inode:
m_data = other.m_data;
break;
case FileAddressType::None:
break;
}
other.clear();
return *this;
}
bool FileAddress::operator==(CRStringView path) const noexcept {
auto [p, err] = getPath();
if (err) {
return false;
}
return path == p;
}
}

View File

@@ -0,0 +1,180 @@
/*
* Copyright 2015 - 2022 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/std.hpp>
#include <ox/model/def.hpp>
#include <ox/model/typenamecatcher.hpp>
#include <ox/model/types.hpp>
namespace ox {
enum class FileAddressType: int8_t {
None = -1,
Path,
ConstPath,
Inode,
};
class FileAddress {
template<typename T>
friend constexpr Error model(T *h, CommonPtrWith<FileAddress> auto *fa) noexcept;
public:
static constexpr auto TypeName = "net.drinkingtea.ox.FileAddress";
static constexpr auto TypeVersion = 1;
union Data {
static constexpr auto TypeName = "net.drinkingtea.ox.FileAddress.Data";
static constexpr auto TypeVersion = 1;
char *path = nullptr;
const char *constPath;
uint64_t inode;
};
protected:
FileAddressType m_type = FileAddressType::None;
Data m_data;
public:
constexpr FileAddress() noexcept {
m_data.inode = 0;
m_type = FileAddressType::None;
}
FileAddress(const FileAddress &other) noexcept;
FileAddress(FileAddress &&other) noexcept;
FileAddress(std::nullptr_t) noexcept;
FileAddress(uint64_t inode) noexcept;
explicit FileAddress(CRStringView path) noexcept;
constexpr FileAddress(ox::StringLiteral path) noexcept;
constexpr ~FileAddress() noexcept;
FileAddress &operator=(const FileAddress &other) noexcept;
FileAddress &operator=(FileAddress &&other) noexcept;
bool operator==(CRStringView path) const noexcept;
[[nodiscard]]
constexpr FileAddressType type() const noexcept {
switch (m_type) {
case FileAddressType::Path:
case FileAddressType::ConstPath:
return FileAddressType::Path;
default:
return m_type;
}
}
constexpr Result<uint64_t> getInode() const noexcept {
switch (m_type) {
case FileAddressType::Inode:
return m_data.inode;
default:
return OxError(1);
}
}
constexpr Result<ox::StringView> getPath() const noexcept {
switch (m_type) {
case FileAddressType::Path:
return ox::StringView(m_data.path);
case FileAddressType::ConstPath:
return ox::StringView(m_data.constPath);
default:
return OxError(1);
}
}
explicit constexpr operator bool() const noexcept {
return m_type != FileAddressType::None;
}
private:
/**
* Cleanup memory allocations.
*/
constexpr void cleanup() noexcept;
/**
* Clears fields, but does not delete allocations.
*/
constexpr void clear() noexcept;
};
constexpr FileAddress::FileAddress(ox::StringLiteral path) noexcept {
m_data.constPath = path.c_str();
m_type = FileAddressType::ConstPath;
}
constexpr FileAddress::~FileAddress() noexcept {
cleanup();
}
constexpr void FileAddress::cleanup() noexcept {
if (m_type == FileAddressType::Path) {
safeDeleteArray(m_data.path);
clear();
}
}
constexpr void FileAddress::clear() noexcept {
m_data.path = nullptr;
m_type = FileAddressType::None;
}
template<>
constexpr const char *getModelTypeName<FileAddress::Data>() noexcept {
return FileAddress::Data::TypeName;
}
template<>
constexpr const char *getModelTypeName<FileAddress>() noexcept {
return FileAddress::TypeName;
}
template<typename T>
constexpr Error model(T *h, CommonPtrWith<FileAddress::Data> auto *obj) noexcept {
oxReturnError(h->template setTypeInfo<FileAddress::Data>());
oxReturnError(h->fieldCString("path", &obj->path));
oxReturnError(h->fieldCString("constPath", &obj->path));
oxReturnError(h->field("inode", &obj->inode));
return {};
}
template<typename T>
constexpr Error model(T *h, CommonPtrWith<FileAddress> auto *fa) noexcept {
oxReturnError(h->template setTypeInfo<FileAddress>());
if constexpr(T::opType() == OpType::Reflect) {
int8_t type = -1;
oxReturnError(h->field("type", &type));
oxReturnError(h->field("data", UnionView(&fa->m_data, type)));
} else if constexpr(T::opType() == OpType::Read) {
auto type = static_cast<int8_t>(fa->m_type);
oxReturnError(h->field("type", &type));
fa->m_type = static_cast<FileAddressType>(type);
oxReturnError(h->field("data", UnionView(&fa->m_data, static_cast<int>(fa->m_type))));
} else if constexpr(T::opType() == OpType::Write) {
auto const type = static_cast<int8_t>(fa->m_type);
oxReturnError(h->field("type", &type));
oxReturnError(h->field("data", UnionView(&fa->m_data, static_cast<int>(fa->m_type))));
}
return {};
}
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright 2015 - 2023 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <ox/std/error.hpp>
#include <ox/std/utility.hpp>
#include "filesystem.hpp"
namespace ox {
Result<const char*> MemFS::directAccess(const FileAddress &addr) const noexcept {
switch (addr.type()) {
case FileAddressType::Inode:
return directAccess(addr.getInode().value);
case FileAddressType::ConstPath:
case FileAddressType::Path:
return directAccess(StringView(addr.getPath().value));
default:
return OxError(1);
}
}
Error FileSystem::read(const FileAddress &addr, void *buffer, std::size_t size) noexcept {
switch (addr.type()) {
case FileAddressType::Inode:
return readFileInode(addr.getInode().value, buffer, size);
case FileAddressType::ConstPath:
case FileAddressType::Path:
return readFilePath(StringView(addr.getPath().value), buffer, size);
default:
return OxError(1);
}
}
Result<Buffer> FileSystem::read(const FileAddress &addr) noexcept {
oxRequire(s, stat(addr));
Buffer buff(static_cast<std::size_t>(s.size));
oxReturnError(read(addr, buff.data(), buff.size()));
return buff;
}
Result<Buffer> FileSystem::read(CRStringView path) noexcept {
oxRequire(s, statPath(path));
Buffer buff(static_cast<std::size_t>(s.size));
oxReturnError(readFilePath(path, buff.data(), buff.size()));
return buff;
}
Error FileSystem::read(const FileAddress &addr, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size) noexcept {
switch (addr.type()) {
case FileAddressType::Inode:
return read(addr.getInode().value, readStart, readSize, buffer, size);
case FileAddressType::ConstPath:
case FileAddressType::Path:
return OxError(2, "Unsupported for path lookups");
default:
return OxError(1);
}
}
Error FileSystem::remove(const FileAddress &addr, bool recursive) noexcept {
switch (addr.type()) {
case FileAddressType::Inode:
return remove(addr.getInode().value, recursive);
case FileAddressType::ConstPath:
case FileAddressType::Path:
return remove(StringView(addr.getPath().value), recursive);
default:
return OxError(1);
}
}
Error FileSystem::write(const FileAddress &addr, const void *buffer, uint64_t size, FileType fileType) noexcept {
switch (addr.type()) {
case FileAddressType::Inode:
return writeFileInode(addr.getInode().value, buffer, size, fileType);
case FileAddressType::ConstPath:
case FileAddressType::Path:
return writeFilePath(StringView(addr.getPath().value), buffer, size, fileType);
default:
return OxError(1);
}
}
Result<FileStat> FileSystem::stat(const FileAddress &addr) const noexcept {
switch (addr.type()) {
case FileAddressType::Inode:
return statInode(addr.getInode().value);
case FileAddressType::ConstPath:
case FileAddressType::Path:
return statPath(StringView(addr.getPath().value));
default:
return OxError(1);
}
}
template class FileSystemTemplate<FileStore16, Directory16>;
template class FileSystemTemplate<FileStore32, Directory32>;
}

View File

@@ -0,0 +1,496 @@
/*
* Copyright 2015 - 2023 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <ox/std/buffer.hpp>
#include <ox/std/span.hpp>
#include <ox/fs/filestore/filestoretemplate.hpp>
#include <ox/fs/filesystem/filelocation.hpp>
#include <ox/fs/filesystem/types.hpp>
#include "directory.hpp"
namespace ox {
namespace detail {
static inline void fsBuffFree(char *buff) noexcept {
safeDelete(buff);
}
}
class FileSystem {
public:
virtual ~FileSystem() noexcept = default;
virtual Error mkdir(CRStringView path, bool recursive) noexcept = 0;
/**
* Moves an entry from one directory to another.
* @param src the path to the file
* @param dest the path of the destination directory
*/
virtual Error move(CRStringView src, CRStringView dest) noexcept = 0;
Error read(const FileAddress &addr, void *buffer, std::size_t size) noexcept;
Result<Buffer> read(const FileAddress &addr) noexcept;
Result<Buffer> read(CRStringView path) noexcept;
inline Error read(CRStringView path, void *buffer, std::size_t buffSize) noexcept {
return readFilePath(path, buffer, buffSize);
}
inline Error read(uint64_t inode, void *buffer, std::size_t buffSize) noexcept {
return readFileInode(inode, buffer, buffSize);
}
Error read(const FileAddress &addr, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size) noexcept;
virtual Result<Vector<String>> ls(CRStringView dir) const noexcept = 0;
virtual Error remove(CRStringView path, bool recursive) noexcept = 0;
Error remove(const FileAddress &addr, bool recursive = false) noexcept;
virtual Error resize(uint64_t size, void *buffer) noexcept = 0;
Error write(CRStringView path, const void *buffer, uint64_t size) noexcept {
return writeFilePath(path, buffer, size, FileType::NormalFile);
}
Error write(CRStringView path, ox::Span<char> const&buff) noexcept {
return write(path, buff.data(), buff.size(), FileType::NormalFile);
}
Error write(uint64_t inode, const void *buffer, uint64_t size) noexcept {
return write(inode, buffer, size, FileType::NormalFile);
}
Error write(uint64_t inode, ox::Span<char> const&buff) noexcept {
return write(inode, buff.data(), buff.size(), FileType::NormalFile);
}
Error write(const FileAddress &addr, const void *buffer, uint64_t size, FileType fileType = FileType::NormalFile) noexcept;
inline Error write(CRStringView path, const void *buffer, uint64_t size, FileType fileType) noexcept {
return writeFilePath(path, buffer, size, fileType);
}
inline Error write(uint64_t inode, const void *buffer, uint64_t size, FileType fileType) noexcept {
return writeFileInode(inode, buffer, size, fileType);
}
inline Result<FileStat> stat(uint64_t inode) const noexcept {
return statInode(inode);
}
inline Result<FileStat> stat(CRStringView path) const noexcept {
return statPath(path);
}
Result<FileStat> stat(const FileAddress &addr) const noexcept;
[[nodiscard]]
virtual uint64_t spaceNeeded(uint64_t size) const noexcept = 0;
[[nodiscard]]
virtual Result<uint64_t> available() const noexcept = 0;
[[nodiscard]]
virtual Result<uint64_t> size() const noexcept = 0;
[[nodiscard]]
virtual char *buff() noexcept = 0;
virtual Error walk(Error(*cb)(uint8_t, uint64_t, uint64_t)) noexcept = 0;
[[nodiscard]]
virtual bool valid() const noexcept = 0;
protected:
virtual Result<FileStat> statInode(uint64_t inode) const noexcept = 0;
virtual Result<FileStat> statPath(CRStringView path) const noexcept = 0;
virtual Error readFilePath(CRStringView path, void *buffer, std::size_t buffSize) noexcept = 0;
virtual Error readFileInode(uint64_t inode, void *buffer, std::size_t size) noexcept = 0;
virtual Error readFileInodeRange(uint64_t inode, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size) noexcept = 0;
virtual Error writeFilePath(CRStringView path, const void *buffer, uint64_t size, FileType fileType) noexcept = 0;
virtual Error writeFileInode(uint64_t inode, const void *buffer, uint64_t size, FileType fileType) noexcept = 0;
};
class MemFS: public FileSystem {
public:
Result<const char*> directAccess(const FileAddress &addr) const noexcept;
inline Result<const char*> directAccess(CRStringView path) const noexcept {
return directAccessPath(path);
}
inline Result<const char*> directAccess(uint64_t inode) const noexcept {
return directAccessInode(inode);
}
protected:
virtual Result<const char*> directAccessPath(CRStringView path) const noexcept = 0;
virtual Result<const char*> directAccessInode(uint64_t inode) const noexcept = 0;
};
/**
* FileSystemTemplate used to create file system that wraps around a FileStore,
* taking an inode size and a directory type as parameters.
*
* Note: Directory parameter must have a default constructor.
*/
template<typename FileStore, typename Directory>
class FileSystemTemplate: public MemFS {
private:
static constexpr auto InodeFsData = 2;
struct OX_PACKED FileSystemData {
LittleEndian<typename FileStore::InodeId_t> rootDirInode;
};
FileStore m_fs;
void(*m_freeBuffer)(char*) = nullptr;
public:
FileSystemTemplate() noexcept = default;
FileSystemTemplate(void *buffer, uint64_t bufferSize, void(*freeBuffer)(char*) = detail::fsBuffFree) noexcept;
explicit FileSystemTemplate(ox::Buffer &buffer) noexcept;
explicit FileSystemTemplate(FileStore fs) noexcept;
~FileSystemTemplate() noexcept override;
static Error format(void *buff, uint64_t buffSize) noexcept;
Error mkdir(CRStringView path, bool recursive) noexcept override;
Error move(CRStringView src, CRStringView dest) noexcept override;
Error readFilePath(CRStringView path, void *buffer, std::size_t buffSize) noexcept override;
Result<const char*> directAccessPath(CRStringView) const noexcept override;
Error readFileInode(uint64_t inode, void *buffer, std::size_t size) noexcept override;
Error readFileInodeRange(uint64_t inode, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size) noexcept override;
Result<const char*> directAccessInode(uint64_t) const noexcept override;
Result<Vector<String>> ls(CRStringView dir) const noexcept override;
template<typename F>
Error ls(CRStringView path, F cb) const;
Error remove(CRStringView path, bool recursive) noexcept override;
/**
* Resizes FileSystem to minimum possible size.
*/
Error resize() noexcept;
Error resize(uint64_t size, void *buffer) noexcept override;
Error writeFilePath(CRStringView path, const void *buffer, uint64_t size, FileType fileType) noexcept override;
Error writeFileInode(uint64_t inode, const void *buffer, uint64_t size, FileType fileType) noexcept override;
Result<FileStat> statInode(uint64_t inode) const noexcept override;
Result<FileStat> statPath(CRStringView path) const noexcept override;
[[nodiscard]]
uint64_t spaceNeeded(uint64_t size) const noexcept override;
Result<uint64_t> available() const noexcept override;
Result<uint64_t> size() const noexcept override;
char *buff() noexcept override;
Error walk(Error(*cb)(uint8_t, uint64_t, uint64_t)) noexcept override;
[[nodiscard]]
bool valid() const noexcept override;
private:
Result<FileSystemData> fileSystemData() const noexcept;
/**
* Finds the inode ID at the given path.
*/
Result<uint64_t> find(CRStringView path) const noexcept;
Result<Directory> rootDir() const noexcept;
};
template<typename FileStore, typename Directory>
FileSystemTemplate<FileStore, Directory>::FileSystemTemplate(FileStore fs) noexcept {
m_fs = fs;
}
template<typename FileStore, typename Directory>
FileSystemTemplate<FileStore, Directory>::FileSystemTemplate(ox::Buffer &buffer) noexcept:
m_fs(buffer.data(), static_cast<std::size_t>(buffer.size())) {
}
template<typename FileStore, typename Directory>
FileSystemTemplate<FileStore, Directory>::FileSystemTemplate(void *buffer, uint64_t bufferSize, void(*freeBuffer)(char*)) noexcept:
m_fs(buffer, static_cast<std::size_t>(bufferSize)),
m_freeBuffer(freeBuffer) {
}
template<typename FileStore, typename Directory>
FileSystemTemplate<FileStore, Directory>::~FileSystemTemplate() noexcept {
if (m_freeBuffer && m_fs.buff()) {
m_freeBuffer(m_fs.buff());
}
}
template<typename FileStore, typename Directory>
Error FileSystemTemplate<FileStore, Directory>::format(void *buff, uint64_t buffSize) noexcept {
oxReturnError(FileStore::format(buff, static_cast<size_t>(buffSize)));
FileStore fs(buff, static_cast<size_t>(buffSize));
constexpr auto rootDirInode = MaxValue<typename FileStore::InodeId_t> / 2;
Directory rootDir(fs, rootDirInode);
oxReturnError(rootDir.init());
FileSystemData fd;
fd.rootDirInode = rootDirInode;
oxTracef("ox.fs.FileSystemTemplate.format", "rootDirInode: {}", fd.rootDirInode.get());
oxReturnError(fs.write(InodeFsData, &fd, sizeof(fd)));
if (!fs.read(fd.rootDirInode).valid()) {
oxTrace("ox.fs.FileSystemTemplate.format.error", "FileSystemTemplate::format did not correctly create root directory");
return OxError(1);
}
return OxError(0);
}
template<typename FileStore, typename Directory>
Error FileSystemTemplate<FileStore, Directory>::mkdir(CRStringView path, bool recursive) noexcept {
oxTracef("ox.fs.FileSystemTemplate.mkdir", "path: {}, recursive: {}", path, recursive);
oxRequireM(rootDir, this->rootDir());
return rootDir.mkdir(path, recursive);
}
template<typename FileStore, typename Directory>
Error FileSystemTemplate<FileStore, Directory>::move(CRStringView src, CRStringView dest) noexcept {
oxRequire(fd, fileSystemData());
Directory rootDir(m_fs, fd.rootDirInode);
oxRequireM(inode, rootDir.find(src));
oxReturnError(rootDir.write(dest, inode));
oxReturnError(rootDir.remove(src));
return OxError(0);
}
template<typename FileStore, typename Directory>
Error FileSystemTemplate<FileStore, Directory>::readFilePath(CRStringView path, void *buffer, std::size_t buffSize) noexcept {
oxRequire(fd, fileSystemData());
Directory rootDir(m_fs, fd.rootDirInode);
oxRequire(s, stat(path));
if (s.size > buffSize) {
return OxError(1, "Buffer to small to load file");
}
return readFileInodeRange(s.inode, 0, static_cast<size_t>(s.size), buffer, &buffSize);
}
template<typename FileStore, typename Directory>
Result<const char*> FileSystemTemplate<FileStore, Directory>::directAccessPath(CRStringView path) const noexcept {
oxRequire(fd, fileSystemData());
Directory rootDir(m_fs, fd.rootDirInode);
oxRequire(inode, rootDir.find(path));
return directAccessInode(inode);
}
template<typename FileStore, typename Directory>
Error FileSystemTemplate<FileStore, Directory>::readFileInode(uint64_t inode, void *buffer, std::size_t buffSize) noexcept {
oxRequire(s, stat(inode));
if (s.size > buffSize) {
return OxError(1, "Buffer to small to load file");
}
return readFileInodeRange(inode, 0, static_cast<size_t>(s.size), buffer, &buffSize);
}
template<typename FileStore, typename Directory>
Error FileSystemTemplate<FileStore, Directory>::readFileInodeRange(uint64_t inode, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size) noexcept {
return m_fs.read(inode, readStart, readSize, reinterpret_cast<uint8_t*>(buffer), size);
}
template<typename FileStore, typename Directory>
Result<const char*> FileSystemTemplate<FileStore, Directory>::directAccessInode(uint64_t inode) const noexcept {
auto data = m_fs.read(inode);
if (!data.valid()) {
return OxError(1, "Data not valid");
}
return reinterpret_cast<char*>(data.get());
}
template<typename FileStore, typename Directory>
Result<Vector<String>> FileSystemTemplate<FileStore, Directory>::ls(CRStringView path) const noexcept {
Vector<String> out;
oxReturnError(ls(path, [&out](CRStringView name, typename FileStore::InodeId_t) {
out.emplace_back(name);
return OxError(0);
}));
return out;
}
template<typename FileStore, typename Directory>
template<typename F>
Error FileSystemTemplate<FileStore, Directory>::ls(CRStringView path, F cb) const {
oxTracef("ox.fs.FileSystemTemplate.ls", "path: {}", path);
oxRequire(s, stat(path));
Directory dir(m_fs, s.inode);
return dir.ls(cb);
}
template<typename FileStore, typename Directory>
Error FileSystemTemplate<FileStore, Directory>::remove(CRStringView path, bool recursive) noexcept {
oxRequire(fd, fileSystemData());
Directory rootDir(m_fs, fd.rootDirInode);
oxRequire(inode, rootDir.find(path));
oxRequire(st, statInode(inode));
if (st.fileType == FileType::NormalFile || recursive) {
if (auto err = rootDir.remove(path)) {
// removal failed, try putting the index back
oxLogError(rootDir.write(path, inode));
return err;
}
} else {
oxTrace("FileSystemTemplate.remove.fail", "Tried to remove directory without recursive setting.");
return OxError(1);
}
return OxError(0);
}
template<typename FileStore, typename Directory>
Error FileSystemTemplate<FileStore, Directory>::resize() noexcept {
return m_fs.resize();
}
template<typename FileStore, typename Directory>
Error FileSystemTemplate<FileStore, Directory>::resize(uint64_t size, void *buffer) noexcept {
oxReturnError(m_fs.resize(static_cast<size_t>(size), buffer));
return OxError(0);
}
template<typename FileStore, typename Directory>
Error FileSystemTemplate<FileStore, Directory>::writeFilePath(CRStringView path, const void *buffer, uint64_t size, FileType fileType) noexcept {
auto [inode, err] = find(path);
if (err) {
oxRequire(generatedId, m_fs.generateInodeId());
inode = generatedId;
oxRequireM(rootDir, this->rootDir());
oxReturnError(rootDir.write(path, inode));
}
oxReturnError(writeFileInode(inode, buffer, size, fileType));
return OxError(0);
}
template<typename FileStore, typename Directory>
Error FileSystemTemplate<FileStore, Directory>::writeFileInode(uint64_t inode, const void *buffer, uint64_t size, FileType fileType) noexcept {
return m_fs.write(inode, buffer, static_cast<size_t>(size), static_cast<uint8_t>(fileType));
}
template<typename FileStore, typename Directory>
Result<FileStat> FileSystemTemplate<FileStore, Directory>::statInode(uint64_t inode) const noexcept {
oxRequire(s, m_fs.stat(inode));
FileStat out;
out.inode = s.inode;
out.links = s.links;
out.size = s.size;
out.fileType = static_cast<FileType>(s.fileType);
return out;
}
template<typename FileStore, typename Directory>
Result<FileStat> FileSystemTemplate<FileStore, Directory>::statPath(CRStringView path) const noexcept {
oxRequire(inode, find(path));
return stat(inode);
}
template<typename FileStore, typename Directory>
uint64_t FileSystemTemplate<FileStore, Directory>::spaceNeeded(uint64_t size) const noexcept {
return m_fs.spaceNeeded(static_cast<size_t>(size));
}
template<typename FileStore, typename Directory>
Result<uint64_t> FileSystemTemplate<FileStore, Directory>::available() const noexcept {
return m_fs.available();
}
template<typename FileStore, typename Directory>
Result<uint64_t> FileSystemTemplate<FileStore, Directory>::size() const noexcept {
return m_fs.size();
}
template<typename FileStore, typename Directory>
char *FileSystemTemplate<FileStore, Directory>::buff() noexcept {
return m_fs.buff();
}
template<typename FileStore, typename Directory>
Error FileSystemTemplate<FileStore, Directory>::walk(Error(*cb)(uint8_t, uint64_t, uint64_t)) noexcept {
return m_fs.walk(cb);
}
template<typename FileStore, typename Directory>
bool FileSystemTemplate<FileStore, Directory>::valid() const noexcept {
return m_fs.valid();
}
template<typename FileStore, typename Directory>
Result<typename FileSystemTemplate<FileStore, Directory>::FileSystemData> FileSystemTemplate<FileStore, Directory>::fileSystemData() const noexcept {
FileSystemData fd;
oxReturnError(m_fs.read(InodeFsData, &fd, sizeof(fd)));
return fd;
}
template<typename FileStore, typename Directory>
Result<uint64_t> FileSystemTemplate<FileStore, Directory>::find(CRStringView path) const noexcept {
oxRequire(fd, fileSystemData());
// return root as a special case
if (path == "/") {
return static_cast<uint64_t>(fd.rootDirInode);
}
Directory rootDir(m_fs, fd.rootDirInode);
oxRequire(out, rootDir.find(path));
return static_cast<uint64_t>(out);
}
template<typename FileStore, typename Directory>
Result<Directory> FileSystemTemplate<FileStore, Directory>::rootDir() const noexcept {
oxRequire(fd, fileSystemData());
return Directory(m_fs, fd.rootDirInode);
}
extern template class FileSystemTemplate<FileStore16, Directory16>;
extern template class FileSystemTemplate<FileStore32, Directory32>;
using FileSystem16 = FileSystemTemplate<FileStore16, Directory16>;
using FileSystem32 = FileSystemTemplate<FileStore32, Directory32>;
}

View File

@@ -0,0 +1,198 @@
/*
* Copyright 2015 - 2023 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <ox/std/error.hpp>
#include "passthroughfs.hpp"
#if defined(OX_HAS_PASSTHROUGHFS)
#include <fstream>
#include <string_view>
namespace ox {
PassThroughFS::PassThroughFS(CRStringView dirPath) {
m_path = std::string_view(dirPath.data(), dirPath.bytes());
}
PassThroughFS::~PassThroughFS() noexcept = default;
String PassThroughFS::basePath() const noexcept {
return ox::String(m_path.string().c_str());
}
Error PassThroughFS::mkdir(CRStringView path, bool recursive) noexcept {
bool success = false;
const auto p = m_path / stripSlash(path);
const auto u8p = p.u8string();
oxTrace("ox.fs.PassThroughFS.mkdir", std::bit_cast<const char*>(u8p.c_str()));
if (recursive) {
std::error_code ec;
const auto isDir = std::filesystem::is_directory(p, ec);
if (isDir && !ec.value()) {
success = true;
} else {
success = std::filesystem::create_directories(p, ec);
oxReturnError(OxError(static_cast<ox::ErrorCode>(ec.value()), "PassThroughFS: mkdir failed"));
}
} else {
std::error_code ec;
const auto isDir = std::filesystem::is_directory(p, ec);
if (isDir) {
success = true;
} else {
success = std::filesystem::create_directory(p, ec);
oxReturnError(OxError(static_cast<ox::ErrorCode>(ec.value()), "PassThroughFS: mkdir failed"));
}
}
return OxError(success ? 0 : 1);
}
Error PassThroughFS::move(CRStringView src, CRStringView dest) noexcept {
std::error_code ec;
std::filesystem::rename(m_path / stripSlash(src), m_path / stripSlash(dest), ec);
if (ec.value()) {
return OxError(1);
}
return OxError(0);
}
Result<Vector<String>> PassThroughFS::ls(CRStringView dir) const noexcept {
Vector<String> out;
std::error_code ec;
const auto di = std::filesystem::directory_iterator(m_path / stripSlash(dir), ec);
oxReturnError(OxError(static_cast<ox::ErrorCode>(ec.value()), "PassThroughFS: ls failed"));
for (const auto &p : di) {
const auto u8p = p.path().filename().u8string();
out.emplace_back(reinterpret_cast<const char*>(u8p.c_str()));
}
return out;
}
Error PassThroughFS::remove(CRStringView path, bool recursive) noexcept {
if (recursive) {
return OxError(std::filesystem::remove_all(m_path / stripSlash(path)) != 0);
} else {
return OxError(std::filesystem::remove(m_path / stripSlash(path)) != 0);
}
}
Error PassThroughFS::resize(uint64_t, void*) noexcept {
// unsupported
return OxError(1, "resize is not supported by PassThroughFS");
}
Result<FileStat> PassThroughFS::statInode(uint64_t) const noexcept {
// unsupported
return OxError(1, "statInode(uint64_t) is not supported by PassThroughFS");
}
Result<FileStat> PassThroughFS::statPath(CRStringView path) const noexcept {
std::error_code ec;
const auto p = m_path / stripSlash(path);
const FileType type = std::filesystem::is_directory(p, ec) ?
FileType::Directory : FileType::NormalFile;
oxTracef("ox.fs.PassThroughFS.statInode", "{} {}", ec.message(), path);
const uint64_t size = type == FileType::Directory ? 0 : std::filesystem::file_size(p, ec);
oxTracef("ox.fs.PassThroughFS.statInode", "{} {}", ec.message(), path);
oxTracef("ox.fs.PassThroughFS.statInode.size", "{} {}", path, size);
oxReturnError(OxError(static_cast<ox::ErrorCode>(ec.value()), "PassThroughFS: stat failed"));
return FileStat{0, 0, size, type};
}
uint64_t PassThroughFS::spaceNeeded(uint64_t size) const noexcept {
return size;
}
Result<uint64_t> PassThroughFS::available() const noexcept {
std::error_code ec;
const auto s = std::filesystem::space(m_path, ec);
oxReturnError(OxError(static_cast<ox::ErrorCode>(ec.value()), "PassThroughFS: could not get FS size"));
return s.available;
}
Result<uint64_t> PassThroughFS::size() const noexcept {
std::error_code ec;
const auto s = std::filesystem::space(m_path, ec);
oxReturnError(OxError(static_cast<ox::ErrorCode>(ec.value()), "PassThroughFS: could not get FS size"));
return s.capacity;
}
char *PassThroughFS::buff() noexcept {
return nullptr;
}
Error PassThroughFS::walk(Error(*)(uint8_t, uint64_t, uint64_t)) noexcept {
return OxError(1, "walk(Error(*)(uint8_t, uint64_t, uint64_t)) is not supported by PassThroughFS");
}
bool PassThroughFS::valid() const noexcept {
std::error_code ec;
const auto out = std::filesystem::is_directory(m_path, ec);
if (!ec.value()) {
return out;
}
return false;
}
Error PassThroughFS::readFilePath(CRStringView path, void *buffer, std::size_t buffSize) noexcept {
try {
std::ifstream file((m_path / stripSlash(path)), std::ios::binary | std::ios::ate);
const std::size_t size = static_cast<std::size_t>(file.tellg());
file.seekg(0, std::ios::beg);
if (size > buffSize) {
oxTracef("ox.fs.PassThroughFS.read.error", "Read failed: Buffer too small: {}", path);
return OxError(1);
}
file.read(static_cast<char*>(buffer), static_cast<std::streamsize>(buffSize));
} catch (const std::fstream::failure &f) {
oxTracef("ox.fs.PassThroughFS.read.error", "Read of {} failed: {}", path, f.what());
return OxError(2);
}
return OxError(0);
}
Error PassThroughFS::readFileInode(uint64_t, void*, std::size_t) noexcept {
// unsupported
return OxError(1, "readFileInode(uint64_t, void*, std::size_t) is not supported by PassThroughFS");
}
Error PassThroughFS::readFileInodeRange(uint64_t, std::size_t, std::size_t, void*, std::size_t*) noexcept {
// unsupported
return OxError(1, "read(uint64_t, std::size_t, std::size_t, void*, std::size_t*) is not supported by PassThroughFS");
}
Error PassThroughFS::writeFilePath(CRStringView path, const void *buffer, uint64_t size, FileType) noexcept {
const auto p = (m_path / stripSlash(path));
try {
std::ofstream f(p, std::ios::binary);
f.write(static_cast<const char*>(buffer), static_cast<std::streamsize>(size));
} catch (const std::fstream::failure &f) {
oxTracef("ox.fs.PassThroughFS.read.error", "Write of {} failed: {}", path, f.what());
return OxError(1);
}
return OxError(0);
}
Error PassThroughFS::writeFileInode(uint64_t, const void*, uint64_t, FileType) noexcept {
// unsupported
return OxError(1, "writeFileInode(uint64_t, void*, uint64_t, uint8_t) is not supported by PassThroughFS");
}
std::string_view PassThroughFS::stripSlash(StringView path) noexcept {
const auto pathLen = ox_strlen(path);
for (auto i = 0u; i < pathLen && path[0] == '/'; i++) {
path = substr(path, 1);
}
return {path.data(), path.bytes()};
}
}
#endif

View File

@@ -0,0 +1,112 @@
/*
* Copyright 2015 - 2023 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#if __has_include(<filesystem>) && defined(OX_USE_STDLIB)
#include <filesystem>
#include <ox/std/bit.hpp>
#include "filesystem.hpp"
#define OX_HAS_PASSTHROUGHFS
namespace ox {
constexpr auto HasPassThroughFS = true;
/**
*
*/
class PassThroughFS: public FileSystem {
private:
std::filesystem::path m_path;
public:
explicit PassThroughFS(CRStringView dirPath);
~PassThroughFS() noexcept override;
[[nodiscard]]
String basePath() const noexcept;
Error mkdir(CRStringView path, bool recursive) noexcept override;
Error move(CRStringView src, CRStringView dest) noexcept override;
Result<Vector<String>> ls(CRStringView dir) const noexcept override;
template<typename F>
Error ls(CRStringView dir, F cb) const noexcept;
Error remove(CRStringView path, bool recursive) noexcept override;
Error resize(uint64_t size, void *buffer) noexcept override;
Result<FileStat> statInode(uint64_t inode) const noexcept override;
Result<FileStat> statPath(CRStringView path) const noexcept override;
[[nodiscard]]
uint64_t spaceNeeded(uint64_t size) const noexcept override;
Result<uint64_t> available() const noexcept override;
Result<uint64_t> size() const noexcept override;
[[nodiscard]]
char *buff() noexcept override;
Error walk(Error(*cb)(uint8_t, uint64_t, uint64_t)) noexcept override;
[[nodiscard]]
bool valid() const noexcept override;
protected:
Error readFilePath(CRStringView path, void *buffer, std::size_t buffSize) noexcept override;
Error readFileInode(uint64_t inode, void *buffer, std::size_t size) noexcept override;
Error readFileInodeRange(uint64_t inode, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size) noexcept override;
Error writeFilePath(CRStringView path, const void *buffer, uint64_t size, FileType fileType) noexcept override;
Error writeFileInode(uint64_t inode, const void *buffer, uint64_t size, FileType fileType) noexcept override;
private:
/**
* Strips the leading slashes from a string.
*/
[[nodiscard]]
static std::string_view stripSlash(StringView path) noexcept;
};
template<typename F>
Error PassThroughFS::ls(CRStringView dir, F cb) const noexcept {
std::error_code ec;
const auto di = std::filesystem::directory_iterator(m_path / stripSlash(dir), ec);
oxReturnError(OxError(static_cast<ox::ErrorCode>(ec.value()), "PassThroughFS: ls failed"));
for (auto &p : di) {
oxReturnError(cb(p.path().filename().c_str(), 0));
}
return OxError(0);
}
}
#else
namespace ox {
constexpr auto HasPassThroughFS = false;
}
#endif

View File

@@ -0,0 +1,217 @@
/*
* Copyright 2015 - 2023 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <ox/std/memops.hpp>
#include <ox/std/strops.hpp>
#include <ox/std/trace.hpp>
#include "pathiterator.hpp"
namespace ox {
PathIterator::PathIterator(const char *path, std::size_t maxSize, std::size_t iterator) {
m_path = path;
m_maxSize = maxSize;
m_iterator = iterator;
}
PathIterator::PathIterator(const char *path): PathIterator(path, ox_strlen(path)) {
}
PathIterator::PathIterator(CRStringView path): PathIterator(path.data(), path.bytes()) {
}
/**
* @return 0 if no error
*/
Error PathIterator::dirPath(char *out, std::size_t outSize) {
const auto idx = ox_lastIndexOf(m_path, '/', m_maxSize);
const auto size = static_cast<std::size_t>(idx + 1);
if (idx >= 0 && size < outSize) {
ox_memcpy(out, m_path, size);
out[size] = 0;
return OxError(0);
} else {
return OxError(1);
}
}
/**
* @return 0 if no error
*/
Error PathIterator::fileName(char *out, std::size_t outSize) {
auto idx = ox_lastIndexOf(m_path, '/', m_maxSize);
if (idx >= 0) {
idx++; // pass up the preceding /
std::size_t fileNameSize = static_cast<size_t>(ox_strlen(&m_path[idx]));
if (fileNameSize < outSize) {
ox_memcpy(out, &m_path[idx], fileNameSize);
out[fileNameSize] = 0;
return OxError(0);
} else {
return OxError(1);
}
} else {
return OxError(2);
}
}
// Gets the get item in the path
Error PathIterator::get(char *pathOut, std::size_t pathOutSize) {
std::size_t size = 0;
if (m_iterator >= m_maxSize) {
oxTracef("ox.fs.PathIterator.get", "m_iterator ({}) >= m_maxSize ({})", m_iterator, m_maxSize);
return OxError(1);
}
if (!ox_strlen(&m_path[m_iterator])) {
oxTrace("ox.fs.PathIterator.get", "!ox_strlen(&m_path[m_iterator])");
return OxError(1);
}
auto start = m_iterator;
if (m_path[start] == '/') {
start++;
}
// end is at the next /
const char *substr = ox_strchr(&m_path[start], '/', m_maxSize - start);
// correct end if it is invalid, which happens if there is no next /
if (!substr) {
substr = ox_strchr(&m_path[start], 0, m_maxSize - start);
}
const auto end = static_cast<size_t>(substr - m_path);
size = end - start;
// cannot fit the output in the output parameter
if (size >= pathOutSize || size == 0) {
return OxError(1);
}
ox_memcpy(pathOut, &m_path[start], size);
// truncate trailing /
if (size && pathOut[size - 1] == '/') {
size--;
}
pathOut[size] = 0; // end with null terminator
return OxError(0);
}
// Gets the get item in the path
Error PathIterator::next(char *pathOut, std::size_t pathOutSize) {
std::size_t size = 0;
auto retval = OxError(1);
if (m_iterator < m_maxSize && ox_strlen(&m_path[m_iterator])) {
retval = OxError(0);
if (m_path[m_iterator] == '/') {
m_iterator++;
}
const auto start = m_iterator;
// end is at the next /
const char *substr = ox_strchr(&m_path[start], '/', m_maxSize - start);
// correct end if it is invalid, which happens if there is no next /
if (!substr) {
substr = ox_strchr(&m_path[start], 0, m_maxSize - start);
}
const auto end = static_cast<size_t>(substr - m_path);
size = end - start;
// cannot fit the output in the output parameter
if (size >= pathOutSize) {
return OxError(1);
}
ox_memcpy(pathOut, &m_path[start], size);
}
// truncate trailing /
if (size && pathOut[size - 1] == '/') {
size--;
}
pathOut[size] = 0; // end with null terminator
m_iterator += size;
return retval;
}
/**
* @return 0 if no error
*/
Error PathIterator::get(BString<MaxFileNameLength> *fileName) {
return get(fileName->data(), fileName->cap());
}
/**
* @return 0 if no error
*/
Error PathIterator::next(BString<MaxFileNameLength> *fileName) {
return next(fileName->data(), fileName->cap());
}
Result<std::size_t> PathIterator::nextSize() const {
std::size_t size = 0;
auto retval = OxError(1);
auto it = m_iterator;
if (it < m_maxSize && ox_strlen(&m_path[it])) {
retval = OxError(0);
if (m_path[it] == '/') {
it++;
}
const auto start = it;
// end is at the next /
const char *substr = ox_strchr(&m_path[start], '/', m_maxSize - start);
// correct end if it is invalid, which happens if there is no next /
if (!substr) {
substr = ox_strchr(&m_path[start], 0, m_maxSize - start);
}
const auto end = static_cast<std::size_t>(substr - m_path);
size = end - start;
}
it += size;
return {size, retval};
}
bool PathIterator::hasNext() const {
std::size_t size = 0;
if (m_iterator < m_maxSize && ox_strlen(&m_path[m_iterator])) {
std::size_t start = m_iterator;
if (m_path[start] == '/') {
start++;
}
// end is at the next /
const char *substr = ox_strchr(&m_path[start], '/', m_maxSize - start);
// correct end if it is invalid, which happens if there is no next /
if (!substr) {
substr = ox_strchr(&m_path[start], 0, m_maxSize - start);
}
const auto end = static_cast<std::size_t>(substr - m_path);
size = end - start;
}
return size > 0;
}
bool PathIterator::valid() const {
return m_iterator < m_maxSize && m_path[m_iterator] != 0;
}
PathIterator PathIterator::next() const {
std::size_t size = 0;
auto iterator = m_iterator;
if (iterator < m_maxSize && ox_strlen(&m_path[iterator])) {
if (m_path[iterator] == '/') {
iterator++;
}
const auto start = iterator;
// end is at the next /
const char *substr = ox_strchr(&m_path[start], '/', m_maxSize - start);
// correct end if it is invalid, which happens if there is no next /
if (!substr) {
substr = ox_strchr(&m_path[start], 0, m_maxSize - start);
}
const auto end = static_cast<std::size_t>(substr - m_path);
size = end - start;
}
iterator += size;
return {m_path, m_maxSize, iterator + 1};
}
const char *PathIterator::fullPath() const {
return m_path;
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright 2015 - 2022 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <ox/std/std.hpp>
namespace ox {
constexpr std::size_t MaxFileNameLength = 255;
using FileName = BString<MaxFileNameLength>;
class PathIterator {
private:
const char *m_path = nullptr;
std::size_t m_iterator = 0;
std::size_t m_maxSize = 0;
public:
PathIterator(const char *path, std::size_t maxSize, std::size_t iterator = 0);
PathIterator(const char *path);
PathIterator(CRStringView path);
/**
* @return 0 if no error
*/
Error dirPath(char *pathOut, std::size_t pathOutSize);
/**
* @return 0 if no error
*/
Error fileName(char *out, std::size_t outSize);
/**
* @return 0 if no error
*/
Error next(char *pathOut, std::size_t pathOutSize);
/**
* @return 0 if no error
*/
Error get(char *pathOut, std::size_t pathOutSize);
/**
* @return 0 if no error
*/
Error next(FileName *fileName);
/**
* @return 0 if no error
*/
Error get(FileName *fileName);
/**
* @return 0 if no error
*/
Result<std::size_t> nextSize() const;
[[nodiscard]]
bool hasNext() const;
[[nodiscard]]
bool valid() const;
[[nodiscard]]
PathIterator next() const;
[[nodiscard]]
const char *fullPath() const;
};
}

39
deps/ox/src/ox/fs/filesystem/types.hpp vendored Normal file
View File

@@ -0,0 +1,39 @@
/*
* Copyright 2015 - 2022 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <ox/std/types.hpp>
namespace ox {
enum class FileType: uint8_t {
None = 0,
NormalFile = 1,
Directory = 2
};
constexpr const char *toString(FileType t) noexcept {
switch (t) {
case FileType::NormalFile:
return "Normal File";
case FileType::Directory:
return "Directory";
default:
return "";
}
}
struct FileStat {
uint64_t inode = 0;
uint64_t links = 0;
uint64_t size = 0;
FileType fileType = FileType::None;
};
}

15
deps/ox/src/ox/fs/fs.hpp vendored Normal file
View File

@@ -0,0 +1,15 @@
/*
* Copyright 2015 - 2022 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "filestore/filestoretemplate.hpp"
#include "filesystem/filelocation.hpp"
#include "filesystem/filesystem.hpp"
#include "filesystem/passthroughfs.hpp"
#include "filesystem/directory.hpp"

View File

@@ -0,0 +1,451 @@
/*
* Copyright 2015 - 2023 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <ox/std/stddef.hpp>
#include <ox/std/trace.hpp>
#include "ptr.hpp"
namespace ox::ptrarith {
template<typename size_t, typename Item>
class OX_PACKED NodeBuffer {
public:
struct OX_PACKED Header {
LittleEndian<size_t> size = sizeof(Header); // capacity
LittleEndian<size_t> bytesUsed = sizeof(Header);
LittleEndian<size_t> firstItem = 0;
};
using ItemPtr = Ptr<Item, size_t, sizeof(Header)>;
class Iterator {
private:
NodeBuffer *m_buffer = nullptr;
ItemPtr m_current;
size_t m_it = 0;
public:
Iterator(NodeBuffer *buffer, ItemPtr current) noexcept {
m_buffer = buffer;
m_current = current;
oxTrace("ox.ptrarith.Iterator.start") << current.offset();
}
operator const Item*() const noexcept {
return m_current;
}
ItemPtr ptr() noexcept {
return m_current;
}
[[nodiscard]]
Item *get() noexcept {
return m_current;
}
operator ItemPtr() noexcept {
return m_current;
}
operator Item*() noexcept {
return m_current;
}
const Item *operator->() const noexcept {
return m_current;
}
Item *operator->() noexcept {
return m_current;
}
[[nodiscard]]
bool valid() const noexcept {
return m_current.valid();
}
[[nodiscard]]
bool hasNext() noexcept {
if (m_current.valid()) {
oxTrace("ox.ptrarith.NodeBuffer.Iterator.hasNext.current") << m_current.offset();
auto next = m_buffer->next(m_current);
return next.valid() && m_buffer->firstItem() != next;
}
return false;
}
void next() noexcept {
oxTrace("ox.ptrarith.NodeBuffer.Iterator.next") << m_it++;
if (hasNext()) {
m_current = m_buffer->next(m_current);
} else {
m_current = nullptr;
}
}
};
Header m_header;
public:
NodeBuffer(const NodeBuffer &other, std::size_t size) noexcept;
explicit NodeBuffer(std::size_t size) noexcept;
[[nodiscard]]
const Iterator iterator() const noexcept;
[[nodiscard]]
Iterator iterator() noexcept;
ItemPtr firstItem() noexcept;
ItemPtr lastItem() noexcept;
/**
* @return the data section of the given item
*/
template<typename T>
Ptr<T, size_t, sizeof(Item)> dataOf(ItemPtr) noexcept;
ItemPtr prev(Item *item) noexcept;
ItemPtr next(Item *item) noexcept;
/**
* Like pointer but omits checks that assume the memory at the offset has
* already been initialed as an Item.
*/
ItemPtr uninitializedPtr(size_t offset) noexcept;
ItemPtr ptr(size_t offset) noexcept;
Result<ItemPtr> malloc(std::size_t size) noexcept;
Error free(ItemPtr item) noexcept;
[[nodiscard]]
bool valid(size_t maxSize) const noexcept;
/**
* Set size, capacity.
*/
Error setSize(std::size_t size) noexcept;
/**
* Get size, capacity.
* @return capacity
*/
[[nodiscard]]
constexpr size_t size() const noexcept;
/**
* @return the bytes still available in this NodeBuffer
*/
[[nodiscard]]
size_t available() const noexcept;
/**
* @return the actual number a bytes need to store the given number of
* bytes
*/
[[nodiscard]]
static size_t spaceNeeded(std::size_t size) noexcept;
template<typename F>
Error compact(F cb = [](uint64_t, ItemPtr) {}) noexcept;
private:
[[nodiscard]]
uint8_t *data() noexcept;
};
template<typename size_t, typename Item>
NodeBuffer<size_t, Item>::NodeBuffer(std::size_t size) noexcept {
m_header.size = static_cast<size_t>(size);
ox_memset(this + 1, 0, size - sizeof(*this));
oxTracef("ox.NodeBuffer.constructor", "{}", m_header.firstItem.get());
}
template<typename size_t, typename Item>
NodeBuffer<size_t, Item>::NodeBuffer(const NodeBuffer &other, std::size_t size) noexcept {
oxTracef("ox.ptrarith.NodeBuffer.copy", "other.m_header.firstItem: {}", other.m_header.firstItem.get());
ox_memset(this + 1, 0, size - sizeof(*this));
ox_memcpy(this, &other, size);
}
template<typename size_t, typename Item>
const typename NodeBuffer<size_t, Item>::Iterator NodeBuffer<size_t, Item>::iterator() const noexcept {
return Iterator(this, firstItem());
}
template<typename size_t, typename Item>
typename NodeBuffer<size_t, Item>::Iterator NodeBuffer<size_t, Item>::iterator() noexcept {
oxTracef("ox.ptrarith.NodeBuffer.iterator.size", "{}", m_header.size.get());
return Iterator(this, firstItem());
}
template<typename size_t, typename Item>
typename NodeBuffer<size_t, Item>::ItemPtr NodeBuffer<size_t, Item>::firstItem() noexcept {
//oxTrace("ox::ptrarith::NodeBuffer::firstItem") << m_header.firstItem;
return ptr(m_header.firstItem);
}
template<typename size_t, typename Item>
typename NodeBuffer<size_t, Item>::ItemPtr NodeBuffer<size_t, Item>::lastItem() noexcept {
auto first = ptr(m_header.firstItem);
if (first.valid()) {
return prev(first);
}
return nullptr;
}
template<typename size_t, typename Item>
template<typename T>
Ptr<T, size_t, sizeof(Item)> NodeBuffer<size_t, Item>::dataOf(ItemPtr ip) noexcept {
auto out = ip.template subPtr<T>(sizeof(Item));
oxAssert(out.size() == ip.size() - sizeof(Item), "Sub Ptr has invalid size.");
return out;
}
template<typename size_t, typename Item>
typename NodeBuffer<size_t, Item>::ItemPtr NodeBuffer<size_t, Item>::prev(Item *item) noexcept {
return ptr(item->prev);
}
template<typename size_t, typename Item>
typename NodeBuffer<size_t, Item>::ItemPtr NodeBuffer<size_t, Item>::next(Item *item) noexcept {
return ptr(item->next);
}
template<typename size_t, typename Item>
typename NodeBuffer<size_t, Item>::ItemPtr NodeBuffer<size_t, Item>::uninitializedPtr(size_t itemOffset) noexcept {
// make sure this can be read as an Item, and then use Item::size for the size
std::size_t itemSpace = m_header.size - itemOffset;
if (itemOffset >= sizeof(Header) &&
itemOffset + itemSpace <= size() &&
itemSpace >= sizeof(Item)) {
return ItemPtr(this, m_header.size, itemOffset, itemSpace);
} else {
//oxTrace("ox::ptrarith::NodeBuffer::ptr::null") << "itemOffset:" << itemOffset;
//oxTrace("ox::ptrarith::NodeBuffer::ptr::null") << "itemOffset >= sizeof(Header):" << (itemOffset >= sizeof(Header));
//oxTrace("ox::ptrarith::NodeBuffer::ptr::null") << "itemSpace >= sizeof(Item):" << (itemSpace >= sizeof(Item));
//oxTrace("ox::ptrarith::NodeBuffer::ptr::null") << "itemSpace >= item->fullSize():" << (itemSpace >= item->fullSize());
return ItemPtr(this, m_header.size, 0, 0);
}
}
template<typename size_t, typename Item>
typename NodeBuffer<size_t, Item>::ItemPtr NodeBuffer<size_t, Item>::ptr(size_t itemOffset) noexcept {
// make sure this can be read as an Item, and then use Item::size for the size
std::size_t itemSpace = m_header.size - itemOffset;
auto item = reinterpret_cast<Item*>(reinterpret_cast<uint8_t*>(this) + itemOffset);
if (itemOffset >= sizeof(Header) &&
itemOffset + itemSpace <= size() &&
itemSpace >= sizeof(Item) &&
itemSpace >= item->fullSize()) {
return ItemPtr(this, m_header.size, itemOffset, item->fullSize());
} else {
//oxTrace("ox::ptrarith::NodeBuffer::ptr::null") << "itemOffset:" << itemOffset;
//oxTrace("ox::ptrarith::NodeBuffer::ptr::null") << "itemOffset >= sizeof(Header):" << (itemOffset >= sizeof(Header));
//oxTrace("ox::ptrarith::NodeBuffer::ptr::null") << "itemSpace >= sizeof(Item):" << (itemSpace >= sizeof(Item));
//oxTrace("ox::ptrarith::NodeBuffer::ptr::null") << "itemSpace >= item->fullSize():" << (itemSpace >= item->fullSize());
return ItemPtr(this, m_header.size, 0, 0);
}
}
template<typename size_t, typename Item>
Result<typename NodeBuffer<size_t, Item>::ItemPtr> NodeBuffer<size_t, Item>::malloc(std::size_t size) noexcept {
const auto sz = static_cast<std::size_t>(size);
oxTracef("ox.ptrarith.NodeBuffer.malloc", "Size: {}", sz);
size_t fullSize = static_cast<size_t>(sz + sizeof(Item));
if (m_header.size - m_header.bytesUsed >= fullSize) {
auto last = lastItem();
size_t addr;
if (last.valid()) {
addr = last.offset() + last.size();
} else {
// there is no first item, so this must be the first item
if (!m_header.firstItem) {
oxTrace("ox.ptrarith.NodeBuffer.malloc", "No first item, initializing.");
m_header.firstItem = static_cast<size_t>(sizeof(m_header));
addr = m_header.firstItem;
} else {
oxTrace("ox.ptrarith.NodeBuffer.malloc.fail", "NodeBuffer is in invalid state.");
return OxError(1, "NodeBuffer is in invalid state.");
}
}
oxTracef("ox.ptrarith.NodeBuffer.malloc", "buffer size: {}; addr: {}; fullSize: {}", m_header.size.get(), addr, fullSize);
auto out = ItemPtr(this, m_header.size, addr, fullSize);
if (!out.valid()) {
oxTrace("ox.ptrarith.NodeBuffer.malloc.fail", "Unknown");
return OxError(1, "NodeBuffer::malloc: unknown failure");
}
ox_memset(out, 0, fullSize);
new (out) Item;
out->setSize(sz);
auto first = firstItem();
auto oldLast = last;
out->next = first.offset();
if (first.valid()) {
first->prev = out.offset();
} else {
oxTrace("ox.ptrarith.NodeBuffer.malloc.fail", "NodeBuffer malloc failed due to invalid first element pointer.");
return OxError(1, "NodeBuffer malloc failed due to invalid first element pointer.");
}
if (oldLast.valid()) {
out->prev = oldLast.offset();
oldLast->next = out.offset();
} else { // check to see if this is the first allocation
if (out.offset() != first.offset()) {
// if this is not the first allocation, there should be an oldLast
oxTrace("ox.ptrarith.NodeBuffer.malloc.fail", "NodeBuffer malloc failed due to invalid last element pointer.");
return OxError(1, "NodeBuffer malloc failed due to invalid last element pointer.");
}
out->prev = out.offset();
}
m_header.bytesUsed += out.size();
oxTracef("ox.ptrarith.NodeBuffer.malloc", "Offset: {}", out.offset());
return out;
}
oxTracef("ox.ptrarith.NodeBuffer.malloc.fail", "Insufficient space: {} needed, {} available", fullSize, available());
return OxError(1);
}
template<typename size_t, typename Item>
Error NodeBuffer<size_t, Item>::free(ItemPtr item) noexcept {
oxTracef("ox.ptrarith.NodeBuffer.free", "offset: {}", item.offset());
auto prev = this->prev(item);
auto next = this->next(item);
if (prev.valid() && next.valid()) {
if (next != item) {
prev->next = next;
next->prev = prev;
if (item.offset() == m_header.firstItem) {
m_header.firstItem = next;
}
} else {
// only one item, null out first
oxTrace("ox.ptrarith.NodeBuffer.free", "Nulling out firstItem.");
m_header.firstItem = 0;
}
} else {
if (!prev.valid()) {
oxTracef("ox.ptrarith.NodeBuffer.free.fail", "NodeBuffer free failed due to invalid prev element pointer: {}", prev.offset());
return OxError(1);
}
if (!next.valid()) {
oxTracef("ox.ptrarith.NodeBuffer.free.fail", "NodeBuffer free failed due to invalid next element pointer: {}", next.offset());
return OxError(1);
}
}
m_header.bytesUsed -= item.size();
return OxError(0);
}
template<typename size_t, typename Item>
Error NodeBuffer<size_t, Item>::setSize(std::size_t size) noexcept {
oxTracef("ox.ptrarith.NodeBuffer.setSize", "{} to {}", m_header.size.get(), size);
auto last = lastItem();
auto end = last.valid() ? last.end() : sizeof(m_header);
oxTracef("ox.ptrarith.NodeBuffer.setSize", "end: {}", end);
if (end > size) {
// resizing to less than buffer size
return OxError(1);
} else {
m_header.size = static_cast<size_t>(size);
auto data = reinterpret_cast<uint8_t*>(this) + end;
ox_memset(data, 0, size - end);
return OxError(0);
}
}
template<typename size_t, typename Item>
constexpr size_t NodeBuffer<size_t, Item>::size() const noexcept {
return m_header.size;
}
template<typename size_t, typename Item>
bool NodeBuffer<size_t, Item>::valid(size_t maxSize) const noexcept {
return m_header.size <= maxSize;
}
template<typename size_t, typename Item>
size_t NodeBuffer<size_t, Item>::available() const noexcept {
return m_header.size - m_header.bytesUsed;
}
template<typename size_t, typename Item>
size_t NodeBuffer<size_t, Item>::spaceNeeded(std::size_t size) noexcept {
return static_cast<size_t>(sizeof(Item) + size);
}
template<typename size_t, typename Item>
template<typename F>
Error NodeBuffer<size_t, Item>::compact(F cb) noexcept {
auto src = firstItem();
auto dest = ptr(sizeof(*this));
while (dest.offset() <= src.offset()) {
if (!src.valid()) {
return OxError(1);
}
if (!dest.valid()) {
return OxError(2);
}
// move node
ox_memcpy(dest, src, src->fullSize());
oxReturnError(cb(src, dest));
// update surrounding nodes
auto prev = ptr(dest->prev);
if (prev.valid()) {
prev->next = dest;
}
auto next = ptr(dest->next);
if (next.valid()) {
next->prev = dest;
}
// update iterators
src = ptr(dest->next);
dest = uninitializedPtr(dest.offset() + dest->fullSize());
}
return OxError(0);
}
template<typename size_t, typename Item>
uint8_t *NodeBuffer<size_t, Item>::data() noexcept {
return reinterpret_cast<uint8_t*>(ptr(sizeof(*this)).get());
}
template<typename size_t>
struct OX_PACKED Item {
public:
LittleEndian<size_t> prev = 0;
LittleEndian<size_t> next = 0;
private:
LittleEndian<size_t> m_size = sizeof(Item);
public:
size_t size() const {
return m_size;
}
void setSize(std::size_t size) {
m_size = static_cast<size_t>(size);
}
};
}

258
deps/ox/src/ox/fs/ptrarith/ptr.hpp vendored Normal file
View File

@@ -0,0 +1,258 @@
/*
* Copyright 2015 - 2023 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <ox/std/std.hpp>
namespace ox::ptrarith {
template<typename T, typename size_t, size_t minOffset = 1>
class [[nodiscard]] Ptr {
private:
uint8_t *m_dataStart = nullptr;
size_t m_dataSize = 0;
size_t m_itemOffset = 0;
size_t m_itemSize = 0;
// this should be removed later on, but the excessive validation is
// desirable during during heavy development
mutable uint8_t m_validated = false;
public:
constexpr Ptr() noexcept = default;
constexpr Ptr(std::nullptr_t) noexcept;
constexpr Ptr(
void *pDataStart,
std::size_t pDataSize,
std::size_t pItemStart,
std::size_t pItemSize = sizeof(T),
std::size_t pItemTypeSize = sizeof(T),
bool pPrevalidated = false) noexcept;
[[nodiscard]]
constexpr bool valid() const noexcept;
constexpr size_t size() const noexcept;
constexpr size_t offset() const noexcept;
constexpr size_t end() noexcept;
constexpr const T *get() const noexcept;
constexpr T *get() noexcept;
constexpr const T *operator->() const noexcept;
constexpr T *operator->() noexcept;
constexpr operator const T*() const noexcept;
constexpr operator T*() noexcept;
constexpr const T &operator*() const noexcept;
constexpr T &operator*() noexcept;
constexpr operator size_t() const noexcept;
constexpr bool operator==(const Ptr<T, size_t, minOffset> &other) const noexcept;
constexpr bool operator!=(const Ptr<T, size_t, minOffset> &other) const noexcept;
template<typename SubT>
constexpr const Ptr<SubT, size_t, sizeof(T)> subPtr(size_t offset, size_t size) const noexcept;
template<typename SubT>
constexpr const Ptr<SubT, size_t, sizeof(T)> subPtr(size_t offset) const noexcept;
template<typename SubT>
constexpr Ptr<SubT, size_t, sizeof(T)> subPtr(size_t offset, size_t size) noexcept;
template<typename SubT>
constexpr Ptr<SubT, size_t, sizeof(T)> subPtr(size_t offset) noexcept;
template<typename SubT>
constexpr const Ptr<SubT, size_t, minOffset> to() const noexcept;
constexpr Result<Ptr<T, size_t, minOffset>> validate() const noexcept;
};
template<typename T, typename size_t, size_t minOffset>
constexpr Ptr<T, size_t, minOffset>::Ptr(std::nullptr_t) noexcept {
}
template<typename T, typename size_t, size_t minOffset>
constexpr Ptr<T, size_t, minOffset>::Ptr(
void *pDataStart,
std::size_t pDataSize,
std::size_t pItemStart,
std::size_t pItemSize,
std::size_t pItemTypeSize,
bool pPrevalidated) noexcept {
const auto dataSize = static_cast<size_t>(pDataSize);
const auto itemStart = static_cast<size_t>(pItemStart);
const auto itemSize = static_cast<size_t>(pItemSize);
const auto itemTypeSize = static_cast<size_t>(pItemTypeSize);
// do some sanity checks before assuming this is valid
if (itemSize >= itemTypeSize &&
pDataStart &&
itemStart >= minOffset &&
itemStart + itemSize <= dataSize) {
m_dataStart = reinterpret_cast<uint8_t*>(pDataStart);
m_dataSize = dataSize;
m_itemOffset = itemStart;
m_itemSize = itemSize;
m_validated = pPrevalidated;
}
}
template<typename T, typename size_t, size_t minOffset>
constexpr bool Ptr<T, size_t, minOffset>::valid() const noexcept {
m_validated = m_dataStart != nullptr;
return m_validated;
}
template<typename T, typename size_t, size_t minOffset>
constexpr size_t Ptr<T, size_t, minOffset>::size() const noexcept {
return m_itemSize;
}
template<typename T, typename size_t, size_t minOffset>
constexpr size_t Ptr<T, size_t, minOffset>::offset() const noexcept {
return m_itemOffset;
}
template<typename T, typename size_t, size_t minOffset>
constexpr size_t Ptr<T, size_t, minOffset>::end() noexcept {
return m_itemOffset + m_itemSize;
}
template<typename T, typename size_t, size_t minOffset>
constexpr const T *Ptr<T, size_t, minOffset>::get() const noexcept {
oxAssert(m_validated, "Unvalidated pointer access. (ox::fs::Ptr::get())");
oxAssert(valid(), "Invalid pointer access. (ox::fs::Ptr::get())");
return reinterpret_cast<T*>(m_dataStart + m_itemOffset);
}
template<typename T, typename size_t, size_t minOffset>
constexpr T *Ptr<T, size_t, minOffset>::get() noexcept {
oxAssert(m_validated, "Unvalidated pointer access. (ox::fs::Ptr::get())");
oxAssert(valid(), "Invalid pointer access. (ox::fs::Ptr::get())");
return reinterpret_cast<T*>(m_dataStart + m_itemOffset);
}
template<typename T, typename size_t, size_t minOffset>
constexpr const T *Ptr<T, size_t, minOffset>::operator->() const noexcept {
oxAssert(m_validated, "Unvalidated pointer access. (ox::fs::Ptr::operator->())");
oxAssert(valid(), "Invalid pointer access. (ox::fs::Ptr::operator->())");
return reinterpret_cast<T*>(m_dataStart + m_itemOffset);
}
template<typename T, typename size_t, size_t minOffset>
constexpr T *Ptr<T, size_t, minOffset>::operator->() noexcept {
oxAssert(m_validated, "Unvalidated pointer access. (ox::fs::Ptr::operator->())");
oxAssert(valid(), "Invalid pointer access. (ox::fs::Ptr::operator->())");
return reinterpret_cast<T*>(m_dataStart + m_itemOffset);
}
template<typename T, typename size_t, size_t minOffset>
constexpr Ptr<T, size_t, minOffset>::operator const T*() const noexcept {
oxAssert(m_validated, "Unvalidated pointer access. (ox::fs::Ptr::operator const T*())");
oxAssert(valid(), "Invalid pointer access. (ox::fs::Ptr::operator const T*())");
return reinterpret_cast<T*>(m_dataStart + m_itemOffset);
}
template<typename T, typename size_t, size_t minOffset>
constexpr Ptr<T, size_t, minOffset>::operator T*() noexcept {
oxAssert(m_validated, "Unvalidated pointer access. (ox::fs::Ptr::operator T*())");
oxAssert(valid(), "Invalid pointer access. (ox::fs::Ptr::operator T*())");
return reinterpret_cast<T*>(m_dataStart + m_itemOffset);
}
template<typename T, typename size_t, size_t minOffset>
constexpr const T &Ptr<T, size_t, minOffset>::operator*() const noexcept {
oxAssert(m_validated, "Unvalidated pointer dereference. (ox::fs::Ptr::operator*())");
oxAssert(valid(), "Invalid pointer dereference. (ox::fs::Ptr::operator*())");
return *reinterpret_cast<T*>(m_dataStart + m_itemOffset);
}
template<typename T, typename size_t, size_t minOffset>
constexpr T &Ptr<T, size_t, minOffset>::operator*() noexcept {
oxAssert(m_validated, "Unvalidated pointer dereference. (ox::fs::Ptr::operator*())");
oxAssert(valid(), "Invalid pointer dereference. (ox::fs::Ptr::operator*())");
return *reinterpret_cast<T*>(m_dataStart + m_itemOffset);
}
template<typename T, typename size_t, size_t minOffset>
constexpr Ptr<T, size_t, minOffset>::operator size_t() const noexcept {
if (m_dataStart && m_itemOffset) {
return m_itemOffset;
}
return 0;
}
template<typename T, typename size_t, size_t minOffset>
constexpr bool Ptr<T, size_t, minOffset>::operator==(const Ptr<T, size_t, minOffset> &other) const noexcept {
return m_dataStart == other.m_dataStart &&
m_itemOffset == other.m_itemOffset &&
m_itemSize == other.m_itemSize;
}
template<typename T, typename size_t, size_t minOffset>
constexpr bool Ptr<T, size_t, minOffset>::operator!=(const Ptr<T, size_t, minOffset> &other) const noexcept {
return m_dataStart != other.m_dataStart ||
m_itemOffset != other.m_itemOffset ||
m_itemSize != other.m_itemSize;
}
template<typename T, typename size_t, size_t minOffset>
template<typename SubT>
constexpr const Ptr<SubT, size_t, sizeof(T)> Ptr<T, size_t, minOffset>::subPtr(size_t offset, size_t size) const noexcept {
return Ptr<SubT, size_t, sizeof(T)>(get(), this->size(), offset, size);
}
template<typename T, typename size_t, size_t minOffset>
template<typename SubT>
constexpr const Ptr<SubT, size_t, sizeof(T)> Ptr<T, size_t, minOffset>::subPtr(size_t offset) const noexcept {
oxTracef("ox.fs.Ptr.subPtr", "{} {} {} {} {}", m_itemOffset, this->size(), offset, m_itemSize, (m_itemSize - offset));
return subPtr<SubT>(offset, m_itemSize - offset);
}
template<typename T, typename size_t, size_t minOffset>
template<typename SubT>
constexpr Ptr<SubT, size_t, sizeof(T)> Ptr<T, size_t, minOffset>::subPtr(size_t offset, size_t size) noexcept {
return Ptr<SubT, size_t, sizeof(T)>(get(), this->size(), offset, size);
}
template<typename T, typename size_t, size_t minOffset>
template<typename SubT>
constexpr Ptr<SubT, size_t, sizeof(T)> Ptr<T, size_t, minOffset>::subPtr(size_t offset) noexcept {
oxTracef("ox.fs.Ptr.subPtr", "{} {} {} {} {}", m_itemOffset, this->size(), offset, m_itemSize, (m_itemSize - offset));
return subPtr<SubT>(offset, m_itemSize - offset);
}
template<typename T, typename size_t, size_t minOffset>
template<typename SubT>
constexpr const Ptr<SubT, size_t, minOffset> Ptr<T, size_t, minOffset>::to() const noexcept {
return Ptr<SubT, size_t, minOffset>(m_dataStart, m_dataSize, m_itemOffset, m_itemSize);
}
template<typename T, typename size_t, size_t minOffset>
constexpr Result<Ptr<T, size_t, minOffset>> Ptr<T, size_t, minOffset>::validate() const noexcept {
if (valid()) {
return *this;
}
return OxError(1);
}
}

28
deps/ox/src/ox/fs/test/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,28 @@
add_executable(
FSTests
tests.cpp
)
target_link_libraries(
FSTests
OxFS
OxMetalClaw
)
add_test("[ox/fs] PtrArith::setSize" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/FSTests PtrArith::setSize)
add_test("[ox/fs] PathIterator::next1" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/FSTests PathIterator::next1)
add_test("[ox/fs] PathIterator::next2" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/FSTests PathIterator::next2)
add_test("[ox/fs] PathIterator::next3" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/FSTests PathIterator::next3)
add_test("[ox/fs] PathIterator::next4" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/FSTests PathIterator::next4)
add_test("[ox/fs] PathIterator::next5" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/FSTests PathIterator::next5)
add_test("[ox/fs] PathIterator::hasNext" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/FSTests PathIterator::hasNext)
add_test("[ox/fs] PathIterator::dirPath" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/FSTests PathIterator::dirPath)
add_test("[ox/fs] PathIterator::fileName" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/FSTests PathIterator::fileName)
add_test("[ox/fs] NodeBuffer::insert" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/FSTests "NodeBuffer::insert")
add_test("[ox/fs] FileStore::readWrite" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/FSTests "FileStore::readWrite")
add_test("[ox/fs] Directory" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/FSTests "Directory")
add_test("[ox/fs] FileSystem" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/FSTests "FileSystem")

251
deps/ox/src/ox/fs/test/tests.cpp vendored Normal file
View File

@@ -0,0 +1,251 @@
/*
* Copyright 2015 - 2022 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// make sure asserts are enabled for the test file
#undef NDEBUG
#include <functional>
#include <map>
#include <string_view>
#include <ox/std/std.hpp>
#include <ox/fs/ptrarith/nodebuffer.hpp>
#include <ox/fs/filestore/filestoretemplate.hpp>
#include <ox/fs/filesystem/filesystem.hpp>
template<typename T>
struct OX_PACKED NodeType: public ox::ptrarith::Item<T> {
public:
[[nodiscard]]
size_t fullSize() const {
return this->size() + sizeof(*this);
}
};
const std::map<ox::StringView, std::function<ox::Error(ox::StringView)>> tests = {
{
{
"PtrArith::setSize",
[](ox::StringView) {
using BuffPtr_t = uint32_t;
ox::Vector<char> buff(5 * ox::units::MB);
auto buffer = new (buff.data()) ox::ptrarith::NodeBuffer<BuffPtr_t, NodeType<BuffPtr_t>>(buff.size());
using String = ox::BString<6>;
auto a1 = buffer->malloc(sizeof(String)).value;
auto a2 = buffer->malloc(sizeof(String)).value;
oxAssert(a1.valid(), "Allocation 1 failed.");
oxAssert(a2.valid(), "Allocation 2 failed.");
auto s1Buff = buffer->dataOf<String>(a1);
auto s2Buff = buffer->dataOf<String>(a2);
oxAssert(s1Buff.valid(), "s1 allocation 1 failed.");
oxAssert(s2Buff.valid(), "s2 allocation 2 failed.");
auto &s1 = *new (s1Buff) String("asdf");
auto &s2 = *new (s2Buff) String("aoeu");
oxTrace("test") << "s1: " << s1.c_str();
oxTrace("test") << "s2: " << s2.c_str();
oxAssert(s1 == "asdf", "Allocation 1 not as expected.");
oxAssert(s2 == "aoeu", "Allocation 2 not as expected.");
oxAssert(buffer->free(a1), "Free failed.");
oxAssert(buffer->free(a2), "Free failed.");
oxAssert(buffer->setSize(buffer->size() - buffer->available()), "Resize failed.");
return OxError(0);
}
},
{
"PathIterator::next1",
[](ox::StringView) {
auto const path = ox::String("/usr/share/charset.gbag");
ox::PathIterator it(path.c_str(), path.len());
auto buff = static_cast<char*>(ox_alloca(path.len() + 1));
oxAssert(it.next(buff, path.len()) == 0 && ox_strcmp(buff, "usr") == 0, "PathIterator shows wrong next");
oxAssert(it.next(buff, path.len()) == 0 && ox_strcmp(buff, "share") == 0, "PathIterator shows wrong next");
oxAssert(it.next(buff, path.len()) == 0 && ox_strcmp(buff, "charset.gbag") == 0, "PathIterator shows wrong next");
return OxError(0);
}
},
{
"PathIterator::next2",
[](ox::StringView) {
auto const path = ox::String("/usr/share/");
ox::PathIterator it(path.c_str(), path.len());
auto buff = static_cast<char*>(ox_alloca(path.len() + 1));
oxAssert(it.next(buff, path.len()) == 0 && ox_strcmp(buff, "usr") == 0, "PathIterator shows wrong next");
oxAssert(it.next(buff, path.len()) == 0 && ox_strcmp(buff, "share") == 0, "PathIterator shows wrong next");
return OxError(0);
}
},
{
"PathIterator::next3",
[](ox::StringView) {
auto const path = ox::String("/");
ox::PathIterator it(path.c_str(), path.len());
auto buff = static_cast<char*>(ox_alloca(path.len() + 1));
oxAssert(it.next(buff, path.len()) == 0 && ox_strcmp(buff, "\0") == 0, "PathIterator shows wrong next");
return OxError(0);
}
},
{
"PathIterator::next4",
[](ox::StringView) {
auto const path = ox::String("usr/share/charset.gbag");
ox::PathIterator it(path.c_str(), path.len());
auto buff = static_cast<char*>(ox_alloca(path.len() + 1));
oxAssert(it.next(buff, path.len()) == 0 && ox_strcmp(buff, "usr") == 0, "PathIterator shows wrong next");
oxAssert(it.next(buff, path.len()) == 0 && ox_strcmp(buff, "share") == 0, "PathIterator shows wrong next");
oxAssert(it.next(buff, path.len()) == 0 && ox_strcmp(buff, "charset.gbag") == 0, "PathIterator shows wrong next");
return OxError(0);
}
},
{
"PathIterator::next5",
[](ox::StringView) {
auto const path = ox::String("usr/share/");
ox::PathIterator it(path.c_str(), path.len());
auto buff = static_cast<char*>(ox_alloca(path.len() + 1));
oxAssert(it.next(buff, path.len()) == 0 && ox_strcmp(buff, "usr") == 0, "PathIterator shows wrong next");
oxAssert(it.next(buff, path.len()) == 0 && ox_strcmp(buff, "share") == 0, "PathIterator shows wrong next");
return OxError(0);
}
},
{
"PathIterator::dirPath",
[] (ox::StringView) {
auto const path = ox::String("/usr/share/charset.gbag");
ox::PathIterator it(path.c_str(), path.len());
auto buff = static_cast<char*>(ox_alloca(path.len() + 1));
oxAssert(it.dirPath(buff, path.len()) == 0 && ox_strcmp(buff, "/usr/share/") == 0, "PathIterator shows incorrect dir path");
return OxError(0);
}
},
{
"PathIterator::fileName",
[](ox::StringView) {
auto const path = ox::String("/usr/share/charset.gbag");
ox::PathIterator it(path.c_str(), path.len());
auto buff = static_cast<char*>(ox_alloca(path.len() + 1));
oxAssert(it.fileName(buff, path.len()) == 0 && ox_strcmp(buff, "charset.gbag") == 0, "PathIterator shows incorrect file name");
return OxError(0);
}
},
{
"PathIterator::hasNext",
[](ox::StringView) {
const auto path = "/file1";
ox::PathIterator it(path, ox_strlen(path));
oxAssert(it.hasNext(), "PathIterator shows incorrect hasNext");
oxAssert(!it.next().hasNext(), "PathIterator shows incorrect hasNext");
return OxError(0);
}
},
{
"Ptr::subPtr",
[](ox::StringView) {
constexpr auto buffLen = 5000;
ox::ptrarith::Ptr<uint8_t, uint32_t> p(ox_alloca(buffLen), buffLen, 500, 500);
oxAssert(p.valid(), "Ptr::subPtr: Ptr p is invalid.");
auto subPtr = p.subPtr<uint64_t>(50);
oxAssert(subPtr.valid(), "Ptr::subPtr: Ptr subPtr is invalid.");
return OxError(0);
}
},
{
"NodeBuffer::insert",
[](ox::StringView) {
constexpr auto buffLen = 5000;
auto list = new (ox_alloca(buffLen)) ox::ptrarith::NodeBuffer<uint32_t, ox::FileStoreItem<uint32_t>>(buffLen);
oxAssert(list->malloc(50).value.valid(), "NodeBuffer::insert: malloc 1 failed");
oxAssert(list->malloc(50).value.valid(), "NodeBuffer::insert: malloc 2 failed");
auto first = list->firstItem();
oxAssert(first.valid(), "NodeBuffer::insert: Could not access first item");
oxAssert(first->size() == 50, "NodeBuffer::insert: First item size invalid");
return OxError(0);
}
},
{
"FileStore::readWrite",
[](ox::StringView) {
constexpr auto buffLen = 5000;
constexpr auto str1 = "Hello, World!";
constexpr auto str1Len = ox_strlen(str1) + 1;
constexpr auto str2 = "Hello, Moon!";
constexpr auto str2Len = ox_strlen(str2) + 1;
auto list = new (ox_alloca(buffLen)) ox::ptrarith::NodeBuffer<uint32_t, ox::FileStoreItem<uint32_t>>(buffLen);
oxAssert(ox::FileStore32::format(list, buffLen), "FileStore::format failed.");
ox::FileStore32 fileStore(list, buffLen);
oxAssert(fileStore.write(4, const_cast<char*>(str1), str1Len, 1), "FileStore::write 1 failed.");
oxAssert(fileStore.write(5, const_cast<char*>(str2), str2Len, 1), "FileStore::write 2 failed.");
char str1Read[str1Len];
size_t str1ReadSize = 0;
oxAssert(fileStore.read(4, reinterpret_cast<void*>(str1Read), str1Len, &str1ReadSize), "FileStore::read 1 failed.");
return OxError(0);
}
},
{
"Directory",
[](ox::StringView) {
ox::Vector<uint8_t> fsBuff(5000);
oxAssert(ox::FileStore32::format(fsBuff.data(), fsBuff.size()), "FS format failed");
ox::FileStore32 fileStore(fsBuff.data(), fsBuff.size());
ox::Directory32 dir(fileStore, 105);
oxTrace("ox.fs.test.Directory") << "Init";
oxAssert(dir.init(), "Init failed");
oxTrace("ox.fs.test.Directory") << "write 1";
oxAssert(dir.write("/file1", 1), "Directory write of file1 failed");
oxTrace("ox.fs.test.Directory") << "find";
oxAssert(dir.find("file1").error, "Could not find file1");
oxAssert(dir.find("file1").value == 1, "Could not find file1");
oxTrace("ox.fs.test.Directory") << "write 2";
oxAssert(dir.write("/file3", 3), "Directory write of file3 failed");
oxTrace("ox.fs.test.Directory") << "write 3";
oxAssert(dir.write("/file2", 2), "Directory write of file2 failed");
return OxError(0);
}
},
{
"FileSystem",
[](ox::StringView) {
ox::Vector<uint8_t> fsBuff(5000);
oxTrace("ox.fs.test.FileSystem") << "format";
oxAssert(ox::FileSystem32::format(fsBuff.data(), fsBuff.size()), "FileSystem format failed");
ox::FileSystem32 fs(ox::FileStore32(fsBuff.data(), fsBuff.size()));
oxTrace("ox.fs.test.FileSystem") << "mkdir";
oxAssert(fs.mkdir("/dir", true), "mkdir failed");
oxAssert(fs.stat("/dir").error, "mkdir failed");
oxAssert(fs.mkdir("/l1d1/l2d1/l3d1", true), "mkdir failed");
oxAssert(fs.stat("/l1d1/l2d1/l3d1").error, "mkdir failed");
oxAssert(fs.mkdir("/l1d1/l2d2", true), "mkdir failed");
oxAssert(fs.stat("/l1d1/l2d2").error, "mkdir failed");
return OxError(0);
}
},
},
};
int main(int argc, const char **args) {
if (argc < 3) {
oxError("Must specify test to run and test argument");
}
ox::StringView const testName = args[1];
ox::StringView const testArg = args[2] ? args[2] : nullptr;
auto const func = tests.find(testName);
if (func != tests.end()) {
oxAssert(func->second(testArg), "Test returned Error");
return 0;
}
return -1;
}

84
deps/ox/src/ox/fs/tool.cpp vendored Normal file
View File

@@ -0,0 +1,84 @@
/*
* Copyright 2015 - 2023 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <cstdio>
#include <fstream>
#include <ox/fs/fs.hpp>
struct Buff {
char *data = nullptr;
std::size_t size = 0;
};
static ox::Result<Buff> loadFsBuff(const char *path) noexcept {
std::ifstream file(path, std::ios::binary | std::ios::ate);
if (!file.good()) {
oxErrorf("Could not find OxFS file: {}", path);
return OxError(1, "Could not find OxFS file");
}
try {
const auto size = static_cast<std::size_t>(file.tellg());
file.seekg(0, std::ios::beg);
const auto buff = new char[size];
file.read(buff, static_cast<std::streamsize>(size));
return Buff{buff, size};
} catch (const std::ios_base::failure &e) {
oxErrorf("Could not read OxFS file: {}", e.what());
return OxError(2, "Could not read OxFS file");
}
}
static ox::Result<ox::UniquePtr<ox::FileSystem>> loadFs(const char *path) noexcept {
oxRequire(buff, loadFsBuff(path));
return {ox::make_unique<ox::FileSystem32>(buff.data, buff.size)};
}
static ox::Error runLs(ox::FileSystem *fs, int argc, const char **argv) noexcept {
if (argc < 2) {
oxErr("Must provide a directory to ls\n");
return OxError(1);
}
oxRequire(files, fs->ls(argv[1]));
for (const auto &file : files) {
oxOutf("{}\n", file);
}
return OxError(0);
}
static ox::Error runRead(ox::FileSystem *fs, int argc, const char **argv) noexcept {
if (argc < 2) {
oxErr("Must provide a path to a file to read\n");
return OxError(1);
}
oxRequire(buff, fs->read(ox::StringView(argv[1])));
fwrite(buff.data(), sizeof(decltype(buff)::value_type), buff.size(), stdout);
return OxError(0);
}
static ox::Error run(int argc, const char **argv) noexcept {
if (argc < 3) {
oxErr("OxFS file and subcommand arguments are required\n");
return OxError(1);
}
const auto fsPath = argv[1];
ox::String subCmd(argv[2]);
oxRequire(fs, loadFs(fsPath));
if (subCmd == "ls") {
return runLs(fs.get(), argc - 2, argv + 2);
} else if (subCmd == "read") {
return runRead(fs.get(), argc - 2, argv + 2);
}
return OxError(1);
}
int main(int argc, const char **argv) {
auto err = run(argc, argv);
oxAssert(err, "unhandled error");
return static_cast<int>(err);
}

40
deps/ox/src/ox/logconn/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,40 @@
cmake_minimum_required(VERSION 3.10)
add_library(
OxLogConn
logconn.cpp
)
set_property(
TARGET
OxLogConn
PROPERTY
POSITION_INDEPENDENT_CODE ON
)
if(NOT MSVC)
target_compile_options(OxLogConn PRIVATE -Wsign-conversion)
endif()
target_link_libraries(
OxLogConn PUBLIC
OxStd
OxMetalClaw
$<$<BOOL:${OX_OS_FREEBSD}>:pthread>
$<$<BOOL:${OX_OS_WINDOWS}>:ws2_32>
)
install(
FILES
circularbuff.hpp
logconn.hpp
DESTINATION
include/ox/logconn
)
install(
TARGETS
OxLogConn
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)

99
deps/ox/src/ox/logconn/circularbuff.hpp vendored Normal file
View File

@@ -0,0 +1,99 @@
/*
* Copyright 2015 - 2022 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/buffer.hpp>
#include <ox/std/units.hpp>
namespace ox::detail {
class CirculerBuffer {
private:
std::size_t m_readPt = 0;
std::size_t m_writePt = 0;
ox::Buffer m_buff = ox::Buffer(ox::units::MB);
private:
[[nodiscard]]
constexpr auto avail() const noexcept {
if (m_writePt >= m_readPt) {
return m_buff.size() - (m_writePt - m_readPt);
} else {
return (m_buff.size() - m_writePt) - (m_buff.size() - m_readPt);
}
}
public:
constexpr ox::Error put(char v) noexcept {
return write(&v, 1);
if (1 > avail()) {
return OxError(1, "Insufficient space in buffer");
}
m_buff[m_writePt] = v;
++m_writePt;
return {};
}
constexpr ox::Error write(const char *buff, std::size_t sz) noexcept {
if (sz > avail()) {
return OxError(1, "Insufficient space in buffer");
}
// write seg 1
const auto seg1Sz = ox::min(sz, m_buff.size() - m_writePt);
ox_memcpy(&m_buff[m_writePt], &buff[0], seg1Sz);
m_writePt += sz;
if (seg1Sz != sz) {
m_writePt -= m_buff.size();
// write seg 2
const auto seg2Sz = sz - seg1Sz;
ox_memcpy(&m_buff[0], &buff[seg1Sz], seg2Sz);
oxAssert(m_buff[0] == buff[seg1Sz], "break");
}
return {};
}
constexpr ox::Error seekp(std::size_t bytesFwd) noexcept {
if (bytesFwd > avail()) {
return OxError(1, "Insufficient space in buffer to seek that far ahead");
}
m_writePt += bytesFwd;
if (m_writePt > m_buff.size()) {
m_writePt -= m_buff.size();
}
return {};
}
constexpr ox::Error seekp(int, ios_base::seekdir) {
return OxError(1, "Unimplemented");
}
[[nodiscard]]
constexpr std::size_t tellp() const noexcept {
return m_buff.size() - avail();
}
[[nodiscard]]
constexpr std::size_t read(char *out, std::size_t outSize) noexcept {
const auto bytesRead = ox::min(outSize, m_buff.size() - avail());
// read seg 1
const auto seg1Sz = ox::min(bytesRead, m_buff.size() - m_readPt);
ox_memcpy(&out[0], &m_buff[m_readPt], seg1Sz);
m_readPt += bytesRead;
if (seg1Sz != bytesRead) {
m_readPt -= m_buff.size();
// read seg 2
const auto seg2Sz = bytesRead - seg1Sz;
ox_memcpy(&out[seg1Sz], &m_buff[0], seg2Sz);
}
return bytesRead;
}
};
}

27
deps/ox/src/ox/logconn/def.hpp vendored Normal file
View File

@@ -0,0 +1,27 @@
/*
* Copyright 2015 - 2023 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 defined(DEBUG) && !defined(OX_BARE_METAL)
#define OX_INIT_DEBUG_LOGGER(loggerName, appName) \
ox::LoggerConn loggerName; \
{ \
const auto loggerErr = (loggerName).initConn(appName); \
if (loggerErr) { \
oxErrf("Could not connect to logger: {} ({}:{})\n", toStr(loggerErr), loggerErr.file, loggerErr.line); \
} else { \
ox::trace::setLogger(&(loggerName)); \
} \
ox::trace::init(); \
}
#else
#define OX_INIT_DEBUG_LOGGER(loggerName, appName)
#endif

107
deps/ox/src/ox/logconn/logconn.cpp vendored Normal file
View File

@@ -0,0 +1,107 @@
/*
* Copyright 2015 - 2023 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/.
*/
#ifdef OX_USE_STDLIB
#include <cstdio>
#include <sys/types.h>
#ifndef _WIN32
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#else
#include <winsock.h>
#undef interface
#undef min
#undef max
#endif
#include "logconn.hpp"
#include <ox/std/bit.hpp>
namespace ox {
using namespace trace;
void closeSock(auto s) noexcept {
#ifdef _WIN32
closesocket(s);
#else
close(s);
#endif
}
LoggerConn::LoggerConn() noexcept: m_netThread([this]{this->msgSend();}) {
}
LoggerConn::~LoggerConn() noexcept {
m_running = false;
m_waitCond.notify_one();
m_netThread.join();
if (m_socket) {
closeSock(m_socket);
}
}
ox::Error LoggerConn::initConn(ox::CRStringView appName) noexcept {
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
addr.sin_port = htons(5590);
m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
oxReturnError(OxError(static_cast<ox::ErrorCode>(connect(m_socket, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)))));
return sendInit({.appName = ox::BasicString<128>(appName)});
}
ox::Error LoggerConn::send(const char *buff, std::size_t len) const noexcept {
std::size_t totalSent = 0;
while (totalSent < len) {
//std::fprintf(stdout, "Sending %lu/%lu bytes on socket %d\n", len, totalSent, m_socket);
const auto sent = ::send(m_socket, buff, len, 0);
if (sent < 0) {
std::fprintf(stderr, "Could not send msg\n");
return OxError(1, "Could not send msg");
}
totalSent += static_cast<std::size_t>(sent);
}
return {};
}
ox::Error LoggerConn::send(const TraceMsg &msg) noexcept {
return send(MsgId::TraceEvent, msg);
}
ox::Error LoggerConn::sendInit(const InitTraceMsg &msg) noexcept {
return send(MsgId::Init, msg);
}
void LoggerConn::msgSend() noexcept {
while (true) {
std::unique_lock lk(m_waitMut);
m_waitCond.wait(lk);
if (!m_running) {
break;
}
std::lock_guard buffLk(m_buffMut);
while (true) {
ox::Array<char, ox::units::KB> tmp;
const auto read = m_buff.read(tmp.data(), tmp.size());
if (!read) {
break;
}
//std::printf("LoggerConn: sending %lu bytes\n", read);
oxIgnoreError(send(tmp.data(), read));
}
}
}
}
#endif

78
deps/ox/src/ox/logconn/logconn.hpp vendored Normal file
View File

@@ -0,0 +1,78 @@
/*
* Copyright 2015 - 2022 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
#ifdef OX_USE_STDLIB
#include <condition_variable>
#include <mutex>
#include <thread>
#endif
#include <ox/mc/write.hpp>
#include <ox/std/trace.hpp>
#include "circularbuff.hpp"
namespace ox {
#ifdef OX_USE_STDLIB
class LoggerConn: public trace::Logger {
private:
int m_socket = 0;
detail::CirculerBuffer m_buff;
std::thread m_netThread;
std::condition_variable m_waitCond;
std::mutex m_waitMut;
std::mutex m_buffMut;
bool m_running = true;
public:
LoggerConn() noexcept;
LoggerConn(const LoggerConn&) noexcept = delete;
~LoggerConn() noexcept override;
LoggerConn &operator=(const LoggerConn&) noexcept = delete;
ox::Error send(const trace::TraceMsg&) noexcept final;
ox::Error sendInit(const trace::InitTraceMsg&) noexcept final;
ox::Error initConn(ox::CRStringView appName) noexcept;
ox::Error send(const char *buff, std::size_t len) const noexcept;
private:
void msgSend() noexcept;
ox::Error send(trace::MsgId msgId, const auto &msg) noexcept {
ox::Array<char, 10 * ox::units::KB> buff;
std::size_t sz = 0;
oxReturnError(ox::writeMC(&buff[0], buff.size(), msg, &sz));
//std::printf("sz: %lu\n", sz);
oxRequire(szBuff, serialize(static_cast<uint32_t>(sz)));
std::unique_lock buffLk(m_buffMut);
oxReturnError(m_buff.put(static_cast<char>(msgId)));
oxReturnError(m_buff.write(szBuff.data(), szBuff.size()));
oxReturnError(m_buff.write(buff.data(), sz));
buffLk.unlock();
m_waitCond.notify_one();
return {};
}
};
#else
class LoggerConn: public trace::Logger {
private:
public:
constexpr LoggerConn() noexcept = default;
LoggerConn(const LoggerConn&) noexcept = delete;
constexpr ~LoggerConn() noexcept override = default;
LoggerConn &operator=(const LoggerConn&) noexcept = delete;
ox::Error send(const trace::TraceMsg&) noexcept final { return {}; }
static ox::Error initConn() noexcept { return {}; }
static ox::Error send(const char*, std::size_t) noexcept { return {}; }
};
#endif
}

48
deps/ox/src/ox/mc/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,48 @@
add_library(
OxMetalClaw
presenceindicator.cpp
read.cpp
write.cpp
)
if(NOT MSVC)
target_compile_options(OxMetalClaw PRIVATE -Wsign-conversion)
target_compile_options(OxMetalClaw PRIVATE -Wconversion)
endif()
target_link_libraries(
OxMetalClaw PUBLIC
OxModel
OxStd
)
if(NOT OX_BARE_METAL)
set_property(
TARGET
OxMetalClaw
PROPERTY
POSITION_INDEPENDENT_CODE ON
)
endif()
install(
FILES
intops.hpp
err.hpp
mc.hpp
presenceindicator.hpp
read.hpp
types.hpp
write.hpp
DESTINATION
include/ox/mc
)
install(TARGETS OxMetalClaw
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
if(OX_RUN_TESTS)
add_subdirectory(test)
endif()

19
deps/ox/src/ox/mc/err.hpp vendored Normal file
View File

@@ -0,0 +1,19 @@
/*
* Copyright 2015 - 2022 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
namespace ox {
enum {
McPresenceMapOverflow = 1,
McBuffEnded = 2,
McOutputBuffEnded = 4
};
}

192
deps/ox/src/ox/mc/intops.hpp vendored Normal file
View File

@@ -0,0 +1,192 @@
/*
* Copyright 2015 - 2022 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <ox/std/array.hpp>
#include <ox/std/assert.hpp>
#include <ox/std/bit.hpp>
#include <ox/std/byteswap.hpp>
#include <ox/std/math.hpp>
#include <ox/std/memops.hpp>
#include <ox/std/reader.hpp>
namespace ox::mc {
template<typename T>
static constexpr auto Bits = sizeof(T) << 3;
/**
* Returns highest bit other than possible signed bit.
* Bit numbering starts at 0.
*/
template<typename I>
[[nodiscard]]
constexpr std::size_t highestBit(I val) noexcept {
unsigned shiftStart = sizeof(I) * 8 - 1;
// find most significant non-sign indicator bit
std::size_t highestBit = 0;
// start at one bit lower if signed
if constexpr(is_signed_v<I>) {
--shiftStart;
}
for (auto i = shiftStart; i > 0; --i) {
const auto bitValue = (val >> i) & 1;
if (bitValue) {
highestBit = i;
break;
}
}
return highestBit;
}
static_assert(highestBit(int8_t(0b10000000)) == 0);
static_assert(highestBit(~static_cast<int8_t>(-1)) == 0);
static_assert(highestBit(~static_cast<int8_t>(-2)) == 0);
static_assert(highestBit(~static_cast<int8_t>(-3)) == 1);
static_assert(highestBit(1) == 0);
static_assert(highestBit(2) == 1);
static_assert(highestBit(4) == 2);
static_assert(highestBit(8) == 3);
static_assert(highestBit(uint64_t(1) << 31) == 31);
static_assert(highestBit(uint64_t(1) << 63) == 63);
struct McInt {
uint8_t data[9] = {};
// length of integer in bytes
std::size_t length = 0;
};
template<typename I>
[[nodiscard]]
constexpr McInt encodeInteger(I pInput) noexcept {
auto const input = ox::ResizedInt_t<I, 64>{pInput};
McInt out;
const auto inputNegative = is_signed_v<I> && input < 0;
// move input to uint64_t to allow consistent bit manipulation, and to avoid
// overflow concerns
uint64_t val = 0;
ox_memcpy(&val, &input, sizeof(input));
if (val) {
// bits needed to represent number factoring in space possibly
// needed for signed bit
const auto highBit = inputNegative ? highestBit(~val) : highestBit(val);
const auto bits = highBit + 1 + (is_signed_v<I> ? 1 : 0);
// bytes needed to store value
std::size_t bytes = bits / 8 + (bits % 8 != 0);
const auto bitsAvailable = bytes * 8; // bits available to integer value
const auto bitsNeeded = bits + bytes;
// factor in bits needed for bytesIndicator (does not affect bytesIndicator)
// bits for integer + bits needed to represent bytes > bits available
if (bitsNeeded > bitsAvailable && bytes != 9) {
++bytes;
}
const auto bytesIndicator = onMask<uint8_t>(bytes - 1);
// ensure we are copying from little endian representation
LittleEndian<uint64_t> leVal = val;
if (inputNegative) {
leVal |= static_cast<uint64_t>(1 << (bitsNeeded - 1));
}
if (bytes == 9) {
out.data[0] = bytesIndicator;
ox_memcpy(&out.data[1], &leVal, 8);
if (inputNegative) {
out.data[1] |= 0b1000'0000;
}
} else {
const auto valBits = bytes * 8;
uint64_t negBit = inputNegative ? 1 : 0;
auto intermediate =
static_cast<uint64_t>(leVal.raw() | (negBit << (valBits - 1))) << bytes |
static_cast<uint64_t>(bytesIndicator);
ox_memcpy(out.data, &intermediate, sizeof(intermediate));
}
out.length = bytes;
}
return out;
}
/**
* Returns the number of bytes indicated by the bytes indicator of a variable
* length integer.
*/
[[nodiscard]]
constexpr std::size_t countBytes(unsigned b) noexcept {
std::size_t i = 0;
while ((b >> i) & 1) ++i;
return i + 1;
}
static_assert(countBytes(0b0000'0000) == 1);
static_assert(countBytes(0b0000'0001) == 2);
static_assert(countBytes(0b0000'0011) == 3);
static_assert(countBytes(0b0000'0111) == 4);
static_assert(countBytes(0b0000'1111) == 5);
static_assert(countBytes(0b0001'1111) == 6);
static_assert(countBytes(0b0011'1111) == 7);
static_assert(countBytes(0b0111'1111) == 8);
static_assert(countBytes(0b1111'1111) == 9);
template<typename I>
constexpr Result<I> decodeInteger(Reader_c auto&rdr, std::size_t *bytesRead) noexcept {
uint8_t firstByte = 0;
oxReturnError(rdr.read(&firstByte, 1));
oxReturnError(rdr.seekg(-1, ox::ios_base::cur));
const auto bytes = countBytes(firstByte);
if (bytes == 9) {
*bytesRead = bytes;
I out = 0;
oxReturnError(rdr.seekg(1, ox::ios_base::cur));
oxReturnError(rdr.read(&out, sizeof(I)));
return fromLittleEndian<I>(out);
}
*bytesRead = bytes;
uint64_t decoded = 0;
oxReturnError(rdr.read(&decoded, bytes));
decoded >>= bytes;
// move sign bit
if constexpr(is_signed_v<I>) {
const auto negBit = bytes * 8 - bytes - 1;
// move sign
const auto negative = (decoded >> negBit) == 1;
if (negative) {
// fill in all bits between encoded sign and real sign with 1s
// split it up because the 32-bit ARM can't shift more than 32 bits
ox::Array<uint32_t, 2> d = {};
//d[0] = decoded & 0xffff'ffff;
//d[1] = decoded >> 32;
ox_memcpy(d.data(), &decoded, sizeof(decoded));
auto bit = negBit;
for (; bit < ox::min<std::size_t>(Bits<I>, 32); ++bit) {
d[0] |= 1 << bit;
}
bit -= 32;
for (; bit < Bits<I>; ++bit) {
d[1] |= 1 << bit;
}
I out = 0;
if constexpr(ox::defines::BigEndian) {
const auto d0Tmp = d[0];
d[0] = d[1];
d[1] = d0Tmp;
}
ox_memcpy(&out, d.data(), sizeof(out));
return out;
}
}
return static_cast<I>(decoded);
}
template<typename I>
Result<I> decodeInteger(McInt m) noexcept {
std::size_t bytesRead{};
BufferReader br(reinterpret_cast<const char*>(m.data), 9);
return decodeInteger<I>(br, &bytesRead);
}
}

14
deps/ox/src/ox/mc/mc.hpp vendored Normal file
View File

@@ -0,0 +1,14 @@
/*
* Copyright 2015 - 2022 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "intops.hpp"
#include "read.hpp"
#include "types.hpp"
#include "write.hpp"

17
deps/ox/src/ox/mc/presenceindicator.cpp vendored Normal file
View File

@@ -0,0 +1,17 @@
/*
* Copyright 2015 - 2022 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include "err.hpp"
#include "presenceindicator.hpp"
namespace ox {
template class FieldBitmapWriterBase<uint8_t*>;
template class FieldBitmapWriterBase<const uint8_t*>;
}

161
deps/ox/src/ox/mc/presenceindicator.hpp vendored Normal file
View File

@@ -0,0 +1,161 @@
/*
* Copyright 2015 - 2023 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <ox/std/array.hpp>
#include <ox/std/bit.hpp>
#include <ox/std/error.hpp>
#include <ox/std/types.hpp>
#include <ox/std/reader.hpp>
#include "err.hpp"
namespace ox {
template<Reader_c Reader>
class FieldBitmapReader {
protected:
mutable std::size_t m_mapBlockIdx = ~std::size_t{0};
mutable uint64_t m_mapBlock = 0;
std::size_t m_mapStart = 0;
Reader &m_reader;
public:
explicit constexpr FieldBitmapReader(Reader &reader) noexcept;
constexpr Result<bool> get(std::size_t i) const noexcept;
private:
constexpr ox::Error loadMapBlock(std::size_t id) const noexcept;
};
template<Reader_c Reader>
constexpr FieldBitmapReader<Reader>::FieldBitmapReader(Reader &reader) noexcept:
m_mapStart(reader.tellg()),
m_reader(reader) {
}
template<Reader_c Reader>
constexpr Result<bool> FieldBitmapReader<Reader>::get(std::size_t idx) const noexcept {
constexpr auto blockBits = sizeof(m_mapBlock);
auto const blockIdx = idx / blockBits;
if (m_mapBlockIdx != blockIdx) [[unlikely]] {
oxReturnError(loadMapBlock(blockIdx));
}
idx %= blockBits;
return (m_mapBlock >> idx) & 1;
}
template<Reader_c Reader>
constexpr ox::Error FieldBitmapReader<Reader>::loadMapBlock(std::size_t idx) const noexcept {
oxRequire(g, m_reader.tellg());
oxReturnError(m_reader.seekg(static_cast<int>(m_mapStart + idx), ox::ios_base::beg));
ox::Array<char, sizeof(m_mapBlock)> mapBlock{};
oxReturnError(m_reader.read(mapBlock.data(), sizeof(m_mapBlock)));
// Warning: narrow-conv
oxReturnError(m_reader.seekg(static_cast<int>(g), ox::ios_base::beg));
m_mapBlock = 0;
for (auto i = 0ull; auto b : mapBlock) {
m_mapBlock |= static_cast<uint64_t>(std::bit_cast<uint8_t>(b)) << i;
i += 8;
}
m_mapBlockIdx = idx;
return {};
}
template<typename T>
class FieldBitmapWriterBase {
protected:
T m_map = nullptr;
std::size_t m_mapLen = 0;
public:
constexpr FieldBitmapWriterBase(T map, std::size_t maxLen) noexcept;
constexpr auto setBuffer(T map, std::size_t maxLen) noexcept;
constexpr Result<bool> get(std::size_t i) const noexcept;
constexpr void setFields(int) noexcept;
constexpr void setMaxLen(int) noexcept;
[[nodiscard]]
constexpr int64_t getMaxLen() const noexcept;
};
template<typename T>
constexpr FieldBitmapWriterBase<T>::FieldBitmapWriterBase(T map, std::size_t maxLen) noexcept {
m_map = map;
m_mapLen = maxLen;
}
template<typename T>
constexpr auto FieldBitmapWriterBase<T>::setBuffer(T map, std::size_t maxLen) noexcept {
m_map = map;
m_mapLen = maxLen;
}
template<typename T>
constexpr Result<bool> FieldBitmapWriterBase<T>::get(std::size_t i) const noexcept {
if (i / 8 < m_mapLen) {
return (m_map[i / 8] >> (i % 8)) & 1;
} else {
return OxError(McPresenceMapOverflow);
}
}
template<typename T>
constexpr void FieldBitmapWriterBase<T>::setFields(int fields) noexcept {
m_mapLen = static_cast<std::size_t>((fields / 8 + 1) - (fields % 8 == 0));
}
template<typename T>
constexpr void FieldBitmapWriterBase<T>::setMaxLen(int maxLen) noexcept {
m_mapLen = static_cast<std::size_t>(maxLen);
}
template<typename T>
constexpr int64_t FieldBitmapWriterBase<T>::getMaxLen() const noexcept {
return static_cast<int64_t>(m_mapLen);
}
extern template class FieldBitmapWriterBase<uint8_t*>;
extern template class FieldBitmapWriterBase<const uint8_t*>;
class FieldBitmap: public FieldBitmapWriterBase<uint8_t*> {
public:
constexpr FieldBitmap(uint8_t *map, std::size_t maxLen) noexcept;
constexpr Error set(std::size_t i, bool on) noexcept;
};
constexpr FieldBitmap::FieldBitmap(uint8_t *map, std::size_t maxLen) noexcept:
FieldBitmapWriterBase<uint8_t*>(map, maxLen) {
}
constexpr Error FieldBitmap::set(std::size_t i, bool on) noexcept {
if (i / 8 < m_mapLen) {
if (on) {
m_map[i / 8] |= 1 << (i % 8);
} else {
m_map[i / 8] &= ~static_cast<uint8_t>(1 << (i % 8));
}
return {};
} else {
return OxError(McPresenceMapOverflow);
}
}
}

19
deps/ox/src/ox/mc/read.cpp vendored Normal file
View File

@@ -0,0 +1,19 @@
/*
* Copyright 2015 - 2022 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/modelhandleradaptor.hpp>
#include <ox/std/buffer.hpp>
#include <ox/std/reader.hpp>
#include "read.hpp"
namespace ox {
template class ModelHandlerInterface<MetalClawReaderTemplate<BufferReader>>;
}

548
deps/ox/src/ox/mc/read.hpp vendored Normal file
View File

@@ -0,0 +1,548 @@
/*
* Copyright 2015 - 2023 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/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/optional.hpp>
#include <ox/std/string.hpp>
#include <ox/std/trace.hpp>
#include <ox/std/vector.hpp>
#include "err.hpp"
#include "intops.hpp"
#include "presenceindicator.hpp"
#include "types.hpp"
namespace ox {
template<Reader_c Reader>
class MetalClawReaderTemplate: public ModelHandlerBase<MetalClawReaderTemplate<Reader>, ox::OpType::Read> {
private:
FieldBitmapReader<Reader> m_fieldPresence;
std::size_t m_fields = 0;
std::size_t m_field = 0;
ox::Optional<int> m_unionIdx;
Reader &m_reader;
public:
explicit constexpr MetalClawReaderTemplate(
Reader &reader,
ox::Optional<int> const&unionIdx = {}) noexcept;
constexpr ~MetalClawReaderTemplate() noexcept;
constexpr Error field(const char*, int8_t *val) noexcept;
constexpr Error field(const char*, int16_t *val) noexcept;
constexpr Error field(const char*, int32_t *val) noexcept;
constexpr Error field(const char*, int64_t *val) noexcept;
constexpr Error field(const char*, uint8_t *val) noexcept;
constexpr Error field(const char*, uint16_t *val) noexcept;
constexpr Error field(const char*, uint32_t *val) noexcept;
constexpr Error field(const char*, uint64_t *val) noexcept;
constexpr Error field(const char*, bool *val) noexcept;
// array handler
constexpr Error field(const char*, auto *val, std::size_t len) noexcept;
// map handler
template<typename T>
constexpr Error field(const char*, HashMap<String, T> *val) noexcept;
// array handler, with callback to allow handling individual elements
template<typename T, typename CB>
constexpr Error field(const char*, CB cb) noexcept;
template<typename T>
constexpr Error field(const char*, T *val) noexcept;
template<typename U, bool force>
constexpr Error field(const char*, UnionView<U, force> val) noexcept;
template<std::size_t SmallStringSize>
constexpr Error field(const char*, BasicString<SmallStringSize> *val) noexcept;
template<std::size_t L>
constexpr Error field(const char*, BString<L> *val) noexcept;
constexpr Error fieldCString(const char*, char *val, std::size_t buffLen) noexcept;
constexpr Error fieldCString(const char*, char **val) noexcept;
constexpr Error fieldCString(const char*, char **val, std::size_t buffLen) noexcept;
/**
* Reads an array length from the current location in the buffer.
* @param pass indicates that the parsing should iterate past the array length
*/
constexpr Result<ArrayLength> arrayLength(const char *name, bool pass = true) noexcept;
/**
* Reads an string length from the current location in the buffer.
*/
constexpr Result<StringLength> stringLength(const char *name) noexcept;
template<typename T = std::nullptr_t>
constexpr ox::Error setTypeInfo(
const char *name = T::TypeName,
int version = T::TypeVersion,
const Vector<String>& = {},
std::size_t fields = ModelFieldCount_v<T>) noexcept;
/**
* Returns a MetalClawReader to parse a child object.
*/
[[nodiscard]]
constexpr MetalClawReaderTemplate<Reader> child(const char *name, ox::Optional<int> unionIdx = {}) noexcept;
/**
* Indicates whether or not the next field to be read is present.
*/
[[nodiscard]]
constexpr bool fieldPresent(const char *name) const noexcept;
/**
* Indicates whether or not the given field is present.
*/
[[nodiscard]]
constexpr bool fieldPresent(int fieldNo) const noexcept;
[[nodiscard]]
constexpr int whichFieldPresent(const char *name, const ModelUnion&) const noexcept;
constexpr void nextField() noexcept;
private:
template<typename I>
constexpr Error readInteger(I *val) noexcept;
};
template<Reader_c Reader>
constexpr MetalClawReaderTemplate<Reader>::MetalClawReaderTemplate(
Reader &reader,
ox::Optional<int> const&unionIdx) noexcept:
m_fieldPresence(reader),
m_unionIdx(unionIdx),
m_reader(reader) {
}
template<Reader_c Reader>
constexpr MetalClawReaderTemplate<Reader>::~MetalClawReaderTemplate() noexcept {
if (m_field != m_fields) {
oxTrace("ox.mc.MetalClawReader.error") << "MetalClawReader: incorrect fields number given";
}
}
template<Reader_c Reader>
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, int8_t *val) noexcept {
return readInteger(val);
}
template<Reader_c Reader>
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, int16_t *val) noexcept {
return readInteger(val);
}
template<Reader_c Reader>
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, int32_t *val) noexcept {
return readInteger(val);
}
template<Reader_c Reader>
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, int64_t *val) noexcept {
return readInteger(val);
}
template<Reader_c Reader>
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, uint8_t *val) noexcept {
return readInteger(val);
}
template<Reader_c Reader>
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, uint16_t *val) noexcept {
return readInteger(val);
}
template<Reader_c Reader>
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, uint32_t *val) noexcept {
return readInteger(val);
}
template<Reader_c Reader>
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, uint64_t *val) noexcept {
return readInteger(val);
}
template<Reader_c Reader>
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, bool *val) noexcept {
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
auto const result = m_fieldPresence.get(static_cast<std::size_t>(m_field));
*val = result.value;
oxReturnError(result);
}
++m_field;
return OxError(0);
}
// array handler
template<Reader_c Reader>
constexpr Error MetalClawReaderTemplate<Reader>::field(const char *name, auto *val, std::size_t valLen) noexcept {
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
// read the length
std::size_t bytesRead = 0;
oxRequire(len, mc::decodeInteger<ArrayLength>(m_reader, &bytesRead));
// read the list
if (valLen >= len) {
auto reader = child({});
auto &handler = *reader.interface();
oxReturnError(handler.setTypeInfo("List", 0, {}, static_cast<std::size_t>(len)));
for (std::size_t i = 0; i < len; ++i) {
oxReturnError(handler.field({}, &val[i]));
}
} else {
oxTracef("ox.mc.read.field(T)", "{}, length: {}", name, valLen);
return OxError(McOutputBuffEnded);
}
}
}
++m_field;
return {};
}
template<Reader_c Reader>
template<typename T>
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, HashMap<String, T> *val) noexcept {
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
// read the length
oxRequire(g, m_reader.tellg());
std::size_t bytesRead = 0;
oxRequire(len, mc::decodeInteger<ArrayLength>(m_reader, &bytesRead));
oxReturnError(m_reader.seekg(g));
// read the list
auto reader = child("");
auto &handler = *reader.interface();
oxReturnError(handler.setTypeInfo("List", 0, {}, static_cast<std::size_t>(len)));
// this loop body needs to be in a lambda because of the potential alloca call
constexpr auto loopBody = [](auto &handler, auto &val) {
oxRequire(keyLen, handler.stringLength(nullptr));
auto wkey = ox_malloca(keyLen + 1, char, 0);
auto wkeyPtr = wkey.get();
oxReturnError(handler.fieldCString("", &wkeyPtr, keyLen + 1));
return handler.field("", &val[wkeyPtr]);
};
for (std::size_t i = 0; i < len; ++i) {
oxReturnError(loopBody(handler, *val));
}
}
}
++m_field;
return OxError(0);
}
template<Reader_c Reader>
template<typename T>
constexpr Error MetalClawReaderTemplate<Reader>::field(const char *name, T *val) noexcept {
if constexpr(isVector_v<T>) {
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
// set size of val if the field is present, don't worry about it if not
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
oxRequire(len, arrayLength(name, false));
oxReturnError(ox::resizeVector(*val, len));
return field(name, val->data(), val->size());
}
oxReturnError(ox::resizeVector(*val, 0));
}
++m_field;
return {};
} else if constexpr(isArray_v<T>) {
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
// set size of val if the field is present, don't worry about it if not
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
oxRequire(len, arrayLength(name, false));
if (len > val->size()) {
return OxError(1, "Input array is too long");
}
}
return field(name, val->data(), val->size());
}
++m_field;
return {};
} else {
if ((!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) && val) {
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
auto reader = child("");
oxReturnError(model(reader.interface(), val));
}
}
++m_field;
return {};
}
}
template<Reader_c Reader>
template<typename U, bool force>
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, UnionView<U, force> val) noexcept {
if ((!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) && val.get()) {
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
auto reader = child("", ox::Optional<int>(ox::in_place, val.idx()));
oxReturnError(model(reader.interface(), val.get()));
}
}
++m_field;
return {};
}
template<Reader_c Reader>
template<std::size_t SmallStringSize>
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, BasicString<SmallStringSize> *val) noexcept {
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
// read the length
std::size_t bytesRead = 0;
oxRequire(size, mc::decodeInteger<StringLength>(m_reader, &bytesRead));
const auto cap = size;
*val = BasicString<SmallStringSize>(cap);
auto data = val->data();
// read the string
if (static_cast<StringLength>(cap) < size) {
return OxError(McOutputBuffEnded);
}
oxReturnError(m_reader.read(data, size));
} else {
*val = "";
}
}
++m_field;
return OxError(0);
}
template<Reader_c Reader>
template<std::size_t L>
constexpr Error MetalClawReaderTemplate<Reader>::field(const char *name, BString<L> *val) noexcept {
return fieldCString(name, val->data(), val->cap());
}
template<Reader_c Reader>
constexpr Error MetalClawReaderTemplate<Reader>::fieldCString(const char*, char *val, std::size_t buffLen) noexcept {
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
// read the length
std::size_t bytesRead = 0;
oxRequire(size, mc::decodeInteger<StringLength>(m_reader, &bytesRead));
if (size > buffLen) {
return OxError(McOutputBuffEnded);
}
// re-allocate in case too small
auto data = val;
// read the string
oxReturnError(m_reader.read(data, size));
data[size] = 0;
}
++m_field;
return OxError(0);
}
template<Reader_c Reader>
constexpr Error MetalClawReaderTemplate<Reader>::fieldCString(const char*, char **val) noexcept {
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
// read the length
std::size_t bytesRead = 0;
oxRequire(size, mc::decodeInteger<StringLength>(m_reader, &bytesRead));
// re-allocate in case too small
safeDelete(*val);
*val = new char[size + 1];
auto data = *val;
// read the string
oxReturnError(m_reader.read(data, size));
data[size] = 0;
}
++m_field;
return OxError(0);
}
template<Reader_c Reader>
constexpr Error MetalClawReaderTemplate<Reader>::fieldCString(const char*, char **val, std::size_t buffLen) noexcept {
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
// read the length
std::size_t bytesRead = 0;
oxRequire(size, mc::decodeInteger<StringLength>(m_reader, &bytesRead));
// re-allocate if too small
if (buffLen < size + 1) {
safeDelete(*val);
*val = new char[size + 1];
buffLen = size + 1;
}
auto data = *val;
// read the string
oxReturnError(m_reader.read(data, size));
data[size] = 0;
} else {
auto data = *val;
if (data) {
data[0] = 0;
}
}
}
++m_field;
return OxError(0);
}
template<Reader_c Reader>
constexpr Result<ArrayLength> MetalClawReaderTemplate<Reader>::arrayLength(const char*, bool pass) noexcept {
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
// read the length
std::size_t bytesRead = 0;
oxRequire(g, m_reader.tellg());
oxRequire(out, mc::decodeInteger<ArrayLength>(m_reader, &bytesRead));
if (!pass) {
oxReturnError(m_reader.seekg(g));
}
return out;
}
}
return OxError(1);
}
template<Reader_c Reader>
constexpr Result<StringLength> MetalClawReaderTemplate<Reader>::stringLength(const char*) noexcept {
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
// read the length
std::size_t bytesRead = 0;
auto len = mc::decodeInteger<StringLength>(m_reader, &bytesRead);
oxReturnError(m_reader.seekg(-static_cast<int64_t>(bytesRead), ox::ios_base::cur));
return len;
}
}
return 0;
}
template<Reader_c Reader>
template<typename I>
constexpr Error MetalClawReaderTemplate<Reader>::readInteger(I *val) noexcept {
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
std::size_t bytesRead = 0;
auto const result = mc::decodeInteger<I>(m_reader, &bytesRead);
oxReturnError(result);
*val = result.value;
} else {
*val = 0;
}
}
++m_field;
return OxError(0);
}
template<Reader_c Reader>
template<typename T, typename CB>
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, CB cb) noexcept {
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
// read the length
std::size_t bytesRead = 0;
oxRequire(len, mc::decodeInteger<ArrayLength>(m_reader, &bytesRead));
// read the list
auto reader = child("");
auto &handler = *reader.interface();
oxReturnError(handler.setTypeInfo("List", 0, {}, static_cast<std::size_t>(len)));
for (std::size_t i = 0; i < len; ++i) {
T val;
oxReturnError(handler.field("", &val));
oxReturnError(cb(i, &val));
}
}
}
++m_field;
return OxError(0);
}
template<Reader_c Reader>
template<typename T>
constexpr ox::Error MetalClawReaderTemplate<Reader>::setTypeInfo(
const char*, int, const Vector<String>&, std::size_t fields) noexcept {
m_fields = fields;
// Warning: narrow-conv
return m_reader.seekg(
static_cast<int>((fields / 8 + 1) - (fields % 8 == 0)),
ox::ios_base::cur);
}
template<Reader_c Reader>
constexpr MetalClawReaderTemplate<Reader> MetalClawReaderTemplate<Reader>::child(
const char*,
ox::Optional<int> unionIdx) noexcept {
return MetalClawReaderTemplate<Reader>(m_reader, unionIdx);
}
template<Reader_c Reader>
constexpr bool MetalClawReaderTemplate<Reader>::fieldPresent(const char*) const noexcept {
return m_fieldPresence.get(static_cast<std::size_t>(m_field)).value;
}
template<Reader_c Reader>
constexpr bool MetalClawReaderTemplate<Reader>::fieldPresent(int fieldNo) const noexcept {
return m_fieldPresence.get(static_cast<std::size_t>(fieldNo)).value;
}
template<Reader_c Reader>
[[nodiscard]]
constexpr int MetalClawReaderTemplate<Reader>::whichFieldPresent(const char*, const ModelUnion &u) const noexcept {
FieldBitmapReader<Reader> p(m_reader);
for (auto i = 0u; i < u.fieldCount(); ++i) {
if (p.get(i)) {
return static_cast<int>(i);
}
}
return -1;
}
template<Reader_c Reader>
constexpr void MetalClawReaderTemplate<Reader>::nextField() noexcept {
++m_field;
}
using MetalClawReader = MetalClawReaderTemplate<ox::BufferReader>;
template<typename T>
Error readMC(const char *buff, std::size_t buffLen, T *val) noexcept {
BufferReader br(buff, buffLen);
MetalClawReader reader(br);
ModelHandlerInterface<MetalClawReader, ox::OpType::Read> handler(&reader);
return model(&handler, val);
}
template<typename T>
Result<T> readMC(const char *buff, std::size_t buffLen) noexcept {
T val;
oxReturnError(readMC(buff, buffLen, &val));
return val;
}
template<typename T>
Result<T> readMC(const Buffer &buff) noexcept {
return readMC<T>(buff.data(), buff.size());
}
extern template class ModelHandlerInterface<MetalClawReaderTemplate<BufferReader>>;
}

16
deps/ox/src/ox/mc/test/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,16 @@
add_executable(
McTest
tests.cpp
)
target_link_libraries(
McTest
OxMetalClaw
)
add_test("[ox/mc] Writer" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/McTest MetalClawWriter)
add_test("[ox/mc] Reader" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/McTest MetalClawReader)
#add_test("[ox/mc] MetalClawDef" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/McTest MetalClawDef)
add_test("[ox/mc] MetalClawModelValue" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/McTest MetalClawModelValue)
add_test("[ox/mc] encodeInteger" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/McTest encodeInteger)
add_test("[ox/mc] decodeInteger" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/McTest decodeInteger)

476
deps/ox/src/ox/mc/test/tests.cpp vendored Normal file
View File

@@ -0,0 +1,476 @@
/*
* Copyright 2015 - 2022 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 <iostream>
#include <map>
#include <ox/mc/mc.hpp>
#include <ox/model/model.hpp>
#include <ox/std/std.hpp>
union TestUnion {
static constexpr auto TypeName = "TestUnion";
static constexpr auto TypeVersion = 1;
bool Bool;
uint32_t Int;
char *CString{};
};
struct TestStructNest {
static constexpr auto TypeName = "TestStructNest";
static constexpr auto TypeVersion = 1;
bool Bool = false;
uint32_t Int = 0;
ox::BString<32> BString = "";
};
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;
ox::BString<32> BString = "";
uint32_t List[4] = {0, 0, 0, 0};
ox::Vector<uint32_t> Vector = {1, 2, 3, 4, 5};
ox::Vector<uint32_t> Vector2 = {1, 2, 3, 4, 5};
ox::HashMap<ox::String, int> Map;
TestStructNest EmptyStruct;
TestStructNest Struct;
constexpr ~TestStruct() noexcept {
if (unionIdx == 2) {
ox::safeDelete(Union.CString);
}
}
};
template<typename T>
constexpr ox::Error model(T *io, ox::CommonPtrWith<TestUnion> auto *obj) noexcept {
oxReturnError(io->template setTypeInfo<TestUnion>());
oxReturnError(io->field("Bool", &obj->Bool));
oxReturnError(io->field("Int", &obj->Int));
oxReturnError(io->fieldCString("CString", &obj->CString));
return OxError(0);
}
oxModelBegin(TestStructNest)
oxModelField(Bool)
oxModelField(Int)
oxModelField(BString)
oxModelEnd()
template<typename T>
constexpr ox::Error model(T *io, ox::CommonPtrWith<TestStruct> auto *obj) noexcept {
oxReturnError(io->template setTypeInfo<TestStruct>());
oxReturnError(io->field("Bool", &obj->Bool));
oxReturnError(io->field("Int", &obj->Int));
oxReturnError(io->field("Int1", &obj->Int1));
oxReturnError(io->field("Int2", &obj->Int2));
oxReturnError(io->field("Int3", &obj->Int3));
oxReturnError(io->field("Int4", &obj->Int4));
oxReturnError(io->field("Int5", &obj->Int5));
oxReturnError(io->field("Int6", &obj->Int6));
oxReturnError(io->field("Int7", &obj->Int7));
oxReturnError(io->field("Int8", &obj->Int8));
oxReturnError(io->field("unionIdx", &obj->unionIdx));
if constexpr(T::opType() == ox::OpType::Reflect) {
oxReturnError(io->field("Union", ox::UnionView{&obj->Union, 0}));
} else {
oxReturnError(io->field("Union", ox::UnionView{&obj->Union, obj->unionIdx}));
}
oxReturnError(io->field("String", &obj->String));
oxReturnError(io->field("BString", &obj->BString));
oxReturnError(io->field("List", obj->List, 4));
oxReturnError(io->field("Vector", &obj->Vector));
oxReturnError(io->field("Vector2", &obj->Vector2));
oxReturnError(io->field("Map", &obj->Map));
oxReturnError(io->field("Struct", &obj->Struct));
oxReturnError(io->field("EmptyStruct", &obj->EmptyStruct));
return OxError(0);
}
std::map<ox::StringView, ox::Error(*)()> tests = {
{
{
"MetalClawWriter",
[] {
// This test doesn't confirm much, but it does show that the writer
// doesn't segfault
ox::Array<char, 1024> buff;
TestStruct ts;
oxReturnError(ox::writeMC(buff.data(), buff.size(), ts));
oxReturnError(ox::writeMC(ts));
return OxError(0);
}
},
{
"MetalClawReader",
[] {
// setup for tests
TestStruct testIn, testOut;
testIn.Bool = true;
testIn.Int = 42;
testIn.BString = "Test String 1";
testIn.String = "Test String 2";
testIn.Vector = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, };
testIn.Vector2 = {};
testIn.List[0] = 1;
testIn.List[1] = 2;
testIn.List[2] = 3;
testIn.List[3] = 4;
testIn.Struct.Bool = true;
testIn.Struct.Int = 300;
testIn.Struct.BString = "Test String 3";
testIn.unionIdx = 1;
testIn.Union.Int = 93;
// run tests
const auto [buff, err] = ox::writeMC(testIn);
oxAssert(err, "writeMC failed");
oxAssert(ox::readMC(buff.data(), buff.size(), &testOut), "readMC failed");
//std::cout << testIn.Union.Int << "|" << testOut.Union.Int << "|\n";
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.BString == testOut.BString, "BString 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.Vector.size() == testOut.Vector.size(), "Vector size mismatch");
for (auto i = 0u; i < testIn.Vector.size(); ++i) {
oxAssert(testIn.Vector[i] == testOut.Vector[i], ox::sfmt("Vector[{}] value mismatch", i));
}
oxAssert(testIn.Vector2.size() == testOut.Vector2.size(), "Vector2 size 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.BString == testOut.EmptyStruct.BString, "EmptyStruct.BString value mismatch");
oxAssert(testIn.Struct.Int == testOut.Struct.Int, "Struct.Int value mismatch");
oxAssert(testIn.Struct.BString == testOut.Struct.BString, "Struct.BString value mismatch");
oxAssert(testIn.Struct.Bool == testOut.Struct.Bool, "Struct.Bool value mismatch");
return OxError(0);
}
},
{
"encodeInteger",
[] {
using ox::MaxValue;
using ox::mc::McInt;
using ox::mc::encodeInteger;
static constexpr auto check = [](McInt val, const ox::Vector<uint8_t, 9> &expected) {
if (val.length != expected.size()) {
std::cout << "val.length: " << val.length << ", expected: " << expected.size() << '\n';
return OxError(1);
}
for (std::size_t i = 0; i < expected.size(); i++) {
if (expected[i] != val.data[i]) {
std::cout << "decoded: " << static_cast<uint32_t>(val.data[i]) << ", expected: " << static_cast<uint32_t>(expected[i]) << '\n';
std::cout << "decoded: " << i << ": " << static_cast<uint32_t>(val.data[i]) << '\n';
return OxError(1);
}
}
return OxError(0);
};
constexpr auto check64 = [](McInt val, auto expected) {
if (val.length != 9) {
std::cout << "val.length: " << val.length << '\n';
return OxError(1);
}
ox::LittleEndian<decltype(expected)> decoded = *reinterpret_cast<decltype(expected)*>(&val.data[1]);
if (expected != decoded) {
std::cout << "decoded: " << decoded << ", expected: " << expected << '\n';
return OxError(1);
}
return OxError(0);
};
// signed positive
oxAssert(check(encodeInteger(int64_t(1)), {0b000'0001'0}), "Encode 1 fail");
oxAssert(check(encodeInteger(int64_t(2)), {0b000'0010'0}), "Encode 2 fail");
oxAssert(check(encodeInteger(int64_t(3)), {0b000'0011'0}), "Encode 3 fail");
oxAssert(check(encodeInteger(int64_t(4)), {0b000'0100'0}), "Encode 4 fail");
oxAssert(check(encodeInteger(int64_t(64)), {0b00'0000'01, 0b1}), "Encode 64 fail");
oxAssert(check(encodeInteger(int64_t(128)), {0b00'0000'01, 0b10}), "Encode 128 fail");
oxAssert(check(encodeInteger(int64_t(129)), {0b00'0001'01, 0b10}), "Encode 129 fail");
oxAssert(check(encodeInteger(int64_t(130)), {0b00'0010'01, 0b10}), "Encode 130 fail");
oxAssert(check(encodeInteger(int64_t(131)), {0b00'0011'01, 0b10}), "Encode 131 fail");
// signed negative
oxAssert(check(encodeInteger( int64_t(-1)), {0b111'1111'0}), "Encode -1 fail");
oxAssert(check(encodeInteger( int64_t(-2)), {0b111'1110'0}), "Encode -2 fail");
oxAssert(check(encodeInteger( int64_t(-3)), {0b111'1101'0}), "Encode -3 fail");
oxAssert(check(encodeInteger( int64_t(-4)), {0b111'1100'0}), "Encode -4 fail");
oxAssert(check(encodeInteger( int64_t(-64)), {0b100'0000'0}), "Encode -64 fail");
oxAssert(check(encodeInteger(int64_t(-128)), {0b00'0000'01, 0b11'1111'10}), "Encode -128 fail");
oxAssert(check(encodeInteger(int64_t(-129)), {0b11'1111'01, 0b11'1111'01}), "Encode -129 fail");
oxAssert(check(encodeInteger(int64_t(-130)), {0b11'1110'01, 0b11'1111'01}), "Encode -130 fail");
oxAssert(check(encodeInteger(int64_t(-131)), {0b11'1101'01, 0b11'1111'01}), "Encode -131 fail");
// unsigned
oxAssert(check(encodeInteger(uint32_t(0xffffffff)), {0b11101111, 255, 255, 255, 0b00011111}), "Encode 0xffffffff fail");
oxAssert(check(encodeInteger(uint64_t(1)), {0b0010}), "Encode 1 fail");
oxAssert(check(encodeInteger(uint64_t(2)), {0b0100}), "Encode 2 fail");
oxAssert(check(encodeInteger(uint64_t(3)), {0b0110}), "Encode 3 fail");
oxAssert(check(encodeInteger(uint64_t(4)), {0b1000}), "Encode 4 fail");
oxAssert(check(encodeInteger(uint64_t(64)), {0b1000'000'0}), "Encode 4 fail");
oxAssert(check(encodeInteger(uint64_t(128)), {0b0001, 0b10}), "Encode 128 fail");
oxAssert(check(encodeInteger(uint64_t(129)), {0b0101, 0b10}), "Encode 129 fail");
oxAssert(check(encodeInteger(uint64_t(130)), {0b1001, 0b10}), "Encode 130 fail");
oxAssert(check(encodeInteger(uint64_t(131)), {0b1101, 0b10}), "Encode 131 fail");
// Signed check needs lambda templates to run correctly without
// code deduplication
oxAssert(check64(encodeInteger(MaxValue<int64_t>), MaxValue<int64_t>), "Encode MaxValue<int64_t> fail");
oxAssert(check64(encodeInteger(MaxValue<uint64_t>), MaxValue<uint64_t>), "Encode MaxValue<uint64_t> fail");
return OxError(0);
}
},
{
"decodeInteger",
[] {
using ox::MaxValue;
using ox::mc::McInt;
using ox::mc::encodeInteger;
using ox::mc::decodeInteger;
static constexpr auto check = [](auto val) {
auto result = decodeInteger<decltype(val)>(encodeInteger(val));
oxReturnError(result.error);
if (result.value != val) {
std::cout << "Bad value: " << result.value << ", expected: " << val << '\n';
return OxError(1);
}
return OxError(0);
};
oxAssert(check(uint32_t(14)), "Decode of 14 failed.");
oxAssert(check(int8_t(-1)), "Decode of -1 failed.");
oxAssert(check(int16_t(-1)), "Decode of -1 failed.");
oxAssert(check(int32_t(-1)), "Decode of -1 failed.");
oxAssert(check(int64_t(-1)), "Decode of -1 failed.");
oxAssert(check(int64_t(-2)), "Decode of -2 failed.");
oxAssert(check(int64_t(-127)), "Decode of -127 failed.");
oxAssert(check(int64_t(-128)), "Decode of -128 failed.");
oxAssert(check(int64_t(-129)), "Decode of -129 failed.");
oxAssert(check(int64_t(-129000)), "Decode of -129000 failed.");
oxAssert(check(int64_t(1)), "Decode of 1 failed.");
oxAssert(check(int64_t(2)), "Decode of 2 failed.");
oxAssert(check(int64_t(42)), "Decode of 42 failed.");
oxAssert(check(int64_t(130)), "Decode of 130 failed.");
oxAssert(check(int64_t(131)), "Decode of 131 failed.");
oxAssert(check(int64_t(131000)), "Decode of 131000 failed.");
oxAssert(check(uint64_t(1)), "Decode of 1 failed.");
oxAssert(check(uint64_t(2)), "Decode of 2 failed.");
oxAssert(check(uint64_t(42)), "Decode of 42 failed.");
oxAssert(check(uint64_t(130)), "Decode of 130 failed.");
oxAssert(check(uint64_t(131)), "Decode of 131 failed.");
oxAssert(check(0xffffffff), "Decode of 0xffffffff failed.");
oxAssert(check(0xffffffffffff), "Decode of 0xffffffffffff failed.");
oxAssert(check(0xffffffffffffffff), "Decode of U64 max failed.");
return OxError(0);
}
},
{
"MetalClawModelValue",
[] {
static constexpr size_t dataBuffLen = ox::units::MB;
ox::Buffer dataBuff(dataBuffLen);
TestStruct testIn;
testIn.Bool = true;
testIn.Int = 42;
testIn.BString = "Test String 1";
testIn.List[0] = 1;
testIn.List[1] = 2;
testIn.List[2] = 3;
testIn.List[3] = 4;
testIn.Struct.Bool = true;
testIn.Struct.Int = 300;
testIn.Struct.BString = "Test String 2";
testIn.unionIdx = 1;
testIn.Union.Int = 93;
oxAssert(ox::writeMC(dataBuff.data(), dataBuff.size(), testIn), "Data generation failed");
ox::TypeStore typeStore;
const auto [type, typeErr] = ox::buildTypeDef(&typeStore, &testIn);
oxAssert(typeErr, "Descriptor write failed");
ox::ModelObject testOut;
oxReturnError(testOut.setType(type));
oxAssert(ox::readMC(dataBuff.data(), dataBuff.size(), &testOut), "Data read failed");
oxAssert(testOut["Int"].get<int>() == testIn.Int, "testOut.Int failed");
oxAssert(testOut["Bool"].get<bool>() == testIn.Bool, "testOut.Bool failed");
oxAssert(testOut["BString"].get<ox::String>() == testIn.BString.c_str(), "testOut.String failed");
oxAssert(testOut["String"].get<ox::String>() == testIn.String, "testOut.String failed");
auto &testOutStruct = testOut["Struct"].get<ox::ModelObject>();
auto &testOutUnion = testOut["Union"].get<ox::ModelUnion>();
auto &testOutList = testOut["List"].get<ox::ModelValueVector>();
auto testOutStructCopy = testOut["Struct"].get<ox::ModelObject>();
auto testOutUnionCopy = testOut["Union"].get<ox::ModelUnion>();
auto testOutListCopy = testOut["List"].get<ox::ModelValueVector>();
oxAssert(testOutStruct.typeName() == TestStructNest::TypeName, "ModelObject TypeName failed");
oxAssert(testOutStruct.typeVersion() == TestStructNest::TypeVersion, "ModelObject TypeVersion failed");
oxAssert(testOutStruct["Bool"].get<bool>() == testIn.Struct.Bool, "testOut.Struct.Bool failed");
oxAssert(testOutStruct["BString"].get<ox::String>() == testIn.Struct.BString.c_str(), "testOut.Struct.BString failed");
oxAssert(testOut["unionIdx"].get<int>() == testIn.unionIdx, "testOut.unionIdx failed");
oxAssert(testOutUnion.unionIdx() == testIn.unionIdx, "testOut.Union idx wrong");
oxAssert(testOutUnion["Int"].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["Bool"].get<bool>() == testIn.Struct.Bool, "testOut.Struct.Bool (copy) failed");
oxAssert(testOutStructCopy["BString"].get<ox::String>() == testIn.Struct.BString.c_str(), "testOut.Struct.BString (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 OxError(0);
}
},
{
"MetalClawDef",
[] {
//constexpr size_t descBuffLen = 1024;
//uint8_t descBuff[descBuffLen];
static constexpr size_t dataBuffLen = ox::units::MB;
char dataBuff[dataBuffLen];
TestStruct testIn, testOut;
testIn.Bool = true;
testIn.Int = 42;
testIn.BString = "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.BString = "Test String 2";
oxAssert(ox::writeMC(dataBuff, dataBuffLen, testIn), "Data generation failed");
ox::TypeStore typeStore;
const auto [type, typeErr] = ox::buildTypeDef(&typeStore, &testIn);
oxAssert(typeErr, "Descriptor write failed");
ox::BufferReader br(dataBuff, dataBuffLen);
oxReturnError(ox::walkModel<ox::MetalClawReader>(type, br,
[](const ox::Vector<ox::FieldName>&, const ox::Vector<ox::String>&, const ox::DescriptorField &f, ox::MetalClawReader *rdr) -> ox::Error {
//std::cout << f.fieldName.c_str() << '\n';
auto fieldName = f.fieldName.c_str();
switch (f.type->primitiveType) {
case ox::PrimitiveType::UnsignedInteger:
std::cout << fieldName << ":\tuint" << f.type->length * 8 << "_t:\t";
switch (f.type->length) {
case 1: {
uint8_t i = {};
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
std::cout << i;
break;
}
case 2: {
uint16_t i = {};
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
std::cout << i;
break;
}
case 4: {
uint32_t i = {};
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
std::cout << i;
break;
}
case 8: {
uint64_t i = {};
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
std::cout << i;
break;
}
}
std::cout << '\n';
break;
case ox::PrimitiveType::SignedInteger:
std::cout << fieldName << ":\tint" << f.type->length * 8 << "_t:\t";
switch (f.type->length) {
case 1: {
int8_t i = {};
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
std::cout << i;
break;
}
case 2: {
int16_t i = {};
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
std::cout << i;
break;
}
case 4: {
int32_t i = {};
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
std::cout << i;
break;
}
case 8: {
int64_t i = {};
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
std::cout << i;
break;
}
}
std::cout << '\n';
break;
case ox::PrimitiveType::Bool: {
bool i = {};
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
std::cout << fieldName << ":\t" << "bool:\t\t" << (i ? "true" : "false") << '\n';
break;
}
case ox::PrimitiveType::String: {
ox::String s;
//std::cout << rdr->stringLength() << '\n';
oxAssert(rdr->field(fieldName, &s), "Walking model failed.");
oxOutf("{}:\tstring:\t\t{}\n", fieldName, s);
break;
}
case ox::PrimitiveType::Struct:
break;
case ox::PrimitiveType::Union:
break;
}
return OxError(0);
}
));
return OxError(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;
}

20
deps/ox/src/ox/mc/types.hpp vendored Normal file
View File

@@ -0,0 +1,20 @@
/*
* Copyright 2015 - 2022 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <ox/std/string.hpp>
#include <ox/std/strops.hpp>
#include <ox/std/types.hpp>
namespace ox {
using StringLength = std::size_t;
using ArrayLength = std::size_t;
}

21
deps/ox/src/ox/mc/write.cpp vendored Normal file
View File

@@ -0,0 +1,21 @@
/*
* Copyright 2015 - 2022 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/assert.hpp>
#include <ox/std/byteswap.hpp>
#include <ox/std/memops.hpp>
#include <ox/std/trace.hpp>
#include "write.hpp"
namespace ox {
template class ModelHandlerInterface<MetalClawWriter<BufferWriter>>;
template class ModelHandlerInterface<MetalClawWriter<CharBuffWriter>>;
}

406
deps/ox/src/ox/mc/write.hpp vendored Normal file
View File

@@ -0,0 +1,406 @@
/*
* Copyright 2015 - 2023 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/model/fieldcounter.hpp>
#include <ox/model/modelhandleradaptor.hpp>
#include <ox/model/optype.hpp>
#include <ox/model/types.hpp>
#include <ox/std/bit.hpp>
#include <ox/std/buffer.hpp>
#include <ox/std/byteswap.hpp>
#include <ox/std/hashmap.hpp>
#include <ox/std/optional.hpp>
#include <ox/std/string.hpp>
#include <ox/std/types.hpp>
#include <ox/std/units.hpp>
#include "intops.hpp"
#include "err.hpp"
#include "presenceindicator.hpp"
#include "types.hpp"
namespace ox {
template<Writer_c Writer>
class MetalClawWriter {
private:
ox::Vector<uint8_t, 16> m_presenceMapBuff{};
FieldBitmap m_fieldPresence;
int m_field = 0;
ox::Optional<int> m_unionIdx;
std::size_t m_writerBeginP{};
Writer &m_writer;
public:
constexpr explicit MetalClawWriter(Writer &writer, ox::Optional<int> const&unionIdx = {}) noexcept;
constexpr ~MetalClawWriter() noexcept = default;
constexpr Error field(const char*, const int8_t *val) noexcept;
constexpr Error field(const char*, const int16_t *val) noexcept;
constexpr Error field(const char*, const int32_t *val) noexcept;
constexpr Error field(const char*, const int64_t *val) noexcept;
constexpr Error field(const char*, const uint8_t *val) noexcept;
constexpr Error field(const char*, const uint16_t *val) noexcept;
constexpr Error field(const char*, const uint32_t *val) noexcept;
constexpr Error field(const char*, const uint64_t *val) noexcept;
constexpr Error field(const char*, const bool *val) noexcept;
template<typename T>
constexpr Error field(const char*, const T *val, std::size_t len) noexcept;
template<typename T>
constexpr Error field(const char *name, const HashMap<String, T> *val) noexcept;
template<std::size_t SmallStringSize>
constexpr Error field(const char*, const BasicString<SmallStringSize> *val) noexcept;
template<std::size_t L>
constexpr Error field(const char*, const BString<L> *val) noexcept;
constexpr Error fieldCString(const char *name, const char *const*val, std::size_t buffLen) noexcept;
constexpr Error fieldCString(const char *name, const char **val) noexcept;
constexpr Error fieldCString(const char *name, const char *const*val) noexcept;
constexpr Error fieldCString(const char *name, const char *val, std::size_t len) noexcept;
template<typename T>
constexpr Error field(const char*, const T *val) noexcept;
template<typename U, bool force = false>
constexpr Error field(const char*, UnionView<U, force> val) noexcept;
template<typename T = std::nullptr_t>
constexpr ox::Error setTypeInfo(
const char *name = T::TypeName,
int version = T::TypeVersion,
const Vector<String>& = {},
std::size_t fields = ModelFieldCount_v<T>) noexcept;
/**
* stringLength is not implemented in MetalClawWriter
*/
[[nodiscard]]
constexpr auto stringLength(const char*) noexcept {
return 0;
}
/**
* stringLength is not implemented in MetalClawWriter
*/
[[nodiscard]]
constexpr auto arrayLength(const char*, bool = true) noexcept {
return 0;
}
[[nodiscard]]
static constexpr auto opType() noexcept {
return OpType::Write;
}
ox::Error finalize() noexcept;
private:
constexpr Error appendInteger(Integer_c auto val) noexcept {
bool fieldSet = false;
if (val && (!m_unionIdx.has_value() || *m_unionIdx == m_field)) {
auto mi = mc::encodeInteger(val);
oxReturnError(m_writer.write(reinterpret_cast<const char*>(mi.data), mi.length));
fieldSet = true;
}
oxReturnError(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
++m_field;
return OxError(0);
}
};
extern template class ModelHandlerInterface<MetalClawWriter<BufferWriter>>;
extern template class ModelHandlerInterface<MetalClawWriter<CharBuffWriter>>;
template<Writer_c Writer>
constexpr MetalClawWriter<Writer>::MetalClawWriter(Writer &writer, ox::Optional<int> const&unionIdx) noexcept:
m_fieldPresence(m_presenceMapBuff.data(), m_presenceMapBuff.size()),
m_unionIdx(unionIdx),
m_writerBeginP(writer.tellp()),
m_writer(writer) {
}
template<Writer_c Writer>
constexpr Error MetalClawWriter<Writer>::field(const char*, const int8_t *val) noexcept {
return appendInteger(*val);
}
template<Writer_c Writer>
constexpr Error MetalClawWriter<Writer>::field(const char*, const int16_t *val) noexcept {
return appendInteger(*val);
}
template<Writer_c Writer>
constexpr Error MetalClawWriter<Writer>::field(const char*, const int32_t *val) noexcept {
return appendInteger(*val);
}
template<Writer_c Writer>
constexpr Error MetalClawWriter<Writer>::field(const char*, const int64_t *val) noexcept {
return appendInteger(*val);
}
template<Writer_c Writer>
constexpr Error MetalClawWriter<Writer>::field(const char*, const uint8_t *val) noexcept {
return appendInteger(*val);
}
template<Writer_c Writer>
constexpr Error MetalClawWriter<Writer>::field(const char*, const uint16_t *val) noexcept {
return appendInteger(*val);
}
template<Writer_c Writer>
constexpr Error MetalClawWriter<Writer>::field(const char*, const uint32_t *val) noexcept {
return appendInteger(*val);
}
template<Writer_c Writer>
constexpr Error MetalClawWriter<Writer>::field(const char*, const uint64_t *val) noexcept {
return appendInteger(*val);
}
template<Writer_c Writer>
constexpr Error MetalClawWriter<Writer>::field(const char*, const bool *val) noexcept {
if (!m_unionIdx.has_value() || *m_unionIdx == m_field) {
oxReturnError(m_fieldPresence.set(static_cast<std::size_t>(m_field), *val));
}
++m_field;
return OxError(0);
}
template<Writer_c Writer>
template<std::size_t SmallStringSize>
constexpr Error MetalClawWriter<Writer>::field(const char*, const BasicString<SmallStringSize> *val) noexcept {
bool fieldSet = false;
if (val->len() && (!m_unionIdx.has_value() || *m_unionIdx == m_field)) {
// write the length
const auto strLen = mc::encodeInteger(val->len());
oxReturnError(m_writer.write(reinterpret_cast<const char*>(strLen.data), strLen.length));
// write the string
oxReturnError(m_writer.write(val->c_str(), static_cast<std::size_t>(val->len())));
fieldSet = true;
}
oxReturnError(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
++m_field;
return OxError(0);
}
template<Writer_c Writer>
template<std::size_t L>
constexpr Error MetalClawWriter<Writer>::field(const char *name, const BString<L> *val) noexcept {
return fieldCString(name, val->data(), val->cap());
}
template<Writer_c Writer>
constexpr Error MetalClawWriter<Writer>::fieldCString(const char*, const char *const*val, std::size_t) noexcept {
bool fieldSet = false;
if (!m_unionIdx.has_value() || *m_unionIdx == m_field) {
const auto strLen = *val ? ox_strlen(*val) : 0;
// write the length
const auto strLenBuff = mc::encodeInteger(strLen);
oxReturnError(m_writer.write(reinterpret_cast<const char*>(strLenBuff.data), strLenBuff.length));
// write the string
oxReturnError(m_writer.write(*val, static_cast<std::size_t>(strLen)));
fieldSet = true;
}
oxReturnError(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
++m_field;
return OxError(0);
}
template<Writer_c Writer>
constexpr Error MetalClawWriter<Writer>::fieldCString(const char *name, const char **val) noexcept {
return fieldCString(name, val, {});
}
template<Writer_c Writer>
constexpr Error MetalClawWriter<Writer>::fieldCString(const char *name, const char *const*val) noexcept {
return fieldCString(name, val, {});
}
template<Writer_c Writer>
constexpr Error MetalClawWriter<Writer>::fieldCString(const char*, const char *val, std::size_t strLen) noexcept {
bool fieldSet = false;
if (strLen && (!m_unionIdx.has_value() || *m_unionIdx == m_field)) {
// write the length
const auto strLenBuff = mc::encodeInteger(strLen);
oxReturnError(m_writer.write(reinterpret_cast<const char*>(strLenBuff.data), strLenBuff.length));
// write the string
oxReturnError(m_writer.write(val, static_cast<std::size_t>(strLen)));
fieldSet = true;
}
oxReturnError(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
++m_field;
return OxError(0);
}
template<Writer_c Writer>
template<typename T>
constexpr Error MetalClawWriter<Writer>::field(const char*, const T *val) noexcept {
if constexpr(isVector_v<T> || isArray_v<T>) {
return field(nullptr, val->data(), val->size());
} else {
bool fieldSet = false;
if (val && (!m_unionIdx.has_value() || *m_unionIdx == m_field)) {
auto const writeIdx = m_writer.tellp();
MetalClawWriter<Writer> writer(m_writer);
ModelHandlerInterface<MetalClawWriter<Writer>> handler{&writer};
oxReturnError(model(&handler, val));
oxReturnError(writer.finalize());
fieldSet = writeIdx != m_writer.tellp();
}
oxReturnError(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
++m_field;
return {};
}
}
template<Writer_c Writer>
template<typename U, bool force>
constexpr Error MetalClawWriter<Writer>::field(const char*, UnionView<U, force> val) noexcept {
bool fieldSet = false;
if (val.get() && (!m_unionIdx.has_value() || *m_unionIdx == m_field)) {
auto const writeIdx = m_writer.tellp();
MetalClawWriter<Writer> writer(m_writer, ox::Optional<int>(ox::in_place, val.idx()));
ModelHandlerInterface handler{&writer};
oxReturnError(model(&handler, val.get()));
oxReturnError(writer.finalize());
fieldSet = writeIdx != m_writer.tellp();
}
oxReturnError(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
++m_field;
return {};
}
template<Writer_c Writer>
template<typename T>
constexpr Error MetalClawWriter<Writer>::field(const char*, const T *val, std::size_t len) noexcept {
bool fieldSet = false;
if (len && (!m_unionIdx.has_value() || *m_unionIdx == m_field)) {
// write the length
const auto arrLen = mc::encodeInteger(len);
oxReturnError(m_writer.write(reinterpret_cast<const char*>(arrLen.data), arrLen.length));
auto const writeIdx = m_writer.tellp();
MetalClawWriter<Writer> writer(m_writer);
ModelHandlerInterface handler{&writer};
oxReturnError(handler.template setTypeInfo<T>("List", 0, {}, static_cast<std::size_t>(len)));
// write the array
for (std::size_t i = 0; i < len; ++i) {
oxReturnError(handler.field("", &val[i]));
}
oxReturnError(writer.finalize());
fieldSet = writeIdx != m_writer.tellp();
}
oxReturnError(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
++m_field;
return OxError(0);
}
template<Writer_c Writer>
template<typename T>
constexpr Error MetalClawWriter<Writer>::field(const char*, const HashMap<String, T> *val) noexcept {
const auto &keys = val->keys();
const auto len = keys.size();
bool fieldSet = false;
if (len && (!m_unionIdx.has_value() || *m_unionIdx == m_field)) {
// write the length
const auto arrLen = mc::encodeInteger(len);
oxReturnError(m_writer.write(reinterpret_cast<const char*>(arrLen.data), arrLen.length));
// write map
MetalClawWriter<Writer> writer(m_writer);
ModelHandlerInterface handler{&writer};
// double len for both key and value
oxReturnError(handler.setTypeInfo("Map", 0, {}, len * 2));
// this loop body needs to be in a lambda because of the potential alloca call
constexpr auto loopBody = [](auto &handler, auto const&key, auto const&val) -> ox::Error {
const auto keyLen = key.len();
auto wkey = ox_malloca(keyLen + 1, char, 0);
memcpy(wkey.get(), key.c_str(), keyLen + 1);
oxReturnError(handler.fieldCString("", wkey.get(), keyLen));
oxRequireM(value, val.at(key));
return handler.field("", value);
};
// write the array
for (std::size_t i = 0; i < len; ++i) {
auto const&key = keys[i];
oxReturnError(loopBody(handler, key, *val));
}
oxReturnError(writer.finalize());
fieldSet = true;
}
oxReturnError(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
++m_field;
return OxError(0);
}
template<Writer_c Writer>
template<typename T>
constexpr ox::Error MetalClawWriter<Writer>::setTypeInfo(
const char*,
int,
const Vector<String>&,
std::size_t fields) noexcept {
const auto fieldPresenceLen = (fields - 1) / 8 + 1;
oxReturnError(m_writer.write(nullptr, fieldPresenceLen));
m_presenceMapBuff.resize(fieldPresenceLen);
m_fieldPresence.setBuffer(m_presenceMapBuff.data(), m_presenceMapBuff.size());
m_fieldPresence.setFields(static_cast<int>(fields));
return {};
}
template<Writer_c Writer>
ox::Error MetalClawWriter<Writer>::finalize() noexcept {
const auto end = m_writer.tellp();
oxReturnError(m_writer.seekp(m_writerBeginP));
oxReturnError(m_writer.write(
reinterpret_cast<const char*>(m_presenceMapBuff.data()),
m_presenceMapBuff.size()));
oxReturnError(m_writer.seekp(end));
return {};
}
Result<Buffer> writeMC(Writer_c auto &writer, const auto &val) noexcept {
MetalClawWriter mcWriter(writer);
ModelHandlerInterface handler{&mcWriter};
oxReturnError(model(&handler, &val));
oxReturnError(mcWriter.finalize());
return {};
}
Result<Buffer> writeMC(auto const&val, std::size_t buffReserveSz = 2 * units::KB) noexcept {
Buffer buff(buffReserveSz);
BufferWriter bw(&buff, 0);
oxReturnError(writeMC(bw, val));
buff.resize(bw.tellp());
return buff;
}
Error writeMC(char *buff, std::size_t buffLen, auto const&val, std::size_t *sizeOut = nullptr) noexcept {
CharBuffWriter bw(buff, buffLen);
oxReturnError(writeMC(bw, val));
if (sizeOut) {
*sizeOut = bw.tellp();
}
return {};
}
}

54
deps/ox/src/ox/model/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,54 @@
add_library(
OxModel
desctypes.cpp
descwrite.cpp
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()
install(
FILES
def.hpp
descread.hpp
desctypes.hpp
descwrite.hpp
optype.hpp
metadata.hpp
model.hpp
modelhandleradaptor.hpp
modelops.hpp
modelvalue.hpp
typenamecatcher.hpp
types.hpp
typestore.hpp
walk.hpp
DESTINATION
include/ox/model
)
install(TARGETS OxModel
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
if(OX_RUN_TESTS)
add_subdirectory(test)
endif()

17
deps/ox/src/ox/model/def.hpp vendored Normal file
View File

@@ -0,0 +1,17 @@
/*
* Copyright 2015 - 2022 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>
#define oxModelBegin(modelName) constexpr ox::Error model(auto *io, [[maybe_unused]] ox::CommonPtrWith<modelName> auto *o) noexcept { oxReturnError(io->template setTypeInfo<modelName>());
#define oxModelEnd() return OxError(0); }
#define oxModelField(fieldName) oxReturnError(io->field(#fieldName, &o->fieldName));
#define oxModelFieldRename(serFieldName, objFieldName) oxReturnError(io->field(#serFieldName, &o->objFieldName));
#define oxModelFriend(modelName) friend constexpr ox::Error model(auto*, ox::CommonPtrWith<modelName> auto*) noexcept

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>

45
deps/ox/src/ox/model/descread.hpp vendored Normal file
View File

@@ -0,0 +1,45 @@
/*
* Copyright 2015 - 2022 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "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);
}
}

13
deps/ox/src/ox/model/desctypes.cpp vendored Normal file
View File

@@ -0,0 +1,13 @@
/*
* Copyright 2015 - 2022 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include "desctypes.hpp"
namespace ox {
}

249
deps/ox/src/ox/model/desctypes.hpp vendored Normal file
View File

@@ -0,0 +1,249 @@
/*
* Copyright 2015 - 2022 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(CRStringView name, int version,
const TypeParamPack &typeParams = {}) noexcept {
String tp;
if (typeParams.size()) {
tp = "#";
for (const auto &p : typeParams) {
tp += p + ",";
}
tp = tp.substr(0, tp.len() - 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 {
oxReturnError(io->template setTypeInfo<Subscript>());
if constexpr(T::opType() == OpType::Reflect) {
uint32_t st = 0;
oxReturnError(io->field("subscriptType", &st));
} else if constexpr(T::opType() == OpType::Write) {
auto pt = type ? static_cast<uint8_t>(type->subscriptType) : 0;
oxReturnError(io->field("subscriptType", &pt));
} else {
auto pt = type ? static_cast<uint32_t>(type->subscriptType) : 0;
oxReturnError(io->field("subscriptType", &pt));
type->subscriptType = static_cast<Subscript::SubscriptType>(pt);
}
oxReturnError(io->field("length", &type->length));
oxReturnError(io->field("smallSzLen", &type->smallSzLen));
return OxError(0);
}
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)) {
}
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 {
oxReturnError(io->template setTypeInfo<DescriptorType>());
oxReturnError(io->field("typeName", &type->typeName));
oxReturnError(io->field("typeVersion", &type->typeVersion));
if constexpr(T::opType() == OpType::Reflect) {
uint8_t pt = 0;
oxReturnError(io->field("primitiveType", &pt));
} else if constexpr(T::opType() == OpType::Write) {
auto pt = type ? static_cast<uint8_t>(type->primitiveType) : 0;
oxReturnError(io->field("primitiveType", &pt));
} else {
auto pt = type ? static_cast<uint8_t>(type->primitiveType) : 0;
oxReturnError(io->field("primitiveType", &pt));
type->primitiveType = static_cast<PrimitiveType>(pt);
}
oxReturnError(io->field("typeParams", &type->typeParams));
oxReturnError(io->field("fieldList", &type->fieldList));
oxReturnError(io->field("length", &type->length));
oxReturnError(io->field("preloadable", &type->preloadable));
return OxError(0);
}
template<typename T>
constexpr Error model(T *io, CommonPtrWith<DescriptorField> auto *field) noexcept {
oxReturnError(io->template setTypeInfo<DescriptorField>());
oxReturnError(io->field("typeId", &field->typeId));
oxReturnError(io->field("fieldName", &field->fieldName));
oxReturnError(io->field("subscriptLevels", &field->subscriptLevels));
oxReturnError(io->field("subscriptStack", &field->subscriptStack));
// defaultValue is unused now, but leave placeholder for backwards compatibility
int defaultValue = 0;
oxReturnError(io->field("defaultValue", &defaultValue));
return OxError(0);
}
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 OxError(0);
}
#endif
}

49
deps/ox/src/ox/model/descwrite.cpp vendored Normal file
View File

@@ -0,0 +1,49 @@
/*
* Copyright 2015 - 2022 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 "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[])");
}

381
deps/ox/src/ox/model/descwrite.hpp vendored Normal file
View File

@@ -0,0 +1,381 @@
/*
* Copyright 2015 - 2023 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/bstring.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 UniquePtr<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(CRStringView name = T::TypeName,
int version = T::TypeVersion,
const TypeParamPack &typeParams = {},
std::size_t fields = ModelFieldCount_v<T>) noexcept;
template<typename T>
constexpr Error field(CRStringView name, const T *val, std::size_t valLen,
const SubscriptStack &subscriptStack = {}) noexcept;
template<typename T, bool force>
constexpr Error field(CRStringView name, UnionView<T, force> val) noexcept;
template<typename T>
constexpr Error field(CRStringView name, const T *val) noexcept;
template<typename ...Args>
constexpr Error fieldCString(CRStringView 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 BString<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(CRStringView 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(
CRStringView 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> || isBString_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(CRStringView name, const T*, std::size_t, const SubscriptStack &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 OxError(0);
}
return OxError(1);
}
template<typename T, bool force>
constexpr Error TypeDescWriter::field(CRStringView 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 OxError(0);
}
return OxError(1);
}
template<typename T>
constexpr Error TypeDescWriter::field(CRStringView name, const T *val) noexcept {
if (m_type) {
if constexpr(isVector_v<T> || isArray_v<T>) {
return field(name, val->data(), 0, detail::buildSubscriptStack(val));
} else if constexpr(isSmartPtr_v<T>) {
return field(name, val->get(), 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 OxError(1);
}
template<typename ...Args>
constexpr Error TypeDescWriter::fieldCString(CRStringView 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 BString<sz>*) const noexcept {
constexpr auto PT = PrimitiveType::String;
return getType(types::BString, 0, PT, 0);
}
constexpr const DescriptorType *TypeDescWriter::getType(CRStringView 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);
oxReturnError(model(&handler, t));
a.deallocate(t, 1);
} else {
auto t = ox_malloca(sizeof(T), T);
oxReturnError(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);
oxReturnError(model(&handler, val));
return writer.definition();
}
}

79
deps/ox/src/ox/model/fieldcounter.hpp vendored Normal file
View File

@@ -0,0 +1,79 @@
/*
* Copyright 2015 - 2023 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(CRStringView = "", int = 0, const Vector<String>& = {}, std::size_t = 0) {
return {};
}
template<typename U>
constexpr ox::Error field(CRStringView, U) noexcept {
++fields;
return OxError(0);
}
template<typename U>
constexpr ox::Error field(CRStringView, U, std::size_t) noexcept {
++fields;
return OxError(0);
}
template<typename U, typename Handler>
constexpr Error field(CRStringView, Handler) {
++fields;
return OxError(0);
}
template<typename ...Args>
constexpr Error fieldCString(Args&&...) noexcept {
++fields;
return OxError(0);
}
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
deps/ox/src/ox/model/metadata.hpp vendored Normal file
View File

@@ -0,0 +1,66 @@
/*
* Copyright 2015 - 2022 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <ox/std/byteswap.hpp>
#include <ox/std/bstring.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
deps/ox/src/ox/model/model.hpp vendored Normal file
View File

@@ -0,0 +1,22 @@
/*
* Copyright 2015 - 2022 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"

View File

@@ -0,0 +1,229 @@
/*
* Copyright 2015 - 2023 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 = nullptr;
public:
constexpr explicit ModelHandlerInterface(Handler *handler) noexcept: m_handler(handler) {
}
template<typename T = std::nullptr_t>
constexpr ox::Error setTypeInfo(
const char* name = T::TypeName,
int version = T::TypeVersion,
const Vector<String>& typeParams = {}) noexcept {
return m_handler->template setTypeInfo<T>(name, version, typeParams, ModelFieldCount_v<T>);
}
template<typename T = std::nullptr_t>
constexpr ox::Error setTypeInfo(
const char *name,
int version,
const Vector<String>& typeParams,
std::size_t fields) noexcept {
return m_handler->template setTypeInfo<T>(name, version, typeParams, fields);
}
template<std::size_t len>
constexpr Error fieldCString(const char *name, char val[len]) noexcept {
return m_handler->fieldCString(name, &val[0], len);
}
template<std::size_t len>
constexpr Error fieldCString(const char *name, const char 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(const char *name, char **val) noexcept {
return m_handler->fieldCString(name, val);
}
constexpr Error fieldCString(const char *name, const 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(const char *name, const char **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(const char *name, char **val, std::size_t buffLen) noexcept {
return m_handler->fieldCString(name, val, buffLen);
}
constexpr Error fieldCString(const char *name, const char **val, std::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(const char *name, char *val, std::size_t buffLen) noexcept {
return m_handler->fieldCString(name, val, buffLen);
}
constexpr Error fieldModelValue(const char *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(m_handler->whichFieldPresent(name, u));
return m_handler->field(name, UnionView<ModelUnion, true>(&u, u.unionIdx()));
} else {
return m_handler->field(name, UnionView<const ModelUnion, true>(&u, u.unionIdx()));
}
}
case ModelValue::Type::Vector:
return m_handler->field(name, &v->template get<ModelValueVector>());
}
oxErrf("invalid type: {}: {}\n", name, static_cast<int>(v->type()));
oxPanic(OxError(1), "invalid type");
return OxError(1, "invalid type");
}
// array handler, with callback to allow handling individual elements
template<typename T, typename Callback>
constexpr Error field(const char *name, Callback cb) noexcept {
return m_handler->template field<T, Callback>(name, cb);
}
template<typename T>
constexpr Error field(const char *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(const char *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(const char *name, UnionView<U, force> val) noexcept {
return m_handler->field(name, val);
}
constexpr Error field(const char *name, auto *val, std::size_t len) noexcept {
return m_handler->field(name, val, len);
}
/**
* Reads an array length from the current location in the buffer.
* @param pass indicates that the parsing should iterate past the array length
*/
[[nodiscard]]
constexpr auto arrayLength(const char *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(const char *name) noexcept {
return m_handler->stringLength(name);
}
[[nodiscard]]
static constexpr auto opType() noexcept {
return Handler::opType();
}
[[nodiscard]]
constexpr auto handler() noexcept {
return m_handler;
}
};
template<typename Handler, ox::OpType opType_v = Handler::opType()>
class ModelHandlerBase {
private:
ModelHandlerInterface<Handler, opType_v> m_interface;
public:
constexpr ModelHandlerBase() noexcept: m_interface(static_cast<Handler*>(this)) {}
constexpr ModelHandlerBase(const ModelHandlerBase&) noexcept: m_interface(static_cast<Handler*>(this)) {}
constexpr ModelHandlerBase(ModelHandlerBase&&) noexcept: m_interface(static_cast<Handler*>(this)) {}
[[nodiscard]]
constexpr auto interface() noexcept {
return &m_interface;
}
[[nodiscard]]
static constexpr ox::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
deps/ox/src/ox/model/modelops.hpp vendored Normal file
View File

@@ -0,0 +1,303 @@
/*
* Copyright 2015 - 2023 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <ox/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 OxError(0);
}
template<typename T>
constexpr Error field(const char*, T *v, int) noexcept {
vars[m_i++] = static_cast<void*>(v);
return OxError(0);
}
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 OxError(0);
}
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 OxError(0);
}
}
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 OxError(0);
}
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 OxError(0);
}
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 OxError(0);
}
}
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 OxError(0);
}
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 OxError(0);
}
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 OxError(0);
} else {
this->value = false;
return OxError(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 OxError(1);
}
}
++m_i;
return OxError(0);
}
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 OxError(0);
} else {
this->value = false;
return OxError(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);
oxIgnoreError(model(&dstFields, dst));
oxIgnoreError(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);
oxIgnoreError(model(&dstFields, dst));
oxIgnoreError(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);
oxIgnoreError(model(&aFields, a));
oxIgnoreError(model(&equals, b));
return equals.value;
}
}

23
deps/ox/src/ox/model/modelvalue.cpp vendored Normal file
View File

@@ -0,0 +1,23 @@
/*
* Copyright 2015 - 2022 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 "modelvalue.hpp"
namespace ox {
static_assert([]() -> ox::Error {
ox::ModelValue v;
oxReturnError(v.setType<int32_t>());
if (v.type() != ModelValue::Type::SignedInteger32) {
return OxError(1, "type is wrong");
}
//oxReturnError(v.set<int32_t>(5));
return {};
}() == OxError(0));
}

1128
deps/ox/src/ox/model/modelvalue.hpp vendored Normal file

File diff suppressed because it is too large Load Diff

21
deps/ox/src/ox/model/optype.hpp vendored Normal file
View File

@@ -0,0 +1,21 @@
/*
* Copyright 2015 - 2022 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,
};
}

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
deps/ox/src/ox/model/test/tests.cpp vendored Normal file
View File

@@ -0,0 +1,82 @@
/*
* Copyright 2015 - 2023 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;
};
oxModelBegin(TestType)
oxModelEnd()
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;
oxReturnError(v.setType<int32_t>());
//v.m_type = ox::ModelValue::getType<int32_t>();
if (v.type() != ox::ModelValue::Type::SignedInteger32) {
return OxError(1, "type is wrong");
}
oxReturnError(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;
}

150
deps/ox/src/ox/model/typenamecatcher.hpp vendored Normal file
View File

@@ -0,0 +1,150 @@
/*
* Copyright 2015 - 2023 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 TypeNameCatcher {
const char *name = "";
int version = 0;
constexpr TypeNameCatcher() noexcept = default;
template<typename T>
constexpr ox::Error setTypeInfo(
const char *n = T::TypeName,
int v = T::TypeVersion,
const Vector<String>& = {},
std::size_t = ModelFieldCount_v<T>) noexcept {
this->name = n;
this->version = v;
return {};
}
template<typename T>
constexpr Error field(const char*, T*, std::size_t) noexcept {
return OxError(0);
}
template<typename T>
constexpr Error field(const char*, T) noexcept {
return OxError(0);
}
template<typename ...Args>
constexpr Error fieldCString(Args&&...) noexcept {
return OxError(0);
}
static constexpr auto opType() noexcept {
return OpType::Reflect;
}
};
struct TypeInfoCatcher {
const char *name = "";
int version = 0;
constexpr TypeInfoCatcher() noexcept = default;
template<typename T = std::nullptr_t>
constexpr ox::Error setTypeInfo(
const char *n = T::TypeName,
int v = T::TypeVersion,
const Vector<String>& = {},
std::size_t = 0) noexcept {
this->name = n;
this->version = v;
return {};
}
template<typename T>
constexpr Error field(const char*, T*, std::size_t) noexcept {
return OxError(0);
}
template<typename T>
constexpr Error field(const char*, T) noexcept {
return OxError(0);
}
template<typename T>
constexpr Error fieldCString(const char*, T) noexcept {
return OxError(0);
}
static constexpr auto opType() noexcept {
return OpType::Reflect;
}
};
template<typename T>
constexpr int getModelTypeVersion(T *val) noexcept {
TypeInfoCatcher nc;
oxIgnoreError(model(&nc, val));
return nc.version;
}
template<typename T>
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>
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*>
constexpr Str getModelTypeName(T *val) noexcept {
TypeNameCatcher nc;
oxIgnoreError(model(&nc, val));
return nc.name;
}
template<typename T, typename Str = const char*>
constexpr 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>
consteval auto requireModelTypeName() noexcept {
constexpr auto name = getModelTypeName<T>();
return name;
}
template<typename T, typename Str = const char*>
constexpr auto ModelTypeName_v = getModelTypeName<T, Str>();
template<typename T, typename Str = const char*>
constexpr auto ModelTypeVersion_v = requireModelTypeVersion<T>();
}

185
deps/ox/src/ox/model/types.hpp vendored Normal file
View File

@@ -0,0 +1,185 @@
/*
* Copyright 2015 - 2023 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/bstring.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 StringView BasicString = "net.drinkingtea.ox.BasicString";
constexpr StringView BString = "net.drinkingtea.ox.BString";
constexpr StringView String = "B.string";
constexpr StringView Bool = "B.bool";
constexpr StringView Uint8 = "B.uint8";
constexpr StringView Uint16 = "B.uint16";
constexpr StringView Uint32 = "B.uint32";
constexpr StringView Uint64 = "B.uint64";
constexpr StringView Int8 = "B.int8";
constexpr StringView Int16 = "B.int16";
constexpr StringView Int32 = "B.int32";
constexpr StringView 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 isBString(const T*) noexcept {
return false;
}
template<std::size_t SmallVecSize>
consteval bool isBString(const BasicString<SmallVecSize>*) noexcept {
return true;
}
template<typename T>
constexpr bool isBString_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 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>
constexpr bool isArray_v = false;
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>
constexpr bool isSmartPtr_v = false;
template<typename T>
constexpr bool isSmartPtr_v<::ox::UniquePtr<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>
class UnionView {
protected:
int m_idx = -1;
typename enable_if<is_union_v<Union> || force, Union>::type *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;
}
};
}

116
deps/ox/src/ox/model/typestore.hpp vendored Normal file
View File

@@ -0,0 +1,116 @@
/*
* Copyright 2015 - 2022 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, UniquePtr<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);
oxRequire(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, {});
oxRequire(out, m_cache.at(typeId));
return out->get();
}
constexpr DescriptorType *getInit(CRStringView 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()) {
oxRequireM(dt, loadDescriptor(typeId));
for (auto &f : dt->fieldList) {
oxReturnError(this->getLoad(f.typeId).moveTo(f.type));
}
auto &out = m_cache[typeId];
out = std::move(dt);
return out.get();
} else {
return OxError(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, UniquePtr<DescriptorType> dt) noexcept {
m_cache[typeId] = std::move(dt);
}
constexpr void set(const auto &typeId, DescriptorType *dt) noexcept {
m_cache[typeId] = UniquePtr<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<UniquePtr<DescriptorType>> loadDescriptor(ox::CRStringView) noexcept {
return OxError(1);
}
Result<UniquePtr<DescriptorType>> loadDescriptor(ox::CRStringView name, int version,
const ox::TypeParamPack &typeParams) noexcept {
const auto typeId = buildTypeId(name, version, typeParams);
return loadDescriptor(typeId);
}
};
}

161
deps/ox/src/ox/model/walk.hpp vendored Normal file
View File

@@ -0,0 +1,161 @@
/*
* Copyright 2015 - 2023 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <ox/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 {
oxRequire(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.push_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
oxRequire(arrayLen, rdr->arrayLength(field.fieldName.c_str(), true));
auto child = rdr->child(field.fieldName.c_str());
oxReturnError(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);
oxReturnError(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:
oxReturnError(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);
oxReturnError(model(&child, walker));
walker->popType();
rdr->nextField();
} else {
// skip and discard absent field
int discard;
oxReturnError(rdr->field(field.fieldName.c_str(), &discard));
}
break;
}
}
walker->popNamePath();
return OxError(0);
}
template<typename Reader, typename FH>
constexpr Error model(Reader *rdr, DataWalker<Reader, FH> *walker) noexcept {
oxRequire(type, walker->type());
auto typeName = type->typeName.c_str();
auto typeVersion = type->typeVersion;
auto typeParams = type->typeParams;
auto &fields = type->fieldList;
oxReturnError(rdr->setTypeInfo(typeName, typeVersion, typeParams, fields.size()));
for (const auto &field : fields) {
oxReturnError(parseField(field, rdr, walker));
}
return OxError(0);
}
template<typename Reader, typename Handler>
constexpr Error walkModel(DescriptorType *type, Reader_c auto &reader, Handler handler) noexcept {
DataWalker<Reader, Handler> walker(type, handler);
Reader rdr(reader);
return model(&rdr, &walker);
}
template<typename Reader, typename Handler>
constexpr Error walkModel(DescriptorType *type, const char *data, std::size_t dataLen, Handler handler) noexcept {
DataWalker<Reader, Handler> walker(type, handler);
Reader rdr(reinterpret_cast<const uint8_t*>(data), dataLen);
return model(&rdr, &walker);
}
}

46
deps/ox/src/ox/oc/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,46 @@
add_library(
OxOrganicClaw
read.cpp
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
)
install(
FILES
oc.hpp
read.hpp
write.hpp
DESTINATION
include/ox/oc
)
install(TARGETS OxOrganicClaw
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
if(OX_RUN_TESTS)
add_subdirectory(test)
endif()

12
deps/ox/src/ox/oc/oc.hpp vendored Normal file
View File

@@ -0,0 +1,12 @@
/*
* Copyright 2015 - 2022 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "read.hpp"
#include "write.hpp"

328
deps/ox/src/ox/oc/read.cpp vendored Normal file
View File

@@ -0,0 +1,328 @@
/*
* Copyright 2015 - 2022 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 "read.hpp"
namespace ox {
OrganicClawReader::OrganicClawReader(const uint8_t *buff, std::size_t buffSize) {
auto json = reinterpret_cast<const char*>(buff);
auto jsonLen = ox_strnlen(json, buffSize);
Json::CharReaderBuilder parserBuilder;
auto parser = std::unique_ptr<Json::CharReader>(parserBuilder.newCharReader());
if (!parser->parse(json, json + jsonLen, &m_json, nullptr)) {
throw OxException(1, "Could not parse JSON");
}
}
OrganicClawReader::OrganicClawReader(const char *json, std::size_t jsonLen) {
Json::CharReaderBuilder parserBuilder;
auto parser = std::unique_ptr<Json::CharReader>(parserBuilder.newCharReader());
if (!parser->parse(json, json + jsonLen, &m_json, nullptr)) {
throw OxException(1, "Could not parse JSON");
}
}
OrganicClawReader::OrganicClawReader(Json::Value json, int unionIdx) noexcept:
m_json(std::move(json)),
m_unionIdx(unionIdx) {
}
Error OrganicClawReader::field(const char *key, int8_t *val) noexcept {
auto err = OxError(0);
if (targetValid()) {
const auto &jv = value(key);
if (jv.empty()) {
*val = 0;
} else if (jv.isInt()) {
*val = static_cast<int8_t>(jv.asInt());
} else {
err = OxError(1, "Type mismatch");
}
}
++m_fieldIt;
return err;
}
Error OrganicClawReader::field(const char *key, int16_t *val) noexcept {
auto err = OxError(0);
if (targetValid()) {
const auto &jv = value(key);
if (jv.empty()) {
*val = 0;
} else if (jv.isInt()) {
*val = static_cast<int16_t>(jv.asInt());
} else {
err = OxError(1, "Type mismatch");
}
}
++m_fieldIt;
return err;
}
Error OrganicClawReader::field(const char *key, int32_t *val) noexcept {
auto err = OxError(0);
if (targetValid()) {
const auto &jv = value(key);
if (jv.empty()) {
*val = 0;
} else if (jv.isInt()) {
*val = static_cast<int32_t>(jv.asInt());
} else {
err = OxError(1, "Type mismatch");
}
}
++m_fieldIt;
return err;
}
Error OrganicClawReader::field(const char *key, int64_t *val) noexcept {
auto err = OxError(0);
if (targetValid()) {
const auto &jv = value(key);
if (jv.empty()) {
*val = 0;
} else if (jv.isInt() || jv.isInt64()) {
*val = static_cast<int64_t>(jv.asInt64());
} else {
err = OxError(1, "Type mismatch");
}
}
++m_fieldIt;
return err;
}
Error OrganicClawReader::field(const char *key, uint8_t *val) noexcept {
auto err = OxError(0);
if (targetValid()) {
const auto &jv = value(key);
if (jv.empty()) {
*val = 0;
} else if (jv.isUInt()) {
*val = static_cast<uint8_t>(jv.asUInt());
} else {
err = OxError(1, "Type mismatch");
}
}
++m_fieldIt;
return err;
}
Error OrganicClawReader::field(const char *key, uint16_t *val) noexcept {
auto err = OxError(0);
if (targetValid()) {
const auto &jv = value(key);
if (jv.empty()) {
*val = 0;
} else if (jv.isUInt()) {
*val = static_cast<uint16_t>(jv.asUInt());
} else {
err = OxError(1, "Type mismatch");
}
}
++m_fieldIt;
return err;
}
Error OrganicClawReader::field(const char *key, uint32_t *val) noexcept {
auto err = OxError(0);
if (targetValid()) {
const auto &jv = value(key);
if (jv.empty()) {
*val = 0;
} else if (jv.isUInt()) {
*val = static_cast<uint32_t>(jv.asUInt());
} else {
err = OxError(1, "Type mismatch");
}
}
++m_fieldIt;
return err;
}
Error OrganicClawReader::field(const char *key, uint64_t *val) noexcept {
auto err = OxError(0);
if (targetValid()) {
const auto &jv = value(key);
if (jv.empty()) {
*val = 0;
} else if (jv.isUInt() || jv.isUInt64()) {
*val = static_cast<uint64_t>(jv.asUInt64());
} else {
err = OxError(1, "Type mismatch");
}
}
++m_fieldIt;
return err;
}
Error OrganicClawReader::field(const char *key, bool *val) noexcept {
auto err = OxError(0);
if (targetValid()) {
const auto &jv = value(key);
if (jv.empty()) {
*val = false;
} else if (jv.isBool()) {
*val = jv.asBool();
} else {
err = OxError(1, "Type mismatch");
}
}
++m_fieldIt;
return err;
}
Error OrganicClawReader::fieldCString(const char *key, char *val, std::size_t buffLen) noexcept {
auto err = OxError(0);
const char *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<std::size_t>(end - begin);
auto data = val;
if (strSize >= buffLen) {
err = OxError(2, "String size exceeds capacity of destination");
} else {
ox_memcpy(data, begin, static_cast<std::size_t>(strSize));
data[strSize] = 0;
}
} else {
err = OxError(1, "Type mismatch");
}
}
++m_fieldIt;
return err;
}
Error OrganicClawReader::fieldCString(const char *key, char **val) noexcept {
auto err = OxError(0);
const char *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<std::size_t>(end - begin);
safeDelete(*val);
*val = new char[strSize + 1];
ox_memcpy(data, begin, static_cast<std::size_t>(strSize));
data[strSize] = 0;
} else {
err = OxError(1, "Type mismatch");
}
}
++m_fieldIt;
return err;
}
Error OrganicClawReader::fieldCString(const char *key, char **val, std::size_t buffLen) noexcept {
auto err = OxError(0);
const char *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<std::size_t>(end - begin);
auto data = val;
if (strSize >= buffLen) {
safeDelete(*val);
*val = new char[strSize + 1];
}
ox_memcpy(data, begin, static_cast<std::size_t>(strSize));
data[strSize] = 0;
} else {
err = OxError(1, "Type mismatch");
}
}
++m_fieldIt;
return err;
}
Error OrganicClawReader::field(const char *key, UUID *val) noexcept {
UUIDStr str;
oxReturnError(field(key, &str));
return UUID::fromString(str).moveTo(*val);
}
Result<std::size_t> OrganicClawReader::arrayLength(const char *key, bool) noexcept {
const auto &jv = value(key);
if (jv.empty()) {
return 0;
}
if (jv.isArray()) {
return jv.size();
}
return OxError(1, "Type mismatch");
}
[[nodiscard]]
std::size_t OrganicClawReader::stringLength(const char *key) noexcept {
const char *begin = nullptr, *end = nullptr;
const auto &jv = value(key);
if (jv.empty()) {
return 0;
}
if (jv.isString()) {
jv.getString(&begin, &end);
return static_cast<std::size_t>(end - begin);
}
return OxError(1, "Type mismatch");
}
OrganicClawReader OrganicClawReader::child(const char *key, int unionIdx) noexcept {
return OrganicClawReader(value(key), unionIdx);
}
bool OrganicClawReader::fieldPresent(const char *key) noexcept {
return !m_json[key].empty();
}
int OrganicClawReader::whichFieldPresent(const char *name, const ModelUnion &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(const char *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;
}
}

285
deps/ox/src/ox/oc/read.hpp vendored Normal file
View File

@@ -0,0 +1,285 @@
/*
* Copyright 2015 - 2023 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <json/json.h>
#include <ox/model/fieldcounter.hpp>
#include <ox/model/modelhandleradaptor.hpp>
#include <ox/model/optype.hpp>
#include <ox/model/typenamecatcher.hpp>
#include <ox/model/types.hpp>
#include <ox/std/buffer.hpp>
#include <ox/std/byteswap.hpp>
#include <ox/std/hashmap.hpp>
#include <ox/std/memops.hpp>
#include <ox/std/memory.hpp>
#include <ox/std/string.hpp>
#include <ox/std/uuid.hpp>
namespace ox {
class OrganicClawReader {
private:
Json::Value m_json;
Json::ArrayIndex m_fieldIt = 0;
int m_unionIdx = -1;
public:
OrganicClawReader() noexcept = default;
OrganicClawReader(const uint8_t *buff, std::size_t buffSize);
OrganicClawReader(const char *json, std::size_t buffSize);
explicit OrganicClawReader(Json::Value json, int unionIdx = -1) noexcept;
Error field(const char *key, int8_t *val) noexcept;
Error field(const char *key, int16_t *val) noexcept;
Error field(const char *key, int32_t *val) noexcept;
Error field(const char *key, int64_t *val) noexcept;
Error field(const char *key, uint8_t *val) noexcept;
Error field(const char *key, uint16_t *val) noexcept;
Error field(const char *key, uint32_t *val) noexcept;
Error field(const char *key, uint64_t *val) noexcept;
Error field(const char *key, bool *val) noexcept;
// array handler
template<typename T>
Error field(const char *key, T *val, std::size_t len) noexcept;
template<typename T>
Error field(const char*, HashMap<String, T> *val) noexcept;
template<typename T>
Error field(const char *key, T *val) noexcept;
template<typename U, bool force = false>
Error field(const char *key, UnionView<U, force> val) noexcept;
template<std::size_t L>
Error field(const char *key, BasicString<L> *val) noexcept;
template<std::size_t L>
Error field(const char *key, BString<L> *val) noexcept;
Error fieldCString(const char *key, char *val, std::size_t buffLen) noexcept;
Error fieldCString(const char *key, char **val) noexcept;
Error fieldCString(const char *key, char **val, std::size_t buffLen) noexcept;
Error field(const char *key, UUID *val) noexcept;
/**
* Reads an array length from the current location in the buffer.
* @param pass indicates that the parsing should iterate past the array length
*/
Result<std::size_t> arrayLength(const char *key, bool pass = true) noexcept;
/**
* Reads an string length from the current location in the buffer.
*/
[[nodiscard]]
std::size_t stringLength(const char *name) noexcept;
template<typename T = void>
constexpr ox::Error setTypeInfo() noexcept {
return {};
}
template<typename T = void>
constexpr ox::Error setTypeInfo(const char*) noexcept {
return {};
}
template<typename T = void>
constexpr ox::Error setTypeInfo(const char*, int, const Vector<String>& = {}) noexcept {
return {};
}
template<typename T = void>
constexpr ox::Error setTypeInfo(const char*, int, const Vector<String>& = {}, std::size_t = {}) noexcept {
return {};
}
/**
* Returns a OrganicClawReader to parse a child object.
*/
[[nodiscard]]
OrganicClawReader child(const char *key, int unionIdx = -1) noexcept;
// compatibility stub
constexpr void nextField() noexcept {}
[[nodiscard]]
bool fieldPresent(const char *key) noexcept;
[[nodiscard]]
int whichFieldPresent(const char *name, const ModelUnion &u) const noexcept;
[[nodiscard]]
static constexpr auto opType() noexcept {
return OpType::Read;
}
private:
[[nodiscard]]
Json::Value &value(const char *key) noexcept;
[[nodiscard]]
bool targetValid() const noexcept;
};
template<typename T>
Error OrganicClawReader::field(const char *key, T *val) noexcept {
auto err = OxError(0);
if constexpr(isVector_v<T>) {
const auto &srcVal = value(key);
const auto srcSize = srcVal.size();
oxReturnError(ox::resizeVector(*val, srcSize));
err = field(key, val->data(), val->size());
} else if constexpr(isArray_v<T>) {
const auto &srcVal = value(key);
const auto srcSize = srcVal.size();
if (srcSize > val->size()) {
err = OxError(1, "Input array is too long");
} else {
err = field(key, val->data(), val->size());
}
} else if (targetValid()) {
const auto &jv = value(key);
if (jv.empty() || jv.isObject()) {
auto reader = child(key);
ModelHandlerInterface handler(&reader);
err = model(&handler, val);
} else {
err = OxError(1, "Type mismatch");
}
}
++m_fieldIt;
return err;
}
template<typename U, bool force>
Error OrganicClawReader::field(const char *key, UnionView<U, force> val) noexcept {
auto err = OxError(0);
if (targetValid()) {
const auto &jv = value(key);
if (jv.empty() || jv.isObject()) {
auto reader = child(key, val.idx());
ModelHandlerInterface handler(&reader);
err = model(&handler, val.get());
} else {
err = OxError(1, "Type mismatch");
}
}
++m_fieldIt;
return err;
}
template<std::size_t L>
Error OrganicClawReader::field(const char *key, BasicString<L> *val) noexcept {
auto err = OxError(0);
if (targetValid()) {
const auto &jv = value(key);
if (jv.empty()) {
*val = BasicString<L>{};
} else if (jv.isString()) {
*val = jv.asString().c_str();
} else {
err = OxError(1, "Type mismatch");
}
}
++m_fieldIt;
return err;
}
template<std::size_t L>
Error OrganicClawReader::field(const char *key, BString<L> *val) noexcept {
return fieldCString(key, val->data(), val->cap());
}
// array handler
template<typename T>
Error OrganicClawReader::field(const char *key, T *val, std::size_t valLen) noexcept {
const auto &srcVal = value(key);
if (!srcVal.isNull() && !srcVal.isArray()) {
return OxError(1, "Type mismatch");
}
auto srcSize = srcVal.size();
if (srcSize > valLen) {
return OxError(1);
}
OrganicClawReader r(srcVal);
ModelHandlerInterface handler{&r};
for (decltype(srcSize) i = 0; i < srcSize; ++i) {
oxReturnError(handler.field("", &val[i]));
}
return OxError(0);
}
template<typename T>
Error OrganicClawReader::field(const char *key, HashMap<String, T> *val) noexcept {
const auto &srcVal = value(key);
if (!srcVal.isObject()) {
return OxError(1, "Type mismatch");
}
auto keys = srcVal.getMemberNames();
auto srcSize = srcVal.size();
OrganicClawReader r(srcVal);
ModelHandlerInterface handler{&r};
for (decltype(srcSize) i = 0; i < srcSize; ++i) {
const auto k = keys[i].c_str();
oxReturnError(handler.field(k, &val->operator[](k)));
}
return OxError(0);
}
Error readOC(const char *buff, std::size_t buffSize, auto *val) noexcept {
// OrganicClawReader constructor can throw, but readOC should return its errors.
try {
Json::Value doc;
Json::CharReaderBuilder parserBuilder;
auto parser = UniquePtr<Json::CharReader>(parserBuilder.newCharReader());
if (!parser->parse(buff, buff + buffSize, &doc, nullptr)) {
return OxError(1, "Could not parse JSON");
}
OrganicClawReader reader(buff, buffSize);
ModelHandlerInterface handler(&reader);
return model(&handler, val);
} catch (const Error &err) {
return err;
} catch (...) {
return OxError(1, "Unknown Error");
}
}
template<typename T>
Result<T> readOC(const char *json, std::size_t jsonLen) noexcept {
T val;
oxReturnError(readOC(json, jsonLen, &val));
return val;
}
template<typename T>
Result<T> readOC(const char *json) noexcept {
return readOC<T>(json, ox_strlen(json));
}
template<typename T>
Result<T> readOC(const Buffer &buff) noexcept {
return readOC<T>(buff.data(), buff.size());
}
}

14
deps/ox/src/ox/oc/test/CMakeLists.txt vendored Normal file
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
deps/ox/src/ox/oc/test/tests.cpp vendored Normal file
View File

@@ -0,0 +1,364 @@
/*
* Copyright 2015 - 2023 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::BString<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 {
oxReturnError(io->template setTypeInfo<TestUnion>());
oxReturnError(io->field("Bool", &obj->Bool));
oxReturnError(io->field("Int", &obj->Int));
oxReturnError(io->fieldCString("String", &obj->String));
return OxError(0);
}
constexpr ox::Error model(auto *io, ox::CommonPtrWith<TestStructNest> auto *obj) noexcept {
oxReturnError(io->template setTypeInfo<TestStructNest>());
oxReturnError(io->field("Bool", &obj->Bool));
oxReturnError(io->field("Int", &obj->Int));
oxReturnError(io->field("String", &obj->String));
return OxError(0);
}
constexpr ox::Error model(auto *io, ox::CommonPtrWith<TestStruct> auto *obj) noexcept {
oxReturnError(io->template setTypeInfo<TestStruct>());
oxReturnError(io->field("Bool", &obj->Bool));
oxReturnError(io->field("Int", &obj->Int));
oxReturnError(io->field("Int1", &obj->Int1));
oxReturnError(io->field("Int2", &obj->Int2));
oxReturnError(io->field("Int3", &obj->Int3));
oxReturnError(io->field("Int4", &obj->Int4));
oxReturnError(io->field("Int5", &obj->Int5));
oxReturnError(io->field("Int6", &obj->Int6));
oxReturnError(io->field("Int7", &obj->Int7));
oxReturnError(io->field("Int8", &obj->Int8));
oxReturnError(io->field("unionIdx", &obj->unionIdx));
if (io->opType() == ox::OpType::Reflect) {
oxReturnError(io->field("Union", ox::UnionView{&obj->Union, 0}));
} else {
oxReturnError(io->field("Union", ox::UnionView{&obj->Union, obj->unionIdx}));
}
oxReturnError(io->field("String", &obj->String));
oxReturnError(io->field("List", obj->List, 4));
oxReturnError(io->field("Map", &obj->Map));
oxReturnError(io->field("EmptyStruct", &obj->EmptyStruct));
oxReturnError(io->field("Struct", &obj->Struct));
return OxError(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 OxError(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;
oxReturnError(testOut.setType(type.value));
oxAssert(ox::readOC(dataBuff.data(), dataBuff.size(), &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 OxError(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");
oxReturnError(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 OxError(0);
}
));
return OxError(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;
}

50
deps/ox/src/ox/oc/write.cpp vendored Normal file
View File

@@ -0,0 +1,50 @@
/*
* Copyright 2015 - 2022 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 "write.hpp"
namespace ox {
OrganicClawWriter::OrganicClawWriter(int unionIdx) noexcept: m_unionIdx(unionIdx) {
}
OrganicClawWriter::OrganicClawWriter(Json::Value json, int unionIdx) noexcept:
m_json(std::move(json)),
m_unionIdx(unionIdx) {
}
Error OrganicClawWriter::fieldCString(const char *key, const char *const*val, int len) noexcept {
if (targetValid() && len) {
value(key) = *val;
}
++m_fieldIt;
return OxError(0);
}
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, const UUID *uuid) noexcept {
const auto uuidStr = uuid->toString();
if (targetValid() && uuidStr.len()) {
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];
}
}
}

251
deps/ox/src/ox/oc/write.hpp vendored Normal file
View File

@@ -0,0 +1,251 @@
/*
* Copyright 2015 - 2023 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <json/json.h>
#include <ox/model/fieldcounter.hpp>
#include <ox/model/modelhandleradaptor.hpp>
#include <ox/model/optype.hpp>
#include <ox/model/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;
protected:
Json::Value m_json;
Json::ArrayIndex m_fieldIt = 0;
int m_unionIdx = -1;
public:
explicit OrganicClawWriter(int unionIdx = -1) noexcept;
explicit OrganicClawWriter(Json::Value json, int unionIdx = -1) noexcept;
Error field(const char *key, const int8_t *val) noexcept {
if (*val) {
value(key) = *val;
}
++m_fieldIt;
return OxError(0);
}
Error field(const char *key, const int16_t *val) noexcept {
if (*val) {
value(key) = *val;
}
++m_fieldIt;
return OxError(0);
}
Error field(const char *key, const int32_t *val) noexcept {
if (*val) {
value(key) = *val;
}
++m_fieldIt;
return OxError(0);
}
Error field(const char *key, const int64_t *val) noexcept {
if (*val) {
value(key) = *val;
}
++m_fieldIt;
return OxError(0);
}
Error field(const char *key, const uint8_t *val) noexcept {
if (*val) {
value(key) = *val;
}
++m_fieldIt;
return OxError(0);
}
Error field(const char *key, const uint16_t *val) noexcept {
if (targetValid() && *val) {
value(key) = *val;
}
++m_fieldIt;
return OxError(0);
}
Error field(const char *key, const uint32_t *val) noexcept {
if (targetValid() && *val) {
value(key) = *val;
}
++m_fieldIt;
return OxError(0);
}
Error field(const char *key, const uint64_t *val) noexcept {
if (targetValid() && *val) {
value(key) = *val;
}
++m_fieldIt;
return OxError(0);
}
Error field(const char *key, const bool *val) noexcept {
if (targetValid() && *val) {
value(key) = *val;
}
++m_fieldIt;
return {};
}
template<typename U, bool force = true>
Error field(const char*, UnionView<U, force> val) noexcept;
template<typename T>
Error field(const char *key, const HashMap<String, T> *val) noexcept {
if (targetValid()) {
const auto &keys = val->keys();
OrganicClawWriter w;
ModelHandlerInterface<OrganicClawWriter, OpType::Write> handler{&w};
for (std::size_t i = 0; i < keys.size(); ++i) {
const auto k = keys[i].c_str();
if (k) [[likely]] {
oxRequireM(value, val->at(k));
oxReturnError(handler.field(k, value));
}
}
value(key) = w.m_json;
}
++m_fieldIt;
return {};
}
template<std::size_t L>
Error field(const char *key, const BString<L> *val) noexcept {
if (targetValid() && val->len()) {
value(key) = val->c_str();
}
++m_fieldIt;
return {};
}
template<std::size_t L>
Error field(const char *key, const BasicString<L> *val) noexcept {
if (targetValid() && val->len()) {
value(key) = val->c_str();
}
++m_fieldIt;
return OxError(0);
}
Error fieldCString(const char*, const char *const*val, int len) noexcept;
Error fieldCString(const char *name, const char *const*val) noexcept;
Error field(const char *key, const UUID *uuid) noexcept;
template<typename T>
Error field(const char*, const T *val, std::size_t len) noexcept;
template<typename T>
Error field(const char*, const T *val) noexcept;
template<typename T>
constexpr ox::Error setTypeInfo(
const char* = T::TypeName,
int = T::TypeVersion) noexcept {
return {};
}
template<typename T>
constexpr ox::Error setTypeInfo(
const char*,
int,
const Vector<String>&,
std::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(const char *key) noexcept;
};
template<typename T>
Error OrganicClawWriter::field(const char *key, const T *val, std::size_t len) noexcept {
if (targetValid() && len) {
OrganicClawWriter w((Json::Value(Json::arrayValue)));
ModelHandlerInterface<OrganicClawWriter, OpType::Write> handler{&w};
for (std::size_t i = 0; i < len; ++i) {
oxReturnError(handler.field("", &val[i]));
}
value(key) = w.m_json;
}
++m_fieldIt;
return OxError(0);
}
template<typename T>
Error OrganicClawWriter::field(const char *key, const T *val) noexcept {
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};
oxReturnError(model(&handler, val));
if (!w.m_json.isNull()) {
value(key) = w.m_json;
}
}
++m_fieldIt;
return OxError(0);
}
template<typename U, bool force>
Error OrganicClawWriter::field(const char *key, UnionView<U, force> val) noexcept {
if (targetValid()) {
OrganicClawWriter w(val.idx());
ModelHandlerInterface<OrganicClawWriter, OpType::Write> handler{&w};
oxReturnError(model(&handler, val.get()));
if (!w.m_json.isNull()) {
value(key) = w.m_json;
}
}
++m_fieldIt;
return OxError(0);
}
Result<Buffer> writeOC(const auto &val) noexcept {
OrganicClawWriter writer;
ModelHandlerInterface<OrganicClawWriter, OpType::Write> handler(&writer);
oxReturnError(model(&handler, &val));
Json::StreamWriterBuilder jsonBuilder;
const auto str = Json::writeString(jsonBuilder, writer.m_json);
Buffer buff(str.size() + 1);
memcpy(buff.data(), str.c_str(), str.size() + 1);
return buff;
}
}

34
deps/ox/src/ox/preloader/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,34 @@
add_library(
OxPreloader
preloader.cpp
)
if(NOT MSVC)
target_compile_options(OxPreloader PRIVATE -Wsign-conversion)
target_compile_options(OxPreloader PRIVATE -Wconversion)
endif()
target_link_libraries(
OxPreloader PUBLIC
OxClaw
OxModel
OxStd
)
install(
FILES
alignmentcatcher.hpp
platspecs.hpp
preloader.hpp
unionsizecatcher.hpp
DESTINATION
include/nostalgia/preloader
)
install(
TARGETS
OxPreloader
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)

View File

@@ -0,0 +1,69 @@
/*
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#pragma once
#include <ox/model/modelhandleradaptor.hpp>
#include <ox/model/optype.hpp>
#include <ox/model/types.hpp>
#include <ox/std/error.hpp>
#include <ox/std/string.hpp>
#include <ox/std/types.hpp>
namespace ox {
template<typename PlatSpec, typename T>
[[nodiscard]]
constexpr std::size_t alignOf(const T &v) noexcept;
template<typename PlatSpec, typename T>
[[nodiscard]]
constexpr std::size_t sizeOf(const T *t) noexcept;
template<typename PlatSpec>
struct AlignmentCatcher: public ModelHandlerBase<AlignmentCatcher<PlatSpec>, OpType::Reflect> {
std::size_t biggestAlignment = 1;
template<typename T>
constexpr ox::Error setTypeInfo(
const char* = T::TypeName,
int = T::TypeVersion) noexcept {
return {};
}
template<typename T>
constexpr ox::Error setTypeInfo(
const char*,
int,
const Vector<String>&,
std::size_t) noexcept {
return {};
}
template<typename T, bool force>
constexpr ox::Error field(CRStringView name, const UnionView<T, force> val) noexcept {
return field(name, val.get());
}
template<typename T>
constexpr ox::Error field(CRStringView, const T *val) noexcept {
if constexpr(ox::is_pointer_v<T> || ox::is_integer_v<T>) {
biggestAlignment = ox::max(biggestAlignment, PlatSpec::alignOf(*val));
} else {
biggestAlignment = ox::max(biggestAlignment, alignOf<PlatSpec>(*val));
}
return {};
}
template<typename T>
constexpr ox::Error field(CRStringView, const T *val, std::size_t cnt) noexcept {
for (std::size_t i = 0; i < cnt; ++i) {
oxReturnError(field(nullptr, &val[i]));
}
return {};
}
};
}

56
deps/ox/src/ox/preloader/platspecs.hpp vendored Normal file
View File

@@ -0,0 +1,56 @@
/*
* Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#pragma once
#include <ox/std/serialize.hpp>
#include <ox/std/typetraits.hpp>
#include "alignmentcatcher.hpp"
#include "sizecatcher.hpp"
namespace ox {
struct NativePlatSpec {
static constexpr std::size_t RomStart = 1024;
using PtrType = uintptr_t;
using size_t = std::size_t;
template<typename T>
[[nodiscard]]
static constexpr std::size_t alignOf(const T &v) noexcept {
if constexpr(ox::is_integral_v<T>) {
return alignof(T);
} else if constexpr(ox::is_pointer_v<T>) {
const PtrType p = 0;
return alignOf(p);
} else {
AlignmentCatcher<NativePlatSpec> c;
oxAssert(model(c.interface(), &v), "Could not get alignment for type");
return c.biggestAlignment;
}
}
[[nodiscard]]
static constexpr auto correctEndianness(auto v) noexcept {
return v;
}
};
template<typename PlatSpec, typename T>
[[nodiscard]]
constexpr std::size_t alignOf(const T &v) noexcept {
if constexpr(ox::is_integral_v<T>) {
return alignof(T);
} else if constexpr(ox::is_pointer_v<T>) {
typename PlatSpec::PtrType p = 0;
return PlatSpec::alignOf(p);
} else {
AlignmentCatcher<NativePlatSpec> c;
oxAssert(model(c.interface(), &v), "Could not get alignment for type");
return c.biggestAlignment;
}
}
}

11
deps/ox/src/ox/preloader/preloader.cpp vendored Normal file
View File

@@ -0,0 +1,11 @@
/*
* Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include "preloader.hpp"
namespace ox {
template class Preloader<NativePlatSpec>;
}

363
deps/ox/src/ox/preloader/preloader.hpp vendored Normal file
View File

@@ -0,0 +1,363 @@
/*
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#pragma once
#include <ox/std/array.hpp>
#include <ox/std/buffer.hpp>
#include <ox/std/byteswap.hpp>
#include <ox/std/error.hpp>
#include <ox/std/memops.hpp>
#include <ox/std/memory.hpp>
#include <ox/std/types.hpp>
#include <ox/std/typetraits.hpp>
#include <ox/std/units.hpp>
#include <ox/model/modelhandleradaptor.hpp>
#include "platspecs.hpp"
namespace ox {
template<typename PlatSpec>
class Preloader;
template<typename PlatSpec>
class Preloader: public ModelHandlerBase<Preloader<PlatSpec>, OpType::Reflect> {
private:
using PtrType = typename PlatSpec::PtrType;
static constexpr auto PtrSize = sizeof(PtrType);
class UnionIdxTracker {
private:
int m_unionIdx = -1;
int m_it = 0;
public:
constexpr UnionIdxTracker() noexcept = default;
constexpr explicit UnionIdxTracker(int idx) noexcept: m_unionIdx(idx) {}
constexpr auto checkAndIterate() noexcept {
return m_unionIdx == -1 || m_it++ == m_unionIdx;
}
};
ox::Buffer m_buff;
ox::BufferWriter m_writer;
// list of all the places where ptrs were written to buffer
struct PtrPair {
std::size_t loc = 0;
typename PlatSpec::PtrType value = 0;
constexpr PtrPair() noexcept = default;
constexpr PtrPair(std::size_t pLoc, typename PlatSpec::PtrType pValue) noexcept:
loc(pLoc), value(pValue) {}
};
ox::Vector<PtrPair> m_ptrs;
ox::Vector<UnionIdxTracker, 8> m_unionIdx = {{}};
class AllocStackItem {
public:
PtrType restore = 0;
ox::ios_base::seekdir seekdir = ox::ios_base::end;
constexpr AllocStackItem(PtrType pRestore, ox::ios_base::seekdir pSeekdir = ox::ios_base::end) noexcept:
restore(pRestore), seekdir(pSeekdir) {}
};
ox::Vector<AllocStackItem, 8> m_allocStack;
constexpr Preloader() noexcept: m_writer(&m_buff) {}
public:
Preloader(const Preloader &src) = delete;
Preloader(Preloader &&src) = delete;
const Preloader &operator=(const Preloader &src) = delete;
const Preloader &operator=(Preloader &&src) = delete;
constexpr static ox::Result<ox::UniquePtr<Preloader>> make(ox::ios_base::seekdir anchor = ox::ios_base::cur,
std::size_t sz = 0) noexcept;
template<typename T>
constexpr ox::Error setTypeInfo(
const char* = T::TypeName,
int = T::TypeVersion) noexcept {
return {};
}
template<typename T>
constexpr ox::Error setTypeInfo(
const char*,
int,
const Vector<String>&,
std::size_t) noexcept {
return {};
}
template<typename U, bool force>
constexpr ox::Error field(CRStringView, const ox::UnionView<U, force> val) noexcept;
template<typename T>
constexpr ox::Error field(CRStringView, const T *val) noexcept;
template<std::size_t SmallStringSize>
constexpr ox::Error field(CRStringView, const ox::BasicString<SmallStringSize> *val) noexcept;
template<typename T, std::size_t sz>
constexpr ox::Error field(CRStringView, const ox::Array<T, sz> *valArray) noexcept;
template<typename T>
constexpr ox::Error field(CRStringView, const T **val, std::size_t cnt) noexcept;
constexpr ox::Result<std::size_t> startAlloc(std::size_t sz) noexcept;
constexpr ox::Result<std::size_t> startAlloc(std::size_t sz, std::size_t restore) noexcept;
constexpr ox::Error endAlloc() noexcept;
constexpr ox::Error offsetPtrs(std::size_t offset) noexcept;
[[nodiscard]]
constexpr auto &buff() const noexcept {
return m_buff;
}
template<typename T>
constexpr ox::Error pad(const T*) noexcept;
private:
constexpr ox::Error fieldVector(CRStringView name, const ox::ModelValueVector *val) noexcept;
template<typename T, std::size_t SmallVectorSize, typename Allocator>
constexpr ox::Error fieldVector(CRStringView, const ox::Vector<T, SmallVectorSize, Allocator> *val) noexcept;
constexpr ox::Error fieldVector(CRStringView, const auto *val, ox::VectorMemMap<PlatSpec> vecVal) noexcept;
constexpr bool unionCheckAndIt() noexcept;
};
template<typename PlatSpec>
constexpr ox::Result<ox::UniquePtr<Preloader<PlatSpec>>>
Preloader<PlatSpec>::make(ox::ios_base::seekdir anchor, std::size_t sz) noexcept {
auto p = ox::UniquePtr<Preloader>(new Preloader);
if (const auto err = p->m_writer.seekp(0, anchor)) {
return {std::move(p), err};
}
if (const auto err = p->m_writer.write(nullptr, sz)) {
return {std::move(p), err};
}
if (const auto err = p->m_writer.seekp(p->m_writer.tellp() - sz)) {
return {std::move(p), err};
}
return p;
}
template<typename PlatSpec>
template<typename U, bool force>
constexpr ox::Error Preloader<PlatSpec>::field(CRStringView, const ox::UnionView<U, force> val) noexcept {
if (!unionCheckAndIt()) {
return {};
}
oxReturnError(pad(val.get()));
m_unionIdx.emplace_back(val.idx());
const auto err = preload<PlatSpec, U>(this, val.get());
m_unionIdx.pop_back();
return err;
}
template<typename PlatSpec>
template<typename T>
constexpr ox::Error Preloader<PlatSpec>::field(CRStringView name, const T *val) noexcept {
if (!unionCheckAndIt()) {
return {};
}
oxReturnError(pad(val));
if constexpr(ox::is_integral_v<T>) {
//oxDebugf("Preloader::field(name, val): {}", name);
return ox::serialize(&m_writer, PlatSpec::correctEndianness(*val));
} else if constexpr(ox::is_pointer_v<T>) {
const PtrType a = startAlloc(sizeOf<PlatSpec>(*val), m_writer.tellp()) + PlatSpec::RomStart;
oxReturnError(field(name, *val));
oxReturnError(endAlloc());
return ox::serialize(&m_writer, PlatSpec::correctEndianness(a));
} else if constexpr(ox::isVector_v<T> || ox::is_same_v<T, ox::ModelValueVector>) {
return fieldVector(name, val);
} else {
m_unionIdx.emplace_back(-1);
const auto out = preload<PlatSpec, T>(this, val);
m_unionIdx.pop_back();
return out;
}
}
template<typename PlatSpec>
template<std::size_t SmallStringSize>
constexpr ox::Error Preloader<PlatSpec>::field(CRStringView, const ox::BasicString<SmallStringSize> *val) noexcept {
if (!unionCheckAndIt()) {
return {};
}
using VecMap = ox::VectorMemMap<PlatSpec>;
const auto sz = val->bytes();
VecMap vecVal{
.smallVecSize = SmallStringSize,
.size = PlatSpec::correctEndianness(static_cast<typename PlatSpec::size_t>(sz)),
.cap = PlatSpec::correctEndianness(static_cast<typename PlatSpec::size_t>(sz)),
};
oxReturnError(pad(&vecVal));
const auto restore = m_writer.tellp();
std::size_t a = 0;
if (sz && sz >= SmallStringSize) {
oxReturnError(ox::allocate(&m_writer, sz).moveTo(a));
} else {
a = restore;
}
vecVal.items = PlatSpec::correctEndianness(static_cast<PtrType>(a) + PlatSpec::RomStart);
oxReturnError(m_writer.seekp(a));
oxReturnError(m_writer.write(val->data(), sz));
oxReturnError(m_writer.seekp(restore));
oxReturnError(serialize(&m_writer, vecVal));
m_ptrs.emplace_back(restore + offsetof(VecMap, items), vecVal.items);
return {};
}
template<typename PlatSpec>
template<typename T, std::size_t sz>
constexpr ox::Error Preloader<PlatSpec>::field(CRStringView name, const ox::Array<T, sz> *val) noexcept {
if (!unionCheckAndIt()) {
return {};
}
// serialize the Array elements
if constexpr(sz) {
m_unionIdx.emplace_back(-1);
for (std::size_t i = 0; i < val->size(); ++i) {
oxReturnError(this->interface()->field(name, &(*val)[i]));
}
m_unionIdx.pop_back();
}
return {};
}
template<typename PlatSpec>
template<typename T>
constexpr ox::Error Preloader<PlatSpec>::field(CRStringView, const T **val, std::size_t cnt) noexcept {
if (!unionCheckAndIt()) {
return {};
}
// serialize the array
m_unionIdx.emplace_back(-1);
for (std::size_t i = 0; i < cnt; ++i) {
oxReturnError(this->interface()->field(nullptr, &val[i]));
}
m_unionIdx.pop_back();
return {};
}
template<typename PlatSpec>
constexpr ox::Result<std::size_t> Preloader<PlatSpec>::startAlloc(std::size_t sz) noexcept {
oxRequire(a, ox::allocate(&m_writer, sz));
m_allocStack.emplace_back(static_cast<typename PlatSpec::PtrType>(m_writer.tellp()));
oxReturnError(m_writer.seekp(a));
return a;
}
template<typename PlatSpec>
constexpr ox::Result<std::size_t> Preloader<PlatSpec>::startAlloc(std::size_t sz, std::size_t restore) noexcept {
oxRequire(a, ox::allocate(&m_writer, sz));
m_allocStack.emplace_back(restore, ox::ios_base::beg);
oxReturnError(m_writer.seekp(a));
return a;
}
template<typename PlatSpec>
constexpr ox::Error Preloader<PlatSpec>::endAlloc() noexcept {
if (m_allocStack.empty()) {
return m_writer.seekp(0, ox::ios_base::end);
}
const auto &si = *m_allocStack.back().unwrap();
oxReturnError(m_writer.seekp(static_cast<ox::ssize_t>(si.restore), si.seekdir));
m_allocStack.pop_back();
return {};
}
template<typename PlatSpec>
constexpr ox::Error Preloader<PlatSpec>::offsetPtrs(std::size_t offset) noexcept {
for (const auto &p : m_ptrs) {
oxReturnError(m_writer.seekp(p.loc));
const auto val = PlatSpec::template correctEndianness<typename PlatSpec::PtrType>(static_cast<typename PlatSpec::PtrType>(p.value + offset));
oxReturnError(ox::serialize(&m_writer, val));
}
oxReturnError(m_writer.seekp(0, ox::ios_base::end));
return {};
}
template<typename PlatSpec>
template<typename T>
constexpr ox::Error Preloader<PlatSpec>::pad(const T *v) noexcept {
const auto a = alignOf<PlatSpec>(*v);
const auto excess = m_writer.tellp() % a;
if (excess) {
return m_writer.write(nullptr, a - excess);
} else {
return {};
}
}
template<typename PlatSpec>
constexpr ox::Error Preloader<PlatSpec>::fieldVector(CRStringView name, const ox::ModelValueVector *val) noexcept {
// serialize the Vector
ox::VectorMemMap<PlatSpec> vecVal{
.size = PlatSpec::correctEndianness(static_cast<typename PlatSpec::size_t>(val->size())),
.cap = PlatSpec::correctEndianness(static_cast<typename PlatSpec::size_t>(val->size())),
};
return fieldVector(name, val, vecVal);
}
template<typename PlatSpec>
template<typename T, std::size_t SmallVectorSize, typename Allocator>
constexpr ox::Error Preloader<PlatSpec>::fieldVector(
CRStringView name, const ox::Vector<T, SmallVectorSize, Allocator> *val) noexcept {
// serialize the Vector
ox::VectorMemMap<PlatSpec> vecVal{
.smallVecSize = SmallVectorSize * sizeOf<PlatSpec>(static_cast<T*>(nullptr)),
.size = PlatSpec::correctEndianness(
static_cast<typename PlatSpec::size_t>(val->size())),
.cap = PlatSpec::correctEndianness(
static_cast<typename PlatSpec::size_t>(val->size())),
};
return fieldVector(name, val, vecVal);
}
template<typename PlatSpec>
constexpr ox::Error Preloader<PlatSpec>::fieldVector(
CRStringView, const auto *val, ox::VectorMemMap<PlatSpec> vecVal) noexcept {
oxReturnError(pad(&vecVal));
const auto vecValPt = m_writer.tellp();
// serialize the Vector elements
if (val->size()) {
const auto sz = sizeOf<PlatSpec>(&(*val)[0]) * val->size();
oxRequire(p, ox::allocate(&m_writer, sz));
oxReturnError(m_writer.seekp(p));
m_unionIdx.emplace_back(-1);
for (std::size_t i = 0; i < val->size(); ++i) {
oxReturnError(this->interface()->field(nullptr, &val->operator[](i)));
}
m_unionIdx.pop_back();
vecVal.items = PlatSpec::correctEndianness(
static_cast<typename PlatSpec::size_t>(p + PlatSpec::RomStart));
oxReturnError(m_writer.seekp(vecValPt));
} else {
vecVal.items = 0;
}
// serialize the Vector
oxReturnError(serialize(&m_writer, vecVal));
m_ptrs.emplace_back(m_writer.tellp() - PtrSize, vecVal.items);
return {};
}
template<typename PlatSpec>
constexpr bool Preloader<PlatSpec>::unionCheckAndIt() noexcept {
auto &u = *m_unionIdx.back().unwrap();
return u.checkAndIterate();
}
template<typename PlatSpec, typename T>
constexpr ox::Error preload(Preloader<PlatSpec> *pl, ox::CommonPtrWith<T> auto *obj) noexcept {
oxReturnError(model(pl->interface(), obj));
return pl->pad(obj);
}
extern template class Preloader<NativePlatSpec>;
}

119
deps/ox/src/ox/preloader/sizecatcher.hpp vendored Normal file
View File

@@ -0,0 +1,119 @@
/*
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#pragma once
#include <ox/model/modelhandleradaptor.hpp>
#include <ox/model/optype.hpp>
#include <ox/model/types.hpp>
#include <ox/std/error.hpp>
#include <ox/std/string.hpp>
#include <ox/std/types.hpp>
#include "unionsizecatcher.hpp"
namespace ox {
template<typename PlatSpec, typename T>
[[nodiscard]]
constexpr std::size_t alignOf(const T &t) noexcept;
template<typename PlatSpec, typename T>
[[nodiscard]]
constexpr std::size_t sizeOf(const T *t) noexcept;
template<typename PlatSpec>
class SizeCatcher: public ModelHandlerBase<SizeCatcher<PlatSpec>, OpType::Reflect> {
private:
std::size_t m_size = 0;
public:
constexpr explicit SizeCatcher() noexcept = default;
template<typename T>
constexpr ox::Error setTypeInfo(
const char* = T::TypeName,
int = T::TypeVersion) noexcept {
return {};
}
template<typename T>
constexpr ox::Error setTypeInfo(
const char*,
int,
const Vector<String>&,
std::size_t) noexcept {
return {};
}
template<typename T, bool force>
constexpr ox::Error field(const char*, UnionView<T, force>) noexcept;
template<typename T>
constexpr ox::Error field(const char*, const T *val) noexcept;
template<typename T>
constexpr ox::Error field(const char*, const T **val, std::size_t cnt) noexcept;
[[nodiscard]]
constexpr auto size() const noexcept {
return m_size;
}
private:
constexpr void pad(const auto *val) noexcept;
};
template<typename PlatSpec>
template<typename T, bool force>
constexpr ox::Error SizeCatcher<PlatSpec>::field(const char*, const UnionView<T, force> val) noexcept {
pad(val.get());
UnionSizeCatcher<PlatSpec> sc;
oxReturnError(model(sc.interface(), val.get()));
m_size += sc.size();
return {};
}
template<typename PlatSpec>
template<typename T>
constexpr ox::Error SizeCatcher<PlatSpec>::field(const char*, const T *val) noexcept {
pad(val);
m_size += sizeOf<PlatSpec>(val);
return {};
}
template<typename PlatSpec>
template<typename T>
constexpr ox::Error SizeCatcher<PlatSpec>::field(const char*, const T **val, std::size_t cnt) noexcept {
for (std::size_t i = 0; i < cnt; ++i) {
oxReturnError(field("", &val[i]));
}
return {};
}
template<typename PlatSpec>
constexpr void SizeCatcher<PlatSpec>::pad(const auto *val) noexcept {
const auto a = alignOf<PlatSpec>(*val);
const auto excess = m_size % a;
if (excess) {
m_size += a - excess;
}
}
template<typename PlatSpec, typename T>
[[nodiscard]]
constexpr std::size_t sizeOf(const T *t) noexcept {
if constexpr(ox::is_integral_v<T>) {
return sizeof(T);
} else if constexpr(ox::is_pointer_v<T>) {
return sizeof(typename PlatSpec::PtrType);
} else {
SizeCatcher<PlatSpec> sc;
const auto err = model(sc.interface(), t);
oxAssert(err, "Could not get size of type");
return sc.size();
}
}
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#pragma once
#include <ox/model/modelhandleradaptor.hpp>
#include <ox/model/optype.hpp>
#include <ox/model/types.hpp>
#include <ox/std/error.hpp>
#include <ox/std/string.hpp>
#include <ox/std/types.hpp>
namespace ox {
template<typename PlatSpec>
class UnionSizeCatcher: public ModelHandlerBase<UnionSizeCatcher<PlatSpec>, OpType::Reflect> {
private:
std::size_t m_size = 0;
public:
template<typename T>
constexpr ox::Error setTypeInfo(
const char* = T::TypeName,
int = T::TypeVersion) noexcept {
return {};
}
template<typename T>
constexpr ox::Error setTypeInfo(
const char*,
int,
const Vector<String>&,
std::size_t) noexcept {
return {};
}
template<typename T, bool force>
constexpr ox::Error field(CRStringView, const UnionView<T, force> val) noexcept {
UnionSizeCatcher<PlatSpec> sc;
oxReturnError(model(sc.interface(), val.get()));
m_size += sc.size();
return {};
}
template<typename T>
constexpr ox::Error field(CRStringView, const T *val) noexcept;
template<typename T>
constexpr ox::Error field(CRStringView, const T **val, std::size_t cnt) noexcept;
[[nodiscard]]
constexpr auto size() const noexcept {
return m_size;
}
private:
template<typename T, std::size_t SmallVecSize>
constexpr ox::Error fieldStr(CRStringView, const ox::BasicString<SmallVecSize> *val) noexcept;
template<typename T, std::size_t SmallVecSize>
constexpr ox::Error fieldVector(CRStringView, const ox::Vector<T, SmallVecSize> *val) noexcept;
constexpr void setSize(std::size_t sz) noexcept;
};
template<typename PlatSpec>
template<typename T>
constexpr ox::Error UnionSizeCatcher<PlatSpec>::field(CRStringView, const T *val) noexcept {
setSize(sizeOf<PlatSpec>(val));
return {};
}
template<typename PlatSpec>
template<typename T>
constexpr ox::Error UnionSizeCatcher<PlatSpec>::field(CRStringView, const T **val, std::size_t cnt) noexcept {
for (std::size_t i = 0; i < cnt; ++i) {
oxReturnError(field("", &val[i]));
}
return {};
}
template<typename PlatSpec>
template<typename T, std::size_t SmallStrSize>
constexpr ox::Error UnionSizeCatcher<PlatSpec>::fieldStr(CRStringView, const ox::BasicString<SmallStrSize>*) noexcept {
ox::VectorMemMap<PlatSpec> v;
setSize(sizeOf<PlatSpec>(v));
return {};
}
template<typename PlatSpec>
template<typename T, std::size_t SmallVecSize>
constexpr ox::Error UnionSizeCatcher<PlatSpec>::fieldVector(CRStringView, const ox::Vector<T, SmallVecSize>*) noexcept {
ox::VectorMemMap<PlatSpec> v;
setSize(sizeOf<PlatSpec>(v));
return {};
}
template<typename PlatSpec>
constexpr void UnionSizeCatcher<PlatSpec>::setSize(std::size_t sz) noexcept {
m_size = ox::max(m_size, sz);
}
}

145
deps/ox/src/ox/std/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,145 @@
if(OX_USE_STDLIB AND OX_ENABLE_TRACEHOOK)
add_library(
OxTraceHook SHARED
tracehook.cpp
)
else()
add_library(
OxTraceHook
tracehook.cpp
)
endif()
target_compile_definitions(
OxTraceHook PUBLIC
$<$<BOOL:${OX_BARE_METAL}>:OX_BARE_METAL>
$<$<BOOL:${OX_USE_STDLIB}>:OX_USE_STDLIB>
$<$<BOOL:${OX_NODEBUG}>:OX_NODEBUG>
)
add_library(
OxStd
assert.cpp
bit.cpp
buffer.cpp
buildinfo.cpp
byteswap.cpp
concepts.cpp
fmt.cpp
heapmgr.cpp
math.cpp
memops.cpp
random.cpp
reader.cpp
substitutes.cpp
stacktrace.cpp
string.cpp
stringview.cpp
strops.cpp
trace.cpp
typetraits.cpp
uuid.cpp
vec.cpp
)
if(NOT MSVC)
target_compile_options(OxStd PRIVATE -Wsign-conversion)
target_compile_options(OxStd PRIVATE -Wconversion)
if(${OX_OS_LINUX})
target_compile_options(OxStd PUBLIC -export-dynamic)
#target_link_options(OxStd PUBLIC -W1,-E)
elseif(${OX_OS_FREEBSD})
target_link_libraries(
OxStd PUBLIC
execinfo
)
endif()
endif()
if(NOT OX_BARE_METAL)
set_property(
TARGET
OxStd
PROPERTY
POSITION_INDEPENDENT_CODE ON
)
endif()
target_compile_definitions(
OxStd PUBLIC
$<$<BOOL:${OX_USE_STDLIB}>:OX_USE_STDLIB>
$<$<BOOL:${OX_NODEBUG}>:OX_NODEBUG>
)
if(NOT WIN32)
target_link_libraries(
OxStd PUBLIC
$<$<CXX_COMPILER_ID:GNU>:$<$<BOOL:${OX_USE_STDLIB}>:dl>>
)
endif()
target_link_libraries(
OxStd PUBLIC
$<$<CXX_COMPILER_ID:GNU>:gcc>
OxTraceHook
)
install(
FILES
algorithm.hpp
array.hpp
assert.hpp
bit.hpp
bounds.hpp
bstring.hpp
buffer.hpp
buildinfo.hpp
byteswap.hpp
concepts.hpp
def.hpp
defer.hpp
defines.hpp
error.hpp
fmt.hpp
hardware.hpp
hashmap.hpp
heapmgr.hpp
iterator.hpp
math.hpp
memops.hpp
memory.hpp
new.hpp
optional.hpp
point.hpp
random.hpp
ranges.hpp
serialize.hpp
size.hpp
stacktrace.hpp
std.hpp
stddef.hpp
string.hpp
stringliteral.hpp
stringview.hpp
strongint.hpp
strops.hpp
trace.hpp
typeinfo.hpp
types.hpp
typetraits.hpp
units.hpp
uuid.hpp
vec.hpp
vector.hpp
writer.hpp
DESTINATION
include/ox/std
)
install(TARGETS OxStd OxTraceHook
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
if(OX_RUN_TESTS)
add_subdirectory(test)
endif()

43
deps/ox/src/ox/std/algorithm.hpp vendored Normal file
View File

@@ -0,0 +1,43 @@
/*
* Copyright 2015 - 2022 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
namespace ox {
template<typename It, typename T>
constexpr It find(It begin, It end, const T &value) {
for (; begin != end; ++begin) {
if (*begin == value) {
return begin;
}
}
return end;
}
template<typename It>
constexpr It find_if(It begin, It end, auto predicate) {
for (; begin != end; ++begin) {
if (predicate(*begin)) {
return begin;
}
}
return end;
}
template<typename It, typename Size, typename OutIt>
constexpr OutIt copy_n(It in, Size cnt, OutIt out) {
for (Size i = 0; i < cnt; ++i) {
*out = *in;
++out;
++in;
}
return out;
}
}

195
deps/ox/src/ox/std/array.hpp vendored Normal file
View File

@@ -0,0 +1,195 @@
/*
* Copyright 2015 - 2023 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "bit.hpp"
#include "error.hpp"
#include "initializerlist.hpp"
#include "iterator.hpp"
#include "math.hpp"
#include "memory.hpp"
#include "new.hpp"
#include "types.hpp"
#include "utility.hpp"
namespace ox {
template<typename T, std::size_t ArraySize>
class Array {
public:
using value_type = T;
using size_type = std::size_t;
template<typename RefType = T&, typename PtrType = T*, bool reverse = false>
using iterator = SpanIterator<T, RefType, PtrType, reverse>;
private:
T m_items[ArraySize]{};
public:
constexpr Array() noexcept = default;
constexpr Array(std::initializer_list<T> list) noexcept;
constexpr Array(const Array &other);
constexpr Array(Array &&other) noexcept;
~Array() = default;
constexpr iterator<> begin() noexcept {
return iterator<>(&m_items[0], 0, ArraySize);
}
constexpr iterator<> end() noexcept {
return iterator<>(&m_items[0], ArraySize, ArraySize);
}
constexpr iterator<const T&, const T*> begin() const noexcept {
return iterator<const T&, const T*>(&m_items[0], 0, ArraySize);
}
constexpr iterator<const T&, const T*> end() const noexcept {
return iterator<const T&, const T*>(&m_items[0], ArraySize, ArraySize);
}
constexpr iterator<T&, T*, true> rbegin() noexcept {
return iterator<T&, T*, true>(&m_items[0], ArraySize - 1, ArraySize);
}
constexpr iterator<T&, T*, true> rend() noexcept {
return iterator<T&, T*, true>(&m_items[0], MaxValue<size_type>, ArraySize);
}
constexpr iterator<const T&, const T*, true> rbegin() const noexcept {
return iterator<const T&, const T*, true>(m_items, ArraySize - 1, ArraySize);
}
constexpr iterator<const T&, const T*, true> rend() const noexcept {
return iterator<const T&, const T*, true>(m_items, MaxValue<size_type>, ArraySize);
}
constexpr bool operator==(const Array &other) const;
constexpr Array &operator=(const Array &other);
constexpr Array &operator=(Array &&other) noexcept;
constexpr T &operator[](std::size_t i) noexcept;
constexpr const T &operator[](std::size_t i) const noexcept;
[[nodiscard]]
constexpr std::size_t size() const noexcept;
[[nodiscard]]
constexpr T *data() noexcept {
return m_items;
}
[[nodiscard]]
constexpr const T *data() const noexcept {
return m_items;
}
[[nodiscard]]
constexpr bool contains(const T&) const;
};
template<typename T, std::size_t ArraySize, typename RefType, bool reverse>
using ArrayIt = typename Array<T, ArraySize>::template iterator<RefType, reverse>;
template<typename T, std::size_t ArraySize, typename RefType, bool reverse>
constexpr ArrayIt<T, ArraySize, RefType, reverse> operator+(std::size_t n, const ArrayIt<T, ArraySize, RefType, reverse> &a) {
return a + n;
}
template<typename T, std::size_t ArraySize>
constexpr Array<T, ArraySize>::Array(std::initializer_list<T> list) noexcept {
for (auto i = 0ul; auto &item : list) {
this->operator[](i) = item;
++i;
}
}
template<typename T, std::size_t ArraySize>
constexpr Array<T, ArraySize>::Array(const Array &other) {
for (std::size_t i = 0; i < ArraySize; ++i) {
m_items[i] = T(other.m_items[i]);
}
}
template<typename T, std::size_t ArraySize>
constexpr Array<T, ArraySize>::Array(Array &&other) noexcept {
if (this != &other) {
for (std::size_t i = 0; i < ArraySize; ++i) {
m_items[i] = T(std::move(other.m_items[i]));
}
}
}
template<typename T, std::size_t ArraySize>
constexpr bool Array<T, ArraySize>::operator==(const Array &other) const {
for (std::size_t i = 0; i < ArraySize; i++) {
if (!(m_items[i] == other.m_items[i])) {
return false;
}
}
return true;
}
template<typename T, std::size_t ArraySize>
constexpr Array<T, ArraySize> &Array<T, ArraySize>::operator=(const Array &other) {
if (this != &other) {
for (std::size_t i = 0; i < ArraySize; ++i) {
m_items[i] = other.m_items[i];
}
}
return *this;
return *this;
}
template<typename T, std::size_t ArraySize>
constexpr Array<T, ArraySize> &Array<T, ArraySize>::operator=(Array &&other) noexcept {
if (this != &other) {
for (std::size_t i = 0; i < ArraySize; ++i) {
m_items[i] = std::move(other.m_items[i]);
}
}
return *this;
}
template<typename T, std::size_t ArraySize>
constexpr T &Array<T, ArraySize>::operator[](std::size_t i) noexcept {
return m_items[i];
}
template<typename T, std::size_t ArraySize>
constexpr const T &Array<T, ArraySize>::operator[](std::size_t i) const noexcept {
return m_items[i];
}
template<typename T, std::size_t ArraySize>
constexpr std::size_t Array<T, ArraySize>::size() const noexcept {
return ArraySize;
}
template<typename T, std::size_t ArraySize>
constexpr bool Array<T, ArraySize>::contains(const T &v) const {
for (std::size_t i = 0; i < ArraySize; i++) {
if (m_items[i] == v) {
return true;
}
}
return false;
}
}

34
deps/ox/src/ox/std/assert.cpp vendored Normal file
View File

@@ -0,0 +1,34 @@
/*
* Copyright 2015 - 2022 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include "stacktrace.hpp"
#include "trace.hpp"
#include "assert.hpp"
namespace ox {
void panic(const char *file, int line, const char *panicMsg, const Error &err) noexcept {
oxErrf("\033[31;1;1mPANIC:\033[0m [{}:{}]: {}\n", file, line, panicMsg);
if (err.msg) {
oxErrf("\tError Message:\t{}\n", err.msg);
}
oxErrf("\tError Code:\t{}\n", static_cast<ErrorCode>(err));
if (err.file != nullptr) {
oxErrf("\tError Location:\t{}:{}\n", err.file, err.line);
}
#ifdef OX_USE_STDLIB
printStackTrace(2);
oxTrace("panic").del("") << "Panic: " << panicMsg << " (" << file << ":" << line << ")";
std::abort();
#else
while (1);
#endif
}
}

Some files were not shown because too many files have changed in this diff Show More