From 8559ab53ccc74e63924b4a9a31bc91ee1dafefa9 Mon Sep 17 00:00:00 2001 From: Gary Talent Date: Wed, 11 Oct 2017 19:20:46 -0500 Subject: [PATCH] Squashed 'deps/ox/' content from commit c63e0c1 git-subtree-dir: deps/ox git-subtree-split: c63e0c1d72f30cfb7b3076c69744b0f53d4d79b1 --- .gitignore | 5 + .liccor.yml | 9 + .travis.yml | 11 + CMakeLists.txt | 44 ++ LICENSE | 373 +++++++++++ Makefile | 72 ++ OxConfig.cmake | 11 + build/Makefile | 32 + cmake/Modules/FindJansson.cmake | 49 ++ cmake/Modules/GBA.cmake | 41 ++ cmake/Modules/Mingw.cmake | 18 + cmake/Modules/address_sanitizer.cmake | 52 ++ scripts/cibuild | 8 + scripts/setup_build | 30 + src/CMakeLists.txt | 3 + src/ox/CMakeLists.txt | 9 + src/ox/clargs/CMakeLists.txt | 27 + src/ox/clargs/clargs.cpp | 53 ++ src/ox/clargs/clargs.hpp | 32 + src/ox/fs/CMakeLists.txt | 54 ++ src/ox/fs/filestore.hpp | 923 ++++++++++++++++++++++++++ src/ox/fs/filesystem.cpp | 73 ++ src/ox/fs/filesystem.hpp | 884 ++++++++++++++++++++++++ src/ox/fs/oxfstool.cpp | 395 +++++++++++ src/ox/fs/pathiterator.cpp | 107 +++ src/ox/fs/pathiterator.hpp | 42 ++ src/ox/fs/test/CMakeLists.txt | 69 ++ src/ox/fs/test/filestore_format.cpp | 18 + src/ox/fs/test/filestoreio.cpp | 68 ++ src/ox/fs/test/filesystem_format.cpp | 23 + src/ox/fs/test/tests.cpp | 346 ++++++++++ src/ox/fs/toollib.cpp | 33 + src/ox/fs/toollib.hpp | 14 + src/ox/log/CMakeLists.txt | 25 + src/ox/log/log.cpp | 105 +++ src/ox/log/log.hpp | 27 + src/ox/mc/CMakeLists.txt | 34 + src/ox/mc/err.hpp | 19 + src/ox/mc/mc.hpp | 12 + src/ox/mc/presencemask.cpp | 49 ++ src/ox/mc/presencemask.hpp | 32 + src/ox/mc/read.cpp | 55 ++ src/ox/mc/read.hpp | 154 +++++ src/ox/mc/test/CMakeLists.txt | 16 + src/ox/mc/test/tests.cpp | 158 +++++ src/ox/mc/write.cpp | 54 ++ src/ox/mc/write.hpp | 152 +++++ src/ox/std/CMakeLists.txt | 38 ++ src/ox/std/bitops.hpp | 19 + src/ox/std/byteswap.hpp | 141 ++++ src/ox/std/memops.cpp | 41 ++ src/ox/std/memops.hpp | 16 + src/ox/std/random.cpp | 40 ++ src/ox/std/random.hpp | 30 + src/ox/std/std.hpp | 16 + src/ox/std/string.hpp | 128 ++++ src/ox/std/strops.cpp | 91 +++ src/ox/std/strops.hpp | 27 + src/ox/std/test/CMakeLists.txt | 69 ++ src/ox/std/test/byteswap_test.cpp | 39 ++ src/ox/std/test/strops_test.cpp | 79 +++ src/ox/std/test/tests.cpp | 50 ++ src/ox/std/types.hpp | 40 ++ 63 files changed, 5684 insertions(+) create mode 100644 .gitignore create mode 100644 .liccor.yml create mode 100644 .travis.yml create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 OxConfig.cmake create mode 100644 build/Makefile create mode 100644 cmake/Modules/FindJansson.cmake create mode 100644 cmake/Modules/GBA.cmake create mode 100644 cmake/Modules/Mingw.cmake create mode 100644 cmake/Modules/address_sanitizer.cmake create mode 100755 scripts/cibuild create mode 100755 scripts/setup_build create mode 100644 src/CMakeLists.txt create mode 100644 src/ox/CMakeLists.txt create mode 100644 src/ox/clargs/CMakeLists.txt create mode 100644 src/ox/clargs/clargs.cpp create mode 100644 src/ox/clargs/clargs.hpp create mode 100644 src/ox/fs/CMakeLists.txt create mode 100644 src/ox/fs/filestore.hpp create mode 100644 src/ox/fs/filesystem.cpp create mode 100644 src/ox/fs/filesystem.hpp create mode 100644 src/ox/fs/oxfstool.cpp create mode 100644 src/ox/fs/pathiterator.cpp create mode 100644 src/ox/fs/pathiterator.hpp create mode 100644 src/ox/fs/test/CMakeLists.txt create mode 100644 src/ox/fs/test/filestore_format.cpp create mode 100644 src/ox/fs/test/filestoreio.cpp create mode 100644 src/ox/fs/test/filesystem_format.cpp create mode 100644 src/ox/fs/test/tests.cpp create mode 100644 src/ox/fs/toollib.cpp create mode 100644 src/ox/fs/toollib.hpp create mode 100644 src/ox/log/CMakeLists.txt create mode 100644 src/ox/log/log.cpp create mode 100644 src/ox/log/log.hpp create mode 100644 src/ox/mc/CMakeLists.txt create mode 100644 src/ox/mc/err.hpp create mode 100644 src/ox/mc/mc.hpp create mode 100644 src/ox/mc/presencemask.cpp create mode 100644 src/ox/mc/presencemask.hpp create mode 100644 src/ox/mc/read.cpp create mode 100644 src/ox/mc/read.hpp create mode 100644 src/ox/mc/test/CMakeLists.txt create mode 100644 src/ox/mc/test/tests.cpp create mode 100644 src/ox/mc/write.cpp create mode 100644 src/ox/mc/write.hpp create mode 100644 src/ox/std/CMakeLists.txt create mode 100644 src/ox/std/bitops.hpp create mode 100644 src/ox/std/byteswap.hpp create mode 100644 src/ox/std/memops.cpp create mode 100644 src/ox/std/memops.hpp create mode 100644 src/ox/std/random.cpp create mode 100644 src/ox/std/random.hpp create mode 100644 src/ox/std/std.hpp create mode 100644 src/ox/std/string.hpp create mode 100644 src/ox/std/strops.cpp create mode 100644 src/ox/std/strops.hpp create mode 100644 src/ox/std/test/CMakeLists.txt create mode 100644 src/ox/std/test/byteswap_test.cpp create mode 100644 src/ox/std/test/strops_test.cpp create mode 100644 src/ox/std/test/tests.cpp create mode 100644 src/ox/std/types.hpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..41c3c78f --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +build/current +build/gba +build/*-debug +build/*-release +tags diff --git a/.liccor.yml b/.liccor.yml new file mode 100644 index 00000000..55025146 --- /dev/null +++ b/.liccor.yml @@ -0,0 +1,9 @@ +--- +source: +- src +copyright_notice: |- + Copyright 2015 - 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/. diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..3c7d22c4 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,11 @@ +language: cpp +sudo: false +dist: trusty +compiler: + - clang + - gcc +addons: + apt: + packages: + - cmake +script: ./scripts/cibuild diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..dc872d29 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,44 @@ +cmake_minimum_required(VERSION 2.8) + +project(Ox) + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules) +include(address_sanitizer) + +set(OX_BUILD_EXEC "ON" CACHE STRING "Build executables (ON/OFF)") +set(OX_RUN_TESTS "ON" CACHE STRING "Run tests (ON/OFF)") +set(OX_USE_STDLIB "ON" CACHE STRING "Build libraries that need the std lib (ON/OFF)") + +# can't run tests without building them +if(OX_BUILD_EXEC STREQUAL "OFF" OR OX_USE_STDLIB STREQUAL "OFF") + set(OX_BUILD_EXEC "OFF") + set(OX_RUN_TESTS "OFF") +endif() + +if(NOT MSVC) + add_definitions( + -std=c++11 + -Wall + -nostdlib + -fno-exceptions + -fno-rtti + -Wsign-compare + -Wunused-variable + #--analyze + #-Os # GCC size optimization flag + ) + + if (CMAKE_BUILD_TYPE STREQUAL "Release") + add_definitions( + -Werror + ) + endif() +endif(NOT MSVC) + +enable_testing() + +include_directories("src") + +install(FILES OxConfig.cmake DESTINATION lib/ox) + +add_subdirectory(src) diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..14e2f777 --- /dev/null +++ b/LICENSE @@ -0,0 +1,373 @@ +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. diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..3c2b781f --- /dev/null +++ b/Makefile @@ -0,0 +1,72 @@ +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 gmake),) + MAKE=gmake -s +else + MAKE=make -s +endif +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 + +make: + ${ENV_RUN} ${MAKE} -j -C build HOST_ENV=${HOST_ENV} +preinstall: + ${ENV_RUN} ${MAKE} -j -C build ARGS="preinstall" HOST_ENV=${HOST_ENV} +install: + ${ENV_RUN} ${MAKE} -j -C build ARGS="install" HOST_ENV=${HOST_ENV} +clean: + ${ENV_RUN} ${MAKE} -j -C build ARGS="clean" HOST_ENV=${HOST_ENV} +purge: + ${ENV_RUN} rm -rf $(shell find build -mindepth 1 -maxdepth 1 -type d) +test: + ${ENV_RUN} ${MAKE} -j -C build ARGS="test" HOST_ENV=${HOST_ENV} +run: make + ./build/current/src/wombat/wombat -debug +gdb: make + gdb ./build/current/src/wombat/wombat + +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} + +shell: + ${ENV_RUN} bash + +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 + +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 + +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 + +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 + +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 diff --git a/OxConfig.cmake b/OxConfig.cmake new file mode 100644 index 00000000..6312e31c --- /dev/null +++ b/OxConfig.cmake @@ -0,0 +1,11 @@ +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) +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) +endif("${CMAKE_FIND_ROOT_PATH}" STREQUAL "") diff --git a/build/Makefile b/build/Makefile new file mode 100644 index 00000000..a1bd3c20 --- /dev/null +++ b/build/Makefile @@ -0,0 +1,32 @@ +ifneq ($(shell which gmake),) + MAKE=gmake -j +else + MAKE=make +endif + +all: gba_build native_build native_debug_build windows_release windows_debug + +gba_build: + @if [ -d gba-release ]; then \ + ${MAKE} -C gba-release ${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 diff --git a/cmake/Modules/FindJansson.cmake b/cmake/Modules/FindJansson.cmake new file mode 100644 index 00000000..19ee0774 --- /dev/null +++ b/cmake/Modules/FindJansson.cmake @@ -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) diff --git a/cmake/Modules/GBA.cmake b/cmake/Modules/GBA.cmake new file mode 100644 index 00000000..6440e228 --- /dev/null +++ b/cmake/Modules/GBA.cmake @@ -0,0 +1,41 @@ +set(CMAKE_SYSTEM_NAME "Generic") + +set(DEVKITARM $ENV{DEVKITARM}) +set(DEVKITPRO $ENV{DEVKITPRO}) + +if(NOT DEVKITPRO) + message(FATAL_ERROR "DEVKITPRO environment variable not set") +endif() + +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} ${DEVKITPRO}/libgba) +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 +) + +find_library(GBA_LIBRARY + NAMES + gba + PATHS + /lib +) +find_path(GBA_INCLUDE_DIR gba.h + PATHS + /include +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(GBA DEFAULT_MSG GBA_LIBRARY) diff --git a/cmake/Modules/Mingw.cmake b/cmake/Modules/Mingw.cmake new file mode 100644 index 00000000..bf1d08f1 --- /dev/null +++ b/cmake/Modules/Mingw.cmake @@ -0,0 +1,18 @@ +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) diff --git a/cmake/Modules/address_sanitizer.cmake b/cmake/Modules/address_sanitizer.cmake new file mode 100644 index 00000000..f1076b24 --- /dev/null +++ b/cmake/Modules/address_sanitizer.cmake @@ -0,0 +1,52 @@ +# 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) diff --git a/scripts/cibuild b/scripts/cibuild new file mode 100755 index 00000000..236a44cb --- /dev/null +++ b/scripts/cibuild @@ -0,0 +1,8 @@ +#! /usr/bin/env bash + +set -e + +make -j release +make -j debug +make -j +make -j test diff --git a/scripts/setup_build b/scripts/setup_build new file mode 100755 index 00000000..f416aa15 --- /dev/null +++ b/scripts/setup_build @@ -0,0 +1,30 @@ +#! /usr/bin/env bash + +set -e + +project=$(pwd)/ + +TARGET=$1 +BUILD_TYPE=$2 + +if [[ $TARGET == windows ]]; then + toolchain="-DCMAKE_TOOLCHAIN_FILE=cmake/Modules/Mingw.cmake" +elif [[ $TARGET == gba ]]; then + toolchain="-DCMAKE_TOOLCHAIN_FILE=cmake/Modules/GBA.cmake -DOX_USE_STDLIB=OFF -DCMAKE_INSTALL_PREFIX=$DEVKITARM" +fi + +if [[ $BUILD_TYPE == debug ]]; then + buildTypeArgs="-DUSE_ASAN=ON -DCMAKE_BUILD_TYPE=Debug" + buildDir="build/${TARGET}-debug" +else + buildTypeArgs="-DCMAKE_BUILD_TYPE=Release" + buildDir="build/${TARGET}-release" +fi + +mkdir -p $buildDir +pushd $buildDir +cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ + $buildTypeArgs \ + $toolchain \ + $project +popd diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 00000000..81ecf243 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 2.8) + +add_subdirectory(ox) diff --git a/src/ox/CMakeLists.txt b/src/ox/CMakeLists.txt new file mode 100644 index 00000000..3dcead2c --- /dev/null +++ b/src/ox/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 2.8) + +if(OX_USE_STDLIB STREQUAL "ON") + add_subdirectory(clargs) +endif(OX_USE_STDLIB STREQUAL "ON") +add_subdirectory(fs) +add_subdirectory(log) +add_subdirectory(mc) +add_subdirectory(std) diff --git a/src/ox/clargs/CMakeLists.txt b/src/ox/clargs/CMakeLists.txt new file mode 100644 index 00000000..a096079c --- /dev/null +++ b/src/ox/clargs/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 2.8) + +add_library( + OxClArgs + clargs.cpp +) + +set_property( + TARGET + OxClArgs + PROPERTY + POSITION_INDEPENDENT_CODE ON +) + +install( + FILES + clargs.hpp + DESTINATION + include/ox/clargs +) + +install( + TARGETS + OxClArgs + LIBRARY DESTINATION lib/ox + ARCHIVE DESTINATION lib/ox +) diff --git a/src/ox/clargs/clargs.cpp b/src/ox/clargs/clargs.cpp new file mode 100644 index 00000000..c028e308 --- /dev/null +++ b/src/ox/clargs/clargs.cpp @@ -0,0 +1,53 @@ +/* + * Copyright 2015 - 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/. + */ + +#include +#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]; +} + +} diff --git a/src/ox/clargs/clargs.hpp b/src/ox/clargs/clargs.hpp new file mode 100644 index 00000000..4b7b740f --- /dev/null +++ b/src/ox/clargs/clargs.hpp @@ -0,0 +1,32 @@ +/* + * Copyright 2015 - 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/. + */ + +#pragma once + +#include +#include + +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); +}; + +} diff --git a/src/ox/fs/CMakeLists.txt b/src/ox/fs/CMakeLists.txt new file mode 100644 index 00000000..5c333217 --- /dev/null +++ b/src/ox/fs/CMakeLists.txt @@ -0,0 +1,54 @@ +cmake_minimum_required(VERSION 2.8) + +add_library( + OxFS + filesystem.cpp + pathiterator.cpp +) + +set_property( + TARGET + OxFS + PROPERTY + POSITION_INDEPENDENT_CODE ON +) + +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 + OxLog + OxStd + ) +endif() + +install( + FILES + filestore.hpp + filesystem.hpp + pathiterator.hpp + DESTINATION + include/ox/fs +) + +install( + TARGETS + OxFS + LIBRARY DESTINATION lib/ox + ARCHIVE DESTINATION lib/ox +) + +if(OX_BUILD_EXEC STREQUAL "ON") + if(OX_RUN_TESTS STREQUAL "ON") + add_subdirectory(test) + endif() + install(TARGETS oxfstool + RUNTIME DESTINATION bin + ) +endif() diff --git a/src/ox/fs/filestore.hpp b/src/ox/fs/filestore.hpp new file mode 100644 index 00000000..99092bd9 --- /dev/null +++ b/src/ox/fs/filestore.hpp @@ -0,0 +1,923 @@ +/* + * Copyright 2015 - 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/. + */ + +#pragma once + +#include + +namespace ox { + +template +struct __attribute__((packed)) FileStoreHeader { + public: + typedef InodeId InodeId_t; + typedef FsT FsSize_t; + const static auto VERSION = 6; + + private: + uint16_t m_version; + uint16_t m_fsType; + FsSize_t m_size; + FsSize_t m_memUsed; + FsSize_t m_rootInode; + + public: + void setVersion(uint16_t); + uint16_t getVersion(); + + void setFsType(uint16_t); + uint16_t getFsType(); + + void setSize(FsSize_t); + FsSize_t getSize(); + + void setMemUsed(FsSize_t); + FsSize_t getMemUsed(); + + void setRootInode(FsSize_t); + FsSize_t getRootInode(); +}; + +template +void FileStoreHeader::setVersion(uint16_t version) { + m_version = bigEndianAdapt(version); +} + +template +uint16_t FileStoreHeader::getVersion() { + return bigEndianAdapt(m_version); +} + +template +void FileStoreHeader::setFsType(uint16_t fsType) { + m_fsType = bigEndianAdapt(fsType); +} + +template +uint16_t FileStoreHeader::getFsType() { + return bigEndianAdapt(m_fsType); +} + +template +void FileStoreHeader::setSize(FsSize_t size) { + m_size = bigEndianAdapt(size); +} + +template +FsSize_t FileStoreHeader::getSize() { + return bigEndianAdapt(m_size); +} + +template +void FileStoreHeader::setMemUsed(FsSize_t memUsed) { + m_memUsed = bigEndianAdapt(memUsed); +} + +template +FsSize_t FileStoreHeader::getMemUsed() { + return bigEndianAdapt(m_memUsed); +} + +template +void FileStoreHeader::setRootInode(FsSize_t rootInode) { + m_rootInode = bigEndianAdapt(rootInode); +} + +template +FsSize_t FileStoreHeader::getRootInode() { + return bigEndianAdapt(m_rootInode); +} + +template +class FileStore { + + public: + typedef typename Header::InodeId_t InodeId_t; + typedef typename Header::FsSize_t FsSize_t; + const static auto VERSION = Header::VERSION; + + struct StatInfo { + InodeId_t inodeId; + InodeId_t links; + typename Header::FsSize_t size; + uint8_t fileType; + }; + + private: + struct __attribute__((packed)) Inode { + private: + // the next Inode in memory + typename Header::FsSize_t m_prev; + typename Header::FsSize_t m_next; + typename Header::FsSize_t m_dataLen; + + InodeId_t m_id; + InodeId_t m_links; + uint8_t m_fileType; + typename Header::FsSize_t m_left; + typename Header::FsSize_t m_right; + + public: + typename Header::FsSize_t size(); + + void setDataLen(typename Header::FsSize_t); + typename Header::FsSize_t getDataLen(); + + void setPrev(typename Header::FsSize_t); + typename Header::FsSize_t getPrev(); + + void setNext(typename Header::FsSize_t); + typename Header::FsSize_t getNext(); + + void setId(InodeId_t); + InodeId_t getId(); + + void setLinks(InodeId_t); + InodeId_t getLinks(); + + void setFileType(uint8_t); + uint8_t getFileType(); + + void setLeft(typename Header::FsSize_t); + typename Header::FsSize_t getLeft(); + + void setRight(typename Header::FsSize_t); + typename Header::FsSize_t getRight(); + + void setData(void *data, typename Header::FsSize_t size); + uint8_t *getData(); + }; + + Header m_header; + + public: + /** + * Dumps this file store's inodes to the given file store. + */ + int dumpTo(FileStore
*dest); + + /** + * Compacts and resizes the file store to the minimum possible size for + * the contents. + * + * NOTE: This does NOT resize the buffer that this file store refers to! + */ + void resize(typename Header::FsSize_t size = 0); + + /** + * Writes the given data to a "file" with the given id. + * @param id the id of the file + * @param data the contents of the file + * @param dataLen the number of bytes data points to + */ + int write(InodeId_t id, void *data, typename Header::FsSize_t dataLen, uint8_t fileType = 0); + + /** + * Removes the inode of the given ID. + * @param id the id of the file + */ + int remove(InodeId_t id); + + /** + * Increments the links of the inode of the given ID. + * @param id the id of the inode + */ + int incLinks(InodeId_t id); + + /** + * Decrements the links of the inode of the given ID. + * @param id the id of the inode + */ + int decLinks(InodeId_t id); + + /** + * Removes all inodes of the type. + * @param fileType the type of file to remove + */ + int removeAllType(uint8_t fileType); + + /** + * 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 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 + */ + int read(InodeId_t id, void *data, typename Header::FsSize_t *size); + + /** + * 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 + */ + int read(InodeId_t id, typename Header::FsSize_t readStart, + typename Header::FsSize_t readSize, void *data, + typename Header::FsSize_t *size); + + /** + * 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 + int read(InodeId_t id, typename Header::FsSize_t readStart, + typename Header::FsSize_t readSize, T *data, + typename Header::FsSize_t *size); + + /** + * Reads the stat information of the inode of the given inode id. + * If the returned inode id is 0, then the requested inode was not found. + * @param id id of the inode to stat + * @return the stat information of the inode of the given inode id + */ + StatInfo stat(InodeId_t id); + + /** + * Returns the space needed for this data at the given inode address. + * @param id the target inode id + * @param size the size of the data to insert + * @return the space currently available in this file store. + */ + typename Header::FsSize_t spaceNeeded(typename Header::FsSize_t size); + + /** + * Returns the size of the file store. + * @return the size of the file store. + */ + typename Header::FsSize_t size(); + + /** + * Returns the space currently available in this file store. + * @return the space currently available in this file store. + */ + typename Header::FsSize_t available(); + + void walk(int(*cb)(const char*, uint64_t start, uint64_t end)); + + uint16_t fsType(); + + uint16_t version(); + + static uint8_t *format(uint8_t *buffer, typename Header::FsSize_t size, uint16_t fsType = 0); + + private: + /** + * Gets the inode at the given id. + * @param root the root node to start comparing on + * @param id id of the "file" + * @param pathLen number of characters in pathLen + * @return the requested Inode, if available + */ + Inode *getInode(Inode *root, InodeId_t id); + + /** + * Gets the parent inode at the given id. + * @param root the root node to start comparing on + * @param id id of the "file" + * @param pathLen number of characters in pathLen + * @param targetAddr the address of the target inode + * @return the requested Inode, if available + */ + Inode *getInodeParent(Inode *root, InodeId_t id, typename Header::FsSize_t targetAddr); + + /** + * Reads the "file" at the given id. You are responsible for freeing + * the data when done with it. + * @param inode inode 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 + int read(Inode *inode, typename Header::FsSize_t readStart, + typename Header::FsSize_t readSize, T *data, + typename Header::FsSize_t *size); + + /** + * Removes the inode of the given ID. + * @param id the id of the file + */ + int remove(Inode *root, InodeId_t id); + + /** + * Removes the given node from the linked list. + * @param node node to remove + */ + void dealloc(Inode *node); + + /** + * Gets the address of the next available inode, assuming there is a next + * available inode. + */ + typename Header::FsSize_t nextInodeAddr(); + + /** + * Gets an address for a new Inode. + * @param size the size of the Inode + */ + void *alloc(typename Header::FsSize_t size); + + /** + * Compacts all of the inodes into a contiguous space, starting at the first inode. + */ + void compact(); + + /** + * Inserts the given insertValue into the tree of the given root. + * If the inode already exists, it replaces the old on deletes it. + * @return true if the inode was inserted + */ + bool insert(Inode *root, Inode *insertValue); + + typename Header::FsSize_t firstInode(); + + typename Header::FsSize_t lastInode(); + + /** + * Updates the address of the inode in the tree. + */ + void updateInodeAddress(InodeId_t id, typename Header::FsSize_t oldAddr, typename Header::FsSize_t newAddr); + + uint8_t *begin() { + return (uint8_t*) this; + } + + uint8_t *end() { + return begin() + this->m_header.getSize(); + } + + /** + * Converts an actual pointer to a FsSize_t. + */ + typename Header::FsSize_t ptr(void *ptr); + + /** + * Converts a FsSize_t to an actual pointer. + */ + template + T ptr(typename Header::FsSize_t ptr) { + return (T) (begin() + ptr); + }; + +}; + +template +typename Header::FsSize_t FileStore
::Inode::size() { + return sizeof(Inode) + getDataLen(); +} + +template +void FileStore
::Inode::setDataLen(typename Header::FsSize_t dataLen) { + this->m_dataLen = bigEndianAdapt(dataLen); +} + +template +typename Header::FsSize_t FileStore
::Inode::getDataLen() { + return bigEndianAdapt(m_dataLen); +} + +template +void FileStore
::Inode::setPrev(typename Header::FsSize_t prev) { + this->m_prev = bigEndianAdapt(prev); +} + +template +typename Header::FsSize_t FileStore
::Inode::getPrev() { + return bigEndianAdapt(m_prev); +} + +template +void FileStore
::Inode::setNext(typename Header::FsSize_t next) { + this->m_next = bigEndianAdapt(next); +} + +template +typename Header::FsSize_t FileStore
::Inode::getNext() { + return bigEndianAdapt(m_next); +} + +template +void FileStore
::Inode::setId(InodeId_t id) { + this->m_id = bigEndianAdapt(id); +} + +template +typename Header::InodeId_t FileStore
::Inode::getId() { + return bigEndianAdapt(m_id); +} + +template +void FileStore
::Inode::setLinks(InodeId_t links) { + this->m_links = bigEndianAdapt(links); +} + +template +typename Header::InodeId_t FileStore
::Inode::getLinks() { + return bigEndianAdapt(m_links); +} + +template +void FileStore
::Inode::setFileType(uint8_t fileType) { + this->m_fileType = bigEndianAdapt(fileType); +} + +template +uint8_t FileStore
::Inode::getFileType() { + return bigEndianAdapt(m_fileType); +} + +template +void FileStore
::Inode::setLeft(typename Header::FsSize_t left) { + this->m_left = bigEndianAdapt(left); +} + +template +typename Header::FsSize_t FileStore
::Inode::getLeft() { + return bigEndianAdapt(m_left); +} + +template +void FileStore
::Inode::setRight(typename Header::FsSize_t right) { + this->m_right = bigEndianAdapt(right); +} + +template +typename Header::FsSize_t FileStore
::Inode::getRight() { + return bigEndianAdapt(m_right); +} + +template +void FileStore
::Inode::setData(void *data, typename Header::FsSize_t size) { + ox_memcpy(getData(), data, size); + setDataLen(size); +} + +template +uint8_t *FileStore
::Inode::getData() { + return (uint8_t*) (this + 1); +} + + +// FileStore + +template +int FileStore
::dumpTo(FileStore
*dest) { + if (dest->size() >= size()) { + auto i = ptr(firstInode()); + do { + dest->write(i->getId(), i->getData(), i->getDataLen(), i->getFileType()); + i = ptr(i->getNext()); + } while (ptr(i) != firstInode()); + return 0; + } else { + return -1; + } +} + +template +void FileStore
::resize(typename Header::FsSize_t size) { + if (size < m_header.getSize()) { + // shrink file store + if (m_header.getMemUsed() > size) { + size = m_header.getMemUsed(); + } + compact(); + m_header.setSize(size); + } else if (size > m_header.getSize()) { + // grow file store + m_header.setSize(size); + } +} + +template +int FileStore
::write(InodeId_t id, void *data, typename Header::FsSize_t dataLen, uint8_t fileType) { + auto retval = 1; + const typename Header::FsSize_t size = sizeof(Inode) + dataLen; + if (size <= (m_header.getSize() - m_header.getMemUsed())) { + auto inode = (Inode*) alloc(size); + if (inode) { + remove(id); + inode->setId(id); + inode->setFileType(fileType); + inode->setData(data, dataLen); + auto root = ptr(m_header.getRootInode()); + if (insert(root, inode) || root == inode) { + retval = 0; + } else { + dealloc(inode); + retval = 2; + } + } else { + retval = 3; + } + } else { + retval = 4; + } + return retval; +} + +template +int FileStore
::remove(InodeId_t id) { + return remove(ptr(m_header.getRootInode()), id); +} + +/** + * Increments the links of the inode of the given ID. + * @param id the id of the inode + */ +template +int FileStore
::incLinks(InodeId_t id) { + auto inode = getInode(ptr(m_header.getRootInode()), id); + if (inode) { + inode->setLinks(inode->getLinks() + 1); + return 0; + } else { + return 1; + } +} + +/** + * Decrements the links of the inode of the given ID. + * @param id the id of the inode + */ +template +int FileStore
::decLinks(InodeId_t id) { + auto inode = getInode(ptr(m_header.getRootInode()), id); + if (inode) { + inode->setLinks(inode->getLinks() - 1); + return 0; + } else { + return 1; + } +} + +template +int FileStore
::remove(Inode *root, InodeId_t id) { + auto err = 1; + + if (root->getId() > id) { + if (root->getLeft()) { + auto left = ptr(root->getLeft()); + if (left->getId() != id) { + err = remove(left, id); + } else { + root->setLeft(0); + // pass children to parent + if (left->getRight()) { + insert(root, ptr(left->getRight())); + } + if (left->getLeft()) { + insert(root, ptr(left->getLeft())); + } + dealloc(left); + err = 0; + } + } + } else if (root->getId() < id) { + if (root->getRight()) { + auto right = ptr(root->getRight()); + if (right->getId() != id) { + err = remove(right, id); + } else { + root->setRight(0); + // pass children to parent + if (right->getRight()) { + insert(root, ptr(right->getRight())); + } + if (right->getLeft()) { + insert(root, ptr(right->getLeft())); + } + dealloc(right); + err = 0; + } + } + } else if (ptr(m_header.getRootInode())->getId() == id) { + m_header.setRootInode(root->getRight()); + if (root->getLeft()) { + insert(ptr(m_header.getRootInode()), ptr(root->getLeft())); + } + dealloc(root); + err = 0; + } + + return err; +} + +template +int FileStore
::removeAllType(uint8_t fileType) { + int err = 0; + auto first = ptr(firstInode()); + // skip the first inode for now, because removing the first inode will cause compact to run + auto current = first; + auto next = ptr(current->getNext()); + + while (next != first) { + current = next; + // get next before current is possibly cleared + next = ptr(current->getNext()); + + if (current->getFileType() == fileType) { + err |= remove(current->getId()); + } + } + + if (first->getFileType() == fileType) { + err |= remove(first->getId()); + } + + return err; +} + +template +void FileStore
::dealloc(Inode *inode) { + auto next = ptr(inode->getNext()); + auto prev = ptr(inode->getPrev()); + prev->setNext(ptr(next)); + next->setPrev(ptr(prev)); + + m_header.setMemUsed(m_header.getMemUsed() - inode->size()); + + ox_memset(inode, 0, inode->size()); +} + +template +void FileStore
::updateInodeAddress(InodeId_t id, typename Header::FsSize_t oldAddr, typename Header::FsSize_t newAddr) { + auto parent = getInodeParent(ptr(m_header.getRootInode()), id, oldAddr); + if (parent) { + if (parent->getLeft() == oldAddr) { + parent->setLeft(newAddr); + } else if (parent->getRight() == oldAddr) { + parent->setRight(newAddr); + } + } +} + +template +int FileStore
::read(InodeId_t id, void *data, typename Header::FsSize_t *size) { + auto inode = getInode(ptr(m_header.getRootInode()), id); + return inode ? read(inode, 0, inode->getDataLen(), (uint8_t*) data, size) : 1; +} + +template +int FileStore
::read(InodeId_t id, typename Header::FsSize_t readStart, + typename Header::FsSize_t readSize, void *data, typename Header::FsSize_t *size) { + auto inode = getInode(ptr(m_header.getRootInode()), id); + return inode ? read(inode, readStart, readSize, (uint8_t*) data, size) : 1; +} + +template +template +int FileStore
::read(InodeId_t id, typename Header::FsSize_t readStart, + typename Header::FsSize_t readSize, T *data, typename Header::FsSize_t *size) { + auto inode = getInode(ptr(m_header.getRootInode()), id); + return inode ? read(inode, readStart, readSize, data, size) : 1; +} + +template +template +int FileStore
::read(Inode *inode, typename Header::FsSize_t readStart, + typename Header::FsSize_t readSize, T *data, typename Header::FsSize_t *size) { + // be sure read size is not greater than what is available to read + if (inode->getDataLen() - readStart < readSize) { + readSize = inode->getDataLen() - readStart; + } + if (size) { + *size = readSize; + } + + readSize /= sizeof(T); + uint8_t *it = &(inode->getData()[readStart]); + for (typename Header::FsSize_t i = 0; i < readSize; i++) { + T val; + for (size_t i = 0; i < sizeof(T); i++) { + ((uint8_t*) (&val))[i] = *(it++); + } + *(data++) = val; + } + return 0; +} + +template +typename FileStore
::StatInfo FileStore
::stat(InodeId_t id) { + auto inode = getInode(ptr(m_header.getRootInode()), id); + StatInfo stat; + if (inode) { + stat.size = inode->getDataLen(); + stat.fileType = inode->getFileType(); + stat.links = inode->getLinks(); + stat.inodeId = id; + } else { + stat.inodeId = 0; + } + return stat; +} + +template +typename Header::FsSize_t FileStore
::spaceNeeded(typename Header::FsSize_t size) { + return sizeof(Inode) + size; +} + +template +typename Header::FsSize_t FileStore
::size() { + return m_header.getSize(); +} + +template +typename Header::FsSize_t FileStore
::available() { + return m_header.getSize() - m_header.getMemUsed(); +} + +template +typename FileStore
::Inode *FileStore
::getInode(Inode *root, InodeId_t id) { + Inode *retval = nullptr; + + if (root->getId() > id) { + if (root->getLeft()) { + retval = getInode(ptr(root->getLeft()), id); + } + } else if (root->getId() < id) { + if (root->getRight()) { + retval = getInode(ptr(root->getRight()), id); + } + } else if (root->getId() == id) { + retval = root; + } + + return retval; +} + +template +typename FileStore
::Inode *FileStore
::getInodeParent(Inode *root, InodeId_t id, typename Header::FsSize_t targetAddr) { + Inode *retval = nullptr; + + if (root->getId() > id) { + if (root->getLeft()) { + if (root->getLeft() == targetAddr) { + retval = root; + } else { + retval = getInodeParent(ptr(root->getLeft()), id, targetAddr); + } + } + } else if (root->getId() < id) { + if (root->getRight()) { + if (root->getRight() == targetAddr) { + retval = root; + } else { + retval = getInodeParent(ptr(root->getRight()), id, targetAddr); + } + } + } + + return retval; +} + +template +typename Header::FsSize_t FileStore
::nextInodeAddr() { + return lastInode() + ptr(lastInode())->size(); +} + +template +void *FileStore
::alloc(typename Header::FsSize_t size) { + auto next = nextInodeAddr(); + if ((next + size) > ptr(end())) { + compact(); + next = nextInodeAddr(); + if ((next + size) > ptr(end())) { + return nullptr; + } + } + + const auto retval = next; + const auto inode = ptr(retval); + ox_memset(inode, 0, size); + inode->setPrev(ptr(firstInode())->getPrev()); + inode->setNext(firstInode()); + m_header.setMemUsed(m_header.getMemUsed() + size); + ptr(lastInode())->setNext(retval); + ptr(firstInode())->setPrev(retval); + return inode; +} + +template +void FileStore
::compact() { + auto dest = ptr(firstInode()); + auto current = ptr(firstInode()); + while (current->getNext() > firstInode() && current->getNext() < ptr(end())) { + ox_memcpy(dest, current, current->size()); + if (dest->getNext() != firstInode()) { + dest->setNext(ptr(dest) + dest->size()); + } + ptr(dest->getNext())->setPrev(ptr(dest)); + updateInodeAddress(dest->getId(), ptr(current), ptr(dest)); + current = ptr(dest->getNext()); + dest = ptr(ptr(dest) + dest->size()); + } +} + +template +bool FileStore
::insert(Inode *root, Inode *insertValue) { + auto retval = false; + + if (root->getId() > insertValue->getId()) { + if (root->getLeft()) { + retval = insert(ptr(root->getLeft()), insertValue); + } else { + root->setLeft(ptr(insertValue)); + retval = true; + } + } else if (root->getId() < insertValue->getId()) { + if (root->getRight()) { + retval = insert(ptr(root->getRight()), insertValue); + } else { + root->setRight(ptr(insertValue)); + retval = true; + } + } else if (m_header.getRootInode() == 0) { + m_header.setRootInode(ptr(insertValue)); + retval = true; + } + + return retval; +} + +template +typename Header::FsSize_t FileStore
::ptr(void *ptr) { +#ifdef _MSC_VER +#pragma warning(disable:4244) +#endif + return ((uint8_t*) ptr) - begin(); +#ifdef _MSC_VER +#pragma warning(default:4244) +#endif +} + +template +typename Header::FsSize_t FileStore
::firstInode() { + return sizeof(FileStore
); +} + +template +typename Header::FsSize_t FileStore
::lastInode() { + return ptr(firstInode())->getPrev(); +} + +template +uint16_t FileStore
::fsType() { + return m_header.getFsType(); +}; + +template +uint16_t FileStore
::version() { + return m_header.getVersion(); +}; + +template +void FileStore
::walk(int(*cb)(const char*, uint64_t start, uint64_t end)) { + auto err = cb("Header", 0, sizeof(Header)); + auto inode = ptr(firstInode()); + do { + auto start = ptr(inode); + err = cb("Inode", start, start + inode->size()); + inode = ptr(inode->getNext()); + } while (!err && inode != ptr(firstInode())); +} + +template +uint8_t *FileStore
::format(uint8_t *buffer, typename Header::FsSize_t size, uint16_t fsType) { + ox_memset(buffer, 0, size); + + auto *fs = (FileStore*) buffer; + fs->m_header.setFsType(fsType); + fs->m_header.setVersion(Header::VERSION); + fs->m_header.setSize(size); + fs->m_header.setMemUsed(sizeof(FileStore
) + sizeof(Inode)); + fs->m_header.setRootInode(sizeof(FileStore
)); + ((Inode*) (fs + 1))->setPrev(sizeof(FileStore
)); + ((Inode*) (fs + 1))->setNext(sizeof(FileStore
)); + + return (uint8_t*) buffer; +} + +typedef FileStore> FileStore16; +typedef FileStore> FileStore32; +typedef FileStore> FileStore64; + +} diff --git a/src/ox/fs/filesystem.cpp b/src/ox/fs/filesystem.cpp new file mode 100644 index 00000000..b6f9b94f --- /dev/null +++ b/src/ox/fs/filesystem.cpp @@ -0,0 +1,73 @@ +/* + * Copyright 2015 - 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/. + */ + +#include "filesystem.hpp" + +namespace ox { + +FileSystem *createFileSystem(uint8_t *buff, size_t buffSize, bool ownsBuff) { + auto version = ((FileStore16*) buff)->version(); + auto type = ((FileStore16*) buff)->fsType(); + FileSystem *fs = nullptr; + + switch (version) { + case 6: + switch (type) { + case ox::OxFS_16: + fs = new FileSystem16(buff, ownsBuff); + break; + case ox::OxFS_32: + fs = new FileSystem32(buff, ownsBuff); + break; + case ox::OxFS_64: + fs = new FileSystem64(buff, ownsBuff); + break; + } + break; + default: + break; + } + + if (fs && fs->size() > buffSize) { + delete fs; + fs = nullptr; + } + + return fs; +} + +FileSystem *expandCopy(FileSystem *fs, size_t size) { + auto fsBuff = fs->buff(); + FileSystem *retval = nullptr; + + if (fs->size() <= size) { + auto cloneBuff = new uint8_t[size]; + ox_memcpy(cloneBuff, fsBuff, fs->size()); + + fsBuff = cloneBuff; + retval = createFileSystem(fsBuff, size); + retval->resize(size); + } + + return retval; +} + +FileSystem *expandCopyCleanup(FileSystem *fs, size_t size) { + auto out = expandCopy(fs, size); + + if (out) { + delete[] fs->buff(); + delete fs; + } else { + out = fs; + } + + return out; +} + +} diff --git a/src/ox/fs/filesystem.hpp b/src/ox/fs/filesystem.hpp new file mode 100644 index 00000000..1092195e --- /dev/null +++ b/src/ox/fs/filesystem.hpp @@ -0,0 +1,884 @@ +/* + * Copyright 2015 - 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/. + */ +#pragma once + +#include +#include "pathiterator.hpp" +#include "filestore.hpp" + +namespace ox { + +enum FsType { + OxFS_16 = 1, + OxFS_32 = 2, + OxFS_64 = 3 +}; + +enum FileType { + FileType_NormalFile = 1, + FileType_Directory = 2 +}; + +struct FileStat { + uint64_t inode; + uint64_t links; + uint64_t size; + uint8_t fileType; +}; + +template +struct DirectoryListing { + String name; + FileStat stat; + + DirectoryListing() = default; + + DirectoryListing(const char *name) { + this->name = name; + } +}; + +template +bool operator<(const DirectoryListing &a, const DirectoryListing &b) { + return a.name < b.name; +} + +template +struct __attribute__((packed)) DirectoryEntry { + InodeId_t inode; + + char *getName() { + return (char*) (this + 1); + } + + void setName(const char *name) { + auto data = getName(); + auto nameLen = ox_strlen(name); + ox_memcpy(data, name, nameLen); + data[nameLen] = 0; + } + + static uint64_t spaceNeeded(const char *fileName) { + return sizeof(DirectoryEntry) + ox_strlen(fileName) + 1; + } + + /** + * The size in bytes. + */ + uint64_t size() { + return spaceNeeded(getName()); + } +}; + +template +struct __attribute__((packed)) Directory { + /** + * Number of bytes after this Directory struct. + */ + FsSize_t size = 0; + FsSize_t children = 0; + + DirectoryEntry *files() { + return size ? (DirectoryEntry*) (this + 1) : nullptr; + } + + uint64_t getFileInode(const char *name); + + int getChildrenInodes(InodeId_t *inodes, size_t inodesLen); + + int rmFile(const char *name); + + int copy(Directory *dirOut); + + template + int ls(List *list); +}; + +template +uint64_t Directory::getFileInode(const char *name) { + uint64_t inode = 0; + auto current = files(); + if (current) { + for (uint64_t i = 0; ox_strcmp(current->getName(), name) != 0;) { + i += current->size(); + if (i < this->size) { + current = (DirectoryEntry*) (((uint8_t*) current) + current->size()); + } else { + current = nullptr; + break; + } + } + if (current) { + inode = current->inode; + } + } + return inode; +} + +template +int Directory::getChildrenInodes(InodeId_t *inodes, size_t inodesLen) { + if (inodesLen >= this->children) { + auto current = files(); + if (current) { + for (uint64_t i = 0; i < this->children; i++) { + if (ox_strcmp(current->getName(), ".") and ox_strcmp(current->getName(), "..")) { + inodes[i] = current->inode; + } + current = (DirectoryEntry*) (((uint8_t*) current) + current->size()); + } + return 0; + } else { + return 1; + } + } else { + return 2; + } +} + +template +int Directory::rmFile(const char *name) { + int err = 1; + auto current = files(); + if (current) { + for (uint64_t i = 0; i < this->size;) { + i += current->size(); + if (ox_strcmp(current->getName(), name) == 0) { + auto dest = (uint8_t*) current; + auto src = dest + current->size(); + ox_memcpy(dest, src, this->size - i); + this->size -= current->size(); + this->children--; + err = 0; + break; + } + current = (DirectoryEntry*) (((uint8_t*) current) + current->size()); + } + } + return err; +} + +template +int Directory::copy(Directory *dirOut) { + auto current = files(); + auto dirOutBuff = (uint8_t*) dirOut; + dirOutBuff += sizeof(Directory); + dirOut->size = this->size; + dirOut->children = this->children; + if (current) { + for (uint64_t i = 0; i < this->children; i++) { + auto entry = (DirectoryEntry*) dirOutBuff; + entry->inode = current->inode; + entry->setName(current->getName()); + + current = (DirectoryEntry*) (((uint8_t*) current) + current->size()); + dirOutBuff += entry->size(); + } + return 0; + } else { + return 1; + } +} + +template +template +int Directory::ls(List *list) { + auto current = files(); + if (current) { + for (uint64_t i = 0; i < this->children; i++) { + list->push_back(current->getName()); + (*list)[i].stat.inode = current->inode; + current = (DirectoryEntry*) (((uint8_t*) current) + current->size()); + } + return 0; + } else { + return 1; + } +} + + +class FileSystem { + public: + virtual ~FileSystem() {}; + + virtual int stripDirectories() = 0; + + virtual int mkdir(const char *path) = 0; + + /** + * Moves an entry from one directory to another. + * @param src the path to the file + * @param dest the path of the destination directory + */ + virtual int move(const char *src, const char *dest) = 0; + + template + int ls(const char *path, List *list); + + virtual int read(const char *path, void *buffer, size_t buffSize) = 0; + + virtual int read(uint64_t inode, void *buffer, size_t size) = 0; + + virtual int read(uint64_t inode, size_t readStart, size_t readSize, void *buffer, size_t *size) = 0; + + virtual uint8_t *read(uint64_t inode, size_t *size) = 0; + + virtual int remove(uint64_t inode, bool recursive = false) = 0; + + virtual int remove(const char *path, bool recursive = false) = 0; + + virtual void resize(uint64_t size = 0) = 0; + + virtual int write(const char *path, void *buffer, uint64_t size, uint8_t fileType = FileType_NormalFile) = 0; + + virtual int write(uint64_t inode, void *buffer, uint64_t size, uint8_t fileType = FileType_NormalFile) = 0; + + virtual FileStat stat(uint64_t inode) = 0; + + virtual FileStat stat(const char *path) = 0; + + virtual uint64_t spaceNeeded(uint64_t size) = 0; + + virtual uint64_t available() = 0; + + virtual uint64_t size() = 0; + + virtual uint8_t *buff() = 0; + + virtual void walk(int(*cb)(const char*, uint64_t, uint64_t)) = 0; + + protected: + virtual int readDirectory(const char *path, Directory *dirOut) = 0; +}; + +template +int FileSystem::ls(const char *path, List *list) { + int err = 0; + auto s = stat(path); + if (s.fileType == FileType_Directory) { + uint8_t dirBuff[s.size * 4]; + auto dir = (Directory*) dirBuff; + err = readDirectory(path, dir); + err |= dir->ls(list); + } + return err; +} + +FileSystem *createFileSystem(uint8_t *buff, size_t buffSize, bool ownsBuff = false); + +/** + * Creates a larger version of the given FileSystem. + */ +FileSystem *expandCopy(FileSystem *src); + +/** + * Calls expandCopy and deletes the original FileSystem and buff a resize was + * performed. + */ +FileSystem *expandCopyCleanup(FileSystem *fs, size_t size); + +template +class FileSystemTemplate: public FileSystem { + + private: + FileStore *m_store = nullptr; + bool m_ownsBuff = false; + + public: + // static members + static typename FileStore::InodeId_t INODE_RANDOM; + static typename FileStore::InodeId_t INODE_ROOT_DIR; + static typename FileStore::InodeId_t INODE_RESERVED_END; + + explicit FileSystemTemplate(uint8_t *buff, bool ownsBuff = false); + + ~FileSystemTemplate(); + + int stripDirectories() override; + + int mkdir(const char *path) override; + + int read(const char *path, void *buffer, size_t buffSize) override; + + int read(uint64_t inode, void *buffer, size_t buffSize) override; + + int read(uint64_t inode, size_t readStart, size_t readSize, void *buffer, size_t *size) override; + + uint8_t *read(uint64_t inode, size_t *size) override; + + void resize(uint64_t size = 0) override; + + int remove(uint64_t inode, bool recursive = false) override; + + int remove(const char *path, bool recursive = false) override; + + int write(const char *path, void *buffer, uint64_t size, uint8_t fileType = FileType_NormalFile) override; + + int write(uint64_t inode, void *buffer, uint64_t size, uint8_t fileType = FileType_NormalFile) override; + + FileStat stat(const char *path) override; + + FileStat stat(uint64_t inode) override; + + uint64_t findInodeOf(const char *name); + + uint64_t spaceNeeded(uint64_t size) override; + + uint64_t available() override; + + uint64_t size() override; + + uint8_t *buff() override; + + int move(const char *src, const char *dest) override; + + /** + * Removes an entry from a directory. This does not delete the referred to file. + */ + int rmDirectoryEntry(const char *path); + + void walk(int(*cb)(const char*, uint64_t, uint64_t)) override; + + static uint8_t *format(uint8_t *buffer, typename FileStore::FsSize_t size, bool useDirectories); + + protected: + int readDirectory(const char *path, Directory *dirOut) override; + + private: + uint64_t generateInodeId(); + + int insertDirectoryEntry(const char *dirPath, const char *fileName, uint64_t inode); + + void expand(uint64_t size); +}; + +template +FileSystemTemplate::FileSystemTemplate(uint8_t *buff, bool ownsBuff) { + m_store = (FileStore*) buff; + m_ownsBuff = ownsBuff; +} + +template +FileSystemTemplate::~FileSystemTemplate() { + if (m_ownsBuff) { + delete[] (uint8_t*) m_store; + } +} + +template +typename FileStore::InodeId_t FileSystemTemplate::INODE_RANDOM = 1; + +template +typename FileStore::InodeId_t FileSystemTemplate::INODE_ROOT_DIR = 2; + +template +typename FileStore::InodeId_t FileSystemTemplate::INODE_RESERVED_END = 100; + +template +int FileSystemTemplate::stripDirectories() { + return m_store->removeAllType(FileType::FileType_Directory); +} + +template +int FileSystemTemplate::mkdir(const char *pathIn) { + if (!findInodeOf(pathIn)) { + auto pathLen = ox_strlen(pathIn); + char path[pathLen + 1]; + ox_memcpy(path, pathIn, pathLen + 1); + + // make sure last character does not end with / + if (pathLen >= 1 && path[pathLen - 1] == '/') { + path[pathLen - 1] = 0; + pathLen--; + } + + Directory dir; + auto err = write(path, &dir, sizeof(dir), FileType::FileType_Directory); + if (err) { + return err; + } + + // add . entry for self + auto inode = findInodeOf(path); + err = insertDirectoryEntry(path, ".", inode); + if (err) { + remove(inode); + return err; + } + + // add .. entry for parent + char dirPath[pathLen]; + PathIterator pathReader(path, pathLen); + err |= pathReader.dirPath(dirPath, pathLen); + err = insertDirectoryEntry(path, "..", findInodeOf(dirPath)); + if (err) { + remove(inode); + return err; + } + + return err; + } else { + return 1; + } +} + +template +FileStat FileSystemTemplate::stat(const char *path) { + auto inode = findInodeOf(path); + FileStat stat; + auto s = m_store->stat(inode); + stat.size = s.size; + stat.inode = s.inodeId; + stat.fileType = s.fileType; + return stat; +} + +#ifdef _MSC_VER +#pragma warning(disable:4244) +#endif +template +FileStat FileSystemTemplate::stat(uint64_t inode) { + FileStat stat; + auto s = m_store->stat(inode); + stat.size = s.size; + stat.inode = s.inodeId; + stat.links = s.links; + stat.fileType = s.fileType; + return stat; +} +#ifdef _MSC_VER +#pragma warning(default:4244) +#endif + +#ifdef _MSC_VER +#pragma warning(disable:4244) +#endif +template +int FileSystemTemplate::read(const char *path, void *buffer, size_t buffSize) { + int retval = -1; + + // find the inode for the given path + auto inode = findInodeOf(path); + + // if inode exists, read the data into buffer + if (inode) { + retval = read(inode, buffer, buffSize); + } + + return retval; +} +#ifdef _MSC_VER +#pragma warning(default:4244) +#endif + +#ifdef _MSC_VER +#pragma warning(disable:4244) +#endif +template +int FileSystemTemplate::read(uint64_t inode, void *buffer, size_t buffSize) { + auto stat = m_store->stat(inode); + if (stat.size <= buffSize) { + return m_store->read(inode, buffer, nullptr); + } + return -1; +} +#ifdef _MSC_VER +#pragma warning(default:4244) +#endif + +#ifdef _MSC_VER +#pragma warning(disable:4244) +#endif +template +int FileSystemTemplate::read(uint64_t inode, size_t readStart, + size_t readSize, void *buffer, + size_t *size) { + if (size) { + auto stat = m_store->stat(inode); + *size = stat.size; + } + return m_store->read(inode, readStart, readSize, buffer, nullptr); +} +#ifdef _MSC_VER +#pragma warning(disable:4244) +#endif + +#ifdef _MSC_VER +#pragma warning(disable:4244) +#endif +template +uint8_t *FileSystemTemplate::read(uint64_t inode, size_t *size) { + auto s = m_store->stat(inode); + auto buff = new uint8_t[s.size]; + if (size) { + *size = s.size; + } + if (m_store->read(inode, buff, nullptr)) { + delete []buff; + buff = nullptr; + } + return buff; +} +#ifdef _MSC_VER +#pragma warning(default:4244) +#endif + +template +int FileSystemTemplate::remove(const char *path, bool recursive) { + auto inode = findInodeOf(path); + if (inode) { + return rmDirectoryEntry(path) | remove(inode, recursive); + } else { + return 1; + } +} + +#ifdef _MSC_VER +#pragma warning(disable:4244) +#endif +template +int FileSystemTemplate::remove(uint64_t inode, bool recursive) { + auto fileType = stat(inode).fileType; + if (fileType != FileType::FileType_Directory) { + return m_store->remove(inode); + } else if (fileType == FileType::FileType_Directory && recursive) { + int err = 0; + auto dirStat = stat(inode); + auto dirBuffLen = dirStat.size; + uint8_t dirBuff[dirBuffLen]; + auto dir = (Directory*) dirBuff; + + err = read(dirStat.inode, dirBuff, dirBuffLen); + if (err) { + return 1; + } + + typename FileStore::InodeId_t inodes[dir->children]; + ox_memset(inodes, 0, sizeof(typename FileStore::InodeId_t) * dir->children); + dir->getChildrenInodes(inodes, dir->children); + + for (auto i : inodes) { + if (i) { + err |= remove(i, true); + } + } + + if (!err) { + err |= m_store->remove(inode); + } + + return err; + } else { + return 1; + } +} +#ifdef _MSC_VER +#pragma warning(default:4244) +#endif + +#ifdef _MSC_VER +#pragma warning(disable:4244) +#endif +template +int FileSystemTemplate::write(const char *path, void *buffer, uint64_t size, uint8_t fileType) { + int err = 0; + size_t pathLen = ox_strlen(path); + char dirPath[pathLen]; + char fileName[pathLen]; + PathIterator pathReader(path, pathLen); + err |= pathReader.fileName(fileName, pathLen); + err |= pathReader.dirPath(dirPath, pathLen); + if (err) { + return err; + } + + uint64_t inode = findInodeOf(path); + // find an inode value for the given path + if (!inode) { + inode = generateInodeId(); + err |= write(inode, buffer, 0, fileType); // ensure file exists before indexing it + err |= insertDirectoryEntry(dirPath, fileName, inode); + } + + if (!err) { + err = write(inode, buffer, size, fileType); + } + + return err; +} +#ifdef _MSC_VER +#pragma warning(default:4244) +#endif + +#ifdef _MSC_VER +#pragma warning(disable:4244) +#endif +template +int FileSystemTemplate::write(uint64_t inode, void *buffer, uint64_t size, uint8_t fileType) { + if (m_ownsBuff) { + while (m_store->spaceNeeded(size) > m_store->available()) { + expand(this->size() * 2); + } + } + return m_store->write(inode, buffer, size, fileType); +} +#ifdef _MSC_VER +#pragma warning(default:4244) +#endif + +#ifdef _MSC_VER +#pragma warning(disable:4244) +#endif +template +uint64_t FileSystemTemplate::findInodeOf(const char *path) { + const auto pathLen = ox_strlen(path); + PathIterator it(path, pathLen); + char fileName[pathLen]; + uint64_t inode = INODE_ROOT_DIR; + while (it.hasNext() && it.next(fileName, pathLen) == 0 && ox_strlen(fileName)) { + auto dirStat = stat(inode); + if (dirStat.inode && dirStat.size >= sizeof(Directory)) { + uint8_t dirBuffer[dirStat.size]; + auto dir = (Directory*) dirBuffer; + if (read(inode, dirBuffer, dirStat.size) == 0) { + if (dirStat.fileType == FileType::FileType_Directory) { + inode = dir->getFileInode(fileName); + } else { + inode = 0; // null out inode and break + break; + } + } else { + inode = 0; // null out inode and break + break; + } + } else { + inode = 0; // null out inode and break + break; + } + } + return inode; +} +#ifdef _MSC_VER +#pragma warning(default:4244) +#endif + +template +void FileSystemTemplate::resize(uint64_t size) { + return m_store->resize(size); +} + +template +uint64_t FileSystemTemplate::spaceNeeded(uint64_t size) { + return m_store->spaceNeeded(size); +} + +template +uint64_t FileSystemTemplate::available() { + return m_store->available(); +} + +template +uint64_t FileSystemTemplate::size() { + return m_store->size(); +} + +template +uint8_t *FileSystemTemplate::buff() { + return (uint8_t*) m_store; +} + +#ifdef _MSC_VER +#pragma warning(disable:4244) +#endif +template +uint8_t *FileSystemTemplate::format(uint8_t *buffer, typename FileStore::FsSize_t size, bool useDirectories) { + buffer = FileStore::format(buffer, size, (uint16_t) FS_TYPE); + + if (buffer && useDirectories) { + Directory dir; + FileSystemTemplate fs((uint8_t*) buffer); + fs.write(INODE_ROOT_DIR, &dir, sizeof(dir), FileType::FileType_Directory); + } + + return (uint8_t*) buffer; +} +#ifdef _MSC_VER +#pragma warning(default:4244) +#endif + +template +uint64_t FileSystemTemplate::generateInodeId() { + Random rand; + read(INODE_RANDOM, &rand, sizeof(rand)); + + uint64_t inode = 0; + // find an inode value for the given path + while (!inode) { + inode = rand.gen(); + inode >>= 64 - 8 * sizeof(typename FileStore::InodeId_t); + + // make sure this does not already exist + if (inode < INODE_RESERVED_END || stat(inode).inode) { + // that result was unusable, try again + inode = 0; + } + } + + write(INODE_RANDOM, &rand, sizeof(rand)); + + return inode; +} + +#ifdef _MSC_VER +#pragma warning(disable:4244) +#endif +template +int FileSystemTemplate::insertDirectoryEntry(const char *dirPath, const char *fileName, uint64_t inode) { + auto s = stat(dirPath); + if (s.inode) { + auto spaceNeeded = DirectoryEntry::spaceNeeded(fileName); + size_t dirBuffSize = s.size + spaceNeeded; + uint8_t dirBuff[dirBuffSize]; + int err = read(s.inode, dirBuff, dirBuffSize); + + if (!err) { + auto dir = (Directory*) dirBuff; + dir->size += spaceNeeded; + dir->children++; + auto entry = (DirectoryEntry*) &dirBuff[s.size]; + entry->inode = inode; + entry->setName(fileName); + err = write(s.inode, dirBuff, dirBuffSize, FileType_Directory); + err |= m_store->incLinks(inode); + return err; + } else { + return 1; + } + } else { + return 2; + } +} +#ifdef _MSC_VER +#pragma warning(default:4244) +#endif + +template +int FileSystemTemplate::move(const char *src, const char *dest) { + auto inode = stat(src).inode; + if (inode && !stat(dest).inode) { + int err = 0; + + size_t srcLen = ox_strlen(src); + char srcDirPath[srcLen]; + char srcFileName[srcLen]; + PathIterator srcPathReader(src, srcLen); + err |= srcPathReader.fileName(srcFileName, srcLen); + err |= srcPathReader.dirPath(srcDirPath, srcLen); + if (err) { + return err; + } + + size_t destLen = ox_strlen(dest); + char destDirPath[destLen]; + char destFileName[destLen]; + PathIterator destPathReader(dest, destLen); + err |= destPathReader.fileName(destFileName, destLen); + err |= destPathReader.dirPath(destDirPath, destLen); + if (err) { + return err; + } + + err = rmDirectoryEntry(src); + if (err) { + return err; + } + + err = insertDirectoryEntry(destDirPath, destFileName, inode); + if (!err) { + return err; + } + + return 0; + } else { + return 1; + } +} + +template +int FileSystemTemplate::rmDirectoryEntry(const char *path) { + int err = 0; + size_t pathLen = ox_strlen(path); + char dirPath[pathLen]; + char fileName[pathLen]; + PathIterator pathReader(path, pathLen); + err |= pathReader.fileName(fileName, pathLen); + err |= pathReader.dirPath(dirPath, pathLen); + if (err) { + return err; + } + + auto dirStat = stat(dirPath); + auto dirBuffLen = dirStat.size; + uint8_t dirBuff[dirBuffLen]; + + err = read(dirStat.inode, dirBuff, dirBuffLen); + if (err) { + return err; + } + + auto dir = (Directory*) dirBuff; + auto inode = dir->getFileInode(fileName); + err |= dir->rmFile(fileName); + err |= m_store->decLinks(inode); + + if (err) { + return err; + } + + err = write(dirStat.inode, dirBuff, dirBuffLen - DirectoryEntry::spaceNeeded(fileName)); + + return err; +} + +template +int FileSystemTemplate::readDirectory(const char *path, Directory *dirOut) { + int err = 0; + auto inode = findInodeOf(path); + auto dirStat = stat(inode); + auto dirBuffLen = dirStat.size; + uint8_t dirBuff[dirBuffLen]; + auto dir = (Directory*) dirBuff; + + err = read(dirStat.inode, dirBuff, dirBuffLen); + if (!err) { + return dir->copy(dirOut); + } else { + return 1; + } +} + +template +void FileSystemTemplate::expand(uint64_t newSize) { + if (newSize > size()) { + auto newBuff = new uint8_t[newSize]; + ox_memcpy(newBuff, m_store, m_store->size()); + delete[] m_store; + m_store = (FileStore*) newBuff; + resize(newSize); + } +} + +template +void FileSystemTemplate::walk(int(*cb)(const char*, uint64_t, uint64_t)) { + m_store->walk(cb); +} + +typedef FileSystemTemplate FileSystem16; +typedef FileSystemTemplate FileSystem32; +typedef FileSystemTemplate FileSystem64; + +} diff --git a/src/ox/fs/oxfstool.cpp b/src/ox/fs/oxfstool.cpp new file mode 100644 index 00000000..ae13ffe0 --- /dev/null +++ b/src/ox/fs/oxfstool.cpp @@ -0,0 +1,395 @@ +/* + * Copyright 2015 - 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/. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "toollib.hpp" + +// suppress warnings about using fopen +#ifdef _MSC_VER +#pragma warning(disable:4996) +#endif + +using namespace ox; +using namespace std; + +const static auto oxfstoolVersion = "1.4.0"; +const static auto usage = "usage:\n" +"\toxfs format [16,32,64] \n" +"\toxfs read \n" +"\toxfs write \n" +"\toxfs write-expand \n" +"\toxfs rm \n" +"\toxfs compact \n" +"\toxfs walk \n" +"\toxfs version\n"; + +size_t bytes(const char *str) { + auto size = ::ox_strlen(str); + const auto lastChar = str[size-1]; + auto multiplier = 1; + char copy[size + 1]; + ox_memcpy(copy, str, size + 1); + // parse size unit + if (lastChar < '0' || lastChar > '9') { + copy[size-1] = 0; + switch (lastChar) { + case 'k': + case 'K': + multiplier = 1024; + break; + case 'm': + case 'M': + multiplier = 1024 * 1024; + break; + case 'g': + case 'G': + multiplier = 1024 * 1024 * 1024; + break; + default: + multiplier = -1; + } + } + return ox_atoi(copy) * multiplier; +} + +int format(int argc, char **args) { + printf("Creating file system...\n"); + auto err = 0; + if (argc >= 5) { + auto type = ox_atoi(args[2]); + auto size = bytes(args[3]); + auto path = args[4]; + auto buff = new uint8_t[size]; + + + if (size < sizeof(FileStore64)) { + err = 1; + cerr << "File system size " << size << " too small, must be at least " << sizeof(FileStore64) << endl; + } + + if (!err) { + // format + switch (type) { + case 16: + FileSystem16::format(buff, (FileStore16::FsSize_t) size, true); + break; + case 32: + FileSystem32::format(buff, (FileStore32::FsSize_t) size, true); + break; + case 64: + FileSystem64::format(buff, size, true); + break; + default: + err = 1; + } + + if (!err) { + auto file = fopen(path, "wb"); + if (file) { + err = fwrite(buff, size, 1, file) != 1; + err |= fclose(file); + if (err) { + fprintf(stderr, "Could not write to file: %s.\n", path); + } + } else { + fprintf(stderr, "Could not open file: %s.\n", path); + } + } + } + + delete []buff; + + if (err == 0) { + cerr << "Created file system " << path << endl; + cerr << " type " << type << endl; + cerr << " wrote " << size << " bytes\n"; + } + } else { + fprintf(stderr, "Insufficient arguments\n"); + } + + return err; +} + +int read(int argc, char **args) { + auto err = 1; + if (argc >= 4) { + auto fsPath = args[2]; + auto inode = ox_atoi(args[3]); + size_t fsSize; + size_t fileSize; + + auto fsBuff = loadFileBuff(fsPath, &fsSize); + + if (fsBuff) { + auto fs = createFileSystem(fsBuff, fsSize); + + if (fs) { + auto output = fs->read(inode, &fileSize); + + if (output) { + fwrite(output, fileSize, 1, stdout); + delete []output; + err = 0; + } + + delete fs; + delete []fsBuff; + } else { + fprintf(stderr, "Invalid file system type: %d.\n", *(uint32_t*) fsBuff); + } + } else { + fprintf(stderr, "Could not open file: %s\n", fsPath); + } + } else { + fprintf(stderr, "Insufficient arguments\n"); + } + return err; +} + +int write(int argc, char **args, bool expand) { + auto err = 0; + if (argc >= 5) { + auto fsPath = args[2]; + auto inode = ox_atoi(args[3]); + auto srcPath = args[4]; + size_t srcSize; + + auto fsFile = fopen(fsPath, "rb"); + if (fsFile) { + fseek(fsFile, 0, SEEK_END); + + auto fsSize = (size_t) ftell(fsFile); + rewind(fsFile); + auto fsBuff = new uint8_t[fsSize]; + auto itemsRead = fread(fsBuff, fsSize, 1, fsFile); + fclose(fsFile); + + if (itemsRead) { + auto srcBuff = loadFileBuff(srcPath, &srcSize); + if (srcBuff) { + auto expanded = false; + auto fs = createFileSystem(fsBuff, fsSize); + if (fs) { + if (expand && fs->available() <= srcSize) { + auto needed = fs->size() + fs->spaceNeeded(srcSize); + fsSize = needed; + fs = expandCopyCleanup(fs, needed); + fsBuff = fs->buff(); + } + err |= fs->write(inode, srcBuff, srcSize); + + // compact the file system if it was expanded + if (expanded) { + fs->resize(); + } + + if (err) { + fprintf(stderr, "Could not write to file system.\n"); + } + delete fs; + } else { + fprintf(stderr, "Invalid file system type: %d.\n", *(uint32_t*) fsBuff); + err = 1; + } + + if (!err) { + fsFile = fopen(fsPath, "wb"); + + if (fsFile) { + err = fwrite(fsBuff, fsSize, 1, fsFile) != 1; + err |= fclose(fsFile); + if (err) { + fprintf(stderr, "Could not write to file system file.\n"); + } + } else { + err = 1; + } + } + delete []srcBuff; + } else { + err = 1; + fprintf(stderr, "Could not load source file: %s.\n", srcPath); + } + } + + delete []fsBuff; + } else { + fprintf(stderr, "Could not open file system\n"); + } + } else { + fprintf(stderr, "Insufficient arguments\n"); + } + return err; +} + +int compact(int argc, char **args) { + auto err = 1; + if (argc >= 2) { + auto fsPath = args[2]; + size_t fsSize; + + auto fsBuff = loadFileBuff(fsPath, &fsSize); + if (fsBuff) { + auto fs = createFileSystem(fsBuff, fsSize); + + if (fs) { + fs->resize(); + } else { + fprintf(stderr, "Invalid file system.\n"); + } + + // write back to file + auto fsFile = fopen(fsPath, "wb"); + if (fsFile) { + err = fwrite(fsBuff, fs->size(), 1, fsFile) != 1; + err |= fclose(fsFile); + if (err) { + fprintf(stderr, "Could not write to file system file.\n"); + } + } else { + err = 1; + } + + delete fs; + delete []fsBuff; + } else { + fprintf(stderr, "Could not open file: %s\n", fsPath); + } + } else { + fprintf(stderr, "Insufficient arguments\n"); + } + return err; +} + +int remove(int argc, char **args) { + auto err = 1; + if (argc >= 4) { + auto fsPath = args[2]; + auto inode = ox_atoi(args[3]); + size_t fsSize; + + auto fsBuff = loadFileBuff(fsPath, &fsSize); + if (fsBuff) { + auto fs = createFileSystem(fsBuff, fsSize); + + if (fs) { + err = fs->remove(inode); + } else { + fprintf(stderr, "Invalid file system.\n"); + } + + if (err) { + fprintf(stderr, "Could not write to file system.\n"); + } else { + auto fsFile = fopen(fsPath, "wb"); + if (fsFile) { + err = fwrite(fsBuff, fsSize, 1, fsFile) != 1; + err |= fclose(fsFile); + if (err) { + fprintf(stderr, "Could not write to file system file.\n"); + } + } else { + err = 1; + } + } + + delete fs; + delete []fsBuff; + } else { + fprintf(stderr, "Could not open file: %s\n", fsPath); + } + } else { + fprintf(stderr, "Insufficient arguments\n"); + } + return err; +} + +int walk(int argc, char **args) { + int err = 0; + size_t fsSize; + auto fsPath = args[2]; + auto fsBuff = loadFileBuff(fsPath, &fsSize); + if (fsBuff) { + auto fs = createFileSystem(fsBuff, fsSize); + if (fs) { + cout << setw(9) << "Type |"; + cout << setw(10) << "Start |"; + cout << setw(10) << "End |"; + cout << setw(8) << "Size"; + cout << endl; + cout << "-------------------------------------"; + cout << endl; + fs->walk([](const char *type, uint64_t start, uint64_t end) { + cout << setw(7) << type << " |"; + cout << setw(8) << start << " |"; + cout << setw(8) << end << " |"; + cout << setw(8) << (end - start); + cout << endl; + return 0; + }); + delete fs; + } else { + cerr << "Invalid file system.\n"; + err = 1; + } + delete []fsBuff; + } else { + err = 2; + } + return err; +} + +int help(int, char**) { + cout << usage << endl; + return 0; +} + +int version(int, char**) { + cout << "oxfstool version " << oxfstoolVersion << endl; + cout << "oxfs format version " << FileStore16::VERSION << endl; + return 0; +} + +int main(int argc, char **args) { + auto err = 0; + map cmdMap = { + { "format", format }, + { "read", read }, + { "write", [](int argc, char **args) { return write(argc, args, false); } }, + { "write-expand", [](int argc, char **args) { return write(argc, args, true); } }, + { "compact", compact }, + { "rm", remove }, + { "walk", walk }, + { "help", help }, + { "version", version }, + }; + + if (argc > 1) { + auto cmd = args[1]; + auto f = cmdMap[cmd]; + if (f) { + err = f(argc, args); + } else { + cout << "Command '" << cmd << "' not recognized." << endl; + err = 1; + } + } else { + help(argc, args); + } + + return err; +} diff --git a/src/ox/fs/pathiterator.cpp b/src/ox/fs/pathiterator.cpp new file mode 100644 index 00000000..dae07a7e --- /dev/null +++ b/src/ox/fs/pathiterator.cpp @@ -0,0 +1,107 @@ +/* + * Copyright 2015 - 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/. + */ + +#include +#include +#include "pathiterator.hpp" + +namespace ox { + +PathIterator::PathIterator(const char *path, size_t maxSize) { + m_path = path; + m_maxSize = maxSize; +} + +/** + * @return 0 if no error + */ +int PathIterator::dirPath(char *out, size_t outSize) { + int idx = ox_lastIndexOf(m_path, '/', m_maxSize); + size_t size = idx + 1; + if (idx >= 0 && size < outSize) { + ox_memcpy(out, m_path, size); + out[size] = 0; + return 0; + } else { + return 1; + } +} + +/** + * @return 0 if no error + */ +int PathIterator::fileName(char *out, size_t outSize) { + auto idx = ox_lastIndexOf(m_path, '/', m_maxSize); + if (idx >= 0) { + idx++; // pass up the preceding / + size_t fileNameSize = ox_strlen(&m_path[idx]); + if (fileNameSize < outSize) { + ox_memcpy(out, &m_path[idx], fileNameSize); + out[fileNameSize] = 0; + return 0; + } else { + return 1; + } + } else { + return 2; + } +} + +// Gets the next item in the path +int PathIterator::next(char *pathOut, size_t pathOutSize) { + size_t size = 0; + int retval = 1; + if (m_iterator < m_maxSize && ox_strlen(&m_path[m_iterator])) { + retval = 0; + if (m_path[m_iterator] == '/') { + m_iterator++; + } + 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); + } + size_t end = substr - m_path; + size = end - start; + // cannot fit the output in the output parameter + if (size >= pathOutSize) { + return -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; +} + +bool PathIterator::hasNext() { + size_t size = 0; + if (m_iterator < m_maxSize && ox_strlen(&m_path[m_iterator])) { + 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); + } + size_t end = substr - m_path; + size = end - start; + } + return size > 0; +} + +} diff --git a/src/ox/fs/pathiterator.hpp b/src/ox/fs/pathiterator.hpp new file mode 100644 index 00000000..4d55f7f0 --- /dev/null +++ b/src/ox/fs/pathiterator.hpp @@ -0,0 +1,42 @@ +/* + * Copyright 2015 - 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/. + */ + +#pragma once + +#include + +namespace ox { + +class PathIterator { + private: + const char *m_path = nullptr; + size_t m_iterator = 0; + size_t m_maxSize = 0; + + public: + PathIterator(const char *path, size_t maxSize); + + /** + * @return 0 if no error + */ + int dirPath(char *pathOut, size_t pathOutSize); + + /** + * @return 0 if no error + */ + int fileName(char *out, size_t outSize); + + /** + * @return 0 if no error + */ + int next(char *pathOut, size_t pathOutSize); + + bool hasNext(); +}; + +} diff --git a/src/ox/fs/test/CMakeLists.txt b/src/ox/fs/test/CMakeLists.txt new file mode 100644 index 00000000..c6454ef8 --- /dev/null +++ b/src/ox/fs/test/CMakeLists.txt @@ -0,0 +1,69 @@ +cmake_minimum_required(VERSION 2.8) + +add_executable( + FileStoreFormat + filestore_format.cpp +) + +add_executable( + FileSystemFormat + filesystem_format.cpp +) + +add_executable( + FileStoreIO + filestoreio.cpp +) + +add_executable( + FSTests + tests.cpp +) + +target_link_libraries( + FileStoreFormat + OxFS + OxStd + OxLog +) + +target_link_libraries( + FileSystemFormat + OxFS + OxStd + OxLog +) + +target_link_libraries( + FileStoreIO + OxFS + OxStd + OxLog +) + +target_link_libraries( + FSTests + OxFS + OxStd + OxLog +) + +add_test("FileStoreFormat" FileStoreFormat) +add_test("FileSystemFormat" FileSystemFormat) +add_test("FileStoreIO" FileStoreIO) +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::dirPath" FSTests PathIterator::dirPath) +add_test("Test\\ PathIterator::fileName" FSTests PathIterator::fileName) + +add_test("Test\\ FileSystem32::findInodeOf\\ /" FSTests "FileSystem32::findInodeOf /") +add_test("Test\\ FileSystem32::write\\(string\\)" FSTests "FileSystem32::write(string)") +add_test("Test\\ FileSystem32::rmDirectoryEntry\\(string\\)" FSTests "FileSystem32::rmDirectoryEntry(string)") +add_test("Test\\ FileSystem32::remove\\(string,\\ true\\)" FSTests "FileSystem32::remove(string, true)") +add_test("Test\\ FileSystem32::move" FSTests "FileSystem32::move") +add_test("Test\\ FileSystem32::stripDirectories" FSTests "FileSystem32::stripDirectories") +add_test("Test\\ FileSystem32::ls" FSTests "FileSystem32::ls") diff --git a/src/ox/fs/test/filestore_format.cpp b/src/ox/fs/test/filestore_format.cpp new file mode 100644 index 00000000..68e761ef --- /dev/null +++ b/src/ox/fs/test/filestore_format.cpp @@ -0,0 +1,18 @@ +/* + * Copyright 2015 - 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/. + */ +#include + +using namespace ox; + +int main() { + const auto size = 65535; + uint8_t volume[size]; + uint32_t err = 0; + FileStore32::format(volume, size); + return err; +} diff --git a/src/ox/fs/test/filestoreio.cpp b/src/ox/fs/test/filestoreio.cpp new file mode 100644 index 00000000..26c1b641 --- /dev/null +++ b/src/ox/fs/test/filestoreio.cpp @@ -0,0 +1,68 @@ +/* + * Copyright 2015 - 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/. + */ +#include +#include +#include + +using namespace ox; + +template +int test() { + const uint16_t size = ~0; + uint8_t volume[size]; + char out[6]; + typename FileStore::FsSize_t outSize; + FileStore::format(volume, size); + FileStore *fs = (FileStore*) volume; + + if (fs->write(1, (void*) "Hello", 6) || + fs->read(1, (char*) out, &outSize) || + ox_strcmp("Hello", out)) { + printf("Failure 1\n"); + return 1; + } + + if (fs->write(2, (void*) "World", 6) || + fs->read(2, (char*) out, &outSize) || + ox_strcmp("World", out)) { + printf("Failure 2\n"); + return 2; + } + + // make sure first value was not overwritten + if (fs->read(1, (char*) out, &outSize) || + ox_strcmp("Hello", out)) { + printf("Failure 3\n"); + return 3; + } + + if (fs->remove(1)) { + printf("Failure 4\n"); + return 4; + } + + // make sure inode is not found + if (fs->read(1, (char*) out, &outSize) == 0) { + printf("Failure 5\n"); + return 5; + } + + // make sure 2 is still available + if (fs->write(2, (void*) "World", 6) || + fs->read(2, (char*) out, &outSize) || + ox_strcmp("World", out)) { + printf("Failure 6\n"); + return 6; + } + + return 0; +} + +int main() { + return test() || test() | test(); +} diff --git a/src/ox/fs/test/filesystem_format.cpp b/src/ox/fs/test/filesystem_format.cpp new file mode 100644 index 00000000..ff4f020a --- /dev/null +++ b/src/ox/fs/test/filesystem_format.cpp @@ -0,0 +1,23 @@ +/* + * Copyright 2015 - 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/. + */ + +#include + +using namespace ox; + +template +int test() { + const uint16_t size = ~0; + uint8_t volume[size]; + FileSystem::format(volume, size, true); + return 0; +} + +int main() { + return test() | test() | test(); +} diff --git a/src/ox/fs/test/tests.cpp b/src/ox/fs/test/tests.cpp new file mode 100644 index 00000000..47e5a4a2 --- /dev/null +++ b/src/ox/fs/test/tests.cpp @@ -0,0 +1,346 @@ +/* + * Copyright 2015 - 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/. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace ox; + +map tests = { + { + { + "PathIterator::next1", + [](string) { + int retval = 0; + string path = "/usr/share/charset.gbag"; + PathIterator it(path.c_str(), path.size()); + const auto buffSize = 1024; + char buff[buffSize]; + assert(buffSize >= path.size()); + 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()); + const auto buffSize = 1024; + char buff[buffSize]; + assert(buffSize >= path.size()); + 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()); + const auto buffSize = 1024; + char buff[buffSize]; + assert(buffSize >= path.size()); + 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()); + const auto buffSize = 1024; + char buff[buffSize]; + assert(buffSize >= path.size()); + 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()); + const auto buffSize = 1024; + char buff[buffSize]; + assert(buffSize >= path.size()); + 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()); + const auto buffSize = 1024; + char buff[buffSize]; + assert(buffSize >= path.size()); + 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()); + const auto buffSize = 1024; + char buff[buffSize]; + assert(buffSize >= path.size()); + retval |= !(it.fileName(buff, path.size()) == 0 && ox_strcmp(buff, "charset.gbag") == 0); + return retval; + } + }, + { + "FileSystem32::findInodeOf /", + [](string) { + int retval = 0; + const auto size = 1024; + uint8_t buff[size]; + FileSystem32::format(buff, (FileStore32::FsSize_t) size, true); + auto fs = (FileSystem32*) createFileSystem(buff, size); + retval |= !(fs->findInodeOf("/") == FileSystem32::INODE_ROOT_DIR); + delete fs; + return retval; + } + }, + { + "FileSystem32::write(string)", + [](string) { + int retval = 0; + auto path = "/usr/share/test.txt"; + auto dataIn = "test string"; + auto dataOutLen = ox_strlen(dataIn) + 1; + auto dataOut = new char[dataOutLen]; + + const auto size = 1024 * 1024; + auto buff = new uint8_t[size]; + FileSystem32::format(buff, (FileStore32::FsSize_t) size, true); + auto fs = (FileSystem32*) createFileSystem(buff, size); + + retval |= fs->mkdir("/usr"); + retval |= fs->mkdir("/usr/share"); + retval |= fs->mkdir("/usr/lib"); + + retval |= fs->write(path, (void*) dataIn, ox_strlen(dataIn) + 1); + retval |= fs->read(path, dataOut, dataOutLen); + retval |= ox_strcmp(dataIn, dataOut) != 0; + + delete fs; + delete []buff; + delete []dataOut; + + return retval; + } + }, + { + "FileSystem32::rmDirectoryEntry(string)", + [](string) { + int retval = 0; + auto path = "/usr/share/test.txt"; + auto dataIn = "test string"; + auto dataOutLen = ox_strlen(dataIn) + 1; + auto dataOut = new char[dataOutLen]; + + const auto size = 1024 * 1024 * 10; + auto buff = new uint8_t[size]; + FileSystem32::format(buff, (FileStore32::FsSize_t) size, true); + auto fs = (FileSystem32*) createFileSystem(buff, size); + + retval |= fs->mkdir("/usr"); + retval |= fs->mkdir("/usr/share"); + + retval |= fs->write(path, (void*) dataIn, ox_strlen(dataIn) + 1); + retval |= fs->read(path, dataOut, dataOutLen); + retval |= ox_strcmp(dataIn, dataOut) != 0; + + retval |= fs->rmDirectoryEntry(path); + // the lookup should fail + retval |= fs->read(path, dataOut, dataOutLen) == 0; + + delete fs; + delete []buff; + delete []dataOut; + + return retval; + } + }, + { + "FileSystem32::remove(string, true)", + [](string) { + int retval = 0; + auto dataIn = "test string"; + auto dataOutLen = 1024 * 64; + auto dataOut = new char[dataOutLen]; + vector inodes; + + const auto size = 1024 * 1024; + auto buff = new uint8_t[size]; + FileSystem32::format(buff, (FileStore32::FsSize_t) size, true); + auto fs = (FileSystem32*) createFileSystem(buff, size); + + retval |= fs->mkdir("/usr"); + retval |= fs->mkdir("/usr/share"); + retval |= fs->write("/usr/share/test.txt", (void*) dataIn, ox_strlen(dataIn) + 1); + + inodes.push_back(fs->stat("/usr").inode); + inodes.push_back(fs->stat("/usr/share").inode); + inodes.push_back(fs->stat("/usr/share/test.txt").inode); + + retval |= fs->remove("/usr", true); + + // the lookup should fail + for (auto inode : inodes) { + retval |= fs->read(inode, dataOut, dataOutLen) == 0; + } + + delete fs; + delete []buff; + delete []dataOut; + + return retval; + } + }, + { + "FileSystem32::move", + [](string) { + int retval = 0; + auto dataIn = "test string"; + auto dataOutLen = ox_strlen(dataIn) + 1; + auto dataOut = new char[dataOutLen]; + vector inodes; + + const auto size = 1024 * 1024; + auto buff = new uint8_t[size]; + FileSystem32::format(buff, (FileStore32::FsSize_t) size, true); + auto fs = (FileSystem32*) createFileSystem(buff, size); + + retval |= fs->mkdir("/usr"); + retval |= fs->mkdir("/usr/share"); + retval |= fs->write("/usr/share/test.txt", (void*) dataIn, ox_strlen(dataIn) + 1); + + retval |= fs->move("/usr/share", "/share"); + retval |= fs->read("/share/test.txt", dataOut, dataOutLen); + retval |= !(ox_strcmp(dataIn, dataOut) == 0); + + delete fs; + delete []buff; + delete []dataOut; + + return retval; + } + }, + { + "FileSystem32::stripDirectories", + [](string) { + int retval = 0; + auto dataIn = "test string"; + auto dataOutLen = ox_strlen(dataIn) + 1; + auto dataOut = new char[dataOutLen]; + vector inodes; + + const auto size = 1024 * 1024; + auto buff = new uint8_t[size]; + FileSystem32::format(buff, (FileStore32::FsSize_t) size, true); + auto fs = (FileSystem32*) createFileSystem(buff, size); + + retval |= fs->mkdir("/usr"); + retval |= fs->mkdir("/usr/share"); + retval |= fs->write("/usr/share/test.txt", (void*) dataIn, ox_strlen(dataIn) + 1); + + auto inode = fs->stat("/usr/share/test.txt").inode; + + retval |= fs->stripDirectories(); + + // make sure normal file is still there and the directories are gone + retval |= fs->read(inode, dataOut, dataOutLen); + retval |= !(ox_strcmp(dataIn, dataOut) == 0); + retval |= !(fs->stat("/usr").inode == 0); + retval |= !(fs->stat("/usr/share").inode == 0); + + delete fs; + delete []buff; + delete []dataOut; + + return retval; + } + }, + { + "FileSystem32::ls", + [](string) { + int retval = 0; + auto dataIn = "test string"; + auto dataOutLen = ox_strlen(dataIn) + 1; + auto dataOut = new char[dataOutLen]; + vector inodes; + vector> files; + + const auto size = 1024 * 1024; + auto buff = new uint8_t[size]; + FileSystem32::format(buff, (FileStore32::FsSize_t) size, true); + auto fs = (FileSystem32*) createFileSystem(buff, size); + + retval |= fs->mkdir("/usr"); + retval |= fs->mkdir("/usr/share"); + retval |= fs->write("/usr/share/a.txt", (void*) dataIn, ox_strlen(dataIn) + 1); + retval |= fs->write("/usr/share/b.txt", (void*) dataIn, ox_strlen(dataIn) + 1); + retval |= fs->write("/usr/share/c.txt", (void*) dataIn, ox_strlen(dataIn) + 1); + + fs->ls("/usr/share/", &files); + + retval |= !(files[0].name == "."); + retval |= !(files[1].name == ".."); + retval |= !(files[2].name == "a.txt"); + retval |= !(files[3].name == "b.txt"); + retval |= !(files[4].name == "c.txt"); + + delete fs; + delete []buff; + delete []dataOut; + + return retval; + } + }, + }, +}; + +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; +} diff --git a/src/ox/fs/toollib.cpp b/src/ox/fs/toollib.cpp new file mode 100644 index 00000000..fc7836e9 --- /dev/null +++ b/src/ox/fs/toollib.cpp @@ -0,0 +1,33 @@ +/* + * Copyright 2015 - 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/. + */ + +#include +#include + +#include "toollib.hpp" + +uint8_t *loadFileBuff(FILE *file, ::size_t *sizeOut) { + if (file) { + fseek(file, 0, SEEK_END); + const auto size = ftell(file); + rewind(file); + auto buff = new uint8_t[size]; + auto itemsRead = fread(buff, size, 1, file); + fclose(file); + if (sizeOut) { + *sizeOut = itemsRead ? size : 0; + } + return buff; + } else { + return nullptr; + } +} + +uint8_t *loadFileBuff(const char *path, ::size_t *sizeOut) { + return loadFileBuff(fopen(path, "rb"), sizeOut); +} diff --git a/src/ox/fs/toollib.hpp b/src/ox/fs/toollib.hpp new file mode 100644 index 00000000..8c51b776 --- /dev/null +++ b/src/ox/fs/toollib.hpp @@ -0,0 +1,14 @@ +/* + * Copyright 2015 - 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/. + */ + +#include +#include + +uint8_t *loadFileBuff(FILE *file, size_t *sizeOut = nullptr); + +uint8_t *loadFileBuff(const char *path, size_t *sizeOut = nullptr); diff --git a/src/ox/log/CMakeLists.txt b/src/ox/log/CMakeLists.txt new file mode 100644 index 00000000..ca9dfcc4 --- /dev/null +++ b/src/ox/log/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 2.8) + +add_library( + OxLog + log.cpp +) + +set_property( + TARGET + OxLog + PROPERTY + POSITION_INDEPENDENT_CODE ON +) + +install( + FILES + log.hpp + DESTINATION + include/ox/mc +) + +install(TARGETS OxLog + LIBRARY DESTINATION lib/ox + ARCHIVE DESTINATION lib/ox +) diff --git a/src/ox/log/log.cpp b/src/ox/log/log.cpp new file mode 100644 index 00000000..0624fd0d --- /dev/null +++ b/src/ox/log/log.cpp @@ -0,0 +1,105 @@ +/* + * Copyright 2015 - 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/. + */ + +#include +#include + +#include "log.hpp" + +namespace ox { + +class Logger { + + private: + // void* stand-in for FILE* + const char *m_path = nullptr; + + public: + Logger(const char *path = nullptr); + + ~Logger(); + + void log(LogLevel_t level, const char *msg, va_list args); + + void info(const char *msg, ...); + + void debug(const char *msg, ...); + + void error(const char *msg, ...); +}; + +Logger::Logger(const char *path) { + m_path = path; +} + +Logger::~Logger() { +} + +void Logger::log(LogLevel_t level, const char *msg, va_list args) { + if (m_path) { + auto file = fopen(m_path, "a"); + vfprintf(file, msg, args); + fprintf(file, "\n"); + fclose(file); + } +} + +void Logger::info(const char *msg, ...) { + va_list args; + va_start(args, msg); + log(LogLevel_t::Info, msg, args); + va_end(args); +} + +void Logger::debug(const char *msg, ...) { + va_list args; + va_start(args, msg); + log(LogLevel_t::Debug, msg, args); + va_end(args); +} + +void Logger::error(const char *msg, ...) { + va_list args; + va_start(args, msg); + log(LogLevel_t::Error, msg, args); + va_end(args); +} + + +static Logger logger; + +void logFile(const char *path) { + logger = Logger(path); +} + +void log(LogLevel_t level, const char *msg, va_list args) { + logger.log(level, msg, args); +} + +void info(const char *msg, ...) { + va_list args; + va_start(args, msg); + log(LogLevel_t::Info, msg, args); + va_end(args); +} + +void debug(const char *msg, ...) { + va_list args; + va_start(args, msg); + log(LogLevel_t::Debug, msg, args); + va_end(args); +} + +void error(const char *msg, ...) { + va_list args; + va_start(args, msg); + log(LogLevel_t::Error, msg, args); + va_end(args); +} + +} diff --git a/src/ox/log/log.hpp b/src/ox/log/log.hpp new file mode 100644 index 00000000..29b7ccf3 --- /dev/null +++ b/src/ox/log/log.hpp @@ -0,0 +1,27 @@ +/* + * Copyright 2015 - 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/. + */ + +#pragma once + +namespace ox { + +enum class LogLevel_t: int { + Info, + Debug, + Error, +}; + +void logFile(const char *path); + +void info(const char *msg, ...); + +void debug(const char *msg, ...); + +void error(const char *msg, ...); + +} diff --git a/src/ox/mc/CMakeLists.txt b/src/ox/mc/CMakeLists.txt new file mode 100644 index 00000000..0312e35f --- /dev/null +++ b/src/ox/mc/CMakeLists.txt @@ -0,0 +1,34 @@ +cmake_minimum_required(VERSION 2.8) + +add_library( + OxMetalClaw + presencemask.cpp + read.cpp + write.cpp +) + +set_property( + TARGET + OxMetalClaw + PROPERTY + POSITION_INDEPENDENT_CODE ON +) + +install( + FILES + err.hpp + presencemask.hpp + read.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() diff --git a/src/ox/mc/err.hpp b/src/ox/mc/err.hpp new file mode 100644 index 00000000..19510387 --- /dev/null +++ b/src/ox/mc/err.hpp @@ -0,0 +1,19 @@ +/* + * Copyright 2015 - 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/. + */ + +#pragma once + +namespace ox { + +enum { + MC_PRESENCEMASKOUTBOUNDS = 1, + MC_BUFFENDED = 2, + MC_OUTBUFFENDED = 4 +}; + +} diff --git a/src/ox/mc/mc.hpp b/src/ox/mc/mc.hpp new file mode 100644 index 00000000..953e4721 --- /dev/null +++ b/src/ox/mc/mc.hpp @@ -0,0 +1,12 @@ +/* + * Copyright 2015 - 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/. + */ + +#pragma once + +#include "read.hpp" +#include "write.hpp" diff --git a/src/ox/mc/presencemask.cpp b/src/ox/mc/presencemask.cpp new file mode 100644 index 00000000..2ae87bb3 --- /dev/null +++ b/src/ox/mc/presencemask.cpp @@ -0,0 +1,49 @@ +/* + * Copyright 2015 - 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/. + */ + +#include +#include "err.hpp" +#include "presencemask.hpp" + +namespace ox { + +FieldPresenseMask::FieldPresenseMask(uint8_t *mask, size_t maxLen) { + m_mask = mask; + m_maxLen = maxLen; +} + +bool FieldPresenseMask::get(int i) { + if (i / 8 < m_maxLen) { + return (m_mask[i / 8] >> (i % 8)) & 1; + } else { + return MC_PRESENCEMASKOUTBOUNDS; + } +} + +int FieldPresenseMask::set(int i, bool on) { + if (i / 8 < m_maxLen) { + if (on) { + m_mask[i / 8] |= 1 << (i % 8); + } else { + m_mask[i / 8] &= ~(1 << (i % 8)); + } + return 0; + } else { + return MC_PRESENCEMASKOUTBOUNDS; + } +} + +void FieldPresenseMask::setMaxLen(int maxLen) { + m_maxLen = maxLen; +} + +int FieldPresenseMask::getMaxLen() { + return m_maxLen; +} + +} diff --git a/src/ox/mc/presencemask.hpp b/src/ox/mc/presencemask.hpp new file mode 100644 index 00000000..13da66c3 --- /dev/null +++ b/src/ox/mc/presencemask.hpp @@ -0,0 +1,32 @@ +/* + * Copyright 2015 - 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/. + */ + +#pragma once + +#include + +namespace ox { + +class FieldPresenseMask { + private: + uint8_t *m_mask; + int m_maxLen = 0; + + public: + FieldPresenseMask(uint8_t *mask, size_t maxLen); + + bool get(int i); + + int set(int i, bool on); + + void setMaxLen(int); + + int getMaxLen(); +}; + +} diff --git a/src/ox/mc/read.cpp b/src/ox/mc/read.cpp new file mode 100644 index 00000000..3d4f5100 --- /dev/null +++ b/src/ox/mc/read.cpp @@ -0,0 +1,55 @@ +/* + * Copyright 2015 - 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/. + */ + +#include +#include +#include "read.hpp" + +namespace ox { + +MetalClawReader::MetalClawReader(uint8_t *buff, size_t buffLen): m_fieldPresence(buff, buffLen) { + m_buff = buff; + m_buffLen = buffLen; +} + +int MetalClawReader::op(const char*, int16_t *val) { + return readInteger(val); +} + +int MetalClawReader::op(const char*, int32_t *val) { + return readInteger(val); +} + +int MetalClawReader::op(const char*, int64_t *val) { + return readInteger(val); +} + +int MetalClawReader::op(const char*, uint16_t *val) { + return readInteger(val); +} + +int MetalClawReader::op(const char*, uint32_t *val) { + return readInteger(val); +} + +int MetalClawReader::op(const char*, uint64_t *val) { + return readInteger(val); +} + +int MetalClawReader::op(const char*, bool *val) { + *val = m_fieldPresence.get(m_field++); + return 0; +} + +void MetalClawReader::setFields(int fields) { + m_fields = fields; + m_buffIt = (fields / 8 + 1) - (fields % 8 == 0); + m_fieldPresence.setMaxLen(m_buffIt); +} + +} diff --git a/src/ox/mc/read.hpp b/src/ox/mc/read.hpp new file mode 100644 index 00000000..11b39287 --- /dev/null +++ b/src/ox/mc/read.hpp @@ -0,0 +1,154 @@ +/* + * Copyright 2015 - 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/. + */ + +#pragma once + +#include +#include +#include "err.hpp" +#include "presencemask.hpp" + +namespace ox { + +class MetalClawReader { + + private: + FieldPresenseMask m_fieldPresence; + int m_fields = 0; + int m_field = 0; + size_t m_buffIt = 0; + size_t m_buffLen = 0; + uint8_t *m_buff = nullptr; + + public: + MetalClawReader(uint8_t *buff, size_t buffLen); + + int op(const char*, int16_t *val); + int op(const char*, int32_t *val); + int op(const char*, int64_t *val); + + int op(const char*, uint16_t *val); + int op(const char*, uint32_t *val); + int op(const char*, uint64_t *val); + + int op(const char*, bool *val); + + template + int op(const char*, T *val, size_t len); + + template + int op(const char*, T *val); + + template + int op(const char*, ox::bstring *val); + + void setFields(int fields); + + private: + template + int readInteger(I *val); +}; + +template +int MetalClawReader::op(const char*, T *val) { + int err = 0; + if (m_fieldPresence.get(m_field)) { + MetalClawReader reader(m_buff + m_buffIt, m_buffLen - m_buffIt); + err |= ioOp(&reader, val); + m_buffIt += reader.m_buffIt; + } + m_field++; + return err; +}; + +template +int MetalClawReader::op(const char*, ox::bstring *val) { + int err = 0; + if (m_fieldPresence.get(m_field)) { + // read the length + typedef uint32_t StringLength; + size_t size = 0; + if (m_buffIt + sizeof(StringLength) < m_buffLen) { + size = ox::bigEndianAdapt(*((StringLength*) &m_buff[m_buffIt])); + m_buffIt += sizeof(StringLength); + } else { + err |= MC_BUFFENDED; + } + + // read the string + if (val->cap() >= size) { + if (m_buffIt + size < m_buffLen) { + ox_memcpy(val, &m_buff[m_buffIt], size); + m_buffIt += size; + } else { + err |= MC_BUFFENDED; + } + } else { + err |= MC_OUTBUFFENDED; + } + } else { + *val = ""; + } + m_field++; + return err; +}; + +template +int MetalClawReader::readInteger(I *val) { + int err = 0; + if (m_fieldPresence.get(m_field)) { + if (m_buffIt + sizeof(I) < m_buffLen) { + *val = ox::bigEndianAdapt(*((I*) &m_buff[m_buffIt])); + m_buffIt += sizeof(I); + } else { + err = MC_BUFFENDED; + } + } else { + *val = 0; + } + m_field++; + return err; +}; + +template +int MetalClawReader::op(const char*, T *val, size_t valLen) { + int err = 0; + if (m_fieldPresence.get(m_field)) { + // read the length + typedef uint32_t ArrayLength; + size_t len = 0; + if (m_buffIt + sizeof(ArrayLength) < m_buffLen) { + len = ox::bigEndianAdapt(*((T*) &m_buff[m_buffIt])); + m_buffIt += sizeof(ArrayLength); + } else { + err = MC_BUFFENDED; + } + + // read the list + if (valLen >= len) { + MetalClawReader reader(m_buff + m_buffIt, m_buffLen - m_buffIt); + reader.setFields(len); + for (size_t i = 0; i < len; i++) { + err |= reader.op("", &val[i]); + } + m_buffIt += reader.m_buffIt; + } else { + err = MC_OUTBUFFENDED; + } + } + m_field++; + return err; +}; + +template +int read(uint8_t *buff, size_t buffLen, T *val) { + MetalClawReader reader(buff, buffLen); + return ioOp(&reader, val); +} + +} diff --git a/src/ox/mc/test/CMakeLists.txt b/src/ox/mc/test/CMakeLists.txt new file mode 100644 index 00000000..26848191 --- /dev/null +++ b/src/ox/mc/test/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 2.8) + +add_executable( + McTest + tests.cpp +) + +target_link_libraries( + McTest + OxMetalClaw + OxStd + OxLog +) + +add_test("Test\\ McTest\\ Writer" McTest MetalClawWriter) +add_test("Test\\ McTest\\ Reader" McTest MetalClawReader) diff --git a/src/ox/mc/test/tests.cpp b/src/ox/mc/test/tests.cpp new file mode 100644 index 00000000..4190f7e6 --- /dev/null +++ b/src/ox/mc/test/tests.cpp @@ -0,0 +1,158 @@ +/* + * Copyright 2015 - 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/. + */ + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace ox; + +struct TestStructNest { + bool Bool = false; + uint32_t Int = 0; + 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; + bstring<32> String = ""; + uint32_t List[4] = {0, 0, 0 , 0}; + TestStructNest EmptyStruct; + TestStructNest Struct; +}; + +template +int ioOp(T *io, TestStructNest *obj) { + int32_t err = 0; + io->setFields(3); + err |= io->op("Bool", &obj->Bool); + err |= io->op("Int", &obj->Int); + err |= io->op("String", &obj->String); + return err; +} + +template +int ioOp(T *io, TestStruct *obj) { + int err = 0; + io->setFields(13); + err |= io->op("Bool", &obj->Bool); + err |= io->op("Int", &obj->Int); + err |= io->op("Int1", &obj->Int1); + err |= io->op("Int2", &obj->Int2); + err |= io->op("Int3", &obj->Int3); + err |= io->op("Int4", &obj->Int4); + err |= io->op("Int5", &obj->Int5); + err |= io->op("Int6", &obj->Int6); + err |= io->op("Int7", &obj->Int7); + err |= io->op("Int8", &obj->Int8); + err |= io->op("String", &obj->String); + err |= io->op("List", obj->List, 4); + err |= io->op("EmptyStruct", &obj->EmptyStruct); + err |= io->op("Struct", &obj->Struct); + return err; +} + +map tests = { + { + { + "MetalClawWriter", + [](string) { + // This test doesn't confirm much, but it does show that the writer + // doesn't segfault + size_t buffLen = 1024; + auto buff = new uint8_t[buffLen]; + int err = 0; + TestStruct ts; + + err |= write(buff, buffLen, &ts); + + delete []buff; + + return err; + } + }, + { + "MetalClawReader", + [](string) { + int err = 0; + size_t buffLen = 1024; + auto buff = new uint8_t[buffLen]; + 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"; + + err |= write(buff, buffLen, &testIn); + err |= read(buff, buffLen, &testOut); + + err |= !(testIn.Bool == testOut.Bool); + err |= !(testIn.Int == testOut.Int); + err |= !(testIn.Int1 == testOut.Int1); + err |= !(testIn.Int2 == testOut.Int2); + err |= !(testIn.Int3 == testOut.Int3); + err |= !(testIn.Int4 == testOut.Int4); + err |= !(testIn.Int5 == testOut.Int5); + err |= !(testIn.Int6 == testOut.Int6); + err |= !(testIn.Int7 == testOut.Int7); + err |= !(testIn.Int8 == testOut.Int8); + err |= !(testIn.String == testOut.String); + err |= !(testIn.List[0] == testOut.List[0]); + err |= !(testIn.List[1] == testOut.List[1]); + err |= !(testIn.List[2] == testOut.List[2]); + err |= !(testIn.List[3] == testOut.List[3]); + err |= !(testIn.EmptyStruct.Bool == testOut.EmptyStruct.Bool); + err |= !(testIn.EmptyStruct.Int == testOut.EmptyStruct.Int); + err |= !(testIn.EmptyStruct.String == testOut.EmptyStruct.String); + err |= !(testIn.Struct.Int == testOut.Struct.Int); + err |= !(testIn.Struct.String == testOut.Struct.String); + err |= !(testIn.Struct.Bool == testOut.Struct.Bool); + + delete []buff; + + return err; + } + }, + } +}; + +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; +} diff --git a/src/ox/mc/write.cpp b/src/ox/mc/write.cpp new file mode 100644 index 00000000..cf214d0e --- /dev/null +++ b/src/ox/mc/write.cpp @@ -0,0 +1,54 @@ +/* + * Copyright 2015 - 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/. + */ + +#include +#include +#include "write.hpp" + +namespace ox { + +MetalClawWriter::MetalClawWriter(uint8_t *buff, size_t buffLen): m_fieldPresence(buff, buffLen) { + m_buff = buff; + m_buffLen = buffLen; +} + +int MetalClawWriter::op(const char*, int16_t *val) { + return appendInteger(*val); +} + +int MetalClawWriter::op(const char*, int32_t *val) { + return appendInteger(*val); +} + +int MetalClawWriter::op(const char*, int64_t *val) { + return appendInteger(*val); +} + +int MetalClawWriter::op(const char*, uint16_t *val) { + return appendInteger(*val); +} + +int MetalClawWriter::op(const char*, uint32_t *val) { + return appendInteger(*val); +} + +int MetalClawWriter::op(const char*, uint64_t *val) { + return appendInteger(*val); +} + +int MetalClawWriter::op(const char*, bool *val) { + return m_fieldPresence.set(m_field++, *val); +} + +void MetalClawWriter::setFields(int fields) { + m_fields = fields; + m_buffIt = (fields / 8 + 1) - (fields % 8 == 0); + m_fieldPresence.setMaxLen(m_buffIt); +} + +} diff --git a/src/ox/mc/write.hpp b/src/ox/mc/write.hpp new file mode 100644 index 00000000..8380b3c7 --- /dev/null +++ b/src/ox/mc/write.hpp @@ -0,0 +1,152 @@ +/* + * Copyright 2015 - 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/. + */ + +#pragma once + +#include +#include +#include "err.hpp" +#include "presencemask.hpp" + +namespace ox { + +class MetalClawWriter { + + private: + FieldPresenseMask m_fieldPresence; + int m_fields = 0; + int m_field = 0; + size_t m_buffIt = 0; + size_t m_buffLen = 0; + uint8_t *m_buff = nullptr; + + public: + MetalClawWriter(uint8_t *buff, size_t buffLen); + + int op(const char*, int16_t *val); + int op(const char*, int32_t *val); + int op(const char*, int64_t *val); + + int op(const char*, uint16_t *val); + int op(const char*, uint32_t *val); + int op(const char*, uint64_t *val); + + int op(const char*, bool *val); + + template + int op(const char*, T *val, size_t len); + + template + int op(const char*, ox::bstring *val); + + template + int op(const char*, T *val); + + void setFields(int fields); + + private: + template + int appendInteger(I val); +}; + +template +int MetalClawWriter::op(const char*, ox::bstring *val) { + int err = 0; + bool fieldSet = false; + if (val->len()) { + // write the length + typedef uint32_t StringLength; + if (m_buffIt + sizeof(StringLength) + val->size() < m_buffLen) { + *((StringLength*) &m_buff[m_buffIt]) = ox::bigEndianAdapt((StringLength) val->size()); + m_buffIt += sizeof(StringLength); + + // write the string + ox_memcpy(&m_buff[m_buffIt], val, val->size()); + m_buffIt += val->size(); + fieldSet = true; + } else { + err = MC_BUFFENDED; + } + } + err |= m_fieldPresence.set(m_field, fieldSet); + m_field++; + return err; +}; + +template +int MetalClawWriter::op(const char*, T *val) { + int err = 0; + bool fieldSet = false; + MetalClawWriter writer(m_buff + m_buffIt, m_buffLen - m_buffIt); + err |= ioOp(&writer, val); + if ((size_t) writer.m_fieldPresence.getMaxLen() < writer.m_buffIt) { + m_buffIt += writer.m_buffIt; + fieldSet = true; + } + err |= m_fieldPresence.set(m_field, fieldSet); + m_field++; + return err; +}; + +template +int MetalClawWriter::appendInteger(I val) { + int err = 0; + bool fieldSet = false; + if (val) { + if (m_buffIt + sizeof(I) < m_buffLen) { + *((I*) &m_buff[m_buffIt]) = ox::bigEndianAdapt(val); + fieldSet = true; + m_buffIt += sizeof(I); + } else { + err |= MC_BUFFENDED; + } + } + err |= m_fieldPresence.set(m_field, fieldSet); + m_field++; + return err; +}; + +template +int MetalClawWriter::op(const char*, T *val, size_t len) { + int err = 0; + bool fieldSet = false; + + if (len) { + // write the length + typedef uint32_t ArrayLength; + if (m_buffIt + sizeof(ArrayLength) < m_buffLen) { + *((T*) &m_buff[m_buffIt]) = ox::bigEndianAdapt((ArrayLength) len); + m_buffIt += sizeof(ArrayLength); + } else { + err = MC_BUFFENDED; + } + + MetalClawWriter writer(m_buff + m_buffIt, m_buffLen - m_buffIt); + writer.setFields(len); + + // write the array + for (size_t i = 0; i < len; i++) { + err |= writer.op("", &val[i]); + } + + m_buffIt += writer.m_buffIt; + fieldSet = true; + } + + err |= m_fieldPresence.set(m_field, fieldSet); + m_field++; + return err; +}; + +template +int write(uint8_t *buff, size_t buffLen, T *val) { + MetalClawWriter writer(buff, buffLen); + return ioOp(&writer, val); +} + +} diff --git a/src/ox/std/CMakeLists.txt b/src/ox/std/CMakeLists.txt new file mode 100644 index 00000000..5bd0ac2b --- /dev/null +++ b/src/ox/std/CMakeLists.txt @@ -0,0 +1,38 @@ +cmake_minimum_required(VERSION 2.8) + +add_library( + OxStd + memops.cpp + random.cpp + strops.cpp +) + +set_property( + TARGET + OxStd + PROPERTY + POSITION_INDEPENDENT_CODE ON +) + +install( + FILES + bitops.hpp + byteswap.hpp + memops.hpp + random.hpp + string.hpp + strops.hpp + std.hpp + types.hpp + DESTINATION + include/ox/std +) + +install(TARGETS OxStd + LIBRARY DESTINATION lib/ox + ARCHIVE DESTINATION lib/ox +) + +if(OX_RUN_TESTS STREQUAL "ON") + add_subdirectory(test) +endif() diff --git a/src/ox/std/bitops.hpp b/src/ox/std/bitops.hpp new file mode 100644 index 00000000..86379023 --- /dev/null +++ b/src/ox/std/bitops.hpp @@ -0,0 +1,19 @@ +/* + * Copyright 2015 - 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/. + */ + +#pragma once + +#include "types.hpp" + +namespace ox { + +inline uint64_t rotateLeft(uint64_t i, int shift) { + return (i << shift) | (i >> (64 - shift)); +} + +} diff --git a/src/ox/std/byteswap.hpp b/src/ox/std/byteswap.hpp new file mode 100644 index 00000000..9bb199ee --- /dev/null +++ b/src/ox/std/byteswap.hpp @@ -0,0 +1,141 @@ +/* + * Copyright 2015 - 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/. + */ + +#pragma once + +#include "types.hpp" + +namespace ox { + +inline int16_t byteSwap(int16_t i) { + return (i << 8) | (i >> 8); +} + +inline int32_t byteSwap(int32_t i) { + return ((i >> 24) & 0x000000ff) | + ((i >> 8) & 0x0000ff00) | + ((i << 8) & 0x00ff0000) | + ((i << 24) & 0xff000000); +} + +inline int64_t byteSwap(int64_t i) { + return ((i >> 56) & 0x00000000000000ff) | + ((i >> 40) & 0x000000000000ff00) | + ((i >> 24) & 0x0000000000ff0000) | + ((i >> 8) & 0x00000000ff000000) | + ((i << 8) & 0x000000ff00000000) | + ((i << 24) & 0x0000ff0000000000) | + ((i << 40) & 0x00ff000000000000) | + ((i << 56) & 0xff00000000000000); +} + +inline uint16_t byteSwap(uint16_t i) { + return (i << 8) | (i >> 8); +} + +inline uint32_t byteSwap(uint32_t i) { + return ((i >> 24) & 0x000000ff) | + ((i >> 8) & 0x0000ff00) | + ((i << 8) & 0x00ff0000) | + ((i << 24) & 0xff000000); +} + +inline uint64_t byteSwap(uint64_t i) { + return ((i >> 56) & 0x00000000000000ff) | + ((i >> 40) & 0x000000000000ff00) | + ((i >> 24) & 0x0000000000ff0000) | + ((i >> 8) & 0x00000000ff000000) | + ((i << 8) & 0x000000ff00000000) | + ((i << 24) & 0x0000ff0000000000) | + ((i << 40) & 0x00ff000000000000) | + ((i << 56) & 0xff00000000000000); +} + + +/** + * Takes an int and byte swaps if the platform is big endian. + */ +inline int8_t bigEndianAdapt(int8_t i) { + return i; +} + +/** + * Takes an int and byte swaps if the platform is big endian. + */ +inline int16_t bigEndianAdapt(int16_t i) { +#ifdef __BIG_ENDIAN__ + return byteSwap(i); +#else + return i; +#endif +} + +/** + * Takes an int and byte swaps if the platform is big endian. + */ +inline int32_t bigEndianAdapt(int32_t i) { +#ifdef __BIG_ENDIAN__ + return byteSwap(i); +#else + return i; +#endif +} + +/** + * Takes an int and byte swaps if the platform is big endian. + */ +inline int64_t bigEndianAdapt(int64_t i) { +#ifdef __BIG_ENDIAN__ + return byteSwap(i); +#else + return i; +#endif +} + + +/** + * Takes an int and byte swaps if the platform is big endian. + */ +inline uint8_t bigEndianAdapt(uint8_t i) { + return i; +} + +/** + * Takes an int and byte swaps if the platform is big endian. + */ +inline uint16_t bigEndianAdapt(uint16_t i) { +#ifdef __BIG_ENDIAN__ + return byteSwap(i); +#else + return i; +#endif +} + +/** + * Takes an int and byte swaps if the platform is big endian. + */ +inline uint32_t bigEndianAdapt(uint32_t i) { +#ifdef __BIG_ENDIAN__ + return byteSwap(i); +#else + return i; +#endif +} + +/** + * Takes an int and byte swaps if the platform is big endian. + */ +inline uint64_t bigEndianAdapt(uint64_t i) { +#ifdef __BIG_ENDIAN__ + return byteSwap(i); +#else + return i; +#endif +} + +} diff --git a/src/ox/std/memops.cpp b/src/ox/std/memops.cpp new file mode 100644 index 00000000..ab7b5779 --- /dev/null +++ b/src/ox/std/memops.cpp @@ -0,0 +1,41 @@ +/* + * Copyright 2015 - 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/. + */ +#include "memops.hpp" + +int ox_memcmp(const void *ptr1, const void *ptr2, size_t size) { + int retval = 0; + auto block1 = ((uint8_t*) ptr1); + auto block2 = ((uint8_t*) ptr2); + for (size_t i = 0; i < size; i++) { + if (block1[i] < block2[i]) { + retval = -1; + break; + } else if (block1[i] > block2[i]) { + retval = 1; + break; + } + } + return retval; +} + +void *ox_memcpy(void *dest, const void *src, int64_t size) { + char *srcBuf = (char*) src; + char *dstBuf = (char*) dest; + for (int64_t i = 0; i < size; i++) { + dstBuf[i] = (char) srcBuf[i]; + } + return dest; +} + +void *ox_memset(void *ptr, int val, int64_t size) { + char *buf = (char*) ptr; + for (int64_t i = 0; i < size; i++) { + buf[i] = val; + } + return ptr; +} diff --git a/src/ox/std/memops.hpp b/src/ox/std/memops.hpp new file mode 100644 index 00000000..ffba6669 --- /dev/null +++ b/src/ox/std/memops.hpp @@ -0,0 +1,16 @@ +/* + * Copyright 2015 - 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/. + */ +#pragma once + +#include "types.hpp" + +int ox_memcmp(const void *ptr1, const void *ptr2, size_t size); + +void *ox_memcpy(void *src, const void *dest, int64_t size); + +void *ox_memset(void *ptr, int val, int64_t size); diff --git a/src/ox/std/random.cpp b/src/ox/std/random.cpp new file mode 100644 index 00000000..247cab5a --- /dev/null +++ b/src/ox/std/random.cpp @@ -0,0 +1,40 @@ +/* + * Copyright 2015 - 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/. + */ + +#include "bitops.hpp" +#include "random.hpp" + +namespace ox { + +RandomSeed Random::DEFAULT_SEED = {540932923848, 540932540932}; + +Random::Random(RandomSeed seed) { + m_seed[0] = seed[0]; + m_seed[1] = seed[1]; +} + +uint64_t Random::gen() { + // An implementation of the Xoroshiro128+ algorithm + auto s0 = m_seed[0]; + auto s1 = m_seed[1]; + auto retval = s0 + s1; + + s1 ^= s0; + m_seed[0] = ox::rotateLeft(s0, 55) ^ s1 ^ (s1 << 14); + m_seed[1] = ox::rotateLeft(s1, 36); + + return retval; +} + +} + + +uint64_t ox_rand() { + static ox::Random rand; + return rand.gen(); +} diff --git a/src/ox/std/random.hpp b/src/ox/std/random.hpp new file mode 100644 index 00000000..43d4daaf --- /dev/null +++ b/src/ox/std/random.hpp @@ -0,0 +1,30 @@ +/* + * Copyright 2015 - 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/. + */ + +#pragma once + +uint64_t ox_rand(); + +namespace ox { + +typedef uint64_t RandomSeed[2]; + +class Random { + public: + static RandomSeed DEFAULT_SEED; + + private: + RandomSeed m_seed; + + public: + Random(RandomSeed seed = DEFAULT_SEED); + + uint64_t gen(); +}; + +} diff --git a/src/ox/std/std.hpp b/src/ox/std/std.hpp new file mode 100644 index 00000000..f0858ad6 --- /dev/null +++ b/src/ox/std/std.hpp @@ -0,0 +1,16 @@ +/* + * Copyright 2015 - 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/. + */ +#pragma once + +#include "bitops.hpp" +#include "byteswap.hpp" +#include "memops.hpp" +#include "random.hpp" +#include "strops.hpp" +#include "string.hpp" +#include "types.hpp" diff --git a/src/ox/std/string.hpp b/src/ox/std/string.hpp new file mode 100644 index 00000000..31863db8 --- /dev/null +++ b/src/ox/std/string.hpp @@ -0,0 +1,128 @@ +/* + * Copyright 2015 - 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/. + */ + +#pragma once + +#include "memops.hpp" +#include "strops.hpp" +#include "types.hpp" + +namespace ox { + +// Bounded String +template +class bstring { + private: + uint8_t m_buff[buffLen]; + + public: + bstring(); + + bstring(const char *str); + + const bstring &operator=(const char *str); + + const bstring &operator=(char *str); + + bool operator==(const bstring &other); + + char *data(); + + /** + * Returns the number of characters in this string. + */ + size_t len(); + + /** + * Returns the number of bytes used for this string. + */ + size_t size(); + + /** + * Returns the capacity of bytes for this string. + */ + size_t cap(); +}; + +template +bstring::bstring() { + m_buff[0] = 0; +} + +template +bstring::bstring(const char *str) { + *this = str; +} + +template +const bstring &bstring::operator=(const char *str) { + size_t strLen = ox_strlen(str) + 1; + if (cap() < strLen) { + strLen = cap(); + } + ox_memcpy(m_buff, str, strLen); + // make sure last element is a null terminator + m_buff[cap() - 1] = 0; + return *this; +} + +template +const bstring &bstring::operator=(char *str) { + return *this = (const char*) str; +} + +template +bool bstring::operator==(const bstring &other) { + bool retval = true; + size_t i = 0; + while (i < buffLen && (m_buff[i] || other.m_buff[i])) { + if (m_buff[i] != other.m_buff[i]) { + retval = false; + break; + } + i++; + } + return retval; +} + +template +char *bstring::data() { + return (char*) m_buff; +} + +template +size_t bstring::len() { + size_t length = 0; + for (size_t i = 0; i < buffLen; i++) { + uint8_t b = m_buff[i]; + if (b) { + if ((b & 128) == 0) { // normal ASCII character + length++; + } else if ((b & (256 << 6)) == (256 << 6)) { // start of UTF-8 character + length++; + } + } else { + break; + } + } + return length; +} + +template +size_t bstring::size() { + size_t i; + for (i = 0; i < buffLen && m_buff[i]; i++); + return i + 1; // add one for null terminator +} + +template +size_t bstring::cap() { + return buffLen; +} + +} diff --git a/src/ox/std/strops.cpp b/src/ox/std/strops.cpp new file mode 100644 index 00000000..5176b9b0 --- /dev/null +++ b/src/ox/std/strops.cpp @@ -0,0 +1,91 @@ +/* + * Copyright 2015 - 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/. + */ + +#include "strops.hpp" + +int ox_strcmp(const char *str1, const char *str2) { + auto retval = 0; + auto i = 0; + while (str1[i] || str2[i]) { + if (str1[i] < str2[i]) { + retval = -1; + break; + } else if (str1[i] > str2[i]) { + retval = 1; + break; + } + i++; + } + return retval; +} + +int ox_strlen(const char *str1) { + int len; + for (len = 0; str1[len]; len++); + return len; +} + +int ox_strlen(char *str1) { + int len; + for (len = 0; str1[len]; len++); + return len; +} + +const char *ox_strchr(const char *str, int character, size_t maxLen) { + for (size_t i = 0; i <= maxLen; i++) { + if (str[i] == character) { + return &str[i]; + } else if (str[i] == 0) { + return nullptr; + } + } + return nullptr; +} + +char *ox_strchr(char *str, int character, size_t maxLen) { + for (size_t i = 0; i < maxLen; i++) { + if (str[i] == character) { + return &str[i]; + } else if (str[i] == 0) { + return nullptr; + } + } + return nullptr; +} + +int ox_lastIndexOf(const char *str, int character, int maxLen) { + int retval = -1; + for (int i = 0; i < maxLen && str[i]; i++) { + if (str[i] == character) { + retval = i; + } + } + return retval; +} + +int ox_lastIndexOf(char *str, int character, int maxLen) { + int retval = -1; + for (int i = 0; i < maxLen && str[i]; i++) { + if (str[i] == character) { + retval = i; + } + } + return retval; +} + +int ox_atoi(const char *str) { + int total = 0; + int multiplier = 1; + + for (auto i = ox_strlen(str) - 1; i != -1; i--) { + total += (str[i] - '0') * multiplier; + multiplier *= 10; + } + + return total; +} diff --git a/src/ox/std/strops.hpp b/src/ox/std/strops.hpp new file mode 100644 index 00000000..4fc8b915 --- /dev/null +++ b/src/ox/std/strops.hpp @@ -0,0 +1,27 @@ +/* + * Copyright 2015 - 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/. + */ + +#pragma once + +#include "types.hpp" + +int ox_strcmp(const char *str1, const char *str2); + +int ox_strlen(const char *str1); + +int ox_strlen(char *str1); + +const char *ox_strchr(const char *str, int character, size_t maxLen = 0xFFFFFFFF); + +char *ox_strchr(char *str, int character, size_t maxLen = 0xFFFFFFFF); + +int ox_lastIndexOf(const char *str, int character, int maxLen = 0xFFFFFFFF); + +int ox_lastIndexOf(char *str, int character, int maxLen = 0xFFFFFFFF); + +int ox_atoi(const char *str); diff --git a/src/ox/std/test/CMakeLists.txt b/src/ox/std/test/CMakeLists.txt new file mode 100644 index 00000000..1b25b3ba --- /dev/null +++ b/src/ox/std/test/CMakeLists.txt @@ -0,0 +1,69 @@ +cmake_minimum_required(VERSION 2.8) + +add_executable( + StdTest + tests.cpp +) + +target_link_libraries(StdTest OxLog OxStd) + +add_test("Test\\ ox_memcmp\\ ABCDEFG\\ !=\\ HIJKLMN" StdTest "ABCDEFG != HIJKLMN") +add_test("Test\\ ox_memcmp\\ HIJKLMN\\ !=\\ ABCDEFG" StdTest "HIJKLMN != ABCDEFG") +add_test("Test\\ ox_memcmp\\ ABCDEFG\\ ==\\ ABCDEFG" StdTest "ABCDEFG == ABCDEFG") +add_test("Test\\ ox_memcmp\\ ABCDEFGHI\\ ==\\ ABCDEFG" StdTest "ABCDEFGHI == ABCDEFG") + + +################################################################################ +# StrOps Tests + +add_executable( + StrOpsTest + strops_test.cpp +) + +target_link_libraries( + StrOpsTest + OxStd + OxLog +) + +add_test("Test\\ ox_strcmp\\ asdf\\ !=\\ hijk" StrOpsTest "asdf < hijk") +add_test("Test\\ ox_strcmp\\ hijk\\ !=\\ asdf" StrOpsTest "hijk > asdf") +add_test("Test\\ ox_strcmp\\ read\\ !=\\ resize" StrOpsTest "read < resize") +add_test("Test\\ ox_strcmp\\ resize\\ !=\\ read" StrOpsTest "resize > read") +add_test("Test\\ ox_strcmp\\ resize\\ ==\\ resize" StrOpsTest "resize == resize") +add_test("Test\\ ox_strcmp\\ ''\\ ==\\ ''" StrOpsTest " == ") +add_test("Test\\ ox_strchr\\ 0" StrOpsTest "ox_strchr 0") +add_test("Test\\ ox_lastIndexOf\\ aaaa\\ a" StrOpsTest "ox_lastIndexOf aaaa a") + + +################################################################################ +# Byte Swap Tests + +add_executable( + ByteSwapTest + byteswap_test.cpp +) + +target_link_libraries( + ByteSwapTest + OxStd + OxLog +) + +add_test("Test\\ bigEndianAdapt\\ 0x00ff" ByteSwapTest bigEndianAdapt 0x00ff) +add_test("Test\\ bigEndianAdapt\\ 0xff00" ByteSwapTest bigEndianAdapt 0xff00) + +add_test("Test\\ bigEndianAdapt\\ 0x000000ff" ByteSwapTest bigEndianAdapt 0x000000ff) +add_test("Test\\ bigEndianAdapt\\ 0x0000ff00" ByteSwapTest bigEndianAdapt 0x0000ff00) +add_test("Test\\ bigEndianAdapt\\ 0x00ff0000" ByteSwapTest bigEndianAdapt 0x00ff0000) +add_test("Test\\ bigEndianAdapt\\ 0xff000000" ByteSwapTest bigEndianAdapt 0xff000000) + +add_test("Test\\ bigEndianAdapt\\ 0x00000000000000ff" ByteSwapTest bigEndianAdapt 0x00000000000000ff) +add_test("Test\\ bigEndianAdapt\\ 0x000000000000ff00" ByteSwapTest bigEndianAdapt 0x000000000000ff00) +add_test("Test\\ bigEndianAdapt\\ 0x0000000000ff0000" ByteSwapTest bigEndianAdapt 0x0000000000ff0000) +add_test("Test\\ bigEndianAdapt\\ 0x00000000ff000000" ByteSwapTest bigEndianAdapt 0x00000000ff000000) +add_test("Test\\ bigEndianAdapt\\ 0x000000ff00000000" ByteSwapTest bigEndianAdapt 0x000000ff00000000) +add_test("Test\\ bigEndianAdapt\\ 0x0000ff0000000000" ByteSwapTest bigEndianAdapt 0x0000ff0000000000) +add_test("Test\\ bigEndianAdapt\\ 0x00ff000000000000" ByteSwapTest bigEndianAdapt 0x00ff000000000000) +add_test("Test\\ bigEndianAdapt\\ 0xff00000000000000" ByteSwapTest bigEndianAdapt 0xff00000000000000) diff --git a/src/ox/std/test/byteswap_test.cpp b/src/ox/std/test/byteswap_test.cpp new file mode 100644 index 00000000..ff05de99 --- /dev/null +++ b/src/ox/std/test/byteswap_test.cpp @@ -0,0 +1,39 @@ +/* + * Copyright 2015 - 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/. + */ +#include +#include +#include + +using namespace std; +using namespace ox; + +template +int testBigEndianAdapt(string str) { + auto i = (T) stoull(str, nullptr, 16); + return !(bigEndianAdapt(bigEndianAdapt(i)) == i); +} + +map tests = { + { + { "bigEndianAdapt", testBigEndianAdapt }, + { "bigEndianAdapt", testBigEndianAdapt }, + { "bigEndianAdapt", testBigEndianAdapt }, + }, +}; + +int main(int argc, const char **args) { + int retval = -1; + if (argc > 1) { + auto testName = args[1]; + string testArg = args[2]; + if (tests.find(testName) != tests.end()) { + retval = tests[testName](testArg); + } + } + return retval; +} diff --git a/src/ox/std/test/strops_test.cpp b/src/ox/std/test/strops_test.cpp new file mode 100644 index 00000000..9aaf14be --- /dev/null +++ b/src/ox/std/test/strops_test.cpp @@ -0,0 +1,79 @@ +/* + * Copyright 2015 - 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/. + */ +#include +#include +#include +#include + +using namespace std; + +map> tests = { + { + "asdf < hijk", + []() { + return !(ox_strcmp("asdf", "hijk") < 0); + } + }, + { + "hijk > asdf", + []() { + return !(ox_strcmp("hijk", "asdf") > 0); + } + }, + { + "resize > read", + []() { + return !(ox_strcmp("resize", "read") > 0); + } + }, + { + "read < resize", + []() { + return !(ox_strcmp("read", "resize") < 0); + } + }, + { + "resize == resize", + []() { + return !(ox_strcmp("resize", "resize") == 0); + } + }, + { + " == ", + []() { + return !(ox_strcmp("", "") == 0); + } + }, + { + "ox_strchr 0", + []() { + auto testStr = "asdf"; + return !(ox_strchr(testStr, 0, 4) == &testStr[4]); + } + }, + { + "ox_lastIndexOf aaaa a", + []() { + int retval = 0; + auto testStr = "aaaa"; + retval |= !(ox_lastIndexOf((char*) testStr, 'a', ox_strlen(testStr)) == 3); + retval |= !(ox_lastIndexOf((const char*) testStr, 'a', ox_strlen(testStr)) == 3); + return retval; + } + }, +}; + +int main(int argc, const char **args) { + if (argc > 1) { + auto testName = args[1]; + if (tests.find(testName) != tests.end()) { + return tests[testName](); + } + } + return -1; +} diff --git a/src/ox/std/test/tests.cpp b/src/ox/std/test/tests.cpp new file mode 100644 index 00000000..68c09b38 --- /dev/null +++ b/src/ox/std/test/tests.cpp @@ -0,0 +1,50 @@ +/* + * Copyright 2015 - 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/. + */ +#include +#include +#include +#include + +using namespace std; + +map> tests = { + { + "ABCDEFG != HIJKLMN", + []() { + return !(ox_memcmp("ABCDEFG", "HIJKLMN", 7) < 0); + } + }, + { + "HIJKLMN != ABCDEFG", + []() { + return !(ox_memcmp("HIJKLMN", "ABCDEFG", 7) > 0); + } + }, + { + "ABCDEFG == ABCDEFG", + []() { + return !(ox_memcmp("ABCDEFG", "ABCDEFG", 7) == 0); + } + }, + { + "ABCDEFGHI == ABCDEFG", + []() { + return !(ox_memcmp("ABCDEFGHI", "ABCDEFG", 7) == 0); + } + }, +}; + +int main(int argc, const char **args) { + if (argc > 1) { + auto testName = args[1]; + if (tests.find(testName) != tests.end()) { + return tests[testName](); + } + } + return -1; +} diff --git a/src/ox/std/types.hpp b/src/ox/std/types.hpp new file mode 100644 index 00000000..c57108f9 --- /dev/null +++ b/src/ox/std/types.hpp @@ -0,0 +1,40 @@ +/* + * Copyright 2015 - 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/. + */ + +#pragma once + +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef short int16_t; +typedef unsigned short uint16_t; +typedef int int32_t; +typedef unsigned int uint32_t; +typedef unsigned uint_t; +#if defined(_WIN32) || defined(__APPLE__) || defined(__arm__) || defined(__ppc__) +typedef long long int64_t; +typedef unsigned long long uint64_t; +#else +typedef long int64_t; +typedef unsigned long uint64_t; +#endif + +namespace ox { + +typedef uint32_t Error; + +} + +#if defined(_LP64) || defined(__ppc64__) || defined(__aarch64__) +typedef unsigned long size_t; +#elif defined(_WIN64) +typedef uint64_t size_t; +#elif defined(_LP32) || defined(__ppc__) || defined(_WIN32) || defined(__arm__) +typedef uint32_t size_t; +#else +#error size_t undefined +#endif