tmp
This commit is contained in:
parent
6d1c37db0a
commit
d7cc11143b
4 changed files with 767 additions and 137 deletions
|
|
@ -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 <typename NumericType>
|
||||
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 <typename NodeType>
|
||||
inline void updateTreeCacheCallBack(const NodeType&) {}
|
||||
};
|
||||
|
||||
template <typename Key, typename Data, class Allocator = DefaultAllocator>
|
||||
class AvlTree {
|
||||
typedef SelectValueOrReference<Key> KeyArg;
|
||||
typedef SelectValueOrReference<Data> 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 <typename tFunctor>
|
||||
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 <class tArchiver>
|
||||
void archiveWrite(tArchiver& file) const {
|
||||
FAIL("not implemented")
|
||||
}
|
||||
|
||||
template <class tArchiver>
|
||||
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;
|
||||
};
|
||||
}
|
||||
*/
|
||||
|
|
@ -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 <iostream>
|
||||
|
|
@ -32,121 +7,103 @@
|
|||
#include <iomanip>
|
||||
#include <map>
|
||||
#include <filesystem>
|
||||
#include <cmath>
|
||||
|
||||
typedef std::string Key;
|
||||
typedef std::map<Key, struct Node*> 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<Key>& 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<Key>& 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<typename tFunctor>
|
||||
void traverseInorder(tFunctor functor) const {
|
||||
traverseInorderUtil(members, functor);
|
||||
}
|
||||
|
||||
template<typename tFunctor>
|
||||
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<bool> 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<bool>& 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<bool>& 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<bool>& 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<bool>& indents) const {
|
||||
indent(ss, depth, indents);
|
||||
ss << node->key << "\n";
|
||||
}
|
||||
|
||||
void logLink(std::stringstream & ss, const Link* node, int depth, std::vector<bool>& indents) const {
|
||||
indent(ss, depth, indents);
|
||||
ss << "link [" << node->key << "] \n";
|
||||
}
|
||||
};
|
||||
74
Path.hpp
74
Path.hpp
|
|
@ -3,6 +3,9 @@
|
|||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
|
||||
std::vector<char> 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<std::string>& getChain() const {
|
||||
return mChain;
|
||||
}
|
||||
|
||||
std::vector<std::string> getParentChain() const {
|
||||
std::vector<std::string> 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<std::string> mChain;
|
||||
bool mAbsolute = false;
|
||||
bool mIsValid = false;
|
||||
bool mDirectory = false;
|
||||
};
|
||||
22
Tests.cpp
22
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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue