Zstandard Compression
The Zstandard module provides fast compression and decompression services for HTTP content encoding.
Overview
Zstandard (zstd) is a lossless compression algorithm designed for real-time scenarios. It offers a very wide range of speed/ratio trade-offs through its compression levels, compresses at zlib-like speeds with better ratios, and decompresses quickly regardless of the level used. It is registered as the zstd HTTP content coding (RFC 8878).
The services are thin wrappers over the stable libzstd API. The Boost::http_zstd library is built when zstd 1.4.0 or later is found, and defines BOOST_HTTP_HAS_ZSTD for its consumers.
Basic Usage
#include <boost/http/zstd.hpp>
namespace zstd = boost::http::zstd;
// Install services into an execution context
auto& compressor = zstd::install_compress_service(ctx);
auto& decompressor = zstd::install_decompress_service(ctx);
// Or install both into the system context
zstd::install_zstd_service();
Results and Errors
Most functions return a std::size_t which is either a byte count or an
encoded error code, exactly like the underlying C API. Always test a result
with is_error before using it:
std::size_t n = compressor.compress(/* ... */);
if (compressor.is_error(n))
{
boost::system::error_code ec = compressor.get_error_code(n);
std::cerr << compressor.get_error_name(n) << '\n';
return ec;
}
zstd::error is a Boost.System error enum, so an error code converts to
boost::system::error_code and std::error_code.
One-Shot Compression
std::string input = /* ... */;
std::string output(compressor.compress_bound(input.size()), '\0');
std::size_t n = compressor.compress(
output.data(), output.size(),
input.data(), input.size(),
compressor.default_level());
if (! compressor.is_error(n))
output.resize(n);
Compression Levels
Levels range from min_level() (negative, fastest) to max_level()
(slowest, best ratio). default_level() returns the library default:
int fastest = compressor.min_level(); // negative "fast" levels
int best = compressor.max_level(); // 22
int normal = compressor.default_level(); // 3
One-Shot Decompression
The frame header usually records the content size, which gives the exact output buffer size:
auto size = decompressor.get_frame_content_size(
compressed.data(), compressed.size());
if (size == zstd::content_size_error)
return; // not a zstd frame
if (size == zstd::content_size_unknown)
return; // must use the streaming interface
std::string output(size, '\0');
std::size_t n = decompressor.decompress(
output.data(), output.size(),
compressed.data(), compressed.size());
The recorded size comes from the peer; check it against an application limit before allocating.
Streaming Interface
Contexts hold the state of a frame in progress. Input and output are
described by in_buffer and out_buffer, whose pos fields the service
advances.
Compression
zstd::cctx* ctx = compressor.create_cctx();
compressor.set_parameter(ctx, zstd::c_parameter::compression_level, 5);
compressor.set_parameter(ctx, zstd::c_parameter::checksum_flag, 1);
std::vector<char> buf(compressor.stream_out_size());
zstd::in_buffer in{ input.data(), input.size(), 0 };
std::size_t remaining;
do
{
zstd::out_buffer out{ buf.data(), buf.size(), 0 };
remaining = compressor.compress_stream(
ctx, out, in, zstd::end_directive::end);
if (compressor.is_error(remaining))
break;
output.insert(output.end(), buf.data(), buf.data() + out.pos);
}
while (remaining != 0);
compressor.free_cctx(ctx);
Use end_directive::continue_ while more input is coming, flush to force
out a decodable block without closing the frame, and end to finish the
frame. With flush and end, keep calling until zero is returned.
A context is reusable: reset with reset_directive::session_only starts
another frame with the same parameters.
Decompression
zstd::dctx* ctx = decompressor.create_dctx();
std::vector<char> buf(decompressor.stream_out_size());
zstd::in_buffer in{ compressed.data(), compressed.size(), 0 };
std::size_t rs;
do
{
zstd::out_buffer out{ buf.data(), buf.size(), 0 };
rs = decompressor.decompress_stream(ctx, out, in);
if (decompressor.is_error(rs))
break;
output.insert(output.end(), buf.data(), buf.data() + out.pos);
}
while (rs != 0);
decompressor.free_dctx(ctx);
decompress_stream returns zero when a frame is complete and fully flushed.
Any other non-error value means more input or more output space is needed.
Parameters
Parameters are set on a context and are "sticky": they apply to every frame
processed with that context until it is reset with
reset_directive::parameters. Valid ranges can be queried:
zstd::bounds b = compressor.param_bounds(zstd::c_parameter::window_log);
if (! compressor.is_error(b.error))
std::cout << b.lower_bound << ".." << b.upper_bound;
| Parameter | Description |
|---|---|
|
Compression level; negative values select faster modes |
|
Maximum back-reference distance as a power of 2; bounds decoder memory |
|
Match-finding strategy, see |
|
Append a 32-bit content checksum to the frame |
|
Record the content size in the frame header when known |
|
Largest window the decoder will allocate for in streaming mode |
Dictionaries
Dictionaries improve compression of many small, similar messages. A digested
dictionary (cdict / ddict) is prepared once and shared read-only between
contexts and threads:
zstd::cdict* cd = compressor.create_cdict(dict.data(), dict.size(), 3);
compressor.ref_cdict(ctx, cd); // used by all following frames
// ... compress ...
compressor.free_cdict(cd); // after the context stops using it
zstd::ddict* dd = decompressor.create_ddict(dict.data(), dict.size());
decompressor.ref_ddict(ctx, dd);
load_dictionary copies and digests a dictionary into a single context, and
ref_prefix references raw content as a single-use dictionary for the next
frame only.
Reference
Functions
| Function | Description |
|---|---|
|
Install compression service into an execution context |
|
Install decompression service into an execution context |
|
Install both services into the system context |
Types
| Type | Description |
|---|---|
|
Compression service interface |
|
Decompression service interface |
|
Opaque compression and decompression contexts |
|
Opaque digested dictionaries |
|
Streaming buffer descriptors |
|
Error codes |