qb  2.0.0.0
C++17 Actor Framework
qb Issue Watch Star Fork Follow @isndev
Loading...
Searching...
No Matches
compression.h
Go to the documentation of this file.
1
28
29#include <functional>
30#include <limits>
31#include <stdexcept>
32#include <string>
33
34#ifndef QB_IO_COMPRESSION_H
35#define QB_IO_COMPRESSION_H
36
37#ifndef QB_IO_WITH_ZLIB
38#error "missing Z Library"
39#endif
40
41#include <zlib.h>
43
44namespace qb {
45namespace compression {
46
62
73 std::size_t
75 std::size_t
77 bool done;
80};
81
92public:
97 virtual const std::string &algorithm() const = 0;
98
111 virtual std::size_t compress(const uint8_t *input, std::size_t input_size,
112 uint8_t *output, std::size_t output_size,
113 operation_hint hint, std::size_t &input_bytes_processed,
114 bool &done) = 0;
115
122 virtual void reset() = 0;
123
127 virtual ~compress_provider() = default;
128};
129
140public:
145 virtual const std::string &algorithm() const = 0;
146
159 virtual std::size_t decompress(const uint8_t *input, std::size_t input_size,
160 uint8_t *output, std::size_t output_size,
161 operation_hint hint,
162 std::size_t &input_bytes_processed, bool &done) = 0;
163
170 virtual void reset() = 0;
171
175 virtual ~decompress_provider() = default;
176};
177
188public:
193 virtual const std::string &algorithm() const = 0;
194
199 virtual std::unique_ptr<compress_provider> make_compressor() const = 0;
200
204 virtual ~compress_factory() = default;
205};
206
217public:
222 virtual const std::string &algorithm() const = 0;
223
232 virtual uint16_t weight() const = 0;
233
238 virtual std::unique_ptr<decompress_provider> make_decompressor() const = 0;
239
243 virtual ~decompress_factory() = default;
244};
245
254namespace builtin {
255
261
266namespace algorithm {
267
271constexpr const char *const GZIP = "gzip";
272
276constexpr const char *const DEFLATE = "deflate";
277
283bool supported(const std::string &algorithm);
284} // namespace algorithm
285
292std::unique_ptr<compress_provider> make_compressor(const std::string &algorithm);
293
300std::unique_ptr<decompress_provider> make_decompressor(const std::string &algorithm);
301
306const std::vector<std::shared_ptr<compress_factory>> get_compress_factories();
307
313std::shared_ptr<compress_factory> get_compress_factory(const std::string &algorithm);
314
319const std::vector<std::shared_ptr<decompress_factory>> get_decompress_factories();
320
326std::shared_ptr<decompress_factory> get_decompress_factory(const std::string &algorithm);
327
337std::unique_ptr<compress_provider> make_gzip_compressor(int compressionLevel, int method,
338 int strategy, int memLevel);
339
349std::unique_ptr<compress_provider>
350make_deflate_compressor(int compressionLevel, int method, int strategy, int memLevel);
351
352} // namespace builtin
353
361std::shared_ptr<compress_factory> make_compress_factory(
362 const std::string &algorithm,
363 std::function<std::unique_ptr<compress_provider>()> make_compressor);
364
373std::shared_ptr<decompress_factory> make_decompress_factory(
374 const std::string &algorithm, uint16_t weight,
375 std::function<std::unique_ptr<decompress_provider>()> make_decompressor);
376
393template <typename Output>
394size_t
395compress(Output &output, const char *data, std::size_t size, int level,
396 int window_bits) {
397#ifdef DEBUG
398 // Verify if size input will fit into unsigned int, type used for zlib's avail_in
399 if (size > std::numeric_limits<unsigned int>::max()) {
400 throw std::runtime_error("size arg is too large to fit into unsigned int type");
401 }
402#endif
403
404 z_stream deflate_s;
405 deflate_s.zalloc = Z_NULL;
406 deflate_s.zfree = Z_NULL;
407 deflate_s.opaque = Z_NULL;
408 deflate_s.avail_in = 0;
409 deflate_s.next_in = Z_NULL;
410
411 // The windowBits parameter is the base two logarithm of the window size (the size of
412 // the history buffer). It should be in the range 8..15 for this version of the
413 // library. Larger values of this parameter result in better compression at the
414 // expense of memory usage. This range of values also changes the decoding type:
415 // -8 to -15 for raw deflate
416 // 8 to 15 for zlib
417 // (8 to 15) + 16 for gzip
418 // (8 to 15) + 32 to automatically detect gzip/zlib header
419 constexpr int mem_level = 8;
420 // The memory requirements for deflate are (in bytes):
421 // (1 << (window_bits+2)) + (1 << (mem_level+9))
422 // with a default value of 8 for mem_level and our window_bits of 15
423 // this is 128Kb
424
425 DISABLE_WARNING_PUSH
426 DISABLE_WARNING_OLD_STYLE_CAST
427 if (deflateInit2(&deflate_s, level, Z_DEFLATED, window_bits, mem_level,
428 Z_DEFAULT_STRATEGY) != Z_OK) {
429 throw std::runtime_error("deflate init failed");
430 }
431 DISABLE_WARNING_POP
432
433 deflate_s.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
434 deflate_s.avail_in = static_cast<unsigned int>(size);
435
436 std::size_t size_compressed = 0;
437 do {
438 size_t increase = size / 2 + 1024;
439 if (output.size() < (size_compressed + increase)) {
440 output.resize(size_compressed + increase);
441 }
442 // There is no way we see that "increase" would not fit in an unsigned int,
443 // hence we use static cast here to avoid -Wshorten-64-to-32 error
444 deflate_s.avail_out = static_cast<unsigned int>(increase);
445 deflate_s.next_out = reinterpret_cast<Bytef *>((&output[0] + size_compressed));
446 // From http://www.zlib.net/zlib_how.html
447 // "deflate() has a return value that can indicate errors, yet we do not check it
448 // here. Why not? Well, it turns out that deflate() can do no wrong here."
449 // Basically only possible error is from deflateInit not working properly
450 ::deflate(&deflate_s, Z_FINISH);
451 size_compressed += (increase - deflate_s.avail_out);
452 } while (deflate_s.avail_out == 0);
453
454 deflateEnd(&deflate_s);
455 output.resize(size_compressed);
456 return size_compressed;
457}
458
469template <>
470size_t compress(qb::allocator::pipe<char> &output, const char *data, std::size_t size,
471 int level, int window_bits);
472
490template <typename Output>
491std::size_t
492uncompress(Output &output, const char *data, std::size_t size, std::size_t max,
493 int window_bits) {
494 z_stream inflate_s;
495
496 inflate_s.zalloc = Z_NULL;
497 inflate_s.zfree = Z_NULL;
498 inflate_s.opaque = Z_NULL;
499 inflate_s.avail_in = 0;
500 inflate_s.next_in = Z_NULL;
501
502 // The windowBits parameter is the base two logarithm of the window size (the size of
503 // the history buffer). It should be in the range 8..15 for this version of the
504 // library. Larger values of this parameter result in better compression at the
505 // expense of memory usage. This range of values also changes the decoding type:
506 // -8 to -15 for raw deflate
507 // 8 to 15 for zlib
508 // (8 to 15) + 16 for gzip
509 // (8 to 15) + 32 to automatically detect gzip/zlib header
510
511 DISABLE_WARNING_PUSH
512 DISABLE_WARNING_OLD_STYLE_CAST
513 if (inflateInit2(&inflate_s, window_bits) != Z_OK) {
514 throw std::runtime_error("inflate init failed");
515 }
516 DISABLE_WARNING_POP
517 inflate_s.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
518
519#ifdef DEBUG
520 // Verify if size (long type) input will fit into unsigned int, type used for zlib's
521 // avail_in
522 std::uint64_t size_64 = size * 2;
523 if (size_64 > std::numeric_limits<unsigned int>::max()) {
524 inflateEnd(&inflate_s);
525 throw std::runtime_error(
526 "size arg is too large to fit into unsigned int type x2");
527 }
528#endif
529 if (max && (size > max || (size * 2) > max)) {
530 inflateEnd(&inflate_s);
531 throw std::runtime_error(
532 "size may use more memory than intended when decompressing");
533 }
534 inflate_s.avail_in = static_cast<unsigned int>(size);
535 std::size_t size_uncompressed = 0;
536 do {
537 std::size_t resize_to = size_uncompressed + 2 * size;
538 if (max && resize_to > max) {
539 inflateEnd(&inflate_s);
540 throw std::runtime_error("size of output string will use more memory then "
541 "intended when decompressing");
542 }
543 output.resize(resize_to);
544 inflate_s.avail_out = static_cast<unsigned int>(2 * size);
545 inflate_s.next_out = reinterpret_cast<Bytef *>(&output[0] + size_uncompressed);
546 int ret = inflate(&inflate_s, Z_FINISH);
547 if (ret != Z_STREAM_END && ret != Z_OK && ret != Z_BUF_ERROR) {
548 std::string error_msg = inflate_s.msg;
549 inflateEnd(&inflate_s);
550 throw std::runtime_error(error_msg);
551 }
552
553 size_uncompressed += (2 * size - inflate_s.avail_out);
554 } while (inflate_s.avail_out == 0);
555 inflateEnd(&inflate_s);
556 output.resize(size_uncompressed);
557 return size_uncompressed;
558}
559
570template <>
571size_t uncompress(qb::allocator::pipe<char> &output, const char *data, std::size_t size,
572 std::size_t max, int window_bits);
573
582namespace deflate {
583
594template <typename Output>
595size_t
596compress(Output &output, const char *data, std::size_t size,
597 int level = Z_DEFAULT_COMPRESSION) {
598 constexpr int window_bits = 15; // gzip with windowbits of 15
599 return compression::compress(output, data, size, level, window_bits);
600}
601
611template <>
612size_t compress(qb::allocator::pipe<char> &output, const char *data, std::size_t size,
613 int level);
614
624 const char *data;
625 std::size_t size;
626 int level = Z_DEFAULT_COMPRESSION;
628};
629
638template <typename Output>
639Output &
640compress(Output &output, to_compress &info) {
641 info.size_compressed = compress<Output>(output, info.data, info.size, info.level);
642 return output;
643}
644
653std::string compress(const char *data, std::size_t size,
654 int level = Z_DEFAULT_COMPRESSION);
655
666template <typename Output>
667std::size_t
668uncompress(Output &output, const char *data, std::size_t size, std::size_t max = 0) {
669 constexpr int window_bits = 0; // deflate
670
671 return compression::uncompress(output, data, size, max, window_bits);
672}
673
683template <>
684size_t uncompress(qb::allocator::pipe<char> &output, const char *data, std::size_t size,
685 std::size_t level);
686
696 const char *data;
697 std::size_t size;
698 std::size_t max = 0;
699 std::size_t size_uncompressed;
700};
701
710template <typename Output>
711Output &
712uncompress(Output &output, to_uncompress &info) {
713 info.size_uncompressed = uncompress<Output>(output, info.data, info.size, info.max);
714 return output;
715}
716
724std::string uncompress(const char *data, std::size_t size);
725} // namespace deflate
726
735namespace gzip {
746inline bool
747is_compressed(const char *data, std::size_t size) {
748 return size > 2 && (
749 // zlib
750 (static_cast<uint8_t>(data[0]) == 0x78 &&
751 (static_cast<uint8_t>(data[1]) == 0x9C ||
752 static_cast<uint8_t>(data[1]) == 0x01 ||
753 static_cast<uint8_t>(data[1]) == 0xDA ||
754 static_cast<uint8_t>(data[1]) == 0x5E)) ||
755 // gzip
756 (static_cast<uint8_t>(data[0]) == 0x1F &&
757 static_cast<uint8_t>(data[1]) == 0x8B));
758}
759
770template <typename Output>
771size_t
772compress(Output &output, const char *data, std::size_t size,
773 int level = Z_DEFAULT_COMPRESSION) {
774 constexpr int window_bits = 15 + 16; // gzip with windowbits of 15
775 return compression::compress(output, data, size, level, window_bits);
776}
777
787template <>
788size_t compress(qb::allocator::pipe<char> &output, const char *data, std::size_t size,
789 int level);
790
800 const char *data;
801 std::size_t size;
802 int level = Z_DEFAULT_COMPRESSION;
804};
805
814template <typename Output>
815Output &
816compress(Output &output, to_compress &info) {
817 info.size_compressed = compress<Output>(output, info.data, info.size, info.level);
818 return output;
819}
820
829std::string compress(const char *data, std::size_t size,
830 int level = Z_DEFAULT_COMPRESSION);
831
842template <typename Output>
843std::size_t
844uncompress(Output &output, const char *data, std::size_t size, std::size_t max = 0) {
845 constexpr int window_bits = 15 + 32; // auto with windowbits of 15
846
847 return compression::uncompress(output, data, size, max, window_bits);
848}
849
859template <>
860size_t uncompress(qb::allocator::pipe<char> &output, const char *data, std::size_t size,
861 std::size_t level);
862
872 const char *data;
873 std::size_t size;
874 std::size_t max = 0;
875 std::size_t size_uncompressed;
876};
877
886template <typename Output>
887Output &
888uncompress(Output &output, to_uncompress &info) {
889 info.size_uncompressed = uncompress<Output>(output, info.data, info.size, info.max);
890 return output;
891}
892
900std::string uncompress(const char *data, std::size_t size);
901} // namespace gzip
902
903} // namespace compression
904} // namespace qb
905
906namespace qb::allocator {
907
917template <>
919
929template <>
931
941template <>
943
954template <>
956
957} // namespace qb::allocator
958
966namespace qb::gzip {
967using namespace qb::compression::gzip;
968}
969
977namespace qb::deflate {
978using namespace qb::compression::deflate;
979}
980
981#endif // QB_IO_COMPRESSION_H
Extensible buffer optimized for performance.
Definition pipe.h:552
Factory interface for creating compression providers.
Definition compression.h:187
virtual std::unique_ptr< compress_provider > make_compressor() const =0
Create a new compressor instance.
virtual const std::string & algorithm() const =0
Get the name of the compression algorithm.
virtual ~compress_factory()=default
Virtual destructor.
Abstract interface for compression algorithm providers.
Definition compression.h:91
virtual ~compress_provider()=default
Virtual destructor.
virtual std::size_t compress(const uint8_t *input, std::size_t input_size, uint8_t *output, std::size_t output_size, operation_hint hint, std::size_t &input_bytes_processed, bool &done)=0
Compress a block of data.
virtual const std::string & algorithm() const =0
Get the name of the compression algorithm.
virtual void reset()=0
Reset the compressor to its initial state.
Factory interface for creating decompression providers.
Definition compression.h:216
virtual ~decompress_factory()=default
Virtual destructor.
virtual uint16_t weight() const =0
Get the weight of this decompression algorithm.
virtual std::unique_ptr< decompress_provider > make_decompressor() const =0
Create a new decompressor instance.
virtual const std::string & algorithm() const =0
Get the name of the decompression algorithm.
Abstract interface for decompression algorithm providers.
Definition compression.h:139
virtual ~decompress_provider()=default
Virtual destructor.
virtual const std::string & algorithm() const =0
Get the name of the decompression algorithm.
virtual void reset()=0
Reset the decompressor to its initial state.
virtual std::size_t decompress(const uint8_t *input, std::size_t input_size, uint8_t *output, std::size_t output_size, operation_hint hint, std::size_t &input_bytes_processed, bool &done)=0
Decompress a block of data.
std::unique_ptr< compress_provider > make_compressor(const std::string &algorithm)
Create a compressor for the specified algorithm.
std::shared_ptr< decompress_factory > make_decompress_factory(const std::string &algorithm, uint16_t weight, std::function< std::unique_ptr< decompress_provider >()> make_decompressor)
Create a custom decompression factory.
size_t compress(Output &output, const char *data, std::size_t size, int level=Z_DEFAULT_COMPRESSION)
Compress data using deflate algorithm with a generic output container.
Definition compression.h:596
std::size_t uncompress(Output &output, const char *data, std::size_t size, std::size_t max, int window_bits)
Uncompress data using a generic output container.
Definition compression.h:492
std::unique_ptr< compress_provider > make_gzip_compressor(int compressionLevel, int method, int strategy, int memLevel)
Create a gzip compressor with custom parameters.
std::unique_ptr< decompress_provider > make_decompressor(const std::string &algorithm)
Create a decompressor for the specified algorithm.
std::shared_ptr< compress_factory > make_compress_factory(const std::string &algorithm, std::function< std::unique_ptr< compress_provider >()> make_compressor)
Create a custom compression factory.
constexpr const char *const DEFLATE
Identifier for the deflate compression algorithm.
Definition compression.h:276
std::size_t uncompress(Output &output, const char *data, std::size_t size, std::size_t max=0)
Uncompress data using deflate algorithm with a generic output container.
Definition compression.h:668
size_t compress(Output &output, const char *data, std::size_t size, int level, int window_bits)
Compress data using a generic output container.
Definition compression.h:395
std::shared_ptr< compress_factory > get_compress_factory(const std::string &algorithm)
Get a specific compression factory by algorithm name.
bool supported(const std::string &algorithm)
Check if a specific compression algorithm is supported.
std::unique_ptr< compress_provider > make_deflate_compressor(int compressionLevel, int method, int strategy, int memLevel)
Create a deflate compressor with custom parameters.
size_t compress(Output &output, const char *data, std::size_t size, int level=Z_DEFAULT_COMPRESSION)
Compress data using gzip algorithm with a generic output container.
Definition compression.h:772
bool supported()
Check if compression support is available.
constexpr const char *const GZIP
Identifier for the gzip compression algorithm.
Definition compression.h:271
const std::vector< std::shared_ptr< decompress_factory > > get_decompress_factories()
Get all available decompression factories.
const std::vector< std::shared_ptr< compress_factory > > get_compress_factories()
Get all available compression factories.
std::size_t uncompress(Output &output, const char *data, std::size_t size, std::size_t max=0)
Uncompress data using gzip algorithm with a generic output container.
Definition compression.h:844
std::shared_ptr< decompress_factory > get_decompress_factory(const std::string &algorithm)
Get a specific decompression factory by algorithm name.
@ is_last
Used for the expected last compress() call, or for an expected single decompress() call.
Definition compression.h:57
@ has_more
Used when further compress() calls will be made, or when multiple decompress() calls may be required.
Definition compression.h:59
bool is_compressed(const char *data, std::size_t size)
Check if data is compressed using gzip or zlib format.
Definition compression.h:747
operation_hint
Hints for compression/decompression operations.
Definition compression.h:56
Namespace containing algorithm constants and utilities.
Namespace containing built-in compression implementations.
Namespace containing deflate compression utilities.
Namespace containing gzip compression utilities.
Namespace alias for deflate compression utilities.
Namespace alias for gzip compression utilities.
Structure for passing compression parameters for deflate.
Definition compression.h:623
size_t size_compressed
[out] Size of the compressed data
Definition compression.h:627
const char * data
Pointer to the data to compress.
Definition compression.h:624
int level
Compression level.
Definition compression.h:626
std::size_t size
Size of the data in bytes.
Definition compression.h:625
Structure for passing decompression parameters for deflate.
Definition compression.h:695
std::size_t size
Size of the compressed data in bytes.
Definition compression.h:697
std::size_t size_uncompressed
[out] Size of the uncompressed data
Definition compression.h:699
std::size_t max
Maximum allowed output size (0 for unlimited)
Definition compression.h:698
const char * data
Pointer to the compressed data.
Definition compression.h:696
Structure for passing gzip compression parameters.
Definition compression.h:799
int level
Compression level.
Definition compression.h:802
const char * data
Pointer to the data to compress.
Definition compression.h:800
std::size_t size
Size of the data in bytes.
Definition compression.h:801
size_t size_compressed
[out] Size of the compressed data
Definition compression.h:803
Structure for passing gzip decompression parameters.
Definition compression.h:871
const char * data
Pointer to the compressed data.
Definition compression.h:872
std::size_t max
Maximum allowed output size (0 for unlimited)
Definition compression.h:874
std::size_t size_uncompressed
[out] Size of the uncompressed data
Definition compression.h:875
std::size_t size
Size of the compressed data in bytes.
Definition compression.h:873
Result of a compression/decompression operation.
Definition compression.h:72
std::size_t output_bytes_produced
Number of bytes written to the output buffer.
Definition compression.h:76
std::size_t input_bytes_processed
Number of bytes processed from the input buffer.
Definition compression.h:74
bool done
For compress, set when 'last' is true and there was enough space to complete compression; for decompr...
Definition compression.h:77
Implementation of a dynamic extensible buffer.