| 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 | 719 | explicit constexpr ZLIB(Options opts = {}) | |
| 62 | 719 | : _opts(std::move(opts)) | |
| 63 | 719 | {} | |
| 64 | |||
| 65 | template<std::integral HeaderType = std::size_t> | ||
| 66 | 29249 | CompressedBlocks<HeaderType> compress(Serialization& in) const { | |
| 67 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 14627 times.
|
29249 | 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 14627 times.
|
29249 | 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 14627 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 14627 times.
✗ Branch 5 not taken.
|
29249 | auto [blocks, out] = _compress<HeaderType>(in.template as_span_of<const ZLIBByte>()); |
| 73 | 29249 | in = std::move(out); | |
| 74 |
2/4✓ Branch 1 taken 14627 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 14627 times.
✗ Branch 5 not taken.
|
29249 | in.resize(blocks.compressed_size()); |
| 75 |
1/2✓ Branch 1 taken 14627 times.
✗ Branch 2 not taken.
|
58498 | return blocks; |
| 76 | 29249 | } | |
| 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 | 445 | static ZLIB with(Options opts) { | |
| 84 | 445 | return ZLIB{std::move(opts)}; | |
| 85 | } | ||
| 86 | |||
| 87 | private: | ||
| 88 | template<std::integral HeaderType> | ||
| 89 | 29249 | auto _compress(std::span<const ZLIBByte> in) const { | |
| 90 | 29249 | HeaderType block_size = static_cast<HeaderType>(_opts.block_size); | |
| 91 | 29249 | HeaderType size_in_bytes = static_cast<HeaderType>(in.size()); | |
| 92 | 29249 | Blocks<HeaderType> blocks{size_in_bytes, block_size}; | |
| 93 | |||
| 94 | 29249 | Serialization compressed; | |
| 95 | 29249 | std::vector<ZLIBByte> block_buffer; | |
| 96 | 29249 | std::vector<HeaderType> compressed_block_sizes; | |
| 97 |
2/4✓ Branch 1 taken 14627 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 14627 times.
✗ Branch 5 not taken.
|
29249 | block_buffer.reserve(compressBound(_opts.block_size)); |
| 98 |
1/2✓ Branch 1 taken 14627 times.
✗ Branch 2 not taken.
|
29249 | compressed_block_sizes.reserve(blocks.number_of_blocks); |
| 99 |
1/2✓ Branch 2 taken 14627 times.
✗ Branch 3 not taken.
|
29249 | compressed.resize(block_buffer.capacity()*blocks.number_of_blocks); |
| 100 | |||
| 101 | 29249 | HeaderType cur_in = 0; | |
| 102 | 29249 | HeaderType cur_out = 0; | |
| 103 |
1/2✓ Branch 1 taken 14627 times.
✗ Branch 2 not taken.
|
29249 | auto out = compressed.template as_span_of<ZLIBByte>(); |
| 104 |
2/2✓ Branch 0 taken 44298 times.
✓ Branch 1 taken 14627 times.
|
117826 | while (cur_in < size_in_bytes) { |
| 105 | using std::min; | ||
| 106 | 88577 | const HeaderType cur_block_size = min(block_size, size_in_bytes - cur_in); | |
| 107 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 44298 times.
|
88577 | assert(cur_in + cur_block_size <= size_in_bytes); |
| 108 | |||
| 109 | 88577 | uLongf out_len = block_buffer.capacity(); | |
| 110 | 88577 | uLong in_len = cur_block_size; | |
| 111 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 44298 times.
✗ Branch 3 not taken.
|
88577 | if (compress2(block_buffer.data(), &out_len, |
| 112 | 88577 | in.data() + cur_in, in_len, | |
| 113 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 44298 times.
|
177154 | _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 44298 times.
|
88577 | assert(cur_out + out_len <= out.size()); |
| 117 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 44298 times.
✗ Branch 3 not taken.
|
88577 | std::copy_n(block_buffer.data(), |
| 118 | out_len, | ||
| 119 | 88577 | out.data() + cur_out); | |
| 120 | 88577 | cur_in += cur_block_size; | |
| 121 | 88577 | cur_out += out_len; | |
| 122 |
1/2✓ Branch 1 taken 44298 times.
✗ Branch 2 not taken.
|
88577 | compressed_block_sizes.push_back(static_cast<HeaderType>(out_len)); |
| 123 | } | ||
| 124 | |||
| 125 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14627 times.
|
29249 | if (cur_in != size_in_bytes) |
| 126 | ✗ | throw InvalidState(as_error("(ZLIBCompressor) unexpected number of bytes processed")); | |
| 127 | |||
| 128 |
1/2✓ Branch 1 taken 14627 times.
✗ Branch 2 not taken.
|
58498 | return std::make_tuple( |
| 129 | 29249 | CompressedBlocks<HeaderType>{blocks, std::move(compressed_block_sizes)}, | |
| 130 | compressed | ||
| 131 |
1/2✓ Branch 1 taken 14627 times.
✗ Branch 2 not taken.
|
58498 | ); |
| 132 | 29249 | } | |
| 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 |