GCC Code Coverage Report


Directory: libs/http_proto/
File: libs/http_proto/src_zlib/service/zlib_service.cpp
Date: 2024-09-18 08:42:23
Exec Total Coverage
Lines: 49 91 53.8%
Functions: 12 20 60.0%
Branches: 8 30 26.7%

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