Line data Source code
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 0 : throw_zlib_error(
31 : int e,
32 : source_location const& loc = BOOST_CURRENT_LOCATION)
33 : {
34 0 : throw_exception(
35 0 : 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 348 : return ws->reserve_front(n);
149 : }
150 0 : catch(std::length_error const&) // represents OOM
151 : {
152 0 : return Z_NULL;
153 0 : }
154 : }
155 :
156 0 : 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 0 : }
161 :
162 : ::uInt
163 660516 : clamp(std::size_t x) noexcept
164 : {
165 660516 : if(x >= (std::numeric_limits<::uInt>::max)())
166 0 : 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 48 : if(ret != Z_OK)
208 0 : 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 72 : if(ret != Z_OK)
237 0 : 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
|