49 count > std::numeric_limits<std::uint64_t>::max() / meta.
dtype.
itemsize) {
50 throw error(
"chunk byte size overflows uint64");
57 bool past_bytes =
false;
58 bool have_bytes =
false;
60 if (codec.name ==
"transpose") {
62 throw error(
"codec 'transpose' must precede the 'bytes' codec");
64 p.set_transpose(codec);
65 }
else if (codec.name ==
"bytes") {
67 throw error(
"codec chain has more than one 'bytes' codec");
71 p.set_byte_order(codec);
72 }
else if (codec.name ==
"sharding_indexed") {
75 throw error(
"codec 'sharding_indexed' must be lowered into shard levels, not resolved");
78 throw error(
"codec '" + codec.name +
"' must follow the 'bytes' codec");
80 p.add_byte_stage(codec, meta);
84 throw error(
"codec chain is missing the 'bytes' (array->bytes) codec");
86 p.compute_expected_sizes();
97 return !transpose_order_ && !byteswap_ && byte_stages_.empty();
105 return !transpose_order_ && byte_stages_.empty();
111 if (chunk.size() != chunk_bytes_) {
112 throw error(
"encode: chunk buffer is " + std::to_string(chunk.size()) +
" bytes, expected " +
113 std::to_string(chunk_bytes_));
115 if (transpose_order_) {
118 throw error(
"writing to a transposed (order:'F') array is not supported");
121 detail::byteswap_inplace(chunk.data(), chunk.size() / swap_width_, swap_width_);
123 for (
const ByteStage& stage : byte_stages_) {
124 chunk = encode_stage(stage, std::move(chunk));
134 detail::byteswap_inplace(raw.data(), raw.size() / swap_width_, swap_width_);
142 for (std::size_t i = byte_stages_.size(); i-- > 0;) {
143 stored = decode_stage(byte_stages_[i], std::move(stored), decode_expected_[i]);
145 if (stored.size() != chunk_bytes_) {
146 throw error(
"decode: chunk is " + std::to_string(stored.size()) +
" bytes, expected " +
147 std::to_string(chunk_bytes_));
150 detail::byteswap_inplace(stored.data(), stored.size() / swap_width_, swap_width_);
152 if (transpose_order_) {
153 Bytes out(stored.size());
154 detail::gather_strided(stored.data(), gather_strides_, out.data(), chunk_shape_, itemsize_);
162 enum class Kind : std::uint8_t {
169 Kind kind = Kind::deflate;
172 bool gzip_framing =
true;
174 std::string blosc_cname =
"lz4";
175 int blosc_clevel = 5;
176 int blosc_shuffle = 1;
177 std::uint32_t blosc_typesize = 1;
178 std::uint64_t blosc_blocksize = 0;
181 bool zstd_checksum =
false;
183 std::uint32_t shuffle_elementsize = 1;
186 [[nodiscard]]
static Bytes encode_stage(
const ByteStage& stage, Bytes data) {
187 switch (stage.kind) {
188 case ByteStage::Kind::deflate:
189#ifdef LIBZARR_HAS_ZLIB
190 return detail::deflate_bytes(data, stage.level, stage.gzip_framing,
"encode");
192 throw error(
"codec requires zlib but LIBZARR_HAS_ZLIB is not defined");
194 case ByteStage::Kind::crc32c: {
196 const std::uint32_t checksum = detail::crc32c(data.data(), data.size());
197 for (
int i = 0; i < 4; ++i) {
198 data.push_back(
static_cast<std::uint8_t
>(checksum >> (8 * i)));
202 case ByteStage::Kind::blosc:
203#ifdef LIBZARR_HAS_BLOSC
205 detail::BloscParams params;
206 params.cname = stage.blosc_cname;
207 params.clevel = stage.blosc_clevel;
208 params.shuffle = stage.blosc_shuffle;
209 params.typesize = stage.blosc_typesize;
210 params.blocksize = detail::checked_size(stage.blosc_blocksize,
"blosc blocksize");
211 return detail::blosc_compress_bytes(data, params,
"encode");
214 throw error(
"codec requires blosc but LIBZARR_HAS_BLOSC is not defined");
216 case ByteStage::Kind::zstd:
217#if !defined(LIBZARR_HAS_ZSTD)
218 throw error(
"codec requires zstd but LIBZARR_HAS_ZSTD is not defined");
219#elif defined(LIBZARR_ZSTD_DECODE_ONLY)
221 throw error(
"zstd encode is not compiled in (LIBZARR_ZSTD_DECODE_ONLY)");
223 return detail::zstd_compress_bytes(data, stage.zstd_level, stage.zstd_checksum,
"encode");
225 case ByteStage::Kind::shuffle:
226 return detail::shuffle_bytes(data, stage.shuffle_elementsize);
231 [[nodiscard]]
static Bytes decode_stage(
const ByteStage& stage, Bytes data,
232 [[maybe_unused]] std::optional<std::uint64_t> expected) {
233 switch (stage.kind) {
234 case ByteStage::Kind::deflate:
235#ifdef LIBZARR_HAS_ZLIB
236 return detail::inflate_bytes(data, expected,
"decode");
238 throw error(
"codec requires zlib but LIBZARR_HAS_ZLIB is not defined");
240 case ByteStage::Kind::crc32c: {
241 if (data.size() < 4) {
242 throw error(
"decode: crc32c codec needs at least 4 bytes");
244 const std::size_t payload = data.size() - 4;
245 std::uint32_t stored_crc = 0;
246 for (
int i = 3; i >= 0; --i) {
247 stored_crc = (stored_crc << 8U) | data[payload + static_cast<std::size_t>(i)];
249 if (detail::crc32c(data.data(), payload) != stored_crc) {
250 throw error(
"decode: crc32c checksum mismatch (corrupt chunk)");
252 data.resize(payload);
255 case ByteStage::Kind::blosc:
256#ifdef LIBZARR_HAS_BLOSC
257 return detail::blosc_decompress_bytes(data, expected,
"decode");
259 throw error(
"codec requires blosc but LIBZARR_HAS_BLOSC is not defined");
261 case ByteStage::Kind::zstd:
262#ifdef LIBZARR_HAS_ZSTD
263 return detail::zstd_decompress_bytes(data, expected,
"decode");
265 throw error(
"codec requires zstd but LIBZARR_HAS_ZSTD is not defined");
267 case ByteStage::Kind::shuffle:
268 return detail::unshuffle_bytes(data, stage.shuffle_elementsize);
273 void set_transpose(
const CodecSpec& codec) {
274 const auto it = codec.configuration.find(
"order");
275 if (it == codec.configuration.end() || !it->is_array()) {
276 throw error(
"codec 'transpose' requires an 'order' array");
278 const std::size_t rank = chunk_shape_.size();
279 std::vector<std::uint32_t> order;
280 std::vector<bool> seen(rank,
false);
281 for (
const json& v : *it) {
282 const std::uint64_t dim = detail::json_to_uint64(v,
"codec 'transpose': 'order'");
284 throw error(
"codec 'transpose': 'order' must be a permutation of 0.." +
285 std::to_string(rank == 0 ? 0 : rank - 1));
287 const auto d =
static_cast<std::uint32_t
>(dim);
289 throw error(
"codec 'transpose': repeated dimension " + std::to_string(d) +
" in 'order'");
294 if (order.size() != rank) {
295 throw error(
"codec 'transpose': 'order' has " + std::to_string(order.size()) +
296 " entries for a rank-" + std::to_string(rank) +
" array");
298 bool identity =
true;
299 for (std::size_t i = 0; i < rank; ++i) {
300 identity = identity && order[i] == i;
305 transpose_order_ = order;
308 std::vector<std::uint64_t> stored_shape(rank);
309 for (std::size_t i = 0; i < rank; ++i) {
310 stored_shape[i] = chunk_shape_[order[i]];
312 const std::vector<std::uint64_t> stored_strides =
313 detail::c_strides_bytes(stored_shape, itemsize_);
314 gather_strides_.assign(rank, 0);
315 for (std::size_t i = 0; i < rank; ++i) {
316 gather_strides_[order[i]] = stored_strides[i];
320 void set_byte_order(
const CodecSpec& codec) {
321 std::string endian =
"little";
322 const auto it = codec.configuration.find(
"endian");
323 if (it != codec.configuration.end()) {
324 if (!it->is_string()) {
325 throw error(
"codec 'bytes': 'endian' must be a string");
327 endian = it->get<std::string>();
329 if (endian !=
"little" && endian !=
"big") {
330 throw error(
"codec 'bytes': unknown endian '" + endian +
"'");
332 const bool stored_little = endian ==
"little";
333 byteswap_ = swap_width_ > 1 && stored_little != detail::host_is_little_endian();
336 void add_byte_stage(
const CodecSpec& codec,
const ArrayMeta& meta) {
337 if (codec.name ==
"gzip" || codec.name ==
"zlib") {
339 }
else if (codec.name ==
"crc32c") {
341 stage.kind = ByteStage::Kind::crc32c;
342 byte_stages_.push_back(stage);
343 }
else if (codec.name ==
"blosc") {
344 add_blosc(codec, meta);
345 }
else if (codec.name ==
"zstd") {
347 }
else if (codec.name ==
"shuffle") {
348 add_shuffle(codec, meta);
352 throw error(
"unknown codec '" + codec.name +
"'");
356 void add_deflate(
const CodecSpec& codec) {
358 stage.kind = ByteStage::Kind::deflate;
359 stage.gzip_framing = codec.name ==
"gzip";
360 const auto it = codec.configuration.find(
"level");
361 if (it != codec.configuration.end()) {
362 if (!it->is_number_integer() || it->get<std::int64_t>() < 0 || it->get<std::int64_t>() > 9) {
363 throw error(
"codec '" + codec.name +
"': 'level' must be an integer in 0..9");
365 stage.level = it->get<
int>();
367#ifndef LIBZARR_HAS_ZLIB
368 throw error(
"codec '" + codec.name +
369 "' is not built into this libzarr (compile with LIBZARR_HAS_ZLIB and link zlib)");
371 byte_stages_.push_back(stage);
374 void add_zstd(
const CodecSpec& codec) {
376 stage.kind = ByteStage::Kind::zstd;
377 const json config = codec.configuration.is_object() ? codec.configuration : json::object();
378 const json level = config.value(
"level",
json(std::int64_t{0}));
379 if (!level.is_number_integer()) {
380 throw error(
"codec 'zstd': 'level' must be an integer");
382 stage.zstd_level = level.get<
int>();
383 const json checksum = config.value(
"checksum",
json(
false));
384 if (!checksum.is_boolean()) {
385 throw error(
"codec 'zstd': 'checksum' must be a boolean");
387 stage.zstd_checksum = checksum.get<
bool>();
388#ifndef LIBZARR_HAS_ZSTD
390 "codec 'zstd' is not built into this libzarr (compile with LIBZARR_HAS_ZSTD and link "
393 byte_stages_.push_back(stage);
396 void add_shuffle(
const CodecSpec& codec,
const ArrayMeta& meta) {
398 stage.kind = ByteStage::Kind::shuffle;
399 const json config = codec.configuration.is_object() ? codec.configuration : json::object();
400 const std::int64_t elementsize = config.value(
"elementsize", std::int64_t{0});
401 if (elementsize < 0 || elementsize > 0xFFFF) {
402 throw error(
"filter 'shuffle': invalid elementsize " + std::to_string(elementsize));
406 stage.shuffle_elementsize =
407 elementsize == 0 ? meta.dtype.itemsize :
static_cast<std::uint32_t
>(elementsize);
408 if (stage.shuffle_elementsize == 0) {
409 throw error(
"filter 'shuffle': element size cannot be zero");
411 byte_stages_.push_back(stage);
414 void add_blosc(
const CodecSpec& codec,
const ArrayMeta& meta) {
416 stage.kind = ByteStage::Kind::blosc;
419 const json config = codec.configuration.is_object() ? codec.configuration : json::object();
420 stage.blosc_cname = config.value(
"cname",
"lz4");
421 const std::int64_t clevel = config.value(
"clevel", std::int64_t{5});
422 if (clevel < 0 || clevel > 9) {
423 throw error(
"codec 'blosc': 'clevel' must be in 0..9");
425 stage.blosc_clevel =
static_cast<int>(clevel);
427 if (shuffle ==
"noshuffle") {
428 stage.blosc_shuffle = 0;
429 }
else if (shuffle ==
"shuffle") {
430 stage.blosc_shuffle = 1;
431 }
else if (shuffle ==
"bitshuffle") {
432 stage.blosc_shuffle = 2;
433 }
else if (
shuffle.is_number_integer() &&
shuffle.get<std::int64_t>() >= -1 &&
434 shuffle.get<std::int64_t>() <= 2) {
437 const auto n =
shuffle.get<std::int64_t>();
439 stage.blosc_shuffle = meta.dtype.itemsize == 1 ? 2 : 1;
441 stage.blosc_shuffle =
static_cast<int>(n);
444 throw error(
"codec 'blosc': unknown shuffle " +
shuffle.dump());
446 stage.blosc_typesize =
447 static_cast<std::uint32_t
>(config.value(
"typesize", std::int64_t{meta.dtype.itemsize}));
448 stage.blosc_blocksize =
static_cast<std::uint64_t
>(config.value(
"blocksize", std::int64_t{0}));
449 bool known_cname =
false;
450 for (
const char* name : {
"blosclz",
"lz4",
"lz4hc",
"snappy",
"zlib",
"zstd"}) {
451 known_cname = known_cname || stage.blosc_cname == name;
454 throw error(
"codec 'blosc': unknown cname '" + stage.blosc_cname +
"'");
456#ifndef LIBZARR_HAS_BLOSC
458 "codec 'blosc' is not built into this libzarr (compile with LIBZARR_HAS_BLOSC and link "
461 byte_stages_.push_back(stage);
467 void compute_expected_sizes() {
468 decode_expected_.assign(byte_stages_.size(), std::nullopt);
469 std::optional<std::uint64_t> size = chunk_bytes_;
470 for (std::size_t i = 0; i < byte_stages_.size(); ++i) {
471 decode_expected_[i] = size;
475 if (byte_stages_[i].kind == ByteStage::Kind::crc32c) {
477 }
else if (byte_stages_[i].kind == ByteStage::Kind::shuffle) {
485 std::vector<std::uint64_t> chunk_shape_;
486 std::uint32_t itemsize_ = 1;
487 std::uint64_t chunk_bytes_ = 0;
488 std::uint32_t swap_width_ = 1;
489 bool byteswap_ =
false;
490 std::optional<std::vector<std::uint32_t>> transpose_order_;
491 std::vector<std::uint64_t> gather_strides_;
492 std::vector<ByteStage> byte_stages_;
493 std::vector<std::optional<std::uint64_t>> decode_expected_;