This commit is contained in:
IlyaShurupov 2024-03-27 15:31:39 +03:00
parent d7cc11143b
commit 2053bf474b
10 changed files with 595 additions and 525 deletions

View file

@ -4,9 +4,13 @@ project(FileSystemEmulator)
set(CMAKE_CXX_STANDARD 20)
add_library(${PROJECT_NAME} STATIC FileSystemEmulator.cpp FileSystemEmulator.hpp)
target_include_directories(${PROJECT_NAME} PUBLIC .)
file(GLOB SOURCES "./src/*.cpp")
file(GLOB HEADERS "./inc/*.hpp")
add_executable(Test${PROJECT_NAME} Tests.cpp)
add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
target_include_directories(${PROJECT_NAME} PUBLIC ./inc/)
file(GLOB TEST_SOURCES "./test/*.cpp")
add_executable(Test${PROJECT_NAME} ${TEST_SOURCES})
target_link_libraries(Test${PROJECT_NAME} ${PROJECT_NAME})
add_test(NAME Test${PROJECT_NAME} COMMAND Test${PROJECT_NAME})

View file

@ -1,385 +0,0 @@
#pragma once
#include "Path.hpp"
#include <iostream>
#include <sstream>
#include <iomanip>
#include <map>
#include <filesystem>
#include <cmath>
typedef std::string Key;
typedef unsigned long long ui64;
typedef unsigned long long ui32;
struct Node {
enum Type : ui32 { NONE, DIRECTORY, FILE, LINK };
Key key;
Node* parent = nullptr;
Node* left = nullptr;
Node* right = nullptr;
ui32 height = 0;
ui32 incomingLinksHard = 0;
ui32 incomingLinksDynamic = 0;
Type type = NONE;
void updateTreeCache() {
// TODO update cache
}
};
struct File : public Node {
File() {
type = Type::FILE;
}
};
struct Link : public Node {
Link() {
type = LINK;
}
Node* link = nullptr;
bool hard = false;
};
struct Directory : public Node {
Node* members = nullptr;
ui32 size = 0;
Directory() {
type = DIRECTORY;
}
bool attachNode(const std::vector<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) {
// TODO : update error status - given path is not a directory
return false;
}
auto directory = ((Directory*) node);
Node* existingNode = directory->treeSearch(newKey);
if (existingNode) {
if (existingNode->type == newNode->type) return false; // exit silently
// TODO : report error cant add node
return false;
}
directory->treeInsert(newKey, newNode);
updateTreeLinkCount(newNode);
return true;
}
Node* findNode(const std::vector<Key>& path, ui32 currentDepth) {
if (path.size() == currentDepth) {
return this;
}
const Key& key = path[currentDepth];
Node* node = treeSearch(key);
if (!node) {
return nullptr;
}
// link on link is not allowed
while (true) {
switch (node->type) {
case Node::FILE:
if (currentDepth == path.size() - 1) return node;
return nullptr;
case Node::DIRECTORY:
return ((Directory*)node)->findNode(path, ++currentDepth);
case Node::LINK:
node = ((Link*)node)->link;
break;
default:
return nullptr;
}
}
}
Node* treeSearch(const Key& key) {
if (!members) return nullptr;
Node* iterator = members;
while (iterator) {
if (key > iterator->key) {
iterator = iterator->right;
} else if (key < iterator->key) {
iterator = iterator->left;
} else {
return iterator;
}
}
return nullptr;
}
void updateTreeLinkCount(Node* node) {
// TODO : update all caches all the way up to '/'
}
void detachNode(Node* node) {
// TODO : remove util from avl tree
// TODO : update all cache all the way up to the root (due to the links)
// TODO : dont relocate nodes (due existing links to the nodes), only change tree pointers
}
// TODO : user avl tree insertion
// TODO : check for existing file
// TODO : return true if successful (node inserted)
// TODO : dont relocate nodes (due existing links to the nodes), only change tree pointers
void treeInsert(const Key& newKey, Node* newNode) {
newNode->key = newKey;
members = insertUtil(members, newKey, newNode);
}
// recursively returns valid isLeft or isRight child or root
Node* insertUtil(Node* head, const Key& key, Node* aNode) {
Node* insertedNode;
if (head == nullptr) {
size++;
aNode->updateTreeCache();
return aNode;
} else if (head->key == key) {
return head;
} else if (key > head->key) {
insertedNode = insertUtil(head->right, key, aNode);
head->right = insertedNode;
insertedNode->parent = head;
} else {
insertedNode = insertUtil(head->left, key, aNode);
head->left = insertedNode;
insertedNode->parent = head;
}
// update height
head->height = 1 + std::max(getNodeHeight(head->right), getNodeHeight(head->left));
int balance = int(getNodeHeight(head->right) - getNodeHeight(head->left));
if (balance > 1) {
if (key > head->right->key) {
return rotateLeft(head);
} else {
head->right = rotateRight(head->right);
return rotateLeft(head);
}
} else if (balance < -1) {
if (key < head->left->key) {
return rotateRight(head);
} else {
head->left = rotateLeft(head->left);
return rotateRight(head);
}
}
head->updateTreeCache();
return head;
}
// returns new head
Node* rotateLeft(Node* pivot) {
Node* const head = pivot;
Node* const right = pivot->right;
Node* const right_left = right->left;
Node* const parent = pivot->parent;
// parents
if (right_left) right_left->parent = head;
head->parent = right;
right->parent = parent;
// children
head->right = right_left;
right->left = head;
// heights
head->height = 1 + std::max(getNodeHeight(head->left), getNodeHeight(head->right));
right->height = 1 + std::max(getNodeHeight(right->left), getNodeHeight(right->right));
// cache
head->updateTreeCache();
right->updateTreeCache();
return right;
}
Node* rotateRight(Node* pivot) {
Node* const head = pivot;
Node* const left = pivot->left;
Node* const left_right = left->right;
Node* const parent = pivot->parent;
// parents
if (left_right) left_right->parent = head;
head->parent = left;
left->parent = parent;
// children
head->left = left_right;
left->right = head;
// heights
head->height = 1 + std::max(getNodeHeight(head->left), getNodeHeight(head->right));
left->height = 1 + std::max(getNodeHeight(left->left), getNodeHeight(left->right));
// cache
head->updateTreeCache();
left->updateTreeCache();
return left;
}
static inline ui32 getNodeHeight(const Node* node) { return node ? node->height : -1; }
template<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 {
Directory* root = nullptr;
Directory* currentDirectory = nullptr;
public:
FileSystem() {
root = new Directory();
currentDirectory = root;
root->key = "/";
initializeTransitions();
}
~FileSystem() {
delete root;
}
void makeDirectory(const Path& path) {
if (path.getDepth() < 1 || path.isInvalid()) {
// TODO : update error - invalid path
return;
}
Directory* parentDirectory = path.isAbsolute() ? root : currentDirectory;
auto newDirectory = new Directory();
if (!parentDirectory->attachNode(path.getParentChain(), path.getFilename(), newDirectory)) {
delete newDirectory;
}
}
void changeCurrent(const Path& path) {
if (path.isInvalid()) {
// TODO : update error - invalid path
return;
}
auto node = path.isAbsolute() ? root->findNode(path.getChain(), 0) : currentDirectory->findNode(path.getChain(), 0);
if (node->type != Node::DIRECTORY) {
// TODO : update error status - no such directory
return;
}
currentDirectory = (Directory*) node;
}
void log() const {
std::stringstream ss;
std::vector<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";
}
};

