add io::MemoryDevice
This commit is contained in:
parent
dae7d10a74
commit
654267f2d4
210
src/io/devices/MemoryDevice.cpp
Normal file
210
src/io/devices/MemoryDevice.cpp
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
#include "MemoryDevice.hpp"
|
||||||
|
|
||||||
|
#include "../memory_istream.hpp"
|
||||||
|
#include "../memory_ostream.hpp"
|
||||||
|
#include "../finalizing_ostream.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
io::MemoryDevice::MemoryDevice() {}
|
||||||
|
|
||||||
|
std::filesystem::path io::MemoryDevice::resolve(std::string_view path) {
|
||||||
|
throw std::runtime_error("unable to resolve filesystem path");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<std::ostream> io::MemoryDevice::write(std::string_view path) {
|
||||||
|
std::string filePath = std::string(path);
|
||||||
|
return std::make_unique<finalizing_ostream>(
|
||||||
|
std::make_unique<memory_ostream>(),
|
||||||
|
[this, filePath](auto ostream) {
|
||||||
|
auto& memoryStream = dynamic_cast<memory_ostream&>(*ostream);
|
||||||
|
createFile(std::move(filePath), memoryStream.release());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<std::istream> io::MemoryDevice::read(std::string_view path) {
|
||||||
|
const auto& found = nodes.find(std::string(path));
|
||||||
|
if (found == nodes.end()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto& node = found->second;
|
||||||
|
if (auto file = node.get_if<File>()) {
|
||||||
|
if (file->content != nullptr) {
|
||||||
|
return std::make_unique<memory_view_istream>(file->content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t io::MemoryDevice::size(std::string_view path) {
|
||||||
|
const auto& found = nodes.find(std::string(path));
|
||||||
|
if (found == nodes.end()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return std::visit([](auto&& arg) -> size_t {
|
||||||
|
using T = std::decay_t<decltype(arg)>;
|
||||||
|
if constexpr (std::is_same_v<T, File>) {
|
||||||
|
return arg.content.size();
|
||||||
|
} else if constexpr (std::is_same_v<T, Dir>) {
|
||||||
|
return arg.content.size();
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}, found->second.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
io::file_time_type io::MemoryDevice::lastWriteTime(std::string_view path) {
|
||||||
|
return file_time_type::min();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool io::MemoryDevice::exists(std::string_view path) {
|
||||||
|
return nodes.find(std::string(path)) != nodes.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool io::MemoryDevice::isdir(std::string_view path) {
|
||||||
|
const auto& found = nodes.find(std::string(path));
|
||||||
|
if (found == nodes.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return found->second.holds_alternative<Dir>();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool io::MemoryDevice::isfile(std::string_view path) {
|
||||||
|
const auto& found = nodes.find(std::string(path));
|
||||||
|
if (found == nodes.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return found->second.holds_alternative<File>();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool io::MemoryDevice::mkdir(std::string_view path) {
|
||||||
|
return createDir(std::string(path)) != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool io::MemoryDevice::mkdirs(std::string_view path) {
|
||||||
|
io::path dirPath = std::string(path);
|
||||||
|
std::vector<std::string> parts;
|
||||||
|
while (!dirPath.pathPart().empty()) {
|
||||||
|
parts.push_back(dirPath.name());
|
||||||
|
dirPath = dirPath.parent();
|
||||||
|
}
|
||||||
|
for (int i = parts.size() - 1; i >= 0; i--) {
|
||||||
|
dirPath = dirPath / parts[i];
|
||||||
|
createDir(dirPath.string());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool io::MemoryDevice::remove(std::string_view path) {
|
||||||
|
std::string pathString = std::string(path);
|
||||||
|
const auto& found = nodes.find(pathString);
|
||||||
|
if (found == nodes.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (found->second.holds_alternative<Dir>()) {
|
||||||
|
const auto& dir = found->second.get_if<Dir>();
|
||||||
|
if (!dir->content.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
io::path filePath = pathString;
|
||||||
|
io::path parentPath = filePath.parent();
|
||||||
|
auto parentDir = getDir(parentPath.string());
|
||||||
|
if (parentDir) {
|
||||||
|
auto& content = parentDir->content;
|
||||||
|
content.erase(
|
||||||
|
std::remove(content.begin(), content.end(), filePath.name()),
|
||||||
|
content.end()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
nodes.erase(found);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t io::MemoryDevice::removeAll(std::string_view path) {
|
||||||
|
std::string pathString = std::string(path);
|
||||||
|
const auto& found = nodes.find(pathString);
|
||||||
|
if (found == nodes.end()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
io::path filePath = pathString;
|
||||||
|
|
||||||
|
uint64_t count = 0;
|
||||||
|
if (found->second.holds_alternative<Dir>()) {
|
||||||
|
auto dir = found->second.get_if<Dir>();
|
||||||
|
for (const auto& name : dir->content) {
|
||||||
|
io::path subPath = filePath / name;
|
||||||
|
count += removeAll(subPath.string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (remove(pathString)) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct MemoryPathsGenerator : public io::PathsGenerator {
|
||||||
|
std::vector<std::string> entries;
|
||||||
|
size_t index = 0;
|
||||||
|
|
||||||
|
MemoryPathsGenerator(std::vector<std::string>&& entries)
|
||||||
|
: entries(std::move(entries)) {}
|
||||||
|
|
||||||
|
bool next(io::path& outPath) override {
|
||||||
|
if (index >= entries.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
outPath = entries[index++];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<io::PathsGenerator> io::MemoryDevice::list(std::string_view path) {
|
||||||
|
auto dir = getDir(path);
|
||||||
|
if (!dir) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return std::make_unique<MemoryPathsGenerator>(
|
||||||
|
std::vector<std::string>(dir->content)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
io::MemoryDevice::Dir* io::MemoryDevice::createDir(std::string path) {
|
||||||
|
io::path filePath = path;
|
||||||
|
io::path parent = filePath.parent();
|
||||||
|
auto parentDir = getDir(parent.string());
|
||||||
|
if (!parentDir) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
parentDir->content.push_back(filePath.name());
|
||||||
|
auto& node = nodes[std::move(path)];
|
||||||
|
node.data = Dir {};
|
||||||
|
return node.get_if<Dir>();
|
||||||
|
}
|
||||||
|
|
||||||
|
io::MemoryDevice::Node* io::MemoryDevice::createFile(
|
||||||
|
std::string path, util::Buffer<char>&& content
|
||||||
|
) {
|
||||||
|
io::path filePath = path;
|
||||||
|
io::path parent = filePath.parent();
|
||||||
|
auto dir = getDir(parent.string());
|
||||||
|
if (!dir) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
dir->content.push_back(filePath.name());
|
||||||
|
auto& node = nodes[std::move(path)];
|
||||||
|
node.data = File {std::move(content)};
|
||||||
|
return &node;
|
||||||
|
}
|
||||||
|
|
||||||
|
io::MemoryDevice::Dir* io::MemoryDevice::getDir(std::string_view path) {
|
||||||
|
const auto& found = nodes.find(std::string(path));
|
||||||
|
if (found == nodes.end()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto& node = found->second;
|
||||||
|
return node.get_if<Dir>();
|
||||||
|
}
|
||||||
70
src/io/devices/MemoryDevice.hpp
Normal file
70
src/io/devices/MemoryDevice.hpp
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Device.hpp"
|
||||||
|
#include "util/Buffer.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <variant>
|
||||||
|
#include <optional>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace io {
|
||||||
|
/// @brief In-memory filesystem device
|
||||||
|
class MemoryDevice : public Device {
|
||||||
|
enum class NodeType {
|
||||||
|
DIR, FILE
|
||||||
|
};
|
||||||
|
|
||||||
|
struct File {
|
||||||
|
util::Buffer<char> content;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Dir {
|
||||||
|
std::vector<std::string> content;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Node {
|
||||||
|
std::variant<Dir, File> data;
|
||||||
|
|
||||||
|
NodeType type() const {
|
||||||
|
return std::visit([](auto&& arg) -> NodeType {
|
||||||
|
using T = std::decay_t<decltype(arg)>;
|
||||||
|
if constexpr (std::is_same_v<T, Dir>) return NodeType::DIR;
|
||||||
|
else if constexpr (std::is_same_v<T, File>) return NodeType::FILE;
|
||||||
|
}, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool holds_alternative() const {
|
||||||
|
return std::holds_alternative<T>(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T* get_if() {
|
||||||
|
return std::get_if<T>(&data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public:
|
||||||
|
MemoryDevice();
|
||||||
|
|
||||||
|
std::filesystem::path resolve(std::string_view path) override;
|
||||||
|
std::unique_ptr<std::ostream> write(std::string_view path) override;
|
||||||
|
std::unique_ptr<std::istream> read(std::string_view path) override;
|
||||||
|
size_t size(std::string_view path) override;
|
||||||
|
file_time_type lastWriteTime(std::string_view path) override;
|
||||||
|
bool exists(std::string_view path) override;
|
||||||
|
bool isdir(std::string_view path) override;
|
||||||
|
bool isfile(std::string_view path) override;
|
||||||
|
bool mkdir(std::string_view path) override;
|
||||||
|
bool mkdirs(std::string_view path) override;
|
||||||
|
bool remove(std::string_view path) override;
|
||||||
|
uint64_t removeAll(std::string_view path) override;
|
||||||
|
std::unique_ptr<PathsGenerator> list(std::string_view path) override;
|
||||||
|
private:
|
||||||
|
std::unordered_map<std::string, Node> nodes;
|
||||||
|
|
||||||
|
Node* createFile(std::string path, util::Buffer<char>&& content);
|
||||||
|
Dir* createDir(std::string path);
|
||||||
|
Dir* getDir(std::string_view path);
|
||||||
|
};
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user