GridFormat 0.2.1
I/O-Library for grid-like data structures
Loading...
Searching...
No Matches
zlib.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2022-2023 Dennis Gläser <dennis.glaeser@iws.uni-stuttgart.de>
2// SPDX-License-Identifier: MIT
9#ifndef GRIDFORMAT_COMPRESSION_ZLIB_HPP_
10#define GRIDFORMAT_COMPRESSION_ZLIB_HPP_
11#if GRIDFORMAT_HAVE_ZLIB
12
13#include <concepts>
14#include <utility>
15#include <vector>
16#include <cassert>
17#include <algorithm>
18#include <tuple>
19
20#include <zlib.h>
21
22#include <gridformat/common/exceptions.hpp>
23#include <gridformat/common/serialization.hpp>
24#include <gridformat/common/logging.hpp>
25
28
29namespace GridFormat::Compression {
30
33
36 std::size_t block_size = default_block_size;
37 int compression_level = Z_DEFAULT_COMPRESSION;
38};
39
41class ZLIB {
42 using ZLIBByte = unsigned char;
43 static_assert(sizeof(typename Serialization::Byte) == sizeof(ZLIBByte));
44
45 struct BlockDecompressor {
46 using ByteType = ZLIBByte;
47
48 void operator()(std::span<const ByteType> in, std::span<ByteType> out) const {
49 uLongf out_len = out.size();
50 uLong in_len = in.size();
51 if (uncompress(out.data(), &out_len, in.data(), in_len) != Z_OK)
52 throw IOError("(ZLIBCompressor) Error upon decompression");
53 if (out_len != out.size())
54 throw IOError("(ZLIBCompressor) Unexpected decompressed size");
55 }
56 };
57
58 public:
59 using Options = ZLIBOptions;
60
61 explicit constexpr ZLIB(Options opts = {})
62 : _opts(std::move(opts))
63 {}
64
65 template<std::integral HeaderType = std::size_t>
66 CompressedBlocks<HeaderType> compress(Serialization& in) const {
67 if (std::numeric_limits<HeaderType>::max() < in.size())
68 throw TypeError("Chosen HeaderType is too small for given number of bytes");
69 if (std::numeric_limits<HeaderType>::max() < _opts.block_size)
70 throw TypeError("Chosen HeaderType is too small for given block size");
71
72 auto [blocks, out] = _compress<HeaderType>(in.template as_span_of<const ZLIBByte>());
73 in = std::move(out);
74 in.resize(blocks.compressed_size());
75 return blocks;
76 }
77
78 template<std::integral HeaderType>
79 static void decompress(Serialization& in, const CompressedBlocks<HeaderType>& blocks) {
80 Compression::decompress(in, blocks, BlockDecompressor{});
81 }
82
83 static ZLIB with(Options opts) {
84 return ZLIB{std::move(opts)};
85 }
86
87 private:
88 template<std::integral HeaderType>
89 auto _compress(std::span<const ZLIBByte> in) const {
90 HeaderType block_size = static_cast<HeaderType>(_opts.block_size);
91 HeaderType size_in_bytes = static_cast<HeaderType>(in.size());
92 Blocks<HeaderType> blocks{size_in_bytes, block_size};
93
94 Serialization compressed;
95 std::vector<ZLIBByte> block_buffer;
96 std::vector<HeaderType> compressed_block_sizes;
97 block_buffer.reserve(compressBound(_opts.block_size));
98 compressed_block_sizes.reserve(blocks.number_of_blocks);
99 compressed.resize(block_buffer.capacity()*blocks.number_of_blocks);
100
101 HeaderType cur_in = 0;
102 HeaderType cur_out = 0;
103 auto out = compressed.template as_span_of<ZLIBByte>();
104 while (cur_in < size_in_bytes) {
105 using std::min;
106 const HeaderType cur_block_size = min(block_size, size_in_bytes - cur_in);
107 assert(cur_in + cur_block_size <= size_in_bytes);
108
109 uLongf out_len = block_buffer.capacity();
110 uLong in_len = cur_block_size;
111 if (compress2(block_buffer.data(), &out_len,
112 in.data() + cur_in, in_len,
113 _opts.compression_level) != Z_OK)
114 throw InvalidState(as_error("Error upon compression with ZLib"));
115
116 assert(cur_out + out_len <= out.size());
117 std::copy_n(block_buffer.data(),
118 out_len,
119 out.data() + cur_out);
120 cur_in += cur_block_size;
121 cur_out += out_len;
122 compressed_block_sizes.push_back(static_cast<HeaderType>(out_len));
123 }
124
125 if (cur_in != size_in_bytes)
126 throw InvalidState(as_error("(ZLIBCompressor) unexpected number of bytes processed"));
127
128 return std::make_tuple(
129 CompressedBlocks<HeaderType>{blocks, std::move(compressed_block_sizes)},
130 compressed
131 );
132 }
133
134 Options _opts;
135};
136
137inline constexpr ZLIB zlib;
138
139#ifndef DOXYGEN
140namespace Detail { inline constexpr bool _have_zlib = true; }
141#endif // DOXYGEN
142
144
145} // end namespace GridFormat::Compression
146
147#else // GRIDFORMAT_HAVE_ZLIB
148
149namespace GridFormat::Compression {
150namespace Detail { inline constexpr bool _have_zlib = false; }
151class ZLIB {
152 public:
153 template<bool b = false, typename... Args>
154 explicit ZLIB(Args&&...) { static_assert(b, "ZLIB compressor requires the ZLIB library."); }
155};
156
157} // namespace GridFormat::Compression
158
159#endif // GRIDFORMAT_HAVE_ZLIB
160#endif // GRIDFORMAT_COMPRESSION_ZLIB_HPP_
Compressor using the zlib library.
Definition: zlib.hpp:41
Common classes used in the context of data compression.
Decompress compressed data.
constexpr ZLIB zlib
Instance of the zlib compressor.
Definition: zlib.hpp:137
void decompress(Serialization &in, const CompressedBlocks< HeaderType > &blocks, const Decompressor &block_decompressor)
Decompress compressed data.
Definition: decompress.hpp:26
constexpr std::size_t default_block_size
as in VTK (https://gitlab.kitware.com/vtk/vtk/-/blob/65fc526a83ac829628a9462f61fa57f1801e2c7e/IO/XML/...
Definition: common.hpp:23
Stores the block sizes used for compressing the given amount of bytes.
Definition: common.hpp:27
Stores the uncompressed/compressed block sizes after completion of a compression.
Definition: common.hpp:41
Options for the zlib compressor.
Definition: zlib.hpp:35