src/detail/encoder.cpp
89.2% Lines (141/158)
100.0% List of functions (16/16)
76.1% Branches (54/71)
Functions (16)
Function
Calls
Lines
Branches
Blocks
boost::burl::detail::(anonymous namespace)::zlib_encoder::zlib_encoder(boost::http::zlib::deflate_service&, boost::burl::encoder_config::zlib_settings const&, bool)
:49
16x
81.8%
87.5%
52.0%
boost::burl::detail::(anonymous namespace)::zlib_encoder::~zlib_encoder()
:69
32x
100.0%
–
100.0%
boost::burl::detail::(anonymous namespace)::zlib_encoder::process(boost::capy::mutable_buffer, boost::capy::const_buffer, bool)
:75
313x
100.0%
100.0%
100.0%
boost::burl::detail::(anonymous namespace)::zlib_encoder::process(boost::capy::mutable_buffer, boost::capy::const_buffer, bool)::{lambda()#1}::operator()() const
:93
313x
81.8%
50.0%
69.0%
boost::burl::detail::(anonymous namespace)::zlib_encoder::saturate(unsigned long)
:111
1252x
80.0%
50.0%
75.0%
boost::burl::detail::(anonymous namespace)::brotli_encoder::brotli_encoder(boost::http::brotli::encode_service&, boost::burl::encoder_config::brotli_settings const&)
:128
9x
84.0%
85.7%
58.0%
boost::burl::detail::(anonymous namespace)::brotli_encoder::~brotli_encoder()
:159
18x
100.0%
–
100.0%
boost::burl::detail::(anonymous namespace)::brotli_encoder::process(boost::capy::mutable_buffer, boost::capy::const_buffer, bool)
:165
158x
100.0%
100.0%
100.0%
boost::burl::detail::(anonymous namespace)::brotli_encoder::process(boost::capy::mutable_buffer, boost::capy::const_buffer, bool)::{lambda()#1}::operator()() const
:185
158x
81.8%
75.0%
83.0%
boost::burl::detail::(anonymous namespace)::brotli_encoder::set(boost::http::brotli::encoder_parameter, int)
:206
29x
100.0%
50.0%
75.0%
boost::burl::detail::(anonymous namespace)::zstd_encoder::zstd_encoder(boost::http::zstd::compress_service&, boost::burl::encoder_config::zstd_settings const&)
:222
9x
91.7%
83.3%
75.0%
boost::burl::detail::(anonymous namespace)::zstd_encoder::~zstd_encoder()
:240
18x
100.0%
–
100.0%
boost::burl::detail::(anonymous namespace)::zstd_encoder::process(boost::capy::mutable_buffer, boost::capy::const_buffer, bool)
:246
166x
100.0%
100.0%
100.0%
boost::burl::detail::(anonymous namespace)::zstd_encoder::process(boost::capy::mutable_buffer, boost::capy::const_buffer, bool)::{lambda()#1}::operator()() const
:261
166x
90.9%
75.0%
75.0%
boost::burl::detail::(anonymous namespace)::zstd_encoder::set(boost::http::zstd::c_parameter, int)
:282
12x
100.0%
50.0%
80.0%
boost::burl::detail::make_encoder(boost::http::content_coding, boost::burl::encoder_config const&)
:296
37x
86.7%
76.5%
77.0%
| Line | Branch | TLA | Hits | Source Code |
|---|---|---|---|---|
| 1 | // | |||
| 2 | // Copyright (c) 2026 Mohammad Nejati | |||
| 3 | // | |||
| 4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | |||
| 5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |||
| 6 | // | |||
| 7 | // Official repository: https://github.com/cppalliance/burl | |||
| 8 | // | |||
| 9 | ||||
| 10 | #include "encoder.hpp" | |||
| 11 | ||||
| 12 | #include <boost/assert.hpp> | |||
| 13 | #include <boost/capy/error.hpp> | |||
| 14 | #include <boost/capy/ex/system_context.hpp> | |||
| 15 | #include <boost/http/brotli/encode.hpp> | |||
| 16 | #include <boost/http/zlib/compression_method.hpp> | |||
| 17 | #include <boost/http/zlib/compression_strategy.hpp> | |||
| 18 | #include <boost/http/zlib/deflate.hpp> | |||
| 19 | #include <boost/http/zlib/error.hpp> | |||
| 20 | #include <boost/http/zlib/flush.hpp> | |||
| 21 | #include <boost/http/zlib/stream.hpp> | |||
| 22 | #include <boost/http/zstd/compress.hpp> | |||
| 23 | #include <boost/http/zstd/types.hpp> | |||
| 24 | ||||
| 25 | #include <algorithm> | |||
| 26 | #include <cstdint> | |||
| 27 | #include <limits> | |||
| 28 | #include <new> | |||
| 29 | #include <system_error> | |||
| 30 | ||||
| 31 | namespace boost | |||
| 32 | { | |||
| 33 | namespace burl | |||
| 34 | { | |||
| 35 | namespace detail | |||
| 36 | { | |||
| 37 | ||||
| 38 | namespace | |||
| 39 | { | |||
| 40 | ||||
| 41 | class zlib_encoder final | |||
| 42 | : public encoder | |||
| 43 | { | |||
| 44 | http::zlib::deflate_service& svc_; | |||
| 45 | http::zlib::stream strm_ = {}; | |||
| 46 | ||||
| 47 | public: | |||
| 48 | // gzip selects the gzip wrapper over zlib's. | |||
| 49 | 16x | zlib_encoder( | ||
| 50 | http::zlib::deflate_service& svc, | |||
| 51 | encoder_config::zlib_settings const& cfg, | |||
| 52 | bool gzip) | |||
| 53 | 16x | : svc_(svc) | ||
| 54 | { | |||
| 55 | // with the settings clamped, only | |||
| 56 | // allocation can fail | |||
| 57 | 80x | if(svc_.init2( | ||
| 58 | 16x | strm_, | ||
| 59 |
2/2✓ Branch 1 taken 16 times.
✓ Branch 4 taken 16 times.
|
16x | std::clamp(cfg.level, 0, 9), | |
| 60 | http::zlib::deflated, | |||
| 61 | ✗ | std::clamp(cfg.window_bits, 9, 15) + | ||
| 62 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 7 times.
|
16x | (gzip ? 16 : 0), | |
| 63 |
2/2✓ Branch 1 taken 16 times.
✓ Branch 4 taken 16 times.
|
16x | std::clamp(cfg.mem_level, 1, 9), | |
| 64 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
16x | http::zlib::default_strategy) != | |
| 65 | static_cast<int>(http::zlib::error::ok)) | |||
| 66 | ✗ | throw std::bad_alloc(); | ||
| 67 | 16x | } | ||
| 68 | ||||
| 69 | 32x | ~zlib_encoder() override | ||
| 70 | 16x | { | ||
| 71 | 16x | svc_.deflate_end(strm_); | ||
| 72 | 32x | } | ||
| 73 | ||||
| 74 | result | |||
| 75 | 313x | process( | ||
| 76 | capy::mutable_buffer out, | |||
| 77 | capy::const_buffer in, | |||
| 78 | bool more) override | |||
| 79 | { | |||
| 80 | 313x | strm_.next_in = static_cast<unsigned char*>( | ||
| 81 | 313x | const_cast<void*>(in.data())); | ||
| 82 | 313x | strm_.avail_in = saturate(in.size()); | ||
| 83 | 313x | strm_.next_out = | ||
| 84 | 313x | static_cast<unsigned char*>(out.data()); | ||
| 85 | 313x | strm_.avail_out = saturate(out.size()); | ||
| 86 | ||||
| 87 | auto const rs = static_cast<http::zlib::error>( | |||
| 88 |
2/2✓ Branch 0 taken 268 times.
✓ Branch 1 taken 45 times.
|
313x | svc_.deflate( | |
| 89 |
1/1✓ Branch 1 taken 313 times.
|
313x | strm_, | |
| 90 | more ? http::zlib::no_flush | |||
| 91 | 313x | : http::zlib::finish)); | ||
| 92 | ||||
| 93 | 939x | auto const ec = [&]() -> std::error_code | ||
| 94 | { | |||
| 95 |
2/2✓ Branch 0 taken 14 times.
✓ Branch 1 taken 299 times.
|
313x | if(rs == http::zlib::error::stream_end) | |
| 96 | 14x | return capy::error::eof; | ||
| 97 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 299 times.
|
299x | if(rs != http::zlib::error::ok && | |
| 98 | ✗ | rs != http::zlib::error::buf_err) | ||
| 99 | ✗ | return rs; | ||
| 100 | 299x | return {}; | ||
| 101 | 313x | }(); | ||
| 102 | ||||
| 103 | return { | |||
| 104 | 313x | .consumed = saturate(in.size()) - strm_.avail_in, | ||
| 105 | 313x | .produced = saturate(out.size()) - strm_.avail_out, | ||
| 106 | 626x | .ec = ec }; | ||
| 107 | } | |||
| 108 | ||||
| 109 | static | |||
| 110 | unsigned | |||
| 111 | 1252x | saturate(std::size_t n) noexcept | ||
| 112 | { | |||
| 113 | 1252x | constexpr auto max = | ||
| 114 | (std::numeric_limits<unsigned>::max)(); | |||
| 115 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1252 times.
|
1252x | if(n >= max) | |
| 116 | ✗ | return max; | ||
| 117 | 1252x | return static_cast<unsigned>(n); | ||
| 118 | } | |||
| 119 | }; | |||
| 120 | ||||
| 121 | class brotli_encoder final | |||
| 122 | : public encoder | |||
| 123 | { | |||
| 124 | http::brotli::encode_service& svc_; | |||
| 125 | http::brotli::encoder_state* state_; | |||
| 126 | ||||
| 127 | public: | |||
| 128 | 9x | brotli_encoder( | ||
| 129 | http::brotli::encode_service& svc, | |||
| 130 | encoder_config::brotli_settings const& cfg) | |||
| 131 | 18x | : svc_(svc) | ||
| 132 | 18x | , state_(svc.create_instance( | ||
| 133 | 9x | nullptr, nullptr, nullptr)) | ||
| 134 | { | |||
| 135 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9x | if(!state_) | |
| 136 | ✗ | throw std::bad_alloc(); | ||
| 137 | ||||
| 138 | using http::brotli::encoder_parameter; | |||
| 139 | 9x | set(encoder_parameter::quality, | ||
| 140 | 9x | std::clamp<int>( | ||
| 141 | 9x | cfg.quality, | ||
| 142 | ✗ | http::brotli::min_quality, | ||
| 143 |
1/1✓ Branch 1 taken 9 times.
|
9x | http::brotli::max_quality)); | |
| 144 | 9x | set(encoder_parameter::lgwin, | ||
| 145 | 9x | std::clamp<int>( | ||
| 146 | 9x | cfg.lgwin, | ||
| 147 | ✗ | http::brotli::min_window_bits, | ||
| 148 |
1/1✓ Branch 1 taken 9 times.
|
9x | http::brotli::max_window_bits)); | |
| 149 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 7 times.
|
9x | if(cfg.lgblock != 0) | |
| 150 | 2x | set(encoder_parameter::lgblock, | ||
| 151 | 2x | std::clamp<int>( | ||
| 152 | 2x | cfg.lgblock, | ||
| 153 | ✗ | http::brotli::min_input_block_bits, | ||
| 154 |
1/1✓ Branch 1 taken 2 times.
|
4x | http::brotli::max_input_block_bits)); | |
| 155 | 9x | set(encoder_parameter::mode, | ||
| 156 | 9x | static_cast<int>(cfg.mode)); | ||
| 157 | 9x | } | ||
| 158 | ||||
| 159 | 18x | ~brotli_encoder() override | ||
| 160 | 9x | { | ||
| 161 | 9x | svc_.destroy_instance(state_); | ||
| 162 | 18x | } | ||
| 163 | ||||
| 164 | result | |||
| 165 | 158x | process( | ||
| 166 | capy::mutable_buffer out, | |||
| 167 | capy::const_buffer in, | |||
| 168 | bool more) override | |||
| 169 | { | |||
| 170 | 158x | auto* next_in = static_cast<std::uint8_t const*>(in.data()); | ||
| 171 | 158x | auto available_in = in.size(); | ||
| 172 | 158x | auto* next_out = static_cast<std::uint8_t*>(out.data()); | ||
| 173 | 158x | auto available_out = out.size(); | ||
| 174 | ||||
| 175 |
2/2✓ Branch 0 taken 141 times.
✓ Branch 1 taken 17 times.
|
158x | auto const ok = svc_.compress_stream( | |
| 176 | state_, | |||
| 177 | more ? http::brotli::encoder_operation::process | |||
| 178 | : http::brotli::encoder_operation::finish, | |||
| 179 | &available_in, | |||
| 180 | &next_in, | |||
| 181 | &available_out, | |||
| 182 | &next_out, | |||
| 183 | 158x | nullptr); | ||
| 184 | ||||
| 185 | 474x | auto const ec = [&]() -> std::error_code | ||
| 186 | { | |||
| 187 | // brotli reports no error code; failure | |||
| 188 | // means its lazily allocated state could | |||
| 189 | // not be obtained | |||
| 190 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 158 times.
|
158x | if(!ok) | |
| 191 | ✗ | return make_error_code( | ||
| 192 | ✗ | std::errc::not_enough_memory); | ||
| 193 |
2/2✓ Branch 1 taken 8 times.
✓ Branch 2 taken 150 times.
|
158x | if(svc_.is_finished(state_)) | |
| 194 | 8x | return capy::error::eof; | ||
| 195 | 150x | return {}; | ||
| 196 | 158x | }(); | ||
| 197 | ||||
| 198 | return { | |||
| 199 | 158x | .consumed = in.size() - available_in, | ||
| 200 | 316x | .produced = out.size() - available_out, | ||
| 201 | 158x | .ec = ec }; | ||
| 202 | } | |||
| 203 | ||||
| 204 | private: | |||
| 205 | void | |||
| 206 | 29x | set(http::brotli::encoder_parameter p, int v) noexcept | ||
| 207 | { | |||
| 208 | // fails only for an unknown parameter or | |||
| 209 | // once the stream has started | |||
| 210 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 29 times.
|
29x | BOOST_VERIFY(svc_.set_parameter( | |
| 211 | state_, p, static_cast<std::uint32_t>(v))); | |||
| 212 | 29x | } | ||
| 213 | }; | |||
| 214 | ||||
| 215 | class zstd_encoder final | |||
| 216 | : public encoder | |||
| 217 | { | |||
| 218 | http::zstd::compress_service& svc_; | |||
| 219 | http::zstd::cctx* ctx_; | |||
| 220 | ||||
| 221 | public: | |||
| 222 | 9x | zstd_encoder( | ||
| 223 | http::zstd::compress_service& svc, | |||
| 224 | encoder_config::zstd_settings const& cfg) | |||
| 225 | 18x | : svc_(svc) | ||
| 226 | 9x | , ctx_(svc.create_cctx()) | ||
| 227 | { | |||
| 228 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9x | if(!ctx_) | |
| 229 | ✗ | throw std::bad_alloc(); | ||
| 230 | ||||
| 231 | using http::zstd::c_parameter; | |||
| 232 | 9x | set(c_parameter::compression_level, cfg.level); | ||
| 233 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 7 times.
|
9x | if(cfg.window_log != 0) | |
| 234 | 2x | set(c_parameter::window_log, cfg.window_log); | ||
| 235 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 8 times.
|
9x | if(cfg.strategy) | |
| 236 | 1x | set(c_parameter::strategy, | ||
| 237 | 1x | static_cast<int>(*cfg.strategy)); | ||
| 238 | 9x | } | ||
| 239 | ||||
| 240 | 18x | ~zstd_encoder() override | ||
| 241 | 9x | { | ||
| 242 | 9x | svc_.free_cctx(ctx_); | ||
| 243 | 18x | } | ||
| 244 | ||||
| 245 | result | |||
| 246 | 166x | process( | ||
| 247 | capy::mutable_buffer out, | |||
| 248 | capy::const_buffer in, | |||
| 249 | bool more) override | |||
| 250 | { | |||
| 251 | 166x | http::zstd::in_buffer in_buf{ in.data(), in.size(), 0 }; | ||
| 252 | 166x | http::zstd::out_buffer out_buf{ out.data(), out.size(), 0 }; | ||
| 253 | ||||
| 254 |
2/2✓ Branch 0 taken 141 times.
✓ Branch 1 taken 25 times.
|
166x | auto const rs = svc_.compress_stream( | |
| 255 | ctx_, | |||
| 256 | out_buf, | |||
| 257 | in_buf, | |||
| 258 | more ? http::zstd::end_directive::continue_ | |||
| 259 | 166x | : http::zstd::end_directive::end); | ||
| 260 | ||||
| 261 | 498x | auto const ec = [&]() -> std::error_code | ||
| 262 | { | |||
| 263 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 166 times.
|
166x | if(svc_.is_error(rs)) | |
| 264 | ✗ | return svc_.get_error_code(rs); | ||
| 265 | // zero reports the frame complete and | |||
| 266 | // fully flushed | |||
| 267 |
4/4✓ Branch 0 taken 25 times.
✓ Branch 1 taken 141 times.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 17 times.
|
166x | if(!more && rs == 0 && | |
| 268 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8x | in_buf.pos == in_buf.size) | |
| 269 | 8x | return capy::error::eof; | ||
| 270 | 158x | return {}; | ||
| 271 | 166x | }(); | ||
| 272 | ||||
| 273 | return { | |||
| 274 | 166x | .consumed = in_buf.pos, | ||
| 275 | 166x | .produced = out_buf.pos, | ||
| 276 | 166x | .ec = ec }; | ||
| 277 | } | |||
| 278 | ||||
| 279 | private: | |||
| 280 | // clamps to the bounds the library reports | |||
| 281 | void | |||
| 282 | 12x | set(http::zstd::c_parameter p, int v) noexcept | ||
| 283 | { | |||
| 284 | 12x | auto const b = svc_.param_bounds(p); | ||
| 285 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
|
12x | BOOST_ASSERT(!svc_.is_error(b.error)); | |
| 286 |
1/2✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
|
12x | BOOST_VERIFY(!svc_.is_error(svc_.set_parameter( | |
| 287 | ctx_, | |||
| 288 | p, | |||
| 289 | std::clamp(v, b.lower_bound, b.upper_bound)))); | |||
| 290 | 12x | } | ||
| 291 | }; | |||
| 292 | ||||
| 293 | } // namespace | |||
| 294 | ||||
| 295 | std::unique_ptr<encoder> | |||
| 296 | 37x | make_encoder( | ||
| 297 | http::content_coding coding, | |||
| 298 | encoder_config const& cfg) | |||
| 299 | { | |||
| 300 | 37x | auto const& ctx = capy::get_system_context(); | ||
| 301 |
5/5✓ Branch 0 taken 7 times.
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 9 times.
✓ Branch 4 taken 3 times.
|
37x | switch(coding) | |
| 302 | { | |||
| 303 | 7x | case http::content_coding::deflate: | ||
| 304 |
1/2✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
|
7x | if(auto* svc = ctx.find_service< | |
| 305 | 7x | http::zlib::deflate_service>()) | ||
| 306 | 7x | return std::make_unique<zlib_encoder>( | ||
| 307 |
1/1✓ Branch 1 taken 7 times.
|
14x | *svc, cfg.zlib, false); | |
| 308 | ✗ | break; | ||
| 309 | 9x | case http::content_coding::gzip: | ||
| 310 |
1/2✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
|
9x | if(auto* svc = ctx.find_service< | |
| 311 | 9x | http::zlib::deflate_service>()) | ||
| 312 | 9x | return std::make_unique<zlib_encoder>( | ||
| 313 |
1/1✓ Branch 1 taken 9 times.
|
18x | *svc, cfg.zlib, true); | |
| 314 | ✗ | break; | ||
| 315 | 9x | case http::content_coding::br: | ||
| 316 |
1/2✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
|
9x | if(auto* svc = ctx.find_service< | |
| 317 | 9x | http::brotli::encode_service>()) | ||
| 318 | 9x | return std::make_unique<brotli_encoder>( | ||
| 319 |
1/1✓ Branch 1 taken 9 times.
|
9x | *svc, cfg.brotli); | |
| 320 | ✗ | break; | ||
| 321 | 9x | case http::content_coding::zstd: | ||
| 322 |
1/2✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
|
9x | if(auto* svc = ctx.find_service< | |
| 323 | 9x | http::zstd::compress_service>()) | ||
| 324 | 9x | return std::make_unique<zstd_encoder>( | ||
| 325 |
1/1✓ Branch 1 taken 9 times.
|
9x | *svc, cfg.zstd); | |
| 326 | ✗ | break; | ||
| 327 | 3x | default: | ||
| 328 | 3x | break; | ||
| 329 | } | |||
| 330 | 3x | return nullptr; | ||
| 331 | } | |||
| 332 | ||||
| 333 | } // namespace detail | |||
| 334 | } // namespace burl | |||
| 335 | } // namespace boost | |||
| 336 |