GCC Code Coverage Report


Directory: libs/http_proto/
File: libs/http_proto/src_zlib/service/zlib_service.cpp
Date: 2024-07-18 19:38:55
Exec Total Coverage
Lines: 123 153 80.4%
Functions: 17 22 77.3%
Branches: 46 82 56.1%

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