Compare commits

..

88 Commits

Author SHA1 Message Date
257e857116 [ox/std] Add move operator to String 2020-06-20 02:58:32 -05:00
0035faa416 [ox/std] Make Vector::push_back use copy constructor instead of copy assign 2020-06-20 02:58:20 -05:00
5c34c26080 [ox/mc] Fix string allocation size 2020-06-20 02:55:25 -05:00
b6f8c9e242 [ox] Add HashMap<String, T> to serializaton handlers 2020-06-19 07:34:04 -05:00
0698353fbf [nostalgia/core/studio] Fix TileSheetEditor crash on save after increasing grid size 2020-06-19 01:41:42 -05:00
9576f78abf [ox/model] Fix TypeDescWriter NameCatcher to have opType of WriteDefinition 2020-06-17 06:44:31 -05:00
7b07edd9e4 [ox/std] Rename is_signed to is_signed_v 2020-06-17 06:36:26 -05:00
a5b01bb0d7 [ox/fs] Cleanup 2020-06-17 01:53:35 -05:00
1c5fed05bf [ox/std] Make ox_strncmp a template 2020-06-17 01:52:28 -05:00
d4983cf3c6 [nostalgia/studio] Replace garbage last character with \n 2020-06-17 01:43:10 -05:00
fc246ee396 [nostalgia/pack] Make pack ignore /.nostalgia 2020-06-17 01:36:01 -05:00
167e470091 [nostalgia/studio] Remove garbage last character in OC files 2020-06-16 05:43:44 -05:00
dae719f78a [nostalgia/studio] Make OxFS tree view hide files starting with . 2020-06-16 05:01:49 -05:00
df96407669 [nostalgia/studio] Make Project write out type data with data 2020-06-16 04:42:18 -05:00
17762b40b7 [ox/oc] Fix write to ignore null objects passed in 2020-06-16 04:41:43 -05:00
4f4ec089fd [ox/model] Fix model descriptor writing to handle Vector, etc. 2020-06-16 04:40:43 -05:00
f58c658be1 [ox/fs] Make PassthroughFS mkdir pass mkdir -p on existing directory 2020-06-16 04:39:15 -05:00
e92e980fde [ox/std] Change toError(ox::ValErr<T>) to toError(const ox::ValErr<T>&) 2020-06-16 00:45:24 -05:00
71b6c72c46 [ox/model] Add opType method to TypeDescWriter 2020-06-16 00:23:03 -05:00
da75443d4c [nostalgia] Move conan install's --build missing to correct position 2020-06-15 23:41:33 -05:00
a45692fa4c [nostalgia/sample_project] Update project files to have new type IDs 2020-06-15 23:38:06 -05:00
112c2c4212 [ox/fs] Change Claw type IDs to domain based namespaces 2020-06-15 23:16:34 -05:00
f8fb458e32 [nostalgia] Change Claw type IDs to domain based namespaces 2020-06-15 23:15:49 -05:00
20f4d50ad6 [ox/std] Add missing includes to CMake install and std.hpp 2020-06-15 04:54:52 -05:00
0eaf54bd22 [nostalgia/tools/pack] Cleanup debug code 2020-06-15 04:44:07 -05:00
978dd70770 [nostalgia] Switch tools to use Claw 2020-06-15 01:40:59 -05:00
9a49761c01 [nostalgia/core/sdl] Switch from MC to Claw 2020-06-15 01:38:28 -05:00
880e6e49d6 [nostalgia/fs] Fix Directory to write back out as a Directory 2020-06-15 01:34:48 -05:00
4a221e52db [ox/claw] Add strip header function 2020-06-15 01:34:22 -05:00
4f1593a0ed [nostalgia] Make gba-pkg's project an argument 2020-06-14 19:02:38 -05:00
18166f6123 [nostalgia/jenkins/gba] Add artifact 2020-06-13 22:32:34 -05:00
228b6dd031 [nostalgia] Fix DEVKITPRO bin paths for Jenkins 2020-06-13 04:24:49 -05:00
a1a0abe83b [nostalgia] Make setup-build only overwrite current_build for native builds 2020-06-13 04:18:37 -05:00
77ece05bf1 [nostalgia/jenkins] Fix GBA Jenkinsfile 2020-06-13 04:15:04 -05:00
9e624619b3 [nostalgia/jenkins] Add Jenkinsfile for GBA builder 2020-06-13 04:08:59 -05:00
5c5c62dc55 [nostalgia/core/studio] Fix TileSheetEditor not to display context menu when right click is not on tile 2020-06-13 03:37:38 -05:00
53358d2e03 [nostaliga/sample_project] Update to use Claw instead of bare MetalClaw 2020-06-13 03:36:19 -05:00
c178a2273b [ox/std] Fix macOS build 2020-06-05 19:47:29 -05:00
f86d0e697a [nostalgia/studio] Replace deprecated call for Qt 5.15 2020-06-05 19:42:37 -05:00
4d806021d0 [ox/claw] Fix TypeInfoCatcher to fit model handler API 2020-05-29 20:24:09 -05:00
98a0c42040 [nostalgia/core/gba][ox/std] Move heap manager from NostalgiaCore to OxStd 2020-05-29 19:44:10 -05:00
0eb33f823c [nostalgia/core/gba] Fix GBA build issues 2020-05-17 04:20:59 -05:00
2dad1688b5 [ox] Fix GBA build 2020-05-17 04:20:43 -05:00
f1110bd849 [nostalgia] Make Linux Jenkinsfile only target Linux builders 2020-05-16 22:53:16 -05:00
b1a0fcbc57 [ox/claw] Add missing write.cpp 2020-05-16 22:47:51 -05:00
e3e00b1716 [nostalgia] Move Jenkinsfile to allow for platform specificity 2020-05-16 22:46:48 -05:00
faaddd1234 [ox/claw] Fix TypeVersion writing 2020-05-16 22:35:54 -05:00
f37515707f [nostalgia] Make Jenkinsfile do ASAN builds 2020-05-16 17:16:17 -05:00
86652ca8d4 [nostalgia] Fix Mac build 2020-05-09 18:44:37 -05:00
819959745d [ox] Fix Mac build and fix MC to increment field counter on empty string 2020-05-08 21:32:56 -05:00
f91874182e [ox/model] Add preloadable attribute to type descriptor 2020-05-08 01:32:36 -05:00
dfb17c851b [nostalgia] Break out Jenkinsfile steps 2020-05-06 20:59:29 -05:00
e2952ec8c1 [ox/claw] Add Claw 2020-05-06 20:38:39 -05:00
9560ccf476 [nostalgia] Remove conan Qt, as it is currently being ignored 2020-05-05 00:03:58 -05:00
e7ea76a7ac [nostalgia] Explicitly list certain dependencies that were causing problems on Fedora 2020-05-04 19:25:10 -05:00
bae54a906b [nostalgia] Add CMake option to disable engine build 2020-05-04 18:22:42 -05:00
b0f268f371 [nostalgia] Make test run build in Makefile 2020-05-04 18:21:48 -05:00
b39735bb08 [nostalgia] Update Jenkinsfile to force conan remote add 2020-05-04 18:20:50 -05:00
6af681bad6 [nostalgia/core] Enable sign conversion warning publicly 2020-04-16 23:51:06 -05:00
9b84d9d8e0 [nostalgia/world] Fix sign conversion 2020-04-16 23:50:30 -05:00
4694bb9dd7 [nostalgia/tools/pack] Fix sign conversion 2020-04-16 23:49:08 -05:00
f758566041 [nostalgia/studio] Add PathExistsValidator 2020-04-16 23:47:08 -05:00
cef5fcd86f [nostalgia/core/userland] Enable sign-conversion warning 2020-04-16 23:44:06 -05:00
8e297f32d4 [nostalgia/core] Move Nostalgia graphics type models to new setTypeInfo 2020-04-16 23:40:22 -05:00
be16229bf7 [nostalgia/core/sdl] Initialize texture lists 2020-04-16 23:39:18 -05:00
11e500520a [nostalgia] Make ox headers system headers 2020-04-16 23:37:35 -05:00
00e645e7a3 [nostalgia/sample_project] Update for new FileAddress 2020-04-16 23:34:45 -05:00
3ff4a59373 [ox/fs] Make FileAddress model use builtin union support 2020-04-16 23:34:08 -05:00
5265a94a80 [ox/oc] Add support for allocating for strings 2020-04-16 23:28:52 -05:00
b3fa531aa0 [ox/model] Add support to SerStr for allocating string 2020-04-16 22:44:50 -05:00
8753d39b66 [ox/std] Fix OxError for release builds and fix ox::Error assignment operator 2020-04-16 22:19:27 -05:00
ed0bf268ba [ox/fs] Add model definition for FileAddress 2020-04-15 21:02:35 -05:00
d0f5819072 [ox/oc] Add union support 2020-04-13 23:36:27 -05:00
d2e7528dae [ox/mc] Fix int reads not to clear out unions 2020-04-13 02:37:08 -05:00
1d07890668 [ox] Add support for unions to model and mc 2020-04-13 02:28:38 -05:00
82a07737ec [nostalgia] Update README 2020-04-11 03:07:52 -05:00
9091f9bd02 [nostalgia] Add profile setting to libstdc++11 in Jenkinsfile 2020-04-09 23:39:24 -05:00
5d95b188d8 [ox/model] Harmonize make the interface for walking over models 2020-04-09 22:57:03 -05:00
a4000f6497 [ox/std] Fix assert for !OX_USE_STDLIB 2020-04-08 23:30:14 -05:00
12c5339295 [nostalgia] Update JsonCpp in conanfile 2020-04-08 03:55:52 -05:00
4064592acc [nostalgia/core/studio] Remove implicit sign conversions 2020-04-07 22:27:28 -05:00
b9c2f3631d [nostalgia/core] Remove implicit sign conversions 2020-04-07 22:10:34 -05:00
d2ec3b8350 [ox/fs] Remove explicit linking of C++ standard file system library 2020-04-07 22:04:23 -05:00
19422ced3e [ox/std] Add move constructor and set null terminator for String 2020-04-07 22:03:19 -05:00
540e67fcd8 [ox/std] Add support for messages in ox::Error 2020-04-07 22:02:07 -05:00
89854a584a [ox/oc] Add OrganicClaw 2020-04-07 22:00:14 -05:00
8b74920270 [ox/std] Fix implicit sign conversion 2020-04-05 19:48:47 -05:00
a74cd1c926 [nostalgia/core/studio] Fix build error in NewTileSheetWizard 2020-04-02 00:58:40 -05:00
100 changed files with 2978 additions and 636 deletions

View File

@ -5,6 +5,7 @@ project(nostalgia)
set(NOSTALGIA_BUILD_TYPE "Native" CACHE STRING "The type of build to produce(Native/GBA)")
set(NOSTALGIA_IDE_BUILD ON CACHE STRING "Build for IDE's to run")
set(NOSTALGIA_QT_PATH "" CACHE STRING "Path to Qt Libraries")
set(NOSTALGIA_BUILD_PLAYER ON CACHE BOOL "Build Player")
set(NOSTALGIA_BUILD_STUDIO ON CACHE BOOL "Build Studio")
if(NOSTALGIA_BUILD_TYPE STREQUAL "GBA")
@ -37,6 +38,7 @@ if(NOT OX_BARE_METAL)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()
# enable ccache
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
@ -116,11 +118,11 @@ endif()
enable_testing()
add_subdirectory(deps/ox)
include_directories(SYSTEM deps/ox/src)
if(NOSTALGIA_BUILD_TYPE STREQUAL "GBA")
add_subdirectory(deps/gbastartup)
endif()
add_subdirectory(deps/ox)
include_directories(deps/ox/src)
add_subdirectory(src)

17
Jenkinsfile vendored
View File

@ -1,17 +0,0 @@
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'make conan'
sh 'make configure-debug configure-release'
sh 'make'
}
}
stage('Test') {
steps {
sh 'make test'
}
}
}
}

View File

