GridFormat 0.2.1
I/O-Library for grid-like data structures
Loading...
Searching...
No Matches
lz4.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_LZ4_HPP_
10#define GRIDFORMAT_COMPRESSION_LZ4_HPP_
11#if GRIDFORMAT_HAVE_LZ4
12
13#include <concepts>
14#include <utility>
15#include <vector>
16#include <cassert>
17#include <algorithm>
18#include <tuple>
19
20#include <lz4.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
35struct LZ4Options {
36 std::size_t block_size = default_block_size;
37 int acceleration_factor = 1; // LZ4_ACCELERATION_DEFAULT
38};
39
41class LZ4 {
42 using LZ4Byte = char;
43 static_assert(sizeof(typename Serialization::Byte) == sizeof(LZ4Byte));
44
45 struct BlockDecompressor {
46 using ByteType = LZ4Byte;
47
48 void operator()(std::span<const ByteType> in, std::span<ByteType> out) const {
49 int decompressed_length = LZ4_decompress_safe(
50 in.data(),
51 out.data(),
52 static_cast<int>(in.size()),
53 static_cast<int>(out.size())
54 );
55 if (decompressed_length != static_cast<int>(out.size()))
56 throw IOError("(LZ4Compressor) Error upon block decompression");
57 }
58 };
59
60 public:
61 using Options = LZ4Options;
62
63 explicit constexpr LZ4(Options opts = {})
64 : _opts(std::move(opts))
65 {}
66
67 template<std::integral HeaderType = std::size_t>
68 CompressedBlocks<HeaderType> compress(Serialization& in) const {
69 static_assert(sizeof(typename Serialization::Byte) == sizeof(LZ4Byte));
70 if (std::numeric_limits<HeaderType>::max() < in.size())
71 throw TypeError("Chosen HeaderType is too small for given number of bytes");
72 if (std::numeric_limits<HeaderType>::max() < _opts.block_size)
73 throw TypeError("Chosen HeaderType is too small for given block size");
74
75 auto [blocks, out] = _compress<HeaderType>(in.template as_span_of<const LZ4Byte>());
76 in = std::move(out);
77 in.resize(blocks.compressed_size());
78 return blocks;
79 }
80
81 template<typename HeaderType>
82 static void decompress(Serialization& in, const CompressedBlocks<HeaderType>& blocks) {
83 Compression::decompress(in, blocks, BlockDecompressor{});
84 }
85
86 static LZ4 with(Options opts) {
87 return LZ4{std::move(opts)};
88 }
89
90 private:
91 template<std::integral HeaderType>
92 auto _compress(std::span<const LZ4Byte> in) const {
93 HeaderType block_size = static_cast<HeaderType>(_opts.block_size);
94 HeaderType size_in_bytes = static_cast<HeaderType>(in.size());
95 Blocks<HeaderType> blocks{size_in_bytes, block_size};
96
97 Serialization compressed;
98 std::vector<LZ4Byte> block_buffer;
99 std::vector<HeaderType> compressed_block_sizes;
100 block_buffer.reserve(LZ4_COMPRESSBOUND(_opts.block_size));
101 compressed_block_sizes.reserve(blocks.number_of_blocks);
102 compressed.resize(block_buffer.capacity()*blocks.number_of_blocks);
103
104 HeaderType cur_in = 0;
105 HeaderType cur_out = 0;
106 auto out = compressed.template as_span_of<LZ4Byte>();
107 while (cur_in < size_in_bytes) {
108 using std::min;
109 const HeaderType cur_block_size = min(block_size, size_in_bytes - cur_in);
110 assert(cur_in + cur_block_size <= size_in_bytes);
111
112 const auto compressed_length = LZ4_compress_fast(
113 in.data() + cur_in, // const char* src
114 block_buffer.data(), // char* dst
115 cur_block_size, // src_size
116 block_buffer.capacity(), // dst_capacity
117 _opts.acceleration_factor // lz4 acc factor
118 );
119 if (compressed_length == 0)
120 throw InvalidState(as_error("Error upon compression with LZ4"));
121
122 assert(cur_out + compressed_length <= out.size());
123 std::copy_n(block_buffer.data(),
124 compressed_length,
125 out.data() + cur_out);
126 cur_in += cur_block_size;
127 cur_out += compressed_length;
128 compressed_block_sizes.push_back(static_cast<HeaderType>(compressed_length));
129 }
130
131 if (cur_in != size_in_bytes)
132 throw InvalidState(as_error("(LZ4Compressor) unexpected number of bytes processed"));
133
134 return std::make_tuple(
135 CompressedBlocks<HeaderType>{blocks, std::move(compressed_block_sizes)},
136 compressed
137 );
138 }
139
140 Options _opts;
141};
142
143inline constexpr LZ4 lz4;
144
145#ifndef DOXYGEN
146namespace Detail { inline constexpr bool _have_lz4 = true; }
147#endif // DOXYGEN
148
150
151} // end namespace GridFormat::Compression
152
153#else // GRIDFORMAT_HAVE_LZ4
154
155namespace GridFormat::Compression {
156
157namespace Detail { inline constexpr bool _have_lz4 = false; }
158
159class LZ4 {
160 public:
161 template<bool b = false, typename... Args>
162 explicit LZ4(Args&&...) { static_assert(b, "LZ4 compressor requires the LZ4 library."); }
163};
164
165} // namespace GridFormat::Compression
166
167#endif // GRIDFORMAT_HAVE_LZ4
168#endif // GRIDFORMAT_COMPRESSION_LZ4_HPP_
Compressor using the lz4 compression library.
Definition: lz4.hpp:41
Common classes used in the context of data compression.
Decompress compressed data.
constexpr LZ4 lz4
Instance of the lz4 compressor.
Definition: lz4.hpp:143
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 lz4 compressor.
Definition: lz4.hpp:35