GCC Code Coverage Report


Directory: libs/http_proto/
File: src_zlib/service/zlib_service.cpp
Date: 2025-05-26 06:53:21
Exec Total Coverage
Lines: 64 75 85.3%
Functions: 14 16 87.5%
Branches: 4 12 33.3%

Line Branch Exec Source
1 //
2 // Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com)
3 // Copyright (c) 2024 Mohammad Nejati
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // Official repository: https://github.com/cppalliance/http_proto
9 //
10
11 #include <boost/http_proto/service/zlib_service.hpp>
12
13 #include <boost/assert/source_location.hpp>
14 #include <boost/config.hpp>
15 #include <boost/system/result.hpp>
16 #include <boost/throw_exception.hpp>
17 #include <boost/static_assert.hpp>
18 #include <zlib.h>
19
20 namespace boost {
21 namespace http_proto {
22 namespace zlib {
23
24 //------------------------------------------------
25
26 namespace {
27
28 BOOST_NOINLINE BOOST_NORETURN
29 void
30 throw_zlib_error(
31 int e,
32 source_location const& loc = BOOST_CURRENT_LOCATION)
33 {
34 throw_exception(
35 system::system_error(static_cast<error>(e)), loc);
36 }
37
38 // probes memory usage for a config
39 class probe
40 {
41 public:
42 explicit
43 probe() noexcept
44 {
45 zs_.zalloc = &zalloc;
46 zs_.zfree = &zfree;
47 zs_.opaque = this;
48 }
49
50 system::result<std::size_t>
51 deflate_init(
52 int level)
53 {
54 n_ = 0;
55 system::error_code ec;
56 ec = static_cast<error>(
57 deflateInit(&zs_, level));
58 if(ec.failed())
59 return ec;
60 Bytef tmp[24]{};
61 zs_.next_in = &tmp[0];
62 zs_.avail_in = 1;
63 zs_.next_out = &tmp[1];
64 zs_.avail_out = 23;
65 ec = static_cast<error>(
66 deflate(&zs_,
67 Z_FINISH));
68 if( ec.failed() &&
69 ec != error::stream_end)
70 return ec;
71 ec = static_cast<error>(
72 deflateEnd(&zs_));
73 if(ec.failed())
74 return ec;
75 return n_;
76 }
77
78 system::result<std::size_t>
79 deflate_init2(
80 int level,
81 int method,
82 int windowBits,
83 int memLevel,
84 int strategy)
85 {
86 n_ = 0;
87 system::error_code ec;
88 ec = static_cast<error>(
89 deflateInit2(&zs_,
90 level,
91 method,
92 windowBits,
93 memLevel,
94 strategy));
95 if(ec.failed())
96 return ec;
97 Bytef tmp[2];
98 zs_.next_in = &tmp[0];
99 zs_.avail_in = 0;
100 zs_.next_out = &tmp[1];
101 zs_.avail_out = 0;
102 ec = static_cast<error>(
103 deflate(&zs_,
104 Z_FULL_FLUSH));
105 if(ec.failed())
106 return ec;
107 ec = static_cast<error>(
108 deflateEnd(&zs_));
109 if(ec.failed())
110 return ec;
111 return n_;
112 }
113
114 private:
115 static void* zalloc(void* opaque,
116 uInt num, uInt size)
117 {
118 auto& self =
119 *reinterpret_cast<
120 probe*>(opaque);
121 self.n_ += num * size;
122 return new char[num * size];
123 }
124
125 static void zfree(
126 void*, void* address)
127 {
128 delete[] reinterpret_cast<
129 char*>(address);
130 }
131
132 z_stream_s zs_{};
133 std::size_t n_ = 0;
134 };
135
136 348 void* zalloc(
137 void* opaque,
138 unsigned items,
139 unsigned size)
140 {
141 try
142 {
143 348 auto n = items * size;
144 348 auto* ws =
145 reinterpret_cast<
146 http_proto::detail::workspace*>(opaque);
147
148
1/2
✓ Branch 1 taken 348 times.
✗ Branch 2 not taken.
348 return ws->reserve_front(n);
149 }
150 catch(std::length_error const&) // represents OOM
151 {
152 return Z_NULL;
153 }
154 }
155
156 void zfree(void* /* opaque */, void* /* addr */)
157 {
158 // we call ws_.clear() before the serializer is reused
159 // so all the allocations are passively freed
160 }
161
162 ::uInt
163 660516 clamp(std::size_t x) noexcept
164 {
165
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 660516 times.
660516 if(x >= (std::numeric_limits<::uInt>::max)())
166 return (std::numeric_limits<::uInt>::max)();
167 660516 return static_cast<::uInt>(x);
168 }
169
170 void
171 165129 sync(z_stream* zs, params const& p) noexcept
172 {
173 165129 zs->next_in = reinterpret_cast<::Bytef*>(
174 165129 const_cast<void*>(p.next_in));
175 165129 zs->avail_in = clamp(p.avail_in);
176 165129 zs->next_out = reinterpret_cast<::Bytef*>(p.next_out);
177 165129 zs->avail_out = clamp(p.avail_out);
178 165129 }
179
180 void
181 165129 sync(z_stream const& zs, params* p) noexcept
182 {
183 165129 p->next_in = zs.next_in;
184 165129 p->avail_in -= clamp(p->avail_in) - zs.avail_in;
185 165129 p->next_out = zs.next_out;
186 165129 p->avail_out -= clamp(p->avail_out) - zs.avail_out;
187 165129 }
188
189 class deflator
190 : public stream
191 {
192 z_stream zs_;
193
194 public:
195 48 deflator(
196 http_proto::detail::workspace& ws,
197 int level,
198 int window_bits,
199 int mem_level)
200 48 {
201 48 zs_.zalloc = &zalloc;
202 48 zs_.zfree = &zfree;
203 48 zs_.opaque = &ws;
204
205 48 auto ret = deflateInit2(&zs_, level, Z_DEFLATED,
206 window_bits, mem_level, Z_DEFAULT_STRATEGY);
207
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
48 if(ret != Z_OK)
208 throw_zlib_error(ret);
209 48 }
210
211 system::error_code
212 45712 write(params& p, flush f) noexcept override
213 {
214 45712 sync(&zs_, p);
215 45712 auto ret = deflate(&zs_, static_cast<int>(f));
216 45712 sync(zs_, &p);
217 45712 return static_cast<error>(ret);
218 }
219 };
220
221 class inflator
222 : public stream
223 {
224 z_stream zs_;
225
226 public:
227 72 inflator(
228 http_proto::detail::workspace& ws,
229 int window_bits)
230 72 {
231 72 zs_.zalloc = &zalloc;
232 72 zs_.zfree = &zfree;
233 72 zs_.opaque = &ws;
234
235 72 auto ret = inflateInit2(&zs_, window_bits);
236
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 72 times.
72 if(ret != Z_OK)
237 throw_zlib_error(ret);
238 72 }
239
240 system::error_code
241 119417 write(params& p, flush f) noexcept override
242 {
243 119417 sync(&zs_, p);
244 119417 auto ret = inflate(&zs_, static_cast<int>(f));
245 119417 sync(zs_, &p);
246 119417 return static_cast<error>(ret);
247 }
248 };
249
250 struct service_impl
251 : public service
252 {
253 using key_type = service;
254
255 explicit
256 27 service_impl(context&) noexcept
257 27 {
258 27 }
259
260 std::size_t
261 24 deflator_space_needed(
262 int window_bits,
263 int mem_level) const noexcept override
264 {
265 // TODO: Account for the number of allocations and
266 // their overhead in the workspace.
267
268 // https://www.zlib.net/zlib_tech.html
269 return
270 24 (1 << (window_bits + 2)) +
271 24 (1 << (mem_level + 9)) +
272 (6 * 1024) +
273 #ifdef __s390x__
274 5768 +
275 #endif
276 http_proto::detail::
277 24 workspace::space_needed<deflator>();
278 }
279
280 std::size_t
281 2 inflator_space_needed(
282 int window_bits) const noexcept override
283 {
284 // TODO: Account for the number of allocations and
285 // their overhead in the workspace.
286
287 // https://www.zlib.net/zlib_tech.html
288 return
289 2 (1 << window_bits) +
290 (7 * 1024) +
291 #ifdef __s390x__
292 5768 +
293 #endif
294 http_proto::detail::
295 2 workspace::space_needed<inflator>();
296 }
297
298 stream&
299 48 make_deflator(
300 http_proto::detail::workspace& ws,
301 int level,
302 int window_bits,
303 int mem_level) const override
304 {
305 48 return ws.emplace<deflator>(
306 48 ws, level, window_bits, mem_level);
307 }
308
309 stream&
310 72 make_inflator(
311 http_proto::detail::workspace& ws,
312 int window_bits) const override
313 {
314 72 return ws.emplace<inflator>(ws, window_bits);
315 }
316 };
317
318 } // namespace
319
320 void
321 27 install_service(context& ctx)
322 {
323 27 ctx.make_service<service_impl>();
324 27 }
325
326 //------------------------------------------------
327
328 } // zlib
329 } // http_proto
330 } // boost
331