Compare commits

..

1 Commits

Author SHA1 Message Date
c20aa2e0b9 Add puts function and call it for Hello, World! 2017-04-14 05:39:33 -05:00
274 changed files with 1021 additions and 20204 deletions

View File

@@ -1,12 +0,0 @@
{
"log_functions": [
{
"function": "oxTraceHook",
"ignore_frames": 3,
"file_var": "file",
"line_var": "line",
"channel_var": "ch",
"msg_var": "msg"
}
]
}

18
.gitignore vendored
View File

@@ -1,15 +1,5 @@
.current_build build/current
compile_commands.json build/gba
build build/*-release
conanbuild build/*-debug
dist
tags tags
nostalgia.gba
nostalgia.sav
nostalgia_media.oxfs
media_header.txt
studio_state.json
CMakeLists.txt.user
Session.vim
ROM.oxfs
graph_info.json

5
.liccor Normal file
View File

@@ -0,0 +1,5 @@
Copyright 2016-2017 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/.

View File

@@ -1,9 +0,0 @@
---
source:
- src
copyright_notice: |-
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/.

View File

@@ -1,44 +0,0 @@
{
"version": "0.2.1",
"defaults": {},
"configurations": [
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "nostalgia.exe (Install)",
"name": "nostalgia.exe (Install)",
"args": [
"${projectDir}/sample_project"
]
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "nostalgia.exe",
"name": "nostalgia.exe",
"args": [
"${projectDir}/sample_project"
]
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "nostalgia-studio.exe (Install)",
"name": "nostalgia-studio.exe (Install)",
"args": [
"-profile",
"${projectDir}/src/nostalgia/studio/nostalgia-studio-dev.json"
]
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "nostalgia-studio.exe",
"name": "nostalgia-studio.exe",
"args": [
"-profile",
"${projectDir}/src/nostalgia/studio/nostalgia-studio-dev.json"
]
}
]
}

View File

@@ -1,128 +1,35 @@
cmake_minimum_required(VERSION 3.12) cmake_minimum_required(VERSION 2.8.8)
project(nostalgia) project(wombat)
set(NOSTALGIA_BUILD_TYPE "Native" CACHE STRING "The type of build to produce(Native/GBA)") set(WOMBAT_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") list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules)
set(NOSTALGIA_BUILD_STUDIO OFF)
set(OX_BARE_METAL ON)
set(OX_USE_STDLIB OFF)
else()
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${NOSTALGIA_QT_PATH})
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
endif()
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules)
include(GenerateExportHeader)
include(address_sanitizer) include(address_sanitizer)
set(NOSTALGIA_CONAN_PATHS ${CMAKE_SOURCE_DIR}/conanbuild/conan_paths.cmake) if (WOMBAT_BUILD_TYPE STREQUAL "GBA")
if(NOT NOSTALGIA_BUILD_TYPE STREQUAL "GBA" AND EXISTS ${NOSTALGIA_CONAN_PATHS})
include(${NOSTALGIA_CONAN_PATHS})
endif()
set(CMAKE_INSTALL_PREFIX "${CMAKE_SOURCE_DIR}/dist/${NOSTALGIA_BUILD_CONFIG}")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
if(NOT OX_BARE_METAL)
add_definitions(-DOX_USE_STDLIB)
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}")
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
add_definitions(-DDEBUG)
else()
add_definitions(-DNDEBUG)
endif()
if(NOT MSVC)
# forces colored output when using ninja
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color")
# enable warnings
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wcast-align")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wdouble-promotion")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wformat=2")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wmissing-field-initializers")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wnon-virtual-dtor")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wnull-dereference")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wold-style-cast")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Woverloaded-virtual")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wpedantic")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsign-compare")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsign-conversion")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wunused")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wunused-variable")
if(NOSTALGIA_BUILD_TYPE STREQUAL "GBA")
include(GBA) include(GBA)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostdlib")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostdinc++")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-unwind-tables")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-strict-aliasing")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mthumb-interwork")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mthumb")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=arm7tdmi")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=arm7tdmi")
endif() endif()
add_definitions(
-std=c++11
-Wall
-Wsign-compare
-nostdlib
-fno-exceptions
-fno-rtti
#-g
#-fcolor-diagnostics
#--analyze
#-Os # GCC size optimization flag
)
if (CMAKE_BUILD_TYPE STREQUAL "Release") if (CMAKE_BUILD_TYPE STREQUAL "Release")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror") add_definitions(
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3") -Werror
endif() )
endif()
if(NOSTALGIA_IDE_BUILD)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
endif()
if(APPLE)
set(CMAKE_MACOSX_RPATH OFF)
set(CMAKE_INSTALL_NAME_DIR "@executable_path/../Library/nostalgia")
set(NOSTALGIA_DIST_BIN nostalgia-studio.app/Contents/MacOS)
set(NOSTALGIA_DIST_LIB nostalgia-studio.app/Contents/Library)
set(NOSTALGIA_DIST_PLUGIN nostalgia-studio.app/Contents/Plugins)
set(NOSTALGIA_DIST_RESOURCES nostalgia-studio.app/Contents/Resources)
set(NOSTALGIA_DIST_MAC_APP_CONTENTS nostalgia-studio.app/Contents)
else()
set(CMAKE_INSTALL_RPATH "$ORIGIN" "$ORIGIN/../lib/nostalgia" "$ORIGIN/../")
if(NOT ${NOSTALGIA_QT_PATH} STREQUAL "")
set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_RPATH} "${NOSTALGIA_QT_PATH}/lib")
endif()
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
set(NOSTALGIA_DIST_BIN bin)
set(NOSTALGIA_DIST_LIB lib)
set(NOSTALGIA_DIST_PLUGIN lib/nostalgia/plugins)
set(NOSTALGIA_DIST_RESOURCES share)
endif() endif()
enable_testing() 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(src) add_subdirectory(src)

View File

@@ -1,61 +1,33 @@
FROM fedora:30 FROM wombatant/devenv
RUN dnf update -y ENV DEVKITPRO /opt/devkitPro
ENV DEVKITARM ${DEVKITPRO}/devkitARM
############################################################################### ###############################################################################
# Install gosu # Install Ox
RUN dnf install -y curl gnupg RUN git clone -b release-0.1 https://github.com/wombatant/ox.git /usr/local/src/ox && \
RUN gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 cd /usr/local/src/ox && \
RUN curl -o /usr/local/bin/gosu -SL "https://github.com/tianon/gosu/releases/download/1.10/gosu-amd64" && \ # setup build dirs
curl -o /usr/local/bin/gosu.asc -SL "https://github.com/tianon/gosu/releases/download/1.10/gosu-amd64.asc" && \ mkdir -p \
gpg --verify /usr/local/bin/gosu.asc && \ /usr/local/src/ox/build/release \
rm /usr/local/bin/gosu.asc && \ /usr/local/src/ox/build/windows \
chmod +x /usr/local/bin/gosu /usr/local/src/ox/build/gba; \
# install Ox for native environment
############################################################################### cd /usr/local/src/ox/build/release && \
# Install dev tools cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ../../ && \
make -j install; \
RUN dnf install -y clang # install Ox for GBA
RUN dnf install -y llvm cd /usr/local/src/ox/build/gba && \
RUN dnf install -y libasan cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
RUN dnf install -y mingw64-gcc-c++ -DCMAKE_TOOLCHAIN_FILE=cmake/Modules/GBA.cmake \
RUN dnf install -y cmake -DCMAKE_INSTALL_PREFIX=/opt/devkitPro/devkitARM \
RUN dnf install -y make -DOX_USE_STDLIB=OFF ../../ && \
RUN dnf install -y git make -j install; \
RUN dnf install -y vim # install Ox for Windows
RUN dnf install -y sudo cd /usr/local/src/ox/build/windows && \
RUN dnf install -y fuse-devel cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
RUN dnf install -y qt5-devel -DCMAKE_TOOLCHAIN_FILE=cmake/Modules/Mingw.cmake \
RUN dnf install -y findutils -DCMAKE_INSTALL_PREFIX=/usr/x86_64-w64-mingw32 \
RUN dnf install -y ninja-build -DOX_BUILD_EXEC=OFF ../../ && \
RUN dnf install -y libcxx-devel libcxxabi-devel make -j install
###############################################################################
# Install devkitARM
#RUN dnf install -y lbzip2
#RUN curl -o /tmp/devkitArm.tar.bz2 -SL https://phoenixnap.dl.sourceforge.net/project/devkitpro/devkitARM/devkitARM_r47/devkitARM_r47-x86_64-linux.tar.bz2
#WORKDIR /opt
#RUN tar xf /tmp/devkitArm.tar.bz2
#ENV DEVKITARM /opt/devkitARM
###############################################################################
# Setup sudoers
ADD devenv/sudoers /etc/sudoers
###############################################################################
# Setup working directory
RUN mkdir /usr/src/project
WORKDIR /usr/src/project
###############################################################################
# Setup entrypoint
ADD devenv/entrypoint.sh /
ENTRYPOINT ["/entrypoint.sh"]
ENV CC clang
ENV CXX clang++

134
Makefile
View File

@@ -2,113 +2,83 @@ OS=$(shell uname | tr [:upper:] [:lower:])
HOST_ENV=${OS}-$(shell uname -m) HOST_ENV=${OS}-$(shell uname -m)
DEVENV=devenv$(shell pwd | sed 's/\//-/g') DEVENV=devenv$(shell pwd | sed 's/\//-/g')
DEVENV_IMAGE=nostalgia-devenv DEVENV_IMAGE=nostalgia-devenv
CURRENT_BUILD=$(file < .current_build) ifneq ($(shell which docker),)
ifneq ($(shell which docker 2> /dev/null),)
ifeq ($(shell docker inspect --format="{{.State.Status}}" ${DEVENV} 2>&1),running) ifeq ($(shell docker inspect --format="{{.State.Status}}" ${DEVENV} 2>&1),running)
ENV_RUN=docker exec -i -t --user $(shell id -u ${USER}) ${DEVENV} ENV_RUN=docker exec -i -t --user $(shell id -u ${USER}) ${DEVENV}
endif endif
endif endif
ifeq ($(OS),windows) make:
RM_RF=Remove-Item -ErrorAction Ignore -Path -Recurse ${ENV_RUN} make -j -C build HOST_ENV=${HOST_ENV}
else preinstall:
RM_RF=rm -rf ${ENV_RUN} make -j -C build ARGS="preinstall" HOST_ENV=${HOST_ENV}
endif
ifeq ($(OS),darwin)
NOSTALGIA_STUDIO=./dist/${CURRENT_BUILD}/nostalgia-studio.app/Contents/MacOS/nostalgia-studio
NOSTALGIA_STUDIO_PROFILE=dist/${CURRENT_BUILD}/nostalgia-studio.app/Contents/Resources/nostalgia-studio.json
MGBA=/Applications/mGBA.app/Contents/MacOS/mGBA
else
NOSTALGIA_STUDIO=./dist/${CURRENT_BUILD}/bin/nostalgia-studio
NOSTALGIA_STUDIO_PROFILE=dist/${CURRENT_BUILD}/share/nostalgia-studio.json
MGBA=mgba-qt
endif
.PHONY: build
build:
$(foreach file, $(wildcard build/*), cmake --build $(file) --target;)
.PHONY: pkg-gba
pkg-gba:
$(foreach file, $(wildcard build/*), cmake --build $(file) --target install;)
${ENV_RUN} ./scripts/gba-pkg sample_project
.PHONY: install
install: install:
$(foreach file, $(wildcard build/*), cmake --build $(file) --target install;) ${ENV_RUN} make -j -C build ARGS="install" HOST_ENV=${HOST_ENV}
.PHONY: clean
clean: clean:
$(foreach file, $(wildcard build/*), cmake --build $(file) --target clean;) ${ENV_RUN} make -j -C build ARGS="clean" HOST_ENV=${HOST_ENV}
.PHONY: purge
purge: purge:
${ENV_RUN} ${RM_RF} build .current_build dist ${ENV_RUN} rm -rf $$(find build -mindepth 1 -maxdepth 1 -type d)
.PHONY: test test:
test: build ${ENV_RUN} make -j -C build ARGS="test" HOST_ENV=${HOST_ENV}
$(foreach file, $(wildcard build/*), cmake --build $(file) --target test;)
.PHONY: run run: make
run: install ./build/current/src/player/nostalgia -debug
${ENV_RUN} ./dist/${CURRENT_BUILD}/bin/nostalgia sample_project gba-run: make
.PHONY: run-studio ${ENV_RUN} mgba-qt build/current/src/player/nostalgia.bin
run-studio: install gdb: make
${ENV_RUN} ${NOSTALGIA_STUDIO} -profile ${NOSTALGIA_STUDIO_PROFILE} gdb ./build/current/src/wombat/wombat
.PHONY: gba-run
gba-run: pkg-gba
${MGBA} nostalgia.gba
.PHONY: gdb
gdb: install
${ENV_RUN} gdb --args ./dist/${CURRENT_BUILD}/bin/nostalgia sample_project
.PHONY: gdb-studio
gdb-studio: install
${ENV_RUN} gdb --args ${NOSTALGIA_STUDIO} -profile ${NOSTALGIA_STUDIO_PROFILE}
.PHONY: devenv-image devenv-build:
devenv-image: docker build --no-cache . -t ${DEVENV_IMAGE}
docker build . -t ${DEVENV_IMAGE} devenv:
.PHONY: devenv-create docker run -d -v $(shell pwd):/usr/src/project \
devenv-create:
docker run -d \
-e LOCAL_USER_ID=$(shell id -u ${USER}) \ -e LOCAL_USER_ID=$(shell id -u ${USER}) \
-e DISPLAY=$(DISPLAY) \ -e DISPLAY=$(DISPLAY) \
-e QT_AUTO_SCREEN_SCALE_FACTOR=1 \ -e QT_AUTO_SCREEN_SCALE_FACTOR=1 \
-v /tmp/.X11-unix:/tmp/.X11-unix \ -v /tmp/.X11-unix:/tmp/.X11-unix \
-v /run/dbus/:/run/dbus/ \ -v /run/dbus/:/run/dbus/ \
-v $(shell pwd):/usr/src/project \
-v /dev/shm:/dev/shm \ -v /dev/shm:/dev/shm \
--restart=always \ --restart=always \
--name ${DEVENV} \ --name ${DEVENV} \
-t ${DEVENV_IMAGE} bash -t ${DEVENV_IMAGE} bash
.PHONY: devenv-destroy
devenv-destroy: devenv-destroy:
docker rm -f ${DEVENV} docker rm -f ${DEVENV}
.PHONY: devenv-shell
devenv-shell: shell:
${ENV_RUN} bash ${ENV_RUN} bash
.PHONY: conan release:
conan: ${ENV_RUN} rm -rf build/${HOST_ENV}-release
@mkdir -p conanbuild && cd conanbuild && conan install ../ --build=missing ${ENV_RUN} ./scripts/setup_build ${HOST_ENV}
${ENV_RUN} rm -f build/current
${ENV_RUN} ln -s ${HOST_ENV}-release build/current
.PHONY: configure-release debug:
configure-release: ${ENV_RUN} rm -rf build/${HOST_ENV}-debug
${ENV_RUN} ${RM_RF} build/${HOST_ENV}-release ${ENV_RUN} ./scripts/setup_build ${HOST_ENV} debug
${ENV_RUN} ./scripts/setup-build ${HOST_ENV} release ${ENV_RUN} rm -f build/current
${ENV_RUN} ln -s ${HOST_ENV}-debug build/current
.PHONY: configure-debug windows:
configure-debug: ${ENV_RUN} rm -rf build/windows
${ENV_RUN} ${RM_RF} build/${HOST_ENV}-debug ${ENV_RUN} ./scripts/setup_build windows
${ENV_RUN} ./scripts/setup-build ${HOST_ENV} debug ${ENV_RUN} rm -f build/current
${ENV_RUN} ln -s windows build/current
.PHONY: configure-asan windows-debug:
configure-asan: ${ENV_RUN} rm -rf build/windows
${ENV_RUN} ${RM_RF} build/${HOST_ENV}-asan ${ENV_RUN} ./scripts/setup_build windows debug
${ENV_RUN} ./scripts/setup-build ${HOST_ENV} asan ${ENV_RUN} rm -f build/current
${ENV_RUN} ln -s windows build/current
.PHONY: configure-gba gba:
configure-gba: ${ENV_RUN} rm -rf build/gba-release
${ENV_RUN} ${RM_RF} build/gba-release ${ENV_RUN} ./scripts/setup_build gba
${ENV_RUN} ./scripts/setup-build gba release ${ENV_RUN} rm -f build/current
${ENV_RUN} ln -s gba-release build/current
.PHONY: configure-gba-debug gba-debug:
configure-gba-debug: ${ENV_RUN} rm -rf build/gba-debug
${ENV_RUN} ${RM_RF} build/gba-debug ${ENV_RUN} ./scripts/setup_build gba debug
${ENV_RUN} ./scripts/setup-build gba debug ${ENV_RUN} rm -f build/current
${ENV_RUN} ln -s gba-debug build/current

View File

@@ -1,19 +1 @@
# Nostalgia # Nostalgia
## Setup
### Linux
Make sure conan is using the C++11 version of libstdc++.
conan profile update settings.compiler.libcxx=libstdc++11 default
### macOS
Install and use gmake instead of the make that comes with the system.
## Build
Build options: release, debug, gba, gba-debug
make purge conan configure-{gba,release,debug} install

33
build/Makefile Normal file
View File

@@ -0,0 +1,33 @@
all: gba_build gba_debug_build native_build native_debug_build windows_release windows_debug
MAKE=make -j
gba_build:
@if [ -d gba-release ]; then \
${MAKE} -C gba-release ${ARGS}; \
fi
gba_debug_build:
@if [ -d gba-debug ]; then \
${MAKE} -C gba-debug ${ARGS}; \
fi
native_build:
@if [ -d ${HOST_ENV}-release ]; then \
${MAKE} -C ${HOST_ENV}-release ${ARGS}; \
fi
native_debug_build:
@if [ -d ${HOST_ENV}-debug ]; then \
${MAKE} -C ${HOST_ENV}-debug ${ARGS}; \
fi
windows_release:
@if [ -d windows-release ]; then \
${MAKE} -C windows-release ${ARGS}; \
fi
windows_debug:
@if [ -d windows-debug ]; then \
${MAKE} -C windows-debug ${ARGS}; \
fi

10
build_rom.sh Executable file
View File

@@ -0,0 +1,10 @@
#! /usr/bin/env bash
set -e
padbin 32 build/gba-release/src/player/nostalgia.bin
echo NOSTALGIA_MEDIA_HEADER_________ > media_header.txt
oxfs format 32 1m nostalgia_media.oxfs
./build/current/src/tools/nost-pack -fs nostalgia_media.oxfs -img charset.png -inode 1 -c
cat build/gba-release/src/player/nostalgia.bin media_header.txt nostalgia_media.oxfs > nostalgia.gba
gbafix nostalgia.gba

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,49 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# - Try to find Jansson
# Once done this will define
# JANSSON_FOUND - System has Jansson
# JANSSON_INCLUDE_DIRS - The Jansson include directories
# JANSSON_LIBRARIES - The libraries needed to use Jansson
# JANSSON_DEFINITIONS - Compiler switches required for using Jansson
find_path(JANSSON_INCLUDE_DIR jansson.h
PATHS
/usr/include
/usr/local/include
)
find_library(JANSSON_LIBRARY
NAMES
jansson
PATHS
/usr/lib
/usr/local/lib
)
set(JANSSON_LIBRARIES ${JANSSON_LIBRARY})
set(JANSSON_INCLUDE_DIRS ${JANSSON_INCLUDE_DIR})
include(FindPackageHandleStandardArgs)
# handle the QUIETLY and REQUIRED arguments and set JANSSON_FOUND to TRUE
# if all listed variables are TRUE
find_package_handle_standard_args(Jansson DEFAULT_MSG
JANSSON_LIBRARY JANSSON_INCLUDE_DIR)
mark_as_advanced(JANSSON_INCLUDE_DIR JANSSON_LIBRARY)

View File

@@ -1,5 +1,10 @@
set(CMAKE_SYSTEM_NAME "Generic") set(CMAKE_SYSTEM_NAME "Generic")
set(DEVKITARM $ENV{DEVKITARM}) set(DEVKITARM $ENV{DEVKITARM})
set(DEVKITPRO $ENV{DEVKITPRO})
if(NOT DEVKITPRO)
message(FATAL_ERROR "DEVKITPRO environment variable not set")
endif()
if(NOT DEVKITARM) if(NOT DEVKITARM)
message(FATAL_ERROR "DEVKITARM environment variable not set") message(FATAL_ERROR "DEVKITARM environment variable not set")
@@ -19,10 +24,18 @@ set(CMAKE_FIND_LIBRARY_PREFIXES lib)
set(CMAKE_FIND_LIBRARY_SUFFIXES .a) set(CMAKE_FIND_LIBRARY_SUFFIXES .a)
set(LINKER_FLAGS "-specs=gba.specs") set(LINKER_FLAGS "-specs=gba.specs")
add_definitions(-DARM7) add_definitions (
-DARM7
)
function(BuildStaticLib LIBNAME SRCFILES)
add_library(${LIBNAME} OBJECT ${SRCFILES})
set(OBJS ${OBJS} $<TARGET_OBJECTS:${LIBNAME}>)
endfunction()
include(FindPackageHandleStandardArgs) include(FindPackageHandleStandardArgs)
macro(OBJCOPY_FILE EXE_NAME) macro(OBJCOPY_FILE EXE_NAME)
set(FO ${CMAKE_CURRENT_BINARY_DIR}/${EXE_NAME}.bin) set(FO ${CMAKE_CURRENT_BINARY_DIR}/${EXE_NAME}.bin)
set(FI ${CMAKE_CURRENT_BINARY_DIR}/${EXE_NAME}) set(FI ${CMAKE_CURRENT_BINARY_DIR}/${EXE_NAME})

View File

@@ -0,0 +1,4 @@
function(BuildStaticLib libName srcFiles)
endfunction()

View File

@@ -1,9 +0,0 @@
from conans import ConanFile, CMake
class NostalgiaConan(ConanFile):
settings = "os", "compiler", "build_type", "arch"
requires = "jsoncpp/1.9.2", "sdl2/2.0.10@bincrafters/stable"
generators = "cmake", "cmake_find_package", "cmake_paths"
#default_options = {
# "sdl2:nas": False
#}

View File

@@ -1,12 +0,0 @@
enable_language(C ASM)
add_library(
GbaStartup
gba_crt0.s
cstartup.cpp
)
target_link_libraries(
GbaStartup
OxStd
)

View File

@@ -1,63 +0,0 @@
/*
* 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 <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 ox::heapmgr {
void initHeap(char *heapBegin, char *heapEnd);
}
extern "C" {
void __libc_init_array() {
auto preInits = __preinit_array_end - __preinit_array_start;
for (decltype(preInits) i = 0; i < preInits; i++) {
__preinit_array_start[i]();
}
auto inits = __init_array_end - __init_array_start;
for (decltype(inits) i = 0; i < inits; i++) {
__preinit_array_start[i]();
}
}
int main(int argc, const char **argv);
int c_start() {
const char *args[2] = {"", "rom.oxfs"};
ox::heapmgr::initHeap(ox::bit_cast<char*>(HEAP_BEGIN), ox::bit_cast<char*>(HEAP_END));
return main(2, args);
}
}
#pragma GCC diagnostic pop

View File

@@ -1,258 +0,0 @@
/*--------------------------------------------------------------------------------
Copyright devkitPro Project
https://github.com/devkitPro/devkitarm-crtls/blob/master/gba_crt0.s
This Source Code Form is subject to the terms of the Mozilla Public License,
v. 2.0. If a copy of the MPL was not distributed with this file, You can
obtain one at https://mozilla.org/MPL/2.0/.
--------------------------------------------------------------------------------*/
.section ".crt0","ax"
.global _start
.align
.arm
.cpu arm7tdmi
@---------------------------------------------------------------------------------
_start:
@---------------------------------------------------------------------------------
b rom_header_end
.fill 156,1,0 @ Nintendo Logo Character Data (8000004h)
.fill 16,1,0 @ Game Title
.byte 0x30,0x31 @ Maker Code (80000B0h)
.byte 0x96 @ Fixed Value (80000B2h)
.byte 0x00 @ Main Unit Code (80000B3h)
.byte 0x00 @ Device Type (80000B4h)
.fill 7,1,0 @ unused
.byte 0x00 @ Software Version No (80000BCh)
.byte 0xf0 @ Complement Check (80000BDh)
.byte 0x00,0x00 @ Checksum (80000BEh)
@---------------------------------------------------------------------------------
rom_header_end:
@---------------------------------------------------------------------------------
b start_vector @ This branch must be here for proper
@ positioning of the following header.
.GLOBAL __boot_method, __slave_number
@---------------------------------------------------------------------------------
__boot_method:
@---------------------------------------------------------------------------------
.byte 0 @ boot method (0=ROM boot, 3=Multiplay boot)
@---------------------------------------------------------------------------------
__slave_number:
@---------------------------------------------------------------------------------
.byte 0 @ slave # (1=slave#1, 2=slave#2, 3=slave#3)
.byte 0 @ reserved
.byte 0 @ reserved
.word 0 @ reserved
.word 0 @ reserved
.word 0 @ reserved
.word 0 @ reserved
.word 0 @ reserved
.word 0 @ reserved
.global start_vector
.align
@---------------------------------------------------------------------------------
start_vector:
@---------------------------------------------------------------------------------
mov r0, #0x4000000 @ REG_BASE
str r0, [r0, #0x208]
mov r0, #0x12 @ Switch to IRQ Mode
msr cpsr, r0
ldr sp, =__sp_irq @ Set IRQ stack
mov r0, #0x1f @ Switch to System Mode
msr cpsr, r0
ldr sp, =__sp_usr @ Set user stack
@---------------------------------------------------------------------------------
@ Enter Thumb mode
@---------------------------------------------------------------------------------
add r0, pc, #1
bx r0
.thumb
ldr r0, =__text_start
lsl r0, #5 @ Was code compiled at 0x08000000 or higher?
bcs DoEWRAMClear @ yes, you can not run it in external WRAM
mov r0, pc
lsl r0, #5 @ Are we running from ROM (0x8000000 or higher) ?
bcc SkipEWRAMClear @ No, so no need to do a copy.
@---------------------------------------------------------------------------------
@ We were started in ROM, silly emulators. :P
@ So we need to copy to ExWRAM.
@---------------------------------------------------------------------------------
mov r2, #2
lsl r2, r2, #24 @ r2= 0x02000000
ldr r3, =__end__ @ last ewram address
sub r3, r2 @ r3= actual binary size
mov r6, r2 @ r6= 0x02000000
lsl r1, r2, #2 @ r1= 0x08000000
bl CopyMem
bx r6 @ Jump to the code to execute
@---------------------------------------------------------------------------------
DoEWRAMClear: @ Clear External WRAM to 0x00
@---------------------------------------------------------------------------------
mov r1, #0x40
lsl r1, #12 @ r1 = 0x40000
lsl r0, r1, #7 @ r0 = 0x2000000
bl ClearMem
@---------------------------------------------------------------------------------
SkipEWRAMClear: @ Clear Internal WRAM to 0x00
@---------------------------------------------------------------------------------
@---------------------------------------------------------------------------------
@ Clear BSS section to 0x00
@---------------------------------------------------------------------------------
ldr r0, =__bss_start__
ldr r1, =__bss_end__
sub r1, r0
bl ClearMem
@---------------------------------------------------------------------------------
@ Clear SBSS section to 0x00
@---------------------------------------------------------------------------------
ldr r0, =__sbss_start__
ldr r1, =__sbss_end__
sub r1, r0
bl ClearMem
@---------------------------------------------------------------------------------
@ Copy initialized data (data section) from LMA to VMA (ROM to RAM)
@---------------------------------------------------------------------------------
ldr r1, =__data_lma
ldr r2, =__data_start__
ldr r4, =__data_end__
bl CopyMemChk
@---------------------------------------------------------------------------------
@ Copy internal work ram (iwram section) from LMA to VMA (ROM to RAM)
@---------------------------------------------------------------------------------
ldr r1,= __iwram_lma
ldr r2,= __iwram_start__
ldr r4,= __iwram_end__
bl CopyMemChk
@---------------------------------------------------------------------------------
@ Copy internal work ram overlay 0 (iwram0 section) from LMA to VMA (ROM to RAM)
@---------------------------------------------------------------------------------
ldr r2,= __load_stop_iwram0
ldr r1,= __load_start_iwram0
sub r3, r2, r1 @ Is there any data to copy?
beq CIW0Skip @ no
ldr r2,= __iwram_overlay_start
bl CopyMem
@---------------------------------------------------------------------------------
CIW0Skip:
@---------------------------------------------------------------------------------
@ Copy external work ram (ewram section) from LMA to VMA (ROM to RAM)
@---------------------------------------------------------------------------------
ldr r1, =__ewram_lma
ldr r2, =__ewram_start
ldr r4, =__ewram_end
bl CopyMemChk
@---------------------------------------------------------------------------------
CEW0Skip:
@---------------------------------------------------------------------------------
@ set heap end
@---------------------------------------------------------------------------------
// fake_heap_end does not appear to exist,
// and Nostalgia has its own heap allocator anyway
//ldr r1, =fake_heap_end
//ldr r0, =__eheap_end
//str r0, [r1]
@---------------------------------------------------------------------------------
@ global constructors
@---------------------------------------------------------------------------------
ldr r3, =__libc_init_array
bl _blx_r3_stub
@---------------------------------------------------------------------------------
@ Jump to user code
@---------------------------------------------------------------------------------
ldr r3, =c_start
bl _blx_r3_stub
@---------------------------------------------------------------------------------
@ Clear memory to 0x00 if length != 0
@---------------------------------------------------------------------------------
@ r0 = Start Address
@ r1 = Length
@---------------------------------------------------------------------------------
ClearMem:
@---------------------------------------------------------------------------------
mov r2,#3 @ These commands are used in cases where
add r1,r2 @ the length is not a multiple of 4,
bic r1,r2 @ even though it should be.
beq ClearMX @ Length is zero so exit
mov r2,#0
@---------------------------------------------------------------------------------
ClrLoop:
@---------------------------------------------------------------------------------
stmia r0!, {r2}
sub r1,#4
bne ClrLoop
@---------------------------------------------------------------------------------
ClearMX:
@---------------------------------------------------------------------------------
bx lr
@---------------------------------------------------------------------------------
_blx_r3_stub:
@---------------------------------------------------------------------------------
bx r3
@---------------------------------------------------------------------------------
@ Copy memory if length != 0
@---------------------------------------------------------------------------------
@ r1 = Source Address
@ r2 = Dest Address
@ r4 = Dest Address + Length
@---------------------------------------------------------------------------------
CopyMemChk:
@---------------------------------------------------------------------------------
sub r3, r4, r2 @ Is there any data to copy?
@---------------------------------------------------------------------------------
@ Copy memory
@---------------------------------------------------------------------------------
@ r1 = Source Address
@ r2 = Dest Address
@ r3 = Length
@---------------------------------------------------------------------------------
CopyMem:
@---------------------------------------------------------------------------------
mov r0, #3 @ These commands are used in cases where
add r3, r0 @ the length is not a multiple of 4,
bic r3, r0 @ even though it should be.
beq CIDExit @ Length is zero so exit
@---------------------------------------------------------------------------------
CIDLoop:
@---------------------------------------------------------------------------------
ldmia r1!, {r0}
stmia r2!, {r0}
sub r3, #4
bne CIDLoop
@---------------------------------------------------------------------------------
CIDExit:
@---------------------------------------------------------------------------------
bx lr
.align
.pool
.end

View File

@@ -1,12 +0,0 @@
{
"log_functions": [
{
"function": "ox::trace::gdblogger::captureLogFunc",
"ignore_frames": 3,
"file_var": "file",
"line_var": "line",
"channel_var": "ch",
"msg_var": "msg"
}
]
}

10
deps/ox/.gitignore vendored
View File

@@ -1,10 +0,0 @@
build/current
build/gba
build/*-asan
build/*-debug
build/*-release
tags
conanbuildinfo.cmake
conanbuildinfo.txt
conaninfo.txt
graph_info.json

9
deps/ox/.liccor.yml vendored
View File

@@ -1,9 +0,0 @@
---
source:
- src
copyright_notice: |-
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/.

11
deps/ox/.travis.yml vendored
View File

@@ -1,11 +0,0 @@
language: cpp
sudo: false
dist: trusty
compiler:
- clang
- gcc
addons:
apt:
packages:
- cmake
script: ./scripts/cibuild

View File

@@ -1,76 +0,0 @@
cmake_minimum_required(VERSION 3.13)
project(Ox)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules)
include(address_sanitizer)
if(NOT DEFINED OX_RUN_TESTS)
set(OX_RUN_TESTS ON)
endif()
if(NOT DEFINED OX_BUILD_EXEC)
set(OX_BUILD_EXEC ON)
endif()
if(NOT DEFINED OX_USE_STDLIB)
set(OX_USE_STDLIB ON)
endif()
if(NOT DEFINED OX_BARE_METAL)
set(OX_BARE_METAL OFF)
endif()
set(OX_RUN_TESTS ${OX_RUN_TESTS} CACHE BOOL "Run tests (ON/OFF)")
set(OX_BUILD_EXEC ${OX_BUILD_EXEC} CACHE BOOL "Build executables (ON/OFF)")
set(OX_USE_STDLIB ${OX_USE_STDLIB} CACHE BOOL "Build libraries that need the std lib (ON/OFF)")
set(OX_BARE_METAL ${OX_BARE_METAL} CACHE BOOL "Bare metal build (TRUE/FALSE)")
# can't run tests without building them
if(NOT OX_BUILD_EXEC OR NOT OX_USE_STDLIB)
set(OX_BUILD_EXEC OFF)
set(OX_RUN_TESTS OFF)
endif()
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
if (CMAKE_BUILD_TYPE STREQUAL "Release")
add_definitions(-DNDEBUG)
else()
add_definitions(-DDEBUG)
endif()
if(NOT OX_USE_STDLIB)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostdlib")
endif()
if(NOT MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wcast-align")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wdouble-promotion")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wformat=2")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wmissing-field-initializers")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wnon-virtual-dtor")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wnull-dereference")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wold-style-cast")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Woverloaded-virtual")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wpedantic")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsign-compare")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsign-conversion")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wunused")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wunused-variable")
if (CMAKE_BUILD_TYPE STREQUAL "Release")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
endif()
# forces colored output when using ninja
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color")
endif()
enable_testing()
include_directories("src")
install(FILES OxConfig.cmake DESTINATION lib/ox)
add_subdirectory(src)

373
deps/ox/LICENSE vendored
View File

@@ -1,373 +0,0 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
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/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

68
deps/ox/Makefile vendored
View File

@@ -1,68 +0,0 @@
OS=$(shell uname | tr [:upper:] [:lower:])
HOST_ENV=${OS}-$(shell uname -m)
DEVENV=devenv$(shell pwd | sed 's/\//-/g')
DEVENV_IMAGE=wombatant/devenv
ifneq ($(shell which docker 2>&1),)
ifeq ($(shell docker inspect --format="{{.State.Status}}" ${DEVENV} 2>&1),running)
ENV_RUN=docker exec -i -t --user $(shell id -u ${USER}) ${DEVENV}
endif
endif
all:
${ENV_RUN} ./scripts/run-make build
preinstall:
${ENV_RUN} ./scripts/run-make build preinstall
install:
${ENV_RUN} ./scripts/run-make build install
clean:
${ENV_RUN} ./scripts/run-make build clean
purge:
${ENV_RUN} rm -rf build
test:
${ENV_RUN} ./scripts/run-make build test
devenv:
docker pull ${DEVENV_IMAGE}
docker run -d -v $(shell pwd):/usr/src/project \
-e LOCAL_USER_ID=$(shell id -u ${USER}) \
--name ${DEVENV} -t ${DEVENV_IMAGE} bash
devenv-destroy:
docker rm -f ${DEVENV}
devenv-shell:
${ENV_RUN} bash
configure-release:
${ENV_RUN} rm -rf build/${HOST_ENV}-release
${ENV_RUN} ./scripts/setup_build ${HOST_ENV}
${ENV_RUN} rm -f build/current
${ENV_RUN} ln -s ${HOST_ENV}-release build/current
configure-debug:
${ENV_RUN} rm -rf build/${HOST_ENV}-debug
${ENV_RUN} ./scripts/setup_build ${HOST_ENV} debug
${ENV_RUN} rm -f build/current
${ENV_RUN} ln -s ${HOST_ENV}-debug build/current
configure-asan:
${ENV_RUN} rm -rf build/${HOST_ENV}-asan
${ENV_RUN} ./scripts/setup_build ${HOST_ENV} asan
${ENV_RUN} rm -f build/current
${ENV_RUN} ln -s ${HOST_ENV}-asan build/current
configure-windows:
${ENV_RUN} rm -rf build/windows
${ENV_RUN} ./scripts/setup_build windows
${ENV_RUN} rm -f build/current
${ENV_RUN} ln -s windows build/current
configure-windows-debug:
${ENV_RUN} rm -rf build/windows
${ENV_RUN} ./scripts/setup_build windows debug
${ENV_RUN} rm -f build/current
${ENV_RUN} ln -s windows build/current
configure-gba:
${ENV_RUN} rm -rf build/gba-release
${ENV_RUN} ./scripts/setup_build gba
${ENV_RUN} rm -f build/current
${ENV_RUN} ln -s gba-release build/current

View File

@@ -1,15 +0,0 @@
if("${CMAKE_FIND_ROOT_PATH}" STREQUAL "")
set(Ox_INCLUDE_DIRS /usr/local/include/)
set(OxStd_LIBRARY /usr/local/lib/ox/libOxStd.a)
set(OxFS_LIBRARY /usr/local/lib/ox/libOxFS.a)
set(OxClArgs_LIBRARY /usr/local/lib/ox/libOxClArgs.a)
set(OxMetalClaw_LIBRARY /usr/local/lib/ox/libOxMetalClaw.a)
set(OxModel_LIBRARY /usr/local/lib/ox/libOxModelClaw.a)
else("${CMAKE_FIND_ROOT_PATH}" STREQUAL "")
set(Ox_INCLUDE_DIRS ${CMAKE_FIND_ROOT_PATH}/include/)
set(OxStd_LIBRARY ${CMAKE_FIND_ROOT_PATH}/lib/ox/libOxStd.a)
set(OxFS_LIBRARY ${CMAKE_FIND_ROOT_PATH}/lib/ox/libOxFS.a)
set(OxClArgs_LIBRARY ${CMAKE_FIND_ROOT_PATH}/lib/ox/libOxClArgs.a)
set(OxMetalClaw_LIBRARY ${CMAKE_FIND_ROOT_PATH}/lib/ox/libOxMetalClaw.a)
set(OxModel_LIBRARY ${CMAKE_FIND_ROOT_PATH}/lib/ox/libOxModel.a)
endif("${CMAKE_FIND_ROOT_PATH}" STREQUAL "")

View File

@@ -1,24 +0,0 @@
set(CMAKE_SYSTEM_NAME "Generic")
set(DEVKITARM $ENV{DEVKITARM})
if(NOT DEVKITARM)
message(FATAL_ERROR "DEVKITARM environment variable not set")
endif()
set(CMAKE_C_COMPILER ${DEVKITARM}/bin/arm-none-eabi-gcc)
set(CMAKE_CXX_COMPILER ${DEVKITARM}/bin/arm-none-eabi-g++)
set(CMAKE_FIND_ROOT_PATH ${DEVKITARM})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_LIBRARY_PREFIXES lib)
set(CMAKE_FIND_LIBRARY_SUFFIXES .a)
set(LINKER_FLAGS "-specs=gba.specs")
add_definitions (
-DARM7
)
include(FindPackageHandleStandardArgs)

View File

@@ -1,18 +0,0 @@
set(CMAKE_SYSTEM_NAME Windows)
set(TOOLCHAIN_PREFIX x86_64-w64-mingw32)
# cross compilers to use for C and C++
set(CMAKE_C_COMPILER /usr/bin/${TOOLCHAIN_PREFIX}-gcc)
set(CMAKE_CXX_COMPILER /usr/bin/${TOOLCHAIN_PREFIX}-g++)
set(CMAKE_RC_COMPILER /usr/bin/${TOOLCHAIN_PREFIX}-windres)
# target environment on the build host system
# set 1st to dir with the cross compiler's C/C++ headers/libs
set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX})
# modify default behavior of FIND_XXX() commands to
# search for headers/libs in the target environment and
# search for programs in the build host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

View File

@@ -1,52 +0,0 @@
# This file belongs Nick Overdijk, and is from https://github.com/NickNick/wubwubcmake
# The MIT License (MIT)
#
# Copyright (c) 2013 Nick Overdijk
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.option(USE_ASAN "Enable Address Sanitizer, if your compiler supports it" ON)
option(USE_ASAN "Enable Address Sanitizer, if your compiler supports it" OFF)
if(USE_ASAN)
include(CheckCXXSourceCompiles)
# If the compiler understands -fsanitize=address, add it to the flags (gcc since 4.8 & clang since version 3.2)
set(CMAKE_REQUIRED_FLAGS_BAK "${CMAKE_REQUIRED_FLAGS}")
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -fsanitize=address")
CHECK_CXX_SOURCE_COMPILES("int main() { return 0; }" FLAG_FSANA_SUPPORTED)
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS_BAK}")
if(FLAG_FSANA_SUPPORTED)
set(asan_flag "-fsanitize=address")
else(FLAG_FSANA_SUPPORTED)
# Alternatively, try if it understands -faddress-sanitizer (clang until version 3.2)
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -faddress-sanitizer")
CHECK_CXX_SOURCE_COMPILES("int main() { return 0; }" FLAG_FASAN_SUPPORTED)
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS_BAK}")
if(FLAG_FASAN_SUPPORTED)
set(asan_flag "-faddress-sanitizer")
endif(FLAG_FASAN_SUPPORTED)
endif(FLAG_FSANA_SUPPORTED)
if(FLAG_FSANA_SUPPORTED OR FLAG_FASAN_SUPPORTED)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${asan_flag}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${asan_flag}")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${asan_flag}")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${asan_flag}")
endif()
endif(USE_ASAN)

View File

@@ -1,8 +0,0 @@
#! /usr/bin/env bash
set -e
make -j release
make -j debug
make -j
make -j test

View File

@@ -1,8 +0,0 @@
#! /usr/bin/env bash
set -e
for f in $(find $1 -maxdepth 1 -mindepth 1 -type d)
do
cmake --build "$f" --target $2 --
done

View File

@@ -1,3 +0,0 @@
cmake_minimum_required(VERSION 2.8)
add_subdirectory(ox)

View File

@@ -1,10 +0,0 @@
if(${OX_USE_STDLIB})
add_subdirectory(clargs)
add_subdirectory(claw)
add_subdirectory(oc)
endif()
add_subdirectory(fs)
add_subdirectory(mc)
add_subdirectory(ptrarith)
add_subdirectory(model)
add_subdirectory(std)

View File

@@ -1,29 +0,0 @@
cmake_minimum_required(VERSION 2.8)
add_library(
OxClArgs
clargs.cpp
)
set_property(
TARGET
OxClArgs
PROPERTY
POSITION_INDEPENDENT_CODE ON
)
target_link_libraries(OxClArgs OxStd)
install(
FILES
clargs.hpp
DESTINATION
include/ox/clargs
)
install(
TARGETS
OxClArgs
LIBRARY DESTINATION lib/ox
ARCHIVE DESTINATION lib/ox
)

View File

@@ -1,53 +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/.
*/
#include <ox/std/strops.hpp>
#include "clargs.hpp"
namespace ox {
using namespace ::std;
ClArgs::ClArgs(int argc, const char **args) {
for (int i = 0; i < argc; i++) {
string arg = args[i];
if (arg[0] == '-') {
while (arg[0] == '-' && arg.size()) {
arg = arg.substr(1);
}
m_bools[arg] = true;
// parse additional arguments
if (i < argc && args[i + 1]) {
string val = args[i + 1];
if (val.size() && val[i] != '-') {
if (val == "false") {
m_bools[arg] = false;
}
m_strings[arg] = val;
m_ints[arg] = ox_atoi(val.c_str());
i++;
}
}
}
}
}
bool ClArgs::getBool(const char *arg) {
return m_bools[arg];
}
string ClArgs::getString(const char *arg) {
return m_strings[arg];
}
int ClArgs::getInt(const char *arg) {
return m_ints[arg];
}
}

View File

@@ -1,32 +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 <map>
#include <string>
namespace ox {
class ClArgs {
private:
::std::map<::std::string, bool> m_bools;
::std::map<::std::string, ::std::string> m_strings;
::std::map<::std::string, int> m_ints;
public:
ClArgs(int argc, const char **args);
bool getBool(const char *arg);
::std::string getString(const char *arg);
int getInt(const char *arg);
};
}

View File

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

View File

@@ -1,12 +0,0 @@
/*
* 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"

View File

@@ -1,68 +0,0 @@
/*
* 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);
}
}

View File

@@ -1,57 +0,0 @@
/*
* 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);
}
}

View File

@@ -1,14 +0,0 @@
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)

View File

@@ -1,206 +0,0 @@
/*
* 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;
}

View File

@@ -1,23 +0,0 @@
/*
* 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);
}

View File

@@ -1,104 +0,0 @@
/*
* 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

@@ -1,71 +0,0 @@
add_library(
OxFS
filestore/filestoretemplate.cpp
filesystem/filelocation.cpp
filesystem/pathiterator.cpp
filesystem/directory.cpp
filesystem/filesystem.cpp
filesystem/passthroughfs.cpp
)
if(NOT OX_BARE_METAL)
if(NOT APPLE)
target_link_libraries(
OxFS PUBLIC
stdc++fs
)
endif()
set_property(
TARGET
OxFS
PROPERTY
POSITION_INDEPENDENT_CODE ON
)
endif()
target_link_libraries(
OxFS PUBLIC
OxMetalClaw
)
if(OX_BUILD_EXEC STREQUAL "ON")
#add_executable(
# oxfstool
# toollib.cpp
# oxfstool.cpp
#)
#set_target_properties(oxfstool PROPERTIES OUTPUT_NAME oxfs)
#target_link_libraries(
# oxfstool
# OxFS
# OxMetalClaw
# OxStd
#)
endif()
install(
FILES
filestore/filestoretemplate.hpp
DESTINATION
include/ox/fs/filestore
)
install(
FILES
filesystem/filesystem.hpp
filesystem/pathiterator.hpp
DESTINATION
include/ox/fs/filesystem
)
install(
TARGETS
OxFS
LIBRARY DESTINATION lib/ox
ARCHIVE DESTINATION lib/ox
)
if(OX_RUN_TESTS)
add_subdirectory(test)
endif()

View File

@@ -1,16 +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/.
*/
#include "filestoretemplate.hpp"
namespace ox {
template class FileStoreTemplate<uint16_t>;
template class FileStoreTemplate<uint32_t>;
}

View File

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

View File

@@ -1,19 +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/.
*/
#include "directory.hpp"
namespace ox {
template class Directory<FileStore16, uint16_t>;
template class Directory<FileStore32, uint32_t>;
template struct DirectoryEntry<uint16_t>;
template struct DirectoryEntry<uint32_t>;
}

View File

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

View File

@@ -1,69 +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/.
*/
#include "filelocation.hpp"
namespace ox {
FileAddress::FileAddress() {
m_data.inode = 0;
m_type = FileAddressType::Inode;
}
FileAddress::FileAddress(const FileAddress &other) {
operator=(other);
}
FileAddress::FileAddress(std::nullptr_t) {
}
FileAddress::FileAddress(uint64_t inode) {
m_data.inode = inode;
m_type = FileAddressType::Inode;
}
FileAddress::FileAddress(char *path) {
auto pathSize = ox_strlen(path) + 1;
m_data.path = new char[pathSize];
memcpy(m_data.path, path, pathSize);
m_type = FileAddressType::Path;
}
FileAddress::FileAddress(const char *path) {
m_data.constPath = path;
m_type = FileAddressType::ConstPath;
}
FileAddress::~FileAddress() {
if (m_type == FileAddressType::Path) {
delete[] m_data.path;
m_data.path = nullptr;
}
}
const FileAddress &FileAddress::operator=(const FileAddress &other) {
m_type = other.m_type;
switch (m_type) {
case FileAddressType::Path:
{
auto strSize = ox_strlen(other.m_data.path) + 1;
m_data.path = new char[strSize];
ox_memcpy(m_data.path, other.m_data.path, strSize);
break;
}
case FileAddressType::ConstPath:
case FileAddressType::Inode:
m_data = other.m_data;
break;
case FileAddressType::None:
break;
}
return *this;
}
}

View File

@@ -1,114 +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/std/std.hpp>
#include <ox/model/types.hpp>
namespace ox {
enum class FileAddressType: int8_t {
None = -1,
Path,
ConstPath,
Inode,
};
class FileAddress {
template<typename T>
friend ox::Error model(T*, FileAddress*);
public:
static constexpr auto TypeName = "net.drinkingtea.ox.FileAddress";
static constexpr auto Fields = 2;
union Data {
static constexpr auto TypeName = "net.drinkingtea.ox.FileAddress.Data";
static constexpr auto Fields = 3;
char *path;
const char *constPath;
uint64_t inode;
};
protected:
FileAddressType m_type = FileAddressType::None;
Data m_data;
public:
FileAddress();
FileAddress(const FileAddress &other);
FileAddress(std::nullptr_t);
FileAddress(uint64_t inode);
FileAddress(char *path);
FileAddress(const char *path);
~FileAddress();
const FileAddress &operator=(const FileAddress &other);
[[nodiscard]] constexpr FileAddressType type() const noexcept {
switch (m_type) {
case FileAddressType::Path:
case FileAddressType::ConstPath:
return FileAddressType::Path;
default:
return m_type;
}
}
[[nodiscard]] constexpr ValErr<uint64_t> getInode() const noexcept {
switch (m_type) {
case FileAddressType::Inode:
return m_data.inode;
default:
return OxError(1);
}
}
[[nodiscard]] constexpr ValErr<const char*> getPath() const noexcept {
switch (m_type) {
case FileAddressType::Path:
return m_data.path;
case FileAddressType::ConstPath:
return m_data.constPath;
default:
return OxError(1);
}
}
operator bool() const {
return m_type != FileAddressType::None;
}
};
template<typename T>
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 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

@@ -1,88 +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/.
*/
#include "filesystem.hpp"
namespace ox {
[[nodiscard]] ox::ValErr<uint8_t*> FileSystem::read(FileAddress addr) {
switch (addr.type()) {
case FileAddressType::Inode:
return read(addr.getInode().value);
case FileAddressType::ConstPath:
case FileAddressType::Path:
return read(addr.getPath().value);
default:
return OxError(1);
}
}
[[nodiscard]] ox::Error FileSystem::read(FileAddress addr, void *buffer, std::size_t size) {
switch (addr.type()) {
case FileAddressType::Inode:
return read(addr.getInode().value, buffer, size);
case FileAddressType::ConstPath:
case FileAddressType::Path:
return read(addr.getPath().value, buffer, size);
default:
return OxError(1);
}
}
[[nodiscard]] ox::Error FileSystem::read(FileAddress addr, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size) {
switch (addr.type()) {
case FileAddressType::Inode:
return read(addr.getInode().value, readStart, readSize, buffer, size);
case FileAddressType::ConstPath:
case FileAddressType::Path:
return read(addr.getPath().value, readStart, readSize, buffer, size);
default:
return OxError(1);
}
}
[[nodiscard]] ox::Error FileSystem::remove(FileAddress addr, bool recursive) {
switch (addr.type()) {
case FileAddressType::Inode:
return remove(addr.getInode().value, recursive);
case FileAddressType::ConstPath:
case FileAddressType::Path:
return remove(addr.getPath().value, recursive);
default:
return OxError(1);
}
}
ox::Error FileSystem::write(FileAddress addr, void *buffer, uint64_t size, uint8_t fileType) {
switch (addr.type()) {
case FileAddressType::Inode:
return write(addr.getInode().value, buffer, size, fileType);
case FileAddressType::ConstPath:
case FileAddressType::Path:
return write(addr.getPath().value, buffer, size, fileType);
default:
return OxError(1);
}
}
ox::ValErr<FileStat> FileSystem::stat(FileAddress addr) {
switch (addr.type()) {
case FileAddressType::Inode:
return stat(addr.getInode().value);
case FileAddressType::ConstPath:
case FileAddressType::Path:
return stat(addr.getPath().value);
default:
return OxError(1);
}
}
template class FileSystemTemplate<FileStore16, Directory16>;
template class FileSystemTemplate<FileStore32, Directory32>;
}

View File

@@ -1,423 +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/fs/filestore/filestoretemplate.hpp>
#include <ox/fs/filesystem/filelocation.hpp>
#include <ox/fs/filesystem/types.hpp>
#include "directory.hpp"
namespace ox {
class FileSystem {
public:
virtual ~FileSystem() = default;
[[nodiscard]] virtual ox::Error mkdir(const char *path, bool recursive = false) = 0;
/**
* Moves an entry from one directory to another.
* @param src the path to the file
* @param dest the path of the destination directory
*/
[[nodiscard]] virtual ox::Error move(const char *src, const char *dest) = 0;
[[nodiscard]] virtual ox::Error read(const char *path, void *buffer, std::size_t buffSize) = 0;
[[nodiscard]] virtual ox::ValErr<uint8_t*> read(const char *path) = 0;
[[nodiscard]] virtual ox::Error read(uint64_t inode, void *buffer, std::size_t size) = 0;
[[nodiscard]] virtual ox::Error read(uint64_t inode, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size) = 0;
[[nodiscard]] virtual ox::ValErr<uint8_t*> read(uint64_t inode) = 0;
[[nodiscard]] ox::Error read(FileAddress addr, void *buffer, std::size_t size);
[[nodiscard]] ox::Error read(FileAddress addr, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size);
[[nodiscard]] ox::ValErr<uint8_t*> read(FileAddress addr);
[[nodiscard]] virtual ox::Error remove(const char *path, bool recursive = false) = 0;
[[nodiscard]] ox::Error remove(FileAddress addr, bool recursive = false);
[[nodiscard]] virtual ox::Error resize(uint64_t size, void *buffer = nullptr) = 0;
[[nodiscard]] virtual ox::Error write(const char *path, void *buffer, uint64_t size, uint8_t fileType = FileType_NormalFile) = 0;
[[nodiscard]] virtual ox::Error write(uint64_t inode, void *buffer, uint64_t size, uint8_t fileType = FileType_NormalFile) = 0;
[[nodiscard]] ox::Error write(FileAddress addr, void *buffer, uint64_t size, uint8_t fileType = FileType_NormalFile);
[[nodiscard]] virtual ox::ValErr<FileStat> stat(uint64_t inode) = 0;
[[nodiscard]] virtual ox::ValErr<FileStat> stat(const char *path) = 0;
[[nodiscard]] ox::ValErr<FileStat> stat(FileAddress addr);
[[nodiscard]] virtual uint64_t spaceNeeded(uint64_t size) = 0;
[[nodiscard]] virtual uint64_t available() = 0;
[[nodiscard]] virtual uint64_t size() const = 0;
[[nodiscard]] virtual char *buff() = 0;
[[nodiscard]] virtual ox::Error walk(ox::Error(*cb)(uint8_t, uint64_t, uint64_t)) = 0;
[[nodiscard]] virtual bool valid() const = 0;
};
/**
* FileSystemTemplate used to create file system that wraps around a FileStore,
* taking an inode size and a directory type as parameters.
*
* Note: Directory parameter must have a default constructor.
*/
template<typename FileStore, typename Directory>
class FileSystemTemplate: public FileSystem {
private:
static constexpr auto InodeFsData = 2;
struct OX_PACKED FileSystemData {
LittleEndian<typename FileStore::InodeId_t> rootDirInode;
};
FileStore m_fs;
void(*m_freeBuffer)(char*) = nullptr;
public:
FileSystemTemplate() = default;
FileSystemTemplate(void *buffer, uint64_t bufferSize, void(*freeBuffer)(char*) = [] (char *buff) { delete buff; });
FileSystemTemplate(FileStore fs);
~FileSystemTemplate();
[[nodiscard]] static ox::Error format(void *buff, uint64_t buffSize);
[[nodiscard]] ox::Error mkdir(const char *path, bool recursive = false) override;
[[nodiscard]] ox::Error move(const char *src, const char *dest) override;
[[nodiscard]] ox::Error read(const char *path, void *buffer, std::size_t buffSize) override;
[[nodiscard]] ox::ValErr<uint8_t*> read(const char*) override;
[[nodiscard]] ox::Error read(uint64_t inode, void *buffer, std::size_t size) override;
[[nodiscard]] ox::Error read(uint64_t inode, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size) override;
[[nodiscard]] ox::ValErr<uint8_t*> read(uint64_t) override;
template<typename F>
[[nodiscard]] ox::Error ls(const char *dir, F cb);
[[nodiscard]] ox::Error remove(const char *path, bool recursive = false) override;
/**
* Resizes FileSystem to minimum possible size.
*/
[[nodiscard]] ox::Error resize();
[[nodiscard]] ox::Error resize(uint64_t size, void *buffer = nullptr) override;
[[nodiscard]] ox::Error write(const char *path, void *buffer, uint64_t size, uint8_t fileType = FileType_NormalFile) override;
[[nodiscard]] ox::Error write(uint64_t inode, void *buffer, uint64_t size, uint8_t fileType = FileType_NormalFile) override;
[[nodiscard]] ox::ValErr<FileStat> stat(uint64_t inode) override;
[[nodiscard]] ox::ValErr<FileStat> stat(const char *path) override;
uint64_t spaceNeeded(uint64_t size) override;
uint64_t available() override;
uint64_t size() const override;
char *buff() override;
[[nodiscard]] ox::Error walk(ox::Error(*cb)(uint8_t, uint64_t, uint64_t)) override;
bool valid() const override;
private:
[[nodiscard]] ValErr<FileSystemData> fileSystemData() const noexcept;
/**
* Finds the inode ID at the given path.
*/
[[nodiscard]] ValErr<uint64_t> find(const char *path) const noexcept;
[[nodiscard]] ValErr<Directory> rootDir() const noexcept;
};
template<typename FileStore, typename Directory>
FileSystemTemplate<FileStore, Directory>::FileSystemTemplate(FileStore fs) {
m_fs = fs;
}
template<typename FileStore, typename Directory>
FileSystemTemplate<FileStore, Directory>::FileSystemTemplate(void *buffer, uint64_t bufferSize, void(*freeBuffer)(char*)):
m_fs(buffer, bufferSize),
m_freeBuffer(freeBuffer) {
}
template<typename FileStore, typename Directory>
FileSystemTemplate<FileStore, Directory>::~FileSystemTemplate() {
if (m_freeBuffer && m_fs.buff()) {
m_freeBuffer(m_fs.buff());
}
}
template<typename FileStore, typename Directory>
ox::Error FileSystemTemplate<FileStore, Directory>::format(void *buff, uint64_t buffSize) {
oxReturnError(FileStore::format(buff, buffSize));
FileStore fs(buff, buffSize);
constexpr auto rootDirInode = MaxValue<typename FileStore::InodeId_t> / 2;
Directory rootDir(fs, rootDirInode);
oxReturnError(rootDir.init());
FileSystemData fd;
fd.rootDirInode = rootDirInode;
oxTrace("ox::fs::FileSystemTemplate::format") << "rootDirInode:" << fd.rootDirInode;
oxReturnError(fs.write(InodeFsData, &fd, sizeof(fd)));
if (!fs.read(fd.rootDirInode).valid()) {
oxTrace("ox::fs::FileSystemTemplate::format::error") << "FileSystemTemplate::format did not correctly create root directory";
return OxError(1);
}
return OxError(0);
}
template<typename FileStore, typename Directory>
ox::Error FileSystemTemplate<FileStore, Directory>::mkdir(const char *path, bool recursive) {
oxTrace("ox::fs::FileSystemTemplate::mkdir") << "path:" << path << "recursive:" << recursive;
auto rootDir = this->rootDir();
oxReturnError(rootDir.error);
return rootDir.value.mkdir(path, recursive);
}
template<typename FileStore, typename Directory>
ox::Error FileSystemTemplate<FileStore, Directory>::move(const char *src, const char *dest) {
auto fd = fileSystemData();
oxReturnError(fd.error);
Directory rootDir(m_fs, fd.value.rootDirInode);
auto [inode, err] = rootDir.find(src);
oxReturnError(err);
oxReturnError(rootDir.write(dest, inode));
oxReturnError(rootDir.remove(src));
return OxError(0);
}
template<typename FileStore, typename Directory>
ox::Error FileSystemTemplate<FileStore, Directory>::read(const char *path, void *buffer, std::size_t buffSize) {
auto fd = fileSystemData();
oxReturnError(fd.error);
Directory rootDir(m_fs, fd.value.rootDirInode);
auto [inode, err] = rootDir.find(path);
oxReturnError(err);
return read(inode, buffer, buffSize);
}
template<typename FileStore, typename Directory>
[[nodiscard]] ox::ValErr<uint8_t*> FileSystemTemplate<FileStore, Directory>::read(const char *path) {
auto fd = fileSystemData();
oxReturnError(fd.error);
Directory rootDir(m_fs, fd.value.rootDirInode);
auto [inode, err] = rootDir.find(path);
oxReturnError(err);
return read(inode);
}
template<typename FileStore, typename Directory>
ox::Error FileSystemTemplate<FileStore, Directory>::read(uint64_t inode, void *buffer, std::size_t buffSize) {
return m_fs.read(inode, buffer, buffSize);
}
template<typename FileStore, typename Directory>
ox::Error FileSystemTemplate<FileStore, Directory>::read(uint64_t inode, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size) {
return m_fs.read(inode, readStart, readSize, reinterpret_cast<uint8_t*>(buffer), size);
}
template<typename FileStore, typename Directory>
[[nodiscard]] ox::ValErr<uint8_t*> FileSystemTemplate<FileStore, Directory>::read(uint64_t inode) {
auto data = m_fs.read(inode);
if (!data.valid()) {
return OxError(1);
}
return data.get();
}
template<typename FileStore, typename Directory>
template<typename F>
ox::Error FileSystemTemplate<FileStore, Directory>::ls(const char *path, F cb) {
oxTrace("ox::FileSystemTemplate::ls") << "path:" << path;
auto [s, err] = stat(path);
oxReturnError(err);
Directory dir(m_fs, s.inode);
return dir.ls(cb);
}
template<typename FileStore, typename Directory>
ox::Error FileSystemTemplate<FileStore, Directory>::remove(const char *path, bool recursive) {
auto fd = fileSystemData();
oxReturnError(fd.error);
Directory rootDir(m_fs, fd.value.rootDirInode);
auto inode = rootDir.find(path);
oxReturnError(inode.error);
auto st = stat(inode.value);
oxReturnError(st.error);
if (st.value.fileType == FileType_NormalFile || recursive) {
if (auto err = rootDir.remove(path)) {
// removal failed, try putting the index back
oxLogError(rootDir.write(path, inode.value));
return err;
}
} else {
oxTrace("FileSystemTemplate::remove::fail") << "Tried to remove directory without recursive setting.";
return OxError(1);
}
return OxError(0);
}
template<typename FileStore, typename Directory>
ox::Error FileSystemTemplate<FileStore, Directory>::resize() {
return m_fs.resize();
}
template<typename FileStore, typename Directory>
ox::Error FileSystemTemplate<FileStore, Directory>::resize(uint64_t size, void *buffer) {
oxReturnError(m_fs.resize(size, buffer));
return OxError(0);
}
template<typename FileStore, typename Directory>
ox::Error FileSystemTemplate<FileStore, Directory>::write(const char *path, void *buffer, uint64_t size, uint8_t fileType) {
auto [inode, err] = find(path);
if (err) {
auto generated = m_fs.generateInodeId();
oxReturnError(generated.error);
inode = generated.value;
}
auto rootDir = this->rootDir();
oxReturnError(rootDir.error);
oxReturnError(rootDir.value.write(path, inode));
oxReturnError(write(inode, buffer, size, fileType));
return OxError(0);
}
template<typename FileStore, typename Directory>
ox::Error FileSystemTemplate<FileStore, Directory>::write(uint64_t inode, void *buffer, uint64_t size, uint8_t fileType) {
return m_fs.write(inode, buffer, size, fileType);
}
template<typename FileStore, typename Directory>
ValErr<FileStat> FileSystemTemplate<FileStore, Directory>::stat(uint64_t inode) {
auto s = m_fs.stat(inode);
FileStat out;
out.inode = s.value.inode;
out.links = s.value.links;
out.size = s.value.size;
out.fileType = s.value.fileType;
return {out, s.error};
}
template<typename FileStore, typename Directory>
ValErr<FileStat> FileSystemTemplate<FileStore, Directory>::stat(const char *path) {
auto inode = find(path);
if (inode.error) {
return {{}, inode.error};
}
return stat(inode.value);
}
template<typename FileStore, typename Directory>
uint64_t FileSystemTemplate<FileStore, Directory>::spaceNeeded(uint64_t size) {
return m_fs.spaceNeeded(size);
}
template<typename FileStore, typename Directory>
uint64_t FileSystemTemplate<FileStore, Directory>::available() {
return m_fs.available();
}
template<typename FileStore, typename Directory>
uint64_t FileSystemTemplate<FileStore, Directory>::size() const {
return m_fs.size();
}
template<typename FileStore, typename Directory>
char *FileSystemTemplate<FileStore, Directory>::buff() {
return m_fs.buff();
}
template<typename FileStore, typename Directory>
ox::Error FileSystemTemplate<FileStore, Directory>::walk(ox::Error(*cb)(uint8_t, uint64_t, uint64_t)) {
return m_fs.walk(cb);
}
template<typename FileStore, typename Directory>
bool FileSystemTemplate<FileStore, Directory>::valid() const {
return m_fs.valid();
}
template<typename FileStore, typename Directory>
ValErr<typename FileSystemTemplate<FileStore, Directory>::FileSystemData> FileSystemTemplate<FileStore, Directory>::fileSystemData() const noexcept {
FileSystemData fd;
auto err = m_fs.read(InodeFsData, &fd, sizeof(fd));
if (err != 0) {
return {fd, err};
}
return fd;
}
template<typename FileStore, typename Directory>
ValErr<uint64_t> FileSystemTemplate<FileStore, Directory>::find(const char *path) const noexcept {
auto fd = fileSystemData();
if (fd.error) {
return {0, fd.error};
}
// return root as a special case
if (ox_strcmp(path, "/") == 0) {
return static_cast<uint64_t>(fd.value.rootDirInode);
}
Directory rootDir(m_fs, fd.value.rootDirInode);
auto inode = rootDir.find(path);
if (inode.error) {
return {0, inode.error};
}
return inode.value;
}
template<typename FileStore, typename Directory>
ValErr<Directory> FileSystemTemplate<FileStore, Directory>::rootDir() const noexcept {
auto fd = fileSystemData();
if (fd.error) {
return {{}, fd.error};
}
return Directory(m_fs, fd.value.rootDirInode);
}
extern template class FileSystemTemplate<FileStore16, Directory16>;
extern template class FileSystemTemplate<FileStore32, Directory32>;
using FileSystem16 = FileSystemTemplate<FileStore16, Directory16>;
using FileSystem32 = FileSystemTemplate<FileStore32, Directory32>;
}

View File

@@ -1,170 +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/.
*/
#include "passthroughfs.hpp"
#if defined(OX_HAS_PASSTHROUGHFS)
#include <fstream>
namespace ox {
PassThroughFS::PassThroughFS(const char *dirPath) {
m_path = dirPath;
}
PassThroughFS::~PassThroughFS() {
}
std::string PassThroughFS::basePath() {
return m_path.string();
}
Error PassThroughFS::mkdir(const char *path, bool recursive) {
bool success = false;
const auto p = m_path / stripSlash(path);
const auto u8p = p.u8string();
oxTrace("ox::fs::PassThroughFS::mkdir") << u8p.c_str();
if (recursive) {
if (std::filesystem::is_directory(p)) {
success = true;
} else {
success = std::filesystem::create_directories(p);
}
} else {
success = std::filesystem::create_directory(p);
}
return OxError(success ? 0 : 1);
}
Error PassThroughFS::move(const char *src, const char *dest) {
std::filesystem::rename(m_path / stripSlash(src), m_path / stripSlash(dest));
return OxError(0);
}
Error PassThroughFS::read(const char *path, void *buffer, std::size_t buffSize) {
try {
std::ifstream file((m_path / stripSlash(path)), std::ios::binary | std::ios::ate);
const std::size_t size = file.tellg();
file.seekg(0, std::ios::beg);
if (size > buffSize) {
oxTrace("ox::fs::PassThroughFS::read::error") << "Read failed: Buffer too small:" << path;
return OxError(1);
}
file.read(static_cast<char*>(buffer), buffSize);
} catch (const std::fstream::failure&) {
oxTrace("ox::fs::PassThroughFS::read::error") << "Read failed:" << path;
throw OxError(2);
}
return OxError(0);
}
ValErr<uint8_t*> PassThroughFS::read(const char*) {
return OxError(1);
}
Error PassThroughFS::read(uint64_t, void*, std::size_t) {
// unsupported
return OxError(1);
}
Error PassThroughFS::read(uint64_t, std::size_t, std::size_t, void*, std::size_t*) {
// unsupported
return OxError(1);
}
ValErr<uint8_t*> PassThroughFS::read(uint64_t) {
return OxError(1);
}
Error PassThroughFS::remove(const char *path, bool recursive) {
if (recursive) {
return OxError(std::filesystem::remove_all(m_path / stripSlash(path)) != 0);
} else {
return OxError(std::filesystem::remove(m_path / stripSlash(path)) != 0);
}
}
ox::Error PassThroughFS::resize(uint64_t, void*) {
// unsupported
return OxError(1);
}
Error PassThroughFS::write(const char *path, void *buffer, uint64_t size, uint8_t) {
auto p = (m_path / stripSlash(path));
try {
std::ofstream f(p, std::ios::binary);
f.write(static_cast<char*>(buffer), size);
} catch (const std::fstream::failure&) {
oxTrace("ox::fs::PassThroughFS::write::error") << "Write failed:" << path;
throw OxError(1);
}
return OxError(0);
}
Error PassThroughFS::write(uint64_t, void*, uint64_t, uint8_t) {
// unsupported
return OxError(1);
}
ValErr<FileStat> PassThroughFS::stat(uint64_t) {
// unsupported
return {{}, OxError(1)};
}
ValErr<FileStat> PassThroughFS::stat(const char *path) {
std::error_code ec;
const auto p = m_path / stripSlash(path);
uint8_t type = std::filesystem::is_directory(p, ec) ?
FileType_Directory : FileType_NormalFile;
oxTrace("PassThroughFS::stat") << ec.message().c_str() << path;
uint64_t size = type == FileType_Directory ? 0 : std::filesystem::file_size(p, ec);
oxTrace("PassThroughFS::stat") << ec.message().c_str() << path;
oxTrace("PassThroughFS::stat::size") << path << size;
return {{0, 0, size, type}, OxError(ec.value())};
}
uint64_t PassThroughFS::spaceNeeded(uint64_t size) {
return size;
}
uint64_t PassThroughFS::available() {
std::error_code ec;
auto s = std::filesystem::space(m_path, ec);
return s.available;
}
uint64_t PassThroughFS::size() const {
std::error_code ec;
auto s = std::filesystem::space(m_path, ec);
return s.capacity;
}
char *PassThroughFS::buff() {
return nullptr;
}
Error PassThroughFS::walk(Error(*)(uint8_t, uint64_t, uint64_t)) {
return OxError(1);
}
bool PassThroughFS::valid() const {
return std::filesystem::is_directory(m_path);
}
const char *PassThroughFS::stripSlash(const char *path) {
auto pathLen = ox_strlen(path);
for (decltype(pathLen) i = 0; i < pathLen && path[0] == '/'; i++) {
path++;
}
return path;
}
}
#endif

View File

@@ -1,100 +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
#if __has_include(<filesystem>)
#include <filesystem>
#include <string>
#define OX_HAS_PASSTHROUGHFS
#endif
#ifdef OX_HAS_PASSTHROUGHFS
#include "filesystem.hpp"
namespace ox {
/**
*
*/
class PassThroughFS: public FileSystem {
private:
std::filesystem::path m_path;
public:
PassThroughFS(const char *dirPath);
~PassThroughFS();
[[nodiscard]] std::string basePath();
ox::Error mkdir(const char *path, bool recursive = false) override;
ox::Error move(const char *src, const char *dest) override;
ox::Error read(const char *path, void *buffer, std::size_t buffSize) override;
ox::ValErr<uint8_t*> read(const char*) override;
ox::Error read(uint64_t inode, void *buffer, std::size_t size) override;
ox::Error read(uint64_t inode, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size) override;
ox::ValErr<uint8_t*> read(uint64_t) override;
template<typename F>
ox::Error ls(const char *dir, F cb);
ox::Error remove(const char *path, bool recursive = false) override;
ox::Error resize(uint64_t size, void *buffer = nullptr) override;
ox::Error write(const char *path, void *buffer, uint64_t size, uint8_t fileType = FileType_NormalFile) override;
ox::Error write(uint64_t inode, void *buffer, uint64_t size, uint8_t fileType = FileType_NormalFile) override;
ox::ValErr<FileStat> stat(uint64_t inode) override;
ox::ValErr<FileStat> stat(const char *path) override;
uint64_t spaceNeeded(uint64_t size) override;
uint64_t available() override;
uint64_t size() const override;
char *buff() override;
ox::Error walk(Error(*cb)(uint8_t, uint64_t, uint64_t)) override;
bool valid() const override;
private:
/**
* Strips the leading slashes from a string.
*/
const char *stripSlash(const char *path);
};
template<typename F>
ox::Error PassThroughFS::ls(const char *dir, F cb) {
for (auto &p : std::filesystem::directory_iterator(m_path / stripSlash(dir))) {
auto u8p = p.path().filename().u8string();
oxReturnError(cb(u8p.c_str(), 0));
}
return OxError(0);
}
}
#endif

View File

@@ -1,214 +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/.
*/
#include <ox/std/memops.hpp>
#include <ox/std/strops.hpp>
#include <ox/std/trace.hpp>
#include "pathiterator.hpp"
namespace ox {
PathIterator::PathIterator(const char *path, std::size_t maxSize, std::size_t iterator) {
m_path = path;
m_maxSize = maxSize;
m_iterator = iterator;
}
PathIterator::PathIterator(const char *path): PathIterator(path, ox_strlen(path)) {
}
/**
* @return 0 if no error
*/
Error PathIterator::dirPath(char *out, std::size_t outSize) {
int idx = ox_lastIndexOf(m_path, '/', m_maxSize);
std::size_t size = idx + 1;
if (idx >= 0 && size < outSize) {
ox_memcpy(out, m_path, size);
out[size] = 0;
return OxError(0);
} else {
return OxError(1);
}
}
/**
* @return 0 if no error
*/
Error PathIterator::fileName(char *out, std::size_t outSize) {
auto idx = ox_lastIndexOf(m_path, '/', m_maxSize);
if (idx >= 0) {
idx++; // pass up the preceding /
std::size_t fileNameSize = ox_strlen(&m_path[idx]);
if (fileNameSize < outSize) {
ox_memcpy(out, &m_path[idx], fileNameSize);
out[fileNameSize] = 0;
return OxError(0);
} else {
return OxError(1);
}
} else {
return OxError(2);
}
}
// Gets the get item in the path
Error PathIterator::get(char *pathOut, std::size_t pathOutSize) {
std::size_t size = 0;
if (m_iterator >= m_maxSize) {
oxTrace("ox::fs::PathIterator::get") << "m_iterator >= m_maxSize";
return OxError(1);
}
if (!ox_strlen(&m_path[m_iterator])) {
oxTrace("ox::fs::PathIterator::get") << "!ox_strlen(&m_path[m_iterator])";
return OxError(1);
}
auto start = m_iterator;
if (m_path[start] == '/') {
start++;
}
// end is at the next /
const char *substr = ox_strchr(&m_path[start], '/', m_maxSize - start);
// correct end if it is invalid, which happens if there is no next /
if (!substr) {
substr = ox_strchr(&m_path[start], 0, m_maxSize - start);
}
std::size_t end = substr - m_path;
size = end - start;
// cannot fit the output in the output parameter
if (size >= pathOutSize || size == 0) {
return OxError(1);
}
ox_memcpy(pathOut, &m_path[start], size);
// truncate trailing /
if (size && pathOut[size - 1] == '/') {
size--;
}
pathOut[size] = 0; // end with null terminator
return OxError(0);
}
// Gets the get item in the path
Error PathIterator::next(char *pathOut, std::size_t pathOutSize) {
std::size_t size = 0;
auto retval = OxError(1);
if (m_iterator < m_maxSize && ox_strlen(&m_path[m_iterator])) {
retval = OxError(0);
if (m_path[m_iterator] == '/') {
m_iterator++;
}
std::size_t start = m_iterator;
// end is at the next /
const char *substr = ox_strchr(&m_path[start], '/', m_maxSize - start);
// correct end if it is invalid, which happens if there is no next /
if (!substr) {
substr = ox_strchr(&m_path[start], 0, m_maxSize - start);
}
std::size_t end = substr - m_path;
size = end - start;
// cannot fit the output in the output parameter
if (size >= pathOutSize) {
return OxError(1);
}
ox_memcpy(pathOut, &m_path[start], size);
}
// truncate trailing /
if (size && pathOut[size - 1] == '/') {
size--;
}
pathOut[size] = 0; // end with null terminator
m_iterator += size;
return retval;
}
/**
* @return 0 if no error
*/
Error PathIterator::get(BString<MaxFileNameLength> *fileName) {
return get(fileName->data(), fileName->cap());
}
/**
* @return 0 if no error
*/
Error PathIterator::next(BString<MaxFileNameLength> *fileName) {
return next(fileName->data(), fileName->cap());
}
ValErr<std::size_t> PathIterator::nextSize() const {
std::size_t size = 0;
auto retval = OxError(1);
auto it = m_iterator;
if (it < m_maxSize && ox_strlen(&m_path[it])) {
retval = OxError(0);
if (m_path[it] == '/') {
it++;
}
std::size_t start = it;
// end is at the next /
const char *substr = ox_strchr(&m_path[start], '/', m_maxSize - start);
// correct end if it is invalid, which happens if there is no next /
if (!substr) {
substr = ox_strchr(&m_path[start], 0, m_maxSize - start);
}
std::size_t end = substr - m_path;
size = end - start;
}
it += size;
return {size, retval};
}
bool PathIterator::hasNext() const {
std::size_t size = 0;
if (m_iterator < m_maxSize && ox_strlen(&m_path[m_iterator])) {
std::size_t start = m_iterator;
if (m_path[start] == '/') {
start++;
}
// end is at the next /
const char *substr = ox_strchr(&m_path[start], '/', m_maxSize - start);
// correct end if it is invalid, which happens if there is no next /
if (!substr) {
substr = ox_strchr(&m_path[start], 0, m_maxSize - start);
}
std::size_t end = substr - m_path;
size = end - start;
}
return size > 0;
}
bool PathIterator::valid() const {
return m_iterator < m_maxSize && m_path[m_iterator] != 0;
}
PathIterator PathIterator::next() const {
std::size_t size = 0;
auto iterator = m_iterator;
if (iterator < m_maxSize && ox_strlen(&m_path[iterator])) {
if (m_path[iterator] == '/') {
iterator++;
}
std::size_t start = iterator;
// end is at the next /
const char *substr = ox_strchr(&m_path[start], '/', m_maxSize - start);
// correct end if it is invalid, which happens if there is no next /
if (!substr) {
substr = ox_strchr(&m_path[start], 0, m_maxSize - start);
}
std::size_t end = substr - m_path;
size = end - start;
}
iterator += size;
return PathIterator(m_path, m_maxSize, iterator + 1);
}
const char *PathIterator::fullPath() const {
return m_path;
}
}

View File

@@ -1,74 +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/std/std.hpp>
namespace ox {
constexpr std::size_t MaxFileNameLength = 255;
using FileName = BString<MaxFileNameLength>;
class PathIterator {
private:
const char *m_path = nullptr;
std::size_t m_iterator = 0;
std::size_t m_maxSize = 0;
public:
PathIterator(const char *path, std::size_t maxSize, std::size_t iterator = 0);
PathIterator(const char *path);
/**
* @return 0 if no error
*/
Error dirPath(char *pathOut, std::size_t pathOutSize);
/**
* @return 0 if no error
*/
Error fileName(char *out, std::size_t outSize);
/**
* @return 0 if no error
*/
Error next(char *pathOut, std::size_t pathOutSize);
/**
* @return 0 if no error
*/
Error get(char *pathOut, std::size_t pathOutSize);
/**
* @return 0 if no error
*/
Error next(FileName *fileName);
/**
* @return 0 if no error
*/
Error get(FileName *fileName);
/**
* @return 0 if no error
*/
ValErr<std::size_t> nextSize() const;
bool hasNext() const;
bool valid() const;
[[nodiscard]] PathIterator next() const;
const char *fullPath() const;
};
}

View File

@@ -1,44 +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/std/types.hpp>
namespace ox {
enum FsType {
OxFS_16 = 1,
OxFS_32 = 2,
OxFS_64 = 3
};
enum FileType {
FileType_NormalFile = 1,
FileType_Directory = 2
};
constexpr const char *toString(FileType t) {
switch (t) {
case FileType_NormalFile:
return "Normal File";
case FileType_Directory:
return "Directory";
default:
return "";
}
}
struct FileStat {
uint64_t inode = 0;
uint64_t links = 0;
uint64_t size = 0;
uint8_t fileType = 0;
};
}

View File

@@ -1,15 +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 "filestore/filestoretemplate.hpp"
#include "filesystem/filelocation.hpp"
#include "filesystem/filesystem.hpp"
#include "filesystem/passthroughfs.hpp"
#include "filesystem/directory.hpp"

View File

@@ -1,27 +0,0 @@
add_executable(
FSTests
tests.cpp
)
target_link_libraries(
FSTests
OxFS
OxStd
OxMetalClaw
)
add_test("Test\\ PathIterator::next1" FSTests PathIterator::next1)
add_test("Test\\ PathIterator::next2" FSTests PathIterator::next2)
add_test("Test\\ PathIterator::next3" FSTests PathIterator::next3)
add_test("Test\\ PathIterator::next4" FSTests PathIterator::next4)
add_test("Test\\ PathIterator::next5" FSTests PathIterator::next5)
add_test("Test\\ PathIterator::hasNext" FSTests PathIterator::hasNext)
add_test("Test\\ PathIterator::dirPath" FSTests PathIterator::dirPath)
add_test("Test\\ PathIterator::fileName" FSTests PathIterator::fileName)
add_test("Test\\ NodeBuffer::insert" FSTests "NodeBuffer::insert")
add_test("Test\\ FileStore::readWrite" FSTests "FileStore::readWrite")
add_test("Test\\ Directory" FSTests "Directory")
add_test("Test\\ FileSystem" FSTests "FileSystem")

View File

@@ -1,231 +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/.
*/
// make sure asserts are enabled for the test file
#undef NDEBUG
#include <iostream>
#include <assert.h>
#include <map>
#include <vector>
#include <string>
#include <ox/fs/fs.hpp>
#include <ox/std/std.hpp>
#include <ox/fs/filestore/filestoretemplate.hpp>
#include <ox/fs/filesystem/filesystem.hpp>
using namespace std;
using namespace ox;
map<string, int(*)(string)> tests = {
{
{
"PathIterator::next1",
[](string) {
int retval = 0;
string path = "/usr/share/charset.gbag";
PathIterator it(path.c_str(), path.size());
auto buff = static_cast<char*>(ox_alloca(path.size() + 1));
retval |= !(it.next(buff, path.size()) == 0 && ox_strcmp(buff, "usr") == 0);
retval |= !(it.next(buff, path.size()) == 0 && ox_strcmp(buff, "share") == 0);
retval |= !(it.next(buff, path.size()) == 0 && ox_strcmp(buff, "charset.gbag") == 0);
return retval;
}
},
{
"PathIterator::next2",
[](string) {
int retval = 0;
string path = "/usr/share/";
PathIterator it(path.c_str(), path.size());
auto buff = static_cast<char*>(ox_alloca(path.size() + 1));
retval |= !(it.next(buff, path.size()) == 0 && ox_strcmp(buff, "usr") == 0);
retval |= !(it.next(buff, path.size()) == 0 && ox_strcmp(buff, "share") == 0);
return retval;
}
},
{
"PathIterator::next3",
[](string) {
int retval = 0;
string path = "/";
PathIterator it(path.c_str(), path.size());
auto buff = static_cast<char*>(ox_alloca(path.size() + 1));
retval |= !(it.next(buff, path.size()) == 0 && ox_strcmp(buff, "\0") == 0);
return retval;
}
},
{
"PathIterator::next4",
[](string) {
int retval = 0;
string path = "usr/share/charset.gbag";
PathIterator it(path.c_str(), path.size());
auto buff = static_cast<char*>(ox_alloca(path.size() + 1));
retval |= !(it.next(buff, path.size()) == 0 && ox_strcmp(buff, "usr") == 0);
retval |= !(it.next(buff, path.size()) == 0 && ox_strcmp(buff, "share") == 0);
retval |= !(it.next(buff, path.size()) == 0 && ox_strcmp(buff, "charset.gbag") == 0);
return retval;
}
},
{
"PathIterator::next5",
[](string) {
int retval = 0;
string path = "usr/share/";
PathIterator it(path.c_str(), path.size());
auto buff = static_cast<char*>(ox_alloca(path.size() + 1));
retval |= !(it.next(buff, path.size()) == 0 && ox_strcmp(buff, "usr") == 0);
retval |= !(it.next(buff, path.size()) == 0 && ox_strcmp(buff, "share") == 0);
return retval;
}
},
{
"PathIterator::dirPath",
[] (string) {
int retval = 0;
string path = "/usr/share/charset.gbag";
PathIterator it(path.c_str(), path.size());
auto buff = static_cast<char*>(ox_alloca(path.size() + 1));
retval |= !(it.dirPath(buff, path.size()) == 0 && ox_strcmp(buff, "/usr/share/") == 0);
return retval;
}
},
{
"PathIterator::fileName",
[](string) {
int retval = 0;
string path = "/usr/share/charset.gbag";
PathIterator it(path.c_str(), path.size());
auto buff = static_cast<char*>(ox_alloca(path.size() + 1));
retval |= !(it.fileName(buff, path.size()) == 0 && ox_strcmp(buff, "charset.gbag") == 0);
return retval;
}
},
{
"PathIterator::hasNext",
[](string) {
int retval = 0;
const auto path = "/file1";
PathIterator it(path, ox_strlen(path));
oxAssert(it.hasNext(), "PathIterator shows incorrect hasNext");
oxAssert(!it.next().hasNext(), "PathIterator shows incorrect hasNext");
return retval;
}
},
{
"Ptr::subPtr",
[](string) {
constexpr auto buffLen = 5000;
ox::ptrarith::Ptr<uint8_t, uint32_t> p(ox_alloca(buffLen), buffLen, 500, 500);
oxAssert(p.valid(), "Ptr::subPtr: Ptr p is invalid.");
auto subPtr = p.subPtr<uint64_t>(50);
oxAssert(subPtr.valid(), "Ptr::subPtr: Ptr subPtr is invalid.");
return 0;
}
},
{
"NodeBuffer::insert",
[](string) {
int err = 0;
constexpr auto buffLen = 5000;
auto list = new (ox_alloca(buffLen)) ox::ptrarith::NodeBuffer<uint32_t, ox::FileStoreItem<uint32_t>>(buffLen);
oxAssert(list->malloc(50).valid(), "NodeBuffer::insert: malloc 1 failed");
oxAssert(list->malloc(50).valid(), "NodeBuffer::insert: malloc 2 failed");
auto first = list->firstItem();
oxAssert(first.valid(), "NodeBuffer::insert: Could not access first item");
oxAssert(first->size() == 50, "NodeBuffer::insert: First item size invalid");
return err;
}
},
{
"FileStore::readWrite",
[](string) {
constexpr auto buffLen = 5000;
constexpr auto str1 = "Hello, World!";
constexpr auto str1Len = ox_strlen(str1) + 1;
constexpr auto str2 = "Hello, Moon!";
constexpr auto str2Len = ox_strlen(str2) + 1;
auto list = new (ox_alloca(buffLen)) ox::ptrarith::NodeBuffer<uint32_t, ox::FileStoreItem<uint32_t>>(buffLen);
oxAssert(ox::FileStore32::format(list, buffLen), "FileStore::format failed.");
ox::FileStore32 fileStore(list, buffLen);
oxAssert(fileStore.write(4, const_cast<char*>(str1), str1Len, 1), "FileStore::write 1 failed.");
oxAssert(fileStore.write(5, const_cast<char*>(str2), str2Len, 1), "FileStore::write 2 failed.");
char str1Read[str1Len];
size_t str1ReadSize = 0;
oxAssert(fileStore.read(4, reinterpret_cast<void*>(str1Read), str1Len, &str1ReadSize), "FileStore::read 1 failed.");
return 0;
}
},
{
"Directory",
[](string) {
std::vector<uint8_t> fsBuff(5000);
oxAssert(ox::FileStore32::format(fsBuff.data(), fsBuff.size()), "FS format failed");
ox::FileStore32 fileStore(fsBuff.data(), fsBuff.size());
ox::Directory32 dir(fileStore, 105);
oxTrace("ox::fs::test::Directory") << "Init";
oxAssert(dir.init(), "Init failed");
oxTrace("ox::fs::test::Directory") << "write 1";
oxAssert(dir.write("/file1", 1), "Directory write of file1 failed");
oxTrace("ox::fs::test::Directory") << "find";
oxAssert(dir.find("file1").error, "Could not find file1");
oxAssert(dir.find("file1").value == 1, "Could not find file1");
oxTrace("ox::fs::test::Directory") << "write 2";
oxAssert(dir.write("/file3", 3), "Directory write of file3 failed");
oxTrace("ox::fs::test::Directory") << "write 3";
oxAssert(dir.write("/file2", 2), "Directory write of file2 failed");
return 0;
}
},
{
"FileSystem",
[](string) {
std::vector<uint8_t> fsBuff(5000);
oxTrace("ox::fs::test::FileSystem") << "format";
oxAssert(ox::FileSystem32::format(fsBuff.data(), fsBuff.size()), "FileSystem format failed");
ox::FileSystem32 fs(ox::FileStore32(fsBuff.data(), fsBuff.size()));
oxTrace("ox::fs::test::FileSystem") << "mkdir";
oxAssert(fs.mkdir("/dir", true), "mkdir failed");
oxAssert(fs.stat("/dir").error, "mkdir failed");
oxAssert(fs.mkdir("/l1d1/l2d1/l3d1", true), "mkdir failed");
oxAssert(fs.stat("/l1d1/l2d1/l3d1").error, "mkdir failed");
oxAssert(fs.mkdir("/l1d1/l2d2", true), "mkdir failed");
oxAssert(fs.stat("/l1d1/l2d2").error, "mkdir failed");
return 0;
}
},
},
};
int main(int argc, const char **args) {
int retval = -1;
if (argc > 1) {
auto testName = args[1];
string testArg = "";
if (args[2]) {
testArg = args[2];
}
if (tests.find(testName) != tests.end()) {
retval = tests[testName](testArg);
}
}
return retval;
}

View File

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

View File

@@ -1,19 +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
namespace ox {
enum {
MC_PRESENCEMASKOUTBOUNDS = 1,
MC_BUFFENDED = 2,
MC_OUTBUFFENDED = 4
};
}

View File

@@ -1,152 +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/std/assert.hpp>
#include <ox/std/bit.hpp>
#include <ox/std/byteswap.hpp>
#include <ox/std/memops.hpp>
namespace ox::mc {
template<typename T>
static constexpr auto Bits = sizeof(T) << 3;
/**
* Returns highest bit other than possible signed bit.
* Bit numbering starts at 0.
*/
template<typename I>
[[nodiscard]] constexpr std::size_t highestBit(I val) noexcept {
auto shiftStart = sizeof(I) * 8 - 1;
// find most significant non-sign indicator bit
std::size_t highestBit = 0;
// start at one bit lower if signed
if constexpr(ox::is_signed_v<I>) {
--shiftStart;
}
for (int i = shiftStart; i > -1; --i) {
const auto bitValue = (val >> i) & 1;
if (bitValue) {
highestBit = i;
break;
}
}
return highestBit;
}
static_assert(highestBit(int8_t(0b10000000)) == 0);
static_assert(highestBit(1) == 0);
static_assert(highestBit(2) == 1);
static_assert(highestBit(4) == 2);
static_assert(highestBit(8) == 3);
static_assert(highestBit(uint64_t(1) << 31) == 31);
static_assert(highestBit(uint64_t(1) << 63) == 63);
struct McInt {
uint8_t data[9] = {0};
// length of integer in bytes
std::size_t length = 0;
};
template<typename I>
[[nodiscard]] constexpr McInt encodeInteger(I input) noexcept {
McInt out;
// move input to uint64_t to allow consistent bit manipulation, and to avoid
// overflow concerns
uint64_t val = 0;
ox_memcpy(&val, &input, sizeof(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_v<I> ? 1 : 0);
// bytes needed to store value
std::size_t bytes = bits / 8 + (bits % 8 != 0);
const auto bitsAvailable = bytes * 8; // bits available to integer value
const auto bitsNeeded = bits + bytes;
// factor in bits needed for bytesIndicator (does not affect bytesIndicator)
// bits for integer + bits neded to represent bytes > bits available
if (bitsNeeded > bitsAvailable && bytes != 9) {
++bytes;
}
const auto bytesIndicator = onMask<uint8_t>(bytes - 1);
// ensure we are copying from little endian represenstation
LittleEndian<I> leVal = val;
if (bytes == 9) {
out.data[0] = bytesIndicator;
ox_memcpy(&out.data[1], &leVal, sizeof(I));
} else {
auto intermediate =
static_cast<uint64_t>(leVal.raw()) << bytes |
static_cast<uint64_t>(bytesIndicator);
ox_memcpy(out.data, &intermediate, sizeof(intermediate));
}
out.length = bytes;
}
return out;
}
/**
* Returns the number of bytes indicated by the bytes indicator of a variable
* length integer.
*/
[[nodiscard]] static constexpr std::size_t countBytes(uint8_t b) noexcept {
std::size_t i = 0;
for (; (b >> i) & 1; i++);
return i + 1;
}
static_assert(countBytes(0b00000000) == 1);
static_assert(countBytes(0b00000001) == 2);
static_assert(countBytes(0b00000011) == 3);
static_assert(countBytes(0b00000111) == 4);
static_assert(countBytes(0b00001111) == 5);
static_assert(countBytes(0b00011111) == 6);
static_assert(countBytes(0b00111111) == 7);
static_assert(countBytes(0b01111111) == 8);
static_assert(countBytes(0b11111111) == 9);
template<typename I>
[[nodiscard]] constexpr ValErr<I> decodeInteger(uint8_t buff[9], std::size_t buffLen, std::size_t *bytesRead) noexcept {
const auto bytes = countBytes(buff[0]);
if (bytes == 9) {
*bytesRead = bytes;
I out = 0;
memcpy(&out, &buff[1], sizeof(I));
return {LittleEndian<I>(out), OxError(0)};
} else if (buffLen >= bytes) {
*bytesRead = bytes;
uint64_t decoded = 0;
memcpy(&decoded, &buff[0], bytes);
decoded >>= bytes;
auto out = static_cast<I>(decoded);
// move sign bit
if constexpr(ox::is_signed_v<I>) {
const auto valBits = bytes << 3;
// get sign
uint64_t sign = decoded >> (valBits - 1);
// remove sign
decoded &= uint64_t(~0) ^ (uint64_t(1) << valBits);
// set sign
decoded |= sign << (Bits<I> - 1);
memcpy(&out, &decoded, sizeof(out));
}
return {out, OxError(0)};
}
return {0, OxError(1)};
}
template<typename I>
[[nodiscard]] ValErr<I> decodeInteger(McInt m) noexcept {
std::size_t bytesRead;
return decodeInteger<I>(m.data, 9, &bytesRead);
}
}

View File

@@ -1,14 +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 "intops.hpp"
#include "read.hpp"
#include "types.hpp"
#include "write.hpp"

View File

@@ -1,59 +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/.
*/
#include <ox/std/byteswap.hpp>
#include <ox/std/memops.hpp>
#include "err.hpp"
#include "presenceindicator.hpp"
namespace ox {
FieldPresenceIndicator::FieldPresenceIndicator(uint8_t *mask, std::size_t maxLen) {
m_mask = mask;
m_maskLen = maxLen;
}
ValErr<bool> FieldPresenceIndicator::get(std::size_t i) const {
if (i / 8 < m_maskLen) {
return (m_mask[i / 8] >> (i % 8)) & 1;
} else {
return {false, OxError(MC_PRESENCEMASKOUTBOUNDS)};
}
}
Error FieldPresenceIndicator::set(std::size_t i, bool on) {
if (i / 8 < m_maskLen) {
if (on) {
m_mask[i / 8] |= 1 << (i % 8);
} else {
m_mask[i / 8] &= ~(1 << (i % 8));
}
return OxError(0);
} else {
return OxError(MC_PRESENCEMASKOUTBOUNDS);
}
}
void FieldPresenceIndicator::setFields(int fields) noexcept {
m_fields = fields;
m_maskLen = (fields / 8 + 1) - (fields % 8 == 0);
}
int FieldPresenceIndicator::getFields() const noexcept {
return m_fields;
}
void FieldPresenceIndicator::setMaxLen(int maxLen) noexcept {
m_maskLen = maxLen;
}
int FieldPresenceIndicator::getMaxLen() const noexcept {
return m_maskLen;
}
}

View File

@@ -1,39 +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/std/error.hpp>
#include <ox/std/types.hpp>
namespace ox {
class FieldPresenceIndicator {
private:
uint8_t *m_mask = nullptr;
std::size_t m_maskLen = 0;
std::size_t m_fields = 0;
public:
FieldPresenceIndicator(uint8_t *mask, std::size_t maxLen);
[[nodiscard]] ValErr<bool> get(std::size_t i) const;
[[nodiscard]] Error set(std::size_t i, bool on);
void setFields(int) noexcept;
int getFields() const noexcept;
void setMaxLen(int) noexcept;
int getMaxLen() const noexcept;
};
}

View File

@@ -1,154 +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/.
*/
#include <ox/std/assert.hpp>
#include <ox/std/byteswap.hpp>
#include <ox/std/memops.hpp>
#include "read.hpp"
namespace ox {
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() noexcept {
if (m_parent) {
m_parent->m_buffIt += m_buffIt;
}
if (m_field != m_fields) {
oxTrace("ox::mc::MetalClawReader::error") << "MetalClawReader: incorrect fields number given";
}
}
Error MetalClawReader::field(const char*, int8_t *val) {
return readInteger(val);
}
Error MetalClawReader::field(const char*, int16_t *val) {
return readInteger(val);
}
Error MetalClawReader::field(const char*, int32_t *val) {
return readInteger(val);
}
Error MetalClawReader::field(const char*, int64_t *val) {
return readInteger(val);
}
Error MetalClawReader::field(const char*, uint8_t *val) {
return readInteger(val);
}
Error MetalClawReader::field(const char*, uint16_t *val) {
return readInteger(val);
}
Error MetalClawReader::field(const char*, uint32_t *val) {
return readInteger(val);
}
Error MetalClawReader::field(const char*, uint64_t *val) {
return readInteger(val);
}
Error MetalClawReader::field(const char*, bool *val) {
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);
}
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);
}
std::size_t bytesRead = 0;
auto out = mc::decodeInteger<ArrayLength>(&m_buff[m_buffIt], m_buffLen - m_buffIt, &bytesRead).value;
if (pass) {
m_buffIt += bytesRead;
}
return out;
}
return OxError(1);
}
[[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);
return len.value;
}
return 0;
}
MetalClawReader MetalClawReader::child(const char*, int unionIdx) {
return MetalClawReader(m_buff + m_buffIt, m_buffLen - m_buffIt, unionIdx, this);
}
bool MetalClawReader::fieldPresent(const char*) const {
return m_fieldPresence.get(m_field).value;
}
bool MetalClawReader::fieldPresent(int fieldNo) const {
return m_fieldPresence.get(fieldNo).value;
}
void MetalClawReader::nextField() noexcept {
++m_field;
}
}

