#include // zlib #include // std #include #include #include namespace gzip { class Compressor { std::size_t max_; int level_; public: Compressor(int level = Z_DEFAULT_COMPRESSION, std::size_t max_bytes = 2000000000) // by default refuse operation if uncompressed data is > 2GB : max_(max_bytes), level_(level) { } template void compress(InputType& output, const char* data, std::size_t size) const { #ifdef DEBUG // Verify if size input will fit into unsigned int, type used for zlib's avail_in if (size > std::numeric_limits::max()) { throw std::runtime_error("size arg is too large to fit into unsigned int type"); } #endif if (size > max_) { throw std::runtime_error("size may use more memory than intended when decompressing"); } z_stream deflate_s; deflate_s.zalloc = Z_NULL; deflate_s.zfree = Z_NULL; deflate_s.opaque = Z_NULL; deflate_s.avail_in = 0; deflate_s.next_in = Z_NULL; // The windowBits parameter is the base two logarithm of the window size (the size of the history buffer). // It should be in the range 8..15 for this version of the library. // Larger values of this parameter result in better compression at the expense of memory usage. // This range of values also changes the decoding type: // -8 to -15 for raw deflate // 8 to 15 for zlib // (8 to 15) + 16 for gzip // (8 to 15) + 32 to automatically detect gzip/zlib header (decompression/inflate only) constexpr int window_bits = 15 + 16; // gzip with windowbits of 15 constexpr int mem_level = 8; // The memory requirements for deflate are (in bytes): // (1 << (window_bits+2)) + (1 << (mem_level+9)) // with a default value of 8 for mem_level and our window_bits of 15 // this is 128Kb #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wold-style-cast" if (deflateInit2(&deflate_s, level_, Z_DEFLATED, window_bits, mem_level, Z_DEFAULT_STRATEGY) != Z_OK) { throw std::runtime_error("deflate init failed"); } #pragma GCC diagnostic pop deflate_s.next_in = reinterpret_cast(data); deflate_s.avail_in = static_cast(size); std::size_t size_compressed = 0; do { size_t increase = size / 2 + 1024; if (output.size() < (size_compressed + increase)) { output.resize(size_compressed + increase); } // There is no way we see that "increase" would not fit in an unsigned int, // hence we use static cast here to avoid -Wshorten-64-to-32 error deflate_s.avail_out = static_cast(increase); deflate_s.next_out = reinterpret_cast((&output[0] + size_compressed)); // From http://www.zlib.net/zlib_how.html // "deflate() has a return value that can indicate errors, yet we do not check it here. // Why not? Well, it turns out that deflate() can do no wrong here." // Basically only possible error is from deflateInit not working properly deflate(&deflate_s, Z_FINISH); size_compressed += (increase - deflate_s.avail_out); } while (deflate_s.avail_out == 0); deflateEnd(&deflate_s); output.resize(size_compressed); } }; inline std::string compress(const char* data, std::size_t size, int level = Z_DEFAULT_COMPRESSION) { Compressor comp(level); std::string output; comp.compress(output, data, size); return output; } } // namespace gzip