diff options
Diffstat (limited to 'libs/usvfs/src/shared/directory_tree.h')
| -rw-r--r-- | libs/usvfs/src/shared/directory_tree.h | 650 |
1 files changed, 0 insertions, 650 deletions
diff --git a/libs/usvfs/src/shared/directory_tree.h b/libs/usvfs/src/shared/directory_tree.h deleted file mode 100644 index 1712071..0000000 --- a/libs/usvfs/src/shared/directory_tree.h +++ /dev/null @@ -1,650 +0,0 @@ -/* -Userspace Virtual Filesystem - -Copyright (C) 2015 Sebastian Herbord. All rights reserved. - -This file is part of usvfs. - -usvfs is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -usvfs is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with usvfs. If not, see <http://www.gnu.org/licenses/>. -*/ -#pragma once - -#include "exceptionex.h" -#include "logging.h" -#include "shared_memory.h" -#include "stringutils.h" -#include "wildcard.h" - -// simplify unit tests by allowing access to private members -#ifndef PRIVATE -#define PRIVATE private -#endif // PRIVATE - -namespace usvfs::shared -{ - -template <typename T, typename U> -struct SHMDataCreator -{ - static T create(const U& source, const VoidAllocatorT& allocator) - { - return T(source, allocator); - } -}; - -template <typename T, typename U> -T createData(const U& source, const VoidAllocatorT& allocator) -{ - return SHMDataCreator<T, U>::create(source, allocator); -} - -template <typename T> -T createDataEmpty(const VoidAllocatorT& allocator); - -template <typename T> -void dataAssign(T& destination, const T& source); - -// crappy little workaround for fs::path iterating over path separators -fs::path::iterator nextIter(const fs::path::iterator& iter, - const fs::path::iterator& end); - -void advanceIter(fs::path::iterator& iter, const fs::path::iterator& end); - -// decomposes a path into its components -// -class DecomposablePath -{ -public: - explicit DecomposablePath(std::string s) : m_s(std::move(s)), m_begin(0), m_end(0) - { - m_end = nextSeparator(m_begin); - } - - // move to the next component, returns false when there are no more components - // - bool next() - { - for (;;) { - if (m_end >= m_s.size()) { - // done - m_begin = m_end; - return false; - } - - // move begin to one past the last separator found - m_begin = m_end + 1; - - // find the next separator - m_end = nextSeparator(m_begin); - - // check for components that should be ignored: - // - empty, happens when the path ends with a separator - // - slashes, happens with consecutive separators - // - dot, unnecessary - const auto c = current(); - if (!c.empty() && c != "\\" && c != "/" && c != ".") { - return true; - } - } - } - - // checks if next() would return false - // - bool peekNext() const - { - auto copy = *this; - return copy.next(); - } - - // the current component, empty when next() returned false - // - std::string_view current() const { return {m_s.data() + m_begin, m_end - m_begin}; } - -private: - const std::string m_s; - std::size_t m_begin, m_end; - - // finds the next path separator - // - std::size_t nextSeparator(std::size_t from) const - { - while (from < m_s.size()) { - if (m_s[from] == '/' || m_s[from] == '\\') { - break; - } - - ++from; - } - - return from; - } -}; - -namespace bi = boost::interprocess; -namespace bmi = boost::multi_index; - -typedef uint8_t TreeFlags; - -static const TreeFlags FLAG_DIRECTORY = 0x01; -static const TreeFlags FLAG_DUMMY = 0x02; -static const TreeFlags FLAG_FIRSTUSERFLAG = 0x10; - -struct MissingThrowT -{}; -static const MissingThrowT MissingThrow = MissingThrowT(); - -template <typename NodeDataT> -class TreeContainer; - -template <typename T1, typename T2, typename Alloc> -struct mutable_pair -{ - typedef T1 first_type; - typedef T2 second_type; - - mutable_pair(Alloc alloc) : first(T1(alloc)), second(T2(alloc)) {} - - mutable_pair(const T1& f, const T2& s) : first(f), second(s) {} - - mutable_pair(const std::pair<T1, T2>& p) : first(p.first), second(p.second) {} - - T1 first; - mutable T2 second; -}; - -template <typename Key, typename T, typename Compare, typename Allocator, - typename Element = mutable_pair<Key, T, Allocator>> -using mimap = bmi::multi_index_container< - Element, - bmi::indexed_by< - bmi::ordered_unique<bmi::member<Element, Key, &Element::first>, Compare>>, - typename Allocator::template rebind<Element>::other>; - -/** - * a representation of a directory tree in memory. - * This class is designed to be stored in shared memory. - */ -template <typename NodeDataT> -class DirectoryTree -{ - template <typename T> - friend class TreeContainer; - -public: - struct CILess - { - template <typename U, typename V> - bool operator()(const U& lhs, const V& rhs) const - { - const size_t lhsLength = getLength(lhs); - const size_t rhsLength = getLength(rhs); - - const auto r = - _strnicmp(getCharPtr(lhs), getCharPtr(rhs), std::min(lhsLength, rhsLength)); - - if (r == 0) { - return lhsLength < rhsLength; - } - - return (r < 0); - } - - private: - const char* getCharPtr(const StringT& s) const { return s.c_str(); } - - const char* getCharPtr(const std::string& s) const { return s.c_str(); } - - const char* getCharPtr(const char* s) const { return s; } - - const char* getCharPtr(std::string_view s) const { return s.data(); } - - size_t getLength(const StringT& s) const { return s.size(); } - - size_t getLength(const std::string& s) const { return s.size(); } - - size_t getLength(const char* s) const { return strlen(s); } - - size_t getLength(std::string_view s) const { return s.size(); } - }; - - typedef DirectoryTree<NodeDataT> NodeT; - typedef bi::deleter<NodeT, SegmentManagerT> DeleterT; - typedef NodeDataT DataT; - - typedef bi::shared_ptr<NodeT, VoidAllocatorT, DeleterT> NodePtrT; - typedef bi::weak_ptr<NodeT, VoidAllocatorT, DeleterT> WeakPtrT; - - typedef bi::allocator<std::pair<const StringT, NodePtrT>, SegmentManagerT> - NodeEntryAllocatorT; - - typedef mimap<StringT, NodePtrT, CILess, NodeEntryAllocatorT> NodeMapT; - typedef typename NodeMapT::iterator file_iterator; - typedef typename NodeMapT::const_iterator const_file_iterator; - - typedef std::function<void(const NodePtrT&)> VisitorFunction; - - DirectoryTree() = delete; - DirectoryTree(const NodeT& reference) = delete; - DirectoryTree(NodeT&& reference) = delete; - NodeT& operator=(NodeT reference) = delete; - - /** - * @brief construct a new node to be inserted in an existing tree - **/ - DirectoryTree(std::string_view name, TreeFlags flags, const NodePtrT& parent, - const NodeDataT& data, const VoidAllocatorT& allocator) - : m_Parent(parent), m_Name(name.begin(), name.end(), allocator), m_Data(data), - m_Nodes(allocator), m_Flags(flags) - {} - - ~DirectoryTree() { m_Nodes.clear(); } - - /** - * @return parent node - */ - NodePtrT parent() const { return m_Parent.lock(); } - - /** - * @return the full path to the node - */ - fs::path path() const - { - if (m_Parent.lock().get() == nullptr) { - if (m_Name.size() == 0) { - return fs::path(); - } else { - return fs::path(m_Name.c_str()) / "\\"; - } - } else { - return m_Parent.lock()->path() / m_Name.c_str(); - } - } - - /** - * @return data connected to this node - **/ - const NodeDataT& data() const { return m_Data; } - - /** - * @return name of this node - */ - std::string name() const { return m_Name.c_str(); } - - /** - * @brief setFlag change a flag for this node - * @param enabled new state for the specified flag - */ - void setFlag(TreeFlags flag, bool enabled = true) - { - m_Flags = enabled ? m_Flags | flag : m_Flags & ~flag; - } - - /** - * @return true if the specified flag is set, false otherwise - */ - bool hasFlag(TreeFlags flag) const { return (m_Flags & flag) != 0; } - - /** - * @return true if this node is a directory, false if it's a regular file - */ - bool isDirectory() const { return hasFlag(FLAG_DIRECTORY); } - - /** - * @return the number of subnodes (directly) below this one - */ - size_t numNodes() const { return m_Nodes.size(); } - - /** - * @return number of nodes in this (sub-)tree including this one - */ - size_t numNodesRecursive() const - { - size_t result = numNodes() + 1; - - for (const auto& node : m_Nodes) { - result += node.second->numNodesRecursive(); - } - - return result; - } - - /** - * @brief find a node by its path - * @param path the path to look up - * @return a pointer to the node or a null ptr - */ - NodePtrT findNode(const fs::path& path) - { - fs::path::iterator iter = path.begin(); - if (iter == path.end()) { - return NodePtrT(); - } - return findNode(path, iter); - } - - /** - * @brief find a node by its path - * @param path the path to look up - * @return a pointer to the node or a null ptr - */ - const NodePtrT findNode(const fs::path& path) const - { - fs::path::iterator iter = path.begin(); - if (iter == path.end()) { - return NodePtrT(); - } - return findNode(path, iter); - } - - /** - * @brief visit the nodes along the specified path (in order) calling the visitor for - * each - * @param path the path to visit - * @param visitor a function called for each node - */ - void visitPath(const fs::path& path, const VisitorFunction& visitor) const - { - fs::path::iterator iter = path.begin(); - if (iter == path.end()) { - return; - } - visitPath(path, iter, visitor); - } - - /** - * @brief retrieve a node by the specified name - * @param name name of the node - * @return the node found or an empty pointer if no such node was found - */ - NodePtrT node(std::string_view name, MissingThrowT) const - { - auto iter = m_Nodes.find(name); - - if (iter != m_Nodes.end()) { - return iter->second; - } else { - USVFS_THROW_EXCEPTION(node_missing_error()); - } - } - - /** - * @brief retrieve a node by the specified name - * @param name name of the node - * @return the node found or an empty pointer if no such node was found - */ - NodePtrT node(std::string_view name) - { - auto iter = m_Nodes.find(name); - - if (iter != m_Nodes.end()) { - return iter->second; - } else { - return NodePtrT(); - } - } - - /** - * @brief retrieve a node by the specified name - * @param name name of the node - * @return the node found or an empty pointer if no such node was found - */ - const NodePtrT node(std::string_view name, MissingThrowT) - { - auto iter = m_Nodes.find(name); - - if (iter != m_Nodes.end()) { - return iter->second; - } else { - USVFS_THROW_EXCEPTION(node_missing_error()); - } - } - - /** - * @brief retrieve a node by the specified name - * @param name name of the node - * @return the node found or an empty pointer if no such node was found - */ - const NodePtrT node(std::string_view name) const - { - auto iter = m_Nodes.find(name); - - if (iter != m_Nodes.end()) { - return iter->second; - } else { - return NodePtrT(); - } - } - - /** - * @brief test if a node by the specified name exists - * @param name name of the node - * @return true if the node exists, false otherwise - */ - bool exists(std::string_view name) const - { - return m_Nodes.find(name) != m_Nodes.end(); - } - - /** - * @brief find all matches for a pattern - * @param pattern the pattern to look for - * @return a vector of the found nodes - */ - std::vector<NodePtrT> find(const std::string& pattern) const - { - // determine if there is a prefix in the pattern that indicates a specific - // directory. - size_t fixedPart = pattern.find_first_of("*?"); - - if (fixedPart == 0) - fixedPart = std::string::npos; - if (fixedPart != std::string::npos) - fixedPart = pattern.find_last_of(R"(\/)", fixedPart); - - std::vector<NodePtrT> result; - - if (fixedPart != std::string::npos) { - // if there is a prefix, search for the node representing that path and - // search only on that - NodePtrT node = findNode(fs::path(pattern.substr(0, fixedPart))); - if (node.get() != nullptr) { - node->findLocal(result, pattern.substr(fixedPart + 1)); - } - } else { - findLocal(result, pattern); - } - - return result; - } - - /** - * @return an iterator to the first leaf - **/ - file_iterator filesBegin() { return m_Nodes.begin(); } - - /** - * @return a const iterator to the first leaf - **/ - const_file_iterator filesBegin() const { return m_Nodes.begin(); } - - /** - * @return an iterator one past the last leaf - **/ - file_iterator filesEnd() { return m_Nodes.end(); } - - /** - * @return a const iterator one past the last leaf - **/ - const_file_iterator filesEnd() const { return m_Nodes.end(); } - - /** - * @brief erase the leaf at the specified iterator - * @return an iterator to the following file - **/ - file_iterator erase(file_iterator iter) { return m_Nodes.erase(iter); } - - /** - * @brief clear all nodes - */ - void clear() { m_Nodes.clear(); } - - void removeFromTree() - { - if (auto par = parent()) { - spdlog::get("usvfs")->info("remove from tree {}", m_Name.c_str()); - auto self = par->m_Nodes.find(m_Name.c_str()); - if (self != par->m_Nodes.end()) { - par->erase(self); - } else { - // trying to remove a node that does not exist, most likely because it was - // already removed in a lower level call. this is known to happen when MoveFile - // has the MOVEFILE_COPY_ALLOWED flag and moving a mapped file. - spdlog::get("usvfs")->warn("Failed to remove inexisting node from tree: {}", - m_Name.c_str()); - } - } - } - - PRIVATE : void set(StringT key, const NodePtrT& value) - { - auto res = m_Nodes.emplace(std::move(key), value); - if (!res.second) { - res.first->second = value; - } - } - - WeakPtrT findRoot() const - { - if (m_Parent.lock().get() == nullptr) { - return m_Self; - } else { - return m_Parent.lock()->findRoot(); - } - } - - NodePtrT findNode(const fs::path& name, fs::path::iterator& iter) - { - if (iter == name.end()) { - return NodePtrT(); - } - - std::string l = iter->string(); - auto subNode = m_Nodes.find(iter->string()); - advanceIter(iter, name.end()); - - if (iter == name.end()) { - // last name component, should be a local node - if (subNode != m_Nodes.end()) { - return subNode->second; - } else { - return NodePtrT(); - } - } else { - if (subNode != m_Nodes.end()) { - return subNode->second->findNode(name, iter); - } else { - return NodePtrT(); - } - } - } - - const NodePtrT findNode(const fs::path& name, fs::path::iterator& iter) const - { - if (iter == name.end()) { - return NodePtrT(); - } - - auto subNode = m_Nodes.find(iter->string()); - advanceIter(iter, name.end()); - - if (iter == name.end()) { - // last name component, should be a local node - if (subNode != m_Nodes.end()) { - return subNode->second; - } else { - return NodePtrT(); - } - } else { - if (subNode != m_Nodes.end()) { - return subNode->second->findNode(name, iter); - } else { - return NodePtrT(); - } - } - } - - void visitPath(const fs::path& path, fs::path::iterator& iter, - const VisitorFunction& visitor) const - { - if (iter == path.end()) { - return; - } - - auto subNode = m_Nodes.find(iter->string()); - - if (subNode != m_Nodes.end()) { - visitor(subNode->second); - advanceIter(iter, path.end()); - if (iter != path.end()) { - subNode->second->visitPath(path, iter, visitor); - } - } - } - - void findLocal(std::vector<NodePtrT>& output, const std::string& pattern) const - { - for (auto iter = m_Nodes.begin(); iter != m_Nodes.end(); ++iter) { - LPCSTR remainder = nullptr; - - if (pattern.size() > 1 && (pattern[0] == '*') && - ((pattern[1] == '/') || (pattern[1] == '\\')) && - iter->second->isDirectory()) { - // the star may represent a directory (one directory level, not - // multiple!), search in subdirectory - iter->second->findLocal(output, pattern.substr(1)); - } else if ((remainder = wildcard::PartialMatch(iter->second->name().c_str(), - pattern.c_str())) != nullptr) { - if ((*remainder == '\0') || (strcmp(remainder, "*") == 0)) { - NodePtrT node = iter->second; - output.push_back(node); - } - - if (iter->second->isDirectory()) { - iter->second->findLocal(output, remainder); - } - } - } - } - - PRIVATE : TreeFlags m_Flags; - - WeakPtrT m_Parent; - WeakPtrT m_Self; - - StringT m_Name; - NodeDataT m_Data; - - NodeMapT m_Nodes; -}; - -template <typename NodeDataT> -void dumpTree(std::ostream& stream, const DirectoryTree<NodeDataT>& tree, int level = 0) -{ - stream << std::string(level, ' ') << tree.name() << " -> " << tree.data() << "\n"; - for (auto iter = tree.filesBegin(); iter != tree.filesEnd(); ++iter) { - dumpTree<NodeDataT>(stream, *iter->second, level + 1); - } -} - -} // namespace usvfs::shared |