111
Path.hpp
View file

@ -1,111 +0,0 @@
#pragma once
#include <algorithm>
#include <vector>
#include <string>
#include <sstream>
#include <iostream>
#include <cassert>
std::vector<char> transitions;
void initializeTransitions() {
transitions.resize(256);
std::fill(transitions.begin(), transitions.end(), 1);
for (char i = 'a'; i <= 'z'; i++) transitions[i] = i;
for (char i = '0'; i <= '9'; i++) transitions[i] = i;
for (char i = 'A'; i <= 'Z'; i++) transitions[i] = i + ('a' - 'A');
transitions['/'] = '/';
transitions['.'] = '.';
}
class Path {
public:
Path() = default;
Path(const char* path) {
set(path);
}
bool isValid() const {
return mIsValid;
}
bool isInvalid() const {
return !mIsValid;
}
bool isAbsolute() const {
return mAbsolute;
}
int getDepth() const {
return mChain.size();
}
const std::vector<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) {
if (path.empty()) {
mIsValid = false;
return;
}
std::string lowercasePath = path;
mIsValid = true;
mAbsolute = path.front() == '/';
mDirectory = path.back() == '/';
mChain.clear();
for (auto & character : lowercasePath) {
character = transitions[character];
if (character == 1) mIsValid = false;
}
if (lowercasePath == "/") return;
const char* begin = &lowercasePath.front() + (lowercasePath.front() == '/');
const char* end = &lowercasePath.back() - (lowercasePath.back() == '/');
const char* prev = begin;
for (const char* iter = begin; iter <= end; iter++) {
if (*iter == '/') {
const auto string = lowercasePath.substr(prev - lowercasePath.c_str(), iter - prev);
mChain.push_back(string);
prev = iter + 1;
}
}
const auto string = lowercasePath.substr(prev - lowercasePath.c_str(), end - prev + 1);
mChain.push_back(string);
for (auto & key : mChain) {
if (key.empty()) mIsValid = false;
// std::cout << key << ' ';
}
// std::cout << "\n";
}
const std::string& operator[](int idx) const {
return mChain[idx];
};
private:
std::vector<std::string> mChain;
bool mAbsolute = false;
bool mIsValid = false;
bool mDirectory = false;
};

