remove tree cache (fallback to O(n) move operation). Use std::map
This commit is contained in:
parent
b093ba7ad4
commit
7655a7f5f0
7 changed files with 122 additions and 693 deletions
|
|
@ -1,42 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include "Tree.hpp"
|
||||
typedef unsigned long long ui64;
|
||||
typedef unsigned long ui32;
|
||||
typedef long long i64;
|
||||
typedef long i32;
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class Link;
|
||||
|
||||
extern std::string gError;
|
||||
typedef std::string Key;
|
||||
|
||||
struct DirectoryKey {
|
||||
DirectoryKey() = default;
|
||||
explicit DirectoryKey(Key val) : val(std::move(val)) {}
|
||||
|
||||
[[nodiscard]] inline bool descentRight(const DirectoryKey& in) const { return in.val > val; }
|
||||
[[nodiscard]] inline bool descentLeft(const DirectoryKey& in) const { return in.val < val; }
|
||||
[[nodiscard]] inline bool exactNode(const DirectoryKey& in) const { return in.val == val; }
|
||||
|
||||
[[nodiscard]] inline const DirectoryKey& getFindKey() const { return *this; }
|
||||
static inline const DirectoryKey& keyInRightSubtree(const DirectoryKey& in) { return in; }
|
||||
static inline const DirectoryKey& keyInLeftSubtree(const DirectoryKey& in) { return in; }
|
||||
|
||||
template <typename NodeType>
|
||||
inline void updateTreeCacheCallBack(NodeType& treeNode);
|
||||
|
||||
public:
|
||||
Key val;
|
||||
|
||||
ui32 incomingLinksHard = 0;
|
||||
ui32 incomingLinksDynamic = 0;
|
||||
};
|
||||
|
||||
typedef AvlTree<DirectoryKey, class Node*> DirectoryTree;
|
||||
|
||||
class Node {
|
||||
public:
|
||||
enum Type : ui32 { NONE, DIRECTORY, FILE, LINK } ;
|
||||
|
||||
public:
|
||||
Node() = default;
|
||||
Node(const Node& node);
|
||||
|
|
@ -45,14 +24,17 @@ public:
|
|||
[[nodiscard]] virtual Node* clone() const;
|
||||
|
||||
public:
|
||||
Type mType = NONE;
|
||||
Node* mParent = nullptr;
|
||||
DirectoryTree::Node* mTreeNode = nullptr;
|
||||
enum Type : ui32 { NONE, DIRECTORY, FILE, LINK } ;
|
||||
Type mType = NONE; // TODO : remove
|
||||
|
||||
std::vector<class Link*> mIncomingHardLinks;
|
||||
std::vector<class Link*> mIncomingDynamicLinks;
|
||||
class Directory* mParent = nullptr;
|
||||
|
||||
std::vector<Link*> mIncomingHardLinks;
|
||||
std::vector<Link*> mIncomingDynamicLinks;
|
||||
};
|
||||
|
||||
typedef std::map<Key, Node*> DirectoryTree;
|
||||
|
||||
class File : public Node {
|
||||
public:
|
||||
File();
|
||||
|
|
@ -99,46 +81,13 @@ public:
|
|||
|
||||
[[nodiscard]] ui32 getMaxDepth() const;
|
||||
|
||||
template<typename tFunctor>
|
||||
void traverseInorder(tFunctor functor) const {
|
||||
mMembers.traverseInorder(mMembers.getRoot(), functor);
|
||||
}
|
||||
|
||||
void getNodePath(Node* node, std::vector<const Key*>& path) const;
|
||||
void getNodeStraightPath(Node* node, std::vector<const Node*>& path) const;
|
||||
[[nodiscard]] ui64 size() const;
|
||||
|
||||
static void updateTreeLinkCount(Node* node);
|
||||
|
||||
private:
|
||||
void getMaxDepthUtil(ui32 depth, ui32& maxDepth) const;
|
||||
void dumpUtil(std::stringstream& ss, ui32 currentDepth, std::vector<bool>& indents);
|
||||
|
||||
public:
|
||||
DirectoryTree mMembers;
|
||||
};
|
||||
|
||||
|
||||
template <typename NodeType>
|
||||
inline void DirectoryKey::updateTreeCacheCallBack(NodeType& treeNode) {
|
||||
treeNode.data->mTreeNode = &treeNode;
|
||||
|
||||
incomingLinksHard = 0;
|
||||
if (treeNode.mLeft) incomingLinksHard += treeNode.mLeft->key.incomingLinksHard;
|
||||
if (treeNode.mRight) incomingLinksHard += treeNode.mRight->key.incomingLinksHard;
|
||||
|
||||
incomingLinksDynamic = 0;
|
||||
if (treeNode.mLeft) incomingLinksDynamic += treeNode.mLeft->key.incomingLinksDynamic;
|
||||
if (treeNode.mRight) incomingLinksDynamic += treeNode.mRight->key.incomingLinksDynamic;
|
||||
|
||||
incomingLinksHard += treeNode.data->mIncomingHardLinks.size();
|
||||
incomingLinksDynamic += treeNode.data->mIncomingDynamicLinks.size();
|
||||
|
||||
if (treeNode.data->mType == Node::DIRECTORY) {
|
||||
auto directory = (Directory*)treeNode.data;
|
||||
if (directory->mMembers.size()) {
|
||||
const auto& rootKey = directory->mMembers.getRoot()->key;
|
||||
incomingLinksDynamic += rootKey.incomingLinksDynamic;
|
||||
incomingLinksHard += rootKey.incomingLinksHard;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -5,10 +5,21 @@
|
|||
|
||||
#include <sstream>
|
||||
|
||||
// FIX MEMORY VIOLATIONS WITH LINKS
|
||||
|
||||
|
||||
// DONT STORE CACHE
|
||||
// DO O(N) deletion and moving
|
||||
// in-tree node links will report false to those operations and hard nodes deletion
|
||||
// use in node 'is_delete' flag and travers all nodes with link checks
|
||||
// remove mParent mTreeNode links
|
||||
|
||||
// Key (is copied on each tree access) !!!
|
||||
|
||||
// Functionality:
|
||||
// - print links and link counts
|
||||
// - update link counts
|
||||
// - add link creation commands
|
||||
// deleting directory - mark all nodes as deleted
|
||||
// traverse and check for links
|
||||
// unlink if those links are outgoing
|
||||
|
||||
// Refactor
|
||||
// - remove code duplication
|
||||
|
|
@ -17,6 +28,7 @@
|
|||
// - introduce smart pointers
|
||||
// - reconsider switch statements
|
||||
|
||||
|
||||
class FileSystem {
|
||||
public:
|
||||
FileSystem();
|
||||
|
|
|
|||
391
inc/Tree.hpp
391
inc/Tree.hpp
|
|
@ -1,391 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
|
||||
typedef unsigned long long ui64;
|
||||
typedef unsigned long ui32;
|
||||
typedef long long i64;
|
||||
typedef long i32;
|
||||
|
||||
template <typename T>
|
||||
struct SelectValueOrReference {
|
||||
using type = typename std::conditional<std::is_scalar<T>::value, T, const T&>::type;
|
||||
};
|
||||
|
||||
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 const AvlNumericKey& getFindKey(/**/) const { return *this; }
|
||||
inline const AvlNumericKey& keyInRightSubtree(const AvlNumericKey& in) const { return in; }
|
||||
inline const AvlNumericKey& keyInLeftSubtree(const AvlNumericKey& in) const { return in; }
|
||||
|
||||
template <typename NodeType>
|
||||
inline void updateTreeCacheCallBack(const NodeType&) {}
|
||||
};
|
||||
|
||||
template <typename Key, typename Data>
|
||||
class AvlTree {
|
||||
typedef SelectValueOrReference<Key>::type KeyArg;
|
||||
typedef SelectValueOrReference<Data>::type 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;
|
||||
ui64 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(/*node*/); }
|
||||
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() = default;
|
||||
~AvlTree() { removeAll(); }
|
||||
|
||||
[[nodiscard]] ui64 size() const { return mSize; }
|
||||
|
||||
Node* getRoot() 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 (std::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(getRoot()) == nullptr; }
|
||||
|
||||
template <typename tFunctor>
|
||||
void traverseInorder(Node* node, tFunctor functor) {
|
||||
if (!node) return;
|
||||
traverseInorder(node->mLeft, functor);
|
||||
functor(node);
|
||||
traverseInorder(node->mRight, functor);
|
||||
}
|
||||
|
||||
template <typename tFunctor>
|
||||
void traverseInorder(const Node* node, tFunctor functor) const {
|
||||
if (!node) return;
|
||||
traverseInorder(node->mLeft, functor);
|
||||
functor(node);
|
||||
traverseInorder(node->mRight, functor);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private:
|
||||
inline void deleteNode(Node* node) {
|
||||
delete node;
|
||||
}
|
||||
|
||||
inline Node* newNode(KeyArg key, DataArg data) {
|
||||
auto out = new Node(key, data);
|
||||
return out;
|
||||
}
|
||||
|
||||
inline void injectNodeInstead(Node* target, Node* from) {
|
||||
std::swap(target->data, from->data);
|
||||
target->key = from->key;
|
||||
target->updateTreeCacheCallBack();
|
||||
from->updateTreeCacheCallBack();
|
||||
}
|
||||
|
||||
inline i64 getNodeHeight(const Node* node) const { return node ? node->mHeight : -1; }
|
||||
|
||||
// returns new head
|
||||
Node* rotateLeft(Node* pivot) {
|
||||
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 + std::max(getNodeHeight(head->mLeft), getNodeHeight(head->mRight));
|
||||
right->mHeight = 1 + std::max(getNodeHeight(right->mLeft), getNodeHeight(right->mRight));
|
||||
|
||||
// cache
|
||||
head->updateTreeCacheCallBack();
|
||||
right->updateTreeCacheCallBack();
|
||||
|
||||
return right;
|
||||
}
|
||||
|
||||
Node* rotateRight(Node* pivot) {
|
||||
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 + std::max(getNodeHeight(head->mLeft), getNodeHeight(head->mRight));
|
||||
left->mHeight = 1 + std::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 + std::max(getNodeHeight(head->mRight), getNodeHeight(head->mLeft));
|
||||
|
||||
i64 balance = i64(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 + std::max(getNodeHeight(head->mRight), getNodeHeight(head->mLeft));
|
||||
i64 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;
|
||||
ui64 mSize = 0;
|
||||
};
|
||||
|
|
@ -2,11 +2,19 @@
|
|||
#include "DirectoryTree.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
|
||||
std::string gError;
|
||||
bool gDebug = true;
|
||||
|
||||
Node::~Node() = default;
|
||||
Node::~Node() {
|
||||
// assert(mIncomingHardLinks.empty());
|
||||
// for (auto dynamicLink : mIncomingDynamicLinks) {
|
||||
// dynamicLink->mParent->detachNode(dynamicLink->mTreeNode->key.val);
|
||||
// delete dynamicLink;
|
||||
//
|
||||
}
|
||||
|
||||
Node::Node(const Node &node) {
|
||||
mType = node.mType;
|
||||
|
|
@ -65,23 +73,17 @@ Link *Link::clone() const {
|
|||
}
|
||||
|
||||
Link::~Link() {
|
||||
assert(mLink);
|
||||
auto& links = mIsHard ? mLink->mIncomingHardLinks : mLink->mIncomingDynamicLinks;
|
||||
links.erase(std::remove(links.begin(), links.end(), this), links.end());
|
||||
Directory::updateTreeLinkCount(mLink);
|
||||
mLink = nullptr;
|
||||
// assert(mLink);
|
||||
// auto& links = mIsHard ? mLink->mIncomingHardLinks : mLink->mIncomingDynamicLinks;
|
||||
// links.erase(std::remove(links.begin(), links.end(), this), links.end());
|
||||
// Directory::updateTreeLinkCount(mLink);
|
||||
// mLink = nullptr;
|
||||
}
|
||||
|
||||
Directory::Directory() {
|
||||
mType = DIRECTORY;
|
||||
}
|
||||
|
||||
Directory::~Directory() {
|
||||
mMembers.traverseInorder(mMembers.getRoot(), [](DirectoryTree::Node* node){
|
||||
delete node->data;
|
||||
});
|
||||
}
|
||||
|
||||
bool Directory::attachNode(const std::vector<Key>& directoryPath, const Key& newKey, Node* newNode) {
|
||||
Node* node = findNode(directoryPath, 0);
|
||||
|
||||
|
|
@ -96,17 +98,16 @@ bool Directory::attachNode(const std::vector<Key>& directoryPath, const Key& new
|
|||
}
|
||||
|
||||
bool Directory::attachNode(const Key &newKey, Node *newNode) {
|
||||
DirectoryTree::Node* iterNode = mMembers.find(DirectoryKey(newKey));
|
||||
if (iterNode) {
|
||||
if (iterNode->data->mType == newNode->mType) return false; // exit silently
|
||||
auto iterNode = mMembers.find(newKey);
|
||||
if (iterNode != mMembers.end()) {
|
||||
if (iterNode->second->mType == newNode->mType) return false; // exit silently
|
||||
gError = "Can not add node";
|
||||
return false;
|
||||
}
|
||||
|
||||
mMembers.insert(DirectoryKey(newKey), newNode);
|
||||
mMembers.insert({ newKey, newNode });
|
||||
|
||||
newNode->mParent = this;
|
||||
updateTreeLinkCount(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -121,27 +122,25 @@ bool Directory::detachNode(const std::vector<Key>& directoryPath, const Key& key
|
|||
}
|
||||
|
||||
bool Directory::detachNode(const Key& key) {
|
||||
DirectoryTree::Node* removeNode = mMembers.find(DirectoryKey(key));
|
||||
if (!removeNode) {
|
||||
auto removeNode = mMembers.find(key);
|
||||
if (removeNode == mMembers.end()) {
|
||||
gError = "Invalid path";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (removeNode->key.incomingLinksHard || removeNode->key.incomingLinksDynamic) {
|
||||
gError = "Cannot modify node with incoming hard links";
|
||||
return false;
|
||||
}
|
||||
//if (removeNode->key.incomingLinksHard || removeNode->key.incomingLinksDynamic) {
|
||||
// gError = "Cannot modify node with incoming hard links";
|
||||
// return false;
|
||||
//}
|
||||
|
||||
removeNode->mParent = nullptr;
|
||||
mMembers.remove(DirectoryKey(key));
|
||||
|
||||
updateTreeLinkCount(this);
|
||||
removeNode->second->mParent = nullptr;
|
||||
mMembers.erase(key);
|
||||
return true;
|
||||
}
|
||||
|
||||
Node* Directory::findNode(const Key& key) {
|
||||
DirectoryTree::Node* iterNode = mMembers.find(DirectoryKey(key));
|
||||
return iterNode ? iterNode->data : nullptr;
|
||||
auto iterNode = mMembers.find(key);
|
||||
return iterNode != mMembers.end() ? iterNode->second : nullptr;
|
||||
}
|
||||
|
||||
Node* Directory::findNode(const std::vector<Key>& path, ui32 currentDepth) {
|
||||
|
|
@ -150,13 +149,13 @@ Node* Directory::findNode(const std::vector<Key>& path, ui32 currentDepth) {
|
|||
}
|
||||
|
||||
const Key& key = path[currentDepth];
|
||||
DirectoryTree::Node* iterNode = mMembers.find(DirectoryKey(key));
|
||||
auto iterNode = mMembers.find(key);
|
||||
|
||||
if (!iterNode) {
|
||||
if (iterNode == mMembers.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Node* node = iterNode->data;
|
||||
Node* node = iterNode->second;
|
||||
|
||||
// link on link is not allowed
|
||||
while (true) {
|
||||
|
|
@ -179,26 +178,14 @@ Node* Directory::findNode(const std::vector<Key>& path, ui32 currentDepth) {
|
|||
}
|
||||
}
|
||||
|
||||
void Directory::updateTreeLinkCount(Node* node) {
|
||||
if (!node || !node->mTreeNode) return;
|
||||
|
||||
node->mTreeNode->key.updateTreeCacheCallBack(*node->mTreeNode);
|
||||
|
||||
if (node->mTreeNode->mParent) {
|
||||
updateTreeLinkCount(node->mTreeNode->mParent->data);
|
||||
} else {
|
||||
updateTreeLinkCount(node->mParent);
|
||||
}
|
||||
}
|
||||
|
||||
void Directory::getMaxDepthUtil(ui32 depth, ui32& maxDepth) const {
|
||||
if (!mMembers.getRoot()) return;
|
||||
if (mMembers.empty()) return;
|
||||
maxDepth = std::max(depth, maxDepth);
|
||||
mMembers.traverseInorder(mMembers.getRoot(), [&](const DirectoryTree::Node* node){
|
||||
if (node->data->mType == DIRECTORY) {
|
||||
((Directory*)node->data)->getMaxDepthUtil(++depth, maxDepth);
|
||||
for (auto& node : mMembers) {
|
||||
if (node.second->mType == DIRECTORY) {
|
||||
((Directory*)node.second)->getMaxDepthUtil(++depth, maxDepth);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ui32 Directory::getMaxDepth() const {
|
||||
|
|
@ -223,51 +210,57 @@ static void indent(std::stringstream & ss, ui32 depth, std::vector<bool>& indent
|
|||
}
|
||||
|
||||
void Directory::dumpUtil(std::stringstream& ss, ui32 currentDepth, std::vector<bool>& indents) {
|
||||
if (mMembers.empty())
|
||||
return;
|
||||
|
||||
indents[currentDepth] = true;
|
||||
currentDepth++;
|
||||
|
||||
auto lastNode = mMembers.maxNode(mMembers.getRoot());
|
||||
traverseInorder([&](const DirectoryTree::Node* node){
|
||||
if (lastNode == node) indents[currentDepth - 1] = false;
|
||||
const auto& lastNode = mMembers.rbegin()->first;
|
||||
|
||||
for (auto & member : mMembers) {
|
||||
if (lastNode == member.first)
|
||||
indents[currentDepth - 1] = false;
|
||||
|
||||
indent(ss, currentDepth, indents);
|
||||
|
||||
switch (node->data->mType) {
|
||||
switch (member.second->mType) {
|
||||
case Node::DIRECTORY:
|
||||
ss << node->key.val;
|
||||
if (gDebug) ss << " [" << node->key.incomingLinksHard << ":" << node->key.incomingLinksDynamic << "]";
|
||||
ss << member.first;
|
||||
if (gDebug) ss << " [" << member.second->mIncomingHardLinks.size() << ":" << member.second->mIncomingDynamicLinks.size() << "]";
|
||||
ss << "\n";
|
||||
((Directory*) node->data)->dumpUtil(ss, currentDepth, indents);
|
||||
return;
|
||||
((Directory*) member.second)->dumpUtil(ss, currentDepth, indents);
|
||||
break;
|
||||
|
||||
case Node::LINK: {
|
||||
auto linkNode = ((Link*)node->data);
|
||||
ss << node->key.val << (linkNode->isHard() ? " hlink[/" : " dlink[/");
|
||||
std::vector<const Key*> path;
|
||||
getNodePath(linkNode->getLink(), path);
|
||||
auto linkNode = ((Link*)member.second);
|
||||
ss << member.first << (linkNode->isHard() ? " hlink[/" : " dlink[/");
|
||||
std::vector<const Node*> path;
|
||||
getNodeStraightPath(linkNode->getLink(), path);
|
||||
std::reverse(path.begin(), path.end());
|
||||
for (auto key : path) ss << *key << "/";
|
||||
for (auto key : path)
|
||||
ss << "X" << "/";
|
||||
ss << "]\n";
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
case Node::FILE:
|
||||
ss << node->key.val;
|
||||
ss << member.first;
|
||||
if (gDebug) ss << " [file]";
|
||||
ss << "\n";
|
||||
return;
|
||||
break;
|
||||
|
||||
default:
|
||||
ss << " ERROR \n";
|
||||
return;
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Directory::getNodePath(Node* node, std::vector<const Key*>& path) const {
|
||||
if (!node || !node->mTreeNode) return;
|
||||
path.push_back(&node->mTreeNode->key.val);
|
||||
getNodePath(node->mParent, path);
|
||||
void Directory::getNodeStraightPath(Node* node, std::vector<const Node*>& path) const {
|
||||
if (!node) return;
|
||||
path.push_back(node);
|
||||
getNodeStraightPath(node->mParent, path);
|
||||
}
|
||||
|
||||
ui64 Directory::size() const {
|
||||
|
|
@ -275,16 +268,17 @@ ui64 Directory::size() const {
|
|||
}
|
||||
|
||||
Directory::Directory(const Directory &node) : Node(node) {
|
||||
node.traverseInorder([&](const DirectoryTree::Node* node){
|
||||
auto newNode = node->data->clone();
|
||||
mMembers.insert(node->key, newNode);
|
||||
for (auto & member : node.mMembers) {
|
||||
auto newNode = member.second->clone();
|
||||
mMembers.insert({ member.first, newNode });
|
||||
newNode->mParent = this;
|
||||
if (newNode->mType == LINK) {
|
||||
updateTreeLinkCount(((Link*)newNode)->getLink());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
updateTreeLinkCount(this);
|
||||
Directory::~Directory() {
|
||||
for (auto node : mMembers) {
|
||||
delete node.second;
|
||||
}
|
||||
}
|
||||
|
||||
Directory *Directory::clone() const {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
#include "FileSystem.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
|
||||
FileSystem::FileSystem() {
|
||||
root = new Directory();
|
||||
|
|
@ -122,7 +124,7 @@ bool FileSystem::copyNode(const Path& source, const Path& target) {
|
|||
|
||||
auto targetDirectory = (Directory*)targetNode;
|
||||
|
||||
Key key = sourceNode->mTreeNode->key.val;
|
||||
Key key = source.getFilename();
|
||||
|
||||
if (targetDirectory->findNode(key)) {
|
||||
// gError = "Node with such name already exists in the target directory";
|
||||
|
|
@ -163,7 +165,7 @@ bool FileSystem::moveNode(const Path &source, const Path &target) {
|
|||
auto sourceParentDirectory = (Directory*)sourceParentNode;
|
||||
auto targetDirectory = (Directory*)targetNode;
|
||||
|
||||
Key key = sourceNode->mTreeNode->key.val;
|
||||
Key key = source.getFilename();
|
||||
|
||||
if (targetDirectory->findNode(key)) {
|
||||
gError = "Node with such name already exists in the target directory";
|
||||
|
|
@ -206,7 +208,7 @@ bool FileSystem::makeLink(const Path& source, const Path& target, bool isDynamic
|
|||
return false;
|
||||
}
|
||||
|
||||
const Key& key = sourceNode->mTreeNode->key.val;
|
||||
const Key& key = source.getFilename();
|
||||
auto targetDirectory = (Directory*)targetNode;
|
||||
|
||||
if (targetDirectory->findNode(key)) {
|
||||
|
|
@ -218,8 +220,6 @@ bool FileSystem::makeLink(const Path& source, const Path& target, bool isDynamic
|
|||
|
||||
assert(targetDirectory->attachNode(key, newLink));
|
||||
|
||||
Directory::updateTreeLinkCount(sourceNode);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -267,13 +267,13 @@ bool FileSystem::changeCurrent(const Path& path) {
|
|||
void FileSystem::log() const {
|
||||
std::stringstream ss;
|
||||
|
||||
std::vector<const Key*> currentPath;
|
||||
root->getNodePath(currentDirectory, currentPath);
|
||||
std::vector<const Node*> currentPath;
|
||||
root->getNodeStraightPath(currentDirectory, currentPath);
|
||||
std::reverse(currentPath.begin(), currentPath.end());
|
||||
|
||||
ss << "cd - /";
|
||||
for (auto key : currentPath) {
|
||||
ss << *key << "/";
|
||||
ss << "X" << "/";
|
||||
}
|
||||
|
||||
ss << "\n";
|
||||
|
|
@ -286,11 +286,11 @@ const std::string& FileSystem::getLastError() {
|
|||
}
|
||||
|
||||
bool FileSystem::isPathContainsCurrent(Node* node) {
|
||||
std::vector<const Key*> currentPath;
|
||||
std::vector<const Key*> path;
|
||||
std::vector<const Node*> currentPath;
|
||||
std::vector<const Node*> path;
|
||||
|
||||
root->getNodePath(currentDirectory, currentPath);
|
||||
root->getNodePath(node, path);
|
||||
root->getNodeStraightPath(currentDirectory, currentPath);
|
||||
root->getNodeStraightPath(node, path);
|
||||
|
||||
std::reverse(currentPath.begin(), currentPath.end());
|
||||
std::reverse(path.begin(), path.end());
|
||||
|
|
|
|||
|
|
@ -108,6 +108,8 @@ void Interpreter::interpret(const std::string& command) {
|
|||
std::vector<std::string> words;
|
||||
getWords(command, words);
|
||||
|
||||
std::cout << command << "\n";
|
||||
|
||||
if (words.empty()) {
|
||||
reportError("Empty command");
|
||||
return;
|
||||
|
|
@ -131,10 +133,10 @@ void Interpreter::interpret(const std::string& command) {
|
|||
|
||||
bool success = iter->second.callback(mFileSystem, words);
|
||||
|
||||
mFileSystem.log();
|
||||
|
||||
if (!success) {
|
||||
reportError(FileSystem::getLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
mFileSystem.log();
|
||||
}
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
#include "Tree.hpp"
|
||||
|
||||
#include "UnitTest++/UnitTest++.h"
|
||||
|
||||
static double randomFloat() {
|
||||
return static_cast<double>(std::rand()) / static_cast<double>(RAND_MAX);
|
||||
}
|
||||
|
||||
const auto size = 1000;
|
||||
|
||||
class TestClass {
|
||||
ui64 val1 = 0;
|
||||
|
||||
public:
|
||||
TestClass() = default;
|
||||
explicit TestClass(ui64 val) : val1(val) {}
|
||||
[[nodiscard]] bool operator==(const TestClass& in) const { return in.val1 == val1; }
|
||||
[[nodiscard]] ui64 getVal() const { return val1; }
|
||||
void setVal(ui64 val) { val1 = val; }
|
||||
};
|
||||
|
||||
SUITE(AvlTree) {
|
||||
TEST(Simple) {
|
||||
AvlTree<AvlNumericKey<i64>, TestClass> tree;
|
||||
|
||||
CHECK(tree.size() == 0);
|
||||
CHECK(tree.getRoot() == nullptr);
|
||||
|
||||
tree.insert(6, TestClass(6));
|
||||
CHECK(tree.isValid());
|
||||
CHECK(tree.size() == 1);
|
||||
CHECK(tree.getRoot()->data == TestClass(6));
|
||||
|
||||
tree.remove(6);
|
||||
CHECK(tree.isValid());
|
||||
CHECK(tree.size() == 0);
|
||||
CHECK(tree.getRoot() == nullptr);
|
||||
}
|
||||
|
||||
TEST(Persistance) {
|
||||
AvlTree<AvlNumericKey<i64>, TestClass> tree;
|
||||
|
||||
struct Item {
|
||||
Item() :
|
||||
data(0) {}
|
||||
bool presents = false;
|
||||
TestClass data;
|
||||
};
|
||||
|
||||
Item buff[size];
|
||||
|
||||
for (auto i = 0; i < size; i++) {
|
||||
buff[i].data.setVal(i);
|
||||
}
|
||||
|
||||
// random load
|
||||
ui64 loadSize = 0;
|
||||
while (loadSize < size / 2) {
|
||||
auto idx = ui64(randomFloat() * (size - 1));
|
||||
assert(idx < size);
|
||||
if (!buff[idx].presents) {
|
||||
tree.insert((i64) buff[idx].data.getVal(), buff[idx].data);
|
||||
loadSize++;
|
||||
buff[idx].presents = true;
|
||||
|
||||
CHECK(tree.isValid());
|
||||
CHECK(tree.size() == loadSize);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& item : buff) {
|
||||
if (item.presents) continue;
|
||||
tree.insert((i64) item.data.getVal(), item.data);
|
||||
loadSize++;
|
||||
item.presents = true;
|
||||
|
||||
CHECK(tree.isValid());
|
||||
CHECK(tree.size() == loadSize);
|
||||
}
|
||||
|
||||
CHECK(tree.size() == size);
|
||||
CHECK(tree.maxNode(tree.getRoot())->data.getVal() == size - 1);
|
||||
CHECK(tree.minNode(tree.getRoot())->data.getVal() == 0);
|
||||
|
||||
// find
|
||||
for (auto item : buff) {
|
||||
auto node = tree.find((i64) item.data.getVal());
|
||||
CHECK(node);
|
||||
if (!node) continue;
|
||||
CHECK(node->data.getVal() == item.data.getVal());
|
||||
}
|
||||
|
||||
CHECK(!tree.find(size + 1));
|
||||
CHECK(!tree.find(-1));
|
||||
|
||||
// random unload
|
||||
ui64 unloadSize = 0;
|
||||
while (unloadSize < size / 2) {
|
||||
auto idx = ui64(randomFloat() * (size - 1));
|
||||
if (buff[idx].presents) {
|
||||
|
||||
tree.remove((i64) buff[idx].data.getVal());
|
||||
|
||||
unloadSize++;
|
||||
buff[idx].presents = false;
|
||||
|
||||
// find
|
||||
for (auto item : buff) {
|
||||
if (!item.presents) continue;
|
||||
auto node = tree.find((i64) item.data.getVal());
|
||||
CHECK(node);
|
||||
if (!node) continue;
|
||||
CHECK(node->data.getVal() == item.data.getVal());
|
||||
}
|
||||
|
||||
CHECK(tree.isValid());
|
||||
CHECK(tree.size() == size - unloadSize);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& item : buff) {
|
||||
if (item.presents) {
|
||||
tree.remove((i64) item.data.getVal());
|
||||
unloadSize++;
|
||||
item.presents = false;
|
||||
|
||||
CHECK(tree.isValid());
|
||||
CHECK(tree.size() == size - unloadSize);
|
||||
}
|
||||
}
|
||||
|
||||
CHECK(tree.size() == 0);
|
||||
CHECK(tree.getRoot() == nullptr);
|
||||
CHECK(tree.maxNode(tree.getRoot()) == nullptr);
|
||||
CHECK(tree.minNode(tree.getRoot()) == nullptr);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue