3#ifndef LIBZARR_METADATA_HPP
4#define LIBZARR_METADATA_HPP
15#include <nlohmann/json.hpp>
17#include "libzarr/detail/common.hpp"
30using json = nlohmann::json;
49[[nodiscard]]
inline CodecSpec gzip(
int level = 5) {
return {
"gzip", {{
"level", level}}}; }
52[[nodiscard]]
inline CodecSpec zlib(
int level = 5) {
return {
"zlib", {{
"level", level}}}; }
55[[nodiscard]]
inline CodecSpec blosc(
const std::string& cname =
"lz4",
int clevel = 5,
56 const std::string&
shuffle =
"shuffle") {
57 return {
"blosc", {{
"cname", cname}, {
"clevel", clevel}, {
"shuffle",
shuffle}}};
61[[nodiscard]]
inline CodecSpec zstd(
int level = 0,
bool checksum =
false) {
62 return {
"zstd", {{
"level", level}, {
"checksum", checksum}}};
70 return {
"shuffle", {{
"elementsize", elementsize}}};
127 return detail::checked_product(
shape,
"array shape");
131 return detail::checked_product(
chunk_shape,
"chunk shape");
134 [[nodiscard]] std::vector<std::uint64_t>
grid_shape()
const {
135 std::vector<std::uint64_t> grid(
shape.size());
136 for (std::size_t d = 0; d <
shape.size(); ++d) {
154 const std::string text = j.dump(4);
155 return {text.begin(), text.end()};
164template <
typename Fn>
165auto guard_json(
const std::string& ctx,
const Fn& fn) ->
decltype(fn()) {
168 }
catch (
const json::exception& e) {
169 throw error(ctx +
": malformed metadata (" + e.what() +
")");
176inline json parse_json(
const Bytes& bytes,
const std::string& ctx) {
178 return json::parse(bytes.begin(), bytes.end());
179 }
catch (
const json::exception& e) {
180 throw error(ctx +
": " + e.what());
187inline std::uint64_t json_to_uint64(
const json& v,
const std::string& ctx) {
188 if (v.is_number_unsigned()) {
189 return v.get<std::uint64_t>();
191 if (v.is_number_integer()) {
192 const auto i = v.get<std::int64_t>();
194 throw error(ctx +
": expected a non-negative integer, got " + std::to_string(i));
196 return static_cast<std::uint64_t
>(i);
198 throw error(ctx +
": expected a non-negative integer, got " + v.dump());
204inline std::int64_t lenient_int(
const json& obj,
const char* key, std::int64_t fallback,
205 const std::string& ctx) {
206 const auto it = obj.find(key);
207 if (it == obj.end()) {
210 if (it->is_number_integer() || it->is_number_unsigned()) {
211 return it->get<std::int64_t>();
213 if (it->is_string()) {
214 const auto s = it->get<std::string>();
217 const long long v = std::strtoll(s.c_str(), &end, 10);
218 if (end == s.c_str() + s.size()) {
219 return static_cast<std::int64_t
>(v);
223 throw error(ctx +
": '" + key +
"' must be an integer, got " + it->dump());
227Bytes scalar_bytes(T value) {
228 Bytes out(
sizeof(T));
229 std::memcpy(out.data(), &value,
sizeof(T));
234inline std::vector<std::uint64_t> parse_extents(
const json& v,
const char* name,
235 const std::string& ctx) {
237 throw error(ctx +
": '" + name +
"' must be an array");
239 std::vector<std::uint64_t> out;
240 out.reserve(v.size());
241 for (
const json& e : v) {
242 out.push_back(json_to_uint64(e, ctx +
": " + name));
250inline Bytes quiet_nan_bytes(DType kind) {
251 if (kind == DType::float16) {
252 return scalar_bytes<std::uint16_t>(0x7e00U);
254 if (kind == DType::float32) {
255 return scalar_bytes<std::uint32_t>(0x7fc00000U);
257 assert(kind == DType::float64);
258 return scalar_bytes<std::uint64_t>(0x7ff8000000000000ULL);
262inline Bytes infinity_bytes(DType kind,
bool negative) {
263 if (kind == DType::float16) {
264 return scalar_bytes<std::uint16_t>(negative ? 0xfc00U : 0x7c00U);
266 if (kind == DType::float32) {
267 const float inf = std::numeric_limits<float>::infinity();
268 return scalar_bytes(negative ? -inf : inf);
270 assert(kind == DType::float64);
271 const double inf = std::numeric_limits<double>::infinity();
272 return scalar_bytes(negative ? -inf : inf);
275inline Bytes fill_from_double(
double value, DataType dt,
const std::string& ctx);
278inline Bytes fill_from_int(std::int64_t value, DataType dt,
const std::string& ctx) {
279 const auto check = [&](std::int64_t lo, std::int64_t hi) {
280 if (value < lo || value > hi) {
281 throw error(ctx +
": fill_value " + std::to_string(value) +
" out of range for dtype");
287 return scalar_bytes<std::uint8_t>(
static_cast<std::uint8_t
>(value));
289 check(std::numeric_limits<std::int8_t>::min(), std::numeric_limits<std::int8_t>::max());
290 return scalar_bytes<std::int8_t>(
static_cast<std::int8_t
>(value));
292 check(std::numeric_limits<std::int16_t>::min(), std::numeric_limits<std::int16_t>::max());
293 return scalar_bytes<std::int16_t>(
static_cast<std::int16_t
>(value));
295 check(std::numeric_limits<std::int32_t>::min(), std::numeric_limits<std::int32_t>::max());
296 return scalar_bytes<std::int32_t>(
static_cast<std::int32_t
>(value));
298 return scalar_bytes<std::int64_t>(value);
302 case DType::uint64: {
304 throw error(ctx +
": fill_value " + std::to_string(value) +
305 " is negative for unsigned dtype");
307 const auto u =
static_cast<std::uint64_t
>(value);
308 if (dt.kind == DType::uint8 && u > std::numeric_limits<std::uint8_t>::max()) {
311 if (dt.kind == DType::uint16 && u > std::numeric_limits<std::uint16_t>::max()) {
314 if (dt.kind == DType::uint32 && u > std::numeric_limits<std::uint32_t>::max()) {
315 check(0, 4294967295LL);
317 if (dt.kind == DType::uint8) {
318 return scalar_bytes<std::uint8_t>(
static_cast<std::uint8_t
>(u));
320 if (dt.kind == DType::uint16) {
321 return scalar_bytes<std::uint16_t>(
static_cast<std::uint16_t
>(u));
323 if (dt.kind == DType::uint32) {
324 return scalar_bytes<std::uint32_t>(
static_cast<std::uint32_t
>(u));
326 return scalar_bytes<std::uint64_t>(u);
330 return fill_from_double(
static_cast<double>(value), dt, ctx);
332 throw error(ctx +
": numeric fill_value invalid for this dtype");
338inline Bytes fill_from_uint(std::uint64_t value, DataType dt,
const std::string& ctx) {
339 if (value <=
static_cast<std::uint64_t
>(std::numeric_limits<std::int64_t>::max())) {
340 return fill_from_int(
static_cast<std::int64_t
>(value), dt, ctx);
342 if (dt.kind != DType::uint64) {
343 throw error(ctx +
": fill_value " + std::to_string(value) +
" out of range for dtype");
345 return scalar_bytes<std::uint64_t>(value);
348inline Bytes fill_from_double(
double value, DataType dt,
const std::string& ctx) {
351 return scalar_bytes<std::uint16_t>(double_to_half_bits(value));
353 return scalar_bytes<float>(
static_cast<float>(value));
355 return scalar_bytes<double>(value);
359 if (std::nearbyint(value) == value &&
360 value >=
static_cast<double>(std::numeric_limits<std::int64_t>::min()) &&
361 value <=
static_cast<double>(std::numeric_limits<std::int64_t>::max())) {
362 return fill_from_int(
static_cast<std::int64_t
>(value), dt, ctx);
364 throw error(ctx +
": non-integral fill_value for integer dtype");
371inline json fill_to_json(
const std::optional<Bytes>& fill, DataType dt) {
375 const std::uint8_t* p = fill->data();
376 const auto load = [&](
auto probe) {
378 std::memcpy(&v, p,
sizeof(v));
383 return load(std::uint8_t{}) != 0;
385 return load(std::int8_t{});
387 return load(std::int16_t{});
389 return load(std::int32_t{});
391 return load(std::int64_t{});
393 return load(std::uint8_t{});
395 return load(std::uint16_t{});
397 return load(std::uint32_t{});
399 return load(std::uint64_t{});
402 case DType::float64: {
404 if (dt.kind == DType::float16) {
405 v = half_bits_to_double(load(std::uint16_t{}));
406 }
else if (dt.kind == DType::float32) {
407 v =
static_cast<double>(load(
float{}));
415 return v > 0 ?
"Infinity" :
"-Infinity";
419 case DType::complex64:
420 case DType::complex128: {
421 const DType component = dt.kind == DType::complex64 ? DType::float32 : DType::float64;
422 const std::uint32_t half = dt.itemsize / 2;
427 return base64_encode(p, dt.itemsize);
429 throw error(
"fill_value emission not implemented for this dtype");
CodecSpec zlib(int level=5)
zlib (RFC 1950) at level (0-9). Zarr v2 only.
Definition metadata.hpp:52
CodecSpec shuffle(int elementsize=0)
shuffle: byte-transposition filter. elementsize 0 means the dtype size.
Definition metadata.hpp:69
CodecSpec gzip(int level=5)
gzip (RFC 1952) at level (0-9).
Definition metadata.hpp:49
CodecSpec blosc(const std::string &cname="lz4", int clevel=5, const std::string &shuffle="shuffle")
blosc (v3-style named shuffle: "noshuffle", "shuffle" or "bitshuffle").
Definition metadata.hpp:55
CodecSpec zstd(int level=0, bool checksum=false)
zstd. Level 0 means zstd's default; checksum appends a frame checksum.
Definition metadata.hpp:61
CodecSpec crc32c()
crc32c (Castagnoli) trailing checksum. v3 only.
Definition metadata.hpp:66
Definition metadata.hpp:36
json configuration
Codec-specific configuration.
Definition metadata.hpp:40
std::string name
Codec name ("transpose", "bytes", "gzip", "zlib", ...).
Definition metadata.hpp:38
A concrete element type: kind plus size (the size only varies for raw).
Definition types.hpp:122
static constexpr DataType of(DType kind)
Definition types.hpp:131
Options for opening arrays and groups.
Definition metadata.hpp:144
bool lenient
Definition metadata.hpp:148
Definition metadata.hpp:86
std::vector< std::uint64_t > shard_shape
Shard (outer chunk) shape at this level.
Definition metadata.hpp:88
bool index_at_end
v3 sharding spec index_location: end (default) or start.
Definition metadata.hpp:92
std::vector< CodecSpec > index_codecs
Codecs of the shard index (fixed-size: bytes and optional crc32c).
Definition metadata.hpp:90
DType
Definition types.hpp:52
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