View file

@ -1,26 +0,0 @@
#include "FileSystemEmulator.hpp"
namespace fs = std::filesystem;
int main() {
FileSystem fse;
fse.makeDirectory("/a");
fse.makeDirectory("/a/b");
fse.makeDirectory("/a/b/k");
fse.makeDirectory("/a/c");
fse.makeDirectory("/a/c/k");
fse.makeDirectory("/d");
fse.makeDirectory("/d/b");
fse.makeDirectory("/d/b/k");
fse.makeDirectory("/d/c");
fse.makeDirectory("/d/c/k");
fse.makeDirectory("d");
fse.log();
return 0;
}

96
inc/FileSystem.hpp Normal file
View file

@ -0,0 +1,96 @@
#pragma once
#include "Path.hpp"
#include <sstream>
typedef std::string Key;
typedef unsigned long long ui64;
typedef unsigned long long ui32;
struct Node {
enum Type : ui32 { NONE, DIRECTORY, FILE, LINK };
Key key;
Node* parent = nullptr;
Node* left = nullptr;
Node* right = nullptr;
ui32 height = 0;
ui32 incomingLinksHard = 0;
ui32 incomingLinksDynamic = 0;
Type type = NONE;
void updateTreeCache();
};
struct File : public Node {
File();
};
struct Link : public Node {
Link();
Node* link = nullptr;
bool hard = false;
};
struct Directory : public Node {
Directory();
bool attachNode(const std::vector<Key>& directoryPath, const Key& newKey, Node* newNode);
Node* findNode(const std::vector<Key>& path, ui32 currentDepth);
void detachNode(Node* node);
template<typename tFunctor>
void traverseInorder(tFunctor functor) const {
traverseInorderUtil(members, functor);
}
[[nodiscard]] Node* maxNode() const;
[[nodiscard]] ui32 getMaxDepth() const;
private:
Node* treeSearch(const Key& key);
void updateTreeLinkCount(Node* node);
void treeInsert(const Key& newKey, Node* newNode);
Node* insertUtil(Node* head, const Key& key, Node* aNode);
Node* rotateLeft(Node* pivot);
Node* rotateRight(Node* pivot);
static inline ui32 getNodeHeight(const Node* node);
void getMaxDepthUtil(ui32 depth, ui32& maxDepth) const;
template<typename tFunctor>
void traverseInorderUtil(Node* node, tFunctor functor) const {
if (!node) return;
traverseInorderUtil(node->left, functor);
functor(node);
traverseInorderUtil(node->right, functor);
}
public:
Node* members = nullptr;
ui32 size = 0;
};
class FileSystem {
public:
FileSystem();
~FileSystem();
void makeDirectory(const Path& path);
void changeCurrent(const Path& path);
void log() const;
private:
void logNode(std::stringstream& ss, const Node* node, int depth, std::vector<bool>& indents) const;
void indent(std::stringstream & ss, int depth, std::vector<bool>& indents) const;
void logDirectory(std::stringstream & ss, const Directory* node, int depth, std::vector<bool>& indents) const;
void logFile(std::stringstream& ss, const File* node, int depth, std::vector<bool>& indents) const;
void logLink(std::stringstream & ss, const Link* node, int depth, std::vector<bool>& indents) const;
private:
Directory* root = nullptr;
Directory* currentDirectory = nullptr;
};

