GCC Code Coverage Report


Directory: gridformat/
File: gridformat/compression/zlib.hpp
Date: 2024-11-20 14:41:59
Exec Total Coverage
Lines: 54 60 90.0%
Functions: 7 9 77.8%
Branches: 27 70 38.6%

Line Branch Exec Source
1 // SPDX-FileCopyrightText: 2022-2023 Dennis Gläser <dennis.glaeser@iws.uni-stuttgart.de>
2 // SPDX-License-Identifier: MIT
3 /*!
4 * \file
5 * \ingroup Common
6 * \ingroup Compression
7 * \brief Compressor using the ZLIB library.
8 */
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
26 #include <gridformat/compression/common.hpp>
27 #include <gridformat/compression/decompress.hpp>
28
29 namespace GridFormat::Compression {
30
31 //! \addtogroup Compression
32 //! @{
33
34 //! Options for the zlib compressor
35 struct ZLIBOptions {
36 std::size_t block_size = default_block_size;
37 int compression_level = Z_DEFAULT_COMPRESSION;
38 };
39
40 //! Compressor using the zlib library
41 class 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 277 void operator()(std::span<const ByteType> in, std::span<ByteType> out) const {
49 277 uLongf out_len = out.size();
50 277 uLong in_len = in.size();
51
2/4
✓ Branch 3 taken 277 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 277 times.
277 if (uncompress(out.data(), &out_len, in.data(), in_len) != Z_OK)
52 throw IOError("(ZLIBCompressor) Error upon decompression");
53
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 277 times.
277 if (out_len != out.size())
54 throw IOError("(ZLIBCompressor) Unexpected decompressed size");
55 277 }
56 };
57
58 public:
59 using Options = ZLIBOptions;
60
61 713 explicit constexpr ZLIB(Options opts = {})
62 713 : _opts(std::move(opts))
63 713 {}
64
65 template<std::integral HeaderType = std::size_t>
66 28889 CompressedBlocks<HeaderType> compress(Serialization& in) const {
67
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 14447 times.
28889 if (std::numeric_limits<HeaderType>::max() < in.size())
68 throw TypeError("Chosen HeaderType is too small for given number of bytes");
69
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 14447 times.
28889 if (std::numeric_limits<HeaderType>::max() < _opts.block_size)
70 throw TypeError("Chosen HeaderType is too small for given block size");
71
72
2/4
✓ Branch 1 taken 14447 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 14447 times.
✗ Branch 5 not taken.
28889 auto [blocks, out] = _compress<HeaderType>(in.template as_span_of<const ZLIBByte>());
73 28889 in = std::move(out);
74
2/4
✓ Branch 1 taken 14447 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 14447 times.
✗ Branch 5 not taken.
28889 in.resize(blocks.compressed_size());
75
1/2
✓ Branch 1 taken 14447 times.
✗ Branch 2 not taken.
57778 return blocks;
76 28889 }
77
78 template<std::integral HeaderType>
79 548 static void decompress(Serialization& in, const CompressedBlocks<HeaderType>& blocks) {
80
1/2
✓ Branch 1 taken 275 times.
✗ Branch 2 not taken.
548 Compression::decompress(in, blocks, BlockDecompressor{});
81 548 }
82
83 439 static ZLIB with(Options opts) {
84 439 return ZLIB{std::move(opts)};
85 }
86
87 private:
88 template<std::integral HeaderType>
89 28889 auto _compress(std::span<const ZLIBByte> in) const {
90 28889 HeaderType block_size = static_cast<HeaderType>(_opts.block_size);
91 28889 HeaderType size_in_bytes = static_cast<HeaderType>(in.size());
92 28889 Blocks<HeaderType> blocks{size_in_bytes, block_size};
93
94 28889 Serialization compressed;
95 28889 std::vector<ZLIBByte> block_buffer;
96 28889 std::vector<HeaderType> compressed_block_sizes;
97
2/4
✓ Branch 1 taken 14447 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 14447 times.
✗ Branch 5 not taken.
28889 block_buffer.reserve(compressBound(_opts.block_size));
98
1/2
✓ Branch 1 taken 14447 times.
✗ Branch 2 not taken.
28889 compressed_block_sizes.reserve(blocks.number_of_blocks);
99
1/2
✓ Branch 2 taken 14447 times.
✗ Branch 3 not taken.
28889 compressed.resize(block_buffer.capacity()*blocks.number_of_blocks);
100
101 28889 HeaderType cur_in = 0;
102 28889 HeaderType cur_out = 0;
103
1/2
✓ Branch 1 taken 14447 times.
✗ Branch 2 not taken.
28889 auto out = compressed.template as_span_of<ZLIBByte>();
104
2/2
✓ Branch 0 taken 44088 times.
✓ Branch 1 taken 14447 times.
117046 while (cur_in < size_in_bytes) {
105 using std::min;
106 88157 const HeaderType cur_block_size = min(block_size, size_in_bytes - cur_in);
107
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44088 times.
88157 assert(cur_in + cur_block_size <= size_in_bytes);
108
109 88157 uLongf out_len = block_buffer.capacity();
110 88157 uLong in_len = cur_block_size;
111
1/3
✗ Branch 1 not taken.
✓ Branch 2 taken 44088 times.
✗ Branch 3 not taken.
88157 if (compress2(block_buffer.data(), &out_len,
112 88157 in.data() + cur_in, in_len,
113
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 44088 times.
176314 _opts.compression_level) != Z_OK)
114 throw InvalidState(as_error("Error upon compression with ZLib"));
115
116
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 44088 times.
88157 assert(cur_out + out_len <= out.size());
117
1/3
✗ Branch 1 not taken.
✓ Branch 2 taken 44088 times.
✗ Branch 3 not taken.
88157 std::copy_n(block_buffer.data(),
118 out_len,
119 88157 out.data() + cur_out);
120 88157 cur_in += cur_block_size;
121 88157 cur_out += out_len;
122
1/2
✓ Branch 1 taken 44088 times.
✗ Branch 2 not taken.
88157 compressed_block_sizes.push_back(static_cast<HeaderType>(out_len));
123 }
124
125
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14447 times.
28889 if (cur_in != size_in_bytes)
126 throw InvalidState(as_error("(ZLIBCompressor) unexpected number of bytes processed"));
127
128
1/2
✓ Branch 1 taken 14447 times.
✗ Branch 2 not taken.
57778 return std::make_tuple(
129 28889 CompressedBlocks<HeaderType>{blocks, std::move(compressed_block_sizes)},
130 compressed
131
1/2
✓ Branch 1 taken 14447 times.
✗ Branch 2 not taken.
57778 );
132 28889 }
133
134 Options _opts;
135 };
136
137 inline constexpr ZLIB zlib; //!< Instance of the zlib compressor
138
139 #ifndef DOXYGEN
140 namespace Detail { inline constexpr bool _have_zlib = true; }
141 #endif // DOXYGEN
142
143 //! @} group Compression
144
145 } // end namespace GridFormat::Compression
146
147 #else // GRIDFORMAT_HAVE_ZLIB
148
149 namespace GridFormat::Compression {
150 namespace Detail { inline constexpr bool _have_zlib = false; }
151 class 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_
161