remove tree cache (fallback to O(n) move operation). Use std::map

This commit is contained in:
IlyaShurupov 2024-03-29 09:23:34 +03:00
parent b093ba7ad4
commit 7655a7f5f0
7 changed files with 122 additions and 693 deletions

View file

@ -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;
}
}
}
};

View file

@ -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();

View file

@ -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;
};

View file

@ -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 {

View file

@ -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());

View file

@ -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();
}

View file

@ -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);
}
}