@ -31,7 +31,7 @@ build:
.PHONY: pkg-gba
pkg-gba:
$(foreach file, $(wildcard build/*), cmake --build $(file) --target install;)
${ENV_RUN} ./scripts/gba-pkg
${ENV_RUN} ./scripts/gba-pkg sample_project
.PHONY: install
install:
$(foreach file, $(wildcard build/*), cmake --build $(file) --target install;)
@ -40,9 +40,9 @@ clean:
$(foreach file, $(wildcard build/*), cmake --build $(file) --target clean;)
.PHONY: purge
purge:
${ENV_RUN} ${RM_RF} build .current_build
${ENV_RUN} ${RM_RF} build .current_build dist
.PHONY: test
test:
test: build
$(foreach file, $(wildcard build/*), cmake --build $(file) --target test;)
.PHONY: run
@ -86,7 +86,7 @@ devenv-shell:
.PHONY: conan
conan:
@mkdir -p conanbuild && cd conanbuild && conan install --build missing ../
@mkdir -p conanbuild && cd conanbuild && conan install ../ --build=missing
.PHONY: configure-release
configure-release:

View File

@ -2,8 +2,18 @@
## Setup
### Linux
Make sure conan is using the C++11 version of libstdc++.
conan profile update settings.compiler.libcxx=libstdc++11 default
### macOS
Run something along the lines of the following to let CMake know where Qt is installed. You may need to adjust this location.
Install and use gmake instead of the make that comes with the system.
export NOSTALGIA_QT_PATH=/usr/local/Cellar/qt5/5.8.0_2/
## Build
Build options: release, debug, gba, gba-debug
make purge conan configure-{gba,release,debug} install

View File

@ -2,8 +2,8 @@ from conans import ConanFile, CMake
class NostalgiaConan(ConanFile):
settings = "os", "compiler", "build_type", "arch"
requires = "jsoncpp/1.9.0@theirix/stable", "sdl2/2.0.10@bincrafters/stable"#, "qt/5.14.0@bincrafters/stable" # comma-separated list of requirements
requires = "jsoncpp/1.9.2", "sdl2/2.0.10@bincrafters/stable"
generators = "cmake", "cmake_find_package", "cmake_paths"
def requirements(self):
pass
#default_options = {
# "sdl2:nas": False
#}

View File

@ -1,4 +1,5 @@
enable_language(C ASM)
add_library(
GbaStartup
gba_crt0.s
@ -7,4 +8,5 @@ add_library(
target_link_libraries(
GbaStartup
OxStd
)

View File

@ -6,17 +6,33 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <ox/std/bit.hpp>
#include <ox/std/heapmgr.hpp>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
// this warning is too dumb to realize that it can actually confirm the hard
// coded address aligns with the requirement of HeapSegment, so it must be
// suppressed
#pragma GCC diagnostic ignored "-Wcast-align"
#define MEM_WRAM_BEGIN reinterpret_cast<uint8_t*>(0x02000000)
#define MEM_WRAM_END reinterpret_cast<uint8_t*>(0x0203FFFF)
#define HEAP_BEGIN reinterpret_cast<ox::heapmgr::HeapSegment*>(MEM_WRAM_BEGIN)
// set size to half of WRAM
#define HEAP_SIZE ((MEM_WRAM_END - MEM_WRAM_BEGIN) / 2)
#define HEAP_END reinterpret_cast<ox::heapmgr::HeapSegment*>(MEM_WRAM_BEGIN + HEAP_SIZE)
extern void (*__preinit_array_start[]) (void);
extern void (*__preinit_array_end[]) (void);
extern void (*__init_array_start[]) (void);
extern void (*__init_array_end[]) (void);
namespace nostalgia::core {
namespace ox::heapmgr {
void initHeap();
void initHeap(char *heapBegin, char *heapEnd);
}
@ -38,7 +54,7 @@ int main(int argc, const char **argv);
int c_start() {
const char *args[2] = {"", "rom.oxfs"};
nostalgia::core::initHeap();
ox::heapmgr::initHeap(ox::bit_cast<char*>(HEAP_BEGIN), ox::bit_cast<char*>(HEAP_END));
return main(2, args);
}

View File

@ -1,5 +1,7 @@
if(${OX_USE_STDLIB})
add_subdirectory(clargs)
add_subdirectory(claw)
add_subdirectory(oc)
endif()
add_subdirectory(fs)
add_subdirectory(mc)

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

@ -0,0 +1,14 @@
add_library(
OxClaw
read.cpp
write.cpp
)
target_link_libraries(
OxClaw
OxMetalClaw
OxOrganicClaw
)
add_subdirectory(test)

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

@ -0,0 +1,12 @@
/*
* Copyright 2016 - 2020 gtalent2@gmail.com
*
* 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 2016 - 2020 gtalent2@gmail.com
*
* 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,
};
}

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

@ -0,0 +1,68 @@
/*
* Copyright 2016 - 2020 gtalent2@gmail.com
*
* 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 "read.hpp"
namespace ox {
namespace detail {
ValErr<ClawHeader> readHeader(const char *buff, std::size_t buffLen) noexcept {
const auto s1End = ox_strchr(buff, ';', buffLen);
if (!s1End) {
return OxError(1);
}
const auto s1Size = s1End - buff;
String fmt(buff, s1Size);
buff += s1Size + 1;
buffLen -= s1Size + 1;
const auto s2End = ox_strchr(buff, ';', buffLen);
if (!s2End) {
return OxError(1);
}
const auto s2Size = s2End - buff;
String typeName(buff, s2Size);
buff += s2Size + 1;
buffLen -= s2Size + 1;
const auto s3End = ox_strchr(buff, ';', buffLen);
if (!s3End) {
return OxError(1);
}
const auto s3Size = s3End - buff;
String versionStr(buff, s3Size);
buff += s3Size + 1;
buffLen -= s3Size + 1;
ClawHeader hdr;
if (fmt == "M1") {
hdr.fmt = ClawFormat::Metal;
} else if (fmt == "O1") {
hdr.fmt = ClawFormat::Organic;
} else {
return OxError(1);
}
hdr.typeName = typeName;
hdr.typeVersion = ox_atoi(versionStr.c_str());
hdr.data = buff;
hdr.dataSize = buffLen;
return hdr;
}
}
ValErr<Vector<char>> stripClawHeader(const char *buff, std::size_t buffLen) noexcept {
auto header = detail::readHeader(buff, buffLen);
oxReturnError(header);
Vector<char> out(header.value.dataSize);
ox_memcpy(out.data(), header.value.data, out.size());
return ox::move(out);
}
}

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

@ -0,0 +1,57 @@
/*
* Copyright 2016 - 2020 gtalent2@gmail.com
*
* 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/read.hpp>
#include <ox/oc/read.hpp>
#include <ox/std/string.hpp>
#include <ox/std/vector.hpp>
#include "format.hpp"
namespace ox {
namespace detail {
struct ClawHeader {
String typeName;
int typeVersion = -1;
ClawFormat fmt = ClawFormat::None;
const char *data = nullptr;
std::size_t dataSize = 0;
};
[[nodiscard]] ValErr<ClawHeader> readHeader(const char *buff, std::size_t buffLen) noexcept;
}
[[nodiscard]] ValErr<Vector<char>> stripClawHeader(const char *buff, std::size_t buffLen) noexcept;
template<typename T>
[[nodiscard]] Error readClaw(char *buff, std::size_t buffLen, T *val) {
auto header = detail::readHeader(buff, buffLen);
oxReturnError(header);
switch (header.value.fmt) {
case ClawFormat::Metal:
{
MetalClawReader reader(bit_cast<uint8_t*>(header.value.data), buffLen);
return model(&reader, val);
}
case ClawFormat::Organic:
{
OrganicClawReader reader(bit_cast<uint8_t*>(header.value.data), buffLen);
return model(&reader, val);
}
case ClawFormat::None:
return OxError(1);
}
return OxError(1);
}
}

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("Test\\ ClawTest\\ ClawHeaderReader" ClawTest ClawHeaderReader)
add_test("Test\\ ClawTest\\ ClawHeaderReader2" ClawTest ClawHeaderReader2)
add_test("Test\\ ClawTest\\ ClawWriter" ClawTest ClawWriter)
add_test("Test\\ ClawTest\\ ClawReader" ClawTest ClawReader)

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

@ -0,0 +1,206 @@
/*
* Copyright 2016 - 2020 gtalent2@gmail.com
*
* 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 <assert.h>
#include <iostream>
#include <map>
#include <memory>
#include <string>
#include <vector>
#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 Fields = 3;
bool Bool;
uint32_t Int = 5;
char String[32];
};
struct TestStructNest {
static constexpr auto TypeName = "TestStructNest";
static constexpr auto Fields = 3;
bool Bool = false;
uint32_t Int = 0;
ox::BString<32> String = "";
};
struct TestStruct {
static constexpr auto TypeName = "TestStruct";
static constexpr auto Fields = 16;
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;
TestUnion Union;
char *CString = nullptr;
ox::BString<32> String = "";
uint32_t List[4] = {0, 0, 0, 0};
TestStructNest EmptyStruct;
TestStructNest Struct;
~TestStruct() {
delete[] CString;
}
};
template<typename T>
ox::Error model(T *io, TestUnion *obj) {
io->template setTypeInfo<TestUnion>();
oxReturnError(io->field("Bool", &obj->Bool));
oxReturnError(io->field("Int", &obj->Int));
oxReturnError(io->field("String", ox::SerStr(obj->String)));
return OxError(0);
}
template<typename T>
ox::Error model(T *io, TestStructNest *obj) {
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>
ox::Error model(T *io, TestStruct *obj) {
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("Union", ox::UnionView{&obj->Union, 1}));
oxReturnError(io->field("CString", ox::SerStr(&obj->CString)));
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);
}
std::map<std::string, ox::Error(*)()> tests = {
{
{
"ClawHeaderReader",
[] {
ox::String hdr = "O1;com.drinkingtea.ox.claw.test.Header;2;";
auto [ch, err] = ox::detail::readHeader(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",
[] {
ox::String hdr = "M1;com.drinkingtea.ox.claw.test.Header2;3;";
auto [ch, err] = ox::detail::readHeader(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.CString = new char[ox_strlen("c-string") + 1];
ox_strcpy(testIn.CString, "c-string");
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 [buff, err] = ox::writeClaw(&testIn, ox::ClawFormat::Metal);
oxAssert(err, "writeMC failed");
oxAssert(ox::readClaw(buff.data(), buff.size(), &testOut), "writeMC 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(ox_strcmp(testIn.CString, testOut.CString) == 0, "CString 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) {
int retval = -1;
if (argc > 0) {
auto testName = args[1];
if (tests.find(testName) != tests.end()) {
retval = tests[testName]();
} else {
retval = 1;
}
}
return retval;
}

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

@ -0,0 +1,23 @@
/*
* Copyright 2016 - 2020 gtalent2@gmail.com
*
* 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);
}

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

@ -0,0 +1,104 @@
/*
* Copyright 2016 - 2020 gtalent2@gmail.com
*
* 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>
#include <ox/oc/write.hpp>
#include <ox/std/string.hpp>
#include <ox/std/vector.hpp>
#include "format.hpp"
namespace ox {
namespace detail {
struct TypeInfoCatcher {
const char *name = nullptr;
template<typename T = void>
constexpr void setTypeInfo(const char *name = T::TypeName, int = T::Fields) noexcept {
this->name = name;
}
constexpr ox::Error field(...) noexcept {
return OxError(0);
}
static constexpr auto opType() {
return OpType::Write;
}
};
template<typename T>
[[nodiscard]] constexpr int getTypeVersion(int version = T::TypeVersion) noexcept {
return version;
}
[[nodiscard]] constexpr int getTypeVersion(...) noexcept {
return -1;
}
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(T *t) noexcept {
TypeInfoCatcher tnc;
model(&tnc, t);
return tnc.name;
}
template<typename T>
ValErr<String> writeClawHeader(T *t, ClawFormat fmt) noexcept {
String out;
switch (fmt) {
case ClawFormat::Metal:
out += "M1;";
break;
case ClawFormat::Organic:
out += "O1;";
break;
default:
return OxError(1);
}
out += detail::getTypeName(t);
out += ";";
const auto tn = detail::type_version<T>::value;
if (tn > -1) {
out += tn;
}
out += ";";
return out;
}
}
template<typename T>
ValErr<Vector<char>> writeClaw(T *t, ClawFormat fmt) {
auto [header, headerErr] = detail::writeClawHeader(t, fmt);
oxReturnError(headerErr);
const auto [data, dataErr] = fmt == ClawFormat::Metal ? writeMC(t) : writeOC(t);
oxReturnError(dataErr);
ox::Vector<char> out(header.len() + data.size());
memcpy(out.data(), header.data(), header.len());
memcpy(out.data() + header.len(), data.data(), data.size());
return out;
}
}

View File

@ -10,11 +10,12 @@ add_library(
)
if(NOT OX_BARE_METAL)
target_link_libraries(
OxFS PUBLIC
$<$<CXX_COMPILER_ID:Clang>:c++fs>
$<$<CXX_COMPILER_ID:GNU>:stdc++fs>
)
if(NOT APPLE)
target_link_libraries(
OxFS PUBLIC
stdc++fs
)
endif()
set_property(
TARGET
OxFS

View File

@ -13,6 +13,8 @@
#include <ox/ptrarith/nodebuffer.hpp>
#include <ox/std/byteswap.hpp>
#include "types.hpp"
namespace ox {
template<typename InodeId_t>
@ -131,7 +133,7 @@ template<typename FileStore, typename InodeId_t>
ox::Error Directory<FileStore, InodeId_t>::init() noexcept {
constexpr auto Size = sizeof(Buffer);
oxTrace("ox::fs::Directory::init") << "Initializing Directory with Inode ID:" << m_inodeId;
oxReturnError(m_fs.write(m_inodeId, nullptr, Size));
oxReturnError(m_fs.write(m_inodeId, nullptr, Size, FileType_Directory));
auto buff = m_fs.read(m_inodeId).template to<Buffer>();
if (!buff.valid()) {
m_size = 0;
@ -254,7 +256,7 @@ ox::Error Directory<FileStore, InodeId_t>::write(PathIterator path, InodeId_t in
oxTrace("ox::fs::Directory::write") << "Attempting to write Directory entry:" << name->data();
oxTrace("ox::fs::Directory::write") << "Attempting to write Directory to FileStore";
oxReturnError(val->init(inode, name->data(), val.size()));
return m_fs.write(m_inodeId, cpy, cpy->size());
return m_fs.write(m_inodeId, cpy, cpy->size(), FileType_Directory);
}
}

View File

@ -23,21 +23,23 @@ enum class FileAddressType: int8_t {
class FileAddress {
template<typename T>
friend ox::Error modelRead(T*, FileAddress*);
template<typename T>
friend ox::Error modelWrite(T*, FileAddress*);
friend ox::Error model(T*, FileAddress*);
public:
static constexpr auto TypeName = "net.drinkingtea.ox.FileAddress";
static constexpr auto Fields = 2;
protected:
FileAddressType m_type = FileAddressType::None;
union {
union Data {
static constexpr auto TypeName = "net.drinkingtea.ox.FileAddress.Data";
static constexpr auto Fields = 3;
char *path;
const char *constPath;
uint64_t inode;
} m_data;
};
protected:
FileAddressType m_type = FileAddressType::None;
Data m_data;
public:
FileAddress();
@ -93,55 +95,19 @@ class FileAddress {
};
template<typename T>
ox::Error modelRead(T *io, FileAddress *fa) {
io->setTypeInfo("ox::FileAddress", FileAddress::Fields);
decltype(fa->m_data.inode) inode = 0;
const auto strSize = io->stringLength() + 1;
auto path = new char[strSize];
oxReturnError(io->field("path", SerStr(path, strSize - 1)));
oxReturnError(io->field("inode", &inode));
if (strSize) {
fa->m_data.path = path;
fa->m_type = FileAddressType::Path;
} else {
fa->m_data.inode = inode;
fa->m_type = FileAddressType::Inode;
delete[] path;
}
ox::Error model(T *io, FileAddress::Data *obj) {
io->template setTypeInfo<FileAddress::Data>();
oxReturnError(io->field("path", SerStr(&obj->path)));
oxReturnError(io->field("constPath", SerStr(&obj->path)));
oxReturnError(io->field("inode", &obj->inode));
return OxError(0);
}
template<typename T>
ox::Error modelWrite(T *io, FileAddress *fa) {
io->setTypeInfo("ox::FileAddress", FileAddress::Fields);
switch (fa->m_type) {
case FileAddressType::Path:
case FileAddressType::ConstPath:
{
decltype(fa->m_data.inode) blank = 0;
const auto strSize = ox_strlen(fa->m_data.constPath) + 1;
auto path = ox_malloca(strSize, char, 0);
memcpy(path.get(), fa->m_data.constPath, strSize);
oxReturnError(io->field("path", SerStr(path.get(), strSize - 1)));
oxReturnError(io->field("inode", &blank));
break;
}
case FileAddressType::Inode:
{
char blankPath[1] = "";
oxReturnError(io->field("path", SerStr(blankPath, 0)));
oxReturnError(io->field("inode", &fa->m_data.inode));
break;
}
case FileAddressType::None:
{
char blankPath[1] = "";
decltype(fa->m_data.inode) blankInode = 0;
oxReturnError(io->field("path", SerStr(blankPath, 0)));
oxReturnError(io->field("inode", &blankInode));
break;
}
}
ox::Error model(T *io, FileAddress *fa) {
io->template setTypeInfo<FileAddress>();
oxReturnError(io->field("type", bit_cast<int8_t*>(&fa->m_type)));
oxReturnError(io->field("data", UnionView(&fa->m_data, static_cast<int>(fa->m_type))));
return OxError(0);
}

View File

@ -31,7 +31,11 @@ Error PassThroughFS::mkdir(const char *path, bool recursive) {
const auto u8p = p.u8string();
oxTrace("ox::fs::PassThroughFS::mkdir") << u8p.c_str();
if (recursive) {
success = std::filesystem::create_directories(p);
if (std::filesystem::is_directory(p)) {
success = true;
} else {
success = std::filesystem::create_directories(p);
}
} else {
success = std::filesystem::create_directory(p);
}

View File

@ -28,7 +28,7 @@ template<typename I>
// find most significant non-sign indicator bit
std::size_t highestBit = 0;
// start at one bit lower if signed
if constexpr(ox::is_signed<I>) {
if constexpr(ox::is_signed_v<I>) {
--shiftStart;
}
for (int i = shiftStart; i > -1; --i) {
@ -65,7 +65,7 @@ template<typename I>
if (val) {
// bits needed to represent number factoring in space possibly
// needed for signed bit
const auto bits = highestBit(val) + 1 + (ox::is_signed<I> ? 1 : 0);
const auto bits = highestBit(val) + 1 + (ox::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
@ -77,13 +77,6 @@ template<typename I>
}
const auto bytesIndicator = onMask<uint8_t>(bytes - 1);
// move sign bit
if constexpr(ox::is_signed<I>) {
if (val < 0) {
val ^= uint64_t(1) << (sizeof(I) * 8 - 1);
val |= uint64_t(1) << (bitsAvailable - 1);
}
}
// ensure we are copying from little endian represenstation
LittleEndian<I> leVal = val;
if (bytes == 9) {
@ -135,7 +128,7 @@ template<typename I>
decoded >>= bytes;
auto out = static_cast<I>(decoded);
// move sign bit
if constexpr(ox::is_signed<I>) {
if constexpr(ox::is_signed_v<I>) {
const auto valBits = bytes << 3;
// get sign
uint64_t sign = decoded >> (valBits - 1);

View File

@ -11,5 +11,4 @@
#include "intops.hpp"
#include "read.hpp"
#include "types.hpp"
#include "walk.hpp"
#include "write.hpp"

View File

@ -7,6 +7,7 @@
*/
#include <ox/std/byteswap.hpp>
#include <ox/std/memops.hpp>
#include "err.hpp"
#include "presenceindicator.hpp"
@ -40,6 +41,7 @@ Error FieldPresenceIndicator::set(std::size_t i, bool on) {
void FieldPresenceIndicator::setFields(int fields) noexcept {
m_fields = fields;
m_maskLen = (fields / 8 + 1) - (fields % 8 == 0);
}
int FieldPresenceIndicator::getFields() const noexcept {

View File

@ -22,9 +22,9 @@ class FieldPresenceIndicator {
public:
FieldPresenceIndicator(uint8_t *mask, std::size_t maxLen);
ValErr<bool> get(std::size_t i) const;
[[nodiscard]] ValErr<bool> get(std::size_t i) const;
Error set(std::size_t i, bool on);
[[nodiscard]] Error set(std::size_t i, bool on);
void setFields(int) noexcept;

View File

@ -14,17 +14,21 @@
namespace ox {
MetalClawReader::MetalClawReader(uint8_t *buff, std::size_t buffLen, MetalClawReader *parent): m_fieldPresence(buff, buffLen) {
m_buff = buff;
m_buffLen = buffLen;
m_parent = parent;
MetalClawReader::MetalClawReader(uint8_t *buff, std::size_t buffLen, int unionIdx, MetalClawReader *parent) noexcept:
m_fieldPresence(buff, buffLen),
m_unionIdx(unionIdx),
m_buffLen(buffLen),
m_buff(buff),
m_parent(parent) {
}
MetalClawReader::~MetalClawReader() {
MetalClawReader::~MetalClawReader() noexcept {
if (m_parent) {
m_parent->m_buffIt += m_buffIt;
}
//oxAssert(m_field == m_fields, "MetalClawReader: incorrect fields number given");
if (m_field != m_fields) {
oxTrace("ox::mc::MetalClawReader::error") << "MetalClawReader: incorrect fields number given";
}
}
Error MetalClawReader::field(const char*, int8_t *val) {
@ -61,41 +65,52 @@ Error MetalClawReader::field(const char*, uint64_t *val) {
}
Error MetalClawReader::field(const char*, bool *val) {
auto valErr = m_fieldPresence.get(m_field++);
*val = valErr.value;
return valErr.error;
}
Error MetalClawReader::field(const char*, SerStr val) {
if (m_fieldPresence.get(m_field++)) {
// read the length
if (m_buffIt >= m_buffLen) {
return OxError(MC_BUFFENDED);
}
std::size_t bytesRead = 0;
auto [size, err] = mc::decodeInteger<StringLength>(&m_buff[m_buffIt], m_buffLen - m_buffIt, &bytesRead);
m_buffIt += bytesRead;
oxReturnError(err);
// read the string
if (val.cap() > -1 && static_cast<StringLength>(val.cap()) >= size) {
if (m_buffIt + size <= m_buffLen) {
ox_memcpy(val.data(), &m_buff[m_buffIt], size);
m_buffIt += size;
} else {
return OxError(MC_BUFFENDED);
}
} else {
return OxError(MC_OUTBUFFENDED);
}
} else {
val.data()[0] = 0;
if (m_unionIdx == -1 || m_unionIdx == m_field) {
auto valErr = m_fieldPresence.get(m_field);
*val = valErr.value;
oxReturnError(valErr.error);
}
++m_field;
return OxError(0);
}
[[nodiscard]] ValErr<ArrayLength> MetalClawReader::arrayLength(bool pass) {
if (m_fieldPresence.get(m_field)) {
Error MetalClawReader::field(const char*, SerStr val) {
if (m_unionIdx == -1 || m_unionIdx == m_field) {
if (m_fieldPresence.get(m_field)) {
// read the length
if (m_buffIt >= m_buffLen) {
return OxError(MC_BUFFENDED);
}
std::size_t bytesRead = 0;
auto [size, err] = mc::decodeInteger<StringLength>(&m_buff[m_buffIt], m_buffLen - m_buffIt, &bytesRead);
m_buffIt += bytesRead;
oxReturnError(err);
auto data = val.data(size + 1);
// read the string
if (val.cap() > -1 && static_cast<StringLength>(val.cap()) >= size) {
if (m_buffIt + size <= m_buffLen) {
ox_memcpy(data, &m_buff[m_buffIt], size);
data[size] = 0;
m_buffIt += size;
} else {
return OxError(MC_BUFFENDED);
}
} else {
return OxError(MC_OUTBUFFENDED);
}
} else {
auto data = val.data();
if (data) {
data[0] = 0;
}
}
}
++m_field;
return OxError(0);
}
[[nodiscard]] ValErr<ArrayLength> MetalClawReader::arrayLength(const char*, bool pass) {
if ((m_unionIdx == -1 || m_unionIdx == m_field) && m_fieldPresence.get(m_field)) {
// read the length
if (m_buffIt >= m_buffLen) {
return OxError(MC_BUFFENDED);
@ -110,8 +125,8 @@ Error MetalClawReader::field(const char*, SerStr val) {
return OxError(1);
}
[[nodiscard]] StringLength MetalClawReader::stringLength() {
if (m_fieldPresence.get(m_field)) {
[[nodiscard]] StringLength MetalClawReader::stringLength(const char*) {
if ((m_unionIdx == -1 || m_unionIdx == m_field) && m_fieldPresence.get(m_field)) {
// read the length
std::size_t bytesRead = 0;
auto len = mc::decodeInteger<StringLength>(&m_buff[m_buffIt], m_buffLen - m_buffIt, &bytesRead);
@ -120,18 +135,11 @@ Error MetalClawReader::field(const char*, SerStr val) {
return 0;
}
void MetalClawReader::setTypeInfo(const char*, int fields) {
m_fields = fields;
m_buffIt = (fields / 8 + 1) - (fields % 8 == 0);
m_fieldPresence.setFields(fields);
m_fieldPresence.setMaxLen(m_buffIt);
MetalClawReader MetalClawReader::child(const char*, int unionIdx) {
return MetalClawReader(m_buff + m_buffIt, m_buffLen - m_buffIt, unionIdx, this);
}
MetalClawReader MetalClawReader::child() {
return MetalClawReader(m_buff + m_buffIt, m_buffLen - m_buffIt, this);
}
bool MetalClawReader::fieldPresent() const {
bool MetalClawReader::fieldPresent(const char*) const {
return m_fieldPresence.get(m_field).value;
}

View File

@ -28,73 +28,78 @@ class MetalClawReader {
FieldPresenceIndicator m_fieldPresence;
int m_fields = 0;
int m_field = 0;
int m_unionIdx = -1;
std::size_t m_buffIt = 0;
std::size_t m_buffLen = 0;
uint8_t *m_buff = nullptr;
MetalClawReader *m_parent = nullptr;
public:
MetalClawReader(uint8_t *buff, std::size_t buffLen, MetalClawReader *parent = nullptr);
MetalClawReader(uint8_t *buff, std::size_t buffLen, int unionIdx = -1, MetalClawReader *parent = nullptr) noexcept;
~MetalClawReader();
~MetalClawReader() noexcept;
Error field(const char*, int8_t *val);
Error field(const char*, int16_t *val);
Error field(const char*, int32_t *val);
Error field(const char*, int64_t *val);
[[nodiscard]] Error field(const char*, int8_t *val);
[[nodiscard]] Error field(const char*, int16_t *val);
[[nodiscard]] Error field(const char*, int32_t *val);
[[nodiscard]] Error field(const char*, int64_t *val);
Error field(const char*, uint8_t *val);
Error field(const char*, uint16_t *val);
Error field(const char*, uint32_t *val);
Error field(const char*, uint64_t *val);
[[nodiscard]] Error field(const char*, uint8_t *val);
[[nodiscard]] Error field(const char*, uint16_t *val);
[[nodiscard]] Error field(const char*, uint32_t *val);
[[nodiscard]] Error field(const char*, uint64_t *val);
Error field(const char*, bool *val);
[[nodiscard]] Error field(const char*, bool *val);
// array handler
template<typename T>
Error field(const char*, T *val, std::size_t len);
[[nodiscard]] Error field(const char*, T *val, std::size_t len);
// map handler
template<typename T>
[[nodiscard]] Error field(const char*, HashMap<String, T> *val);
// array handler, with callback to allow handling individual elements
template<typename T, typename Handler>
Error field(const char*, Handler handler);
// array handler, with callback to allow handling individual elements
template<typename T, typename Handler>
Error field(const char*, Handler handler, ArrayLength len);
[[nodiscard]] Error field(const char*, Handler handler);
template<typename T>
Error field(const char*, ox::Vector<T> *val);
[[nodiscard]] Error field(const char*, ox::Vector<T> *val);
template<typename T>
Error field(const char*, T *val);
[[nodiscard]] Error field(const char*, T *val);
template<typename U>
[[nodiscard]] Error field(const char*, UnionView<U> val);
template<std::size_t L>
Error field(const char*, ox::BString<L> *val);
[[nodiscard]] Error field(const char*, ox::BString<L> *val);
Error field(const char*, SerStr val);
[[nodiscard]] Error field(const char*, SerStr val);
/**
* Reads an array length from the current location in the buffer.
* @param pass indicates that the parsing should iterate past the array length
*/
[[nodiscard]] ValErr<ArrayLength> arrayLength(bool pass = true);
[[nodiscard]] ValErr<ArrayLength> arrayLength(const char *name, bool pass = true);
/**
* Reads an string length from the current location in the buffer.
*/
[[nodiscard]] StringLength stringLength();
[[nodiscard]] StringLength stringLength(const char *name);
void setTypeInfo(const char *name, int fields);
template<typename T = std::nullptr_t>
void setTypeInfo(const char *name = T::TypeName, int fields = T::Fields);
/**
* Returns a MetalClawReader to parse a child object.
*/
[[nodiscard]] MetalClawReader child();
[[nodiscard]] MetalClawReader child(const char *name, int unionIdx = -1);
/**
* Indicates whether or not the next field to be read is present.
*/
bool fieldPresent() const;
bool fieldPresent(const char *name) const;
/**
* Indicates whether or not the given field is present.
@ -103,22 +108,33 @@ class MetalClawReader {
void nextField() noexcept;
static constexpr OpType opType() {
static constexpr auto opType() {
return OpType::Read;
}
private:
template<typename I>
Error readInteger(I *val);
[[nodiscard]] Error readInteger(I *val);
};
template<typename T>
Error MetalClawReader::field(const char*, T *val) {
if (val && m_fieldPresence.get(m_field++)) {
auto reader = child();
if ((m_unionIdx == -1 || m_unionIdx == m_field) && val && m_fieldPresence.get(m_field)) {
auto reader = child("");
oxReturnError(model(&reader, val));
}
++m_field;
return OxError(0);
}
template<typename U>
Error MetalClawReader::field(const char*, UnionView<U> val) {
if ((m_unionIdx == -1 || m_unionIdx == m_field) && val.get() && m_fieldPresence.get(m_field)) {
auto reader = child("", val.idx());
oxReturnError(model(&reader, val.get()));
}
++m_field;
return OxError(0);
}
@ -129,85 +145,133 @@ Error MetalClawReader::field(const char *name, ox::BString<L> *val) {
template<typename I>
Error MetalClawReader::readInteger(I *val) {
if (m_fieldPresence.get(m_field++)) {
std::size_t bytesRead = 0;
if (m_buffIt >= m_buffLen) {
oxTrace("ox::MetalClaw::readInteger") << "Buffer ended";
return OxError(MC_BUFFENDED);
if (m_unionIdx == -1 || m_unionIdx == m_field) {
if (m_fieldPresence.get(m_field)) {
std::size_t bytesRead = 0;
if (m_buffIt >= m_buffLen) {
oxTrace("ox::MetalClaw::readInteger") << "Buffer ended";
return OxError(MC_BUFFENDED);
}
auto valErr = mc::decodeInteger<I>(&m_buff[m_buffIt], m_buffLen - m_buffIt, &bytesRead);
m_buffIt += bytesRead;
oxReturnError(valErr.error);
*val = valErr.value;
} else {
*val = 0;
}
auto valErr = mc::decodeInteger<I>(&m_buff[m_buffIt], m_buffLen - m_buffIt, &bytesRead);
m_buffIt += bytesRead;
oxReturnError(valErr.error);
*val = valErr.value;
} else {
*val = 0;
}
++m_field;
return OxError(0);
}
// array handler
template<typename T>
Error MetalClawReader::field(const char *name, T *val, std::size_t valLen) {
if (m_fieldPresence.get(m_field++)) {
// read the length
if (m_buffIt >= m_buffLen) {
return OxError(MC_BUFFENDED);
}
std::size_t bytesRead = 0;
auto len = mc::decodeInteger<ArrayLength>(&m_buff[m_buffIt], m_buffLen - m_buffIt, &bytesRead);
m_buffIt += bytesRead;
oxReturnError(len.error);
// read the list
if (valLen >= len.value) {
auto reader = child();
reader.setTypeInfo("List", len.value);
for (std::size_t i = 0; i < len.value; i++) {
oxReturnError(reader.field("", &val[i]));
if (m_unionIdx == -1 || m_unionIdx == m_field) {
if (m_fieldPresence.get(m_field)) {
// read the length
if (m_buffIt >= m_buffLen) {
return OxError(MC_BUFFENDED);
}
std::size_t bytesRead = 0;
auto len = mc::decodeInteger<ArrayLength>(&m_buff[m_buffIt], m_buffLen - m_buffIt, &bytesRead);
m_buffIt += bytesRead;
oxReturnError(len.error);
// read the list
if (valLen >= len.value) {
auto reader = child("");
reader.setTypeInfo("List", len.value);
for (std::size_t i = 0; i < len.value; i++) {
oxReturnError(reader.field("", &val[i]));
}
} else {
oxTrace("ox::mc::read::field(T)") << name << ", size:" << valLen;
return OxError(MC_OUTBUFFENDED);
}
} else {
oxTrace("ox::mc::read::field(T)") << name << ", size:" << valLen;
return OxError(MC_OUTBUFFENDED);
}
}
++m_field;
return OxError(0);
}
template<typename T>
Error MetalClawReader::field(const char*, HashMap<String, T> *val) {
if (m_unionIdx == -1 || m_unionIdx == m_field) {
if (m_fieldPresence.get(m_field)) {
// read the length
if (m_buffIt >= m_buffLen) {
return OxError(MC_BUFFENDED);
}
std::size_t bytesRead = 0;
auto len = mc::decodeInteger<ArrayLength>(&m_buff[m_buffIt], m_buffLen - m_buffIt, &bytesRead);
m_buffIt += bytesRead;
oxReturnError(len.error);
// read the list
auto reader = child("");
reader.setTypeInfo("List", len.value);
for (std::size_t i = 0; i < len.value; i++) {
auto keyLen = reader.stringLength(nullptr);
auto wkey = ox_malloca(keyLen + 1, char, 0);
oxReturnError(reader.field("", SerStr(wkey.get(), keyLen)));
oxReturnError(reader.field("", &val->at(wkey.get())));
}
}
}
++m_field;
return OxError(0);
}
template<typename T, typename Handler>
Error MetalClawReader::field(const char*, Handler handler) {
if (m_fieldPresence.get(m_field++)) {
// read the length
if (m_buffIt >= m_buffLen) {
return OxError(MC_BUFFENDED);
}
std::size_t bytesRead = 0;
auto len = mc::decodeInteger<ArrayLength>(&m_buff[m_buffIt], m_buffLen - m_buffIt, &bytesRead);
m_buffIt += bytesRead;
oxReturnError(len.error);
if (m_unionIdx == -1 || m_unionIdx == m_field) {
if (m_fieldPresence.get(m_field)) {
// read the length
if (m_buffIt >= m_buffLen) {
return OxError(MC_BUFFENDED);
}
std::size_t bytesRead = 0;
auto len = mc::decodeInteger<ArrayLength>(&m_buff[m_buffIt], m_buffLen - m_buffIt, &bytesRead);
m_buffIt += bytesRead;
oxReturnError(len.error);
// read the list
auto reader = child();
reader.setTypeInfo("List", len.value);
for (std::size_t i = 0; i < len.value; i++) {
T val;
oxReturnError(reader.field("", &val));
oxReturnError(handler(i, &val));
// read the list
auto reader = child("");
reader.setTypeInfo("List", len.value);
for (std::size_t i = 0; i < len.value; i++) {
T val;
oxReturnError(reader.field("", &val));
oxReturnError(handler(i, &val));
}
}
}
++m_field;
return OxError(0);
}
template<typename T>
Error MetalClawReader::field(const char* name, ox::Vector<T> *val) {
if (m_fieldPresence.get(m_field)) {
const auto [len, err] = arrayLength(false);
oxReturnError(err);
val->resize(len);
if (m_unionIdx == -1 || m_unionIdx == m_field) {
// set size of val if the field is present, don't worry about it if not
if (m_fieldPresence.get(m_field)) {
const auto [len, err] = arrayLength(name, false);
oxReturnError(err);
val->resize(len);
}
return field(name, val->data(), val->size());
}
return OxError(0);
}
template<typename T>
void MetalClawReader::setTypeInfo(const char*, int fields) {
m_fields = fields;
m_buffIt = (fields / 8 + 1) - (fields % 8 == 0);
m_fieldPresence.setFields(fields);
m_fieldPresence.setMaxLen(m_buffIt);
}
template<typename T>
Error readMC(uint8_t *buff, std::size_t buffLen, T *val) {
MetalClawReader reader(buff, buffLen);

View File

@ -6,6 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include "ox/std/hashmap.hpp"
#undef NDEBUG
#include <assert.h>
@ -18,13 +19,25 @@
#include <ox/model/model.hpp>
#include <ox/std/std.hpp>
union TestUnion {
static constexpr auto TypeName = "TestUnion";
static constexpr auto Fields = 3;
bool Bool;
uint32_t Int = 5;
char String[32];
};
struct TestStructNest {
static constexpr auto TypeName = "TestStructNest";
static constexpr auto Fields = 3;
bool Bool = false;
uint32_t Int = 0;
ox::BString<32> String = "";
};
struct TestStruct {
static constexpr auto TypeName = "TestStruct";
static constexpr auto Fields = 17;
bool Bool = false;
int32_t Int = 0;
int32_t Int1 = 0;
@ -35,41 +48,59 @@ struct TestStruct {
int32_t Int6 = 0;
int32_t Int7 = 0;
int32_t Int8 = 0;
TestUnion Union;
char *CString = nullptr;
ox::BString<32> String = "";
uint32_t List[4] = {0, 0, 0 , 0};
uint32_t List[4] = {0, 0, 0, 0};
ox::HashMap<ox::String, int> Map;
TestStructNest EmptyStruct;
TestStructNest Struct;
~TestStruct() {
delete[] CString;
}
};
template<typename T>
ox::Error model(T *io, TestUnion *obj) {
io->template setTypeInfo<TestUnion>();
oxReturnError(io->field("Bool", &obj->Bool));
oxReturnError(io->field("Int", &obj->Int));
oxReturnError(io->field("String", ox::SerStr(obj->String)));
return OxError(0);
}
template<typename T>
ox::Error model(T *io, TestStructNest *obj) {
auto err = OxError(0);
io->setTypeInfo("TestStructNest", 3);
err |= io->field("Bool", &obj->Bool);
err |= io->field("Int", &obj->Int);
err |= io->field("String", &obj->String);
return err;
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>
ox::Error model(T *io, TestStruct *obj) {
auto err = OxError(0);
io->setTypeInfo("TestStruct", 14);
err |= io->field("Bool", &obj->Bool);
err |= io->field("Int", &obj->Int);
err |= io->field("Int1", &obj->Int1);
err |= io->field("Int2", &obj->Int2);
err |= io->field("Int3", &obj->Int3);
err |= io->field("Int4", &obj->Int4);
err |= io->field("Int5", &obj->Int5);
err |= io->field("Int6", &obj->Int6);
err |= io->field("Int7", &obj->Int7);
err |= io->field("Int8", &obj->Int8);
err |= io->field("String", &obj->String);
err |= io->field("List", obj->List, 4);
err |= io->field("EmptyStruct", &obj->EmptyStruct);
err |= io->field("Struct", &obj->Struct);
return err;
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("Union", ox::UnionView{&obj->Union, 1}));
oxReturnError(io->field("CString", ox::SerStr(&obj->CString)));
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);
}
std::map<std::string, ox::Error(*)()> tests = {
@ -81,12 +112,11 @@ std::map<std::string, ox::Error(*)()> tests = {
// doesn't segfault
constexpr size_t buffLen = 1024;
uint8_t buff[buffLen];
auto err = ox::Error(0);
TestStruct ts;
err |= ox::writeMC(buff, buffLen, &ts);
oxReturnError(ox::writeMC(buff, buffLen, &ts));
return err;
return OxError(0);
}
},
{
@ -98,18 +128,23 @@ std::map<std::string, ox::Error(*)()> tests = {
testIn.Bool = true;
testIn.Int = 42;
testIn.Union.Int = 42;
testIn.String = "Test String 1";
testIn.CString = new char[ox_strlen("c-string") + 1];
ox_strcpy(testIn.CString, "c-string");
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";
oxAssert(ox::writeMC(buff, buffLen, &testIn), "writeMC failed");
oxAssert(ox::readMC(buff, buffLen, &testOut), "writeMC failed");
std::cout << testIn.Int << " " << testOut.Int << '\n';
oxAssert(ox::readMC(buff, buffLen, &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");
@ -121,11 +156,15 @@ std::map<std::string, ox::Error(*)()> tests = {
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(ox_strcmp(testIn.CString, testOut.CString) == 0, "CString 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");
@ -217,10 +256,12 @@ std::map<std::string, ox::Error(*)()> tests = {
oxReturnError(result.error);
if (result.value != val) {
std::cout << "Bad value: " << result.value << ", expected: " << val << '\n';
return OxError(1);
}
return OxError(result.value != val);
return OxError(0);
};
oxAssert(check(uint32_t(14)), "Decode of 14 failed.");
oxAssert(check(int64_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(42)), "Decode of 42 failed.");
@ -241,7 +282,6 @@ std::map<std::string, ox::Error(*)()> tests = {
{
"MetalClawDef",
[] {
auto err = OxError(0);
//constexpr size_t descBuffLen = 1024;
//uint8_t descBuff[descBuffLen];
constexpr size_t dataBuffLen = 1024;
@ -260,9 +300,9 @@ std::map<std::string, ox::Error(*)()> tests = {
testIn.Struct.String = "Test String 2";
oxAssert(ox::writeMC(dataBuff, dataBuffLen, &testIn), "Data generation failed");
auto type = ox::buildMCDef(&testIn);
auto type = ox::buildTypeDef(&testIn);
oxAssert(type.error, "Descriptor write failed");
ox::walkMC<ox::MetalClawReader>(type.value, dataBuff, dataBuffLen,
ox::walkModel<ox::MetalClawReader>(type.value, dataBuff, dataBuffLen,
[](const ox::Vector<ox::FieldName>&, const ox::Vector<ox::TypeName>&, const ox::DescriptorField &f, ox::MetalClawReader *rdr) -> ox::Error {
//std::cout << f.fieldName.c_str() << '\n';
auto fieldName = f.fieldName.c_str();
@ -330,25 +370,26 @@ std::map<std::string, ox::Error(*)()> tests = {
case ox::PrimitiveType::Bool: {
bool i = {};
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
std::cout << fieldName << ":\t" << "bool:\t" << (i ? "true" : "false") << '\n';
std::cout << fieldName << ":\t" << "bool:\t\t" << (i ? "true" : "false") << '\n';
break;
}
case ox::PrimitiveType::String: {
ox::Vector<char> v(rdr->stringLength());
ox::Vector<char> v(rdr->stringLength(fieldName) + 1);
//std::cout << rdr->stringLength() << '\n';
oxAssert(rdr->field(fieldName, ox::SerStr(v.data(), v.size())), "Walking model failed.");
std::cout << fieldName << ":\t" << "string: " << v.data() << '\n';
std::cout << fieldName << ":\t" << "string:\t\t" << v.data() << '\n';
break;
}
case ox::PrimitiveType::Struct:
break;
case ox::PrimitiveType::Union:
break;
}
return OxError(0);
}
);
delete type.value;
return err;
return OxError(0);
}
},
}

View File

@ -1,22 +0,0 @@
/*
* Copyright 2015 - 2018 gtalent2@gmail.com
*
* 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/model/walk.hpp>
namespace ox {
template<typename Reader, typename Handler>
ox::Error walkMC(DescriptorType *type, uint8_t *data, std::size_t dataLen, Handler handler) {
DataWalker<Reader, Handler> walker(type, handler);
Reader rdr(data, dataLen);
return model(&rdr, &walker);
}
}

View File

@ -9,88 +9,88 @@
#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 {
MetalClawWriter::MetalClawWriter(uint8_t *buff, std::size_t buffLen): m_fieldPresence(buff, buffLen) {
m_buff = buff;
m_buffLen = buffLen;
MetalClawWriter::MetalClawWriter(uint8_t *buff, std::size_t buffLen, int unionIdx) noexcept:
m_fieldPresence(buff, buffLen),
m_unionIdx(unionIdx),
m_buffLen(buffLen),
m_buff(buff) {
}
MetalClawWriter::~MetalClawWriter() noexcept {
oxAssert(m_field == m_fields, "MetalClawWriter: incorrect fields number given");
if (m_field != m_fields) {
oxTrace("ox::mc::MetalClawWriter::error") << "MetalClawReader: incorrect fields number given";
}
}
Error MetalClawWriter::field(const char*, int8_t *val) {
Error MetalClawWriter::field(const char*, int8_t *val) noexcept {
return appendInteger(*val);
}
Error MetalClawWriter::field(const char*, int16_t *val) {
Error MetalClawWriter::field(const char*, int16_t *val) noexcept {
return appendInteger(*val);
}
Error MetalClawWriter::field(const char*, int32_t *val) {
Error MetalClawWriter::field(const char*, int32_t *val) noexcept {
return appendInteger(*val);
}
Error MetalClawWriter::field(const char*, int64_t *val) {
Error MetalClawWriter::field(const char*, int64_t *val) noexcept {
return appendInteger(*val);
}
Error MetalClawWriter::field(const char*, uint8_t *val) {
Error MetalClawWriter::field(const char*, uint8_t *val) noexcept {
return appendInteger(*val);
}
Error MetalClawWriter::field(const char*, uint16_t *val) {
Error MetalClawWriter::field(const char*, uint16_t *val) noexcept {
return appendInteger(*val);
}
Error MetalClawWriter::field(const char*, uint32_t *val) {
Error MetalClawWriter::field(const char*, uint32_t *val) noexcept {
return appendInteger(*val);
}
Error MetalClawWriter::field(const char*, uint64_t *val) {
Error MetalClawWriter::field(const char*, uint64_t *val) noexcept {
return appendInteger(*val);
}
Error MetalClawWriter::field(const char*, bool *val) {
return m_fieldPresence.set(m_field++, *val);
Error MetalClawWriter::field(const char*, bool *val) noexcept {
if (m_unionIdx == -1 || m_unionIdx == m_field) {
oxReturnError(m_fieldPresence.set(m_field, *val));
}
++m_field;
return OxError(0);
}
Error MetalClawWriter::field(const char*, SerStr val) {
auto err = OxError(0);
Error MetalClawWriter::field(const char*, SerStr val) noexcept {
bool fieldSet = false;
if (val.cap()) {
if (val.len() && (m_unionIdx == -1 || m_unionIdx == m_field)) {
// write the length
const auto strLen = mc::encodeInteger(val.bytes());
if (m_buffIt + strLen.length + val.bytes() < m_buffLen) {
const auto strLen = mc::encodeInteger(val.len());
if (m_buffIt + strLen.length + val.len() < m_buffLen) {
ox_memcpy(&m_buff[m_buffIt], strLen.data, strLen.length);
m_buffIt += strLen.length;
// write the string
ox_memcpy(&m_buff[m_buffIt], val.c_str(), val.bytes());
m_buffIt += val.bytes();
ox_memcpy(&m_buff[m_buffIt], val.c_str(), val.len());
m_buffIt += val.len();
fieldSet = true;
} else {
err = OxError(MC_BUFFENDED);
return OxError(MC_BUFFENDED);
}
}
err |= m_fieldPresence.set(m_field, fieldSet);
m_field++;
return err;
oxReturnError(m_fieldPresence.set(m_field, fieldSet));
++m_field;
return OxError(0);
}
void MetalClawWriter::setTypeInfo(const char*, int fields) {
m_fields = fields;
m_buffIt = (fields / 8 + 1) - (fields % 8 == 0);
m_fieldPresence.setFields(fields);
m_fieldPresence.setMaxLen(m_buffIt);
}
std::size_t MetalClawWriter::size() {
std::size_t MetalClawWriter::size() noexcept {
return m_buffIt;
}

View File

@ -14,10 +14,12 @@
#include <ox/std/byteswap.hpp>
#include <ox/std/string.hpp>
#include <ox/std/types.hpp>
#include <ox/std/units.hpp>
#include <ox/std/vector.hpp>
#include "intops.hpp"
#include "err.hpp"
#include "ox/std/hashmap.hpp"
#include "presenceindicator.hpp"
#include "types.hpp"
@ -29,64 +31,73 @@ class MetalClawWriter {
FieldPresenceIndicator m_fieldPresence;
int m_fields = 0;
int m_field = 0;
int m_unionIdx = -1;
std::size_t m_buffIt = 0;
std::size_t m_buffLen = 0;
uint8_t *m_buff = nullptr;
public:
MetalClawWriter(uint8_t *buff, std::size_t buffLen);
MetalClawWriter(uint8_t *buff, std::size_t buffLen, int unionIdx = -1) noexcept;
~MetalClawWriter() noexcept;
Error field(const char*, int8_t *val);
Error field(const char*, int16_t *val);
Error field(const char*, int32_t *val);
Error field(const char*, int64_t *val);
[[nodiscard]] Error field(const char*, int8_t *val) noexcept;
[[nodiscard]] Error field(const char*, int16_t *val) noexcept;
[[nodiscard]] Error field(const char*, int32_t *val) noexcept;
[[nodiscard]] Error field(const char*, int64_t *val) noexcept;
Error field(const char*, uint8_t *val);
Error field(const char*, uint16_t *val);
Error field(const char*, uint32_t *val);
Error field(const char*, uint64_t *val);
[[nodiscard]] Error field(const char*, uint8_t *val) noexcept;
[[nodiscard]] Error field(const char*, uint16_t *val) noexcept;
[[nodiscard]] Error field(const char*, uint32_t *val) noexcept;
[[nodiscard]] Error field(const char*, uint64_t *val) noexcept;
Error field(const char*, bool *val);
[[nodiscard]] Error field(const char*, bool *val) noexcept;
template<typename T>
Error field(const char*, T *val, std::size_t len);
[[nodiscard]] Error field(const char*, T *val, std::size_t len);
template<typename T>
Error field(const char*, ox::Vector<T> *val);
[[nodiscard]] Error field(const char*, Vector<T> *val);
template<typename T>
[[nodiscard]] Error field(const char*, HashMap<String, T> *val);
template<std::size_t L>
Error field(const char*, ox::BString<L> *val);
[[nodiscard]] Error field(const char*, ox::BString<L> *val) noexcept;
Error field(const char*, SerStr val);
[[nodiscard]] Error field(const char*, SerStr val) noexcept;
template<typename T>
Error field(const char*, T *val);
[[nodiscard]] Error field(const char*, T *val);
void setTypeInfo(const char *name, int fields);
template<typename U>
[[nodiscard]] Error field(const char*, UnionView<U> val);
std::size_t size();
template<typename T = std::nullptr_t>
void setTypeInfo(const char *name = T::TypeName, int fields = T::Fields);
static constexpr OpType opType() {
std::size_t size() noexcept;
static constexpr auto opType() {
return OpType::Write;
}
private:
template<typename I>
Error appendInteger(I val);
[[nodiscard]] Error appendInteger(I val) noexcept;
};
template<std::size_t L>
Error MetalClawWriter::field(const char *name, ox::BString<L> *val) {
Error MetalClawWriter::field(const char *name, ox::BString<L> *val) noexcept {
return field(name, SerStr(val->data(), val->cap()));
}
template<typename T>
Error MetalClawWriter::field(const char*, T *val) {
bool fieldSet = false;
MetalClawWriter writer(m_buff + m_buffIt, m_buffLen - m_buffIt);
if (val) {
if (val && (m_unionIdx == -1 || m_unionIdx == m_field)) {
MetalClawWriter writer(m_buff + m_buffIt, m_buffLen - m_buffIt);
oxReturnError(model(&writer, val));
if (static_cast<std::size_t>(writer.m_fieldPresence.getMaxLen()) < writer.m_buffIt) {
m_buffIt += writer.m_buffIt;
@ -98,27 +109,42 @@ Error MetalClawWriter::field(const char*, T *val) {
return OxError(0);
}
template<typename U>
Error MetalClawWriter::field(const char*, UnionView<U> val) {
bool fieldSet = false;
if (val.get() && (m_unionIdx == -1 || m_unionIdx == m_field)) {
MetalClawWriter writer(m_buff + m_buffIt, m_buffLen - m_buffIt, val.idx());
oxReturnError(model(&writer, val.get()));
if (static_cast<std::size_t>(writer.m_fieldPresence.getMaxLen()) < writer.m_buffIt) {
m_buffIt += writer.m_buffIt;
fieldSet = true;
}
}
oxReturnError(m_fieldPresence.set(m_field, fieldSet));
m_field++;
return OxError(0);
}
template<typename T>
Error MetalClawWriter::field(const char*, T *val, std::size_t len) {
auto err = OxError(0);
bool fieldSet = false;
if (len) {
if (len && (m_unionIdx == -1 || m_unionIdx == m_field)) {
// write the length
const auto arrLen = mc::encodeInteger(len);
if (m_buffIt + arrLen.length < m_buffLen) {
ox_memcpy(&m_buff[m_buffIt], arrLen.data, arrLen.length);
m_buffIt += arrLen.length;
} else {
err = OxError(MC_BUFFENDED);
return OxError(MC_BUFFENDED);
}
MetalClawWriter writer(m_buff + m_buffIt, m_buffLen - m_buffIt);
writer.setTypeInfo("List", len);
writer.setTypeInfo<T>("List", len);
// write the array
for (std::size_t i = 0; i < len; i++) {
err |= writer.field("", &val[i]);
oxReturnError(writer.field("", &val[i]));
}
m_buffIt += writer.m_buffIt;
@ -127,31 +153,87 @@ Error MetalClawWriter::field(const char*, T *val, std::size_t len) {
oxReturnError(m_fieldPresence.set(m_field, fieldSet));
m_field++;
return err;
return OxError(0);
}
template<typename T>
Error MetalClawWriter::field(const char*, ox::Vector<T> *val) {
Error MetalClawWriter::field(const char*, Vector<T> *val) {
return field(nullptr, val->data(), val->size());
}
template<typename I>
Error MetalClawWriter::appendInteger(I val) {
auto err = OxError(0);
template<typename T>
[[nodiscard]] Error MetalClawWriter::field(const char*, HashMap<String, T> *val) {
auto &keys = val->keys();
auto len = keys.size();
bool fieldSet = false;
if (val) {
if (len && (m_unionIdx == -1 || m_unionIdx == m_field)) {
// write the length
const auto arrLen = mc::encodeInteger(len);
if (m_buffIt + arrLen.length < m_buffLen) {
ox_memcpy(&m_buff[m_buffIt], arrLen.data, arrLen.length);
m_buffIt += arrLen.length;
} else {
return OxError(MC_BUFFENDED);
}
MetalClawWriter writer(m_buff + m_buffIt, m_buffLen - m_buffIt);
// double len for both key and value
writer.setTypeInfo("Map", len * 2);
// write the array
for (std::size_t i = 0; i < len; i++) {
auto &key = keys[i];
const auto keyLen = ox_strlen(key);
auto wkey = static_cast<char*>(ox_alloca(keyLen + 1));
memcpy(wkey, key.c_str(), keyLen + 1);
oxReturnError(writer.field("", SerStr(&wkey, keyLen)));
oxReturnError(writer.field("", &(*val)[key]));
}
m_buffIt += writer.m_buffIt;
fieldSet = true;
}
oxReturnError(m_fieldPresence.set(m_field, fieldSet));
m_field++;
return OxError(0);
}
template<typename I>
Error MetalClawWriter::appendInteger(I val) noexcept {
bool fieldSet = false;
if (val && (m_unionIdx == -1 || m_unionIdx == m_field)) {
auto mi = mc::encodeInteger(val);
if (mi.length < m_buffLen) {
fieldSet = true;
ox_memcpy(&m_buff[m_buffIt], mi.data, mi.length);
m_buffIt += mi.length;
} else {
err |= OxError(MC_BUFFENDED);
oxReturnError(OxError(MC_BUFFENDED));
}
}
err |= m_fieldPresence.set(m_field, fieldSet);
oxReturnError(m_fieldPresence.set(m_field, fieldSet));
m_field++;
return err;
return OxError(0);
;
}
template<typename T>
void MetalClawWriter::setTypeInfo(const char*, int fields) {
m_fields = fields;
m_fieldPresence.setFields(fields);
m_buffIt = m_fieldPresence.getMaxLen();
ox_memset(m_buff, 0, m_buffIt);
}
template<typename T>
ValErr<Vector<char>> writeMC(T *val) {
Vector<char> buff(10 * units::MB);
MetalClawWriter writer(bit_cast<uint8_t*>(buff.data()), buff.size());
oxReturnError(model(&writer, val));
buff.resize(writer.size());
return buff;
}
template<typename T>

View File

@ -8,11 +8,14 @@
#pragma once
#include <ox/std/bit.hpp>
#include <ox/std/error.hpp>
#include <ox/std/hashmap.hpp>
#include <ox/std/bstring.hpp>
#include <ox/std/vector.hpp>
#include "types.hpp"
namespace ox {
using ModelString = BString<100>;
@ -26,13 +29,16 @@ enum class PrimitiveType: uint8_t {
// Float = 3, reserved, but not implemented
String = 4,
Struct = 5,
Union = 6,
};
struct DescriptorField {
// order of fields matters
static constexpr auto TypeVersion = 1;
// only serialize type name if type has already been serialized
const struct DescriptorType *type = nullptr;
struct DescriptorType *type = nullptr;
FieldName fieldName;
int subscriptLevels = 0;
@ -53,7 +59,7 @@ struct DescriptorField {
ownsType = false; // is copy, only owns type if move
}
constexpr DescriptorField(const DescriptorType *type, const FieldName &fieldName, int subscriptLevels, const TypeName &typeName, bool ownsType) noexcept {
constexpr DescriptorField(DescriptorType *type, const FieldName &fieldName, int subscriptLevels, const TypeName &typeName, bool ownsType) noexcept {
this->type = type;
this->fieldName = fieldName;
this->subscriptLevels = subscriptLevels;
@ -98,6 +104,7 @@ struct DescriptorField {
using FieldList = Vector<DescriptorField>;
struct DescriptorType {
static constexpr auto TypeVersion = 1;
TypeName typeName;
PrimitiveType primitiveType;
// fieldList only applies to structs
@ -105,6 +112,7 @@ struct DescriptorType {
// - number of bytes for integer and float types
// - number of fields for structs and lists
int64_t length = 0;
bool preloadable = false;
DescriptorType() = default;
@ -118,13 +126,13 @@ struct DescriptorType {
template<typename T>
Error model(T *io, DescriptorType *type) {
auto err = OxError(0);
io->setTypeInfo("ox::DescriptorType", 4);
err |= io->field("typeName", &type->typeName);
err |= io->field("primitiveType", &type->primitiveType);
err |= io->field("fieldList", &type->fieldList);
err |= io->field("length", &type->length);
return err;
io->template setTypeInfo<T>("net.drinkingtea.ox.DescriptorType", 5);
oxReturnError(io->field("typeName", &type->typeName));
oxReturnError(io->field("primitiveType", bit_cast<uint8_t*>(&type->primitiveType)));
oxReturnError(io->field("fieldList", &type->fieldList));
oxReturnError(io->field("length", &type->length));
oxReturnError(io->field("preloadable", &type->preloadable));
return OxError(0);
}
template<typename T>
@ -132,16 +140,17 @@ Error modelWrite(T *io, DescriptorField *field) {
auto err = OxError(0);
io->setTypeInfo("ox::DescriptorField", 4);
if (field->ownsType) {
err |= io->field("typeName", "");
err |= io->field("type", field->type);
BString<2> empty = "";
oxReturnError(io->field("typeName", SerStr(&empty)));
oxReturnError(io->field("type", field->type));
} else {
err |= io->field("typeName", &field->type->typeName);
err |= io->field("type", static_cast<decltype(field->type)>(nullptr));
oxReturnError(io->field("typeName", SerStr(&field->type->typeName)));
oxReturnError(io->field("type", static_cast<decltype(field->type)>(nullptr)));
}
err |= io->field("fieldName", &field->fieldName);
oxReturnError(io->field("fieldName", &field->fieldName));
// defaultValue is unused now, but leave placeholder for backwards compatibility
const int DefaultValue = 0;
err |= io->field("defaultValue", &DefaultValue);
int DefaultValue = 0;
oxReturnError(io->field("defaultValue", &DefaultValue));
return err;
}

View File

@ -12,6 +12,26 @@
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([] {
int i = 0;
return indirectionLevels(i) == 0;
@ -96,7 +116,7 @@ DescriptorType *TypeDescWriter::type(uint64_t*, bool *alreadyExisted) {
return getType(TypeName, PT, Bytes, alreadyExisted);
}
DescriptorType *TypeDescWriter::type(const char*, bool *alreadyExisted) {
DescriptorType *TypeDescWriter::type(char*, bool *alreadyExisted) {
constexpr auto TypeName = "B:string";
constexpr auto PT = PrimitiveType::String;
return getType(TypeName, PT, 0, alreadyExisted);
@ -108,6 +128,12 @@ DescriptorType *TypeDescWriter::type(SerStr, bool *alreadyExisted) {
return getType(TypeName, PT, 0, alreadyExisted);
}
DescriptorType *TypeDescWriter::type(String*, bool *alreadyExisted) {
constexpr auto TypeName = "B:string";
constexpr auto PT = PrimitiveType::String;
return getType(TypeName, PT, 0, alreadyExisted);
}
DescriptorType *TypeDescWriter::type(bool*, bool *alreadyExisted) {
constexpr auto TypeName = "B:bool";
constexpr auto PT = PrimitiveType::Bool;
@ -115,16 +141,6 @@ DescriptorType *TypeDescWriter::type(bool*, bool *alreadyExisted) {
return getType(TypeName, PT, Bytes, alreadyExisted);
}
void TypeDescWriter::setTypeInfo(const char *name, int) {
auto &t = m_typeStore->at(name);
if (!t) {
t = new DescriptorType;
}
m_type = t;
m_type->typeName = name;
m_type->primitiveType = PrimitiveType::Struct;
}
DescriptorType *TypeDescWriter::getType(TypeName tn, PrimitiveType pt, int b, bool *alreadyExisted) {
if (m_typeStore->contains(tn)) {
*alreadyExisted = true;

View File

@ -9,8 +9,8 @@
#pragma once
#include <ox/std/byteswap.hpp>
#include <ox/std/hashmap.hpp>
#include <ox/std/bstring.hpp>
#include <ox/std/string.hpp>
#include <ox/std/trace.hpp>
#include <ox/std/types.hpp>
#include <ox/std/vector.hpp>
@ -21,6 +21,22 @@
namespace ox {
namespace detail {
template<bool>
struct BoolWrapper {
};
template<typename T, typename = BoolWrapper<true>>
struct preloadable: false_type {};
template<typename T>
struct preloadable<T, BoolWrapper<T::Preloadable>> {
static constexpr bool value = T::Preloadable;
};
}
template<typename T>
static constexpr int indirectionLevels(T) {
return 0;
@ -38,20 +54,25 @@ class TypeDescWriter {
TypeName name;
constexpr void setTypeInfo(const char *n, int) noexcept {
template<typename T = std::nullptr_t>
constexpr void setTypeInfo(const char *n = T::TypeName, int = T::Fields) noexcept {
this->name = n;
}
template<typename T>
constexpr ox::Error field(const char*, T*, std::size_t) noexcept {
[[nodiscard]] constexpr ox::Error field(const char*, T*, std::size_t) noexcept {
return OxError(0);
}
template<typename T>
constexpr ox::Error field(const char*, T*) noexcept {
[[nodiscard]] constexpr ox::Error field(const char*, T) noexcept {
return OxError(0);
}
static constexpr auto opType() {
return OpType::WriteDefinition;
}
};
TypeStore *m_typeStoreOwnerRef = nullptr;
@ -64,21 +85,22 @@ class TypeDescWriter {
~TypeDescWriter();
template<typename T>
ox::Error field(const char *name, T *val, std::size_t valLen);
[[nodiscard]] ox::Error field(const char *name, T *val, std::size_t valLen);
template<typename T>
ox::Error field(const char *name, ox::Vector<T> *val);
[[nodiscard]] ox::Error field(const char *name, T val);
template<typename T>
ox::Error field(const char *name, T *val);
[[nodiscard]] ox::Error field(const char *name, T *val);
void setTypeInfo(const char *name, int fields);
template<typename T = std::nullptr_t>
void setTypeInfo(const char *name = T::TypeName, int fields = T::Fields);
[[nodiscard]] DescriptorType *definition() noexcept {
return m_type;
}
static constexpr OpType opType() {
static constexpr auto opType() {
return OpType::WriteDefinition;
}
@ -95,16 +117,27 @@ class TypeDescWriter {
DescriptorType *type(bool *val, bool *alreadyExisted);
DescriptorType *type(const char *val, bool *alreadyExisted);
DescriptorType *type(char *val, bool *alreadyExisted);
DescriptorType *type(SerStr val, bool *alreadyExisted);
DescriptorType *type(String *val, bool *alreadyExisted);
template<std::size_t sz>
DescriptorType *type(BString<sz> *val, bool *alreadyExisted);
template<typename T>
DescriptorType *type(T *val, bool *alreadyExisted);
template<typename T>
DescriptorType *type(Vector<T> *val, bool *alreadyExisted);
template<typename T>
DescriptorType *type(HashMap<String, T> *val, bool *alreadyExisted);
template<typename U>
DescriptorType *type(UnionView<U> val, bool *alreadyExisted);
DescriptorType *getType(TypeName tn, PrimitiveType t, int b, bool *alreadyExisted);
};
@ -126,8 +159,15 @@ ox::Error TypeDescWriter::field(const char *name, T *val, std::size_t) {
}
template<typename T>
ox::Error TypeDescWriter::field(const char *name, ox::Vector<T> *val) {
return field(name, val->data(), val->size());
ox::Error TypeDescWriter::field(const char *name, T val) {
if (m_type) {
bool alreadyExisted = false;
const auto t = type(val, &alreadyExisted);
oxAssert(t != nullptr, "field(const char *name, T val): Type not found or generated");
m_type->fieldList.emplace_back(t, name, 0, alreadyExisted ? t->typeName : "", !alreadyExisted);
return OxError(0);
}
return OxError(1);
}
template<typename T>
@ -150,7 +190,7 @@ DescriptorType *TypeDescWriter::type(BString<sz> *val, bool *alreadyExisted) {
template<typename T>
DescriptorType *TypeDescWriter::type(T *val, bool *alreadyExisted) {
NameCatcher nc;
model(&nc, val);
oxLogError(model(&nc, val));
if (m_typeStore->contains(nc.name)) {
*alreadyExisted = true;
return m_typeStore->at(nc.name);
@ -163,18 +203,49 @@ DescriptorType *TypeDescWriter::type(T *val, bool *alreadyExisted) {
}
template<typename T>
[[nodiscard]] ValErr<DescriptorType*> buildMCDef(T *val) {
DescriptorType *TypeDescWriter::type(Vector<T> *val, bool *alreadyExisted) {
return type(val->data(), alreadyExisted);
}
template<typename T>
DescriptorType *TypeDescWriter::type(HashMap<String, T>*, bool *alreadyExisted) {
return type(static_cast<T*>(nullptr), alreadyExisted);
}
template<typename U>
DescriptorType *TypeDescWriter::type(UnionView<U> val, bool *alreadyExisted) {
return type(val.get(), alreadyExisted);
}
template<typename T>
void TypeDescWriter::setTypeInfo(const char *name, int) {
auto &t = m_typeStore->at(name);
if (!t) {
t = new DescriptorType;
}
m_type = t;
m_type->typeName = name;
if (is_union_v<T>) {
m_type->primitiveType = PrimitiveType::Union;
} else {
m_type->primitiveType = PrimitiveType::Struct;
}
m_type->preloadable = detail::preloadable<T>::value;
}
template<typename T>
[[nodiscard]] ValErr<DescriptorType*> buildTypeDef(T *val) {
TypeDescWriter writer;
Error err = model(&writer, val);
return {writer.definition(), err};
}
template<typename T>
Error writeMCDef(uint8_t *buff, std::size_t buffLen, T *val, std::size_t *sizeOut = nullptr) {
auto def = buildMCDef(val);
Error writeTypeDef(uint8_t *buff, std::size_t buffLen, T *val, std::size_t *sizeOut = nullptr) {
auto def = buildTypeDef(val);
auto err = def.error;
if (!err) {
err |= writeMC(buff, buffLen, def.value, sizeOut);
oxReturnError(writeType(buff, buffLen, def.value, sizeOut));
}
delete def.value;
return err;

View File

@ -8,43 +8,47 @@
#pragma once
#include <ox/std/assert.hpp>
#include <ox/std/error.hpp>
#include <ox/std/strops.hpp>
namespace ox {
enum class OpType {
Read = 1,
Write = 2,
WriteDefinition = 3,
};
namespace OpType {
constexpr auto Read = "Read";
constexpr auto Write = "Write";
constexpr auto WriteDefinition = "WriteDefinition";
}
// empty default implementations of model functions
template<typename T, typename O>
ox::Error modelRead(T*, O*) {
return OxError(1);
[[nodiscard]] ox::Error modelRead(T*, O*) {
return OxError(1, "Model: modelRead not implemented");
}
template<typename T, typename O>
ox::Error modelWrite(T*, O*) {
return OxError(1);
[[nodiscard]] ox::Error modelWrite(T*, O*) {
return OxError(1, "Model: modelWrite not implemented");
}
template<typename T, typename O>
ox::Error modelWriteDefinition(T*, O*) {
return OxError(1);
[[nodiscard]] ox::Error modelWriteDefinition(T*, O*) {
return OxError(1, "Model: modelWriteDefinition not implemented");
}
template<typename T, typename O>
ox::Error model(T *io, O *obj) {
if constexpr(T::opType() == ox::OpType::Read) {
return modelRead(io, obj);
} else if constexpr(T::opType() == ox::OpType::Write) {
return modelWrite(io, obj);
} else if constexpr(T::opType() == ox::OpType::WriteDefinition) {
return modelWriteDefinition(io, obj);
[[nodiscard]] ox::Error model(T *io, O *obj) {
ox::Error err;
if constexpr(ox_strcmp(T::opType(), ox::OpType::Read) == 0) {
err = modelRead(io, obj);
} else if constexpr(ox_strcmp(T::opType(), ox::OpType::Write) == 0) {
err = modelWrite(io, obj);
} else if constexpr(ox_strcmp(T::opType(), ox::OpType::WriteDefinition) == 0) {
err = modelWriteDefinition(io, obj);
}
return OxError(1);
oxAssert(err, "Missing model function");
return err;
}
}

View File

@ -11,6 +11,7 @@
#include <ox/std/bstring.hpp>
#include <ox/std/strops.hpp>
#include <ox/std/types.hpp>
#include <ox/std/typetraits.hpp>
namespace ox {
@ -19,6 +20,7 @@ class SerStr {
protected:
int m_cap = 0;
char *m_str = nullptr;
char **m_tgt = nullptr;
public:
template<std::size_t sz>
@ -32,21 +34,33 @@ class SerStr {
m_cap = cap;
}
constexpr SerStr(char **tgt, int cap = -1) noexcept {
m_tgt = tgt;
m_str = const_cast<char*>(*tgt);
m_cap = cap;
}
template<std::size_t cap>
constexpr SerStr(char (&str)[cap]) noexcept {
m_str = str;
m_cap = cap;
}
constexpr const char *c_str() noexcept {
return m_str;
}
constexpr char *data() noexcept {
// do not return a non-const pointer to the const_casted m_str
constexpr char *data(std::size_t sz = 0) noexcept {
if (m_tgt && sz) {
*m_tgt = new char[sz];
m_str = *m_tgt;
m_cap = sz;
}
return m_str;
}
constexpr int len() noexcept {
return ox_strlen(m_str);
}
constexpr int bytes() noexcept {
return ox_strlen(m_str) + 1; // adds 1 for \0
return m_str ? ox_strlen(m_str) : 0;
}
constexpr int cap() noexcept {
@ -55,4 +69,25 @@ class SerStr {
};
template<typename Union>
class UnionView {
protected:
int m_idx = -1;
typename enable_if<is_union_v<Union>, Union>::type *m_union = nullptr;
public:
constexpr explicit UnionView(Union *u, int idx) noexcept: m_idx(idx), m_union(u) {
}
constexpr auto idx() noexcept {
return m_idx;
}
constexpr Union *get() noexcept {
return m_union;
}
};
}

View File

@ -30,7 +30,7 @@ class DataWalker {
[[nodiscard]] const DescriptorType *type() const noexcept;
ox::Error read(const DescriptorField&, Reader *rdr);
[[nodiscard]] ox::Error read(const DescriptorField&, Reader *rdr);
protected:
void pushNamePath(FieldName fn);
@ -86,14 +86,14 @@ static ox::Error parseField(const DescriptorField &field, Reader *rdr, DataWalke
walker->pushNamePath(field.fieldName);
if (field.subscriptLevels) {
// add array handling
const auto [arrayLen, err] = rdr->arrayLength(true);
const auto [arrayLen, err] = rdr->arrayLength(field.fieldName.c_str(), true);
oxReturnError(err);
auto child = rdr->child();
auto child = rdr->child(field.fieldName.c_str());
child.setTypeInfo(field.fieldName.c_str(), arrayLen);
DescriptorField f(field); // create mutable copy
--f.subscriptLevels;
BString<100> subscript;
for (ArrayLength i = 0; i < arrayLen; i++) {
for (std::size_t i = 0; i < arrayLen; i++) {
subscript = "[";
subscript += i;
subscript += "]";
@ -111,8 +111,9 @@ static ox::Error parseField(const DescriptorField &field, Reader *rdr, DataWalke
oxReturnError(walker->read(field, rdr));
break;
case PrimitiveType::Struct:
if (rdr->fieldPresent()) {
auto child = rdr->child();
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();
@ -120,7 +121,7 @@ static ox::Error parseField(const DescriptorField &field, Reader *rdr, DataWalke
} else {
// skip and discard absent field
int discard;
oxReturnError(rdr->field("", &discard));
oxReturnError(rdr->field(field.fieldName.c_str(), &discard));
}
break;
}
@ -144,4 +145,11 @@ ox::Error model(Reader *rdr, DataWalker<Reader, FH> *walker) {
return OxError(0);
}
template<typename Reader, typename Handler>
ox::Error walkModel(DescriptorType *type, uint8_t *data, std::size_t dataLen, Handler handler) {
DataWalker<Reader, Handler> walker(type, handler);
Reader rdr(data, dataLen);
return model(&rdr, &walker);
}
}

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

@ -0,0 +1,40 @@
add_library(
OxOrganicClaw
read.cpp
write.cpp
)
find_package(JsonCpp REQUIRED)
target_compile_options(OxOrganicClaw PRIVATE -Wsign-conversion)
target_link_libraries(
OxOrganicClaw PUBLIC
OxModel
JsonCpp::JsonCpp
)
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/ox
ARCHIVE DESTINATION lib/ox
)
if(OX_RUN_TESTS STREQUAL "ON")
add_subdirectory(test)
endif()

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

@ -0,0 +1,12 @@
/*
* Copyright 2015 - 2018 gtalent2@gmail.com
*
* 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"

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

@ -0,0 +1,255 @@
/*
* Copyright 2015 - 2020 gtalent2@gmail.com
*
* 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/bit.hpp>
#include "read.hpp"
namespace ox {
OrganicClawReader::OrganicClawReader(const uint8_t *buff, std::size_t buffSize) {
auto json = bit_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 OxError(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 OxError(1, "Could not parse JSON");
}
}
OrganicClawReader::OrganicClawReader(const Json::Value &json, int unionIdx):
m_json(json),
m_unionIdx(unionIdx) {
}
Error OrganicClawReader::field(const char *key, int8_t *val) {
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) {
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) {
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) {
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) {
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) {
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) {
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) {
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) {
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::field(const char *key, SerStr val) {
auto err = OxError(0);
const char *begin = nullptr, *end = nullptr;
const auto &jv = value(key);
if (targetValid()) {
if (jv.empty()) {
auto data = val.data();
if (data) {
data[0] = 0;
}
} else if (jv.isString()) {
jv.getString(&begin, &end);
auto strSize = end - begin;
auto data = val.data(static_cast<std::size_t>(strSize) + 1);
if (strSize >= val.cap()) {
err = OxError(1, "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;
}
[[nodiscard]] ValErr<std::size_t> OrganicClawReader::arrayLength(const char *key, bool) {
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) {
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) {
return OrganicClawReader(m_json[key], unionIdx);
}
bool OrganicClawReader::fieldPresent(const char *key) {
return !m_json[key].empty();
}
Json::Value &OrganicClawReader::value(const char *key) {
if (m_json.isArray()) {
return m_json[m_fieldIt];
} else {
return m_json[key];
}
}
bool OrganicClawReader::targetValid() noexcept {
return static_cast<int>(m_fieldIt) == m_unionIdx || m_unionIdx == -1;
}
}

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

@ -0,0 +1,204 @@
/*
* Copyright 2015 - 2020 gtalent2@gmail.com
* 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 <json/json.h>
#include <ox/model/optype.hpp>
#include <ox/model/types.hpp>
#include <ox/std/byteswap.hpp>
#include <ox/std/hashmap.hpp>
#include <ox/std/memops.hpp>
#include <ox/std/string.hpp>
#include <ox/std/vector.hpp>
namespace ox {
class OrganicClawReader {
private:
Json::Value m_json;
Json::ArrayIndex m_fieldIt = 0;
int m_unionIdx = -1;
public:
OrganicClawReader() = default;
OrganicClawReader(const uint8_t *buff, std::size_t buffSize);
OrganicClawReader(const char *json, std::size_t buffSize);
OrganicClawReader(const Json::Value &json, int unionIdx = -1);
[[nodiscard]] Error field(const char *key, int8_t *val);
[[nodiscard]] Error field(const char *key, int16_t *val);
[[nodiscard]] Error field(const char *key, int32_t *val);
[[nodiscard]] Error field(const char *key, int64_t *val);
[[nodiscard]] Error field(const char *key, uint8_t *val);
[[nodiscard]] Error field(const char *key, uint16_t *val);
[[nodiscard]] Error field(const char *key, uint32_t *val);
[[nodiscard]] Error field(const char *key, uint64_t *val);
[[nodiscard]] Error field(const char *key, bool *val);
// array handler
template<typename T>
[[nodiscard]] Error field(const char *key, T *val, std::size_t len);
template<typename T>
[[nodiscard]] Error field(const char *key, Vector<T> *val);
template<typename T>
[[nodiscard]] Error field(const char*, HashMap<String, T> *val);
template<typename T>
[[nodiscard]] Error field(const char *key, T *val);
template<typename U>
[[nodiscard]] Error field(const char *key, UnionView<U> val);
template<std::size_t L>
[[nodiscard]] Error field(const char *key, ox::BString<L> *val);
[[nodiscard]] Error field(const char *key, SerStr val);
/**
* Reads an array length from the current location in the buffer.
* @param pass indicates that the parsing should iterate past the array length
*/
[[nodiscard]] ValErr<std::size_t> arrayLength(const char *key, bool pass = true);
/**
* Reads an string length from the current location in the buffer.
*/
[[nodiscard]] std::size_t stringLength(const char *name);
template<typename T = void>
constexpr void setTypeInfo(const char* = T::TypeName, int = T::Fields) {
}
/**
* Returns a OrganicClawReader to parse a child object.
*/
[[nodiscard]] OrganicClawReader child(const char *key, int unionIdx = -1);
// compatibility stub
constexpr void nextField() noexcept {}
bool fieldPresent(const char *key);
static constexpr auto opType() {
return OpType::Read;
}
private:
Json::Value &value(const char *key);
bool targetValid() noexcept;
};
template<typename T>
Error OrganicClawReader::field(const char *key, T *val) {
auto err = OxError(0);
if (targetValid()) {
const auto &jv = value(key);
if (jv.empty() || jv.isObject()) {
auto reader = child(key);
return model(&reader, val);
} else {
err = OxError(1, "Type mismatch");
}
}
++m_fieldIt;
return err;
}
template<typename U>
Error OrganicClawReader::field(const char *key, UnionView<U> val) {
auto err = OxError(0);
if (targetValid()) {
const auto &jv = value(key);
if (jv.empty() || jv.isObject()) {
auto reader = child(key, val.idx());
err = model(&reader, val.get());
} else {
err = OxError(1, "Type mismatch");
}
}
++m_fieldIt;
return err;
}
template<std::size_t L>
Error OrganicClawReader::field(const char *name, ox::BString<L> *val) {
return field(name, SerStr(val->data(), val->cap()));
}
// array handler
template<typename T>
Error OrganicClawReader::field(const char *key, T *val, std::size_t valLen) {
const auto &srcVal = value(key);
auto srcSize = srcVal.size();
if (srcSize > valLen) {
return OxError(1);
}
OrganicClawReader r(srcVal);
for (decltype(srcSize) i = 0; i < srcSize; ++i) {
oxReturnError(r.field("", &val[i]));
}
return OxError(0);
}
template<typename T>
Error OrganicClawReader::field(const char *key, ox::Vector<T> *val) {
return field(key, val->data(), val->size());
}
template<typename T>
[[nodiscard]] Error OrganicClawReader::field(const char *key, HashMap<String, T> *val) {
const auto &srcVal = value(key);
auto keys = srcVal.getMemberNames();
auto srcSize = srcVal.size();
OrganicClawReader r(srcVal);
for (decltype(srcSize) i = 0; i < srcSize; ++i) {
auto k = keys[i].c_str();
oxReturnError(r.field(k, &val->at(k)));
}
return OxError(0);
}
template<typename T>
Error readOC(const char *json, std::size_t jsonSize, T *val) noexcept {
// OrganicClawReader constructor can throw, but readOC should return its errors.
try {
Json::Value doc;
Json::CharReaderBuilder parserBuilder;
auto parser = std::unique_ptr<Json::CharReader>(parserBuilder.newCharReader());
if (!parser->parse(json, json + jsonSize, &doc, nullptr)) {
return OxError(1, "Could not parse JSON");
}
OrganicClawReader reader(json, jsonSize);
return model(&reader, val);
} catch (Error err) {
return err;
} catch (...) {
return OxError(1, "Unkown Error");
}
}
template<typename T>
ValErr<std::unique_ptr<T>> readOC(const char *json) {
auto val = std::make_unique<T>();
oxReturnError(readOC(json, ox_strlen(json), val.get()));
return {std::move(val), OxError(0)};
}
}

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

@ -0,0 +1,13 @@
add_executable(
OcTest
tests.cpp
)
target_link_libraries(
OcTest
OxOrganicClaw
)
add_test("Test\\ OcTest\\ Writer" OcTest OrganicClawWriter)
add_test("Test\\ OcTest\\ Reader" OcTest OrganicClawReader)
add_test("Test\\ OcTest\\ OrganicClawDef" OcTest OrganicClawDef)

288
deps/ox/src/ox/oc/test/tests.cpp vendored Normal file
View File

@ -0,0 +1,288 @@
/*
* Copyright 2015 - 2020 gtalent2@gmail.com
*
* 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 <iostream>
#include <map>
#include <string>
#include <ox/model/model.hpp>
#include <ox/oc/oc.hpp>
#include <ox/std/std.hpp>
union TestUnion {
static constexpr auto TypeName = "TestUnion";
static constexpr auto Fields = 3;
bool Bool;
uint32_t Int = 5;
char String[32];
};
struct TestStructNest {
bool Bool = false;
uint32_t Int = 0;
ox::BString<32> String = "";
};
struct TestStruct {
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;
TestUnion Union;
char *CString = nullptr;
ox::BString<32> String = "";
uint32_t List[4] = {0, 0, 0, 0};
ox::HashMap<ox::String, int> Map;
TestStructNest EmptyStruct;
TestStructNest Struct;
~TestStruct() {
delete[] CString;
}
};
template<typename T>
ox::Error model(T *io, TestUnion *obj) {
io->template setTypeInfo<TestUnion>();
oxReturnError(io->field("Bool", &obj->Bool));
oxReturnError(io->field("Int", &obj->Int));
oxReturnError(io->field("String", ox::SerStr(obj->String)));
return OxError(0);
}
template<typename T>
ox::Error model(T *io, TestStructNest *obj) {
io->setTypeInfo("TestStructNest", 3);
oxReturnError(io->field("Bool", &obj->Bool));
oxReturnError(io->field("Int", &obj->Int));
oxReturnError(io->field("String", &obj->String));
return OxError(0);
}
template<typename T>
ox::Error model(T *io, TestStruct *obj) {
io->setTypeInfo("TestStruct", 17);
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("Union", ox::UnionView{&obj->Union, 1}));
oxReturnError(io->field("CString", ox::SerStr(&obj->CString)));
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);
}
std::map<std::string, 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.CString = new char[ox_strlen("c-string") + 1];
ox_strcpy(testIn.CString, "c-string");
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");
std::cout << oc.data() << '\n';
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(ox_strcmp(testIn.CString, testOut->CString) == 0, "CString 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);
}
},
{
"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");
auto type = ox::buildTypeDef(&testIn);
oxAssert(type.error, "Descriptor write failed");
ox::walkModel<ox::OrganicClawReader>(type.value, ox::bit_cast<uint8_t*>(oc.data()), oc.size(),
[](const ox::Vector<ox::FieldName>&, const ox::Vector<ox::TypeName>&, const ox::DescriptorField &f, ox::OrganicClawReader *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" << (i ? "true" : "false") << '\n';
break;
}
case ox::PrimitiveType::String: {
ox::Vector<char> v(rdr->stringLength(fieldName) + 1);
//std::cout << rdr->stringLength() << '\n';
oxAssert(rdr->field(fieldName, ox::SerStr(v.data(), v.size())), "Walking model failed.");
std::cout << fieldName << ":\t" << "string: " << v.data() << '\n';
break;
}
case ox::PrimitiveType::Struct:
break;
case ox::PrimitiveType::Union:
break;
}
return OxError(0);
}
);
delete type.value;
return OxError(0);
}
},
}
};
int main(int argc, const char **args) {
int retval = -1;
if (argc > 0) {
auto testName = args[1];
if (tests.find(testName) != tests.end()) {
retval = tests[testName]();
}
}
return retval;
}

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

@ -0,0 +1,118 @@
/*
* Copyright 2015 - 2018 gtalent2@gmail.com
*
* 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 {
OrganicClawWriter::OrganicClawWriter(int unionIdx): m_unionIdx(unionIdx) {
}
OrganicClawWriter::OrganicClawWriter(Json::Value json, int unionIdx):
m_json(json),
m_unionIdx(unionIdx) {
}
Error OrganicClawWriter::field(const char *key, int8_t *val) {
if (*val) {
value(key) = *val;
}
++m_fieldIt;
return OxError(0);
}
Error OrganicClawWriter::field(const char *key, int16_t *val) {
if (*val) {
value(key) = *val;
}
++m_fieldIt;
return OxError(0);
}
Error OrganicClawWriter::field(const char *key, int32_t *val) {
if (*val) {
value(key) = *val;
}
++m_fieldIt;
return OxError(0);
}
Error OrganicClawWriter::field(const char *key, int64_t *val) {
if (*val) {
value(key) = *val;
}
++m_fieldIt;
return OxError(0);
}
Error OrganicClawWriter::field(const char *key, uint8_t *val) {
if (*val) {
value(key) = *val;
}
++m_fieldIt;
return OxError(0);
}
Error OrganicClawWriter::field(const char *key, uint16_t *val) {
if (targetValid() && *val) {
value(key) = *val;
}
++m_fieldIt;
return OxError(0);
}
Error OrganicClawWriter::field(const char *key, uint32_t *val) {
if (targetValid() && *val) {
value(key) = *val;
}
++m_fieldIt;
return OxError(0);
}
Error OrganicClawWriter::field(const char *key, uint64_t *val) {
if (targetValid() && *val) {
value(key) = *val;
}
++m_fieldIt;
return OxError(0);
}
Error OrganicClawWriter::field(const char *key, bool *val) {
if (targetValid() && *val) {
value(key) = *val;
}
++m_fieldIt;
return OxError(0);
}
Error OrganicClawWriter::field(const char *key, ox::String val) {
if (targetValid() && val.len()) {
value(key) = val.c_str();
}
++m_fieldIt;
return OxError(0);
}
Error OrganicClawWriter::field(const char *key, SerStr val) {
if (targetValid() && val.len()) {
value(key) = val.c_str();
}
++m_fieldIt;
return OxError(0);
}
Json::Value &OrganicClawWriter::value(const char *key) {
if (m_json.isArray()) {
return m_json[m_fieldIt];
} else {
return m_json[key];
}
}
}

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

@ -0,0 +1,162 @@
/*
* Copyright 2015 - 2018 gtalent2@gmail.com
*
* 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 <json/json.h>
#include <ox/model/optype.hpp>
#include <ox/model/types.hpp>
#include <ox/std/hashmap.hpp>
#include <ox/std/string.hpp>
#include <ox/std/vector.hpp>
namespace ox {
class OrganicClawWriter {
template<typename T>
friend ValErr<Vector<char>> writeOC(T *val);
protected:
Json::Value m_json;
Json::ArrayIndex m_fieldIt = 0;
int m_unionIdx = -1;
public:
OrganicClawWriter(int unionIdx = -1);
OrganicClawWriter(Json::Value json, int unionIdx = -1);
[[nodiscard]] Error field(const char*, int8_t *val);
[[nodiscard]] Error field(const char*, int16_t *val);
[[nodiscard]] Error field(const char*, int32_t *val);
[[nodiscard]] Error field(const char*, int64_t *val);
[[nodiscard]] Error field(const char*, uint8_t *val);
[[nodiscard]] Error field(const char*, uint16_t *val);
[[nodiscard]] Error field(const char*, uint32_t *val);
[[nodiscard]] Error field(const char*, uint64_t *val);
[[nodiscard]] Error field(const char*, bool *val);
template<typename T>
[[nodiscard]] Error field(const char*, T *val, std::size_t len);
template<typename U>
[[nodiscard]] Error field(const char*, UnionView<U> val);
template<typename T>
[[nodiscard]] Error field(const char*, ox::Vector<T> *val);
template<typename T>
[[nodiscard]] Error field(const char*, HashMap<String, T> *val);
template<std::size_t L>
[[nodiscard]] Error field(const char*, ox::BString<L> *val);
[[nodiscard]] Error field(const char*, ox::String val);
[[nodiscard]] Error field(const char*, SerStr val);
template<typename T>
[[nodiscard]] Error field(const char*, T *val);
template<typename T = void>
constexpr void setTypeInfo(const char* = T::TypeName, int = T::Fields) {
}
static constexpr auto opType() {
return OpType::Write;
}
private:
constexpr bool targetValid() noexcept {
return static_cast<int>(m_fieldIt) == m_unionIdx || m_unionIdx == -1;
}
Json::Value &value(const char *key);
};
template<typename T>
Error OrganicClawWriter::field(const char *key, T *val, std::size_t len) {
if (targetValid()) {
OrganicClawWriter w((Json::Value(Json::arrayValue)));
for (std::size_t i = 0; i < len; ++i) {
oxReturnError(w.field("", &val[i]));
}
value(key) = w.m_json;
}
++m_fieldIt;
return OxError(0);
}
template<std::size_t L>
Error OrganicClawWriter::field(const char *key, ox::BString<L> *val) {
return field(key, SerStr(val->data(), val->cap()));
}
template<typename T>
Error OrganicClawWriter::field(const char *key, T *val) {
if (val && targetValid()) {
OrganicClawWriter w;
oxReturnError(model(&w, val));
if (!w.m_json.isNull()) {
value(key) = w.m_json;
}
}
++m_fieldIt;
return OxError(0);
}
template<typename U>
Error OrganicClawWriter::field(const char *key, UnionView<U> val) {
if (targetValid()) {
OrganicClawWriter w(val.idx());
oxReturnError(model(&w, val.get()));
if (!w.m_json.isNull()) {
value(key) = w.m_json;
}
}
++m_fieldIt;
return OxError(0);
}
template<typename T>
Error OrganicClawWriter::field(const char *key, ox::Vector<T> *val) {
return field(key, val->data(), val->size());
}
template<typename T>
[[nodiscard]] Error OrganicClawWriter::field(const char *key, ox::HashMap<String, T> *val) {
if (targetValid()) {
auto &keys = val->keys();
OrganicClawWriter w;
for (std::size_t i = 0; i < keys.size(); ++i) {
auto k = keys[i].c_str();
oxReturnError(w.field(k, &val->at(k)));
}
value(key) = w.m_json;
}
++m_fieldIt;
return OxError(0);
}
template<typename T>
ValErr<Vector<char>> writeOC(T *val) {
OrganicClawWriter writer;
oxReturnError(model(&writer, val));
Json::StreamWriterBuilder jsonBuilder;
auto str = Json::writeString(jsonBuilder, writer.m_json);
Vector<char> buff(str.size() + 1);
memcpy(buff.data(), str.c_str(), str.size() + 1);
return buff;
}
}

View File

@ -3,6 +3,7 @@ add_library(
assert.cpp
buildinfo.cpp
byteswap.cpp
heapmgr.cpp
memops.cpp
new.cpp
random.cpp
@ -13,6 +14,8 @@ add_library(
trace.cpp
)
target_compile_options(OxStd PRIVATE -Wsign-conversion)
if(NOT OX_BARE_METAL)
set_property(
TARGET
@ -45,17 +48,19 @@ install(
error.hpp
hardware.hpp
hashmap.hpp
heapmgr.hpp
math.hpp
memops.hpp
new.hpp
random.hpp
std.hpp
stddef.hpp
stacktrace.hpp
string.hpp
strongint.hpp
strops.hpp
std.hpp
stddef.hpp
trace.hpp
typeinfo.hpp
types.hpp
typetraits.hpp
units.hpp

View File

@ -32,21 +32,38 @@ void assertFunc<bool>([[maybe_unused]]const char *file, [[maybe_unused]]int line
}
template<>
void assertFunc<Error>(const char *file, int line, Error err, const char *msg) {
void assertFunc<Error>(const char *file, int line, Error err, const char *assertMsg) {
if (err) {
panic(file, line, msg, err);
#if defined(OX_USE_STDLIB)
std::cerr << "\033[31;1;1mASSERT FAILURE:\033[0m (" << file << ':' << line << "): " << assertMsg << '\n';
if (err.msg) {
std::cerr << "\tError Message:\t" << err.msg << '\n';
}
std::cerr << "\tError Code:\t" << err << '\n';
if (err.file != nullptr) {
std::cerr << "\tError Location:\t" << reinterpret_cast<const char*>(err.file) << ':' << err.line << '\n';
}
printStackTrace(2);
oxTrace("panic").del("") << "Panic: " << assertMsg << " (" << file << ":" << line << ")";
std::abort();
#else
panic(file, line, assertMsg);
#endif
}
}
#if defined(OX_USE_STDLIB)
void panic(const char *file, int line, const char *msg, Error err) {
std::cerr << "\033[31;1;1mPANIC:\033[0m (" << file << ':' << line << "): " << msg << '\n';
void panic(const char *file, int line, const char *panicMsg, Error err) {
std::cerr << "\033[31;1;1mPANIC:\033[0m (" << file << ':' << line << "): " << panicMsg << '\n';
if (err.msg) {
std::cerr << "\tError Message:\t" << err.msg << '\n';
}
std::cerr << "\tError Code:\t" << err << '\n';
if (err.file != nullptr) {
std::cerr << "\tError Location:\t" << reinterpret_cast<const char*>(err.file) << ':' << err.line << '\n';
}
printStackTrace(2);
oxTrace("assert").del("") << "Failed assert: " << msg << " (" << file << ":" << line << ")";
oxTrace("panic").del("") << "Panic: " << panicMsg << " (" << file << ":" << line << ")";
std::abort();
}
#endif

View File

@ -24,7 +24,7 @@ typename enable_if<sizeof(To) == sizeof(From), To>::type bit_cast(From src) noex
template<typename T>
[[nodiscard]] constexpr T rotl(T i, int shift) noexcept {
constexpr auto bits = sizeof(i) * 8;
return (i << shift) | (i >> (bits - shift));
return (i << static_cast<T>(shift)) | (i >> (bits - static_cast<T>(shift)));
}
template<typename T>

View File

@ -8,37 +8,44 @@
#pragma once
#include "defines.hpp"
#include "strongint.hpp"
#include "typetraits.hpp"
#include "utility.hpp"
#ifdef DEBUG
#define OxError(x) ox::_error(__FILE__, __LINE__, static_cast<ox::Error>(x))
#else
#define OxError(x) static_cast<ox::Error>(x)
#endif
#define OxError(...) ox::_error(__FILE__, __LINE__, __VA_ARGS__)
namespace ox {
struct BaseError {
const char *msg = nullptr;
const char *file = "";
uint16_t line = 0;
BaseError() = default;
constexpr BaseError(const BaseError &o) noexcept {
msg = o.msg;
file = o.file;
line = o.line;
}
constexpr BaseError operator=(const BaseError &o) noexcept {
msg = o.msg;
file = o.file;
line = o.line;
return *this;
}
};
using Error = Integer<uint64_t, BaseError>;
static constexpr Error _error(const char *file, uint32_t line, Error errCode) {
Error err = errCode;
err.file = file;
err.line = line;
static constexpr Error _error(const char *file, uint32_t line, uint64_t errCode, const char *msg = nullptr) {
Error err = static_cast<ox::Error>(errCode);
err.file = file;
err.line = line;
err.msg = msg;
return err;
}
@ -83,7 +90,7 @@ namespace error {
}
template<typename T>
[[nodiscard]] constexpr ox::Error toError(ox::ValErr<T> ve) noexcept {
[[nodiscard]] constexpr ox::Error toError(const ox::ValErr<T> &ve) noexcept {
return ve.error;
}

View File

@ -16,6 +16,9 @@ namespace ox {
template<typename K, typename T>
class HashMap {
using key_t = K;
using value_t = T;
private:
struct Pair {
K key = {};
@ -31,6 +34,8 @@ class HashMap {
~HashMap();
bool operator==(const HashMap &other) const;
HashMap &operator=(HashMap &other);
/**
@ -47,6 +52,8 @@ class HashMap {
std::size_t size() const noexcept;
const Vector<K> &keys() const noexcept;
private:
void expand();
@ -78,6 +85,20 @@ HashMap<K, T>::~HashMap() {
}
}
template<typename K, typename T>
bool HashMap<K, T>::operator==(const HashMap &other) const {
if (m_keys != other.m_keys) {
return false;
}
for (int i = 0; i < m_keys.size(); i++) {
auto &k = m_keys[i];
if (at(k) != other.at(k)) {
return false;
}
}
return true;
}
template<typename K, typename T>
HashMap<K, T> &HashMap<K, T>::operator=(HashMap<K, T> &other) {
this->~HashMap<K, T>();
@ -115,6 +136,11 @@ std::size_t HashMap<K, T>::size() const noexcept {
return m_keys.size();
}
template<typename K, typename T>
const Vector<K> &HashMap<K, T>::keys() const noexcept {
return m_keys;
}
template<typename K, typename T>
void HashMap<K, T>::expand() {
Vector<Pair*> r;

View File

@ -1,27 +1,16 @@
/*
* Copyright 2016 - 2019 gtalent2@gmail.com
* Copyright 2016 - 2020 gtalent2@gmail.com
*
* 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 "addresses.hpp"
#include "assert.hpp"
#include "bit.hpp"
#include "heapmgr.hpp"
#include <ox/std/assert.hpp>
#include <ox/std/bit.hpp>
// this warning is too dumb to realize that it can actually confirm the hard
// coded address aligns with the requirement of HeapSegment, so it must be
// suppressed
#pragma GCC diagnostic ignored "-Wcast-align"
#define HEAP_BEGIN reinterpret_cast<HeapSegment*>(MEM_WRAM_BEGIN)
// set size to half of WRAM
#define HEAP_SIZE ((MEM_WRAM_END - MEM_WRAM_BEGIN) / 2)
#define HEAP_END reinterpret_cast<HeapSegment*>(MEM_WRAM_BEGIN + HEAP_SIZE)
namespace nostalgia::core {
namespace ox::heapmgr {
static struct HeapSegment *volatile g_heapBegin = nullptr;
static struct HeapSegment *volatile g_heapEnd = nullptr;
@ -36,28 +25,23 @@ static constexpr std::size_t alignedSize(T = {}) {
return alignedSize(sizeof(T));
}
struct HeapSegment {
std::size_t size;
uint8_t inUse;
void HeapSegment::init(std::size_t maxSize = ox::bit_cast<std::size_t>(g_heapEnd)) {
this->size = maxSize - ox::bit_cast<std::size_t>(this);
this->inUse = false;
}
void init(std::size_t maxSize = ox::bit_cast<std::size_t>(g_heapEnd)) {
this->size = maxSize - ox::bit_cast<std::size_t>(this);
this->inUse = false;
}
template<typename T>
T *HeapSegment::data() {
return ox::bit_cast<T*>(ox::bit_cast<uint8_t*>(this) + alignedSize(this));
}
template<typename T>
T *data() {
return ox::bit_cast<T*>(ox::bit_cast<uint8_t*>(this) + alignedSize(this));
}
template<typename T>
T *HeapSegment::end() {
const auto size = alignedSize(this) + alignedSize(this->size);
auto e = ox::bit_cast<uintptr_t>(ox::bit_cast<uint8_t*>(this) + size);
return ox::bit_cast<T*>(e);
}
template<typename T = uint8_t>
T *end() {
const auto size = alignedSize(this) + alignedSize(this->size);
auto e = ox::bit_cast<uintptr_t>(ox::bit_cast<uint8_t*>(this) + size);
return ox::bit_cast<T*>(e);
}
};
void initHeap(char *heapBegin, char *heapEnd) {
g_heapBegin = ox::bit_cast<HeapSegment*>(heapBegin);
@ -67,10 +51,6 @@ void initHeap(char *heapBegin, char *heapEnd) {
heapIdx->inUse = false;
}
void initHeap() {
initHeap(ox::bit_cast<char*>(HEAP_BEGIN), ox::bit_cast<char*>(HEAP_END));
}
struct SegmentPair {
HeapSegment *anteSegment = nullptr;
HeapSegment *segment = nullptr;
@ -78,7 +58,7 @@ struct SegmentPair {
static SegmentPair findSegmentOf(void *ptr) {
HeapSegment *prev = nullptr;
for (auto seg = HEAP_BEGIN; seg < HEAP_END;) {
for (auto seg = g_heapBegin; seg < g_heapEnd;) {
if (seg->data<void>() == ptr) {
return {prev, seg};
}
@ -126,38 +106,38 @@ void free(void *ptr) {
#ifndef OX_USE_STDLIB
using namespace nostalgia;
using namespace ox;
void *operator new(std::size_t allocSize) {
return core::malloc(allocSize);
return heapmgr::malloc(allocSize);
}
void *operator new[](std::size_t allocSize) {
return core::malloc(allocSize);
return heapmgr::malloc(allocSize);
}
void operator delete(void *ptr) {
core::free(ptr);
heapmgr::free(ptr);
}
void operator delete[](void *ptr) {
core::free(ptr);
heapmgr::free(ptr);
}
void operator delete(void *ptr, unsigned) {
core::free(ptr);
heapmgr::free(ptr);
}
void operator delete[](void *ptr, unsigned) {
core::free(ptr);
heapmgr::free(ptr);
}
void operator delete(void *ptr, unsigned long int) {
core::free(ptr);
heapmgr::free(ptr);
}
void operator delete[](void *ptr, unsigned long int) {
core::free(ptr);
heapmgr::free(ptr);
}
#endif

33
deps/ox/src/ox/std/heapmgr.hpp vendored Normal file
View File

@ -0,0 +1,33 @@
/*
* Copyright 2016 - 2020 gtalent2@gmail.com
*
* 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 "types.hpp"
namespace ox::heapmgr {
struct HeapSegment {
std::size_t size;
uint8_t inUse;
void init(std::size_t maxSize);
template<typename T>
T *data();
template<typename T = uint8_t>
T *end();
};
void initHeap(char *heapBegin, char *heapEnd);
[[nodiscard]] void *malloc(std::size_t allocSize);
void free(void *ptr);
}

View File

@ -12,18 +12,21 @@
#include "bit.hpp"
#include "bstring.hpp"
#include "byteswap.hpp"
#include "defines.hpp"
#include "error.hpp"
#include "hardware.hpp"
#include "hashmap.hpp"
#include "heapmgr.hpp"
#include "math.hpp"
#include "memops.hpp"
#include "new.hpp"
#include "random.hpp"
#include "stacktrace.hpp"
#include "stddef.hpp"
#include "string.hpp"
#include "strongint.hpp"
#include "strops.hpp"
#include "string.hpp"
#include "trace.hpp"
#include "typeinfo.hpp"
#include "types.hpp"
#include "typetraits.hpp"

View File

@ -12,22 +12,41 @@
namespace ox {
String::String() noexcept {
m_buff.push_back(0);
if (m_buff.size()) {
m_buff[0] = 0;
} else {
m_buff.push_back(0);
}
}
String::String(std::size_t cap) noexcept {
m_buff.resize(cap + 1);
m_buff[0] = 0;
}
String::String(const char *str) noexcept {
m_buff.push_back(0);
if (m_buff.size()) {
m_buff[0] = 0;
} else {
m_buff.push_back(0);
}
*this = str;
}
String::String(String &other) noexcept {
String::String(const char *str, std::size_t size) noexcept {
m_buff.resize(size + 1);
memcpy(m_buff.data(), str, size);
m_buff[size] = 0;
}
String::String(const String &other) noexcept {
m_buff = other.m_buff;
}
String::String(String &&other) noexcept {
m_buff = ox::move(other.m_buff);
}
const String &String::operator=(const char *str) noexcept {
std::size_t strLen = ox_strlen(str) + 1;
m_buff.resize(strLen + 1);
@ -51,6 +70,11 @@ const String &String::operator=(const String &src) noexcept {
return *this = src.c_str();
}
const String &String::operator=(const String &&src) noexcept {
m_buff = ox::move(src.m_buff);
return *this;
}
const String &String::operator+=(const char *str) noexcept {
std::size_t strLen = ox_strlen(str);
auto currentLen = len();

View File

@ -27,7 +27,11 @@ class String {
String(const char *str) noexcept;
String(String&) noexcept;
String(const char *str, std::size_t size) noexcept;
String(const String&) noexcept;
String(String&&) noexcept;
const String &operator=(const char *str) noexcept;
@ -37,6 +41,8 @@ class String {
const String &operator=(const String &src) noexcept;
const String &operator=(const String &&src) noexcept;
const String &operator+=(const char *str) noexcept;
const String &operator+=(char *str) noexcept;

View File

@ -12,13 +12,24 @@
namespace ox {
class BaseInteger {};
struct BaseInteger {
constexpr BaseInteger() = default;
constexpr BaseInteger(const BaseInteger&) {
}
constexpr BaseInteger operator=(const BaseInteger&) {
return *this;
}
};
/**
* Integer is a strongly typed integer wrapper used to create strongly typed
* integers.
*/
template<typename T, class Base = BaseInteger>
template<typename T, typename Base = BaseInteger>
class Integer: public Base {
private:
T m_i;
@ -115,7 +126,9 @@ constexpr Integer<T, Base>::Integer(const Integer<T, Base> &i) noexcept: Base(i)
template<typename T, class Base>
constexpr Integer<T, Base> Integer<T, Base>::operator=(Integer<T, Base> i) noexcept {
return Integer<T, Base>(m_i = i.m_i);
Base::operator=(i);
m_i = i.m_i;
return *this;
}
template<typename T, class Base>

View File

@ -12,6 +12,18 @@
#include "types.hpp"
#include "typetraits.hpp"
template<typename T1, typename T2>
constexpr char *ox_strcpy(T1 dest, T2 src) noexcept {
std::size_t i = 0;
while (src[i]) {
dest[i] = src[i];
++i;
}
// set null terminator
dest[i] = 0;
return dest;
}
template<typename T1, typename T2>
constexpr char *ox_strncpy(T1 dest, T2 src, std::size_t maxLen) noexcept {
std::size_t i = 0;
@ -54,7 +66,8 @@ template<typename T1, typename T2>
return retval;
}
[[nodiscard]] constexpr int ox_strncmp(const char *str1, const char *str2, const std::size_t maxLen) noexcept {
template<typename T1, typename T2>
[[nodiscard]] constexpr int ox_strncmp(T1 str1, T2 str2, const std::size_t maxLen) noexcept {
auto retval = 0;
std::size_t i = 0;
while (i < maxLen && (str1[i] || str2[i])) {

View File

@ -15,3 +15,4 @@ add_test("Test\\ BString" StdTest "BString")
add_test("Test\\ String" StdTest "String")
add_test("Test\\ Vector" StdTest "Vector")
add_test("Test\\ HashMap" StdTest "HashMap")
add_test("Test\\ HeapMgr" StdTest malloc)

View File

@ -1,18 +1,27 @@
/*
* Copyright 2015 - 2018 gtalent2@gmail.com
* Copyright 2015 - 2020 gtalent2@gmail.com
*
* 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 <iostream>
#include <map>
#include <functional>
#include <ox/std/std.hpp>
using namespace std;
map<string, function<int()>> tests = {
std::map<std::string, std::function<int()>> tests = {
{
"malloc",
[] {
ox::Vector<char> buff(ox::units::MB);
ox::heapmgr::initHeap(&buff.front(), &buff.back());
oxAssert(ox::heapmgr::malloc(5) != nullptr, "malloc is broken");
oxAssert(ox::heapmgr::malloc(5) != nullptr, "malloc is broken");
return 0;
}
},
{
"ABCDEFG != HIJKLMN",
[]() {

View File

@ -89,7 +89,7 @@ template<typename T>
constexpr bool is_class_v = ox::is_class<T>();
template<typename T>
constexpr bool is_signed = ox::integral_constant<bool, T(-1) < T(0)>::value;
constexpr bool is_signed_v = ox::integral_constant<bool, T(-1) < T(0)>::value;
// enable_if ///////////////////////////////////////////////////////////////////

View File

@ -27,13 +27,13 @@ class Vector {
explicit Vector(std::size_t size) noexcept;
Vector(Vector &other) noexcept;
Vector(const Vector &other) noexcept;
Vector(Vector &&other) noexcept;
~Vector() noexcept;
Vector &operator=(Vector &other) noexcept;
Vector &operator=(const Vector &other) noexcept;
Vector &operator=(Vector &&other) noexcept;
@ -97,7 +97,7 @@ Vector<T>::Vector(std::size_t size) noexcept {
}
template<typename T>
Vector<T>::Vector(Vector<T> &other) noexcept {
Vector<T>::Vector(const Vector<T> &other) noexcept {
m_size = other.m_size;
m_cap = other.m_cap;
m_items = reinterpret_cast<T*>(new AllocAlias<T>[m_cap]);
@ -128,7 +128,7 @@ Vector<T>::~Vector() noexcept {
}
template<typename T>
Vector<T> &Vector<T>::operator=(Vector<T> &other) noexcept {
Vector<T> &Vector<T>::operator=(const Vector<T> &other) noexcept {
this->~Vector<T>();
m_size = other.m_size;
m_cap = other.m_cap;
@ -245,7 +245,7 @@ void Vector<T>::push_back(const T &item) noexcept {
if (m_size == m_cap) {
expandCap(m_cap ? m_cap * 2 : 100);
}
m_items[m_size] = item;
new (&m_items[m_size]) T(item);
++m_size;
}

49
jenkins/gba/Jenkinsfile vendored Normal file
View File

@ -0,0 +1,49 @@
pipeline {
agent {
label 'gba'
}
environment {
DEVKITPRO = '/opt/devkitpro'
DEVKITARM = '/opt/devkitpro/devkitARM'
}
stages {
stage('Environment') {
steps {
sh 'conan profile update settings.compiler.libcxx=libstdc++11 default'
sh 'conan remote add -f bincrafters https://api.bintray.com/conan/bincrafters/public-conan'
sh 'make conan'
}
}
stage('Build Tools Debug') {
steps {
sh 'make purge configure-debug'
sh 'make install'
}
}
stage('Build GBA Debug') {
steps {
sh 'make configure-gba-debug'
sh 'make'
sh 'make pkg-gba'
}
}
stage('Build Tools Release') {
steps {
sh 'make purge configure-release'
sh 'make install'
}
}
stage('Build GBA Release') {
steps {
sh 'make configure-gba'
sh 'make'
sh 'make pkg-gba'
}
}
}
post {
always {
archiveArtifacts artifacts: 'nostalgia.gba', fingerprint: true
}
}
}

47
jenkins/linux/Jenkinsfile vendored Normal file
View File

@ -0,0 +1,47 @@
pipeline {
agent {
label 'linux-x86_64'
}
stages {
stage('Environment') {
steps {
sh 'conan profile update settings.compiler.libcxx=libstdc++11 default'
sh 'conan remote add -f bincrafters https://api.bintray.com/conan/bincrafters/public-conan'
sh 'make conan'
}
}
stage('Build Asan') {
steps {
sh 'make purge configure-asan'
sh 'make'
}
}
stage('Test Asan') {
steps {
sh 'make test'
}
}
stage('Build Debug') {
steps {
sh 'make purge configure-debug'
sh 'make'
}
}
stage('Test Debug') {
steps {
sh 'make test'
}
}
stage('Build Release') {
steps {
sh 'make purge configure-release'
sh 'make'
}
}
stage('Test Release') {
steps {
sh 'make test'
}
}
}
}

View File

@ -0,0 +1,108 @@
O1;net.drinkingtea.ox.DescriptorType;1;{
"fieldList" :
[
{
"fieldName" : "bpp",
"type" :
{
"fieldList" : [],
"length" : 1,
"primitiveType" : 1,
"typeName" : "B:int8_t"
}
},
{
"fieldName" : "rows",
"type" :
{
"fieldList" : [],
"length" : 4,
"primitiveType" : 1,
"typeName" : "B:int32_t"
}
},
{
"fieldName" : "columns",
"typeName" : "B:int32_t"
},
{
"fieldName" : "defaultPalette",
"type" :
{
"fieldList" :
[
{
"fieldName" : "type",
"typeName" : "B:int8_t"
},
{
"fieldName" : "data",
"type" :
{
"fieldList" :
[
{
"fieldName" : "path",
"type" :
{
"fieldList" : [],
"primitiveType" : 4,
"typeName" : "B:string"
}
},
{
"fieldName" : "constPath",
"typeName" : "B:string"
},
{
"fieldName" : "inode",
"type" :
{
"fieldList" : [],
"length" : 8,
"typeName" : "B:uint64_t"
}
}
],
"primitiveType" : 6,
"typeName" : "net.drinkingtea.ox.FileAddress.Data"
}
}
],
"primitiveType" : 5,
"typeName" : "net.drinkingtea.ox.FileAddress"
}
},
{
"fieldName" : "pal",
"type" :
{
"fieldList" :
[
{
"fieldName" : "colors",
"type" :
{
"fieldList" : [],
"length" : 2,
"typeName" : "B:uint16_t"
}
}
],
"primitiveType" : 5,
"typeName" : "net.drinkingtea.nostalgia.core.NostalgiaPalette"
}
},
{
"fieldName" : "tiles",
"type" :
{
"fieldList" : [],
"length" : 1,
"typeName" : "B:uint8_t"
}
}
],
"primitiveType" : 5,
"typeName" : "net.drinkingtea.nostalgia.core.NostalgiaGraphic"
}

View File

@ -0,0 +1,16 @@
O1;net.drinkingtea.ox.DescriptorType;1;{
"fieldList" :
[
{
"fieldName" : "colors",
"type" :
{
"fieldList" : [],
"length" : 2,
"typeName" : "B:uint16_t"
}
}
],
"primitiveType" : 5,
"typeName" : "net.drinkingtea.nostalgia.core.NostalgiaPalette"
}

Binary file not shown.

View File

@ -1 +1 @@
<03><><EFBFBD><EFBFBD>
M1;net.drinkingtea.nostalgia.core.NostalgiaPalette;1;<03><><EFBFBD><EFBFBD>

Binary file not shown.

Binary file not shown.

View File

@ -3,10 +3,10 @@
set -e
BIN=./dist/$(cat .current_build)/bin/
PATH=$BIN:${DEVKITARM}/bin/:$PATH
PATH=$BIN:${DEVKITPRO}/tools/bin/:${DEVKITARM}/bin/:$PATH
NOSTALGIA_BIN=build/gba-*/src/nostalgia/player/nostalgia.bin
NOSTALGIA_MEDIA=nostalgia_media.oxfs
NOSTALGIA_PROJECT=sample_project
NOSTALGIA_PROJECT=$1
NOSTALGIA_GBA=nostalgia.gba
MEDIA_HEADER=media_header.txt

View File

@ -1,5 +1,7 @@
#! /usr/bin/env bash
set -e
target=$1
buildType=$2
@ -42,6 +44,8 @@ cmake -S $project -B $buildDir -GNinja \
$toolchain
mkdir -p dist
echo ${buildConfig} > .current_build
if [[ $target != gba ]] && [[ $target != gba-debug ]]; then
echo ${buildConfig} > .current_build
fi
rm -f compile_commands.json
ln -s build/${buildConfig}/compile_commands.json

View File

@ -15,9 +15,12 @@ endif()
add_subdirectory(core)
add_subdirectory(common)
add_subdirectory(player)
add_subdirectory(world)
if(NOSTALGIA_BUILD_PLAYER)
add_subdirectory(player)
endif()
if(NOSTALGIA_BUILD_TYPE STREQUAL "Native")
add_subdirectory(tools)
if(NOSTALGIA_BUILD_STUDIO)

View File

@ -5,6 +5,8 @@ add_library(
media.cpp
)
target_compile_options(NostalgiaCore PUBLIC -Wsign-conversion)
target_link_libraries(
NostalgiaCore PUBLIC
OxFS

View File

@ -4,7 +4,6 @@ if(NOSTALGIA_BUILD_TYPE STREQUAL "GBA")
core.cpp
gfx.cpp
media.cpp
mem.cpp
panic.cpp
)
@ -28,8 +27,6 @@ endif()
if(NOSTALGIA_BUILD_TYPE STREQUAL "Native")
add_executable(NostalgiaCore-GBA_Test
tests.cpp
mem.cpp
)
target_link_libraries(NostalgiaCore-GBA_Test NostalgiaCore)
add_test("NostalgiaCore-GBA\\ Test\\ malloc" NostalgiaCore-GBA_Test malloc)
endif()

View File

@ -25,11 +25,16 @@ constexpr auto GBA_TILE_COLUMNS = 32;
constexpr auto GBA_TILE_ROWS = 32;
struct GbaPaletteTarget {
static constexpr auto TypeName = NostalgiaPalette::TypeName;
static constexpr auto Fields = NostalgiaPalette::Fields;
static constexpr auto TypeVersion = NostalgiaPalette::TypeVersion;
volatile uint16_t *palette = nullptr;
};
struct GbaTileMapTarget {
static constexpr auto Fields = 4;
static constexpr auto TypeName = NostalgiaGraphic::TypeName;
static constexpr auto Fields = NostalgiaGraphic::Fields;
static constexpr auto TypeVersion = NostalgiaGraphic::TypeVersion;
volatile uint32_t *bgCtl = nullptr;
ox::FileAddress defaultPalette;
GbaPaletteTarget pal;
@ -38,17 +43,17 @@ struct GbaTileMapTarget {
template<typename T>
ox::Error modelRead(T *io, GbaPaletteTarget *t) {
io->setTypeInfo("nostalgia::core::NostalgiaPalette", NostalgiaPalette::Fields);
oxReturnError(io->template field<Color16>("colors", [t](auto i, Color16 *c) {
io->template setTypeInfo<GbaPaletteTarget>();
auto colorHandler = [t](std::size_t i, Color16 *c) {
t->palette[i] = *c;
return OxError(0);
}));
return OxError(0);
};
return io->template field<Color16, decltype(colorHandler)>("colors", colorHandler);
}
template<typename T>
ox::Error modelRead(T *io, GbaTileMapTarget *t) {
io->setTypeInfo("nostalgia::core::NostalgiaGraphic", NostalgiaGraphic::Fields);
io->template setTypeInfo<GbaTileMapTarget>();
uint8_t bpp;
int dummy;
@ -66,7 +71,7 @@ ox::Error modelRead(T *io, GbaTileMapTarget *t) {
oxReturnError(io->field("defaultPalette", &t->defaultPalette));
oxReturnError(io->field("pal", &t->pal));
uint16_t intermediate = 0;
auto err = io->template field<uint8_t>("tileMap", [t, &intermediate](auto i, uint8_t *tile) {
auto handleTileMap = [t, &intermediate](std::size_t i, uint8_t *tile) {
if (i & 1) { // i is odd
intermediate |= static_cast<uint16_t>(*tile) << 8;
t->tileMap[i / 2] = intermediate;
@ -74,8 +79,8 @@ ox::Error modelRead(T *io, GbaTileMapTarget *t) {
intermediate = *tile & 0x00ff;
}
return OxError(0);
});
return OxError(err);
};
return io->template field<uint8_t, decltype(handleTileMap)>("tileMap", handleTileMap);
}
ox::Error initGfx(Context*) {

View File

@ -1,3 +1,10 @@
/*
* Copyright 2016 - 2020 gtalent2@gmail.com
*
* 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 <map>
#include <string>
@ -5,9 +12,7 @@
#include <ox/std/std.hpp>
#include <nostalgia/core/mem.hpp>
namespace nostalgia::core {
namespace ox::heapmgr {
[[nodiscard]] void *malloc(std::size_t allocSize);
@ -17,20 +22,7 @@ void initHeap(char *heapBegin, char *heapEnd);
}
using namespace nostalgia;
int testMalloc(std::string) {
std::vector<char> buff(ox::units::MB);
core::initHeap(&buff.front(), &buff.back());
oxAssert(core::malloc(5) != nullptr, "malloc is broken");
oxAssert(core::malloc(5) != nullptr, "malloc is broken");
return 0;
}
std::map<std::string, int(*)(std::string)> tests = {
{
{ "malloc", testMalloc },
}
};
int main(int argc, const char **args) {

View File

@ -142,9 +142,9 @@ static char charMap[128] = {
};
Color32 toColor32(Color16 nc) noexcept {
Color32 r = ((nc & 0b0000000000011111) >> 0) * 8;
Color32 g = ((nc & 0b0000001111100000) >> 5) * 8;
Color32 b = ((nc & 0b0111110000000000) >> 10) * 8;
Color32 r = static_cast<Color32>(((nc & 0b0000000000011111) >> 0) * 8);
Color32 g = static_cast<Color32>(((nc & 0b0000001111100000) >> 5) * 8);
Color32 b = static_cast<Color32>(((nc & 0b0111110000000000) >> 10) * 8);
Color32 a = 255;
return a | (b << 8) | (g << 16) | (r << 24);
}
@ -177,7 +177,7 @@ uint8_t blue32(Color16 c) noexcept {
void puts(Context *ctx, int column, int row, const char *str) {
for (int i = 0; str[i]; i++) {
setTile(ctx, 0, column + i, row, charMap[static_cast<int>(str[i])]);
setTile(ctx, 0, column + i, row, static_cast<uint8_t>(charMap[static_cast<int>(str[i])]));
}
}

View File

@ -28,12 +28,16 @@ using Color16 = uint16_t;
using Color32 = uint32_t;
struct NostalgiaPalette {
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.NostalgiaPalette";
static constexpr auto Fields = 1;
static constexpr auto TypeVersion = 1;
ox::Vector<Color16> colors;
};
struct NostalgiaGraphic {
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.NostalgiaGraphic";
static constexpr auto Fields = 6;
static constexpr auto TypeVersion = 1;
int8_t bpp = 0;
// rows and columns are really only used by TileSheetEditor
int rows = 1;
@ -45,14 +49,14 @@ struct NostalgiaGraphic {
template<typename T>
ox::Error model(T *io, NostalgiaPalette *pal) {
io->setTypeInfo("nostalgia::core::NostalgiaPalette", NostalgiaPalette::Fields);
io->template setTypeInfo<NostalgiaPalette>();
oxReturnError(io->field("colors", &pal->colors));
return OxError(0);
}
template<typename T>
ox::Error model(T *io, NostalgiaGraphic *ng) {
io->setTypeInfo("nostalgia::core::NostalgiaGraphic", NostalgiaGraphic::Fields);
io->template setTypeInfo<NostalgiaGraphic>();
oxReturnError(io->field("bpp", &ng->bpp));
oxReturnError(io->field("rows", &ng->rows));
oxReturnError(io->field("columns", &ng->columns));

View File

@ -9,6 +9,7 @@ find_package(sdl2 REQUIRED)
target_link_libraries(
NostalgiaCore-SDL PUBLIC
sdl2::sdl2
OxClaw
OxFS
OxStd
NostalgiaCore-Userspace

View File

@ -14,7 +14,7 @@
#include <SDL.h>
#include <ox/mc/read.hpp>
#include <ox/claw/read.hpp>
#include <nostalgia/core/gfx.hpp>
@ -25,26 +25,26 @@ using TileMap = std::array<std::array<int, 128>, 128>;
struct SdlImplData {
SDL_Window *window = nullptr;
SDL_Renderer *renderer = nullptr;
std::array<SDL_Texture*, 4> bgTextures;
std::array<TileMap, 4> bgTileMaps;
uint64_t prevFpsCheckTime = 0;
std::array<SDL_Texture*, 4> bgTextures{};
std::array<TileMap, 4> bgTileMaps{};
int64_t prevFpsCheckTime = 0;
uint64_t draws = 0;
};
[[nodiscard]] static ox::ValErr<ox::Vector<uint8_t>> readFile(Context *ctx, const ox::FileAddress &file) {
[[nodiscard]] static ox::ValErr<ox::Vector<char>> readFile(Context *ctx, const ox::FileAddress &file) {
auto [stat, err] = ctx->rom->stat(file);
oxReturnError(err);
ox::Vector<uint8_t> buff(stat.size);
ox::Vector<char> buff(stat.size);
oxReturnError(ctx->rom->read(file, buff.data(), buff.size()));
return buff;
}
template<typename T>
[[nodiscard]] ox::ValErr<T> readMC(Context *ctx, const ox::FileAddress &file) {
[[nodiscard]] ox::ValErr<T> readObj(Context *ctx, const ox::FileAddress &file) {
auto [buff, err] = readFile(ctx, file);
oxReturnError(err);
T t;
oxReturnError(ox::readMC(buff.data(), buff.size(), &t));
oxReturnError(ox::readClaw(buff.data(), buff.size(), &t));
return t;
}
@ -97,15 +97,15 @@ ox::Error loadTileSheet(Context *ctx,
ox::FileAddress tilesheetPath,
ox::FileAddress palettePath) {
auto id = ctx->implData<SdlImplData>();
auto [tilesheet, tserr] = readMC<NostalgiaGraphic>(ctx, tilesheetPath);
auto [tilesheet, tserr] = readObj<NostalgiaGraphic>(ctx, tilesheetPath);
oxReturnError(tserr);
NostalgiaPalette palette;
if (!palettePath) {
palettePath = tilesheet.defaultPalette;
}
oxReturnError(readMC<NostalgiaPalette>(ctx, palettePath).get(&palette));
oxReturnError(readObj<NostalgiaPalette>(ctx, palettePath).get(&palette));
const auto bytesPerTile = tilesheet.bpp == 8 ? 64 : 32;
const unsigned bytesPerTile = tilesheet.bpp == 8 ? 64 : 32;
const auto tiles = tilesheet.tiles.size() / bytesPerTile;
const int width = 8;
const int height = 8 * tiles;
@ -126,11 +126,12 @@ ox::Error loadTileSheet(Context *ctx,
SDL_FreeSurface(surface);
SDL_FreePalette(sdlPalette);
auto sectionIdx = static_cast<unsigned>(section);
if (tss == TileSheetSpace::Background) {
if (id->bgTextures[section]) {
SDL_DestroyTexture(id->bgTextures[section]);
if (id->bgTextures[sectionIdx]) {
SDL_DestroyTexture(id->bgTextures[sectionIdx]);
}
id->bgTextures[section] = texture;
id->bgTextures[sectionIdx] = texture;
}
return OxError(0);
@ -187,7 +188,10 @@ void draw(Context *ctx) {
void setTile(Context *ctx, int layer, int column, int row, uint8_t tile) {
auto id = ctx->implData<SdlImplData>();
id->bgTileMaps[layer][row][column] = tile;
auto z = static_cast<unsigned>(layer);
auto y = static_cast<unsigned>(row);
auto x = static_cast<unsigned>(column);
id->bgTileMaps[z][y][x] = tile;
}
}

View File

@ -11,6 +11,8 @@ add_library(
rsrc.qrc
)
target_compile_options(NostalgiaCore-Studio PRIVATE -Wsign-conversion)
target_link_libraries(
NostalgiaCore-Studio
Qt5::Core

View File

@ -23,7 +23,10 @@ Rectangle {
onClicked: {
if (mouse.button === Qt.RightButton) {
contextMenu.popup();
var tile = mouseArea.tileAt(mouseX, mouseY);
if (tile) {
contextMenu.popup();
}
} else {
contextMenu.dismiss();
}
@ -92,7 +95,9 @@ Rectangle {
text: "Insert Tile"
onTriggered: {
var tile = mouseArea.tileAt(contextMenu.x, contextMenu.y);
sheetData.insertTileCmd(tile.tileNumber);
if (tile) {
sheetData.insertTileCmd(tile.tileNumber);
}
}
}

View File

@ -22,7 +22,7 @@ namespace {
const auto r = static_cast<uint32_t>(c.red()) >> 3;
const auto g = static_cast<uint32_t>(c.green()) >> 3;
const auto b = static_cast<uint32_t>(c.blue()) >> 3;
const auto a = static_cast<uint32_t>(c.alpha()) > 128 ? 1 : 0;
const auto a = static_cast<uint32_t>(c.alpha() > 128 ? 1 : 0);
return (a << 15) | (r << 10) | (g << 5) | (b << 0);
}
@ -72,11 +72,11 @@ namespace {
QMap<QRgb, int> colors;
auto ng = std::make_unique<core::NostalgiaGraphic>();
ng->pal.colors.resize(countColors(src, Tiles));
ng->pal.colors.resize(static_cast<std::size_t>(countColors(src, Tiles)));
if (argBpp == 4) {
ng->tiles.resize(Pixels / 2);
ng->tiles.resize(static_cast<std::size_t>(Pixels / 2));
} else {
ng->tiles.resize(Pixels);
ng->tiles.resize(static_cast<std::size_t>(Pixels));
}
ng->bpp = argBpp;
ng->columns = src.width() / TileWidth;
@ -97,12 +97,12 @@ namespace {
// set pixel color
if (argBpp == 4) {
if (destI % 2) { // is odd number pixel
ng->tiles[destI / 2] |= colors[c] << 4;
ng->tiles[static_cast<std::size_t>(destI / 2)] |= colors[c] << 4;
} else {
ng->tiles[destI / 2] |= colors[c];
ng->tiles[static_cast<std::size_t>(destI / 2)] |= colors[c];
}
} else {
ng->tiles[destI] = colors[c];
ng->tiles[static_cast<std::size_t>(destI)] = static_cast<std::size_t>(colors[c]);
}
}
}
@ -110,7 +110,7 @@ namespace {
// store colors in palette with the corresponding color id
for (auto key : colors.keys()) {
auto colorId = colors[key];
auto colorId = static_cast<std::size_t>(colors[key]);
ng->pal.colors[colorId] = toGbaColor(key);
}

View File

@ -19,7 +19,7 @@ namespace nostalgia::core {
NewTilesheetWizardPage::NewTilesheetWizardPage(const studio::Context *ctx) {
m_ctx = ctx;
addLineEdit(tr("&Tile Sheet Name:"), QString(TileSheetName) + "*", "", [this](QString name) {
addLineEdit(tr("&Tile Sheet Name:"), QString(TileSheetName) + "*", "", [](QString) {
return 0;
});
m_palettePicker = addComboBox(tr("&Palette:"), QString(Palette) + "*", {""});
@ -33,8 +33,7 @@ NewTilesheetWizardPage::NewTilesheetWizardPage(const studio::Context *ctx) {
int NewTilesheetWizardPage::accept() {
const auto tilesheetName = field(TileSheetName).toString();
const auto palette = m_palettePicker->itemData(field(Palette).toInt()).toString();
const auto outPath = TileSheetDir + tilesheetName + FileExt_ng;
auto outPath = QString(TileSheetDir) + name + FileExt_ng;
const auto outPath = QString(TileSheetDir) + tilesheetName + FileExt_ng;
auto err = m_ctx->project->exists(outPath);
if (err) {
showValidationError(tr("A tile sheet with this name already exists."));

View File

@ -20,7 +20,7 @@ namespace nostalgia::core {
NewPaletteWizardPage::NewPaletteWizardPage(const studio::Context *ctx) {
m_ctx = ctx;
addLineEdit(tr("&Palette Name:"), QString(PaletteName) + "*", "", [this](QString) {
addLineEdit(tr("&Palette Name:"), QString(PaletteName) + "*", "", [](QString) {
return 0;
});
}

View File

@ -191,14 +191,14 @@ QString PaletteEditor::itemName() const {
}
void PaletteEditor::addColor(int idx, Color16 c) {
m_pal->colors.insert(idx, c);
m_pal->colors.insert(static_cast<std::size_t>(idx), c);
addTableRow(idx, c);
setUnsavedChanges(true);
}
void PaletteEditor::rmColor(int idx) {
rmTableRow(idx);
m_pal->colors.erase(idx);
m_pal->colors.erase(static_cast<std::size_t>(idx));
setUnsavedChanges(true);
}
@ -209,7 +209,7 @@ void PaletteEditor::addTableRow(int i, Color16 c) {
m_table->setItem(i, 1, mkCell(green16(c)));
m_table->setItem(i, 2, mkCell(blue16(c)));
m_table->setItem(i, 3, mkCell(alpha16(c)));
m_table->setItem(i, 4, mkCell(toQColor(m_pal->colors[i]).name(QColor::HexArgb), false));
m_table->setItem(i, 4, mkCell(toQColor(m_pal->colors[static_cast<std::size_t>(i)]).name(QColor::HexArgb), false));
connect(m_table, &QTableWidget::cellChanged, this, &PaletteEditor::cellChanged);
}
@ -225,12 +225,12 @@ void PaletteEditor::setTableRow(int idx, Color16 c) {
m_table->item(idx, 1)->setText(QString::number(green16(c)));
m_table->item(idx, 2)->setText(QString::number(blue16(c)));
m_table->item(idx, 3)->setText(QString::number(alpha16(c)));
m_table->item(idx, 4)->setText(toQColor(m_pal->colors[idx]).name(QColor::HexArgb));
m_table->item(idx, 4)->setText(toQColor(m_pal->colors[static_cast<std::size_t>(idx)]).name(QColor::HexArgb));
connect(m_table, &QTableWidget::cellChanged, this, &PaletteEditor::cellChanged);
}
void PaletteEditor::updateColor(int idx, Color16 c) {
m_pal->colors[idx] = c;
m_pal->colors[static_cast<std::size_t>(idx)] = c;
setTableRow(idx, c);
setUnsavedChanges(true);
}
@ -266,7 +266,7 @@ void PaletteEditor::colorSelected() {
}
void PaletteEditor::cellChanged(int row, int) {
auto oldColor = m_pal->colors[row];
auto oldColor = m_pal->colors[static_cast<std::size_t>(row)];
auto newColor = rowColor(row);
undoStack()->push(new UpdateColorCommand(this, row, oldColor, newColor));
}
@ -278,7 +278,7 @@ void PaletteEditor::addColorClicked() {
void PaletteEditor::rmColorClicked() {
auto row = m_table->currentRow();
undoStack()->push(new RemoveColorCommand(this, m_pal->colors[row], row));
undoStack()->push(new RemoveColorCommand(this, m_pal->colors[static_cast<std::size_t>(row)], row));
}
}

View File

@ -366,10 +366,10 @@ std::unique_ptr<NostalgiaGraphic> SheetData::toNostalgiaGraphic() const {
ng->bpp = highestColorIdx > 15 ? 8 : 4;
ng->columns = m_columns;
ng->rows = m_rows;
auto pixelCount = ng->rows * ng->columns * PixelsPerTile;
auto pixelCount = static_cast<std::size_t>(ng->rows * ng->columns * PixelsPerTile);
if (ng->bpp == 4) {
ng->tiles.resize(pixelCount / 2);
for (int i = 0; i < pixelCount; ++i) {
for (std::size_t i = 0; i < pixelCount && i < static_cast<std::size_t>(m_pixels.size()); ++i) {
if (i & 1) {
ng->tiles[i / 2] |= static_cast<uint8_t>(m_pixels[i]) << 4;
} else {

View File

@ -3,6 +3,8 @@ add_library(
media.cpp
)
target_compile_options(NostalgiaCore-Userspace PRIVATE -Wsign-conversion)
target_link_libraries(
NostalgiaCore-Userspace PUBLIC
OxFS

View File

@ -21,9 +21,9 @@ char *loadRom(const char *path) {
return nullptr;
}
const std::size_t size = file.tellg();
const auto size = file.tellg();
file.seekg(0, std::ios::beg);
auto buff = new char[size];
auto buff = new char[static_cast<std::size_t>(size)];
file.read(buff, size);
return buff;
}

View File

@ -19,7 +19,7 @@ target_link_libraries(
NostalgiaStudio PUBLIC
Qt5::Widgets
OxFS
OxMetalClaw
OxClaw
)
install(

View File

@ -56,8 +56,8 @@ void Project::writeBuff(QString path, uint8_t *buff, size_t buffLen) const {
emit fileUpdated(path);
}
std::vector<uint8_t> Project::loadBuff(QString path) const {
std::vector<uint8_t> buff(stat(path).size);
std::vector<char> Project::loadBuff(QString path) const {
std::vector<char> buff(stat(path).size);
const auto csPath = path.toUtf8();
oxThrowError(m_fs.read(csPath.data(), buff.data(), buff.size()));
return buff;

View File

@ -16,8 +16,11 @@
#include <ox/mc/mc.hpp>
#include <qnamespace.h>
#include <ox/claw/claw.hpp>
#include <ox/fs/filesystem/passthroughfs.hpp>
#include <ox/model/descwrite.hpp>
#include "nostalgiastudio_export.h"
#include "ox/fs/filesystem/passthroughfs.hpp"
namespace nostalgia::studio {
@ -71,7 +74,7 @@ class NOSTALGIASTUDIO_EXPORT Project: public QObject {
private:
void writeBuff(QString path, uint8_t *buff, size_t buffLen) const;
std::vector<uint8_t> loadBuff(QString path) const;
std::vector<char> loadBuff(QString path) const;
void procDir(QStringList &paths, QString path) const;
@ -91,12 +94,23 @@ class NOSTALGIASTUDIO_EXPORT Project: public QObject {
template<typename T>
void Project::writeObj(QString path, T *obj) const {
std::vector<uint8_t> buff(10 * ox::units::MB, 0);
// write MetalClaw
size_t mcSize = 0;
oxThrowError(ox::writeMC(buff.data(), buff.size(), obj, &mcSize));
auto [buff, err] = ox::writeClaw(obj, ox::ClawFormat::Metal);
oxThrowError(err);
// write to FS
writeBuff(path, buff.data(), mcSize);
writeBuff(path, ox::bit_cast<uint8_t*>(buff.data()), buff.size());
// write type descriptor
const auto type = ox::buildTypeDef(obj);
auto typeOut = ox::writeClaw(type.value, ox::ClawFormat::Organic);
oxThrowError(typeOut);
// replace garbage last character with new line
typeOut.value.back() = '\n';
// write to FS
QString descPath = "/.nostalgia/type_descriptors/";
const auto typePath = descPath + type.value->typeName.c_str();
mkdir(descPath);
writeBuff(typePath, ox::bit_cast<uint8_t*>(typeOut.value.data()), typeOut.value.size());
emit fileUpdated(path);
}
@ -104,7 +118,7 @@ template<typename T>
std::unique_ptr<T> Project::loadObj(QString path) const {
auto obj = std::make_unique<T>();
auto buff = loadBuff(path);
oxThrowError(ox::readMC<T>(buff.data(), buff.size(), obj.get()));
oxThrowError(ox::readClaw<T>(buff.data(), buff.size(), obj.get()));
return obj;
}

View File

@ -15,12 +15,26 @@
#include <QPushButton>
#include <QVBoxLayout>
#include "context.hpp"
#include "project.hpp"
#include "plugin.hpp"
#include "wizard.hpp"
namespace nostalgia::studio {
using std::function;
PathExistsValidator::PathExistsValidator(const Context *ctx, QString pathTemplate, bool shouldExist) {
m_ctx = ctx;
m_pathTemplate = pathTemplate;
m_shouldExist = shouldExist;
}
QValidator::State PathExistsValidator::validate(QString &input, int&) const {
auto path = m_pathTemplate.arg(input);
if (m_ctx->project->exists(path) == m_shouldExist) {
return QValidator::Acceptable;
}
return QValidator::Invalid;
}
WizardSelect::WizardSelect() {
m_listWidget = new QListWidget(this);
@ -203,7 +217,7 @@ QComboBox *WizardFormPage::addComboBox(QString displayName, QString fieldName, Q
return cb;
}
QLineEdit *WizardFormPage::addLineEdit(QString displayName, QString fieldName, QString defaultVal, function<int(QString)> validator) {
QLineEdit *WizardFormPage::addLineEdit(QString displayName, QString fieldName, QString defaultVal, std::function<int(QString)> validator) {
auto lbl = new QLabel(displayName, this);
auto le = new QLineEdit(this);
lbl->setBuddy(le);

View File

@ -18,6 +18,7 @@
#include <QLineEdit>
#include <QListWidget>
#include <QMap>
#include <QValidator>
#include <QVector>
#include <QWizard>
@ -25,6 +26,19 @@
namespace nostalgia::studio {
class PathExistsValidator: public QValidator {
private:
const class Context *m_ctx = nullptr;
QString m_pathTemplate;
bool m_shouldExist = true;
public:
PathExistsValidator(const class Context *ctx, QString pathTemplate, bool shouldExist);
QValidator::State validate(QString &input, int &pos) const override;
};
struct NOSTALGIASTUDIO_EXPORT WizardMaker {
QString name;
std::function<QVector<QWizardPage*>()> make;

View File

@ -29,7 +29,9 @@ OxFSFile::OxFSFile(PassThroughFS *fs, QString path, OxFSFile *parentItem) {
if (!stat.error) {
if (stat.value.fileType == FileType_Directory) {
fs->ls(m_path.toUtf8(), [&ls](const char *name, ox::InodeId_t) {
ls.push_back(name);
if (name[0] != '.') {
ls.push_back(name);
}
return OxError(0);
});
std::sort(ls.begin(), ls.end());
@ -143,7 +145,7 @@ QVariant OxFSModel::data(const QModelIndex &index, int role) const {
Qt::ItemFlags OxFSModel::flags(const QModelIndex &index) const {
if (!index.isValid()) {
return 0;
return {};
} else {
return QAbstractItemModel::flags(index);
}

View File

@ -18,7 +18,7 @@
static void writeFileBuff(const std::string &path, std::vector<char> &buff) {
try {
std::ofstream f(path, std::ios::binary);
f.write(buff.data(), buff.size());
f.write(buff.data(), static_cast<intptr_t>(buff.size()));
} catch (const std::fstream::failure&) {
throw OxError(2);
}

View File

@ -7,9 +7,9 @@ add_library(
target_link_libraries(
NostalgiaPack PUBLIC
OxClArgs
OxClaw
OxFS
OxStd
OxMetalClaw
NostalgiaCommon
NostalgiaCore
)

View File

@ -10,6 +10,8 @@
#include <string_view>
#include <vector>
#include <ox/claw/read.hpp>
#include "pack.hpp"
namespace nostalgia {
@ -20,6 +22,10 @@ namespace {
return str.size() >= ending.size() && str.substr(str.size() - ending.size()) == ending;
}
static_assert(endsWith("asdf", "df"));
static_assert(!endsWith("asdf", "awefawe"));
static_assert(!endsWith("asdf", "eu"));
/**
* Convert path references to inodes to save space
* @param buff buffer holding file
@ -31,7 +37,11 @@ namespace {
}
// stub for now
[[nodiscard]] ox::Error toMetalClaw(std::vector<uint8_t>*) {
[[nodiscard]] ox::Error toMetalClaw(std::vector<uint8_t> *buff) {
auto [mc, err] = ox::stripClawHeader(ox::bit_cast<char*>(buff->data()), buff->size());
oxReturnError(err);
buff->resize(mc.size());
ox_memcpy(buff->data(), mc.data(), mc.size());
return OxError(0);
}
@ -41,22 +51,23 @@ namespace {
// copy
oxTrace("pack::transformClaw") << "path:" << path.c_str();
return dest->ls(path.c_str(), [dest, path](const char *name, ox::InodeId_t) {
auto [stat, err] = dest->stat(path.c_str());
auto filePath = path + name;
auto [stat, err] = dest->stat(filePath.c_str());
oxReturnError(err);
if (stat.fileType == ox::FileType_Directory) {
const auto dir = path + name + '/';
oxReturnError(transformClaw(dest, dir));
} else {
// do transforms
if (endsWith(path, ".claw")) {
if (endsWith(name, ".ng") || endsWith(name, ".npal")) {
// load file
std::vector<uint8_t> buff(stat.size);
oxReturnError(dest->read(path.c_str(), buff.data(), buff.size()));
oxReturnError(dest->read(filePath.c_str(), buff.data(), buff.size()));
// do transformations
oxReturnError(pathToInode(&buff));
oxReturnError(toMetalClaw(&buff));
// write file to dest
oxReturnError(dest->write(path.c_str(), buff.data(), buff.size()));
oxReturnError(dest->write(filePath.c_str(), buff.data(), buff.size()));
}
}
return OxError(0);
@ -79,8 +90,11 @@ struct VerificationPair {
std::vector<VerificationPair> verficationPairs;
// copy
oxReturnError(src->ls(path.c_str(), [&verficationPairs, src, dest, path](std::string name, ox::InodeId_t) {
std::cout << "reading " << name << '\n';
auto currentFile = path + name;
if (currentFile == "/.nostalgia") {
return OxError(0);
}
std::cout << "reading " << name << '\n';
auto [stat, err] = src->stat((currentFile).c_str());
oxReturnError(err);
if (stat.fileType == ox::FileType_Directory) {

View File

@ -14,7 +14,7 @@ using namespace common;
using namespace core;
ox::Error Zone::init(Context *ctx, Bounds bnds, ox::FileAddress tileSheet, ox::FileAddress palette) {
const auto size = bnds.width * bnds.height;
const auto size = static_cast<std::size_t>(bnds.width * bnds.height);
m_tiles = new Tile[size];
m_bounds = bnds;
return core::loadTileSheet(ctx, core::TileSheetSpace::Background, 0, tileSheet, palette);
@ -37,7 +37,7 @@ void Zone::draw(Context *ctx) {
}
std::size_t Zone::size() {
return sizeof(Zone) + m_bounds.width * m_bounds.height * sizeof(Tile);
return sizeof(Zone) + static_cast<std::size_t>(m_bounds.width * m_bounds.height) * sizeof(Tile);
}
Tile *Zone::tile(int x, int y) {

View File

@ -66,7 +66,7 @@ struct Zone {
template<typename T>
ox::Error modelRead(T *io, Zone *obj) {
auto err = OxError(0);
io->setTypeInfo("nostalgia::world::Zone", Zone::Fields);
io->setTypeInfo("net.drinkingtea.nostalgia.world.Zone", Zone::Fields);
err |= io->field("bounds", &obj->m_bounds);
return err;
}
@ -74,7 +74,7 @@ ox::Error modelRead(T *io, Zone *obj) {
template<typename T>
ox::Error modelWrite(T *io, Zone *obj) {
auto err = OxError(0);
io->setTypeInfo("nostalgia::world::Zone", Zone::Fields);
io->setTypeInfo("net.drinkingtea.nostalgia.world.Zone", Zone::Fields);
err |= io->field("bounds", &obj->m_bounds);
return err;
}
@ -99,14 +99,14 @@ struct Region {
template<typename T>
ox::Error modelRead(T *io, Region*) {
auto err = OxError(0);
io->setTypeInfo("nostalgia::World::Region", Region::Fields);
io->setTypeInfo("net.drinkingtea.nostalgia.world.Region", Region::Fields);
return err;
}
template<typename T>
ox::Error modelWrite(T *io, Region*) {
auto err = OxError(0);
io->setTypeInfo("nostalgia::World::Region", Region::Fields);
io->setTypeInfo("net.drinkingtea.nostalgia.world.Region", Region::Fields);
return err;
}