View File

@@ -1,281 +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/optype.hpp>
#include <ox/model/types.hpp>
#include <ox/std/byteswap.hpp>
#include <ox/std/string.hpp>
#include <ox/std/trace.hpp>
#include <ox/std/vector.hpp>
#include "err.hpp"
#include "intops.hpp"
#include "presenceindicator.hpp"
#include "types.hpp"
namespace ox {
class MetalClawReader {
private:
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, int unionIdx = -1, MetalClawReader *parent = nullptr) noexcept;
~MetalClawReader() noexcept;
[[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);
// array handler
template<typename T>
[[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>
[[nodiscard]] Error field(const char*, Handler handler);
template<typename T>
[[nodiscard]] Error field(const char*, ox::Vector<T> *val);
template<typename T>
[[nodiscard]] Error field(const char*, T *val);
template<typename U>
[[nodiscard]] Error field(const char*, UnionView<U> val);
template<std::size_t L>
[[nodiscard]] Error field(const char*, ox::BString<L> *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(const char *name, bool pass = true);
/**
* Reads an string length from the current location in the buffer.
*/
[[nodiscard]] StringLength stringLength(const char *name);
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(const char *name, int unionIdx = -1);
/**
* Indicates whether or not the next field to be read is present.
*/
bool fieldPresent(const char *name) const;
/**
* Indicates whether or not the given field is present.
*/
bool fieldPresent(int fieldNo) const;
void nextField() noexcept;
static constexpr auto opType() {
return OpType::Read;
}
private:
template<typename I>
[[nodiscard]] Error readInteger(I *val);
};
template<typename T>
Error MetalClawReader::field(const char*, T *val) {
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);
}
template<std::size_t L>
Error MetalClawReader::field(const char *name, ox::BString<L> *val) {
return field(name, SerStr(val->data(), val->cap()));
}
template<typename I>
Error MetalClawReader::readInteger(I *val) {
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;
}
}
++m_field;
return OxError(0);
}
// array handler
template<typename T>
Error MetalClawReader::field(const char *name, T *val, std::size_t valLen) {
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);
}
}
}
++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_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));
}
}
}
++m_field;
return OxError(0);
}
template<typename T>
Error MetalClawReader::field(const char* name, ox::Vector<T> *val) {
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);
return model(&reader, val);
}
}

