From d7cc11143b8554962ac4e86ebe849da335c7b24a Mon Sep 17 00:00:00 2001 From: IlyaShurupov Date: Tue, 26 Mar 2024 20:39:11 +0300 Subject: [PATCH] tmp --- FileSystemEmulator.cpp | 412 +++++++++++++++++++++++++++++++++++++++++ FileSystemEmulator.hpp | 396 ++++++++++++++++++++++++++++----------- Path.hpp | 74 ++++++-- Tests.cpp | 22 +-- 4 files changed, 767 insertions(+), 137 deletions(-) diff --git a/FileSystemEmulator.cpp b/FileSystemEmulator.cpp index e69de29..34c2287 100644 --- a/FileSystemEmulator.cpp +++ b/FileSystemEmulator.cpp @@ -0,0 +1,412 @@ + +// WRITE LOG +// WRITE TESTS + +// use tree for leafs in the node +// use 8byte + 3byte for the name of the node, also key in the tree +// don't store size in the tree +// better use RB tree + +// each node type has different size +// implement node using virtual functions and inheritance +// only directory node has leafs (tree) + +// make two classes - emulator and interpreter +// emulator stores only lower case names + +// store number of incoming hard links in the node cache to ensure no hard-linked nodes can be removed + + +// convert command to lowercase then use full path as the key and update key pointer when descending to the leafs + +// each node has parent pointer to check for current directory + +// base node class has all the cache data. use it directly in the tree +// dynamic links ? -> yet another cache variable in each node? + +/* +#pragma once + +#include "ContainersCommon.hpp" + +namespace tp { + + template + struct AvlNumericKey { + + NumericType val; + + AvlNumericKey() = default; + AvlNumericKey(NumericType val) : + val(val) {} + + inline bool descentRight(AvlNumericKey in) const { return in.val > val; } + inline bool descentLeft(AvlNumericKey in) const { return in.val < val; } + inline bool exactNode(AvlNumericKey in) const { return in.val == val; } + + inline AvlNumericKey getFindKey() const { return *this; } + inline AvlNumericKey keyInRightSubtree(AvlNumericKey in) const { return in; } + inline AvlNumericKey keyInLeftSubtree(AvlNumericKey in) const { return in; } + + template + inline void updateTreeCacheCallBack(const NodeType&) {} + }; + + template + class AvlTree { + typedef SelectValueOrReference KeyArg; + typedef SelectValueOrReference DataArg; + + public: + class Node { + friend AvlTree; + + private: + Node(KeyArg aKey, DataArg aData) : + key(aKey), + data(aData) {} + + public: + Data data; + Key key; + + public: + Node* mLeft = nullptr; + Node* mRight = nullptr; + Node* mParent = nullptr; + ualni mHeight = 0; + + private: + inline bool descentRight(KeyArg aKey) const { return key.descentRight(aKey); } + inline bool descentLeft(KeyArg aKey) const { return key.descentLeft(aKey); } + inline bool exactNode(KeyArg aKey) const { return key.exactNode(aKey); } + + inline KeyArg getFindKey(const Node* node = nullptr) const { return key.getFindKey(); } + inline KeyArg keyInRightSubtree(KeyArg aKey) const { return key.keyInRightSubtree(aKey); } + inline KeyArg keyInLeftSubtree(KeyArg aKey) const { return key.keyInLeftSubtree(aKey); } + + inline void updateTreeCacheCallBack() { key.updateTreeCacheCallBack(*this); } + }; + + public: + AvlTree() {} + ~AvlTree() { removeAll(); } + + [[nodiscard]] ualni size() const { return mSize; } + + Node* head() const { return this->mRoot; } + + void insert(KeyArg key, DataArg data) { + mRoot = insertUtil(mRoot, key, data); + mRoot->mParent = nullptr; + } + + void remove(KeyArg key) { + mRoot = removeUtil(mRoot, key); + if (mRoot) mRoot->mParent = nullptr; + } + + Node* maxNode(Node* head) const { + if (!head) return nullptr; + while (head->mRight != nullptr) { + head = head->mRight; + } + return head; + } + + Node* minNode(Node* head) const { + if (!head) return nullptr; + while (head->mLeft != nullptr) { + head = head->mLeft; + } + return head; + } + + Node* find(KeyArg key) const { + Node* iter = mRoot; + while (true) { + if (!iter) return nullptr; + if (iter->exactNode(key)) return iter; + if (iter->descentLeft(key)) { + key = iter->keyInLeftSubtree(key); + iter = iter->mLeft; + } else { + key = iter->keyInRightSubtree(key); + iter = iter->mRight; + } + } + } + + Node* findLessOrEq(KeyArg key) const { + Node* iter = mRoot; + while (true) { + if (!iter) return nullptr; + if (iter->exactNode(key)) return iter; + if (iter->descentLeft(key)) { + if (iter->mLeft) { + key = iter->keyInLeftSubtree(key); + iter = iter->mLeft; + } else { + return iter; + } + } else { + if (iter->mRight) { + key = iter->keyInRightSubtree(key); + iter = iter->mRight; + } else { + return iter; + } + } + } + } + + // returns first invalid node + const Node* findInvalidNode(const Node* head) const { + if (head == nullptr) return nullptr; + + if (head->mLeft) { + // TODO: incomplete test + if (!head->descentLeft(head->mLeft->getFindKey(head))) return head; + if (head->mLeft->mParent != head) return head; + if (!head->mRight && head->mLeft->mHeight != head->mHeight - 1) return head; + } + + if (head->mRight) { + if (!head->descentRight(head->mRight->getFindKey(head))) return head; + if (head->mRight->mParent != head) return head; + if (!head->mLeft && head->mRight->mHeight != head->mHeight - 1) return head; + } + + if (head->mLeft && head->mRight) { + if (max(head->mLeft->mHeight, head->mRight->mHeight) != head->mHeight - 1) return head; + } + + int balance = getNodeHeight(head->mRight) - getNodeHeight(head->mLeft); + + if (balance > 1 || balance < -1) return head; + + const Node* ret = findInvalidNode(head->mRight); + + if (ret) return ret; + + return findInvalidNode(head->mLeft); + } + + bool isValid() { return findInvalidNode(head()) == nullptr; } + + template + void traverse(Node* node, bool after, tFunctor functor) { + if (!after) functor(node); + if (node->mLeft) traverse(node->mLeft, after, functor); + if (node->mRight) traverse(node->mRight, after, functor); + if (after) functor(node); + } + + void removeAll() { + if (!mRoot) return; + removeUtil(mRoot); + mRoot = nullptr; + mSize = 0; + } + + void removeUtil(Node* node) { + if (node->mLeft) removeUtil(node->mLeft); + if (node->mRight) removeUtil(node->mRight); + deleteNode(node); + } + + public: + template + void archiveWrite(tArchiver& file) const { + FAIL("not implemented") + } + + template + void archiveRead(tArchiver&) { + FAIL("not implemented") + } + + private: + inline void deleteNode(Node* node) { + node->~Node(); + mAlloc.deallocate(node); + } + + inline Node* newNode(KeyArg key, DataArg data) { return new (mAlloc.allocate(sizeof(Node))) Node(key, data); } + + inline void injectNodeInstead(Node* place, Node* inject) { + // TODO : swap instead of copy + place->data = inject->data; + place->key = inject->key; + } + + inline alni getNodeHeight(const Node* node) const { return node ? node->mHeight : -1; } + + // returns new head + Node* rotateLeft(Node* pivot) { + DEBUG_ASSERT(pivot); + + Node* const head = pivot; + Node* const right = pivot->mRight; + Node* const right_left = right->mLeft; + Node* const parent = pivot->mParent; + + // parents + if (right_left) right_left->mParent = head; + head->mParent = right; + right->mParent = parent; + + // children + head->mRight = right_left; + right->mLeft = head; + + // heights + head->mHeight = 1 + max(getNodeHeight(head->mLeft), getNodeHeight(head->mRight)); + right->mHeight = 1 + max(getNodeHeight(right->mLeft), getNodeHeight(right->mRight)); + + // cache + head->updateTreeCacheCallBack(); + right->updateTreeCacheCallBack(); + + return right; + } + + Node* rotateRight(Node* pivot) { + DEBUG_ASSERT(pivot); + + Node* const head = pivot; + Node* const left = pivot->mLeft; + Node* const left_right = left->mRight; + Node* const parent = pivot->mParent; + + // parents + if (left_right) left_right->mParent = head; + head->mParent = left; + left->mParent = parent; + + // children + head->mLeft = left_right; + left->mRight = head; + + // heights + head->mHeight = 1 + max(getNodeHeight(head->mLeft), getNodeHeight(head->mRight)); + left->mHeight = 1 + max(getNodeHeight(left->mLeft), getNodeHeight(left->mRight)); + + // cache + head->updateTreeCacheCallBack(); + left->updateTreeCacheCallBack(); + + return left; + } + + // recursively returns valid isLeft or isRight child or root + Node* insertUtil(Node* head, KeyArg key, DataArg data) { + + Node* insertedNode; + + if (head == nullptr) { + mSize++; + Node* out = newNode(key, data); + out->updateTreeCacheCallBack(); + return out; + } else if (head->exactNode(key)) { + return head; + } else if (head->descentRight(key)) { + insertedNode = insertUtil(head->mRight, head->keyInRightSubtree(key), data); + head->mRight = insertedNode; + insertedNode->mParent = head; + } else { + insertedNode = insertUtil(head->mLeft, head->keyInLeftSubtree(key), data); + head->mLeft = insertedNode; + insertedNode->mParent = head; + } + + // update height + head->mHeight = 1 + max(getNodeHeight(head->mRight), getNodeHeight(head->mLeft)); + + alni balance = alni(getNodeHeight(head->mRight) - getNodeHeight(head->mLeft)); + + if (balance > 1) { + if (head->mRight->descentRight(head->keyInRightSubtree(key))) { + return rotateLeft(head); + } else { + head->mRight = rotateRight(head->mRight); + return rotateLeft(head); + } + } else if (balance < -1) { + if (head->mLeft->descentLeft(head->keyInLeftSubtree(key))) { + return rotateRight(head); + } else { + head->mLeft = rotateLeft(head->mLeft); + return rotateRight(head); + } + } + + head->updateTreeCacheCallBack(); + + return head; + } + + Node* removeUtil(Node* head, KeyArg key) { + if (head == nullptr) return head; + + if (head->exactNode(key)) { + if (head->mRight && head->mLeft) { + Node* min = minNode(head->mRight); + auto const& newKey = min->getFindKey(head->mRight); + injectNodeInstead(head, min); + head->mRight = removeUtil(head->mRight, newKey); + } else if (head->mRight) { + injectNodeInstead(head, head->mRight); + deleteNode(head->mRight); + head->mRight = nullptr; + mSize--; + } else if (head->mLeft) { + injectNodeInstead(head, head->mLeft); + deleteNode(head->mLeft); + head->mLeft = nullptr; + mSize--; + } else { + deleteNode(head); + mSize--; + head = nullptr; + } + } else if (head->descentRight(key)) { + head->mRight = removeUtil(head->mRight, head->keyInRightSubtree(key)); + } else if (head->descentLeft(key)) { + head->mLeft = removeUtil(head->mLeft, head->keyInLeftSubtree(key)); + } + + if (head == nullptr) return head; + + head->mHeight = 1 + max(getNodeHeight(head->mRight), getNodeHeight(head->mLeft)); + alni balance = getNodeHeight(head->mRight) - getNodeHeight(head->mLeft); + + if (balance < -1) { + if (getNodeHeight(head->mLeft->mLeft) >= getNodeHeight(head->mLeft->mRight)) { + return rotateRight(head); + } else { + head->mLeft = rotateLeft(head->mLeft); + return rotateRight(head); + } + } else if (balance > 1) { + if (getNodeHeight(head->mRight->mRight) >= getNodeHeight(head->mRight->mLeft)) { + return rotateLeft(head); + } else { + head->mRight = rotateRight(head->mRight); + return rotateLeft(head); + } + } + + head->updateTreeCacheCallBack(); + + return head; + } + + private: + Node* mRoot = nullptr; + ualni mSize = 0; + Allocator mAlloc; + }; +} +*/ \ No newline at end of file diff --git a/FileSystemEmulator.hpp b/FileSystemEmulator.hpp index 750eeec..08396ff 100644 --- a/FileSystemEmulator.hpp +++ b/FileSystemEmulator.hpp @@ -1,30 +1,5 @@ #pragma once -// WRITE LOG -// WRITE TESTS - -// use tree for leafs in the node -// use 8byte + 3byte for the name of the node, also key in the tree -// don't store size in the tree -// better use RB tree - -// each node type has different size -// implement node using virtual functions and inheritance -// only directory node has leafs (tree) - -// make two classes - emulator and interpreter -// emulator stores only lower case names - -// store number of incoming hard links in the node cache to ensure no hard-linked nodes can be removed - - -// convert command to lowercase then use full path as the key and update key pointer when descending to the leafs - -// each node has parent pointer to check for current directory - -// base node class has all the cache data. use it directly in the tree -// dynamic links ? -> yet another cache variable in each node? - #include "Path.hpp" #include @@ -32,121 +7,103 @@ #include #include #include +#include typedef std::string Key; -typedef std::map Tree; +typedef unsigned long long ui64; +typedef unsigned long long ui32; -class Node { -public: - Node() { - type = NONE; +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 } - - virtual void log(std::stringstream& ss, const Key& key, int depth) { - ss << "ss"; - } - - virtual ~Node() = default; - -protected: - enum Type : unsigned int { NONE, DIRECTORY, FILE, LINK }; - Type type; }; -class File : public Node { -public: +struct File : public Node { File() { type = Type::FILE; } - - void log(std::stringstream& ss, const Key& key, int depth) override { - ss << std::setw(depth * 2) << key << "\n"; - } - - ~File() override = default; }; -class Link : public Node { -public: - +struct Link : public Node { Link() { type = LINK; } - void log(std::stringstream & ss, const Key& key, int depth) override { - ss << std::setw(depth * 2) << "link [" << key << "] \n"; - } - - ~Link() override = default; - -private: Node* link = nullptr; bool hard = false; }; -class Directory : public Node { -public: +struct Directory : public Node { + Node* members = nullptr; + ui32 size = 0; + Directory() { type = DIRECTORY; } - ~Directory() override = default; - - 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 - } - - bool attachNode(const Path& directoryPath, const Key& newKey, Node* newNode) { - Node* node = findNode(directoryPath); + 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) { + if (node->type != DIRECTORY) { // TODO : update error status - given path is not a directory - // return false; - //} + return false; + } - if (!((Directory*) node)->insertUtil(newKey, newNode)) return false; + auto directory = ((Directory*) node); - // if (newNode->type == LINK) { - // TODO : update all caches (due to the new link update) - // } + 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; } - bool insertUtil(const Key& newKey, Node* newNode) { - // 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 - return false; - } - - Node* findNode(const Path& path) { - Node* node = this; - - if (false /*!path.advance()*/) { - return node; + Node* findNode(const std::vector& path, ui32 currentDepth) { + if (path.size() == currentDepth) { + return this; } - const Key& key = {}; // path.getCurrentKey(); - node = subNodes.find(key)->second; + const Key& key = path[currentDepth]; + Node* node = treeSearch(key); if (!node) { return nullptr; } - // TODO : dont allow cyclic links (with length one?)? + // 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); + return ((Directory*)node)->findNode(path, ++currentDepth); case Node::LINK: node = ((Link*)node)->link; @@ -155,19 +112,182 @@ public: default: return nullptr; } - */ } } - void log(std::stringstream & ss, const Key& key, int depth) override { - ss << std::setw(depth * 2) << key; - for (auto & node : subNodes) { - node.second->log(ss, node.first, depth + 1); + 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; } -private: - Tree subNodes; + 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 { @@ -177,6 +297,8 @@ class FileSystem { public: FileSystem() { root = new Directory(); + currentDirectory = root; + root->key = "/"; initializeTransitions(); } @@ -185,27 +307,79 @@ public: } 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.parent_path(), path.filename(), newDirectory)) { + if (!parentDirectory->attachNode(path.getParentChain(), path.getFilename(), newDirectory)) { delete newDirectory; } - */ } void changeCurrent(const Path& path) { - auto node = path.isAbsolute() ? root->findNode(path) : currentDirectory->findNode(path); - // if (node->type != Node::DIRECTORY) { + 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; - // } + return; + } currentDirectory = (Directory*) node; } - void log() { + void log() const { std::stringstream ss; - root->log(ss, "/", 0); + 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 index eaa797c..97b14ae 100644 --- a/Path.hpp +++ b/Path.hpp @@ -3,6 +3,9 @@ #include #include #include +#include +#include +#include std::vector transitions; @@ -13,10 +16,7 @@ void initializeTransitions() { for (char i = '0'; i <= '9'; i++) transitions[i] = i; for (char i = 'A'; i <= 'Z'; i++) transitions[i] = i + ('a' - 'A'); transitions['/'] = '/'; -} - -char getChar(char in) { - return transitions[in]; + transitions['.'] = '.'; } class Path { @@ -27,16 +27,35 @@ public: set(path); } - bool isValid() { + bool isValid() const { return mIsValid; } + bool isInvalid() const { + return !mIsValid; + } + bool isAbsolute() const { return mAbsolute; } int getDepth() const { - return mDepth; + 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) { @@ -45,23 +64,48 @@ public: return; } + std::string lowercasePath = path; mIsValid = true; - mPath = path; - - mDepth = std::count(path.begin(), path.end(), '/') + 1; - mDepth -= (mPath.back() == '/') + (path.front() == '/'); - mAbsolute = path.front() == '/'; + mDirectory = path.back() == '/'; + mChain.clear(); - for (auto & character : mPath) { - character = getChar(character); + 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::string mPath = ""; - int mDepth = 0; + 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 index b52b31a..82d6fb0 100644 --- a/Tests.cpp +++ b/Tests.cpp @@ -6,20 +6,20 @@ namespace fs = std::filesystem; int main() { FileSystem fse; - Path path; + fse.makeDirectory("/a"); + fse.makeDirectory("/a/b"); + fse.makeDirectory("/a/b/k"); + fse.makeDirectory("/a/c"); + fse.makeDirectory("/a/c/k"); - path.set("/"); - path.set("/dir"); - path.set("/dir/"); - path.set("dir/"); + fse.makeDirectory("/d"); + fse.makeDirectory("/d/b"); + fse.makeDirectory("/d/b/k"); + fse.makeDirectory("/d/c"); + fse.makeDirectory("/d/c/k"); - path.set(""); - path.set("asd"); - path.set("asdASD"); - path.set("asdASD123"); - path.set("asdASD123"); + fse.makeDirectory("d"); - fse.makeDirectory("/dir"); fse.log(); return 0;