diff --git a/CMakeLists.txt b/CMakeLists.txt index d093272fb..ccc056f01 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,9 +7,11 @@ 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") +if(OX_BUILD_EXEC STREQUAL "OFF" OR OX_USE_STDLIB STREQUAL "OFF") + set(OX_BUILD_EXEC "OFF") set(OX_RUN_TESTS "OFF") endif() diff --git a/Makefile b/Makefile index 076d34841..390603095 100644 --- a/Makefile +++ b/Makefile @@ -2,14 +2,14 @@ OS=$(shell uname | tr [:upper:] [:lower:]) HOST_ENV=${OS}-$(shell uname -m) DEVENV=devenv$(shell pwd | sed 's/\//-/g') DEVENV_IMAGE=wombatant/devenv -ifneq ($(which gmake),) +ifneq ($(shell which gmake),) MAKE=gmake else MAKE=make endif -ifneq ($(which docker 2>&1),) +ifneq ($(shell which docker 2>&1),) ifeq ($(shell docker inspect --format="{{.State.Status}}" ${DEVENV} 2>&1),running) - ENV_RUN=docker exec --user $(shell id -u ${USER}) ${DEVENV} + ENV_RUN=docker exec -i -t --user $(shell id -u ${USER}) ${DEVENV} endif endif @@ -38,6 +38,9 @@ devenv: 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} diff --git a/OxConfig.cmake b/OxConfig.cmake index 2c7ee2bb4..6312e31c8 100644 --- a/OxConfig.cmake +++ b/OxConfig.cmake @@ -1,3 +1,11 @@ -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) +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 index 0e220133e..a1bd3c204 100644 --- a/build/Makefile +++ b/build/Makefile @@ -1,4 +1,4 @@ -ifneq ($(which gmake 2>&1),) +ifneq ($(shell which gmake),) MAKE=gmake -j else MAKE=make diff --git a/scripts/setup_build b/scripts/setup_build index 7826a3754..f416aa154 100755 --- a/scripts/setup_build +++ b/scripts/setup_build @@ -10,7 +10,7 @@ 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_BUILD_EXEC=OFF" + toolchain="-DCMAKE_TOOLCHAIN_FILE=cmake/Modules/GBA.cmake -DOX_USE_STDLIB=OFF -DCMAKE_INSTALL_PREFIX=$DEVKITARM" fi if [[ $BUILD_TYPE == debug ]]; then diff --git a/src/ox/CMakeLists.txt b/src/ox/CMakeLists.txt index dea24e700..e77392db5 100644 --- a/src/ox/CMakeLists.txt +++ b/src/ox/CMakeLists.txt @@ -1,4 +1,7 @@ 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(std) diff --git a/src/ox/clargs/CMakeLists.txt b/src/ox/clargs/CMakeLists.txt new file mode 100644 index 000000000..a096079cc --- /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 000000000..999f47de6 --- /dev/null +++ b/src/ox/clargs/clargs.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 "clargs.hpp" + +namespace ox { +namespace clargs { + +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 000000000..42acd120e --- /dev/null +++ b/src/ox/clargs/clargs.hpp @@ -0,0 +1,34 @@ +/* + * 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 { +namespace clargs { + +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 index c7cd44c28..e318f0581 100644 --- a/src/ox/fs/CMakeLists.txt +++ b/src/ox/fs/CMakeLists.txt @@ -3,6 +3,14 @@ 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") @@ -19,6 +27,7 @@ install( filestore.hpp filesystem.hpp inodemgr.hpp + pathiterator.hpp DESTINATION include/ox/fs ) diff --git a/src/ox/fs/filestore.hpp b/src/ox/fs/filestore.hpp index 5a8402325..4cafea904 100644 --- a/src/ox/fs/filestore.hpp +++ b/src/ox/fs/filestore.hpp @@ -13,11 +13,11 @@ namespace ox { namespace fs { template -struct FileStoreHeader { +struct __attribute__((packed)) FileStoreHeader { public: typedef InodeId InodeId_t; typedef FsT FsSize_t; - const static auto VERSION = 4; + const static auto VERSION = 5; private: uint16_t m_version; @@ -45,52 +45,52 @@ struct FileStoreHeader { template void FileStoreHeader::setVersion(uint16_t version) { - m_version = version; + m_version = std::bigEndianAdapt(version); } template uint16_t FileStoreHeader::getVersion() { - return m_version; + return std::bigEndianAdapt(m_version); } template void FileStoreHeader::setFsType(uint16_t fsType) { - m_fsType = fsType; + m_fsType = std::bigEndianAdapt(fsType); } template uint16_t FileStoreHeader::getFsType() { - return m_fsType; + return std::bigEndianAdapt(m_fsType); } template void FileStoreHeader::setSize(FsSize_t size) { - m_size = size; + m_size = std::bigEndianAdapt(size); } template FsSize_t FileStoreHeader::getSize() { - return m_size; + return std::bigEndianAdapt(m_size); } template void FileStoreHeader::setMemUsed(FsSize_t memUsed) { - m_memUsed = memUsed; + m_memUsed = std::bigEndianAdapt(memUsed); } template FsSize_t FileStoreHeader::getMemUsed() { - return m_memUsed; + return std::bigEndianAdapt(m_memUsed); } template void FileStoreHeader::setRootInode(FsSize_t rootInode) { - m_rootInode = rootInode; + m_rootInode = std::bigEndianAdapt(rootInode); } template FsSize_t FileStoreHeader::getRootInode() { - return m_rootInode; + return std::bigEndianAdapt(m_rootInode); } template @@ -103,24 +103,27 @@ class FileStore { struct StatInfo { InodeId_t inodeId; - typename Header::FsSize_t size; + typename Header::FsSize_t size; uint8_t fileType; }; private: - struct Inode { + struct __attribute__((packed)) Inode { private: // the next Inode in memory - typename Header::FsSize_t m_prev, m_next; + typename Header::FsSize_t m_prev; + typename Header::FsSize_t m_next; typename Header::FsSize_t m_dataLen; InodeId_t m_id; uint8_t m_fileType; - typename Header::FsSize_t m_left, m_right; + 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); @@ -142,7 +145,7 @@ class FileStore { typename Header::FsSize_t getRight(); void setData(void *data, typename Header::FsSize_t size); - void *getData(); + uint8_t *getData(); }; Header m_header; @@ -185,6 +188,35 @@ class FileStore { */ 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. @@ -199,7 +231,7 @@ class FileStore { * @param size the size of the data to insert * @return the space currently available in this file store. */ - typename Header::FsSize_t spaceNeeded(InodeId_t id, typename Header::FsSize_t size); + typename Header::FsSize_t spaceNeeded(typename Header::FsSize_t size); /** * Returns the size of the file store. @@ -239,6 +271,21 @@ class FileStore { */ 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 @@ -275,15 +322,9 @@ class FileStore { */ bool insert(Inode *root, Inode *insertValue); - /** - * Gets the FsSize_t associated with the next Inode to be allocated. - * @retrun the FsSize_t associated with the next Inode to be allocated - */ - typename Header::FsSize_t iterator(); - typename Header::FsSize_t firstInode(); - Inode *lastInode(); + typename Header::FsSize_t lastInode(); /** * Updates the address of the inode in the tree. @@ -315,84 +356,89 @@ class FileStore { template typename Header::FsSize_t FileStore
::Inode::size() { - return sizeof(Inode) + m_dataLen; + return sizeof(Inode) + getDataLen(); +} + +template +void FileStore
::Inode::setDataLen(typename Header::FsSize_t dataLen) { + this->m_dataLen = std::bigEndianAdapt(dataLen); } template typename Header::FsSize_t FileStore
::Inode::getDataLen() { - return this->m_dataLen; + return std::bigEndianAdapt(m_dataLen); } template void FileStore
::Inode::setPrev(typename Header::FsSize_t prev) { - this->m_prev = prev; + this->m_prev = std::bigEndianAdapt(prev); } template typename Header::FsSize_t FileStore
::Inode::getPrev() { - return this->m_prev; + return std::bigEndianAdapt(m_prev); } template void FileStore
::Inode::setNext(typename Header::FsSize_t next) { - this->m_next = next; + this->m_next = std::bigEndianAdapt(next); } template typename Header::FsSize_t FileStore
::Inode::getNext() { - return this->m_next; + return std::bigEndianAdapt(m_next); } template void FileStore
::Inode::setId(InodeId_t id) { - this->m_id = id; + this->m_id = std::bigEndianAdapt(id); } template typename Header::InodeId_t FileStore
::Inode::getId() { - return this->m_id; + return std::bigEndianAdapt(m_id); } template void FileStore
::Inode::setFileType(uint8_t fileType) { - this->m_fileType = fileType; + this->m_fileType = std::bigEndianAdapt(fileType); } template uint8_t FileStore
::Inode::getFileType() { - return this->m_fileType; + return std::bigEndianAdapt(m_fileType); } template void FileStore
::Inode::setLeft(typename Header::FsSize_t left) { - this->m_left = left; + this->m_left = std::bigEndianAdapt(left); } template typename Header::FsSize_t FileStore
::Inode::getLeft() { - return this->m_left; + return std::bigEndianAdapt(m_left); } template void FileStore
::Inode::setRight(typename Header::FsSize_t right) { - this->m_right = right; + this->m_right = std::bigEndianAdapt(right); } template typename Header::FsSize_t FileStore
::Inode::getRight() { - return this->m_right; + return std::bigEndianAdapt(m_right); } template void FileStore
::Inode::setData(void *data, typename Header::FsSize_t size) { - ox_memcpy(this->getData(), data, size); - m_dataLen = size; + ox_memcpy(getData(), data, size); + setDataLen(size); } template -void *FileStore
::Inode::getData() { - return this + 1; +uint8_t *FileStore
::Inode::getData() { + return (uint8_t*) (this + 1); } @@ -441,8 +487,15 @@ int FileStore
::write(InodeId_t id, void *data, typename Header::FsSize_t 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; } @@ -458,41 +511,37 @@ int FileStore
::remove(Inode *root, InodeId_t id) { if (root->getId() > id) { if (root->getLeft()) { - auto node = ptr(root->getLeft()); - if (node->getId() != id) { - err = remove(node, id); + auto left = ptr(root->getLeft()); + if (left->getId() != id) { + err = remove(left, id); } else { root->setLeft(0); - if (node->getRight()) { - insert(root, ptr(node->getRight())); + // pass children to parent + if (left->getRight()) { + insert(root, ptr(left->getRight())); } - if (node->getLeft()) { - insert(root, ptr(node->getLeft())); + if (left->getLeft()) { + insert(root, ptr(left->getLeft())); } - dealloc(node); - node->setId(0); - node->setLeft(0); - node->setRight(0); + dealloc(left); err = 0; } } } else if (root->getId() < id) { if (root->getRight()) { - auto node = ptr(root->getRight()); - if (node->getId() != id) { - err = remove(node, id); + auto right = ptr(root->getRight()); + if (right->getId() != id) { + err = remove(right, id); } else { root->setRight(0); - if (node->getRight()) { - insert(root, ptr(node->getRight())); + // pass children to parent + if (right->getRight()) { + insert(root, ptr(right->getRight())); } - if (node->getLeft()) { - insert(root, ptr(node->getLeft())); + if (right->getLeft()) { + insert(root, ptr(right->getLeft())); } - dealloc(node); - node->setId(0); - node->setLeft(0); - node->setRight(0); + dealloc(right); err = 0; } } @@ -502,9 +551,6 @@ int FileStore
::remove(Inode *root, InodeId_t id) { insert(ptr(m_header.getRootInode()), ptr(root->getLeft())); } dealloc(root); - root->setId(0); - root->setLeft(0); - root->setRight(0); err = 0; } @@ -538,15 +584,42 @@ void FileStore
::updateInodeAddress(InodeId_t id, typename Header::FsSize template int FileStore
::read(InodeId_t id, void *data, typename Header::FsSize_t *size) { auto inode = getInode(ptr(m_header.getRootInode()), id); - int retval = 1; - if (inode) { - if (size) { - *size = inode->getDataLen(); - } - ox_memcpy(data, inode->getData(), inode->getDataLen()); - retval = 0; + 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; } - return retval; + if (size) { + *size = readSize; + } + + readSize /= sizeof(T); + T *it = (T*) &(inode->getData()[readStart]); + for (typename Header::FsSize_t i = 0; i < readSize; i++) { + *(data++) = *(it++); + } + return 0; } template @@ -564,13 +637,8 @@ typename FileStore
::StatInfo FileStore
::stat(InodeId_t id) { } template -typename Header::FsSize_t FileStore
::spaceNeeded(InodeId_t id, typename Header::FsSize_t size) { - typename Header::FsSize_t needed = sizeof(Inode) + size;; - auto inode = getInode(ptr(m_header.getRootInode()), id); - if (inode) { - needed -= inode->size(); - } - return needed; +typename Header::FsSize_t FileStore
::spaceNeeded(typename Header::FsSize_t size) { + return sizeof(Inode) + size; } template @@ -629,17 +697,16 @@ typename FileStore
::Inode *FileStore
::getInodeParent(Inode *root template typename Header::FsSize_t FileStore
::nextInodeAddr() { - typename Header::FsSize_t next = ptr(lastInode()) + lastInode()->size(); - return next; + return lastInode() + ptr(lastInode())->size(); } template void *FileStore
::alloc(typename Header::FsSize_t size) { - typename Header::FsSize_t next = nextInodeAddr(); - if ((next + size) > (uint64_t) end()) { + auto next = nextInodeAddr(); + if ((next + size) > ptr(end())) { compact(); next = nextInodeAddr(); - if ((next + size) > (uint64_t) end()) { + if ((next + size) > ptr(end())) { return nullptr; } } @@ -648,8 +715,9 @@ void *FileStore
::alloc(typename Header::FsSize_t size) { const auto inode = ptr(retval); ox_memset(inode, 0, size); inode->setPrev(ptr(firstInode())->getPrev()); - inode->setNext(retval + size); + inode->setNext(firstInode()); m_header.setMemUsed(m_header.getMemUsed() + size); + ptr(lastInode())->setNext(retval); ptr(firstInode())->setPrev(retval); return inode; } @@ -696,11 +764,6 @@ bool FileStore
::insert(Inode *root, Inode *insertValue) { return retval; } -template -typename Header::FsSize_t FileStore
::iterator() { - return ptr(lastInode()) + lastInode()->size(); -} - template typename Header::FsSize_t FileStore
::ptr(void *ptr) { #ifdef _MSC_VER @@ -718,8 +781,8 @@ typename Header::FsSize_t FileStore
::firstInode() { } template -typename FileStore
::Inode *FileStore
::lastInode() { - return ptr(ptr(firstInode())->getPrev()); +typename Header::FsSize_t FileStore
::lastInode() { + return ptr(firstInode())->getPrev(); } template @@ -743,7 +806,7 @@ uint8_t *FileStore
::format(uint8_t *buffer, typename Header::FsSize_t si fs->m_header.setMemUsed(sizeof(FileStore
) + sizeof(Inode)); fs->m_header.setRootInode(sizeof(FileStore
)); ((Inode*) (fs + 1))->setPrev(sizeof(FileStore
)); - fs->lastInode()->setNext(sizeof(FileStore
)); + ((Inode*) (fs + 1))->setNext(sizeof(FileStore
)); return (uint8_t*) buffer; } diff --git a/src/ox/fs/filesystem.cpp b/src/ox/fs/filesystem.cpp index b4a3a758a..2fef1603a 100644 --- a/src/ox/fs/filesystem.cpp +++ b/src/ox/fs/filesystem.cpp @@ -5,18 +5,19 @@ * 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 { namespace fs { -FileSystem *createFileSystem(void *buff) { +FileSystem *createFileSystem(void *buff, size_t buffSize) { auto version = ((FileStore16*) buff)->version(); auto type = ((FileStore16*) buff)->fsType(); FileSystem *fs = nullptr; switch (version) { - case 4: + case 5: switch (type) { case ox::fs::OxFS_16: fs = new FileSystem16(buff); @@ -30,11 +31,45 @@ FileSystem *createFileSystem(void *buff) { } break; default: - return nullptr; + 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 index 8cd1f135d..a6b71b43d 100644 --- a/src/ox/fs/filesystem.hpp +++ b/src/ox/fs/filesystem.hpp @@ -8,6 +8,7 @@ #pragma once #include +#include "pathiterator.hpp" #include "filestore.hpp" namespace ox { @@ -34,32 +35,51 @@ class FileSystem { public: virtual ~FileSystem() {}; + 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) = 0; virtual void resize(uint64_t size = 0) = 0; + virtual int write(const char *path, void *buffer, uint64_t size, uint8_t fileType = NormalFile) = 0; + virtual int write(uint64_t inode, void *buffer, uint64_t size, uint8_t fileType = NormalFile) = 0; virtual FileStat stat(uint64_t inode) = 0; - virtual uint64_t spaceNeeded(uint64_t id, uint64_t size) = 0; + virtual uint64_t spaceNeeded(uint64_t size) = 0; virtual uint64_t available() = 0; virtual uint64_t size() = 0; + + virtual uint8_t *buff() = 0; }; -FileSystem *createFileSystem(void *buff); +FileSystem *createFileSystem(void *buff, size_t buffSize); + +/** + * 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: - struct DirectoryEntry { + struct __attribute__((packed)) DirectoryEntry { typename FileStore::InodeId_t inode; char *getName() { @@ -69,60 +89,84 @@ class FileSystemTemplate: public FileSystem { void setName(const char *name) { auto data = getName(); auto nameLen = ox_strlen(name); - ox_memcpy(data, &name, nameLen); + ox_memcpy(data, name, nameLen); data[nameLen] = 0; } + + /** + * The size in bytes. + */ + uint64_t size() { + return sizeof(DirectoryEntry) + ox_strlen(getName()); + } + + static uint64_t spaceNeeded(const char *fileName) { + return sizeof(DirectoryEntry) + ox_strlen(fileName) + 1; + } }; - struct Directory { + struct __attribute__((packed)) Directory { /** * Number of files in this directory. */ - typename FileStore::InodeId_t size = 0; + typename FileStore::FsSize_t size = 0; DirectoryEntry *files() { - return (DirectoryEntry*) (this + 1); + return size ? (DirectoryEntry*) (this + 1) : nullptr; } + + uint64_t getFileInode(const char *name, uint64_t buffSize); }; + FileStore *m_store = nullptr; + + public: // static members static typename FileStore::InodeId_t INODE_ROOT_DIR; - FileStore *store = nullptr; - - public: - FileSystemTemplate(void *buff); + explicit FileSystemTemplate(void *buff); int mkdir(const char *path); - int read(const char *path, void *buffer); + 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; - int read(uint64_t inode, void *buffer, size_t size) override; - void resize(uint64_t size = 0) override; int remove(uint64_t inode) override; + int write(const char *path, void *buffer, uint64_t size, uint8_t fileType = NormalFile) override; + int write(uint64_t inode, void *buffer, uint64_t size, uint8_t fileType) override; FileStat stat(const char *path); FileStat stat(uint64_t inode) override; - uint64_t spaceNeeded(uint64_t id, uint64_t size) 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; + static uint8_t *format(void *buffer, typename FileStore::FsSize_t size, bool useDirectories); + + private: + int insertDirectoryEntry(const char *dirPath, const char *fileName, uint64_t inode); }; template FileSystemTemplate::FileSystemTemplate(void *buff) { - store = (FileStore*) buff; + m_store = (FileStore*) buff; } template @@ -135,7 +179,12 @@ int FileSystemTemplate::mkdir(const char *path) { 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; } @@ -145,7 +194,7 @@ FileStat FileSystemTemplate::stat(const char *path) { template FileStat FileSystemTemplate::stat(uint64_t inode) { FileStat stat; - auto s = store->stat(inode); + auto s = m_store->stat(inode); stat.size = s.size; stat.inode = s.inodeId; stat.fileType = s.fileType; @@ -159,29 +208,67 @@ FileStat FileSystemTemplate::stat(uint64_t inode) { #pragma warning(disable:4244) #endif template -int FileSystemTemplate::read(uint64_t inode, void *buffer, size_t size) { - auto err = 1; - auto s = store->stat(inode); - if (size == s.size) { - err = store->read(inode, buffer, nullptr); +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) { + read(inode, buffer, buffSize); } - return err; + + 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 = store->stat(inode); + auto s = m_store->stat(inode); auto buff = new uint8_t[s.size]; if (size) { *size = s.size; } - if (store->read(inode, buff, nullptr)) { + if (m_store->read(inode, buff, nullptr)) { delete []buff; buff = nullptr; } @@ -196,7 +283,45 @@ uint8_t *FileSystemTemplate::read(uint64_t inode, size_t *si #endif template int FileSystemTemplate::remove(uint64_t inode) { - return store->remove(inode); + return m_store->remove(inode); +} +#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) { + while (!inode) { + inode = ox_rand() >> 48; + // make sure this does not already exist + if (stat(inode).inode) { + // that result was unusable, try again + inode = 0; + } + } + insertDirectoryEntry(dirPath, fileName, inode); + } + + err = write(inode, buffer, size, fileType); + + return err; } #ifdef _MSC_VER #pragma warning(default:4244) @@ -207,7 +332,43 @@ int FileSystemTemplate::remove(uint64_t inode) { #endif template int FileSystemTemplate::write(uint64_t inode, void *buffer, uint64_t size, uint8_t fileType) { - return store->write(inode, buffer, size, fileType); + 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()) { + auto dirStat = m_store->stat(inode); + if (dirStat.size >= sizeof(Directory)) { + uint8_t dirBuffer[dirStat.size]; + auto dir = (Directory*) dirBuffer; + if (read(inode, dirBuffer, dirStat.size) == 0) { + if (dirStat.fileType == FileType::Directory && it.next(fileName, pathLen) == 0) { + inode = dir->getFileInode(fileName, dirStat.size); + } 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) @@ -215,22 +376,27 @@ int FileSystemTemplate::write(uint64_t inode, void *buffer, template void FileSystemTemplate::resize(uint64_t size) { - return store->resize(size); + return m_store->resize(size); } template -uint64_t FileSystemTemplate::spaceNeeded(uint64_t id, uint64_t size) { - return store->spaceNeeded(id, size); +uint64_t FileSystemTemplate::spaceNeeded(uint64_t size) { + return m_store->spaceNeeded(size); } template uint64_t FileSystemTemplate::available() { - return store->available(); + return m_store->available(); } template uint64_t FileSystemTemplate::size() { - return store->size(); + return m_store->size(); +} + +template +uint8_t *FileSystemTemplate::buff() { + return (uint8_t*) m_store; } #ifdef _MSC_VER @@ -242,10 +408,8 @@ uint8_t *FileSystemTemplate::format(void *buffer, typename F FileSystemTemplate fs(buffer); if (buffer && useDirectories) { - char dirBuff[sizeof(Directory) + sizeof(DirectoryEntry) + 2]; - auto *dir = (Directory*) dirBuff; - dir->files(); - fs.write(INODE_ROOT_DIR, dirBuff, useDirectories, FileType::Directory); + Directory dir; + fs.write(INODE_ROOT_DIR, &dir, sizeof(dir), FileType::Directory); } return (uint8_t*) buffer; @@ -254,6 +418,57 @@ uint8_t *FileSystemTemplate::format(void *buffer, typename F #pragma warning(default:4244) #endif +#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) { + size_t dirBuffSize = s.size + DirectoryEntry::spaceNeeded(fileName) + 100; + uint8_t dirBuff[dirBuffSize]; + int err = read(s.inode, dirBuff, dirBuffSize); + + if (!err) { + auto dir = (Directory*) dirBuff; + dir->size += DirectoryEntry::spaceNeeded(fileName); + auto entry = (DirectoryEntry*) &dirBuff[s.size]; + entry->inode = inode; + entry->setName(fileName); + return write(s.inode, dirBuff, dirBuffSize, FileType::Directory); + } else { + return 1; + } + } else { + return 2; + } +} +#ifdef _MSC_VER +#pragma warning(default:4244) +#endif + + +// Directory + +template +uint64_t FileSystemTemplate::Directory::getFileInode(const char *name, uint64_t buffSize) { + uint64_t inode = 0; + auto current = files(); + if (current) { + auto end = (DirectoryEntry*) (((uint8_t*) files()) + buffSize); + while (current && ox_strcmp(current->getName(), name) != 0) { + current = (DirectoryEntry*) (((uint8_t*) current) + current->size()); + if (current >= end) { + current = nullptr; + } + } + if (current) { + inode = current->inode; + } + } + return inode; +} + typedef FileSystemTemplate FileSystem16; typedef FileSystemTemplate FileSystem32; typedef FileSystemTemplate FileSystem64; diff --git a/src/ox/fs/oxfstool.cpp b/src/ox/fs/oxfstool.cpp index 235d1e14a..d7a84da1d 100644 --- a/src/ox/fs/oxfstool.cpp +++ b/src/ox/fs/oxfstool.cpp @@ -30,13 +30,12 @@ const static auto usage = "usage:\n" "\toxfs compact \n" "\toxfs version\n"; -char *loadFileBuff(const char *path, ::size_t *sizeOut = nullptr) { - auto file = fopen(path, "rb"); +char *loadFileBuff(FILE *file, ::size_t *sizeOut = nullptr) { if (file) { fseek(file, 0, SEEK_END); const auto size = ftell(file); rewind(file); - auto buff = (char*) malloc(size); + auto buff = new char[size]; auto itemsRead = fread(buff, size, 1, file); fclose(file); if (sizeOut) { @@ -48,12 +47,17 @@ char *loadFileBuff(const char *path, ::size_t *sizeOut = nullptr) { } } +char *loadFileBuff(const char *path, ::size_t *sizeOut = nullptr) { + return loadFileBuff(fopen(path, "rb"), sizeOut); +} + size_t bytes(const char *str) { auto size = ::ox_strlen(str); const auto lastChar = str[size-1]; auto multiplier = 1; - auto copy = new char[size]; - ox_memcpy(copy, str, size); + char copy[size + 1]; + ox_memcpy(copy, str, size + 1); + // parse size unit if (lastChar < '0' || lastChar > '9') { copy[size-1] = 0; switch (lastChar) { @@ -73,9 +77,7 @@ size_t bytes(const char *str) { multiplier = -1; } } - const auto retval = ((size_t) ox_atoi(copy)) * multiplier; - delete []copy; - return retval; + return ox_atoi(copy) * multiplier; } int format(int argc, char **args) { @@ -83,13 +85,10 @@ int format(int argc, char **args) { auto err = 0; if (argc >= 5) { auto type = ox_atoi(args[2]); - cout << args[3] << endl; auto size = bytes(args[3]); auto path = args[4]; - auto buff = (uint8_t*) malloc(size); + auto buff = new uint8_t[size]; - cout << "Size: " << size << " bytes\n"; - cout << "Type: " << type << endl; if (size < sizeof(FileStore64)) { err = 1; @@ -100,13 +99,13 @@ int format(int argc, char **args) { // format switch (type) { case 16: - FileStore16::format(buff, (FileStore16::FsSize_t) size, ox::fs::OxFS_16); + FileStore16::format(buff, (FileStore16::FsSize_t) size, true); break; case 32: - FileStore32::format(buff, (FileStore32::FsSize_t) size, ox::fs::OxFS_32); + FileStore32::format(buff, (FileStore32::FsSize_t) size, true); break; case 64: - FileStore64::format(buff, size, ox::fs::OxFS_64); + FileStore64::format(buff, size, true); break; default: err = 1; @@ -126,7 +125,7 @@ int format(int argc, char **args) { } } - free(buff); + delete []buff; if (err == 0) { fprintf(stderr, "Created file system %s\n", path); @@ -149,7 +148,7 @@ int read(int argc, char **args) { auto fsBuff = loadFileBuff(fsPath, &fsSize); if (fsBuff) { - auto fs = createFileSystem(fsBuff); + auto fs = createFileSystem(fsBuff, fsSize); if (fs) { auto output = fs->read(inode, &fileSize); @@ -161,7 +160,7 @@ int read(int argc, char **args) { } delete fs; - free(fsBuff); + delete []fsBuff; } else { fprintf(stderr, "Invalid file system type: %d.\n", *(uint32_t*) fsBuff); } @@ -195,22 +194,22 @@ int write(int argc, char **args, bool expand) { if (itemsRead) { auto srcBuff = loadFileBuff(srcPath, &srcSize); if (srcBuff) { - auto fs = createFileSystem(fsBuff); + auto expanded = false; + auto fs = createFileSystem(fsBuff, fsSize); if (fs) { if (expand && fs->available() <= srcSize) { - auto needed = fs->spaceNeeded(inode, srcSize); - auto cloneBuff = new uint8_t[needed]; - ox_memcpy(cloneBuff, fsBuff, fsSize); - - delete fs; - delete []fsBuff; - - fsBuff = cloneBuff; - fs = createFileSystem(fsBuff); + auto needed = fs->size() + fs->spaceNeeded(srcSize); fsSize = needed; - fs->resize(fsSize); + 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"); } @@ -233,7 +232,7 @@ int write(int argc, char **args, bool expand) { err = 1; } } - free(srcBuff); + delete []srcBuff; } else { err = 1; fprintf(stderr, "Could not load source file: %s.\n", srcPath); @@ -258,7 +257,7 @@ int compact(int argc, char **args) { auto fsBuff = loadFileBuff(fsPath, &fsSize); if (fsBuff) { - auto fs = createFileSystem(fsBuff); + auto fs = createFileSystem(fsBuff, fsSize); if (fs) { fs->resize(); @@ -279,7 +278,7 @@ int compact(int argc, char **args) { } delete fs; - free(fsBuff); + delete []fsBuff; } else { fprintf(stderr, "Could not open file: %s\n", fsPath); } @@ -298,7 +297,7 @@ int remove(int argc, char **args) { auto fsBuff = loadFileBuff(fsPath, &fsSize); if (fsBuff) { - auto fs = createFileSystem(fsBuff); + auto fs = createFileSystem(fsBuff, fsSize); if (fs) { err = fs->remove(inode); @@ -322,7 +321,7 @@ int remove(int argc, char **args) { } delete fs; - free(fsBuff); + delete []fsBuff; } else { fprintf(stderr, "Could not open file: %s\n", fsPath); } diff --git a/src/ox/fs/pathiterator.cpp b/src/ox/fs/pathiterator.cpp new file mode 100644 index 000000000..f82cd8bee --- /dev/null +++ b/src/ox/fs/pathiterator.cpp @@ -0,0 +1,100 @@ +/* + * 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 { +namespace fs { + +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; + } +} + +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; + ox_memcpy(pathOut, &m_path[start], 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 000000000..789e8ac8d --- /dev/null +++ b/src/ox/fs/pathiterator.hpp @@ -0,0 +1,44 @@ +/* + * 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 { +namespace fs { + +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 index 9114a83e8..90a8016f3 100644 --- a/src/ox/fs/test/CMakeLists.txt +++ b/src/ox/fs/test/CMakeLists.txt @@ -15,10 +15,27 @@ add_executable( filestoreio.cpp ) +add_executable( + FSTests + tests.cpp +) + target_link_libraries(FileStoreFormat OxFS OxStd) target_link_libraries(FileSystemFormat OxFS OxStd) target_link_libraries(FileStoreIO OxFS OxStd) +target_link_libraries(FSTests OxFS OxStd) 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)") diff --git a/src/ox/fs/test/tests.cpp b/src/ox/fs/test/tests.cpp new file mode 100644 index 000000000..fe1a8d9ae --- /dev/null +++ b/src/ox/fs/test/tests.cpp @@ -0,0 +1,170 @@ +/* + * 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 + +using namespace std; +using namespace ox::fs; + +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) { + // this value will likely need to change if anything about the + // random number generator changes + const auto targetInode = 58542; + + int retval = 0; + auto path = "/charset.gbag"; + auto data = "test"; + + const auto size = 1024; + uint8_t buff[size]; + FileSystem32::format(buff, (FileStore32::FsSize_t) size, true); + auto fs = (FileSystem32*) createFileSystem(buff, size); + + retval |= fs->write(path, &data, ox_strlen(data)); + retval |= !(fs->findInodeOf(path) == targetInode); + + delete fs; + 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/std/CMakeLists.txt b/src/ox/std/CMakeLists.txt index f24ef2fdf..35e38cbe9 100644 --- a/src/ox/std/CMakeLists.txt +++ b/src/ox/std/CMakeLists.txt @@ -3,11 +3,20 @@ 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 + byteswap.hpp memops.hpp strops.hpp std.hpp diff --git a/src/ox/std/bitops.hpp b/src/ox/std/bitops.hpp new file mode 100644 index 000000000..863790235 --- /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 000000000..cbe5e8068 --- /dev/null +++ b/src/ox/std/byteswap.hpp @@ -0,0 +1,82 @@ +/* + * 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 { +namespace std { + + +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 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/random.cpp b/src/ox/std/random.cpp new file mode 100644 index 000000000..247cab5aa --- /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 000000000..43d4daaf7 --- /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 index 7e7f1c9ca..93c9ad20c 100644 --- a/src/ox/std/std.hpp +++ b/src/ox/std/std.hpp @@ -7,6 +7,9 @@ */ #pragma once +#include "bitops.hpp" +#include "byteswap.hpp" #include "memops.hpp" +#include "random.hpp" #include "strops.hpp" #include "types.hpp" diff --git a/src/ox/std/strops.cpp b/src/ox/std/strops.cpp index 83b7085be..5176b9b0b 100644 --- a/src/ox/std/strops.cpp +++ b/src/ox/std/strops.cpp @@ -11,7 +11,7 @@ int ox_strcmp(const char *str1, const char *str2) { auto retval = 0; auto i = 0; - do { + while (str1[i] || str2[i]) { if (str1[i] < str2[i]) { retval = -1; break; @@ -20,7 +20,7 @@ int ox_strcmp(const char *str1, const char *str2) { break; } i++; - } while (str1[i] || str2[i]); + } return retval; } @@ -30,6 +30,54 @@ int ox_strlen(const char *str1) { 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; diff --git a/src/ox/std/strops.hpp b/src/ox/std/strops.hpp index e91a7415a..4fc8b9157 100644 --- a/src/ox/std/strops.hpp +++ b/src/ox/std/strops.hpp @@ -5,6 +5,7 @@ * 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" @@ -13,4 +14,14 @@ 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 index 3fd5793c5..da900ca6c 100644 --- a/src/ox/std/test/CMakeLists.txt +++ b/src/ox/std/test/CMakeLists.txt @@ -28,3 +28,34 @@ 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\\ resize\\ ==\\ resize" 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) + +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 000000000..6bad03536 --- /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::std; + +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 index 8950b5b0d..9aaf14be4 100644 --- a/src/ox/std/test/strops_test.cpp +++ b/src/ox/std/test/strops_test.cpp @@ -43,6 +43,29 @@ map> tests = { 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) { diff --git a/src/ox/std/types.hpp b/src/ox/std/types.hpp index 478af561e..bc200352e 100644 --- a/src/ox/std/types.hpp +++ b/src/ox/std/types.hpp @@ -23,11 +23,9 @@ typedef unsigned long uint64_t; #endif namespace ox { -namespace std { typedef uint32_t Error; -} } #if defined(_LP64) || defined(__ppc64__) || defined(__aarch64__)