diff --git a/vst/CMakeLists.txt b/vst/CMakeLists.txt index 72e27562..0ee8cddc 100644 --- a/vst/CMakeLists.txt +++ b/vst/CMakeLists.txt @@ -21,7 +21,8 @@ set(VSTPLUGIN_SOURCES SfizzForeignPaths.cpp VstPluginFactory.cpp X11RunLoop.cpp - NativeHelpers.cpp) + NativeHelpers.cpp + FileTrie.cpp) set(VSTPLUGIN_HEADERS SfizzVstProcessor.h @@ -31,7 +32,8 @@ set(VSTPLUGIN_HEADERS SfizzFileScan.h SfizzForeignPaths.h X11RunLoop.h - NativeHelpers.h) + NativeHelpers.h + FileTrie.h) if(APPLE) set(VSTPLUGIN_MAC_SOURCES diff --git a/vst/FileTrie.cpp b/vst/FileTrie.cpp new file mode 100644 index 00000000..2819c863 --- /dev/null +++ b/vst/FileTrie.cpp @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "FileTrie.h" +#include +#include + +constexpr size_t FileTrie::npos; + +fs::path FileTrie::at(size_t index) const +{ + if (index >= entries_.size()) + throw std::out_of_range("FileTrie::at"); + return pathFromEntry(index); +} + +fs::path FileTrie::operator[](size_t index) const +{ + assert(index < entries_.size()); + return pathFromEntry(index); +} + +fs::path FileTrie::pathFromEntry(size_t index) const +{ + const Entry* currentEntry = &entries_[index]; + fs::path path { currentEntry->name }; + + size_t currentIndex; + while ((currentIndex = currentEntry->parent) != npos) { + currentEntry = &entries_[currentIndex]; + path = fs::path { currentEntry->name } / path; + } + + return path; +} + +//------------------------------------------------------------------------------ +FileTrieBuilder::FileTrieBuilder(size_t initialCapacity) +{ + FileTrie& trie = trie_; + trie.entries_.reserve(initialCapacity); +} + +FileTrie&& FileTrieBuilder::build() +{ + FileTrie& trie = trie_; + trie.entries_.shrink_to_fit(); + return std::move(trie); +} + +size_t FileTrieBuilder::addFile(const fs::path& path) +{ + if (path.empty()) + return FileTrie::npos; + + size_t dirIndex = ensureDirectory(path.parent_path()); + + FileTrie& trie = trie_; + FileTrie::Entry ent; + ent.parent = dirIndex; + ent.name = (--path.end())->u8string(); + + size_t fileIndex = trie.entries_.size(); + trie.entries_.push_back(std::move(ent)); + + return fileIndex; +} + +size_t FileTrieBuilder::ensureDirectory(const fs::path& dirPath) +{ + if (dirPath.empty()) + return FileTrie::npos; + + const fs::path::string_type& dirNat = dirPath.native(); + auto it = directories_.find(dirNat); + if (it != directories_.end()) + return it->second; + + FileTrie& trie = trie_; + FileTrie::Entry ent; + ent.parent = FileTrie::npos; + ent.name = (--dirPath.end())->u8string(); + if (dirPath.has_parent_path()) { + fs::path parentPath = dirPath.parent_path(); + if (parentPath != dirPath) + ent.parent = ensureDirectory(parentPath); + } + + size_t dirIndex = trie.entries_.size(); + trie.entries_.push_back(std::move(ent)); + + directories_[dirNat] = dirIndex; + + return dirIndex; +} diff --git a/vst/FileTrie.h b/vst/FileTrie.h new file mode 100644 index 00000000..8ea73559 --- /dev/null +++ b/vst/FileTrie.h @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include +#include +#include +#include +#include + +class FileTrie { +public: + static constexpr size_t npos = ~size_t(0); + + size_t size() const noexcept { return entries_.size(); } + fs::path at(size_t index) const; + fs::path operator[](size_t index) const; + void clear() noexcept { entries_.clear(); } + +private: + fs::path pathFromEntry(size_t index) const; + +private: + struct Entry { + size_t parent = npos; + std::string name; + }; + std::vector entries_; + + friend class FileTrieBuilder; +}; + +//------------------------------------------------------------------------------ +class FileTrieBuilder { +public: + explicit FileTrieBuilder(size_t initialCapacity = 8192); + FileTrie&& build(); + size_t addFile(const fs::path& path); + +private: + size_t ensureDirectory(const fs::path& dirPath); + +private: + FileTrie trie_; + std::unordered_map directories_; +}; diff --git a/vst/SfizzFileScan.cpp b/vst/SfizzFileScan.cpp index 951778df..ae2c4b60 100644 --- a/vst/SfizzFileScan.cpp +++ b/vst/SfizzFileScan.cpp @@ -34,13 +34,13 @@ bool SfzFileScan::locateRealFile(const fs::path& pathOrig, fs::path& pathFound) if (it == file_index_.end()) return false; - std::list candidateStrings = it->second; - lock.unlock(); - + const std::list& candidateIndices = it->second; std::vector candidates; - candidates.reserve(candidateStrings.size()); - for (const std::string& str : candidateStrings) - candidates.push_back(fs::u8path(str)); + candidates.reserve(candidateIndices.size()); + for (const size_t index : candidateIndices) + candidates.push_back(file_trie_[index]); + + lock.unlock(); pathFound = electBestMatch(pathOrig, candidates); return true; @@ -57,8 +57,11 @@ void SfzFileScan::refreshScan(bool force) if (!force && !isExpired()) return; + file_trie_.clear(); file_index_.clear(); + FileTrieBuilder builder; + for (const fs::path& dirPath : SfizzPaths::sfzDefaultPaths()) { std::error_code ec; const fs::directory_options dirOpts = @@ -71,11 +74,14 @@ void SfzFileScan::refreshScan(bool force) const fs::directory_entry& ent = *it; const fs::path& filePath = ent.path(); std::error_code ec; - if (ent.is_regular_file(ec) /*&& pathIsSfz(filePath)*/) - file_index_[keyOf(filePath.filename())].push_back(filePath.u8string()); + if (ent.is_regular_file(ec) /*&& pathIsSfz(filePath)*/) { + size_t fileIndex = builder.addFile(filePath); + file_index_[keyOf(filePath.filename())].push_back(fileIndex); + } } } + file_trie_ = builder.build(); completion_time_ = clock::now(); } diff --git a/vst/SfizzFileScan.h b/vst/SfizzFileScan.h index 68b80a90..2626ef15 100644 --- a/vst/SfizzFileScan.h +++ b/vst/SfizzFileScan.h @@ -5,6 +5,7 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #pragma once +#include "FileTrie.h" #include #include #include @@ -23,7 +24,8 @@ private: typedef std::chrono::steady_clock clock; std::mutex mutex; absl::optional completion_time_; - std::unordered_map> file_index_; + FileTrie file_trie_; + std::unordered_map> file_index_; bool isExpired() const; void refreshScan(bool force = false);