Crow/include/crow/compression.h

100 lines
3.5 KiB
C++

#ifdef CROW_ENABLE_COMPRESSION
#pragma once
#include <string>
#include <zlib.h>
// http://zlib.net/manual.html
namespace crow // NOTE: Already documented in "crow/app.h"
{
namespace compression
{
// Values used in the 'windowBits' parameter for deflateInit2.
enum algorithm
{
// 15 is the default value for deflate
DEFLATE = 15,
// windowBits can also be greater than 15 for optional gzip encoding.
// Add 16 to windowBits to write a simple gzip header and trailer around the compressed data instead of a zlib wrapper.
GZIP = 15 | 16,
};
inline std::string compress_string(std::string const& str, algorithm algo)
{
std::string compressed_str;
z_stream stream{};
// Initialize with the default values
if (::deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, algo, 8, Z_DEFAULT_STRATEGY) == Z_OK)
{
char buffer[8192];
stream.avail_in = str.size();
// zlib does not take a const pointer. The data is not altered.
stream.next_in = const_cast<Bytef*>(reinterpret_cast<const Bytef*>(str.c_str()));
int code = Z_OK;
do
{
stream.avail_out = sizeof(buffer);
stream.next_out = reinterpret_cast<Bytef*>(&buffer[0]);
code = ::deflate(&stream, Z_FINISH);
// Successful and non-fatal error code returned by deflate when used with Z_FINISH flush
if (code == Z_OK || code == Z_STREAM_END)
{
std::copy(&buffer[0], &buffer[sizeof(buffer) - stream.avail_out], std::back_inserter(compressed_str));
}
} while (code == Z_OK);
if (code != Z_STREAM_END)
compressed_str.clear();
::deflateEnd(&stream);
}
return compressed_str;
}
inline std::string decompress_string(std::string const& deflated_string)
{
std::string inflated_string;
Bytef tmp[8192];
z_stream zstream{};
zstream.avail_in = deflated_string.size();
// Nasty const_cast but zlib won't alter its contents
zstream.next_in = const_cast<Bytef*>(reinterpret_cast<Bytef const*>(deflated_string.c_str()));
// Initialize with automatic header detection, for gzip support
if (::inflateInit2(&zstream, MAX_WBITS | 32) == Z_OK)
{
do
{
zstream.avail_out = sizeof(tmp);
zstream.next_out = &tmp[0];
auto ret = ::inflate(&zstream, Z_NO_FLUSH);
if (ret == Z_OK || ret == Z_STREAM_END)
{
std::copy(&tmp[0], &tmp[sizeof(tmp) - zstream.avail_out], std::back_inserter(inflated_string));
}
else
{
// Something went wrong with inflate; make sure we return an empty string
inflated_string.clear();
break;
}
} while (zstream.avail_out == 0);
// Free zlib's internal memory
::inflateEnd(&zstream);
}
return inflated_string;
}
} // namespace compression
} // namespace crow
#endif