View File

@@ -1,15 +0,0 @@
add_executable(
McTest
tests.cpp
)
target_link_libraries(
McTest
OxMetalClaw
)
add_test("Test\\ McTest\\ Writer" McTest MetalClawWriter)
add_test("Test\\ McTest\\ Reader" McTest MetalClawReader)
add_test("Test\\ McTest\\ MetalClawDef" McTest MetalClawDef)
add_test("Test\\ McTest\\ encodeInteger" McTest encodeInteger)
add_test("Test\\ McTest\\ decodeInteger" McTest decodeInteger)

View File

@@ -1,407 +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/.
*/
#include "ox/std/hashmap.hpp"
#undef NDEBUG
#include <assert.h>
#include <iostream>
#include <map>
#include <memory>
#include <string>
#include <vector>
#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 = 17;
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->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("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 = {
{
{
"MetalClawWriter",
[] {
// This test doesn't confirm much, but it does show that the writer
// doesn't segfault
constexpr size_t buffLen = 1024;
uint8_t buff[buffLen];
TestStruct ts;
oxReturnError(ox::writeMC(buff, buffLen, &ts));
return OxError(0);
}
},
{
"MetalClawReader",
[] {
constexpr size_t buffLen = 1024;
uint8_t buff[buffLen];
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.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), "readMC failed");
//std::cout << testIn.Union.Int << "|" << testOut.Union.Int << "|\n";
oxAssert(testIn.Bool == testOut.Bool, "Bool value mismatch");
oxAssert(testIn.Int == testOut.Int, "Int value mismatch");
oxAssert(testIn.Int1 == testOut.Int1, "Int1 value mismatch");
oxAssert(testIn.Int2 == testOut.Int2, "Int2 value mismatch");
oxAssert(testIn.Int3 == testOut.Int3, "Int3 value mismatch");
oxAssert(testIn.Int4 == testOut.Int4, "Int4 value mismatch");
oxAssert(testIn.Int5 == testOut.Int5, "Int5 value mismatch");
oxAssert(testIn.Int6 == testOut.Int6, "Int6 value mismatch");
oxAssert(testIn.Int7 == testOut.Int7, "Int7 value mismatch");
oxAssert(testIn.Int8 == testOut.Int8, "Int8 value mismatch");
oxAssert(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);
}
},
{
"encodeInteger",
[] {
using ox::MaxValue;
using ox::mc::McInt;
using ox::mc::encodeInteger;
constexpr auto check = [](McInt val, std::vector<uint8_t> &&expected) {
if (val.length != expected.size()) {
std::cout << "val.length: " << val.length << ", expected: " << expected.size() << '\n';
return OxError(1);
}
for (std::size_t i = 0; i < expected.size(); i++) {
if (expected[i] != val.data[i]) {
std::cout << i << ": " << static_cast<uint32_t>(val.data[i]) << '\n';
return OxError(1);
}
}
return OxError(0);
};
constexpr auto check64 = [](McInt val, auto expected) {
if (val.length != 9) {
std::cout << "val.length: " << val.length << '\n';
return OxError(1);
}
ox::LittleEndian<decltype(expected)> decoded = *reinterpret_cast<decltype(expected)*>(&val.data[1]);
if (expected != decoded) {
std::cout << "decoded: " << decoded << ", expected: " << expected << '\n';
return OxError(1);
}
return OxError(0);
};
oxAssert(check(encodeInteger(int64_t(1)), {0b00000010}), "Encode 1 fail");
oxAssert(check(encodeInteger(int64_t(2)), {0b00000100}), "Encode 2 fail");
oxAssert(check(encodeInteger(int64_t(3)), {0b00000110}), "Encode 3 fail");
oxAssert(check(encodeInteger(int64_t(4)), {0b00001000}), "Encode 4 fail");
oxAssert(check(encodeInteger(int64_t(128)), {0b00000001, 0b10}), "Encode 128 fail");
oxAssert(check(encodeInteger(int64_t(129)), {0b00000101, 0b10}), "Encode 129 fail");
oxAssert(check(encodeInteger(int64_t(130)), {0b00001001, 0b10}), "Encode 130 fail");
oxAssert(check(encodeInteger(int64_t(131)), {0b00001101, 0b10}), "Encode 131 fail");
oxAssert(check(encodeInteger(int64_t(-1)), {255, 255, 255, 255, 255, 255, 255, 255, 255}), "Encode -1 fail");
oxAssert(check(encodeInteger(int64_t(-2)), {255, 254, 255, 255, 255, 255, 255, 255, 255}), "Encode -2 fail");
oxAssert(check(encodeInteger(int64_t(-3)), {255, 253, 255, 255, 255, 255, 255, 255, 255}), "Encode -3 fail");
oxAssert(check(encodeInteger(int64_t(-4)), {255, 252, 255, 255, 255, 255, 255, 255, 255}), "Encode -4 fail");
oxAssert(check(encodeInteger(int64_t(-128)), {255, 128, 255, 255, 255, 255, 255, 255, 255}), "Encode -128 fail");
oxAssert(check(encodeInteger(int64_t(-129)), {255, 127, 255, 255, 255, 255, 255, 255, 255}), "Encode -129 fail");
oxAssert(check(encodeInteger(int64_t(-130)), {255, 126, 255, 255, 255, 255, 255, 255, 255}), "Encode -130 fail");
oxAssert(check(encodeInteger(int64_t(-131)), {255, 125, 255, 255, 255, 255, 255, 255, 255}), "Encode -131 fail");
oxAssert(check(encodeInteger(uint32_t(0xffffffff)), {0b11101111, 255, 255, 255, 0b00011111}), "Encode 0xffffffff fail");
oxAssert(check(encodeInteger(uint64_t(1)), {0b0010}), "Encode 1 fail");
oxAssert(check(encodeInteger(uint64_t(2)), {0b0100}), "Encode 2 fail");
oxAssert(check(encodeInteger(uint64_t(3)), {0b0110}), "Encode 3 fail");
oxAssert(check(encodeInteger(uint64_t(4)), {0b1000}), "Encode 4 fail");
oxAssert(check(encodeInteger(uint64_t(128)), {0b0001, 0b10}), "Encode 128 fail");
oxAssert(check(encodeInteger(uint64_t(129)), {0b0101, 0b10}), "Encode 129 fail");
oxAssert(check(encodeInteger(uint64_t(130)), {0b1001, 0b10}), "Encode 130 fail");
oxAssert(check(encodeInteger(uint64_t(131)), {0b1101, 0b10}), "Encode 131 fail");
// Signed check needs lambda templates to run correctly without
// code deduplication
//oxAssert(check64(encodeInteger(MaxValue<int64_t>), MaxValue<int64_t>), "Encode MaxValue<int64_t> fail");
oxAssert(check64(encodeInteger(MaxValue<uint64_t>), MaxValue<uint64_t>), "Encode MaxValue<uint64_t> fail");
return OxError(0);
}
},
{
"decodeInteger",
[] {
using ox::MaxValue;
using ox::mc::McInt;
using ox::mc::encodeInteger;
using ox::mc::decodeInteger;
constexpr auto check = [](auto val) {
auto result = decodeInteger<decltype(val)>(encodeInteger(val));
oxReturnError(result.error);
if (result.value != val) {
std::cout << "Bad value: " << result.value << ", expected: " << val << '\n';
return OxError(1);
}
return OxError(0);
};
oxAssert(check(uint32_t(14)), "Decode of 14 failed.");
oxAssert(check(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.");
oxAssert(check(int64_t(130)), "Decode of 130 failed.");
oxAssert(check(int64_t(131)), "Decode of 131 failed.");
oxAssert(check(uint64_t(1)), "Decode of 1 failed.");
oxAssert(check(uint64_t(2)), "Decode of 2 failed.");
oxAssert(check(uint64_t(42)), "Decode of 42 failed.");
oxAssert(check(uint64_t(130)), "Decode of 130 failed.");
oxAssert(check(uint64_t(131)), "Decode of 131 failed.");
oxAssert(check(0xffffffff), "Decode of 0xffffffff failed.");
oxAssert(check(0xffffffffffff), "Decode of 0xffffffffffff failed.");
oxAssert(check(0xffffffffffffffff), "Decode of U64 max failed.");
return OxError(0);
}
},
{
"MetalClawDef",
[] {
//constexpr size_t descBuffLen = 1024;
//uint8_t descBuff[descBuffLen];
constexpr size_t dataBuffLen = 1024;
uint8_t dataBuff[dataBuffLen];
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";
oxAssert(ox::writeMC(dataBuff, dataBuffLen, &testIn), "Data generation failed");
auto type = ox::buildTypeDef(&testIn);
oxAssert(type.error, "Descriptor write failed");
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();
switch (f.type->primitiveType) {
case ox::PrimitiveType::UnsignedInteger:
std::cout << fieldName << ":\tuint" << f.type->length * 8 << "_t:\t";
switch (f.type->length) {
case 1: {
uint8_t i = {};
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
std::cout << i;
break;
}
case 2: {
uint16_t i = {};
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
std::cout << i;
break;
}
case 4: {
uint32_t i = {};
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
std::cout << i;
break;
}
case 8: {
uint64_t i = {};
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
std::cout << i;
break;
}
}
std::cout << '\n';
break;
case ox::PrimitiveType::SignedInteger:
std::cout << fieldName << ":\tint" << f.type->length * 8 << "_t:\t";
switch (f.type->length) {
case 1: {
int8_t i = {};
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
std::cout << i;
break;
}
case 2: {
int16_t i = {};
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
std::cout << i;
break;
}
case 4: {
int32_t i = {};
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
std::cout << i;
break;
}
case 8: {
int64_t i = {};
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
std::cout << i;
break;
}
}
std::cout << '\n';
break;
case ox::PrimitiveType::Bool: {
bool i = {};
oxAssert(rdr->field(fieldName, &i), "Walking model failed.");
std::cout << fieldName << ":\t" << "bool:\t\t" << (i ? "true" : "false") << '\n';
break;
}
case ox::PrimitiveType::String: {
ox::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:\t\t" << 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;
}

View File

@@ -1,97 +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/.
*/
#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, int unionIdx) noexcept:
m_fieldPresence(buff, buffLen),
m_unionIdx(unionIdx),
m_buffLen(buffLen),
m_buff(buff) {
}
MetalClawWriter::~MetalClawWriter() noexcept {
if (m_field != m_fields) {
oxTrace("ox::mc::MetalClawWriter::error") << "MetalClawReader: incorrect fields number given";
}
}
Error MetalClawWriter::field(const char*, int8_t *val) noexcept {
return appendInteger(*val);
}
Error MetalClawWriter::field(const char*, int16_t *val) noexcept {
return appendInteger(*val);
}
Error MetalClawWriter::field(const char*, int32_t *val) noexcept {
return appendInteger(*val);
}
Error MetalClawWriter::field(const char*, int64_t *val) noexcept {
return appendInteger(*val);
}
Error MetalClawWriter::field(const char*, uint8_t *val) noexcept {
return appendInteger(*val);
}
Error MetalClawWriter::field(const char*, uint16_t *val) noexcept {
return appendInteger(*val);
}
Error MetalClawWriter::field(const char*, uint32_t *val) noexcept {
return appendInteger(*val);
}
Error MetalClawWriter::field(const char*, uint64_t *val) noexcept {
return appendInteger(*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) noexcept {
bool fieldSet = false;
if (val.len() && (m_unionIdx == -1 || m_unionIdx == m_field)) {
// write the length
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.len());
m_buffIt += val.len();
fieldSet = true;
} else {
return OxError(MC_BUFFENDED);
}
}
oxReturnError(m_fieldPresence.set(m_field, fieldSet));
++m_field;
return OxError(0);
}
std::size_t MetalClawWriter::size() noexcept {
return m_buffIt;
}
}

View File

@@ -1,249 +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/optype.hpp>
#include <ox/model/types.hpp>
#include <ox/std/bit.hpp>
#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"
namespace ox {
class MetalClawWriter {
private:
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, int unionIdx = -1) noexcept;
~MetalClawWriter() noexcept;
[[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;
[[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;
[[nodiscard]] Error field(const char*, bool *val) noexcept;
template<typename T>
[[nodiscard]] Error field(const char*, T *val, std::size_t len);
template<typename T>
[[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>
[[nodiscard]] Error field(const char*, ox::BString<L> *val) noexcept;
[[nodiscard]] Error field(const char*, SerStr val) noexcept;
template<typename T>
[[nodiscard]] Error field(const char*, T *val);
template<typename U>
[[nodiscard]] Error field(const char*, UnionView<U> val);
template<typename T = std::nullptr_t>
void setTypeInfo(const char *name = T::TypeName, int fields = T::Fields);
std::size_t size() noexcept;
static constexpr auto opType() {
return OpType::Write;
}
private:
template<typename I>
[[nodiscard]] Error appendInteger(I val) noexcept;
};
template<std::size_t L>
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;
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;
fieldSet = true;
}
}
oxReturnError(m_fieldPresence.set(m_field, fieldSet));
m_field++;
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) {
bool fieldSet = false;
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);
writer.setTypeInfo<T>("List", len);
// write the array
for (std::size_t i = 0; i < len; i++) {
oxReturnError(writer.field("", &val[i]));
}
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*, Vector<T> *val) {
return field(nullptr, val->data(), val->size());
}
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 (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 {
oxReturnError(OxError(MC_BUFFENDED));
}
}
oxReturnError(m_fieldPresence.set(m_field, fieldSet));
m_field++;
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>
Error writeMC(uint8_t *buff, std::size_t buffLen, T *val, std::size_t *sizeOut = nullptr) {
MetalClawWriter writer(buff, buffLen);
auto err = model(&writer, val);
if (sizeOut) {
*sizeOut = writer.size();
}
return err;
}
}

View File

@@ -1,37 +0,0 @@
add_library(
OxModel
desctypes.cpp
descwrite.cpp
)
target_link_libraries(
OxModel PUBLIC
OxStd
)
if(NOT OX_BARE_METAL)
set_property(
TARGET
OxModel
PROPERTY
POSITION_INDEPENDENT_CODE ON
)
endif()
install(
FILES
descread.hpp
desctypes.hpp
descwrite.hpp
optype.hpp
model.hpp
types.hpp
walk.hpp
DESTINATION
include/ox/model
)
install(TARGETS OxModel
LIBRARY DESTINATION lib/ox
ARCHIVE DESTINATION lib/ox
)

View File

@@ -1,7 +0,0 @@
<Type> : <TypeName><FieldList>
<FieldList> : <FieldList> | <FieldList><Field>
<Field> : <FieldType><TypeID><FieldName>
<TypeID> : <TypeName> | <TypeName><Type>
<TypeName> : <string>
<FieldType> : <0: single> | <1: list>
<FieldName> : <string>

View File

@@ -1,42 +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 "desctypes.hpp"
namespace ox {
template<typename ReaderBase>
class TypeDescReader: public ReaderBase {
private:
TypeStore m_typeStore;
public:
TypeDescReader(uint8_t *buff, std::size_t buffLen);
const TypeStore &typeStore() const;
};
template<typename ReaderBase>
TypeDescReader<ReaderBase>::TypeDescReader(uint8_t *buff, std::size_t buffLen): ReaderBase(buff, buffLen) {
}
template<typename ReaderBase>
const TypeStore &TypeDescReader<ReaderBase>::typeStore() const {
return m_typeStore;
}
template<typename ReaderBase, typename T>
int readMCDef(uint8_t *buff, std::size_t buffLen, T *val) {
TypeDescReader<ReaderBase> reader(buff, buffLen);
return model(&reader, val);
}
}

View File

@@ -1,19 +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/.
*/
#include "desctypes.hpp"
namespace ox {
DescriptorField::~DescriptorField() {
if (ownsType) {
delete type;
}
}
}

View File

@@ -1,184 +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/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>;
using FieldName = ModelString;
using TypeName = ModelString;
enum class PrimitiveType: uint8_t {
UnsignedInteger = 0,
SignedInteger = 1,
Bool = 2,
// 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
struct DescriptorType *type = nullptr;
FieldName fieldName;
int subscriptLevels = 0;
// do not serialize the following
TypeName typeName; // gives reference to type for lookup if type is null
bool ownsType = false;
constexpr DescriptorField() noexcept = default;
/**
* Allow for explicit copying.
*/
constexpr explicit DescriptorField(const DescriptorField &other) noexcept {
type = other.type;
fieldName = other.fieldName;
subscriptLevels = other.subscriptLevels;
typeName = other.typeName;
ownsType = false; // is copy, only owns type if move
}
constexpr DescriptorField(DescriptorType *type, const FieldName &fieldName, int subscriptLevels, const TypeName &typeName, bool ownsType) noexcept {
this->type = type;
this->fieldName = fieldName;
this->subscriptLevels = subscriptLevels;
this->typeName = typeName;
this->ownsType = ownsType;
}
constexpr DescriptorField(DescriptorField &&other) noexcept {
type = other.type;
fieldName = other.fieldName;
subscriptLevels = other.subscriptLevels;
typeName = other.typeName;
ownsType = other.ownsType;
other.type = {};
other.fieldName = "";
other.subscriptLevels = {};
other.typeName = "";
other.ownsType = {};
}
~DescriptorField();
const DescriptorField &operator=(DescriptorField &&other) noexcept {
type = other.type;
fieldName = other.fieldName;
subscriptLevels = other.subscriptLevels;
typeName = other.typeName;
ownsType = other.ownsType;
other.type = {};
other.fieldName = "";
other.subscriptLevels = {};
other.typeName = "";
other.ownsType = {};
return *this;
}
};
using FieldList = Vector<DescriptorField>;
struct DescriptorType {
static constexpr auto TypeVersion = 1;
TypeName typeName;
PrimitiveType primitiveType;
// fieldList only applies to structs
FieldList fieldList;
// - number of bytes for integer and float types
// - number of fields for structs and lists
int64_t length = 0;
bool preloadable = false;
DescriptorType() = default;
DescriptorType(TypeName tn, PrimitiveType t, int b): typeName(tn), primitiveType(t), length(b) {
}
DescriptorType(TypeName tn, PrimitiveType t, FieldList fl): typeName(tn), primitiveType(t), fieldList(fl) {
}
};
template<typename T>
Error model(T *io, DescriptorType *type) {
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>
Error modelWrite(T *io, DescriptorField *field) {
auto err = OxError(0);
io->setTypeInfo("ox::DescriptorField", 4);
if (field->ownsType) {
BString<2> empty = "";
oxReturnError(io->field("typeName", SerStr(&empty)));
oxReturnError(io->field("type", field->type));
} else {
oxReturnError(io->field("typeName", SerStr(&field->type->typeName)));
oxReturnError(io->field("type", static_cast<decltype(field->type)>(nullptr)));
}
oxReturnError(io->field("fieldName", &field->fieldName));
// defaultValue is unused now, but leave placeholder for backwards compatibility
int DefaultValue = 0;
oxReturnError(io->field("defaultValue", &DefaultValue));
return err;
}
template<typename T>
Error modelRead(T *io, DescriptorField *field) {
auto err = OxError(0);
auto &typeStore = io->typeStore();
io->setTypeInfo("ox::DescriptorField", 4);
err |= io->field("typeName", &field->typeName);
if (field->typeName == "") {
field->ownsType = true;
if (field->type == nullptr) {
field->type = new DescriptorType;
}
err |= io->field("type", field->type);
typeStore[field->type->typeName] = field->type;
} else {
// should be empty, so discard
DescriptorType t;
err |= io->field("type", &t);
field->type = typeStore[field->typeName];
}
err |= io->field("fieldName", &field->fieldName);
// defaultValue is unused now, but placeholder for backwards compatibility
err |= io->field("defaultValue", nullptr);
return err;
}
using TypeStore = ox::HashMap<ModelString, DescriptorType*>;
}

View File

@@ -1,163 +0,0 @@
/*
* Copyright 2015 - 2019 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/typeinfo.hpp>
#include "descwrite.hpp"
namespace ox {
namespace detail {
struct preloadable_type {
static constexpr auto Preloadable = true;
};
struct non_preloadable_type {
static constexpr auto Preloadable = false;
};
struct non_preloadable_type2 {
};
static_assert(preloadable<preloadable_type>::value);
static_assert(!preloadable<non_preloadable_type>::value);
static_assert(!preloadable<non_preloadable_type2>::value);
}
static_assert([] {
int i = 0;
return indirectionLevels(i) == 0;
}(), "indirectionLevels broken: indirectionLevels(int)");
static_assert([] {
int i = 0;
return indirectionLevels(&i) == 1;
}(), "indirectionLevels broken: indirectionLevels(int*)");
static_assert([] {
int i[2] = {};
return indirectionLevels(i) == 1;
}(), "indirectionLevels broken: indirectionLevels(int[])");
TypeDescWriter::TypeDescWriter(TypeStore *typeStore) {
if (!typeStore) {
m_typeStoreOwnerRef = new TypeStore;
typeStore = m_typeStoreOwnerRef;
}
m_typeStore = typeStore;
}
TypeDescWriter::~TypeDescWriter() {
// does not own it's elements
delete m_typeStoreOwnerRef;
}
DescriptorType *TypeDescWriter::type(int8_t*, bool *alreadyExisted) {
constexpr auto TypeName = "B:int8_t";
constexpr auto PT = PrimitiveType::SignedInteger;
constexpr auto Bytes = 1;
return getType(TypeName, PT, Bytes, alreadyExisted);
}
DescriptorType *TypeDescWriter::type(int16_t*, bool *alreadyExisted) {
constexpr auto TypeName = "B:int16_t";
constexpr auto PT = PrimitiveType::SignedInteger;
constexpr auto Bytes = 2;
return getType(TypeName, PT, Bytes, alreadyExisted);
}
DescriptorType *TypeDescWriter::type(int32_t*, bool *alreadyExisted) {
constexpr auto TypeName = "B:int32_t";
constexpr auto PT = PrimitiveType::SignedInteger;
constexpr auto Bytes = 4;
return getType(TypeName, PT, Bytes, alreadyExisted);
}
DescriptorType *TypeDescWriter::type(int64_t*, bool *alreadyExisted) {
constexpr auto TypeName = "B:int64_t";
constexpr auto PT = PrimitiveType::SignedInteger;
constexpr auto Bytes = 8;
return getType(TypeName, PT, Bytes, alreadyExisted);
}
DescriptorType *TypeDescWriter::type(uint8_t*, bool *alreadyExisted) {
constexpr auto TypeName = "B:uint8_t";
constexpr auto PT = PrimitiveType::UnsignedInteger;
constexpr auto Bytes = 1;
return getType(TypeName, PT, Bytes, alreadyExisted);
}
DescriptorType *TypeDescWriter::type(uint16_t*, bool *alreadyExisted) {
constexpr auto TypeName = "B:uint16_t";
constexpr auto PT = PrimitiveType::UnsignedInteger;
constexpr auto Bytes = 2;
return getType(TypeName, PT, Bytes, alreadyExisted);
}
DescriptorType *TypeDescWriter::type(uint32_t*, bool *alreadyExisted) {
constexpr auto TypeName = "B:uint32_t";
constexpr auto PT = PrimitiveType::UnsignedInteger;
constexpr auto Bytes = 4;
return getType(TypeName, PT, Bytes, alreadyExisted);
}
DescriptorType *TypeDescWriter::type(uint64_t*, bool *alreadyExisted) {
constexpr auto TypeName = "B:uint64_t";
constexpr auto PT = PrimitiveType::UnsignedInteger;
constexpr auto Bytes = 8;
return getType(TypeName, PT, Bytes, alreadyExisted);
}
DescriptorType *TypeDescWriter::type(char*, bool *alreadyExisted) {
constexpr auto TypeName = "B:string";
constexpr auto PT = PrimitiveType::String;
return getType(TypeName, PT, 0, alreadyExisted);
}
DescriptorType *TypeDescWriter::type(SerStr, bool *alreadyExisted) {
constexpr auto TypeName = "B:string";
constexpr auto PT = PrimitiveType::String;
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;
constexpr auto Bytes = 0;
return getType(TypeName, PT, Bytes, alreadyExisted);
}
DescriptorType *TypeDescWriter::getType(TypeName tn, PrimitiveType pt, int b, bool *alreadyExisted) {
if (m_typeStore->contains(tn)) {
*alreadyExisted = true;
auto type = m_typeStore->at(tn);
oxAssert(type != nullptr, "TypeDescWriter::getType returning null DescriptorType");
return type;
} else {
*alreadyExisted = false;
auto &t = m_typeStore->at(tn);
if (!t) {
t = new DescriptorType;
}
t->typeName = tn;
t->primitiveType = pt;
t->length = b;
return t;
}
}
}

View File

@@ -1,254 +0,0 @@
/*
* Copyright 2015 - 2019 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/std/byteswap.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>
#include "desctypes.hpp"
#include "optype.hpp"
#include "types.hpp"
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;
}
template<typename T>
static constexpr int indirectionLevels(T *t) {
return 1 + indirectionLevels(*t);
}
class TypeDescWriter {
private:
struct NameCatcher {
TypeName name;
template<typename T = std::nullptr_t>
constexpr void setTypeInfo(const char *n = T::TypeName, int = T::Fields) noexcept {
this->name = n;
}
template<typename T>
[[nodiscard]] constexpr ox::Error field(const char*, T*, std::size_t) noexcept {
return OxError(0);
}
template<typename T>
[[nodiscard]] constexpr ox::Error field(const char*, T) noexcept {
return OxError(0);
}
static constexpr auto opType() {
return OpType::WriteDefinition;
}
};
TypeStore *m_typeStoreOwnerRef = nullptr;
TypeStore *m_typeStore = nullptr;
DescriptorType *m_type = nullptr;
public:
explicit TypeDescWriter(TypeStore *typeStore = nullptr);
~TypeDescWriter();
template<typename T>
[[nodiscard]] ox::Error field(const char *name, T *val, std::size_t valLen);
template<typename T>
[[nodiscard]] ox::Error field(const char *name, T val);
template<typename T>
[[nodiscard]] ox::Error field(const char *name, T *val);
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 auto opType() {
return OpType::WriteDefinition;
}
private:
DescriptorType *type(int8_t *val, bool *alreadyExisted);
DescriptorType *type(int16_t *val, bool *alreadyExisted);
DescriptorType *type(int32_t *val, bool *alreadyExisted);
DescriptorType *type(int64_t *val, bool *alreadyExisted);
DescriptorType *type(uint8_t *val, bool *alreadyExisted);
DescriptorType *type(uint16_t *val, bool *alreadyExisted);
DescriptorType *type(uint32_t *val, bool *alreadyExisted);
DescriptorType *type(uint64_t *val, bool *alreadyExisted);
DescriptorType *type(bool *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);
};
// array handler
template<typename T>
ox::Error TypeDescWriter::field(const char *name, T *val, std::size_t) {
if (m_type) {
constexpr typename ox::remove_pointer<decltype(val)>::type *p = nullptr;
bool alreadyExisted = false;
const auto t = type(p, &alreadyExisted);
oxAssert(t != nullptr, "field(const char *name, T *val, std::size_t): Type not found or generated");
if (t == nullptr) {
type(p, &alreadyExisted);
}
m_type->fieldList.emplace_back(t, name, indirectionLevels(val), alreadyExisted ? t->typeName : "", !alreadyExisted);
return OxError(0);
}
return OxError(1);
}
template<typename T>
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>
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<std::size_t sz>
DescriptorType *TypeDescWriter::type(BString<sz> *val, bool *alreadyExisted) {
return type(SerStr(val), alreadyExisted);
}
template<typename T>
DescriptorType *TypeDescWriter::type(T *val, bool *alreadyExisted) {
NameCatcher nc;
oxLogError(model(&nc, val));
if (m_typeStore->contains(nc.name)) {
*alreadyExisted = true;
return m_typeStore->at(nc.name);
} else {
TypeDescWriter dw(m_typeStore);
oxLogError(model(&dw, val));
*alreadyExisted = false;
return dw.m_type;
}
}
template<typename T>
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 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) {
oxReturnError(writeType(buff, buffLen, def.value, sizeOut));
}
delete def.value;
return err;
}
}

View File

@@ -1,15 +0,0 @@
/*
* Copyright 2015 - 2019 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 "descread.hpp"
#include "desctypes.hpp"
#include "descwrite.hpp"
#include "types.hpp"
#include "walk.hpp"

View File

@@ -1,54 +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/std/assert.hpp>
#include <ox/std/error.hpp>
#include <ox/std/strops.hpp>
namespace ox {
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>
[[nodiscard]] ox::Error modelRead(T*, O*) {
return OxError(1, "Model: modelRead not implemented");
}
template<typename T, typename O>
[[nodiscard]] ox::Error modelWrite(T*, O*) {
return OxError(1, "Model: modelWrite not implemented");
}
template<typename T, typename O>
[[nodiscard]] ox::Error modelWriteDefinition(T*, O*) {
return OxError(1, "Model: modelWriteDefinition not implemented");
}
template<typename T, typename O>
[[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);
}
oxAssert(err, "Missing model function");
return err;
}
}

View File

@@ -1,93 +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/std/bstring.hpp>
#include <ox/std/strops.hpp>
#include <ox/std/types.hpp>
#include <ox/std/typetraits.hpp>
namespace ox {
class SerStr {
protected:
int m_cap = 0;
char *m_str = nullptr;
char **m_tgt = nullptr;
public:
template<std::size_t sz>
constexpr SerStr(BString<sz> *str) noexcept {
m_str = str->data();
m_cap = str->cap();
}
constexpr SerStr(char *str, int cap) noexcept {
m_str = str;
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(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 m_str ? ox_strlen(m_str) : 0;
}
constexpr int cap() noexcept {
return m_cap;
}
};
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

@@ -1,155 +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/std/error.hpp>
#include "desctypes.hpp"
namespace ox {
template<typename Reader, typename T>
class DataWalker {
template<typename ReaderBase, typename FH>
friend ox::Error parseField(const DescriptorField &field, ReaderBase *rdr, DataWalker<ReaderBase, FH> *walker);
private:
Vector<const DescriptorType*> m_typeStack;
T m_fieldHandler;
Vector<FieldName> m_path;
Vector<TypeName> m_typePath;
public:
DataWalker(DescriptorType *type, T fieldHandler);
[[nodiscard]] const DescriptorType *type() const noexcept;
[[nodiscard]] ox::Error read(const DescriptorField&, Reader *rdr);
protected:
void pushNamePath(FieldName fn);
void popNamePath();
void pushType(const DescriptorType *type);
void popType();
};
template<typename Reader, typename T>
DataWalker<Reader, T>::DataWalker(DescriptorType *type, T fieldHandler): m_fieldHandler(fieldHandler) {
m_typeStack.push_back(type);
}
template<typename Reader, typename T>
const DescriptorType *DataWalker<Reader, T>::type() const noexcept {
return m_typeStack.back();
}
template<typename Reader, typename T>
ox::Error DataWalker<Reader, T>::read(const DescriptorField &f, Reader *rdr) {
// get const ref of paths
const auto &pathCr = m_path;
const auto &typePathCr = m_typePath;
return m_fieldHandler(pathCr, typePathCr, f, rdr);
}
template<typename Reader, typename T>
void DataWalker<Reader, T>::pushNamePath(FieldName fn) {
m_path.push_back(fn);
}
template<typename Reader, typename T>
void DataWalker<Reader, T>::popNamePath() {
m_path.pop_back();
}
template<typename Reader, typename T>
void DataWalker<Reader, T>::pushType(const DescriptorType *type) {
m_typeStack.push_back(type);
}
template<typename Reader, typename T>
void DataWalker<Reader, T>::popType() {
m_typeStack.pop_back();
}
template<typename Reader, typename FH>
static ox::Error parseField(const DescriptorField &field, Reader *rdr, DataWalker<Reader, FH> *walker) {
walker->pushNamePath(field.fieldName);
if (field.subscriptLevels) {
// add array handling
const auto [arrayLen, err] = rdr->arrayLength(field.fieldName.c_str(), true);
oxReturnError(err);
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 (std::size_t i = 0; i < arrayLen; i++) {
subscript = "[";
subscript += i;
subscript += "]";
walker->pushNamePath(subscript);
oxReturnError(parseField(f, &child, walker));
walker->popNamePath();
}
rdr->nextField();
} else {
switch (field.type->primitiveType) {
case PrimitiveType::UnsignedInteger:
case PrimitiveType::SignedInteger:
case PrimitiveType::Bool:
case PrimitiveType::String:
oxReturnError(walker->read(field, rdr));
break;
case PrimitiveType::Struct:
case PrimitiveType::Union:
if (rdr->fieldPresent(field.fieldName.c_str())) {
auto child = rdr->child(field.fieldName.c_str());
walker->pushType(field.type);
oxReturnError(model(&child, walker));
walker->popType();
rdr->nextField();
} else {
// skip and discard absent field
int discard;
oxReturnError(rdr->field(field.fieldName.c_str(), &discard));
}
break;
}
}
walker->popNamePath();
return OxError(0);
}
template<typename Reader, typename FH>
ox::Error model(Reader *rdr, DataWalker<Reader, FH> *walker) {
auto type = walker->type();
if (!type) {
return OxError(1);
}
auto typeName = type->typeName.c_str();
auto &fields = type->fieldList;
rdr->setTypeInfo(typeName, fields.size());
for (std::size_t i = 0; i < fields.size(); i++) {
oxReturnError(parseField(fields[i], rdr, 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);
}
}

View File

@@ -1,40 +0,0 @@
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()

View File

@@ -1,12 +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 "read.hpp"
#include "write.hpp"

View File

@@ -1,255 +0,0 @@
/*
* 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;
}
}

View File

@@ -1,204 +0,0 @@
/*
* 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)};
}
}

View File

@@ -1,13 +0,0 @@
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)

View File

@@ -1,288 +0,0 @@
/*
* 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;
}

View File

@@ -1,118 +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/.
*/
#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];
}
}
}

View File

@@ -1,162 +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 <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

@@ -1,11 +0,0 @@
install(
FILES
nodebuffer.hpp
ptr.hpp
DESTINATION
include/ox/ptrarith
)
if(OX_RUN_TESTS)
add_subdirectory(test)
endif()

View File

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

View File

@@ -1,230 +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/std/std.hpp>
namespace ox::ptrarith {
template<typename T, typename size_t, size_t minOffset = 1>
class Ptr {
private:
uint8_t *m_dataStart = nullptr;
size_t m_dataSize = 0;
size_t m_itemOffset = 0;
size_t m_itemSize = 0;
// this should be removed later on, but the excessive validation is
// desirable during during heavy development
mutable uint8_t m_validated = false;
public:
inline Ptr() = default;
inline Ptr(std::nullptr_t);
inline Ptr(void *dataStart, size_t dataSize, size_t itemStart, size_t itemSize = sizeof(T), size_t itemTypeSize = sizeof(T));
[[nodiscard]] inline bool valid() const;
inline size_t size() const;
inline size_t offset() const;
inline size_t end();
inline const T *get() const;
inline T *get();
inline const T *operator->() const;
inline T *operator->();
inline operator const T*() const;
inline operator T*();
inline const T &operator*() const;
inline T &operator*();
inline operator size_t() const;
inline bool operator==(const Ptr<T, size_t, minOffset> &other) const;
inline bool operator!=(const Ptr<T, size_t, minOffset> &other) const;
template<typename SubT>
inline const Ptr<SubT, size_t, sizeof(T)> subPtr(size_t offset, size_t size) const;
template<typename SubT>
inline const Ptr<SubT, size_t, sizeof(T)> subPtr(size_t offset) const;
template<typename SubT>
inline Ptr<SubT, size_t, sizeof(T)> subPtr(size_t offset, size_t size);
template<typename SubT>
inline Ptr<SubT, size_t, sizeof(T)> subPtr(size_t offset);
template<typename SubT>
inline const Ptr<SubT, size_t, minOffset> to() const;
};
template<typename T, typename size_t, size_t minOffset>
inline Ptr<T, size_t, minOffset>::Ptr(std::nullptr_t) {
}
template<typename T, typename size_t, size_t minOffset>
inline Ptr<T, size_t, minOffset>::Ptr(void *dataStart, size_t dataSize, size_t itemStart, size_t itemSize, size_t itemTypeSize) {
// do some sanity checks before assuming this is valid
if (itemSize >= itemTypeSize &&
dataStart &&
itemStart >= minOffset &&
itemStart + itemSize <= dataSize) {
m_dataStart = reinterpret_cast<uint8_t*>(dataStart);
m_dataSize = dataSize;
m_itemOffset = itemStart;
m_itemSize = itemSize;
}
}
template<typename T, typename size_t, size_t minOffset>
inline bool Ptr<T, size_t, minOffset>::valid() const {
m_validated = m_dataStart != nullptr;
return m_validated;
}
template<typename T, typename size_t, size_t minOffset>
inline size_t Ptr<T, size_t, minOffset>::size() const {
return m_itemSize;
}
template<typename T, typename size_t, size_t minOffset>
inline size_t Ptr<T, size_t, minOffset>::offset() const {
return m_itemOffset;
}
template<typename T, typename size_t, size_t minOffset>
inline size_t Ptr<T, size_t, minOffset>::end() {
return m_itemOffset + m_itemSize;
}
template<typename T, typename size_t, size_t minOffset>
inline const T *Ptr<T, size_t, minOffset>::get() const {
oxAssert(m_validated, "Unvalidated pointer access. (ox::fs::Ptr::get())");
oxAssert(valid(), "Invalid pointer access. (ox::fs::Ptr::get())");
return reinterpret_cast<T*>(m_dataStart + m_itemOffset);
}
template<typename T, typename size_t, size_t minOffset>
inline T *Ptr<T, size_t, minOffset>::get() {
oxAssert(m_validated, "Unvalidated pointer access. (ox::fs::Ptr::get())");
oxAssert(valid(), "Invalid pointer access. (ox::fs::Ptr::get())");
return reinterpret_cast<T*>(m_dataStart + m_itemOffset);
}
template<typename T, typename size_t, size_t minOffset>
inline const T *Ptr<T, size_t, minOffset>::operator->() const {
oxAssert(m_validated, "Unvalidated pointer access. (ox::fs::Ptr::operator->())");
oxAssert(valid(), "Invalid pointer access. (ox::fs::Ptr::operator->())");
return reinterpret_cast<T*>(m_dataStart + m_itemOffset);
}
template<typename T, typename size_t, size_t minOffset>
inline T *Ptr<T, size_t, minOffset>::operator->() {
oxAssert(m_validated, "Unvalidated pointer access. (ox::fs::Ptr::operator->())");
oxAssert(valid(), "Invalid pointer access. (ox::fs::Ptr::operator->())");
return reinterpret_cast<T*>(m_dataStart + m_itemOffset);
}
template<typename T, typename size_t, size_t minOffset>
inline Ptr<T, size_t, minOffset>::operator const T*() const {
oxAssert(m_validated, "Unvalidated pointer access. (ox::fs::Ptr::operator const T*())");
oxAssert(valid(), "Invalid pointer access. (ox::fs::Ptr::operator const T*())");
return reinterpret_cast<T*>(m_dataStart + m_itemOffset);
}
template<typename T, typename size_t, size_t minOffset>
inline Ptr<T, size_t, minOffset>::operator T*() {
oxAssert(m_validated, "Unvalidated pointer access. (ox::fs::Ptr::operator T*())");
oxAssert(valid(), "Invalid pointer access. (ox::fs::Ptr::operator T*())");
return reinterpret_cast<T*>(m_dataStart + m_itemOffset);
}
template<typename T, typename size_t, size_t minOffset>
inline const T &Ptr<T, size_t, minOffset>::operator*() const {
oxAssert(m_validated, "Unvalidated pointer dereference. (ox::fs::Ptr::operator*())");
oxAssert(valid(), "Invalid pointer dereference. (ox::fs::Ptr::operator*())");
return *reinterpret_cast<T*>(m_dataStart + m_itemOffset);
}
template<typename T, typename size_t, size_t minOffset>
inline T &Ptr<T, size_t, minOffset>::operator*() {
oxAssert(m_validated, "Unvalidated pointer dereference. (ox::fs::Ptr::operator*())");
oxAssert(valid(), "Invalid pointer dereference. (ox::fs::Ptr::operator*())");
return *reinterpret_cast<T*>(m_dataStart + m_itemOffset);
}
template<typename T, typename size_t, size_t minOffset>
inline Ptr<T, size_t, minOffset>::operator size_t() const {
if (m_dataStart && m_itemOffset) {
return m_itemOffset;
}
return 0;
}
template<typename T, typename size_t, size_t minOffset>
inline bool Ptr<T, size_t, minOffset>::operator==(const Ptr<T, size_t, minOffset> &other) const {
return m_dataStart == other.m_dataStart &&
m_itemOffset == other.m_itemOffset &&
m_itemSize == other.m_itemSize;
}
template<typename T, typename size_t, size_t minOffset>
inline bool Ptr<T, size_t, minOffset>::operator!=(const Ptr<T, size_t, minOffset> &other) const {
return m_dataStart != other.m_dataStart ||
m_itemOffset != other.m_itemOffset ||
m_itemSize != other.m_itemSize;
}
template<typename T, typename size_t, size_t minOffset>
template<typename SubT>
inline const Ptr<SubT, size_t, sizeof(T)> Ptr<T, size_t, minOffset>::subPtr(size_t offset, size_t size) const {
return Ptr<SubT, size_t, sizeof(T)>(get(), this->size(), offset, size);
}
template<typename T, typename size_t, size_t minOffset>
template<typename SubT>
inline const Ptr<SubT, size_t, sizeof(T)> Ptr<T, size_t, minOffset>::subPtr(size_t offset) const {
oxTrace("ox::fs::Ptr::subPtr") << m_itemOffset << this->size() << offset << m_itemSize << (m_itemSize - offset);
return subPtr<SubT>(offset, m_itemSize - offset);
}
template<typename T, typename size_t, size_t minOffset>
template<typename SubT>
inline Ptr<SubT, size_t, sizeof(T)> Ptr<T, size_t, minOffset>::subPtr(size_t offset, size_t size) {
return Ptr<SubT, size_t, sizeof(T)>(get(), this->size(), offset, size);
}
template<typename T, typename size_t, size_t minOffset>
template<typename SubT>
inline Ptr<SubT, size_t, sizeof(T)> Ptr<T, size_t, minOffset>::subPtr(size_t offset) {
oxTrace("ox::fs::Ptr::subPtr") << m_itemOffset << this->size() << offset << m_itemSize << (m_itemSize - offset);
return subPtr<SubT>(offset, m_itemSize - offset);
}
template<typename T, typename size_t, size_t minOffset>
template<typename SubT>
inline const Ptr<SubT, size_t, minOffset> Ptr<T, size_t, minOffset>::to() const {
return Ptr<SubT, size_t, minOffset>(m_dataStart, m_dataSize, m_itemOffset, m_itemSize);
}
}

View File

@@ -1,11 +0,0 @@
add_executable(
PtrArithTests
tests.cpp
)
target_link_libraries(
PtrArithTests
OxStd
)
add_test("Test\\ PtrArith::setSize" PtrArithTests PtrArith::setSize)

View File

@@ -1,77 +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/.
*/
// make sure asserts are enabled for the test file
#undef NDEBUG
#include <iostream>
#include <assert.h>
#include <array>
#include <map>
#include <vector>
#include <string>
#include <ox/ptrarith/nodebuffer.hpp>
#include <ox/fs/fs.hpp>
#include <ox/std/std.hpp>
#include <ox/fs/filestore/filestoretemplate.hpp>
#include <ox/fs/filesystem/filesystem.hpp>
using namespace std;
using namespace ox;
template<typename T>
struct OX_PACKED NodeType: public ox::ptrarith::Item<T> {
public:
size_t fullSize() const {
return this->size() + sizeof(*this);
}
};
map<string, int(*)(string)> tests = {
{
{
"PtrArith::setSize",
[](string) {
using BuffPtr_t = uint32_t;
std::vector<char> buff(5 * ox::units::MB);
auto buffer = new (buff.data()) ox::ptrarith::NodeBuffer<BuffPtr_t, NodeType<BuffPtr_t>>(buff.size());
using String = BString<6>;
auto a1 = buffer->malloc(sizeof(String));
auto a2 = buffer->malloc(sizeof(String));
oxAssert(a1.valid(), "Allocation 1 failed.");
oxAssert(a2.valid(), "Allocation 2 failed.");
auto &s1 = *new (buffer->dataOf<String>(a1)) String("asdf");
auto &s2 = *new (buffer->dataOf<String>(a2)) String("aoeu");
oxTrace("test") << "s1: " << s1.c_str();
oxTrace("test") << "s2: " << s2.c_str();
oxAssert(s1 == "asdf", "Allocation 1 not as expected.");
oxAssert(s2 == "aoeu", "Allocation 2 not as expected.");
oxAssert(buffer->free(a1), "Free failed.");
oxAssert(buffer->free(a2), "Free failed.");
oxAssert(buffer->setSize(buffer->size() - buffer->available()), "Resize failed.");
return 0;
}
},
},
};
int main(int argc, const char **args) {
int retval = -1;
if (argc > 1) {
auto testName = args[1];
string testArg = "";
if (args[2]) {
testArg = args[2];
}
if (tests.find(testName) != tests.end()) {
retval = tests[testName](testArg);
}
}
return retval;
}

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