3#ifndef LIBZARR_ADAPTERS_FILESYSTEM_STORE_HPP
4#define LIBZARR_ADAPTERS_FILESYSTEM_STORE_HPP
14#include <system_error>
18#include "libzarr/detail/common.hpp"
34 : root_(std::move(root)) {
36 std::filesystem::create_directories(root_);
40 [[nodiscard]] std::optional<Bytes>
read(std::string_view key)
override {
41 std::ifstream in(key_path(key), std::ios::binary);
46 in.seekg(0, std::ios::end);
47 const std::streamoff
size = in.tellg();
48 in.seekg(0, std::ios::beg);
49 out.resize(
static_cast<std::size_t
>(
size));
50 in.read(
reinterpret_cast<char*
>(out.data()),
size);
52 throw error(
"failed to read '" + std::string(key) +
"'");
61 std::ifstream in(key_path(key), std::ios::binary);
65 in.seekg(0, std::ios::end);
66 const auto size =
static_cast<std::uint64_t
>(in.tellg());
67 std::uint64_t begin = 0;
70 throw error(
"read_range: slice at offset " + std::to_string(range.
offset) +
" of length " +
71 std::to_string(range.
length) +
" out of bounds for '" + std::string(key) +
72 "' (" + std::to_string(
size) +
" bytes)");
77 throw error(
"read_range: suffix of length " + std::to_string(range.
length) +
78 " out of bounds for '" + std::string(key) +
"' (" + std::to_string(
size) +
83 Bytes out(detail::checked_size(range.
length,
"read_range"));
84 in.seekg(
static_cast<std::streamoff
>(begin), std::ios::beg);
85 in.read(
reinterpret_cast<char*
>(out.data()),
static_cast<std::streamsize
>(out.size()));
87 throw error(
"failed to read range from '" + std::string(key) +
"'");
93 const std::filesystem::path path = key_path(key);
94 std::filesystem::create_directories(path.parent_path());
95 std::ofstream out(path, std::ios::binary | std::ios::trunc);
97 throw error(
"cannot open '" + std::string(key) +
"' for writing");
99 out.write(
reinterpret_cast<const char*
>(value.data()),
100 static_cast<std::streamsize
>(value.size()));
102 throw error(
"failed to write '" + std::string(key) +
"'");
106 [[nodiscard]] std::optional<std::uint64_t>
size(std::string_view key)
override {
108 const auto bytes = std::filesystem::file_size(key_path(key), ec);
115 [[nodiscard]]
bool exists(std::string_view key)
override {
116 return std::filesystem::is_regular_file(key_path(key));
119 void erase(std::string_view key)
override {
121 std::filesystem::remove(key_path(key), ec);
124 [[nodiscard]] std::vector<std::string>
list_prefix(std::string_view prefix)
override {
125 check_prefix(prefix);
126 std::vector<std::string> out;
127 if (!std::filesystem::is_directory(root_)) {
130 for (
const auto& entry : std::filesystem::recursive_directory_iterator(root_)) {
131 if (!entry.is_regular_file()) {
134 const std::string key = std::filesystem::relative(entry.path(), root_).generic_string();
135 if (detail::starts_with(key, prefix)) {
139 std::sort(out.begin(), out.end());
144 check_prefix(prefix);
146 std::filesystem::path dir = root_;
147 if (!prefix.empty()) {
148 dir /= std::filesystem::path(prefix.substr(0, prefix.size() - 1));
150 if (!std::filesystem::is_directory(dir)) {
153 for (
const auto& entry : std::filesystem::directory_iterator(dir)) {
154 const std::string name = entry.path().filename().generic_string();
155 if (entry.is_regular_file()) {
156 out.
keys.push_back(name);
157 }
else if (entry.is_directory()) {
161 std::sort(out.
keys.begin(), out.
keys.end());
167 static void check_prefix(std::string_view prefix) {
168 if (!prefix.empty() && prefix.back() !=
'/') {
169 throw error(
"store prefix must be empty or end with '/', got '" + std::string(prefix) +
"'");
173 [[nodiscard]] std::filesystem::path key_path(std::string_view key)
const {
175 throw error(
"store key must not be empty");
177 std::filesystem::path path = root_;
178 std::size_t start = 0;
179 while (start <= key.size()) {
180 const std::size_t slash = key.find(
'/', start);
181 const std::size_t end = slash == std::string_view::npos ? key.size() : slash;
182 const std::string_view segment = key.substr(start, end - start);
184 if (segment.empty() || segment ==
"." || segment ==
"..") {
185 throw error(
"invalid store key '" + std::string(key) +
"'");
188 if (slash == std::string_view::npos) {
196 std::filesystem::path root_;
Store mapping keys to files under a root directory.
Definition filesystem_store.hpp:30
bool exists(std::string_view key) override
True if key holds a value.
Definition filesystem_store.hpp:115
std::optional< Bytes > read(std::string_view key) override
Full value at key, or std::nullopt if the key is absent.
Definition filesystem_store.hpp:40
std::optional< Bytes > read_range(std::string_view key, ByteRange range) override
Definition filesystem_store.hpp:57
std::optional< std::uint64_t > size(std::string_view key) override
Definition filesystem_store.hpp:106
DirListing list_dir(std::string_view prefix) override
Immediate children under prefix ("" or ending in '/').
Definition filesystem_store.hpp:143
FilesystemStore(std::filesystem::path root, bool create=true)
Binds to root, creating the directory when create is true.
Definition filesystem_store.hpp:33
void erase(std::string_view key) override
Remove key; removing an absent key is a no-op.
Definition filesystem_store.hpp:119
void write(std::string_view key, Bytes value) override
Create or replace the value at key.
Definition filesystem_store.hpp:92
std::vector< std::string > list_prefix(std::string_view prefix) override
All keys starting with prefix ("" or ending in '/'), sorted.
Definition filesystem_store.hpp:124
Byte-range request for Store::read_range.
Definition store.hpp:28
@ slice
length bytes starting at offset
std::uint64_t offset
Start of the range; used by Kind::slice only.
Definition store.hpp:39
std::uint64_t length
Number of bytes; used by Kind::slice and Kind::suffix.
Definition store.hpp:41
Kind kind
Which part of the value to read.
Definition store.hpp:37
Immediate children of a prefix, as returned by Store::list_dir.
Definition store.hpp:70
std::vector< std::string > keys
Child keys, relative to the queried prefix, sorted.
Definition store.hpp:72
std::vector< std::string > prefixes
Child prefixes ("directories"), relative, without trailing '/', sorted.
Definition store.hpp:74
std::vector< std::uint8_t > Bytes
Owned byte buffer used throughout the value-based public API.
Definition types.hpp:42