alistair
11 months ago
3 changed files with 266 additions and 0 deletions
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
|
||||
#pragma once |
||||
|
||||
#include <tl/expected.hpp> |
||||
|
||||
using namespace tl; |
@ -0,0 +1,32 @@
@@ -0,0 +1,32 @@
|
||||
|
||||
#include <optional> |
||||
#include <random> |
||||
|
||||
enum random_generator { |
||||
INODE, |
||||
}; |
||||
|
||||
template <auto instance> struct singleton { |
||||
std::optional<decltype(instance)> inner; |
||||
|
||||
singleton() { |
||||
if (!inner.has_value()) { |
||||
inner = instance; |
||||
} |
||||
} |
||||
}; |
||||
|
||||
namespace rng { |
||||
|
||||
template <typename T> struct mersenne_rng { |
||||
static std::random_device device; |
||||
static std::mt19937_64 generator; |
||||
|
||||
mersenne_rng() { generator = std::mt19937_64(device); } |
||||
|
||||
T next() { return generator(); } |
||||
}; |
||||
|
||||
singleton<mersenne_rng<int64_t>{}> rand; |
||||
|
||||
}; // namespace rng
|
@ -0,0 +1,228 @@
@@ -0,0 +1,228 @@
|
||||
#include "expected.hpp" |
||||
#include <SDL2/SDL_rwops.h> |
||||
#include <filesystem> |
||||
#include <fmt/format.h> |
||||
#include <gsl/span> |
||||
#include <inttypes.h> |
||||
#include <optional> |
||||
#include <random> |
||||
#include <sstream> |
||||
#include <string> |
||||
#include <unordered_map> |
||||
#include <vector> |
||||
|
||||
namespace vfs { |
||||
|
||||
struct error { |
||||
std::string message; |
||||
}; |
||||
|
||||
struct vfs_mount {}; |
||||
|
||||
using vpath = std::string; |
||||
|
||||
enum fnode_type { MOUNT, DIR, DATA, DEV }; |
||||
|
||||
struct vfnode { |
||||
static std::mt19937_64 gen64; |
||||
|
||||
uint64_t id; |
||||
fnode_type type; |
||||
|
||||
vfnode(fnode_type type) : id(gen64()), type(type) {} |
||||
vfnode(uint64_t id, fnode_type type) : id(id), type(type) {} |
||||
}; |
||||
|
||||
template <typename T> struct file_node { |
||||
virtual expected<size_t, error> size(); |
||||
virtual expected<size_t, error> seek_from_end(); |
||||
virtual expected<size_t, error> seek_from_start(); |
||||
virtual expected<size_t, error> seek_from_current(); |
||||
virtual expected<size_t, error> tell(); |
||||
virtual bool can_read(); |
||||
virtual bool valid(); |
||||
virtual bool can_write(); |
||||
virtual expected<std::vector<T>, error> read(); |
||||
virtual bool write(std::span<T> data); |
||||
virtual bool close(); |
||||
}; |
||||
|
||||
template <typename T> class caching_file : public file_node<T> { |
||||
private: |
||||
file_node<T> node; |
||||
}; |
||||
|
||||
template <typename T> class sdl_file_node : public file_node<T> { |
||||
private: |
||||
SDL_RWops *ops; |
||||
const char *last_error = ""; |
||||
// https://wiki.libsdl.org/SDL2/SDL_RWops
|
||||
|
||||
expected<size_t, error> seek_sdl(size_t offset, int whence) { |
||||
if (ops == nullptr) |
||||
return unexpected(error("Invalid file")); |
||||
int ret = ops->seek(ops, offset, RW_SEEK_END); |
||||
if (ret == -1) { |
||||
last_error = SDL_GetError(); |
||||
return unexpected(error(fmt::format("Error {}", whence))); |
||||
} |
||||
} |
||||
|
||||
public: |
||||
sdl_file_node(const std::string &path, const std::string &mode) { |
||||
ops = SDL_RWFromFile(path.c_str(), mode.c_str()); |
||||
if (ops == nullptr) |
||||
last_error = SDL_GetError(); |
||||
} |
||||
|
||||
sdl_file_node(const gsl::span<T> &memory) { |
||||
// https://wiki.libsdl.org/SDL2/SDL_RWFromMem
|
||||
ops = SDL_RWFromMem(memory.data(), memory.size_bytes()); |
||||
if (ops == nullptr) |
||||
last_error = SDL_GetError(); |
||||
} |
||||
|
||||
bool valid() { return ops != nullptr; } |
||||
|
||||
expected<size_t, error> size() { |
||||
if (ops == nullptr) |
||||
return unexpected( |
||||
error(fmt::format("Invalid file {}", std::string(last_error)))); |
||||
|
||||
int ret = ops->size(ops); |
||||
if (ret == -1) { |
||||
last_error = SDL_GetError(); |
||||
return unexpected(error(fmt::format("Error {}", last_error))); |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
public: |
||||
expected<size_t, error> seek_from_end(size_t offset) { |
||||
return seek_sdl(offset, RW_SEEK_END); |
||||
} |
||||
|
||||
expected<size_t, error> seek_from_current(size_t offset) { |
||||
return seek_sdl(offset, RW_SEEK_CUR); |
||||
} |
||||
|
||||
expected<size_t, error> seek_from_start(size_t offset) { |
||||
return seek_sdl(offset, RW_SEEK_SET); |
||||
} |
||||
|
||||
expected<std::vector<T>, error> read(size_t offset_from_start, |
||||
size_t length) { |
||||
if (ops == nullptr) |
||||
return unexpected(error("Invalid file")); |
||||
|
||||
std::vector<T> chunks{}; |
||||
chunks.reserve(length); |
||||
|
||||
int chunks_read = ops->read(ops, chunks.data(), sizeof(T), length); |
||||
if (chunks_read == 0) { |
||||
return unexpected(error("Read error")); |
||||
} |
||||
// may be less than length
|
||||
chunks.resize(chunks_read); |
||||
|
||||
auto t = gsl::span(chunks); |
||||
return chunks; |
||||
} |
||||
|
||||
bool write(std::span<T> elems) { |
||||
if (ops == nullptr) |
||||
return true; |
||||
ops->write(ops, elems.data(), sizeof(T), elems.size()); |
||||
} |
||||
|
||||
bool close() { |
||||
if (ops == nullptr) |
||||
return false; |
||||
|
||||
int res = ops->close(ops); |
||||
ops = nullptr; |
||||
return res; |
||||
} |
||||
|
||||
bool can_read() { return ops != nullptr; } |
||||
bool can_write() { return ops != nullptr; } |
||||
bool eof() { return ops != nullptr; } |
||||
}; |
||||
|
||||
struct directory { |
||||
vfnode node; |
||||
std::unordered_map<std::string, vfnode> children; |
||||
}; |
||||
|
||||
struct fdata { |
||||
// read whole file
|
||||
// refresh cache
|
||||
// subscribe modify events
|
||||
// return iterator
|
||||
vfnode node; |
||||
std::vector<unsigned char> read_all(); |
||||
void write_all(const std::vector<unsigned char> data); |
||||
void write_subrange(const std::vector<unsigned char> data, |
||||
std::pair<int, int> bounds); |
||||
}; |
||||
|
||||
struct mount { |
||||
std::vector<vfnode> nodes; |
||||
std::unordered_map<std::string, vfnode> index; |
||||
}; |
||||
|
||||
struct directory_mount : public mount { |
||||
std::filesystem::path directory; |
||||
std::vector<vfnode> nodes; |
||||
std::unordered_map<vpath, vfnode> index; |
||||
|
||||
directory_mount(std::filesystem::path directory) : directory(directory) {} |
||||
|
||||
std::optional<vfnode> at(const vpath &location) { |
||||
std::filesystem::path loc = directory / location; |
||||
|
||||
// query for known
|
||||
if (index.contains(location)) { |
||||
return index.at(location); |
||||
} |
||||
|
||||
// query for existing
|
||||
if (!std::filesystem::exists(loc)) { |
||||
return {}; |
||||
} |
||||
|
||||
if (std::filesystem::is_directory(loc)) { |
||||
nodes.emplace_back(DIR); |
||||
index.insert({location, nodes.back()}); |
||||
return nodes.back(); |
||||
} |
||||
|
||||
if (std::filesystem::is_regular_file(loc)) { |
||||
nodes.emplace_back(DATA); |
||||
index.insert({location, nodes.back()}); |
||||
return nodes.back(); |
||||
} |
||||
|
||||
return {}; |
||||
} |
||||
|
||||
void create(const vpath &location) { |
||||
auto node = at(location); |
||||
|
||||
if (node.has_value()) { |
||||
throw std::runtime_error("Exists"); |
||||
} |
||||
} |
||||
|
||||
void destroy(const vpath &location) {} |
||||
}; |
||||
|
||||
/**
|
||||
* Specialisations |
||||
* |
||||
* - raw directory heirachy |
||||
* - #embed files |
||||
* - zip file |
||||
* - sqlar |
||||
*/ |
||||
}; // namespace vfs
|
Loading…
Reference in new issue