RPNX::Compress
Self-contained C++20 compression and ZIP library
 
Loading...
Searching...
No Matches
compression.hpp
Go to the documentation of this file.
1#ifndef RPNX_COMPRESSION_COMPRESSION_HPP
2#define RPNX_COMPRESSION_COMPRESSION_HPP
3
4#include <cstddef>
5#include <cstdint>
6#include <exception>
7#include <iterator>
8#include <optional>
9#include <span>
10#include <string>
11#include <type_traits>
12#include <utility>
13#include <vector>
14
15/**
16 * @file
17 * @brief Compression formats, options, errors, and buffer or iterator APIs.
18 */
19
20/**
21 * @brief Facilities for creating and decoding supported compressed streams.
22 *
23 * The namespace exposes allocating and caller-owned span overloads for ABI-stable
24 * use, plus iterator overloads for single-pass streaming. All implementations are
25 * self-contained and report format-specific failures through compression_error.
26 */
28{
29
30 /** @brief Wire formats recognized by the library. */
31 enum class format : std::uint8_t
32 {
33 deflate, ///< Raw RFC 1951 DEFLATE stream.
34 zlib, ///< RFC 1950 zlib wrapper around DEFLATE.
35 gzip, ///< RFC 1952 gzip member or concatenated members.
36 bzip2, ///< bzip2 stream.
37 xz, ///< xz container containing LZMA2 data.
38 zstandard, ///< Zstandard frame.
39 lz4_frame, ///< LZ4 frame.
40 zip ///< ZIP32 archive; use create_zip() and extract_zip().
41 };
42
43 /** @brief Stable machine-readable categories reported by compression_error. */
44 enum class error_code : std::uint8_t
45 {
46 invalid_data, ///< The input does not conform to the selected format.
47 invalid_option, ///< An option or format value is outside its accepted range.
48 output_limit_exceeded, ///< Decoding would exceed a configured resource limit.
49 trailing_data, ///< Bytes remain after the permitted stream members.
50 unsupported_feature, ///< Valid input requires a format feature not implemented by the library.
51 backend_failure, ///< A codec encountered an internal failure not covered by another category.
52 insufficient_output_space ///< A caller-owned output span cannot hold the complete result.
53 };
54
55 /** @brief Options shared by compression operations. */
57 {
58 /**
59 * @brief Optional format-specific compression level.
60 *
61 * The accepted ranges are 0--9 for DEFLATE, zlib, and gzip; 1--9 for
62 * bzip2; 0--9 for xz; 0--22 for Zstandard; and 0--12 for LZ4. An empty
63 * value selects the codec's default level.
64 */
65 std::optional< std::int32_t > level;
66 };
67
68 /** @brief Resource and stream-validation policy for decompression operations. */
70 {
71 /**
72 * @brief Maximum total number of bytes the operation may emit.
73 *
74 * Decoding stops with error_code::output_limit_exceeded before the
75 * returned output grows beyond this value. The default is one GiB.
76 */
77 std::size_t maximum_output_size = 1024U * 1024U * 1024U;
78
79 /**
80 * @brief Whether to decode adjacent members for formats that define concatenation.
81 *
82 * When false, bytes following the first complete stream are rejected.
83 * When true, the decoder treats adjacent input as additional members of
84 * the selected format.
85 */
87 };
88
89 /**
90 * @brief Exception raised for malformed streams, invalid options, and codec failures.
91 *
92 * The error category and stream format are stable programmatic context. The
93 * diagnostic returned by what() is intended for humans and is not a stable API.
94 */
95 class compression_error final : public std::exception
96 {
97 public:
98 /**
99 * @brief Constructs a compression exception.
100 * @param code Stable category for the failure.
101 * @param stream_format Wire format being processed when the failure occurred.
102 * @param message Human-readable diagnostic retained by the exception.
103 */
105
106 /**
107 * @brief Returns the human-readable diagnostic.
108 * @return A null-terminated string valid until the exception is destroyed.
109 */
110 [[nodiscard]] char const* what() const noexcept override;
111
112 /**
113 * @brief Returns the stable error category.
114 * @return The category supplied at construction.
115 */
116 [[nodiscard]] error_code code() const noexcept;
117
118 /**
119 * @brief Returns the stream format involved in the error.
120 * @return The format supplied at construction.
121 */
122 [[nodiscard]] format stream_format() const noexcept;
123
124 private:
125 error_code m_code;
126 format m_stream_format;
127 std::string m_message;
128 };
129
130 /**
131 * @brief Compresses a contiguous byte buffer into an owning result.
132 * @param stream_format Wire format to produce. format::zip is not accepted.
133 * @param input Uncompressed bytes. The span is only borrowed for this call.
134 * @param options Format-specific compression settings.
135 * @return The complete encoded stream.
136 * @throws compression_error If the format or options are invalid or encoding fails.
137 */
138 [[nodiscard]] std::vector< std::byte > compress(format stream_format, std::span< std::byte const > input, compression_options const& options = {});
139
140 /**
141 * @brief Decompresses a contiguous byte buffer into an owning result.
142 * @param stream_format Wire format of @p input. format::zip is not accepted.
143 * @param input Complete compressed stream. The span is only borrowed for this call.
144 * @param options Output limits and concatenated-stream policy.
145 * @return The complete uncompressed data.
146 * @throws compression_error If input is invalid, unsupported, or exceeds a limit.
147 */
148 [[nodiscard]] std::vector< std::byte > decompress(format stream_format, std::span< std::byte const > input, decompression_options const& options = {});
149
150 /**
151 * @brief Compresses into caller-owned storage.
152 * @param stream_format Wire format to produce. format::zip is not accepted.
153 * @param input Uncompressed bytes. The span is only borrowed for this call.
154 * @param output Destination storage.
155 * @param options Format-specific compression settings.
156 * @return Number of initialized bytes at the beginning of @p output.
157 * @throws compression_error With error_code::insufficient_output_space if the
158 * destination is exhausted, or with another category for a codec failure.
159 */
160 [[nodiscard]] std::size_t compress(format stream_format, std::span< std::byte const > input, std::span< std::byte > output, compression_options const& options = {});
161
162 /**
163 * @brief Decompresses into caller-owned storage.
164 * @param stream_format Wire format of @p input. format::zip is not accepted.
165 * @param input Complete compressed stream. The span is only borrowed for this call.
166 * @param output Destination storage.
167 * @param options Output limits and concatenated-stream policy.
168 * @return Number of initialized bytes at the beginning of @p output.
169 * @throws compression_error With error_code::insufficient_output_space if the
170 * destination is exhausted, or with another category for invalid input or limits.
171 */
172 [[nodiscard]] std::size_t decompress(format stream_format, std::span< std::byte const > input, std::span< std::byte > output, decompression_options const& options = {});
173
174} // namespace rpnx::compression
175
181
182namespace rpnx::compression
183{
184
185 /**
186 * @brief Compresses an iterator range into an STL output iterator.
187 * @tparam input_iterator Single-pass iterator whose value type is std::byte or a one-byte integral type.
188 * @tparam sentinel Sentinel for @p first.
189 * @tparam output_iterator Output iterator accepting std::byte or std::uint8_t assignments.
190 * @param stream_format Wire format to produce. format::zip is not accepted.
191 * @param first Iterator to the first uncompressed byte.
192 * @param last Sentinel past the final uncompressed byte.
193 * @param output Destination iterator, taken and returned by value.
194 * @param options Format-specific compression settings.
195 * @return The destination iterator advanced past the final encoded byte.
196 * @throws compression_error If the format or options are invalid or encoding fails.
197 */
198 template < std::input_iterator input_iterator, std::sentinel_for< input_iterator > sentinel, typename output_iterator >
199 output_iterator compress(format stream_format, input_iterator first, sentinel last, output_iterator output, compression_options const& options = {})
200 {
201 using input_value = std::remove_cv_t< std::iter_value_t< input_iterator > >;
202 static_assert(std::is_same_v< input_value, std::byte > || (std::is_integral_v< input_value > && sizeof(input_value) == 1U), "compression input iterators must contain byte-sized values");
203
204 if (stream_format == format::deflate || stream_format == format::zlib || stream_format == format::gzip)
205 {
206 return deflate_codec::compress(stream_format, first, last, output, options);
207 }
208 if (stream_format == format::lz4_frame)
209 {
210 return lz4_codec::compress(first, last, output, options);
211 }
212 if (stream_format == format::zstandard)
213 {
214 return zstandard_codec::compress(first, last, output, options);
215 }
216 if (stream_format == format::bzip2)
217 {
218 return bzip2_codec::compress(first, last, output, options);
219 }
220 if (stream_format == format::xz)
221 {
222 return xz_codec::compress(first, last, output, options);
223 }
224 if (stream_format == format::zip)
225 {
226 throw compression_error(error_code::unsupported_feature, stream_format, "use create_zip for ZIP archives");
227 }
228 throw compression_error(error_code::invalid_option, stream_format, "unknown compression format");
229 }
230
231 /**
232 * @brief Compresses an iterator range with compile-time format dispatch.
233 * @tparam stream_format Wire format to produce. format::zip is not accepted.
234 * @tparam input_iterator Single-pass iterator whose value type is std::byte or a one-byte integral type.
235 * @tparam sentinel Sentinel for @p first.
236 * @tparam output_iterator Output iterator accepting std::byte or std::uint8_t assignments.
237 * @param first Iterator to the first uncompressed byte.
238 * @param last Sentinel past the final uncompressed byte.
239 * @param output Destination iterator, taken and returned by value.
240 * @param options Format-specific compression settings.
241 * @return The destination iterator advanced past the final encoded byte.
242 * @throws compression_error If the options are invalid or encoding fails.
243 */
244 template < format stream_format, std::input_iterator input_iterator, std::sentinel_for< input_iterator > sentinel, typename output_iterator >
245 output_iterator compress(input_iterator first, sentinel last, output_iterator output, compression_options const& options = {})
246 {
247 if constexpr (stream_format == format::deflate || stream_format == format::zlib || stream_format == format::gzip)
248 {
249 return deflate_codec::compress(stream_format, first, last, output, options);
250 }
251 else if constexpr (stream_format == format::lz4_frame)
252 {
253 return lz4_codec::compress(first, last, output, options);
254 }
255 else if constexpr (stream_format == format::zstandard)
256 {
257 return zstandard_codec::compress(first, last, output, options);
258 }
259 else if constexpr (stream_format == format::bzip2)
260 {
261 return bzip2_codec::compress(first, last, output, options);
262 }
263 else if constexpr (stream_format == format::xz)
264 {
265 return xz_codec::compress(first, last, output, options);
266 }
267 else
268 {
269 return compress(stream_format, first, last, output, options);
270 }
271 }
272
273 /**
274 * @brief Decompresses an iterator range into an STL output iterator.
275 * @tparam input_iterator Single-pass iterator whose value type is std::byte or a one-byte integral type.
276 * @tparam sentinel Sentinel for @p first.
277 * @tparam output_iterator Output iterator accepting std::byte or std::uint8_t assignments.
278 * @param stream_format Wire format of the input. format::zip is not accepted.
279 * @param first Iterator to the first compressed byte.
280 * @param last Sentinel past the final compressed byte.
281 * @param output Destination iterator, taken and returned by value.
282 * @param options Output limits and concatenated-stream policy.
283 * @return The destination iterator advanced past the final uncompressed byte.
284 * @throws compression_error If input is invalid, unsupported, or exceeds a limit.
285 */
286 template < std::input_iterator input_iterator, std::sentinel_for< input_iterator > sentinel, typename output_iterator >
287 output_iterator decompress(format stream_format, input_iterator first, sentinel last, output_iterator output, decompression_options const& options = {})
288 {
289 using input_value = std::remove_cv_t< std::iter_value_t< input_iterator > >;
290 static_assert(std::is_same_v< input_value, std::byte > || (std::is_integral_v< input_value > && sizeof(input_value) == 1U), "decompression input iterators must contain byte-sized values");
291
292 if (stream_format == format::deflate || stream_format == format::zlib || stream_format == format::gzip)
293 {
294 return deflate_codec::decompress(stream_format, first, last, output, options);
295 }
296 if (stream_format == format::lz4_frame)
297 {
298 return lz4_codec::decompress(first, last, output, options);
299 }
300 if (stream_format == format::zstandard)
301 {
302 return zstandard_codec::decompress(first, last, output, options);
303 }
304 if (stream_format == format::bzip2)
305 {
306 return bzip2_codec::decompress(first, last, output, options);
307 }
308 if (stream_format == format::xz)
309 {
310 return xz_codec::decompress(first, last, output, options);
311 }
312 if (stream_format == format::zip)
313 {
314 throw compression_error(error_code::unsupported_feature, stream_format, "use extract_zip for ZIP archives");
315 }
316 throw compression_error(error_code::invalid_option, stream_format, "unknown compression format");
317 }
318
319 /**
320 * @brief Decompresses an iterator range with compile-time format dispatch.
321 * @tparam stream_format Wire format of the input. format::zip is not accepted.
322 * @tparam input_iterator Single-pass iterator whose value type is std::byte or a one-byte integral type.
323 * @tparam sentinel Sentinel for @p first.
324 * @tparam output_iterator Output iterator accepting std::byte or std::uint8_t assignments.
325 * @param first Iterator to the first compressed byte.
326 * @param last Sentinel past the final compressed byte.
327 * @param output Destination iterator, taken and returned by value.
328 * @param options Output limits and concatenated-stream policy.
329 * @return The destination iterator advanced past the final uncompressed byte.
330 * @throws compression_error If input is invalid, unsupported, or exceeds a limit.
331 */
332 template < format stream_format, std::input_iterator input_iterator, std::sentinel_for< input_iterator > sentinel, typename output_iterator >
333 output_iterator decompress(input_iterator first, sentinel last, output_iterator output, decompression_options const& options = {})
334 {
335 if constexpr (stream_format == format::deflate || stream_format == format::zlib || stream_format == format::gzip)
336 {
337 return deflate_codec::decompress(stream_format, first, last, output, options);
338 }
339 else if constexpr (stream_format == format::lz4_frame)
340 {
341 return lz4_codec::decompress(first, last, output, options);
342 }
343 else if constexpr (stream_format == format::zstandard)
344 {
345 return zstandard_codec::decompress(first, last, output, options);
346 }
347 else if constexpr (stream_format == format::bzip2)
348 {
349 return bzip2_codec::decompress(first, last, output, options);
350 }
351 else if constexpr (stream_format == format::xz)
352 {
353 return xz_codec::decompress(first, last, output, options);
354 }
355 else
356 {
357 return decompress(stream_format, first, last, output, options);
358 }
359 }
360
361} // namespace rpnx::compression
362
363#endif
Native bzip2 block transforms, entropy coding, and stream adapters.
Exception raised for malformed streams, invalid options, and codec failures.
compression_error(error_code code, format stream_format, std::string message)
Constructs a compression exception.
char const * what() const noexcept override
Returns the human-readable diagnostic.
format stream_format() const noexcept
Returns the stream format involved in the error.
error_code code() const noexcept
Returns the stable error category.
Native raw DEFLATE, zlib, and gzip coding implementation.
Native LZ4 block and frame coding implementation.
output_iterator compress(input_iterator first, sentinel last, output_iterator output, compression_options const &options)
Compresses an iterator range as a native bzip2 stream.
Definition bzip2.hpp:834
output_iterator decompress(input_iterator first, sentinel last, output_iterator output, decompression_options const &options)
Decompresses one or more native bzip2 streams.
Definition bzip2.hpp:876
output_iterator decompress(format stream_format, input_iterator first, sentinel last, output_iterator output, decompression_options const &options)
Decompresses raw DEFLATE, zlib, or gzip input.
Definition deflate.hpp:514
output_iterator compress(format stream_format, input_iterator first, sentinel last, output_iterator output, compression_options const &options)
Compresses an iterator range as raw DEFLATE, zlib, or gzip.
Definition deflate.hpp:408
output_iterator decompress(input_iterator first, sentinel last, output_iterator output, decompression_options const &options)
Decompresses one or more LZ4 frames.
Definition lz4.hpp:401
output_iterator compress(input_iterator first, sentinel last, output_iterator output, compression_options const &options)
Compresses an iterator range as an LZ4 frame.
Definition lz4.hpp:336
output_iterator decompress(input_iterator first, sentinel last, output_iterator output, decompression_options const &options)
Decompresses one or more xz streams containing LZMA2.
Definition xz.hpp:925
output_iterator compress(input_iterator first, sentinel last, output_iterator output, compression_options const &options)
Compresses an iterator range as an xz stream containing LZMA2.
Definition xz.hpp:757
output_iterator decompress(input_iterator first, sentinel last, output_iterator output, decompression_options const &options)
Decompresses one or more Zstandard or skippable frames.
output_iterator compress(input_iterator first, sentinel last, output_iterator output, compression_options const &options)
Compresses an iterator range as a Zstandard frame.
Facilities for creating and decoding supported compressed streams.
std::vector< std::byte > decompress(format stream_format, std::span< std::byte const > input, decompression_options const &options={})
Decompresses a contiguous byte buffer into an owning result.
format
Wire formats recognized by the library.
@ gzip
RFC 1952 gzip member or concatenated members.
@ zlib
RFC 1950 zlib wrapper around DEFLATE.
@ deflate
Raw RFC 1951 DEFLATE stream.
@ zip
ZIP32 archive; use create_zip() and extract_zip().
@ zstandard
Zstandard frame.
@ xz
xz container containing LZMA2 data.
error_code
Stable machine-readable categories reported by compression_error.
@ trailing_data
Bytes remain after the permitted stream members.
@ output_limit_exceeded
Decoding would exceed a configured resource limit.
@ backend_failure
A codec encountered an internal failure not covered by another category.
@ insufficient_output_space
A caller-owned output span cannot hold the complete result.
@ invalid_option
An option or format value is outside its accepted range.
@ unsupported_feature
Valid input requires a format feature not implemented by the library.
@ invalid_data
The input does not conform to the selected format.
std::vector< std::byte > compress(format stream_format, std::span< std::byte const > input, compression_options const &options={})
Compresses a contiguous byte buffer into an owning result.
Options shared by compression operations.
std::optional< std::int32_t > level
Optional format-specific compression level.
Resource and stream-validation policy for decompression operations.
std::size_t maximum_output_size
Maximum total number of bytes the operation may emit.
bool allow_concatenated_streams
Whether to decode adjacent members for formats that define concatenation.
Native xz container, LZMA2, and range-coding implementation.
Native Zstandard frame, entropy-table, and sequence implementation.