diff --git a/src/ox/fs/filesystem.hpp b/src/ox/fs/filesystem.hpp index cbd00ff94..a6b71b43d 100644 --- a/src/ox/fs/filesystem.hpp +++ b/src/ox/fs/filesystem.hpp @@ -89,7 +89,7 @@ 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; } @@ -101,7 +101,7 @@ class FileSystemTemplate: public FileSystem { } static uint64_t spaceNeeded(const char *fileName) { - return sizeof(DirectoryEntry) + ox_strlen(fileName); + return sizeof(DirectoryEntry) + ox_strlen(fileName) + 1; } }; @@ -109,7 +109,7 @@ class FileSystemTemplate: public FileSystem { /** * Number of files in this directory. */ - typename FileStore::InodeId_t size = 0; + typename FileStore::FsSize_t size = 0; DirectoryEntry *files() { return size ? (DirectoryEntry*) (this + 1) : nullptr; @@ -161,7 +161,7 @@ class FileSystemTemplate: public FileSystem { static uint8_t *format(void *buffer, typename FileStore::FsSize_t size, bool useDirectories); private: - int insertDirectoryEntry(uint64_t inode, const char *dirPath, const char *fileName); + int insertDirectoryEntry(const char *dirPath, const char *fileName, uint64_t inode); }; template @@ -179,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; } @@ -289,17 +294,34 @@ int FileSystemTemplate::remove(uint64_t inode) { #endif template int FileSystemTemplate::write(const char *path, void *buffer, uint64_t size, uint8_t fileType) { - 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 = write(inode, buffer, size, 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; } - return retval; + 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) @@ -325,26 +347,25 @@ uint64_t FileSystemTemplate::findInodeOf(const char *path) { PathIterator it(path, pathLen); char fileName[pathLen]; uint64_t inode = INODE_ROOT_DIR; - while (inode) { + 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) { - if (!it.hasNext()) { - // no further name components, inode points to the correct file - break; - } 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; @@ -401,30 +422,51 @@ uint8_t *FileSystemTemplate::format(void *buffer, typename F #pragma warning(disable:4244) #endif template -int FileSystemTemplate::insertDirectoryEntry(uint64_t inode, const char *dirPath, const char *fileName) { - return 0; +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 retval = 0; + uint64_t inode = 0; auto current = files(); if (current) { - auto end = (DirectoryEntry*) ((uint8_t*) files()) + buffSize; + 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 = (DirectoryEntry*) (((uint8_t*) current) + current->size()); + if (current >= end) { current = nullptr; } } - retval = current->inode; + if (current) { + inode = current->inode; + } } - return retval; + return inode; } typedef FileSystemTemplate FileSystem16; diff --git a/src/ox/fs/pathiterator.cpp b/src/ox/fs/pathiterator.cpp index c53fd8fb1..f82cd8bee 100644 --- a/src/ox/fs/pathiterator.cpp +++ b/src/ox/fs/pathiterator.cpp @@ -18,6 +18,41 @@ PathIterator::PathIterator(const char *path, size_t maxSize) { 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; @@ -46,6 +81,9 @@ 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 / @@ -55,7 +93,6 @@ bool PathIterator::hasNext() { size_t end = substr - m_path; size = end - start; } - m_iterator += size; return size > 0; } diff --git a/src/ox/fs/pathiterator.hpp b/src/ox/fs/pathiterator.hpp index 4c4921c99..789e8ac8d 100644 --- a/src/ox/fs/pathiterator.hpp +++ b/src/ox/fs/pathiterator.hpp @@ -22,6 +22,16 @@ class PathIterator { 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 */ diff --git a/src/ox/fs/test/CMakeLists.txt b/src/ox/fs/test/CMakeLists.txt index d95fc0051..90a8016f3 100644 --- a/src/ox/fs/test/CMakeLists.txt +++ b/src/ox/fs/test/CMakeLists.txt @@ -28,10 +28,14 @@ target_link_libraries(FSTests OxFS OxStd) add_test("FileStoreFormat" FileStoreFormat) add_test("FileSystemFormat" FileSystemFormat) add_test("FileStoreIO" FileStoreIO) -add_test("Test\\ PathIterator1" FSTests PathIterator1) -add_test("Test\\ PathIterator2" FSTests PathIterator2) -add_test("Test\\ PathIterator3" FSTests PathIterator3) -add_test("Test\\ PathIterator4" FSTests PathIterator4) -add_test("Test\\ PathIterator5" FSTests PathIterator5) +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\\ FileSystem32::findInodeOf" FSTests FileSystem32::findInodeOf) +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 index 9c6072f74..161e2ca6c 100644 --- a/src/ox/fs/test/tests.cpp +++ b/src/ox/fs/test/tests.cpp @@ -15,12 +15,11 @@ using namespace std; using namespace ox::fs; -using namespace ox::std; -map tests = { +map> tests = { { { - "PathIterator1", + "PathIterator::next1", [](string) { int retval = 0; string path = "/usr/share/charset.gbag"; @@ -35,7 +34,7 @@ map tests = { } }, { - "PathIterator2", + "PathIterator::next2", [](string) { int retval = 0; string path = "/usr/share/"; @@ -49,7 +48,7 @@ map tests = { } }, { - "PathIterator3", + "PathIterator::next3", [](string) { int retval = 0; string path = "/"; @@ -62,7 +61,7 @@ map tests = { } }, { - "PathIterator4", + "PathIterator::next4", [](string) { int retval = 0; string path = "usr/share/charset.gbag"; @@ -77,7 +76,7 @@ map tests = { } }, { - "PathIterator5", + "PathIterator::next5", [](string) { int retval = 0; string path = "usr/share/"; @@ -91,7 +90,33 @@ map tests = { } }, { - "FileSystem32::findInodeOf", + "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; @@ -103,6 +128,29 @@ map tests = { 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; + } + }, }, }; diff --git a/src/ox/std/CMakeLists.txt b/src/ox/std/CMakeLists.txt index fe05c4d5f..35e38cbe9 100644 --- a/src/ox/std/CMakeLists.txt +++ b/src/ox/std/CMakeLists.txt @@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 2.8) add_library( OxStd memops.cpp + random.cpp strops.cpp ) 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/random.cpp b/src/ox/std/random.cpp new file mode 100644 index 000000000..98a3dfe43 --- /dev/null +++ b/src/ox/std/random.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 "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() { + 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 823b00943..93c9ad20c 100644 --- a/src/ox/std/std.hpp +++ b/src/ox/std/std.hpp @@ -7,7 +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 52b244ce6..5176b9b0b 100644 --- a/src/ox/std/strops.cpp +++ b/src/ox/std/strops.cpp @@ -58,6 +58,26 @@ char *ox_strchr(char *str, int character, size_t maxLen) { 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 80e574398..4fc8b9157 100644 --- a/src/ox/std/strops.hpp +++ b/src/ox/std/strops.hpp @@ -20,4 +20,8 @@ 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 3bf8f2bf1..da900ca6c 100644 --- a/src/ox/std/test/CMakeLists.txt +++ b/src/ox/std/test/CMakeLists.txt @@ -30,6 +30,7 @@ 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") ################################################################################ diff --git a/src/ox/std/test/strops_test.cpp b/src/ox/std/test/strops_test.cpp index 963b0f486..9aaf14be4 100644 --- a/src/ox/std/test/strops_test.cpp +++ b/src/ox/std/test/strops_test.cpp @@ -56,6 +56,16 @@ map> tests = { 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) {