3#ifndef LIBZARR_CODECS_GZIP_HPP
4#define LIBZARR_CODECS_GZIP_HPP
6#ifndef LIBZARR_HAS_ZLIB
7#error "libzarr/codecs_gzip.hpp requires zlib: define LIBZARR_HAS_ZLIB and link against zlib"
18#include "libzarr/detail/common.hpp"
26namespace zarr::detail {
28class ZlibDeflateGuard {
30 explicit ZlibDeflateGuard(z_stream* zs) : zs_(zs) {}
31 ZlibDeflateGuard(
const ZlibDeflateGuard&) =
delete;
32 ZlibDeflateGuard& operator=(
const ZlibDeflateGuard&) =
delete;
33 ZlibDeflateGuard(ZlibDeflateGuard&&) =
delete;
34 ZlibDeflateGuard& operator=(ZlibDeflateGuard&&) =
delete;
35 ~ZlibDeflateGuard() { deflateEnd(zs_); }
41class ZlibInflateGuard {
43 explicit ZlibInflateGuard(z_stream* zs) : zs_(zs) {}
44 ZlibInflateGuard(
const ZlibInflateGuard&) =
delete;
45 ZlibInflateGuard& operator=(
const ZlibInflateGuard&) =
delete;
46 ZlibInflateGuard(ZlibInflateGuard&&) =
delete;
47 ZlibInflateGuard& operator=(ZlibInflateGuard&&) =
delete;
48 ~ZlibInflateGuard() { inflateEnd(zs_); }
55inline Bytes deflate_bytes(
const Bytes& src,
int level,
bool gzip_framing,
const char* what) {
57 const int window_bits = gzip_framing ? 15 + 16 : 15;
58 if (deflateInit2(&zs, level, Z_DEFLATED, window_bits, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
59 throw error(std::string(what) +
": deflateInit2 failed");
61 const ZlibDeflateGuard guard(&zs);
62 Bytes out(deflateBound(&zs,
static_cast<uLong
>(src.size())));
63 std::size_t in_pos = 0;
64 std::size_t out_pos = 0;
67 const std::size_t in_step = std::min<std::size_t>(src.size() - in_pos, UINT_MAX);
70 zs.next_in =
const_cast<Bytef*
>(src.data() + in_pos);
71 zs.avail_in =
static_cast<uInt
>(in_step);
72 zs.next_out = out.data() + out_pos;
73 zs.avail_out =
static_cast<uInt
>(std::min<std::size_t>(out.size() - out_pos, UINT_MAX));
74 const bool last_input = in_pos + in_step == src.size();
75 ret = deflate(&zs, last_input ? Z_FINISH : Z_NO_FLUSH);
76 if (ret != Z_OK && ret != Z_STREAM_END && ret != Z_BUF_ERROR) {
77 throw error(std::string(what) +
": deflate failed (" + std::to_string(ret) +
")");
79 in_pos += in_step - zs.avail_in;
80 out_pos =
static_cast<std::size_t
>(zs.next_out - out.data());
81 if (out_pos == out.size() && ret != Z_STREAM_END) {
82 out.resize(out.size() + out.size() / 2 + 64);
84 }
while (ret != Z_STREAM_END);
94inline Bytes inflate_bytes(
const Bytes& src, std::optional<std::uint64_t> expected_size,
97 if (inflateInit2(&zs, 15 + 32) != Z_OK) {
98 throw error(std::string(what) +
": inflateInit2 failed");
100 const ZlibInflateGuard guard(&zs);
103 out.resize(checked_size(*expected_size, what));
105 out.resize(std::max<std::size_t>(src.size() * 3, 64));
107 std::size_t in_pos = 0;
108 std::size_t out_pos = 0;
111 const std::size_t in_step = std::min<std::size_t>(src.size() - in_pos, UINT_MAX);
114 zs.next_in =
const_cast<Bytef*
>(src.data() + in_pos);
115 zs.avail_in =
static_cast<uInt
>(in_step);
116 zs.next_out = out.data() + out_pos;
117 zs.avail_out =
static_cast<uInt
>(std::min<std::size_t>(out.size() - out_pos, UINT_MAX));
118 ret = inflate(&zs, Z_NO_FLUSH);
119 if (ret != Z_OK && ret != Z_STREAM_END && ret != Z_BUF_ERROR) {
120 throw error(std::string(what) +
": corrupt compressed data (zlib error " +
121 std::to_string(ret) +
")");
123 in_pos += in_step - zs.avail_in;
124 out_pos =
static_cast<std::size_t
>(zs.next_out - out.data());
125 if (ret == Z_STREAM_END) {
128 if (out_pos == out.size()) {
130 throw error(std::string(what) +
": decompressed data exceeds expected " +
131 std::to_string(*expected_size) +
" bytes");
133 out.resize(out.size() * 2);
134 }
else if (in_pos == src.size()) {
135 throw error(std::string(what) +
": compressed data is truncated");
138 if (in_pos != src.size()) {
139 throw error(std::string(what) +
": trailing garbage after compressed data");
141 if (expected_size && out_pos != *expected_size) {
142 throw error(std::string(what) +
": decompressed to " + std::to_string(out_pos) +
143 " bytes, expected " + std::to_string(*expected_size));
std::vector< std::uint8_t > Bytes
Owned byte buffer used throughout the value-based public API.
Definition types.hpp:42