diff --git a/CMakeLists.txt b/CMakeLists.txt index 0564235..3ccf471 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,9 +4,13 @@ project(FileSystemEmulator) set(CMAKE_CXX_STANDARD 20) -add_library(${PROJECT_NAME} STATIC FileSystemEmulator.cpp FileSystemEmulator.hpp) -target_include_directories(${PROJECT_NAME} PUBLIC .) +file(GLOB SOURCES "./src/*.cpp") +file(GLOB HEADERS "./inc/*.hpp") -add_executable(Test${PROJECT_NAME} Tests.cpp) +add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS}) +target_include_directories(${PROJECT_NAME} PUBLIC ./inc/) + +file(GLOB TEST_SOURCES "./test/*.cpp") +add_executable(Test${PROJECT_NAME} ${TEST_SOURCES}) target_link_libraries(Test${PROJECT_NAME} ${PROJECT_NAME}) add_test(NAME Test${PROJECT_NAME} COMMAND Test${PROJECT_NAME}) \ No newline at end of file diff --git a/FileSystemEmulator.hpp b/FileSystemEmulator.hpp deleted file mode 100644 index 08396ff..0000000 --- a/FileSystemEmulator.hpp +++ /dev/null @@ -1,385 +0,0 @@ -#pragma once - -#include "Path.hpp" - -#include -#include -#include -#include -#include -#include - -typedef std::string Key; -typedef unsigned long long ui64; -typedef unsigned long long ui32; - -struct Node { - enum Type : ui32 { NONE, DIRECTORY, FILE, LINK }; - - Key key; - - Node* parent = nullptr; - Node* left = nullptr; - Node* right = nullptr; - - ui32 height = 0; - ui32 incomingLinksHard = 0; - ui32 incomingLinksDynamic = 0; - - Type type = NONE; - - void updateTreeCache() { - // TODO update cache - } -}; - -struct File : public Node { - File() { - type = Type::FILE; - } -}; - -struct Link : public Node { - Link() { - type = LINK; - } - - Node* link = nullptr; - bool hard = false; -}; - -struct Directory : public Node { - Node* members = nullptr; - ui32 size = 0; - - Directory() { - type = DIRECTORY; - } - - bool attachNode(const std::vector& directoryPath, const Key& newKey, Node* newNode) { - Node* node = findNode(directoryPath, 0); - if (!node) { - // TODO : update error status - invalid path - return false; - } - - if (node->type != DIRECTORY) { - // TODO : update error status - given path is not a directory - return false; - } - - auto directory = ((Directory*) node); - - Node* existingNode = directory->treeSearch(newKey); - if (existingNode) { - if (existingNode->type == newNode->type) return false; // exit silently - // TODO : report error cant add node - return false; - } - - directory->treeInsert(newKey, newNode); - updateTreeLinkCount(newNode); - - return true; - } - - Node* findNode(const std::vector& path, ui32 currentDepth) { - if (path.size() == currentDepth) { - return this; - } - - const Key& key = path[currentDepth]; - Node* node = treeSearch(key); - - if (!node) { - return nullptr; - } - - // link on link is not allowed - while (true) { - switch (node->type) { - case Node::FILE: - if (currentDepth == path.size() - 1) return node; - return nullptr; - - case Node::DIRECTORY: - return ((Directory*)node)->findNode(path, ++currentDepth); - - case Node::LINK: - node = ((Link*)node)->link; - break; - - default: - return nullptr; - } - } - } - - Node* treeSearch(const Key& key) { - if (!members) return nullptr; - Node* iterator = members; - while (iterator) { - if (key > iterator->key) { - iterator = iterator->right; - } else if (key < iterator->key) { - iterator = iterator->left; - } else { - return iterator; - } - } - return nullptr; - } - - void updateTreeLinkCount(Node* node) { - // TODO : update all caches all the way up to '/' - } - - void detachNode(Node* node) { - // TODO : remove util from avl tree - // TODO : update all cache all the way up to the root (due to the links) - // TODO : dont relocate nodes (due existing links to the nodes), only change tree pointers - } - - // TODO : user avl tree insertion - // TODO : check for existing file - // TODO : return true if successful (node inserted) - // TODO : dont relocate nodes (due existing links to the nodes), only change tree pointers - void treeInsert(const Key& newKey, Node* newNode) { - newNode->key = newKey; - members = insertUtil(members, newKey, newNode); - } - - // recursively returns valid isLeft or isRight child or root - Node* insertUtil(Node* head, const Key& key, Node* aNode) { - - Node* insertedNode; - - if (head == nullptr) { - size++; - aNode->updateTreeCache(); - return aNode; - } else if (head->key == key) { - return head; - } else if (key > head->key) { - insertedNode = insertUtil(head->right, key, aNode); - head->right = insertedNode; - insertedNode->parent = head; - } else { - insertedNode = insertUtil(head->left, key, aNode); - head->left = insertedNode; - insertedNode->parent = head; - } - - // update height - head->height = 1 + std::max(getNodeHeight(head->right), getNodeHeight(head->left)); - - int balance = int(getNodeHeight(head->right) - getNodeHeight(head->left)); - - if (balance > 1) { - if (key > head->right->key) { - return rotateLeft(head); - } else { - head->right = rotateRight(head->right); - return rotateLeft(head); - } - } else if (balance < -1) { - if (key < head->left->key) { - return rotateRight(head); - } else { - head->left = rotateLeft(head->left); - return rotateRight(head); - } - } - - head->updateTreeCache(); - - return head; - } - - // returns new head - Node* rotateLeft(Node* pivot) { - Node* const head = pivot; - Node* const right = pivot->right; - Node* const right_left = right->left; - Node* const parent = pivot->parent; - - // parents - if (right_left) right_left->parent = head; - head->parent = right; - right->parent = parent; - - // children - head->right = right_left; - right->left = head; - - // heights - head->height = 1 + std::max(getNodeHeight(head->left), getNodeHeight(head->right)); - right->height = 1 + std::max(getNodeHeight(right->left), getNodeHeight(right->right)); - - // cache - head->updateTreeCache(); - right->updateTreeCache(); - - return right; - } - - Node* rotateRight(Node* pivot) { - Node* const head = pivot; - Node* const left = pivot->left; - Node* const left_right = left->right; - Node* const parent = pivot->parent; - - // parents - if (left_right) left_right->parent = head; - head->parent = left; - left->parent = parent; - - // children - head->left = left_right; - left->right = head; - - // heights - head->height = 1 + std::max(getNodeHeight(head->left), getNodeHeight(head->right)); - left->height = 1 + std::max(getNodeHeight(left->left), getNodeHeight(left->right)); - - // cache - head->updateTreeCache(); - left->updateTreeCache(); - - return left; - } - - static inline ui32 getNodeHeight(const Node* node) { return node ? node->height : -1; } - - template - void traverseInorder(tFunctor functor) const { - traverseInorderUtil(members, functor); - } - - template - void traverseInorderUtil(Node* node, tFunctor functor) const { - if (!node) return; - traverseInorderUtil(node->left, functor); - functor(node); - traverseInorderUtil(node->right, functor); - } - - Node* maxNode() const { - Node* head = members; - if (!head) return nullptr; - while (head->right != nullptr) { - head = head->right; - } - return head; - } - - void getMaxDepthUtil(ui32 depth, ui32& maxDepth) const { - if (!members) return; - maxDepth = std::max(depth, maxDepth); - traverseInorderUtil(members, [&](Node* node){ - if (node->type == DIRECTORY) { - ((Directory*)node)->getMaxDepthUtil(++depth, maxDepth); - } - }); - } - - ui32 getMaxDepth() const { - ui32 maxDepth = 0; - getMaxDepthUtil(0, maxDepth); - return maxDepth; - } -}; - -class FileSystem { - Directory* root = nullptr; - Directory* currentDirectory = nullptr; - -public: - FileSystem() { - root = new Directory(); - currentDirectory = root; - root->key = "/"; - initializeTransitions(); - } - - ~FileSystem() { - delete root; - } - - void makeDirectory(const Path& path) { - if (path.getDepth() < 1 || path.isInvalid()) { - // TODO : update error - invalid path - return; - } - - Directory* parentDirectory = path.isAbsolute() ? root : currentDirectory; - auto newDirectory = new Directory(); - if (!parentDirectory->attachNode(path.getParentChain(), path.getFilename(), newDirectory)) { - delete newDirectory; - } - } - - void changeCurrent(const Path& path) { - if (path.isInvalid()) { - // TODO : update error - invalid path - return; - } - - auto node = path.isAbsolute() ? root->findNode(path.getChain(), 0) : currentDirectory->findNode(path.getChain(), 0); - if (node->type != Node::DIRECTORY) { - // TODO : update error status - no such directory - return; - } - currentDirectory = (Directory*) node; - } - - void log() const { - std::stringstream ss; - std::vector indents; - indents.resize(root->getMaxDepth()); - logNode(ss, root, 0, indents); - std::cout << ss.str(); - } - -private: - void logNode(std::stringstream& ss, const Node* node, int depth, std::vector& indents) const { - switch (node->type) { - case Node::DIRECTORY: - return logDirectory(ss, (Directory*) node, depth, indents); - case Node::FILE: - return logFile(ss, (File*) node, depth, indents); - case Node::LINK: - return logLink(ss, (Link*) node, depth, indents); - } - } - - void indent(std::stringstream & ss, int depth, std::vector& indents) const { - if (!depth) return; - for (auto i = 0; i < depth - 1; i++) { - ss << (indents[i] ? " |" : " "); - } - ss << " |_"; - } - - void logDirectory(std::stringstream & ss, const Directory* node, int depth, std::vector& indents) const { - indent(ss, depth, indents); - ss << node->key << "\n"; - indents[depth] = true; - depth++; - auto lastNode = node->maxNode(); - node->traverseInorder([&](const Node* iterNode){ - if (lastNode == iterNode) indents[depth - 1] = false; - logNode(ss, iterNode, depth, indents); - }); - } - - void logFile(std::stringstream& ss, const File* node, int depth, std::vector& indents) const { - indent(ss, depth, indents); - ss << node->key << "\n"; - } - - void logLink(std::stringstream & ss, const Link* node, int depth, std::vector& indents) const { - indent(ss, depth, indents); - ss << "link [" << node->key << "] \n"; - } -}; \ No newline at end of file diff --git a/Path.hpp b/Path.hpp deleted file mode 100644 index 97b14ae..0000000 --- a/Path.hpp +++ /dev/null @@ -1,111 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -std::vector transitions; - -void initializeTransitions() { - transitions.resize(256); - std::fill(transitions.begin(), transitions.end(), 1); - for (char i = 'a'; i <= 'z'; i++) transitions[i] = i; - for (char i = '0'; i <= '9'; i++) transitions[i] = i; - for (char i = 'A'; i <= 'Z'; i++) transitions[i] = i + ('a' - 'A'); - transitions['/'] = '/'; - transitions['.'] = '.'; -} - -class Path { -public: - Path() = default; - - Path(const char* path) { - set(path); - } - - bool isValid() const { - return mIsValid; - } - - bool isInvalid() const { - return !mIsValid; - } - - bool isAbsolute() const { - return mAbsolute; - } - - int getDepth() const { - return mChain.size(); - } - - const std::vector& getChain() const { - return mChain; - } - - std::vector getParentChain() const { - std::vector out = mChain; - out.pop_back(); - return out; - } - - const std::string& getFilename() const { - assert(getDepth()); - return mChain.back(); - } - - void set(const std::string& path) { - if (path.empty()) { - mIsValid = false; - return; - } - - std::string lowercasePath = path; - mIsValid = true; - mAbsolute = path.front() == '/'; - mDirectory = path.back() == '/'; - mChain.clear(); - - for (auto & character : lowercasePath) { - character = transitions[character]; - if (character == 1) mIsValid = false; - } - - if (lowercasePath == "/") return; - - const char* begin = &lowercasePath.front() + (lowercasePath.front() == '/'); - const char* end = &lowercasePath.back() - (lowercasePath.back() == '/'); - const char* prev = begin; - - for (const char* iter = begin; iter <= end; iter++) { - if (*iter == '/') { - const auto string = lowercasePath.substr(prev - lowercasePath.c_str(), iter - prev); - mChain.push_back(string); - prev = iter + 1; - } - } - - const auto string = lowercasePath.substr(prev - lowercasePath.c_str(), end - prev + 1); - mChain.push_back(string); - - for (auto & key : mChain) { - if (key.empty()) mIsValid = false; - // std::cout << key << ' '; - } - // std::cout << "\n"; - } - - const std::string& operator[](int idx) const { - return mChain[idx]; - }; - -private: - std::vector mChain; - bool mAbsolute = false; - bool mIsValid = false; - bool mDirectory = false; -}; \ No newline at end of file diff --git a/Tests.cpp b/Tests.cpp deleted file mode 100644 index 82d6fb0..0000000 --- a/Tests.cpp +++ /dev/null @@ -1,26 +0,0 @@ - -#include "FileSystemEmulator.hpp" - -namespace fs = std::filesystem; - -int main() { - FileSystem fse; - - fse.makeDirectory("/a"); - fse.makeDirectory("/a/b"); - fse.makeDirectory("/a/b/k"); - fse.makeDirectory("/a/c"); - fse.makeDirectory("/a/c/k"); - - fse.makeDirectory("/d"); - fse.makeDirectory("/d/b"); - fse.makeDirectory("/d/b/k"); - fse.makeDirectory("/d/c"); - fse.makeDirectory("/d/c/k"); - - fse.makeDirectory("d"); - - fse.log(); - - return 0; -} \ No newline at end of file diff --git a/inc/FileSystem.hpp b/inc/FileSystem.hpp new file mode 100644 index 0000000..c9dfec2 --- /dev/null +++ b/inc/FileSystem.hpp @@ -0,0 +1,96 @@ +#pragma once + +#include "Path.hpp" +#include + +typedef std::string Key; +typedef unsigned long long ui64; +typedef unsigned long long ui32; + +struct Node { + enum Type : ui32 { NONE, DIRECTORY, FILE, LINK }; + + Key key; + + Node* parent = nullptr; + Node* left = nullptr; + Node* right = nullptr; + + ui32 height = 0; + ui32 incomingLinksHard = 0; + ui32 incomingLinksDynamic = 0; + + Type type = NONE; + + void updateTreeCache(); +}; + +struct File : public Node { + File(); +}; + +struct Link : public Node { + Link(); + + Node* link = nullptr; + bool hard = false; +}; + +struct Directory : public Node { + Directory(); + + bool attachNode(const std::vector& directoryPath, const Key& newKey, Node* newNode); + Node* findNode(const std::vector& path, ui32 currentDepth); + void detachNode(Node* node); + + template + void traverseInorder(tFunctor functor) const { + traverseInorderUtil(members, functor); + } + + [[nodiscard]] Node* maxNode() const; + [[nodiscard]] ui32 getMaxDepth() const; + +private: + Node* treeSearch(const Key& key); + void updateTreeLinkCount(Node* node); + void treeInsert(const Key& newKey, Node* newNode); + Node* insertUtil(Node* head, const Key& key, Node* aNode); + Node* rotateLeft(Node* pivot); + Node* rotateRight(Node* pivot); + static inline ui32 getNodeHeight(const Node* node); + void getMaxDepthUtil(ui32 depth, ui32& maxDepth) const; + + template + void traverseInorderUtil(Node* node, tFunctor functor) const { + if (!node) return; + traverseInorderUtil(node->left, functor); + functor(node); + traverseInorderUtil(node->right, functor); + } + +public: + Node* members = nullptr; + ui32 size = 0; +}; + +class FileSystem { +public: + FileSystem(); + ~FileSystem(); + + void makeDirectory(const Path& path); + void changeCurrent(const Path& path); + void log() const; + +private: + void logNode(std::stringstream& ss, const Node* node, int depth, std::vector& indents) const; + void indent(std::stringstream & ss, int depth, std::vector& indents) const; + void logDirectory(std::stringstream & ss, const Directory* node, int depth, std::vector& indents) const; + void logFile(std::stringstream& ss, const File* node, int depth, std::vector& indents) const; + void logLink(std::stringstream & ss, const Link* node, int depth, std::vector& indents) const; + +private: + Directory* root = nullptr; + Directory* currentDirectory = nullptr; +}; \ No newline at end of file diff --git a/inc/Path.hpp b/inc/Path.hpp new file mode 100644 index 0000000..f0085e0 --- /dev/null +++ b/inc/Path.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +extern std::vector transitions; +void initializeTransitions(); + +class Path { +public: + Path() = default; + Path(const char* path); + + void set(const std::string& path); + const std::string& operator[](int idx) const; + + bool isValid() const; + bool isInvalid() const; + bool isAbsolute() const; + int getDepth() const; + + const std::vector& getChain() const; + std::vector getParentChain() const; + const std::string& getFilename() const; + +private: + std::vector mChain; + bool mAbsolute = false; + bool mIsValid = false; + bool mDirectory = false; +}; \ No newline at end of file diff --git a/src/FileSystem.cpp b/src/FileSystem.cpp new file mode 100644 index 0000000..ddf8ea7 --- /dev/null +++ b/src/FileSystem.cpp @@ -0,0 +1,327 @@ +#include "FileSystem.hpp" +#include + +void Node::updateTreeCache() { + // TODO update cache +} + +File::File() { + type = Type::FILE; +} + +Link::Link() { + type = LINK; +} + +Directory::Directory() { + type = DIRECTORY; +} + +bool Directory::attachNode(const std::vector& directoryPath, const Key& newKey, Node* newNode) { + Node* node = findNode(directoryPath, 0); + if (!node) { + // TODO : update error status - invalid path + return false; + } + + if (node->type != DIRECTORY) { + // TODO : update error status - given path is not a directory + return false; + } + + auto directory = ((Directory*) node); + + Node* existingNode = directory->treeSearch(newKey); + if (existingNode) { + if (existingNode->type == newNode->type) return false; // exit silently + // TODO : report error cant add node + return false; + } + + directory->treeInsert(newKey, newNode); + updateTreeLinkCount(newNode); + + return true; +} + +Node* Directory::findNode(const std::vector& path, ui32 currentDepth) { + if (path.size() == currentDepth) { + return this; + } + + const Key& key = path[currentDepth]; + Node* node = treeSearch(key); + + if (!node) { + return nullptr; + } + + // link on link is not allowed + while (true) { + switch (node->type) { + case Node::FILE: + if (currentDepth == path.size() - 1) return node; + return nullptr; + + case Node::DIRECTORY: + return ((Directory*)node)->findNode(path, ++currentDepth); + + case Node::LINK: + node = ((Link*)node)->link; + break; + + default: + return nullptr; + } + } +} + +Node* Directory::treeSearch(const Key& key) { + if (!members) return nullptr; + Node* iterator = members; + while (iterator) { + if (key > iterator->key) { + iterator = iterator->right; + } else if (key < iterator->key) { + iterator = iterator->left; + } else { + return iterator; + } + } + return nullptr; +} + +void Directory::updateTreeLinkCount(Node* node) { + // TODO : update all caches all the way up to '/' +} + +void Directory::detachNode(Node* node) { + // TODO : remove util from avl tree + // TODO : update all cache all the way up to the root (due to the links) + // TODO : dont relocate nodes (due existing links to the nodes), only change tree pointers +} + +// TODO : user avl tree insertion +// TODO : check for existing file +// TODO : return true if successful (node inserted) +// TODO : dont relocate nodes (due existing links to the nodes), only change tree pointers +void Directory::treeInsert(const Key& newKey, Node* newNode) { + newNode->key = newKey; + members = insertUtil(members, newKey, newNode); +} + +// recursively returns valid isLeft or isRight child or root +Node* Directory::insertUtil(Node* head, const Key& key, Node* aNode) { + + Node* insertedNode; + + if (head == nullptr) { + size++; + aNode->updateTreeCache(); + return aNode; + } else if (head->key == key) { + return head; + } else if (key > head->key) { + insertedNode = insertUtil(head->right, key, aNode); + head->right = insertedNode; + insertedNode->parent = head; + } else { + insertedNode = insertUtil(head->left, key, aNode); + head->left = insertedNode; + insertedNode->parent = head; + } + + // update height + head->height = 1 + std::max(getNodeHeight(head->right), getNodeHeight(head->left)); + + int balance = int(getNodeHeight(head->right) - getNodeHeight(head->left)); + + if (balance > 1) { + if (key > head->right->key) { + return rotateLeft(head); + } else { + head->right = rotateRight(head->right); + return rotateLeft(head); + } + } else if (balance < -1) { + if (key < head->left->key) { + return rotateRight(head); + } else { + head->left = rotateLeft(head->left); + return rotateRight(head); + } + } + + head->updateTreeCache(); + + return head; +} + +// returns new head +Node* Directory::rotateLeft(Node* pivot) { + Node* const head = pivot; + Node* const right = pivot->right; + Node* const right_left = right->left; + Node* const parent = pivot->parent; + + // parents + if (right_left) right_left->parent = head; + head->parent = right; + right->parent = parent; + + // children + head->right = right_left; + right->left = head; + + // heights + head->height = 1 + std::max(getNodeHeight(head->left), getNodeHeight(head->right)); + right->height = 1 + std::max(getNodeHeight(right->left), getNodeHeight(right->right)); + + // cache + head->updateTreeCache(); + right->updateTreeCache(); + + return right; +} + +Node* Directory::rotateRight(Node* pivot) { + Node* const head = pivot; + Node* const left = pivot->left; + Node* const left_right = left->right; + Node* const parent = pivot->parent; + + // parents + if (left_right) left_right->parent = head; + head->parent = left; + left->parent = parent; + + // children + head->left = left_right; + left->right = head; + + // heights + head->height = 1 + std::max(getNodeHeight(head->left), getNodeHeight(head->right)); + left->height = 1 + std::max(getNodeHeight(left->left), getNodeHeight(left->right)); + + // cache + head->updateTreeCache(); + left->updateTreeCache(); + + return left; +} + +ui32 Directory::getNodeHeight(const Node* node) { return node ? node->height : -1; } + +Node* Directory::maxNode() const { + Node* head = members; + if (!head) return nullptr; + while (head->right != nullptr) { + head = head->right; + } + return head; +} + +void Directory::getMaxDepthUtil(ui32 depth, ui32& maxDepth) const { + if (!members) return; + maxDepth = std::max(depth, maxDepth); + traverseInorderUtil(members, [&](Node* node){ + if (node->type == DIRECTORY) { + ((Directory*)node)->getMaxDepthUtil(++depth, maxDepth); + } + }); +} + +ui32 Directory::getMaxDepth() const { + ui32 maxDepth = 0; + getMaxDepthUtil(0, maxDepth); + return maxDepth; +} + + +FileSystem::FileSystem() { + root = new Directory(); + currentDirectory = root; + root->key = "/"; + initializeTransitions(); + } + +FileSystem::~FileSystem() { + delete root; + } + +void FileSystem::makeDirectory(const Path& path) { + if (path.getDepth() < 1 || path.isInvalid()) { + // TODO : update error - invalid path + return; + } + + Directory* parentDirectory = path.isAbsolute() ? root : currentDirectory; + auto newDirectory = new Directory(); + if (!parentDirectory->attachNode(path.getParentChain(), path.getFilename(), newDirectory)) { + delete newDirectory; + } +} + +void FileSystem::changeCurrent(const Path& path) { + if (path.isInvalid()) { + // TODO : update error - invalid path + return; + } + + auto node = path.isAbsolute() ? root->findNode(path.getChain(), 0) : currentDirectory->findNode(path.getChain(), 0); + if (node->type != Node::DIRECTORY) { + // TODO : update error status - no such directory + return; + } + currentDirectory = (Directory*) node; +} + +void FileSystem::log() const { + std::stringstream ss; + std::vector indents; + indents.resize(root->getMaxDepth()); + logNode(ss, root, 0, indents); + std::cout << ss.str(); +} + + +void FileSystem::logNode(std::stringstream& ss, const Node* node, int depth, std::vector& indents) const { + switch (node->type) { + case Node::DIRECTORY: + return logDirectory(ss, (Directory*) node, depth, indents); + case Node::FILE: + return logFile(ss, (File*) node, depth, indents); + case Node::LINK: + return logLink(ss, (Link*) node, depth, indents); + } +} + +void FileSystem::indent(std::stringstream & ss, int depth, std::vector& indents) const { + if (!depth) return; + for (auto i = 0; i < depth - 1; i++) { + ss << (indents[i] ? " |" : " "); + } + ss << " |_"; +} + +void FileSystem::logDirectory(std::stringstream & ss, const Directory* node, int depth, std::vector& indents) const { + indent(ss, depth, indents); + ss << node->key << "\n"; + indents[depth] = true; + depth++; + auto lastNode = node->maxNode(); + node->traverseInorder([&](const Node* iterNode){ + if (lastNode == iterNode) indents[depth - 1] = false; + logNode(ss, iterNode, depth, indents); + }); +} + +void FileSystem::logFile(std::stringstream& ss, const File* node, int depth, std::vector& indents) const { + indent(ss, depth, indents); + ss << node->key << "\n"; +} + +void FileSystem::logLink(std::stringstream & ss, const Link* node, int depth, std::vector& indents) const { + indent(ss, depth, indents); + ss << "link [" << node->key << "] \n"; +} diff --git a/src/Path.cpp b/src/Path.cpp new file mode 100644 index 0000000..c2f0829 --- /dev/null +++ b/src/Path.cpp @@ -0,0 +1,92 @@ +#include "Path.hpp" +#include + +std::vector transitions; + +void initializeTransitions() { + transitions.resize(256); + std::fill(transitions.begin(), transitions.end(), 1); + for (char i = 'a'; i <= 'z'; i++) transitions[i] = i; + for (char i = '0'; i <= '9'; i++) transitions[i] = i; + for (char i = 'A'; i <= 'Z'; i++) transitions[i] = i + ('a' - 'A'); + transitions['/'] = '/'; + transitions['.'] = '.'; +} + +Path::Path(const char* path) { + set(path); +} + +bool Path::isValid() const { + return mIsValid; +} + +bool Path::isInvalid() const { + return !mIsValid; +} + +bool Path::isAbsolute() const { + return mAbsolute; +} + +int Path::getDepth() const { + return mChain.size(); +} + +const std::vector& Path::getChain() const { + return mChain; +} + +std::vector Path::getParentChain() const { + std::vector out = mChain; + out.pop_back(); + return out; +} + +const std::string& Path::getFilename() const { + assert(getDepth()); + return mChain.back(); +} + +void Path::set(const std::string& path) { + if (path.empty()) { + mIsValid = false; + return; + } + + std::string lowercasePath = path; + mIsValid = true; + mAbsolute = path.front() == '/'; + mDirectory = path.back() == '/'; + mChain.clear(); + + for (auto & character : lowercasePath) { + character = transitions[character]; + if (character == 1) mIsValid = false; + } + + if (lowercasePath == "/") return; + + const char* begin = &lowercasePath.front() + (lowercasePath.front() == '/'); + const char* end = &lowercasePath.back() - (lowercasePath.back() == '/'); + const char* prev = begin; + + for (const char* iter = begin; iter <= end; iter++) { + if (*iter == '/') { + const auto string = lowercasePath.substr(prev - lowercasePath.c_str(), iter - prev); + mChain.push_back(string); + prev = iter + 1; + } + } + + const auto string = lowercasePath.substr(prev - lowercasePath.c_str(), end - prev + 1); + mChain.push_back(string); + + for (auto & key : mChain) { + if (key.empty()) mIsValid = false; + } +} + +const std::string& Path::operator[](int idx) const { + return mChain[idx]; +} diff --git a/test/Tests.cpp b/test/Tests.cpp new file mode 100644 index 0000000..f3dca9a --- /dev/null +++ b/test/Tests.cpp @@ -0,0 +1,42 @@ + +#include "FileSystem.hpp" + +int main() { + FileSystem fse; + + fse.makeDirectory("/a"); + fse.makeDirectory("/a/b"); + fse.makeDirectory("/a/b/k"); + fse.makeDirectory("/a/c"); + fse.makeDirectory("/a/c/k"); + + fse.makeDirectory("/d"); + fse.makeDirectory("/d/b"); + fse.makeDirectory("/d/b/k"); + fse.makeDirectory("/d/c"); + fse.makeDirectory("/d/c/k"); + + fse.makeDirectory("d"); + + fse.changeCurrent("/d/c/k"); + + fse.makeDirectory("/a"); + fse.makeDirectory("/a/b"); + fse.makeDirectory("/a/b/k"); + fse.makeDirectory("/a/c"); + fse.makeDirectory("/a/c/k"); + + fse.makeDirectory("ggad"); + fse.makeDirectory("gad"); + fse.makeDirectory("rwed"); + fse.makeDirectory("e"); + fse.makeDirectory("d"); + fse.makeDirectory("d/b"); + fse.makeDirectory("d/b/k"); + fse.makeDirectory("d/c"); + fse.makeDirectory("d/c/k"); + + fse.log(); + + return 0; +} \ No newline at end of file diff --git a/FileSystemEmulator.cpp b/tmp similarity index 100% rename from FileSystemEmulator.cpp rename to tmp