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 :
18 : #include <zlib.h>
19 :
20 : namespace boost {
21 : namespace http_proto {
22 : namespace zlib {
23 :
24 : namespace {
25 :
26 : BOOST_NOINLINE BOOST_NORETURN
27 : void
28 0 : throw_zlib_error(
29 : int e,
30 : source_location const& loc = BOOST_CURRENT_LOCATION)
31 : {
32 0 : throw_exception(
33 0 : system::system_error(static_cast<error>(e)), loc);
34 : }
35 :
36 : // probes memory usage for a config
37 : class probe
38 : {
39 : public:
40 : explicit
41 : probe() noexcept
42 : {
43 : zs_.zalloc = &zalloc;
44 : zs_.zfree = &zfree;
45 : zs_.opaque = this;
46 : }
47 :
48 : system::result<std::size_t>
49 : deflate_init(
50 : int level)
51 : {
52 : n_ = 0;
53 : system::error_code ec;
54 : ec = static_cast<error>(
55 : deflateInit(&zs_, level));
56 : if(ec.failed())
57 : return ec;
58 : Bytef tmp[24]{};
59 : zs_.next_in = &tmp[0];
60 : zs_.avail_in = 1;
61 : zs_.next_out = &tmp[1];
62 : zs_.avail_out = 23;
63 : ec = static_cast<error>(
64 : deflate(&zs_,
65 : Z_FINISH));
66 : if( ec.failed() &&
67 : ec != error::stream_end)
68 : return ec;
69 : ec = static_cast<error>(
70 : deflateEnd(&zs_));
71 : if(ec.failed())
72 : return ec;
73 : return n_;
74 : }
75 :
76 : system::result<std::size_t>
77 : deflate_init2(
78 : int level,
79 : int method,
80 : int windowBits,
81 : int memLevel,
82 : int strategy)
83 : {
84 : n_ = 0;
85 : system::error_code ec;
86 : ec = static_cast<error>(
87 : deflateInit2(&zs_,
88 : level,
89 : method,
90 : windowBits,
91 : memLevel,
92 : strategy));
93 : if(ec.failed())
94 : return ec;
95 : Bytef tmp[2];
96 : zs_.next_in = &tmp[0];
97 : zs_.avail_in = 0;
98 : zs_.next_out = &tmp[1];
99 : zs_.avail_out = 0;
100 : ec = static_cast<error>(
101 : deflate(&zs_,
102 : Z_FULL_FLUSH));
103 : if(ec.failed())
104 : return ec;
105 : ec = static_cast<error>(
106 : deflateEnd(&zs_));
107 : if(ec.failed())
108 : return ec;
109 : return n_;
110 : }
111 :
112 : private:
113 : static void* zalloc(void* opaque,
114 : uInt num, uInt size)
115 : {
116 : auto& self =
117 : *reinterpret_cast<
118 : probe*>(opaque);
119 : self.n_ += num * size;
120 : return new char[num * size];
121 : }
122 :
123 : static void zfree(
124 : void*, void* address)
125 : {
126 : delete[] reinterpret_cast<
127 : char*>(address);
128 : }
129 :
130 : z_stream_s zs_{};
131 : std::size_t n_ = 0;
132 : };
133 :
134 240 : void* zalloc(
135 : void* opaque,
136 : unsigned items,
137 : unsigned size)
138 : {
139 : try
140 : {
141 240 : auto n = items * size;
142 240 : auto* ws =
143 : reinterpret_cast<
144 : http_proto::detail::workspace*>(opaque);
145 :
146 240 : return ws->reserve_front(n);
147 : }
148 0 : catch(std::length_error const&) // represents OOM
149 : {
150 0 : return Z_NULL;
151 0 : }
152 : }
153 :
154 0 : void zfree(void* /* opaque */, void* /* addr */)
155 : {
156 : // we call ws_.clear() before the serializer is reused
157 : // so all the allocations are passively freed
158 0 : }
159 :
160 : static ::uInt
161 144928 : clamp(std::size_t x) noexcept
162 : {
163 144928 : if(x >= (std::numeric_limits<::uInt>::max)())
164 0 : return (std::numeric_limits<::uInt>::max)();
165 144928 : return static_cast<::uInt>(x);
166 : }
167 :
168 : void
169 36232 : sync(z_stream* zs, params const& p) noexcept
170 : {
171 36232 : zs->next_in = reinterpret_cast<::Bytef*>(
172 36232 : const_cast<void*>(p.next_in));
173 36232 : zs->avail_in = clamp(p.avail_in);
174 36232 : zs->next_out = reinterpret_cast<::Bytef*>(p.next_out);
175 36232 : zs->avail_out = clamp(p.avail_out);
176 36232 : }
177 :
178 : void
179 36232 : sync(z_stream const& zs, params* p) noexcept
180 : {
181 36232 : p->next_in = zs.next_in;
182 36232 : p->avail_in -= clamp(p->avail_in) - zs.avail_in;
183 36232 : p->next_out = zs.next_out;
184 36232 : p->avail_out -= clamp(p->avail_out) - zs.avail_out;
185 36232 : }
186 :
187 : class deflator
188 : : public stream
189 : {
190 : z_stream zs_;
191 :
192 : public:
193 48 : deflator(
194 : http_proto::detail::workspace& ws,
195 : int level,
196 : int window_bits,
197 : int mem_level)
198 48 : {
199 48 : zs_.zalloc = &zalloc;
200 48 : zs_.zfree = &zfree;
201 48 : zs_.opaque = &ws;
202 :
203 48 : auto ret = deflateInit2(&zs_, level, Z_DEFLATED,
204 : window_bits, mem_level, Z_DEFAULT_STRATEGY);
205 48 : if(ret != Z_OK)
206 0 : throw_zlib_error(ret);
207 48 : }
208 :
209 : system::error_code
210 36232 : write(params& p, flush f) noexcept override
211 : {
212 36232 : sync(&zs_, p);
213 36232 : auto ret = deflate(&zs_, static_cast<int>(f));
214 36232 : sync(zs_, &p);
215 36232 : return static_cast<error>(ret);
216 : }
217 : };
218 :
219 : class inflator
220 : : public stream
221 : {
222 : z_stream zs_;
223 :
224 : public:
225 0 : inflator(
226 : http_proto::detail::workspace& ws,
227 : int window_bits)
228 0 : {
229 0 : zs_.zalloc = &zalloc;
230 0 : zs_.zfree = &zfree;
231 0 : zs_.opaque = &ws;
232 :
233 0 : auto ret = inflateInit2(&zs_, window_bits);
234 0 : if(ret != Z_OK)
235 0 : throw_zlib_error(ret);
236 0 : }
237 :
238 : system::error_code
239 0 : write(params& p, flush f) noexcept override
240 : {
241 0 : sync(&zs_, p);
242 0 : auto ret = inflate(&zs_, static_cast<int>(f));
243 0 : sync(zs_, &p);
244 0 : return static_cast<error>(ret);
245 : }
246 : };
247 :
248 : struct service_impl
249 : : public service
250 : {
251 : using key_type = service;
252 :
253 : explicit
254 26 : service_impl(context&) noexcept
255 26 : {
256 26 : }
257 :
258 : std::size_t
259 1 : space_needed() const noexcept override
260 : {
261 1 : return 0; // TODO
262 : }
263 :
264 : stream&
265 48 : make_deflator(
266 : http_proto::detail::workspace& ws,
267 : int level,
268 : int window_bits,
269 : int mem_level) const override
270 : {
271 48 : return ws.emplace<deflator>(
272 48 : ws, level, window_bits, mem_level);
273 : }
274 :
275 : stream&
276 0 : make_inflator(
277 : http_proto::detail::workspace& ws,
278 : int window_bits) const override
279 : {
280 0 : return ws.emplace<inflator>(ws, window_bits);
281 : }
282 : };
283 :
284 : } // namespace
285 :
286 : void
287 26 : install_service(context& ctx)
288 : {
289 26 : ctx.make_service<service_impl>();
290 26 : }
291 :
292 : } // zlib
293 : } // http_proto
294 : } // boost
|