GCC Code Coverage Report


Directory: libs/http_proto/
File: libs/http_proto/src_zlib/service/zlib_service.cpp
Date: 2024-09-13 19:44:07
Exec Total Coverage
Lines: 123 153 80.4%
Functions: 17 22 77.3%
Branches: 45 80 56.2%

Line Branch Exec Source
1 //
2 // Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com)
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/http_proto
8 //
9
10 #ifndef BOOST_HTTP_PROTO_SERVICE_IMPL_ZLIB_SERVICE_IPP
11 #define BOOST_HTTP_PROTO_SERVICE_IMPL_ZLIB_SERVICE_IPP
12
13 #include <boost/http_proto/service/zlib_service.hpp>
14
15 #include <boost/http_proto/metadata.hpp>
16
17 #include <boost/assert/source_location.hpp>
18 #include <boost/buffers/circular_buffer.hpp>
19 #include <boost/config.hpp>
20 #include <boost/system/result.hpp>
21 #include <boost/throw_exception.hpp>
22
23 #include <zlib.h>
24
25 #include "../../src/zlib_service.hpp"
26
27 namespace boost {
28 namespace http_proto {
29 namespace zlib {
30 namespace detail {
31
32 /*
33 DEFLATE Compressed Data Format Specification version 1.3
34 https://www.rfc-editor.org/rfc/rfc1951
35 */
36
37 //------------------------------------------------
38
39 enum class error
40 {
41 ok = 0,
42 stream_end = 1,
43 need_dict = 2,
44 errno_ = -1,
45 stream_err = -2,
46 data_err = -3,
47 mem_err = -4,
48 buf_err = -5,
49 version_err = -6
50 };
51
52 //------------------------------------------------
53 } // detail
54 } // zlib
55 } // http_proto
56 namespace system {
57 template<>
58 struct is_error_code_enum<
59 ::boost::http_proto::zlib::detail::error>
60 {
61 static bool const value = true;
62 };
63 } // system
64 namespace http_proto {
65 namespace zlib {
66 namespace detail {
67 //------------------------------------------------
68
69 struct error_cat_type
70 : system::error_category
71 {
72 BOOST_SYSTEM_CONSTEXPR
73 3 error_cat_type() noexcept
74 3 : error_category(
75 3 0xe6c6d0215d1d6e22)
76 {
77 3 }
78
79 const char*
80 name() const noexcept override
81 {
82 return "boost.http.proto.zlib";
83 }
84
85 std::string
86 message( int ev ) const override
87 {
88 return message( ev, nullptr, 0 );
89 }
90
91 char const*
92 message(
93 int ev,
94 char*,
95 std::size_t) const noexcept override
96 {
97 switch(static_cast<error>(ev))
98 {
99 case error::ok: return "Z_OK";
100 case error::stream_end: return "Z_STREAM_END";
101 case error::need_dict: return "Z_NEED_DICT";
102 case error::errno_: return "Z_ERRNO";
103 case error::stream_err: return "Z_STREAM_ERROR";
104 case error::data_err: return "Z_DATA_ERROR";
105 case error::mem_err: return "Z_MEM_ERROR";
106 case error::buf_err: return "Z_BUF_ERROR";
107 case error::version_err: return "Z_VERSION_ERROR";
108 default:
109 return "unknown";
110 }
111 }
112 };
113
114 system::error_code
115 104 make_error_code(
116 error ev) noexcept
117 {
118 static BOOST_SYSTEM_CONSTEXPR
119
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 101 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
104 error_cat_type cat{};
120 return system::error_code{static_cast<
121 std::underlying_type<
122 104 error>::type>(ev), cat};
123 }
124
125 BOOST_NOINLINE BOOST_NORETURN
126 void
127 throw_zlib_error(
128 int e,
129 source_location const& loc = BOOST_CURRENT_LOCATION)
130 {
131 throw_exception(
132 system::system_error(static_cast<error>(e)), loc);
133 }
134
135 //------------------------------------------------
136
137 // probes memory usage for a config
138 class probe
139 {
140 public:
141 explicit
142 26 probe() noexcept
143 26 {
144 26 zs_.zalloc = &zalloc;
145 26 zs_.zfree = &zfree;
146 26 zs_.opaque = this;
147 26 }
148
149 system::result<std::size_t>
150 26 deflate_init(
151 int level)
152 {
153 26 n_ = 0;
154 26 system::error_code ec;
155 ec = static_cast<error>(
156
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 deflateInit(&zs_, level));
157
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
26 if(ec.failed())
158 return ec;
159 26 Bytef tmp[24]{};
160 26 zs_.next_in = &tmp[0];
161 26 zs_.avail_in = 1;
162 26 zs_.next_out = &tmp[1];
163 26 zs_.avail_out = 23;
164 ec = static_cast<error>(
165
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 deflate(&zs_,
166 26 Z_FINISH));
167
2/4
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 26 times.
52 if( ec.failed() &&
168
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 26 times.
52 ec != error::stream_end)
169 return ec;
170 ec = static_cast<error>(
171
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 deflateEnd(&zs_));
172
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
26 if(ec.failed())
173 return ec;
174 26 return n_;
175 }
176
177 system::result<std::size_t>
178 deflate_init2(
179 int level,
180 int method,
181 int windowBits,
182 int memLevel,
183 int strategy)
184 {
185 n_ = 0;
186 system::error_code ec;
187 ec = static_cast<error>(
188 deflateInit2(&zs_,
189 level,
190 method,
191 windowBits,
192 memLevel,
193 strategy));
194 if(ec.failed())
195 return ec;
196 Bytef tmp[2];
197 zs_.next_in = &tmp[0];
198 zs_.avail_in = 0;
199 zs_.next_out = &tmp[1];
200 zs_.avail_out = 0;
201 ec = static_cast<error>(
202 deflate(&zs_,
203 Z_FULL_FLUSH));
204 if(ec.failed())
205 return ec;
206 ec = static_cast<error>(
207 deflateEnd(&zs_));
208 if(ec.failed())
209 return ec;
210 return n_;
211 }
212
213 private:
214 130 static void* zalloc(void* opaque,
215 uInt num, uInt size)
216 {
217 130 auto& self =
218 *reinterpret_cast<
219 probe*>(opaque);
220 130 self.n_ += num * size;
221 130 return new char[num * size];
222 }
223
224 130 static void zfree(
225 void*, void* address)
226 {
227
1/2
✓ Branch 0 taken 130 times.
✗ Branch 1 not taken.
130 delete[] reinterpret_cast<
228 130 char*>(address);
229 130 }
230
231 z_stream_s zs_{};
232 std::size_t n_ = 0;
233 };
234
235 //------------------------------------------------
236
237
238 namespace {
239 240 void* zalloc_impl(
240 void* opaque,
241 unsigned items,
242 unsigned size)
243 {
244 try
245 {
246 240 auto n = items * size;
247 240 auto* ws =
248 reinterpret_cast<
249 http_proto::detail::workspace*>(opaque);
250
251
1/2
✓ Branch 1 taken 240 times.
✗ Branch 2 not taken.
240 return ws->reserve_front(n);
252 }
253 catch(std::length_error const&) // represents OOM
254 {
255 return Z_NULL;
256 }
257 }
258
259 240 void zfree_impl(void* /* opaque */, void* /* addr */)
260 {
261 // we call ws_.clear() before the serializer is reused
262 // so all the allocations are passively freed
263 240 }
264
265 } // namespace
266
267 class deflate_filter final
268 : public http_proto::detail::filter
269 {
270 private:
271 z_stream stream_;
272 http_proto::detail::workspace& ws_;
273
274 void init(bool use_gzip);
275
276 public:
277 deflate_filter(
278 http_proto::detail::workspace& ws,
279 bool use_gzip = false);
280 ~deflate_filter();
281
282 deflate_filter(deflate_filter const&) = delete;
283 deflate_filter& operator=(
284 deflate_filter const&) = delete;
285
286 filter::results
287 on_process(
288 buffers::mutable_buffer out,
289 buffers::const_buffer in,
290 bool more) override;
291 };
292
293 48 deflate_filter::
294 deflate_filter(
295 http_proto::detail::workspace& ws,
296 48 bool use_gzip)
297 48 : ws_(ws)
298 {
299 48 stream_.zalloc = &zalloc_impl;
300 48 stream_.zfree = &zfree_impl;
301 48 stream_.opaque = &ws_;
302 48 init(use_gzip);
303 48 }
304
305 48 deflate_filter::
306 48 ~deflate_filter()
307 {
308 48 deflateEnd(&stream_);
309 48 }
310
311 void
312 48 deflate_filter::
313 init(bool use_gzip)
314 {
315 48 int ret = -1;
316
317 48 int window_bits = 15;
318
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 24 times.
48 if( use_gzip )
319 24 window_bits += 16;
320
321 48 int mem_level = 8;
322
323 48 ret = deflateInit2(
324 &stream_, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
325 window_bits, mem_level, Z_DEFAULT_STRATEGY);
326
327
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
48 if( ret != Z_OK )
328 throw_zlib_error(ret);
329
330 48 stream_.next_out = nullptr;
331 48 stream_.avail_out = 0;
332
333 48 stream_.next_in = nullptr;
334 48 stream_.avail_in = 0;
335 48 }
336
337 http_proto::detail::filter::results
338 23724 deflate_filter::
339 on_process(
340 buffers::mutable_buffer out,
341 buffers::const_buffer in,
342 bool more)
343 {
344 23724 auto& zstream = stream_;
345
346
2/2
✓ Branch 0 taken 23612 times.
✓ Branch 1 taken 112 times.
23724 auto flush = more ? Z_NO_FLUSH : Z_FINISH;
347 23724 int ret = -1;
348 23724 filter::results results;
349
350 for(;;)
351 {
352 45988 zstream.next_in =
353 reinterpret_cast<unsigned char*>(
354 45988 const_cast<void*>(in.data()));
355 45988 zstream.avail_in = static_cast<unsigned>(
356 45988 in.size());
357
358 45988 zstream.next_out =
359 reinterpret_cast<unsigned char*>(
360 45988 out.data());
361 45988 zstream.avail_out =
362 45988 static_cast<unsigned>(out.size());
363
364 45988 auto n1 = zstream.avail_in;
365 45988 auto n2 = zstream.avail_out;
366 45988 ret = deflate(&zstream, flush);
367
368 45988 in += (n1 - zstream.avail_in);
369 45988 out += (n2 - zstream.avail_out);
370
371 45988 results.in_bytes += (n1 - zstream.avail_in);
372 45988 results.out_bytes += (n2 - zstream.avail_out);
373
374 45988 auto is_empty = (in.size() == 0);
375
376
4/4
✓ Branch 0 taken 22200 times.
✓ Branch 1 taken 23788 times.
✓ Branch 2 taken 96 times.
✓ Branch 3 taken 22104 times.
45988 if( ret != Z_OK &&
377
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 96 times.
96 ret != Z_BUF_ERROR &&
378 ret != Z_STREAM_END )
379 throw_zlib_error(ret);
380
381
2/2
✓ Branch 0 taken 44648 times.
✓ Branch 1 taken 1340 times.
45988 if( is_empty &&
382
4/4
✓ Branch 0 taken 33256 times.
✓ Branch 1 taken 11392 times.
✓ Branch 2 taken 11104 times.
✓ Branch 3 taken 22152 times.
44648 n2 == zstream.avail_out &&
383 ret == Z_OK )
384 {
385 11104 flush = Z_SYNC_FLUSH;
386 11104 continue;
387 }
388
389
2/2
✓ Branch 0 taken 96 times.
✓ Branch 1 taken 34788 times.
34884 if( ret == Z_STREAM_END )
390 96 results.finished = true;
391
392
2/2
✓ Branch 0 taken 22104 times.
✓ Branch 1 taken 12780 times.
34884 if( ret == Z_BUF_ERROR )
393 22104 break;
394
395
2/2
✓ Branch 0 taken 96 times.
✓ Branch 1 taken 12684 times.
12780 if( ret == Z_STREAM_END )
396 96 break;
397
398
5/6
✓ Branch 0 taken 12684 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1524 times.
✓ Branch 3 taken 11160 times.
✓ Branch 4 taken 1524 times.
✓ Branch 5 taken 11160 times.
25368 if( ret == Z_OK &&
399 12684 out.size() == 0 )
400 1524 break;
401 22264 }
402 23724 return results;
403 }
404
405 //------------------------------------------------
406
407 struct
408 deflate_decoder_service_impl
409 : deflate_decoder_service
410 {
411 using key_type =
412 deflate_decoder_service;
413
414 explicit
415 26 deflate_decoder_service_impl(
416 context& ctx,
417 config const& cfg)
418 26 : cfg_(cfg)
419 {
420 (void)ctx;
421 26 probe p;
422
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 auto n0 = p.deflate_init(
423
1/2
✓ Branch 2 taken 26 times.
✗ Branch 3 not taken.
26 Z_DEFAULT_COMPRESSION).value();
424 (void)n0;
425 26 }
426
427 private:
428 config cfg_;
429
430 config const&
431 get_config() const noexcept override
432 {
433 return cfg_;
434 }
435
436 std::size_t
437 1 space_needed() const noexcept override
438 {
439 1 return 0;
440 }
441
442 http_proto::detail::filter&
443 24 make_deflate_filter(
444 http_proto::detail::workspace& ws) const override
445 {
446
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 return ws.emplace<deflate_filter>(ws, false);
447 }
448
449 http_proto::detail::filter&
450 24 make_gzip_filter(
451 http_proto::detail::workspace& ws) const override
452 {
453
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 return ws.emplace<deflate_filter>(ws, true);
454 }
455 };
456
457 } // detail
458
459 void BOOST_HTTP_PROTO_ZLIB_DECL
460 26 install_deflate_encoder(context& ctx)
461 {
462 26 detail::deflate_decoder_service::config cfg;
463 ctx.make_service<
464
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 detail::deflate_decoder_service_impl>(cfg);
465 26 }
466
467 } // zlib
468 } // http_proto
469 } // boost
470
471 #endif
472