31
inc/Path.hpp Normal file
View file

@ -0,0 +1,31 @@
#pragma once
#include <vector>
#include <string>
extern std::vector<char> transitions;
void initializeTransitions();
class Path {
public:
Path() = default;
Path(const char* path);
void set(const std::string& path);
const std::string& operator[](int idx) const;
bool isValid() const;
bool isInvalid() const;
bool isAbsolute() const;
int getDepth() const;
const std::vector<std::string>& getChain() const;
std::vector<std::string> getParentChain() const;
const std::string& getFilename() const;
private:
std::vector<std::string> mChain;
bool mAbsolute = false;
bool mIsValid = false;
bool mDirectory = false;
};

327
src/FileSystem.cpp Normal file
View file

@ -0,0 +1,327 @@
#include "FileSystem.hpp"
#include <iostream>
void Node::updateTreeCache() {
// TODO update cache
}
File::File() {
type = Type::FILE;
}
Link::Link() {
type = LINK;
}
Directory::Directory() {
type = DIRECTORY;
}
bool Directory::attachNode(const std::vector<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) {
// TODO : update error status - given path is not a directory
return false;
}
auto directory = ((Directory*) node);
Node* existingNode = directory->treeSearch(newKey);
if (existingNode) {
if (existingNode->type == newNode->type) return false; // exit silently
// TODO : report error cant add node
return false;
}
directory->treeInsert(newKey, newNode);
updateTreeLinkCount(newNode);
return true;
}
Node* Directory::findNode(const std::vector<Key>& path, ui32 currentDepth) {
if (path.size() == currentDepth) {
return this;
}
const Key& key = path[currentDepth];
Node* node = treeSearch(key);
if (!node) {
return nullptr;
}
// link on link is not allowed
while (true) {
switch (node->type) {
case Node::FILE:
if (currentDepth == path.size() - 1) return node;
return nullptr;
case Node::DIRECTORY:
return ((Directory*)node)->findNode(path, ++currentDepth);
case Node::LINK:
node = ((Link*)node)->link;
break;
default:
return nullptr;
}
}
}
Node* Directory::treeSearch(const Key& key) {
if (!members) return nullptr;
Node* iterator = members;
while (iterator) {
if (key > iterator->key) {
iterator = iterator->right;
} else if (key < iterator->key) {
iterator = iterator->left;
} else {
return iterator;
}
}
return nullptr;
}
void Directory::updateTreeLinkCount(Node* node) {
// TODO : update all caches all the way up to '/'
}
void Directory::detachNode(Node* node) {
// TODO : remove util from avl tree
// TODO : update all cache all the way up to the root (due to the links)
// TODO : dont relocate nodes (due existing links to the nodes), only change tree pointers
}
// TODO : user avl tree insertion
// TODO : check for existing file
// TODO : return true if successful (node inserted)
// TODO : dont relocate nodes (due existing links to the nodes), only change tree pointers
void Directory::treeInsert(const Key& newKey, Node* newNode) {
newNode->key = newKey;
members = insertUtil(members, newKey, newNode);
}
// recursively returns valid isLeft or isRight child or root
Node* Directory::insertUtil(Node* head, const Key& key, Node* aNode) {
Node* insertedNode;
if (head == nullptr) {
size++;
aNode->updateTreeCache();
return aNode;
} else if (head->key == key) {
return head;
} else if (key > head->key) {
insertedNode = insertUtil(head->right, key, aNode);
head->right = insertedNode;
insertedNode->parent = head;
} else {
insertedNode = insertUtil(head->left, key, aNode);
head->left = insertedNode;
insertedNode->parent = head;
}
// update height
head->height = 1 + std::max(getNodeHeight(head->right), getNodeHeight(head->left));
int balance = int(getNodeHeight(head->right) - getNodeHeight(head->left));
if (balance > 1) {
if (key > head->right->key) {
return rotateLeft(head);
} else {
head->right = rotateRight(head->right);
return rotateLeft(head);
}
} else if (balance < -1) {
if (key < head->left->key) {
return rotateRight(head);
} else {
head->left = rotateLeft(head->left);
return rotateRight(head);
}
}
head->updateTreeCache();
return head;
}
// returns new head
Node* Directory::rotateLeft(Node* pivot) {
Node* const head = pivot;
Node* const right = pivot->right;
Node* const right_left = right->left;
Node* const parent = pivot->parent;
// parents
if (right_left) right_left->parent = head;
head->parent = right;
right->parent = parent;
// children
head->right = right_left;
right->left = head;
// heights
head->height = 1 + std::max(getNodeHeight(head->left), getNodeHeight(head->right));
right->height = 1 + std::max(getNodeHeight(right->left), getNodeHeight(right->right));
// cache
head->updateTreeCache();
right->updateTreeCache();
return right;
}
Node* Directory::rotateRight(Node* pivot) {
Node* const head = pivot;
Node* const left = pivot->left;
Node* const left_right = left->right;
Node* const parent = pivot->parent;
// parents
if (left_right) left_right->parent = head;
head->parent = left;
left->parent = parent;
// children
head->left = left_right;
left->right = head;
// heights
head->height = 1 + std::max(getNodeHeight(head->left), getNodeHeight(head->right));
left->height = 1 + std::max(getNodeHeight(left->left), getNodeHeight(left->right));
// cache
head->updateTreeCache();
left->updateTreeCache();
return left;
}
ui32 Directory::getNodeHeight(const Node* node) { return node ? node->height : -1; }
Node* Directory::maxNode() const {
Node* head = members;
if (!head) return nullptr;
while (head->right != nullptr) {
head = head->right;
}
return head;
}
void Directory::getMaxDepthUtil(ui32 depth, ui32& maxDepth) const {
if (!members) return;
maxDepth = std::max(depth, maxDepth);
traverseInorderUtil(members, [&](Node* node){
if (node->type == DIRECTORY) {
((Directory*)node)->getMaxDepthUtil(++depth, maxDepth);
}
});
}
ui32 Directory::getMaxDepth() const {
ui32 maxDepth = 0;
getMaxDepthUtil(0, maxDepth);
return maxDepth;
}
FileSystem::FileSystem() {
root = new Directory();
currentDirectory = root;
root->key = "/";
initializeTransitions();
}
FileSystem::~FileSystem() {
delete root;
}
void FileSystem::makeDirectory(const Path& path) {
if (path.getDepth() < 1 || path.isInvalid()) {
// TODO : update error - invalid path
return;
}
Directory* parentDirectory = path.isAbsolute() ? root : currentDirectory;
auto newDirectory = new Directory();
if (!parentDirectory->attachNode(path.getParentChain(), path.getFilename(), newDirectory)) {
delete newDirectory;
}
}
void FileSystem::changeCurrent(const Path& path) {
if (path.isInvalid()) {
// TODO : update error - invalid path
return;
}
auto node = path.isAbsolute() ? root->findNode(path.getChain(), 0) : currentDirectory->findNode(path.getChain(), 0);
if (node->type != Node::DIRECTORY) {
// TODO : update error status - no such directory
return;
}
currentDirectory = (Directory*) node;
}
void FileSystem::log() const {
std::stringstream ss;
std::vector<bool> indents;
indents.resize(root->getMaxDepth());
logNode(ss, root, 0, indents);
std::cout << ss.str();
}
void FileSystem::logNode(std::stringstream& ss, const Node* node, int depth, std::vector<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 FileSystem::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 FileSystem::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 FileSystem::logFile(std::stringstream& ss, const File* node, int depth, std::vector<bool>& indents) const {
indent(ss, depth, indents);
ss << node->key << "\n";
}
void FileSystem::logLink(std::stringstream & ss, const Link* node, int depth, std::vector<bool>& indents) const {
indent(ss, depth, indents);
ss << "link [" << node->key << "] \n";
}

92
src/Path.cpp Normal file
View file

@ -0,0 +1,92 @@
#include "Path.hpp"
#include <cassert>
std::vector<char> transitions;
void initializeTransitions() {
transitions.resize(256);
std::fill(transitions.begin(), transitions.end(), 1);
for (char i = 'a'; i <= 'z'; i++) transitions[i] = i;
for (char i = '0'; i <= '9'; i++) transitions[i] = i;
for (char i = 'A'; i <= 'Z'; i++) transitions[i] = i + ('a' - 'A');
transitions['/'] = '/';
transitions['.'] = '.';
}
Path::Path(const char* path) {
set(path);
}
bool Path::isValid() const {
return mIsValid;
}
bool Path::isInvalid() const {
return !mIsValid;
}
bool Path::isAbsolute() const {
return mAbsolute;
}
int Path::getDepth() const {
return mChain.size();
}
const std::vector<std::string>& Path::getChain() const {
return mChain;
}
std::vector<std::string> Path::getParentChain() const {
std::vector<std::string> out = mChain;
out.pop_back();
return out;
}
const std::string& Path::getFilename() const {
assert(getDepth());
return mChain.back();
}
void Path::set(const std::string& path) {
if (path.empty()) {
mIsValid = false;
return;
}
std::string lowercasePath = path;
mIsValid = true;
mAbsolute = path.front() == '/';
mDirectory = path.back() == '/';
mChain.clear();
for (auto & character : lowercasePath) {
character = transitions[character];
if (character == 1) mIsValid = false;
}
if (lowercasePath == "/") return;
const char* begin = &lowercasePath.front() + (lowercasePath.front() == '/');
const char* end = &lowercasePath.back() - (lowercasePath.back() == '/');
const char* prev = begin;
for (const char* iter = begin; iter <= end; iter++) {
if (*iter == '/') {
const auto string = lowercasePath.substr(prev - lowercasePath.c_str(), iter - prev);
mChain.push_back(string);
prev = iter + 1;
}
}
const auto string = lowercasePath.substr(prev - lowercasePath.c_str(), end - prev + 1);
mChain.push_back(string);
for (auto & key : mChain) {
if (key.empty()) mIsValid = false;
}
}
const std::string& Path::operator[](int idx) const {
return mChain[idx];
}

42
test/Tests.cpp Normal file
View file

@ -0,0 +1,42 @@
#include "FileSystem.hpp"
int main() {
FileSystem fse;
fse.makeDirectory("/a");
fse.makeDirectory("/a/b");
fse.makeDirectory("/a/b/k");
fse.makeDirectory("/a/c");
fse.makeDirectory("/a/c/k");
fse.makeDirectory("/d");
fse.makeDirectory("/d/b");
fse.makeDirectory("/d/b/k");
fse.makeDirectory("/d/c");
fse.makeDirectory("/d/c/k");
fse.makeDirectory("d");
fse.changeCurrent("/d/c/k");
fse.makeDirectory("/a");
fse.makeDirectory("/a/b");
fse.makeDirectory("/a/b/k");
fse.makeDirectory("/a/c");
fse.makeDirectory("/a/c/k");
fse.makeDirectory("ggad");
fse.makeDirectory("gad");
fse.makeDirectory("rwed");
fse.makeDirectory("e");
fse.makeDirectory("d");
fse.makeDirectory("d/b");
fse.makeDirectory("d/b/k");
fse.makeDirectory("d/c");
fse.makeDirectory("d/c/k");
fse.log();
return 0;
}