libzarr
Header-only C++17 Zarr v2/v3, WASM-compatible
Loading...
Searching...
No Matches
store.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: MIT
2
3#ifndef LIBZARR_STORE_HPP
4#define LIBZARR_STORE_HPP
5
6#include <cstddef>
7#include <cstdint>
8#include <map>
9#include <optional>
10#include <string>
11#include <string_view>
12#include <utility>
13#include <vector>
14
15#include "libzarr/types.hpp"
16
24
25namespace zarr {
26
28struct ByteRange {
30 enum class Kind : std::uint8_t {
31 full,
32 slice,
33 suffix,
34 };
35
39 std::uint64_t offset = 0;
41 std::uint64_t length = 0;
42
44 [[nodiscard]] static constexpr ByteRange full() { return ByteRange{}; }
45
48 [[nodiscard]] static constexpr ByteRange slice(std::uint64_t offset, std::uint64_t length) {
50 }
51
55 [[nodiscard]] static constexpr ByteRange suffix(std::uint64_t length) {
56 return ByteRange{Kind::suffix, 0, length};
57 }
58};
59
64 std::string_view key;
67};
68
70struct DirListing {
72 std::vector<std::string> keys;
74 std::vector<std::string> prefixes;
75};
76
83class Store {
84 public:
85 Store() = default;
86 Store(const Store&) = delete;
87 Store& operator=(const Store&) = delete;
88 Store(Store&&) = delete;
89 Store& operator=(Store&&) = delete;
90 virtual ~Store() = default;
91
93 [[nodiscard]] virtual std::optional<Bytes> read(std::string_view key) = 0;
94
99 [[nodiscard]] virtual std::optional<Bytes> read_range(std::string_view key, ByteRange range);
100
104 [[nodiscard]] virtual std::optional<std::uint64_t> size(std::string_view key) {
105 const auto value = read(key);
106 if (!value) {
107 return std::nullopt;
108 }
109 return value->size();
110 }
111
118 [[nodiscard]] virtual std::vector<std::optional<Bytes>> read_many(
119 const std::vector<ReadRequest>& requests) {
120 std::vector<std::optional<Bytes>> out;
121 out.reserve(requests.size());
122 for (const ReadRequest& req : requests) {
123 out.push_back(read_range(req.key, req.range));
124 }
125 return out;
126 }
127
129 virtual void write(std::string_view key, Bytes value) = 0;
130
132 [[nodiscard]] virtual bool exists(std::string_view key) = 0;
133
135 virtual void erase(std::string_view key) = 0;
136
139 virtual void flush() {}
140
142 [[nodiscard]] virtual std::vector<std::string> list_prefix(std::string_view prefix) = 0;
143
145 [[nodiscard]] virtual DirListing list_dir(std::string_view prefix) = 0;
146};
147
150class MemoryStore final : public Store {
151 public:
152 [[nodiscard]] std::optional<Bytes> read(std::string_view key) override {
153 const auto it = map_.find(key);
154 if (it == map_.end()) {
155 return std::nullopt;
156 }
157 return it->second;
158 }
159
160 void write(std::string_view key, Bytes value) override {
161 map_.insert_or_assign(std::string(key), std::move(value));
162 }
163
164 [[nodiscard]] std::optional<std::uint64_t> size(std::string_view key) override {
165 const auto it = map_.find(key);
166 if (it == map_.end()) {
167 return std::nullopt;
168 }
169 return it->second.size();
170 }
171
172 [[nodiscard]] bool exists(std::string_view key) override { return map_.find(key) != map_.end(); }
173
174 void erase(std::string_view key) override {
175 const auto it = map_.find(key);
176 if (it != map_.end()) {
177 map_.erase(it);
178 }
179 }
180
181 [[nodiscard]] std::vector<std::string> list_prefix(std::string_view prefix) override {
182 check_prefix(prefix);
183 std::vector<std::string> out;
184 for (auto it = map_.lower_bound(prefix); it != map_.end() && starts_with(it->first, prefix);
185 ++it) {
186 out.push_back(it->first);
187 }
188 return out;
189 }
190
191 [[nodiscard]] DirListing list_dir(std::string_view prefix) override {
192 check_prefix(prefix);
193 DirListing out;
194 for (auto it = map_.lower_bound(prefix); it != map_.end() && starts_with(it->first, prefix);
195 ++it) {
196 const auto rest = std::string_view(it->first).substr(prefix.size());
197 const auto slash = rest.find('/');
198 if (slash == std::string_view::npos) {
199 out.keys.emplace_back(rest);
200 } else {
201 // Keys under one child prefix are contiguous in a sorted map, so
202 // comparing against the last emitted prefix deduplicates.
203 const auto child = rest.substr(0, slash);
204 if (out.prefixes.empty() || out.prefixes.back() != child) {
205 out.prefixes.emplace_back(child);
206 }
207 }
208 }
209 return out;
210 }
211
214 [[nodiscard]] std::size_t key_count() const { return map_.size(); }
215
216 private:
217 static bool starts_with(std::string_view text, std::string_view prefix) {
218 return text.size() >= prefix.size() && text.compare(0, prefix.size(), prefix) == 0;
219 }
220
221 static void check_prefix(std::string_view prefix) {
222 if (!prefix.empty() && prefix.back() != '/') {
223 throw error("store prefix must be empty or end with '/', got '" + std::string(prefix) + "'");
224 }
225 }
226
227 std::map<std::string, Bytes, std::less<>> map_;
228};
229
230inline std::optional<Bytes> Store::read_range(std::string_view key, ByteRange range) {
231 auto value = read(key);
232 if (!value) {
233 return std::nullopt;
234 }
235 const std::uint64_t size = value->size();
236 std::uint64_t begin = 0;
237 std::uint64_t count = 0;
238 switch (range.kind) {
240 return value;
242 if (range.length > size || range.offset > size - range.length) {
243 throw error("read_range: slice at offset " + std::to_string(range.offset) + " of length " +
244 std::to_string(range.length) + " out of bounds for \"" + std::string(key) +
245 "\" (" + std::to_string(size) + " bytes)");
246 }
247 begin = range.offset;
248 count = range.length;
249 break;
251 if (range.length > size) {
252 throw error("read_range: suffix of length " + std::to_string(range.length) +
253 " out of bounds for \"" + std::string(key) + "\" (" + std::to_string(size) +
254 " bytes)");
255 }
256 begin = size - range.length;
257 count = range.length;
258 break;
259 }
260 const auto first = value->begin() + static_cast<std::ptrdiff_t>(begin);
261 return Bytes(first, first + static_cast<std::ptrdiff_t>(count));
262}
263
264} // namespace zarr
265
266#endif // LIBZARR_STORE_HPP
Definition store.hpp:150
DirListing list_dir(std::string_view prefix) override
Immediate children under prefix ("" or ending in '/').
Definition store.hpp:191
void write(std::string_view key, Bytes value) override
Create or replace the value at key.
Definition store.hpp:160
std::optional< std::uint64_t > size(std::string_view key) override
Definition store.hpp:164
std::vector< std::string > list_prefix(std::string_view prefix) override
All keys starting with prefix ("" or ending in '/'), sorted.
Definition store.hpp:181
std::optional< Bytes > read(std::string_view key) override
Full value at key, or std::nullopt if the key is absent.
Definition store.hpp:152
std::size_t key_count() const
Definition store.hpp:214
bool exists(std::string_view key) override
True if key holds a value.
Definition store.hpp:172
void erase(std::string_view key) override
Remove key; removing an absent key is a no-op.
Definition store.hpp:174
Definition store.hpp:83
virtual bool exists(std::string_view key)=0
True if key holds a value.
virtual void flush()
Definition store.hpp:139
virtual void write(std::string_view key, Bytes value)=0
Create or replace the value at key.
virtual DirListing list_dir(std::string_view prefix)=0
Immediate children under prefix ("" or ending in '/').
virtual std::vector< std::optional< Bytes > > read_many(const std::vector< ReadRequest > &requests)
Definition store.hpp:118
virtual std::optional< Bytes > read_range(std::string_view key, ByteRange range)
Definition store.hpp:230
virtual std::optional< std::uint64_t > size(std::string_view key)
Definition store.hpp:104
virtual std::vector< std::string > list_prefix(std::string_view prefix)=0
All keys starting with prefix ("" or ending in '/'), sorted.
virtual void erase(std::string_view key)=0
Remove key; removing an absent key is a no-op.
virtual std::optional< Bytes > read(std::string_view key)=0
Full value at key, or std::nullopt if the key is absent.
Definition types.hpp:36
Byte-range request for Store::read_range.
Definition store.hpp:28
Kind
Which part of the value to read.
Definition store.hpp:30
@ suffix
the final length bytes
@ slice
length bytes starting at offset
@ full
the whole value
std::uint64_t offset
Start of the range; used by Kind::slice only.
Definition store.hpp:39
static constexpr ByteRange full()
The whole value.
Definition store.hpp:44
static constexpr ByteRange slice(std::uint64_t offset, std::uint64_t length)
Definition store.hpp:48
std::uint64_t length
Number of bytes; used by Kind::slice and Kind::suffix.
Definition store.hpp:41
static constexpr ByteRange suffix(std::uint64_t length)
Definition store.hpp:55
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
Definition store.hpp:62
std::string_view key
Key to read; must outlive the read_many call (as with any Store key).
Definition store.hpp:64
ByteRange range
Range within the value.
Definition store.hpp:66
std::vector< std::uint8_t > Bytes
Owned byte buffer used throughout the value-based public API.
Definition types.hpp:42