diff --git a/SModule b/SModule index 3e96f9f..845eb17 100644 --- a/SModule +++ b/SModule @@ -17,6 +17,7 @@ mijin_sources = Split(""" source/mijin/util/os.cpp source/mijin/types/name.cpp source/mijin/virtual_filesystem/filesystem.cpp + source/mijin/virtual_filesystem/memory.cpp source/mijin/virtual_filesystem/stacked.cpp """) diff --git a/source/mijin/virtual_filesystem/memory.cpp b/source/mijin/virtual_filesystem/memory.cpp new file mode 100644 index 0000000..45d27e2 --- /dev/null +++ b/source/mijin/virtual_filesystem/memory.cpp @@ -0,0 +1,160 @@ + +#include "./memory.hpp" + +#include "../io/stream.hpp" + +namespace mijin +{ +std::vector MemoryFileSystemAdapter::listFiles(const fs::path& folder) +{ + const detail::MemoryFolder* folderObj = findFolder(folder); + if (folderObj == nullptr) + { + return {}; + } + std::vector result; + result.reserve(folderObj->folders.size() + folderObj->files.size()); + + for (const auto& [name, subFolder] : folderObj->folders) + { + result.push_back(folderInfo(folder / name, subFolder)); + } + + for (const auto& [name, file] : folderObj->files) + { + result.push_back(fileInfo(folder / name, file)); + } + + return result; +} + +FileInfo MemoryFileSystemAdapter::getFileInfo(const fs::path& file) +{ +#if 0 // shouldn't be necessary + // empty means root + if (file.empty()) + { + return { + .path = {}, + .size = root_.folders.size() + root_.files.size(), + .exists = true, + .isFolder = true + }; + } +#endif + + const detail::MemoryFolder* folderObj = findFolder(file.parent_path()); + if (folderObj == nullptr) + { + return {}; + } + const std::string filename = file.filename().generic_string(); + + if (auto itFolder = folderObj->folders.find(filename); itFolder != folderObj->folders.end()) + { + return folderInfo(file, itFolder->second); + } + if (auto itFile = folderObj->files.find(filename); itFile != folderObj->files.end()) + { + return fileInfo(file, itFile->second); + } + return {}; +} + +StreamError MemoryFileSystemAdapter::open(const fs::path& path, FileOpenMode mode, std::unique_ptr& outStream) +{ + if (mode != FileOpenMode::READ) + { + return StreamError::IO_ERROR; + } + + const detail::MemoryFolder* folderObj = findFolder(path.parent_path()); + if (folderObj == nullptr) + { + return StreamError::IO_ERROR; + } + + auto itFile = folderObj->files.find(path.filename().generic_string()); + if (itFile == folderObj->files.end()) + { + return StreamError::IO_ERROR; + } + + std::unique_ptr stream = std::make_unique(); + stream->openRO(itFile->second.data); + outStream = std::move(stream); + + return StreamError::SUCCESS; +} + +bool MemoryFileSystemAdapter::addFile(const fs::path& path, std::span data, Overwrite overwrite, CopyData copyData) +{ + detail::MemoryFolder& folder = *findFolder(path.parent_path(), true); + std::string filename = path.filename().generic_string(); + + if (folder.folders.contains(filename)) + { + return false; + } + + if (!overwrite && folder.files.contains(filename)) + { + return false; + } + + if (copyData) + { + data = fileData_.emplace_back(data.begin(), data.end()); + } + + folder.files.emplace(std::move(filename), detail::MemoryFile{.data = data}); + return true; +} + +void MemoryFileSystemAdapter::addFolder(const fs::path& path) +{ + (void) findFolder(path, true); +} + +detail::MemoryFolder* MemoryFileSystemAdapter::findFolder(const fs::path& path, bool create) MIJIN_NOEXCEPT +{ + detail::MemoryFolder* folder = &root_; + for (const fs::path& part : path) + { + std::string partname = part.generic_string(); + auto it = folder->folders.find(partname); + if (it == folder->folders.end()) + { + if (!create) + { + return nullptr; + } + folder = &folder->folders[std::move(partname)]; + } + else + { + folder = &it->second; + } + } + return folder; +} + +FileInfo MemoryFileSystemAdapter::folderInfo(const fs::path& path, const detail::MemoryFolder& folder) const noexcept +{ + return { + .path = path, + .size = folder.folders.size() + folder.files.size(), + .exists = true, + .isFolder = true + }; +} + +FileInfo MemoryFileSystemAdapter::fileInfo(const fs::path& path, const detail::MemoryFile& file) const noexcept +{ + return { + .path = path, + .size = file.data.size(), + .exists = true + }; +} +} diff --git a/source/mijin/virtual_filesystem/memory.hpp b/source/mijin/virtual_filesystem/memory.hpp new file mode 100644 index 0000000..b5c465d --- /dev/null +++ b/source/mijin/virtual_filesystem/memory.hpp @@ -0,0 +1,55 @@ + +#pragma once + +#if !defined(MIJIN_SOURCE_MIJIN_VIRTUAL_FILESYSTEM_MEMORY_HPP_INCLUDED) +#define MIJIN_SOURCE_MIJIN_VIRTUAL_FILESYSTEM_MEMORY_HPP_INCLUDED 1 + +#include +#include + +#include "../container/vector_map.hpp" +#include "../util/flag.hpp" +#include "../virtual_filesystem/filesystem.hpp" + +namespace mijin +{ +namespace detail +{ +struct MemoryFile +{ + std::span data; +}; +struct MemoryFolder +{ + VectorMap files; + VectorMap folders; +}; +} + +class MemoryFileSystemAdapter : public FileSystemAdapter +{ +public: + MIJIN_DEFINE_FLAG(Overwrite); + MIJIN_DEFINE_FLAG(CopyData); +private: + detail::MemoryFolder root_; + std::vector> fileData_; +public: + [[nodiscard]] std::vector listFiles(const fs::path& folder) override; + [[nodiscard]] FileInfo getFileInfo(const fs::path& file) override; + [[nodiscard]] StreamError open(const fs::path& path, FileOpenMode mode, std::unique_ptr& outStream) override; + + bool addFile(const fs::path& path, std::span data, Overwrite overwrite = Overwrite::NO, CopyData copyData = CopyData::NO); + bool addFile(const fs::path& path, std::span data, CopyData copyData) + { + return addFile(path, data, Overwrite::NO, copyData); + } + void addFolder(const fs::path& path); +private: + detail::MemoryFolder* findFolder(const fs::path& path, bool create = false) MIJIN_NOEXCEPT; + FileInfo folderInfo(const fs::path& path, const detail::MemoryFolder& folder) const MIJIN_NOEXCEPT; + FileInfo fileInfo(const fs::path& path, const detail::MemoryFile& file) const MIJIN_NOEXCEPT; +}; +} // namespace mijin + +#endif // !defined(MIJIN_SOURCE_MIJIN_VIRTUAL_FILESYSTEM_MEMORY_HPP_INCLUDED)