libzarr
Header-only C++17 Zarr v2/v3, WASM-compatible
Loading...
Searching...
No Matches
array.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: MIT
2
3#ifndef LIBZARR_ARRAY_HPP
4#define LIBZARR_ARRAY_HPP
5
6#include <algorithm>
7#include <cstdint>
8#include <cstring>
9#include <memory>
10#include <optional>
11#include <string>
12#include <utility>
13#include <vector>
14
15#include "libzarr/codecs.hpp"
16#include "libzarr/detail/common.hpp"
17#include "libzarr/metadata.hpp"
18#include "libzarr/sharding.hpp"
19#include "libzarr/store.hpp"
20#include "libzarr/types.hpp"
21#include "libzarr/v2.hpp"
22#include "libzarr/v3.hpp"
23
28
29namespace zarr {
30
31namespace detail {
32
35inline void validate_path(const std::string& path) {
36 if (path.empty()) {
37 return;
38 }
39 if (path.front() == '/' || path.back() == '/') {
40 throw error("node path must not start or end with '/': '" + path + "'");
41 }
42 std::size_t start = 0;
43 while (start <= path.size()) {
44 const std::size_t slash = path.find('/', start);
45 const std::size_t end = slash == std::string::npos ? path.size() : slash;
46 if (end == start) {
47 throw error("node path has an empty segment: '" + path + "'");
48 }
49 if (path[start] == '.') {
50 throw error("node path segments must not start with '.': '" + path + "'");
51 }
52 if (slash == std::string::npos) {
53 break;
54 }
55 start = slash + 1;
56 }
57}
58
61inline bool next_index(std::vector<std::uint64_t>& index,
62 const std::vector<std::uint64_t>& extents) {
63 std::size_t d = extents.size();
64 while (d-- > 0) {
65 if (++index[d] < extents[d]) {
66 return true;
67 }
68 index[d] = 0;
69 }
70 return false;
71}
72
75inline bool next_index_box(std::vector<std::uint64_t>& index, const std::vector<std::uint64_t>& lo,
76 const std::vector<std::uint64_t>& hi) {
77 std::size_t d = lo.size();
78 while (d-- > 0) {
79 if (index[d] < hi[d]) {
80 ++index[d];
81 return true;
82 }
83 index[d] = lo[d];
84 }
85 return false;
86}
87
88} // namespace detail
89
91struct ArraySpec {
93 ZarrFormat format = ZarrFormat::v2;
95 std::vector<std::uint64_t> shape;
97 std::vector<std::uint64_t> chunks;
103 std::vector<CodecSpec> codecs;
105 std::optional<Bytes> fill;
107 json attributes = json::object();
116 std::vector<std::uint64_t> shards;
117};
118
121class Array {
122 public:
126 static Array create(std::shared_ptr<Store> store, const std::string& path,
127 const ArraySpec& spec) {
128 if (!store) {
129 throw error("Array::create: null store");
130 }
131 detail::validate_path(path);
132 const bool v3 = spec.format == ZarrFormat::v3;
133 const std::string ctx = v3 ? v3::meta_key(path) : v2::meta_key(path, v2::kArraySuffix);
134 if (spec.chunks.size() != spec.shape.size()) {
135 throw error(ctx + ": chunks rank " + std::to_string(spec.chunks.size()) + " != shape rank " +
136 std::to_string(spec.shape.size()));
137 }
138 for (const std::uint64_t c : spec.chunks) {
139 if (c == 0) {
140 throw error(ctx + ": chunk extents must be positive");
141 }
142 }
143 if (spec.dimension_separator != '.' && spec.dimension_separator != '/') {
144 throw error(ctx + ": dimension_separator must be '.' or '/'");
145 }
146
148 meta.format = spec.format;
149 meta.shape = spec.shape;
150 meta.chunk_shape = spec.chunks;
151 meta.dtype = spec.dtype;
153 apply_format_members(spec, meta, ctx);
154 if (spec.fill) {
155 if (spec.fill->size() != spec.dtype.itemsize) {
156 throw error(ctx + ": fill is " + std::to_string(spec.fill->size()) +
157 " bytes, dtype needs " + std::to_string(spec.dtype.itemsize));
158 }
159 meta.fill = spec.fill;
160 } else {
161 meta.fill = Bytes(spec.dtype.itemsize, 0); // canonical default: zeros
162 }
163 meta.codecs.push_back({"bytes", {{"endian", "little"}}});
164 for (const CodecSpec& codec : spec.codecs) {
165 meta.codecs.push_back(codec);
166 }
167
168 Array array(std::move(store), path, std::move(meta)); // resolves + validates codecs
169 if (v3) {
170 array.store_->write(v3::meta_key(path),
171 canonical_json_bytes(v3::emit_array_meta(array.meta_)));
172 } else {
173 v2::write_meta_key(*array.store_, array.meta_store_key(), v2::emit_array_meta(array.meta_));
174 array.write_attributes();
175 }
176 return array;
177 }
178
181 [[nodiscard]] static Array open(std::shared_ptr<Store> store, const std::string& path,
182 OpenOptions options = {}) {
183 return open_impl(std::move(store), path, options, nullptr);
184 }
185
187 [[nodiscard]] const ArrayMeta& meta() const { return meta_; }
189 [[nodiscard]] const std::string& path() const { return path_; }
191 [[nodiscard]] std::vector<std::uint64_t> grid_shape() const { return meta_.grid_shape(); }
193 [[nodiscard]] std::uint64_t nbytes() const {
194 return meta_.element_count() * meta_.dtype.itemsize;
195 }
197 [[nodiscard]] std::uint64_t chunk_nbytes() const { return pipeline_.decoded_chunk_bytes(); }
198
201 [[nodiscard]] Bytes read_chunk(const std::vector<std::uint64_t>& index) const {
202 auto stored = chunk_store_->read(chunk_store_key(index));
203 if (!stored) {
204 return filled_chunk();
205 }
206 return pipeline_.decode(std::move(*stored));
207 }
208
211 void write_chunk(const std::vector<std::uint64_t>& index, const void* data, std::size_t size) {
212 Bytes chunk(static_cast<const std::uint8_t*>(data),
213 static_cast<const std::uint8_t*>(data) + size);
214 chunk_store_->write(chunk_store_key(index), pipeline_.encode(std::move(chunk)));
215 chunk_store_->flush();
216 }
217
223 [[nodiscard]] Bytes read_chunk_range(const std::vector<std::uint64_t>& index,
224 std::uint64_t element_offset,
225 std::uint64_t element_count) const {
226 if (!pipeline_.supports_partial_read()) {
227 throw error("byte-range chunk reads need an uncompressed, untransposed layout");
228 }
229 const std::uint32_t itemsize = meta_.dtype.itemsize;
230 const std::uint64_t chunk_elements = meta_.chunk_element_count();
231 if (element_count > chunk_elements || element_offset > chunk_elements - element_count) {
232 throw error("chunk range [" + std::to_string(element_offset) + ", +" +
233 std::to_string(element_count) + ") exceeds " + std::to_string(chunk_elements) +
234 " elements");
235 }
236 auto stored = chunk_store_->read_range(
237 chunk_store_key(index),
238 ByteRange::slice(element_offset * itemsize, element_count * itemsize));
239 if (!stored) {
240 Bytes out(detail::checked_size(element_count * itemsize, "chunk range"));
241 detail::fill_elements(out.data(), element_count, meta_.fill ? meta_.fill->data() : nullptr,
242 itemsize);
243 return out;
244 }
245 return pipeline_.decode_range(std::move(*stored));
246 }
247
250 void read(void* dst, std::size_t size) const {
251 read_region(std::vector<std::uint64_t>(meta_.shape.size(), 0), meta_.shape, dst, size);
252 }
253
256 void write(const void* src, std::size_t size) {
257 write_region(std::vector<std::uint64_t>(meta_.shape.size(), 0), meta_.shape, src, size);
258 }
259
263 void read_region(const std::vector<std::uint64_t>& origin,
264 const std::vector<std::uint64_t>& shape, void* dst, std::size_t size) const {
265 validate_region(origin, shape, size, "read_region");
266 if (size == 0) {
267 return;
268 }
269 auto* out = static_cast<std::uint8_t*>(dst);
270 for_each_region_chunk(origin, shape, [&](const RegionChunk& rc) {
271 const Bytes chunk = read_chunk(rc.index);
272 detail::copy_box(chunk.data(), meta_.chunk_shape, rc.origin_in_chunk, out, shape,
273 rc.origin_in_region, rc.box, meta_.dtype.itemsize);
274 });
275 }
276
281 void write_region(const std::vector<std::uint64_t>& origin,
282 const std::vector<std::uint64_t>& shape, const void* src, std::size_t size) {
283 validate_region(origin, shape, size, "write_region");
284 if (size == 0) {
285 return;
286 }
287 const auto* in = static_cast<const std::uint8_t*>(src);
288 for_each_region_chunk(origin, shape, [&](const RegionChunk& rc) {
289 Bytes chunk = rc.covered ? filled_chunk() : read_chunk(rc.index);
290 detail::copy_box(in, shape, rc.origin_in_region, chunk.data(), meta_.chunk_shape,
291 rc.origin_in_chunk, rc.box, meta_.dtype.itemsize);
292 chunk_store_->write(chunk_store_key(rc.index), pipeline_.encode(std::move(chunk)));
293 });
294 chunk_store_->flush();
295 }
296
298 [[nodiscard]] const json& attributes() const { return meta_.attributes; }
299
303 meta_.attributes = std::move(attributes);
304 if (meta_.format == ZarrFormat::v3) {
305 const std::string key = v3::meta_key(path_);
306 const auto bytes = store_->read(key);
307 if (!bytes) {
308 throw error(key + ": metadata disappeared");
309 }
310 json doc = detail::parse_json(*bytes, key);
311 if (meta_.attributes.empty()) {
312 doc.erase("attributes");
313 } else {
314 doc["attributes"] = meta_.attributes;
315 }
316 store_->write(key, canonical_json_bytes(doc));
317 return;
318 }
319 write_attributes();
320 }
321
323 [[nodiscard]] std::string chunk_store_key(const std::vector<std::uint64_t>& index) const {
324 const auto grid = meta_.grid_shape();
325 if (index.size() != grid.size()) {
326 throw error("chunk index rank " + std::to_string(index.size()) + " != array rank " +
327 std::to_string(grid.size()));
328 }
329 for (std::size_t d = 0; d < grid.size(); ++d) {
330 if (index[d] >= grid[d]) {
331 throw error("chunk index " + std::to_string(index[d]) + " out of range for dimension " +
332 std::to_string(d) + " (grid extent " + std::to_string(grid[d]) + ")");
333 }
334 }
335 const std::string relative = meta_.key_encoding == ChunkKeyKind::v3_default
336 ? v3::chunk_key(index, meta_.dimension_separator)
337 : v2::chunk_key(index, meta_.dimension_separator);
338 return path_.empty() ? relative : path_ + "/" + relative;
339 }
340
341 private:
342 friend class Group;
343
347 static Array open_impl(std::shared_ptr<Store> store, const std::string& path, OpenOptions options,
348 const std::shared_ptr<const json>& consolidated) {
349 if (!store) {
350 throw error("Array::open: null store");
351 }
352 detail::validate_path(path);
353 const auto read_doc = [&](const std::string& key) -> std::optional<json> {
354 if (consolidated) {
355 const auto it = consolidated->find(key);
356 if (it == consolidated->end()) {
357 return std::nullopt;
358 }
359 return *it;
360 }
361 const auto bytes = store->read(key);
362 if (!bytes) {
363 return std::nullopt;
364 }
365 return detail::parse_json(*bytes, key);
366 };
367
368 // Probe order: zarr.json first, so v3 opens cost one round-trip.
369 const std::string v3_key = v3::meta_key(path);
370 if (const auto doc = read_doc(v3_key)) {
371 if (doc->is_object() && doc->value("node_type", "") == std::string("group")) {
372 throw error("'" + path + "' is a group, not an array");
373 }
374 return {std::move(store), path, v3::parse_array_meta(*doc, v3_key, options.lenient)};
375 }
376
377 const std::string meta_key = v2::meta_key(path, v2::kArraySuffix);
378 auto doc = read_doc(meta_key);
379 if (!doc) {
380 if (store->exists(v2::meta_key(path, v2::kGroupSuffix))) {
381 throw error("'" + path + "' is a group, not an array");
382 }
383 throw error("no array at '" + path + "' (neither " + v3_key + " nor " + meta_key + " found)");
384 }
385 ArrayMeta meta = v2::parse_array_meta(*doc, meta_key);
386 if (const auto attrs = read_doc(v2::meta_key(path, v2::kAttrsSuffix))) {
387 meta.attributes = *attrs;
388 }
389 return {std::move(store), path, std::move(meta)};
390 }
391
394 static void apply_format_members(const ArraySpec& spec, ArrayMeta& meta, const std::string& ctx) {
395 if (spec.format != ZarrFormat::v3) {
396 meta.dimension_separator = spec.dimension_separator;
397 if (!spec.dimension_names.is_null()) {
398 throw error(ctx + ": dimension_names is a v3 feature");
399 }
400 if (!spec.shards.empty()) {
401 throw error(ctx + ": sharding is a v3 feature");
402 }
403 return;
404 }
405 // Canonical v3 creation: the "default" chunk-key encoding with '/'.
406 meta.key_encoding = ChunkKeyKind::v3_default;
408 if (spec.dimension_names.is_array()) {
409 if (spec.dimension_names.size() != spec.shape.size()) {
410 throw error(ctx + ": dimension_names must have rank length");
411 }
412 meta.dimension_names = spec.dimension_names;
413 }
414 if (spec.shards.empty()) {
415 return;
416 }
417 if (spec.shards.size() != spec.chunks.size()) {
418 throw error(ctx + ": shards rank must match chunks rank");
419 }
420 for (std::size_t d = 0; d < spec.shards.size(); ++d) {
421 // v3 sharding spec: chunks must evenly divide the shard.
422 if (spec.shards[d] == 0 || spec.shards[d] % spec.chunks[d] != 0) {
423 throw error(ctx + ": each shard extent must be a positive multiple of the chunk extent");
424 }
425 }
426 ShardLevel level;
427 level.shard_shape = spec.shards;
428 level.index_codecs = {{"bytes", {{"endian", "little"}}}, {"crc32c", {}}};
429 meta.shard_levels.push_back(std::move(level));
430 }
431
432 Array(std::shared_ptr<Store> store, std::string path, ArrayMeta meta)
433 : store_(std::move(store)),
434 path_(std::move(path)),
435 meta_(std::move(meta)),
436 pipeline_(CodecPipeline::resolve(meta_)),
437 chunk_store_(wrap_shards(store_, meta_, path_)) {
438 // Materializing a chunk must be possible on this platform (wasm32!).
439 detail::checked_size(pipeline_.decoded_chunk_bytes(), "chunk");
440 }
441
445 static std::shared_ptr<Store> wrap_shards(std::shared_ptr<Store> store, const ArrayMeta& meta,
446 const std::string& path) {
447 std::shared_ptr<Store> chunks = std::move(store);
448 const std::string prefix = path.empty() ? "" : path + "/";
449 for (std::size_t i = 0; i < meta.shard_levels.size(); ++i) {
450 chunks = std::make_shared<detail_shard::ShardStore>(
451 std::move(chunks), detail_shard::params_for_level(meta, i, prefix));
452 }
453 return chunks;
454 }
455
456 [[nodiscard]] std::string meta_store_key() const { return v2::meta_key(path_, v2::kArraySuffix); }
457
458 void write_attributes() {
459 const std::string key = v2::meta_key(path_, v2::kAttrsSuffix);
460 if (meta_.attributes.empty()) {
461 v2::erase_meta_key(*store_, key); // canonical: no empty .zattrs documents
462 } else {
463 v2::write_meta_key(*store_, key, meta_.attributes);
464 }
465 }
466
467 [[nodiscard]] Bytes filled_chunk() const {
468 Bytes chunk(detail::checked_size(pipeline_.decoded_chunk_bytes(), "chunk"));
469 detail::fill_elements(chunk.data(), meta_.chunk_element_count(),
470 meta_.fill ? meta_.fill->data() : nullptr, meta_.dtype.itemsize);
471 return chunk;
472 }
473
475 struct RegionChunk {
476 std::vector<std::uint64_t> index;
477 std::vector<std::uint64_t> origin_in_chunk;
478 std::vector<std::uint64_t> origin_in_region;
479 std::vector<std::uint64_t> box;
482 bool covered = true;
483 };
484
485 void validate_region(const std::vector<std::uint64_t>& origin,
486 const std::vector<std::uint64_t>& shape, std::size_t size,
487 const char* what) const {
488 const std::size_t rank = meta_.shape.size();
489 if (origin.size() != rank || shape.size() != rank) {
490 throw error(std::string(what) + ": origin/shape rank must be " + std::to_string(rank));
491 }
492 for (std::size_t d = 0; d < rank; ++d) {
493 if (shape[d] > meta_.shape[d] || origin[d] > meta_.shape[d] - shape[d]) {
494 throw error(std::string(what) + ": region [" + std::to_string(origin[d]) + ", " +
495 std::to_string(origin[d]) + "+" + std::to_string(shape[d]) +
496 ") exceeds dimension " + std::to_string(d) + " (extent " +
497 std::to_string(meta_.shape[d]) + ")");
498 }
499 }
500 const std::uint64_t bytes = detail::checked_product(shape, what) * meta_.dtype.itemsize;
501 if (size != detail::checked_size(bytes, what)) {
502 throw error(std::string(what) + ": buffer is " + std::to_string(size) +
503 " bytes, region needs " + std::to_string(bytes));
504 }
505 }
506
514 template <typename Fn>
515 void for_each_region_chunk(const std::vector<std::uint64_t>& origin,
516 const std::vector<std::uint64_t>& shape, const Fn& fn) const {
517 const std::size_t rank = meta_.shape.size();
518 std::vector<std::uint64_t> first(rank, 0);
519 std::vector<std::uint64_t> last(rank, 0);
520 for (std::size_t d = 0; d < rank; ++d) {
521 first[d] = origin[d] / meta_.chunk_shape[d];
522 last[d] = (origin[d] + shape[d] - 1) / meta_.chunk_shape[d];
523 }
524
525 RegionChunk rc;
526 rc.origin_in_chunk.assign(rank, 0);
527 rc.origin_in_region.assign(rank, 0);
528 rc.box.assign(rank, 0);
529 visit_shard_major(0, first, last, [&](const std::vector<std::uint64_t>& index) {
530 rc.index = index;
531 rc.covered = true;
532 for (std::size_t d = 0; d < rank; ++d) {
533 const std::uint64_t chunk_start = rc.index[d] * meta_.chunk_shape[d];
534 const std::uint64_t valid_end =
535 std::min(chunk_start + meta_.chunk_shape[d], meta_.shape[d]);
536 const std::uint64_t begin = std::max(chunk_start, origin[d]);
537 const std::uint64_t end = std::min(valid_end, origin[d] + shape[d]);
538 rc.origin_in_chunk[d] = begin - chunk_start;
539 rc.origin_in_region[d] = begin - origin[d];
540 rc.box[d] = end - begin;
541 rc.covered = rc.covered && begin == chunk_start && end == valid_end;
542 }
543 fn(rc);
544 });
545 }
546
551 template <typename Leaf>
552 void visit_shard_major(std::size_t level, const std::vector<std::uint64_t>& lo,
553 const std::vector<std::uint64_t>& hi, const Leaf& leaf) const {
554 const std::size_t rank = lo.size();
555 if (level < meta_.shard_levels.size()) {
556 // Level-`level` shard extents in chunk-grid units; shard_shape is a
557 // validated multiple of chunk_shape.
558 std::vector<std::uint64_t> per(rank, 1);
559 std::vector<std::uint64_t> shard_lo(rank, 0);
560 std::vector<std::uint64_t> shard_hi(rank, 0);
561 for (std::size_t d = 0; d < rank; ++d) {
562 per[d] = meta_.shard_levels[level].shard_shape[d] / meta_.chunk_shape[d];
563 shard_lo[d] = lo[d] / per[d];
564 shard_hi[d] = hi[d] / per[d];
565 }
566 std::vector<std::uint64_t> shard = shard_lo;
567 std::vector<std::uint64_t> sub_lo(rank, 0);
568 std::vector<std::uint64_t> sub_hi(rank, 0);
569 while (true) {
570 for (std::size_t d = 0; d < rank; ++d) {
571 sub_lo[d] = std::max(lo[d], shard[d] * per[d]);
572 sub_hi[d] = std::min(hi[d], shard[d] * per[d] + per[d] - 1);
573 }
574 visit_shard_major(level + 1, sub_lo, sub_hi, leaf);
575 if (!detail::next_index_box(shard, shard_lo, shard_hi)) {
576 return;
577 }
578 }
579 }
580 std::vector<std::uint64_t> index = lo;
581 while (true) {
582 leaf(index);
583 if (!detail::next_index_box(index, lo, hi)) {
584 return;
585 }
586 }
587 }
588
589 std::shared_ptr<Store> store_;
590 std::string path_;
591 ArrayMeta meta_;
592 CodecPipeline pipeline_;
593 std::shared_ptr<Store> chunk_store_;
594};
595
596} // namespace zarr
597
598#endif // LIBZARR_ARRAY_HPP
Definition array.hpp:121
Bytes read_chunk_range(const std::vector< std::uint64_t > &index, std::uint64_t element_offset, std::uint64_t element_count) const
Definition array.hpp:223
static Array create(std::shared_ptr< Store > store, const std::string &path, const ArraySpec &spec)
Definition array.hpp:126
Bytes read_chunk(const std::vector< std::uint64_t > &index) const
Definition array.hpp:201
void write_chunk(const std::vector< std::uint64_t > &index, const void *data, std::size_t size)
Definition array.hpp:211
void read(void *dst, std::size_t size) const
Definition array.hpp:250
void write(const void *src, std::size_t size)
Definition array.hpp:256
void read_region(const std::vector< std::uint64_t > &origin, const std::vector< std::uint64_t > &shape, void *dst, std::size_t size) const
Definition array.hpp:263
void write_region(const std::vector< std::uint64_t > &origin, const std::vector< std::uint64_t > &shape, const void *src, std::size_t size)
Definition array.hpp:281
const ArrayMeta & meta() const
Normalized metadata (shape, chunks, dtype, codecs, attributes).
Definition array.hpp:187
std::uint64_t chunk_nbytes() const
One full chunk's size in bytes.
Definition array.hpp:197
const std::string & path() const
Node path within the store ("" = root).
Definition array.hpp:189
void set_attributes(json attributes)
Definition array.hpp:302
std::uint64_t nbytes() const
Whole-array size in bytes (elements x itemsize).
Definition array.hpp:193
static Array open(std::shared_ptr< Store > store, const std::string &path, OpenOptions options={})
Definition array.hpp:181
std::vector< std::uint64_t > grid_shape() const
Chunk-grid extent per dimension.
Definition array.hpp:191
std::string chunk_store_key(const std::vector< std::uint64_t > &index) const
Store key of the chunk at index (bounds-checked against the grid).
Definition array.hpp:323
const json & attributes() const
User attributes (.zattrs).
Definition array.hpp:298
Bytes decode(Bytes stored) const
Definition codecs.hpp:141
bool supports_partial_read() const
Definition codecs.hpp:104
Bytes encode(Bytes chunk) const
Definition codecs.hpp:110
Bytes decode_range(Bytes raw) const
Definition codecs.hpp:131
std::uint64_t decoded_chunk_bytes() const
Size of a decoded full chunk in bytes.
Definition codecs.hpp:91
A Zarr group bound to a Store.
Definition group.hpp:35
Definition types.hpp:36
nlohmann::json json
Definition metadata.hpp:30
Bytes canonical_json_bytes(const json &j)
Definition metadata.hpp:153
Definition metadata.hpp:98
json dimension_names
v3 dimension_names member, preserved verbatim (null when absent).
Definition metadata.hpp:115
std::vector< std::uint64_t > shape
Array shape; empty = 0-dimensional.
Definition metadata.hpp:102
std::uint64_t element_count() const
Number of elements in the whole array (1 for rank 0).
Definition metadata.hpp:126
char dimension_separator
Chunk-key separator ('.' or '/').
Definition metadata.hpp:113
ZarrFormat format
Format this array was read from / will be written as.
Definition metadata.hpp:100
DataType dtype
Element type.
Definition metadata.hpp:106
std::vector< std::uint64_t > chunk_shape
Chunk shape, same rank as shape; chunks may exceed the array extent.
Definition metadata.hpp:104
ChunkKeyKind key_encoding
Chunk-key scheme (see ChunkKeyKind).
Definition metadata.hpp:111
std::vector< ShardLevel > shard_levels
Sharding levels (empty = unsharded); see ShardLevel.
Definition metadata.hpp:121
json attributes
User attributes (v2 .zattrs / v3 attributes).
Definition metadata.hpp:123
std::vector< CodecSpec > codecs
Definition metadata.hpp:119
std::uint64_t chunk_element_count() const
Number of elements in one (full) chunk (1 for rank 0).
Definition metadata.hpp:130
std::vector< std::uint64_t > grid_shape() const
Chunk-grid extent per dimension.
Definition metadata.hpp:134
std::optional< Bytes > fill
Definition metadata.hpp:109
Parameters for Array::create.
Definition array.hpp:91
DataType dtype
Element type.
Definition array.hpp:99
std::optional< Bytes > fill
Fill value as one native-order element; defaults to zeros.
Definition array.hpp:105
char dimension_separator
Definition array.hpp:110
std::vector< std::uint64_t > shards
Definition array.hpp:116
json dimension_names
v3 dimension_names (array of strings/null, rank length), or null.
Definition array.hpp:112
json attributes
Initial user attributes.
Definition array.hpp:107
std::vector< CodecSpec > codecs
Definition array.hpp:103
ZarrFormat format
Storage format version to write.
Definition array.hpp:93
std::vector< std::uint64_t > shape
Array shape; empty = 0-dimensional.
Definition array.hpp:95
std::vector< std::uint64_t > chunks
Chunk shape, same rank as shape, extents >= 1.
Definition array.hpp:97
@ slice
length bytes starting at offset
Definition metadata.hpp:36
A concrete element type: kind plus size (the size only varies for raw).
Definition types.hpp:122
std::uint32_t itemsize
Element size in bytes.
Definition types.hpp:126
Options for opening arrays and groups.
Definition metadata.hpp:144
bool lenient
Definition metadata.hpp:148
std::vector< std::uint8_t > Bytes
Owned byte buffer used throughout the value-based public API.
Definition types.hpp:42
ZarrFormat
Zarr storage format version.
Definition types.hpp:45
std::string meta_key(const std::string &path, const char *suffix)
Store key of a metadata document for the node at path ("" = root).
Definition v2.hpp:36