]>
Commit | Line | Data |
---|---|---|
7c673cae | 1 | // |
92f5a8d4 | 2 | // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) |
7c673cae FG |
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 | // | |
b32b8144 FG |
7 | // Official repository: https://github.com/boostorg/beast |
8 | // | |
7c673cae FG |
9 | |
10 | // Test that header file is self-contained. | |
b32b8144 | 11 | #include <boost/beast/zlib/inflate_stream.hpp> |
7c673cae | 12 | |
b32b8144 | 13 | #include <boost/beast/core/string.hpp> |
92f5a8d4 | 14 | #include <boost/beast/_experimental/unit_test/suite.hpp> |
7c673cae FG |
15 | #include <chrono> |
16 | #include <random> | |
17 | ||
b32b8144 FG |
18 | #include "zlib-1.2.11/zlib.h" |
19 | ||
20 | namespace boost { | |
7c673cae FG |
21 | namespace beast { |
22 | namespace zlib { | |
23 | ||
24 | class inflate_stream_test : public beast::unit_test::suite | |
25 | { | |
92f5a8d4 TL |
26 | struct IDecompressor { |
27 | virtual void init() = 0; | |
28 | virtual void init(int windowBits) = 0; | |
29 | ||
30 | virtual std::size_t avail_in() const noexcept = 0; | |
31 | virtual void avail_in(std::size_t) noexcept = 0; | |
32 | virtual void const* next_in() const noexcept = 0; | |
33 | virtual void next_in(const void*) noexcept = 0; | |
34 | virtual std::size_t avail_out() const noexcept = 0; | |
35 | virtual void avail_out(std::size_t) noexcept = 0; | |
36 | virtual void* next_out() const noexcept = 0; | |
37 | virtual void next_out(void*) noexcept = 0; | |
38 | ||
39 | virtual error_code write(Flush) = 0; | |
40 | virtual ~IDecompressor() = default; | |
41 | }; | |
42 | class ZlibDecompressor : public IDecompressor { | |
43 | z_stream zs; | |
44 | ||
45 | public: | |
46 | ZlibDecompressor() = default; | |
47 | void init(int windowBits) override | |
48 | { | |
49 | inflateEnd(&zs); | |
50 | zs = {}; | |
51 | const auto res = inflateInit2(&zs, windowBits); | |
52 | switch(res){ | |
53 | case Z_OK: | |
54 | break; | |
55 | case Z_MEM_ERROR: | |
f67539c2 | 56 | throw std::runtime_error{"zlib decompressor: no memory"}; |
92f5a8d4 | 57 | case Z_STREAM_ERROR: |
f67539c2 | 58 | throw std::domain_error{"zlib decompressor: bad arg"}; |
92f5a8d4 TL |
59 | } |
60 | } | |
61 | void init() override { | |
62 | inflateEnd(&zs); | |
63 | zs = {}; | |
64 | const auto res = inflateInit2(&zs, -15); | |
65 | switch(res){ | |
66 | case Z_OK: | |
67 | break; | |
68 | case Z_MEM_ERROR: | |
f67539c2 | 69 | throw std::runtime_error{"zlib decompressor: no memory"}; |
92f5a8d4 | 70 | case Z_STREAM_ERROR: |
f67539c2 | 71 | throw std::domain_error{"zlib decompressor: bad arg"}; |
92f5a8d4 TL |
72 | } |
73 | } | |
74 | ||
75 | virtual std::size_t avail_in() const noexcept override { return zs.avail_in; } | |
76 | virtual void avail_in(std::size_t n) noexcept override { zs.avail_in = n; } | |
77 | virtual void const* next_in() const noexcept override { return zs.next_in; } | |
78 | virtual void next_in(const void* ptr) noexcept override { zs.next_in = const_cast<Bytef*>(static_cast<const Bytef*>(ptr)); } | |
79 | virtual std::size_t avail_out() const noexcept override { return zs.avail_out; } | |
80 | virtual void avail_out(std::size_t n_out) noexcept override { zs.avail_out = n_out; } | |
81 | virtual void* next_out() const noexcept override { return zs.next_out; } | |
82 | virtual void next_out(void* ptr) noexcept override { zs.next_out = (Bytef*)ptr; } | |
83 | ||
84 | error_code write(Flush flush) override { | |
85 | constexpr static int zlib_flushes[] = {0, Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, Z_FULL_FLUSH, Z_FINISH, Z_TREES}; | |
86 | const auto zlib_flush = zlib_flushes[static_cast<int>(flush)]; | |
87 | const auto res = inflate(&zs, zlib_flush); | |
88 | switch(res){ | |
89 | case Z_OK: | |
90 | return {}; | |
91 | case Z_STREAM_END: | |
92 | return error::end_of_stream; | |
93 | case Z_NEED_DICT: | |
94 | return error::need_dict; | |
95 | case Z_DATA_ERROR: | |
96 | case Z_STREAM_ERROR: | |
97 | return error::stream_error; | |
98 | case Z_MEM_ERROR: | |
f67539c2 | 99 | throw std::bad_alloc{}; |
92f5a8d4 TL |
100 | case Z_BUF_ERROR: |
101 | return error::need_buffers; | |
102 | default: | |
f67539c2 | 103 | throw std::runtime_error{"zlib decompressor: impossible value"}; |
92f5a8d4 TL |
104 | } |
105 | } | |
106 | ||
107 | ~ZlibDecompressor() override { | |
108 | inflateEnd(&zs); | |
109 | } | |
110 | } zlib_decompressor{}; | |
111 | class BeastCompressor : public IDecompressor { | |
112 | z_params zp; | |
113 | inflate_stream is; | |
114 | ||
115 | public: | |
116 | BeastCompressor() = default; | |
117 | ||
118 | void init(int windowBits) override | |
119 | { | |
120 | zp = {}; | |
121 | is.clear(); | |
122 | is.reset(windowBits); | |
123 | } | |
124 | void init() override { | |
125 | zp = {}; | |
126 | is.clear(); | |
127 | is.reset(); | |
128 | } | |
129 | ||
130 | virtual std::size_t avail_in() const noexcept override { return zp.avail_in; } | |
131 | virtual void avail_in(std::size_t n) noexcept override { zp.avail_in = n; } | |
132 | virtual void const* next_in() const noexcept override { return zp.next_in; } | |
133 | virtual void next_in(const void* ptr) noexcept override { zp.next_in = ptr; } | |
134 | virtual std::size_t avail_out() const noexcept override { return zp.avail_out; } | |
135 | virtual void avail_out(std::size_t n_out) noexcept override { zp.avail_out = n_out; } | |
136 | virtual void* next_out() const noexcept override { return zp.next_out; } | |
137 | virtual void next_out(void* ptr) noexcept override { zp.next_out = (Bytef*)ptr; } | |
138 | ||
139 | error_code write(Flush flush) override { | |
140 | error_code ec{}; | |
141 | is.write(zp, flush, ec); | |
142 | return ec; | |
143 | } | |
144 | ||
145 | ~BeastCompressor() override = default; | |
146 | } beast_decompressor{}; | |
7c673cae | 147 | public: |
b32b8144 FG |
148 | // Lots of repeats, limited char range |
149 | static | |
150 | std::string | |
151 | corpus1(std::size_t n) | |
152 | { | |
153 | static std::string const alphabet{ | |
154 | "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" | |
155 | }; | |
156 | std::string s; | |
157 | s.reserve(n + 5); | |
158 | std::mt19937 g; | |
159 | std::uniform_int_distribution<std::size_t> d0{ | |
160 | 0, alphabet.size() - 1}; | |
161 | std::uniform_int_distribution<std::size_t> d1{ | |
162 | 1, 5}; | |
163 | while(s.size() < n) | |
164 | { | |
165 | auto const rep = d1(g); | |
166 | auto const ch = alphabet[d0(g)]; | |
167 | s.insert(s.end(), rep, ch); | |
168 | } | |
169 | s.resize(n); | |
170 | return s; | |
171 | } | |
172 | ||
173 | // Random data | |
174 | static | |
175 | std::string | |
176 | corpus2(std::size_t n) | |
177 | { | |
178 | std::string s; | |
179 | s.reserve(n); | |
180 | std::mt19937 g; | |
181 | std::uniform_int_distribution<std::uint32_t> d0{0, 255}; | |
182 | while(n--) | |
183 | s.push_back(static_cast<char>(d0(g))); | |
184 | return s; | |
185 | } | |
186 | ||
187 | static | |
188 | std::string | |
189 | compress( | |
190 | string_view const& in, | |
191 | int level, // 0=none, 1..9, -1=default | |
192 | int windowBits, // 9..15 | |
193 | int memLevel, // 1..9 (8=default) | |
194 | int strategy) // e.g. Z_DEFAULT_STRATEGY | |
195 | { | |
196 | int result; | |
197 | z_stream zs; | |
198 | memset(&zs, 0, sizeof(zs)); | |
199 | result = deflateInit2( | |
200 | &zs, | |
201 | level, | |
202 | Z_DEFLATED, | |
203 | -windowBits, | |
204 | memLevel, | |
205 | strategy); | |
206 | if(result != Z_OK) | |
207 | throw std::logic_error{"deflateInit2 failed"}; | |
208 | zs.next_in = (Bytef*)in.data(); | |
209 | zs.avail_in = static_cast<uInt>(in.size()); | |
210 | std::string out; | |
211 | out.resize(deflateBound(&zs, | |
212 | static_cast<uLong>(in.size()))); | |
213 | zs.next_in = (Bytef*)in.data(); | |
214 | zs.avail_in = static_cast<uInt>(in.size()); | |
215 | zs.next_out = (Bytef*)&out[0]; | |
216 | zs.avail_out = static_cast<uInt>(out.size()); | |
217 | result = deflate(&zs, Z_FULL_FLUSH); | |
218 | if(result != Z_OK) | |
219 | throw std::logic_error("deflate failed"); | |
220 | out.resize(zs.total_out); | |
221 | deflateEnd(&zs); | |
222 | return out; | |
223 | } | |
224 | ||
7c673cae FG |
225 | //-------------------------------------------------------------------------- |
226 | ||
227 | enum Split | |
228 | { | |
229 | once, | |
230 | half, | |
231 | full | |
232 | }; | |
233 | ||
234 | class Beast | |
235 | { | |
236 | Split in_; | |
237 | Split check_; | |
238 | Flush flush_; | |
239 | ||
240 | public: | |
241 | Beast(Split in, Split check, Flush flush = Flush::sync) | |
242 | : in_(in) | |
243 | , check_(check) | |
244 | , flush_(flush) | |
245 | { | |
246 | } | |
247 | ||
248 | void | |
249 | operator()( | |
250 | int window, | |
251 | std::string const& in, | |
252 | std::string const& check, | |
253 | unit_test::suite& suite) const | |
254 | { | |
255 | auto const f = | |
256 | [&](std::size_t i, std::size_t j) | |
257 | { | |
258 | std::string out(check.size(), 0); | |
259 | z_params zs; | |
260 | zs.next_in = in.data(); | |
261 | zs.next_out = &out[0]; | |
262 | zs.avail_in = i; | |
263 | zs.avail_out = j; | |
264 | inflate_stream is; | |
265 | is.reset(window); | |
266 | bool bi = ! (i < in.size()); | |
267 | bool bo = ! (j < check.size()); | |
268 | for(;;) | |
269 | { | |
270 | error_code ec; | |
271 | is.write(zs, flush_, ec); | |
272 | if( ec == error::need_buffers || | |
273 | ec == error::end_of_stream) | |
274 | { | |
275 | out.resize(zs.total_out); | |
276 | suite.expect(out == check, __FILE__, __LINE__); | |
277 | break; | |
278 | } | |
279 | if(ec) | |
280 | { | |
281 | suite.fail(ec.message(), __FILE__, __LINE__); | |
282 | break; | |
283 | } | |
284 | if(zs.avail_in == 0 && ! bi) | |
285 | { | |
286 | bi = true; | |
287 | zs.avail_in = in.size() - i; | |
288 | } | |
289 | if(zs.avail_out == 0 && ! bo) | |
290 | { | |
291 | bo = true; | |
292 | zs.avail_out = check.size() - j; | |
293 | } | |
294 | } | |
295 | }; | |
296 | ||
297 | std::size_t i0, i1; | |
298 | std::size_t j0, j1; | |
299 | ||
300 | switch(in_) | |
301 | { | |
302 | default: | |
303 | case once: i0 = in.size(); i1 = i0; break; | |
304 | case half: i0 = in.size() / 2; i1 = i0; break; | |
305 | case full: i0 = 1; i1 = in.size(); break; | |
306 | } | |
307 | ||
308 | switch(check_) | |
309 | { | |
310 | default: | |
311 | case once: j0 = check.size(); j1 = j0; break; | |
312 | case half: j0 = check.size() / 2; j1 = j0; break; | |
313 | case full: j0 = 1; j1 = check.size(); break; | |
314 | } | |
315 | ||
316 | for(std::size_t i = i0; i <= i1; ++i) | |
317 | for(std::size_t j = j0; j <= j1; ++j) | |
318 | f(i, j); | |
319 | } | |
320 | }; | |
321 | ||
7c673cae FG |
322 | class Matrix |
323 | { | |
324 | unit_test::suite& suite_; | |
325 | ||
326 | int level_[2]; | |
327 | int window_[2]; | |
328 | int strategy_[2]; | |
329 | ||
330 | public: | |
331 | explicit | |
332 | Matrix(unit_test::suite& suite) | |
333 | : suite_(suite) | |
334 | { | |
335 | level_[0] = 0; | |
336 | level_[1] = 9; | |
b32b8144 | 337 | window_[0] = 9; |
7c673cae FG |
338 | window_[1] = 15; |
339 | strategy_[0] = 0; | |
340 | strategy_[1] = 4; | |
341 | } | |
342 | ||
343 | void | |
344 | level(int from, int to) | |
345 | { | |
346 | level_[0] = from; | |
347 | level_[1] = to; | |
348 | } | |
349 | ||
350 | void | |
351 | level(int what) | |
352 | { | |
353 | level(what, what); | |
354 | } | |
355 | ||
356 | void | |
357 | window(int from, int to) | |
358 | { | |
359 | window_[0] = from; | |
360 | window_[1] = to; | |
361 | } | |
362 | ||
363 | void | |
364 | window(int what) | |
365 | { | |
366 | window(what, what); | |
367 | } | |
368 | ||
369 | void | |
370 | strategy(int from, int to) | |
371 | { | |
372 | strategy_[0] = from; | |
373 | strategy_[1] = to; | |
374 | } | |
375 | ||
376 | void | |
377 | strategy(int what) | |
378 | { | |
379 | strategy(what, what); | |
380 | } | |
381 | ||
382 | template<class F> | |
383 | void | |
384 | operator()( | |
7c673cae FG |
385 | F const& f, |
386 | std::string const& check) const | |
387 | { | |
7c673cae FG |
388 | for(auto level = level_[0]; |
389 | level <= level_[1]; ++level) | |
390 | { | |
391 | for(auto window = window_[0]; | |
392 | window <= window_[1]; ++window) | |
393 | { | |
394 | for(auto strategy = strategy_[0]; | |
395 | strategy <= strategy_[1]; ++strategy) | |
b32b8144 FG |
396 | f( |
397 | window, | |
398 | compress(check, level, window, 4, strategy), | |
399 | check, | |
400 | suite_); | |
7c673cae FG |
401 | } |
402 | } | |
7c673cae FG |
403 | } |
404 | }; | |
405 | ||
406 | void | |
92f5a8d4 | 407 | testInflate(IDecompressor& d) |
7c673cae FG |
408 | { |
409 | { | |
410 | Matrix m{*this}; | |
411 | std::string check = | |
412 | "{\n \"AutobahnPython/0.6.0\": {\n" | |
413 | " \"1.1.1\": {\n" | |
414 | " \"behavior\": \"OK\",\n" | |
415 | " \"behaviorClose\": \"OK\",\n" | |
416 | " \"duration\": 2,\n" | |
417 | " \"remoteCloseCode\": 1000,\n" | |
418 | " \"reportfile\": \"autobahnpython_0_6_0_case_1_1_1.json\"\n" | |
419 | ; | |
b32b8144 | 420 | m(Beast{half, half}, check); |
7c673cae | 421 | } |
b32b8144 | 422 | |
7c673cae FG |
423 | { |
424 | Matrix m{*this}; | |
b32b8144 FG |
425 | auto const check = corpus1(5000); |
426 | m(Beast{half, half}, check); | |
7c673cae FG |
427 | } |
428 | { | |
429 | Matrix m{*this}; | |
b32b8144 FG |
430 | auto const check = corpus2(5000); |
431 | m(Beast{half, half}, check); | |
7c673cae FG |
432 | } |
433 | { | |
434 | Matrix m{*this}; | |
b32b8144 | 435 | auto const check = corpus1(1000); |
7c673cae FG |
436 | m.level(6); |
437 | m.window(9); | |
438 | m.strategy(Z_DEFAULT_STRATEGY); | |
b32b8144 | 439 | m(Beast{once, full}, check); |
7c673cae FG |
440 | } |
441 | { | |
442 | Matrix m{*this}; | |
b32b8144 | 443 | auto const check = corpus2(1000); |
7c673cae FG |
444 | m.level(6); |
445 | m.window(9); | |
446 | m.strategy(Z_DEFAULT_STRATEGY); | |
b32b8144 | 447 | m(Beast{once, full}, check); |
7c673cae FG |
448 | } |
449 | { | |
450 | Matrix m{*this}; | |
451 | m.level(6); | |
452 | m.window(9); | |
453 | auto const check = corpus1(200); | |
b32b8144 | 454 | m(Beast{full, full}, check); |
7c673cae FG |
455 | } |
456 | { | |
457 | Matrix m{*this}; | |
458 | m.level(6); | |
459 | m.window(9); | |
460 | auto const check = corpus2(500); | |
b32b8144 | 461 | m(Beast{full, full}, check); |
7c673cae FG |
462 | } |
463 | { | |
464 | Matrix m{*this}; | |
b32b8144 | 465 | auto const check = corpus2(1000); |
7c673cae FG |
466 | m.level(6); |
467 | m.window(9); | |
468 | m.strategy(Z_DEFAULT_STRATEGY); | |
b32b8144 | 469 | m(Beast{full, once, Flush::block}, check); |
7c673cae | 470 | } |
92f5a8d4 TL |
471 | { |
472 | // Check a known string - this provides more stable coverage, | |
473 | // independent of the RNG-generated strings. | |
474 | Matrix m{*this}; | |
475 | auto const check = | |
476 | "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do " | |
477 | "eiusmod tempor incididunt ut labore et dolore magna aliqua. " | |
478 | "Ultricies mi eget mauris pharetra et ultrices neque ornare. Eget est " | |
479 | "lorem ipsum dolor. Dui faucibus in ornare quam viverra orci " | |
480 | "sagittis. Lorem mollis aliquam ut porttitor. Pretium quam vulputate " | |
481 | "dignissim suspendisse in est ante in. Tempus egestas sed sed risus " | |
482 | "pretium quam vulputate dignissim. Pellentesque dignissim enim sit " | |
483 | "amet venenatis urna. Eleifend quam adipiscing vitae proin sagittis " | |
484 | "nisl rhoncus. Aliquam etiam erat velit scelerisque in. Accumsan in " | |
485 | "nisl nisi scelerisque eu ultrices vitae auctor eu."; | |
486 | m.level(6); | |
487 | m.window(9); | |
488 | m.strategy(Z_DEFAULT_STRATEGY); | |
489 | m(Beast{full, full}, check); | |
490 | } | |
7c673cae FG |
491 | |
492 | // VFALCO Fails, but I'm unsure of what the correct | |
493 | // behavior of Z_TREES/Flush::trees is. | |
494 | #if 0 | |
495 | { | |
496 | Matrix m{*this}; | |
497 | auto const check = corpus2(10000); | |
498 | m.level(6); | |
499 | m.window(9); | |
500 | m.strategy(Z_DEFAULT_STRATEGY); | |
b32b8144 | 501 | m(Beast{full, once, Flush::trees}, check); |
7c673cae FG |
502 | } |
503 | #endif | |
92f5a8d4 TL |
504 | |
505 | check(d, {0x63, 0x18, 0x05, 0x40, 0x0c, 0x00}, {}, 8, 3); | |
506 | check(d, {0xed, 0xc0, 0x81, 0x00, 0x00, 0x00, 0x00, 0x80, | |
507 | 0xa0, 0xfd, 0xa9, 0x17, 0xa9, 0x00, 0x00, 0x00, | |
508 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
509 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
510 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
511 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, {}); | |
512 | } | |
513 | ||
514 | std::string check(IDecompressor& d, | |
515 | std::initializer_list<std::uint8_t> const& in, | |
516 | error_code expected, | |
517 | std::size_t window_size = 15, | |
518 | std::size_t len = -1) | |
519 | { | |
520 | std::string out(1024, 0); | |
521 | z_params zs; | |
522 | inflate_stream is; | |
523 | is.reset(static_cast<int>(window_size)); | |
524 | boost::system::error_code ec; | |
525 | ||
526 | zs.next_in = &*in.begin(); | |
527 | zs.next_out = &out[0]; | |
528 | zs.avail_in = std::min(in.size(), len); | |
529 | zs.avail_out = out.size(); | |
530 | ||
531 | while (zs.avail_in > 0 && !ec) | |
532 | { | |
533 | is.write(zs, Flush::sync, ec); | |
534 | auto n = std::min(zs.avail_in, len); | |
535 | zs.next_in = static_cast<char const*>(zs.next_in) + n; | |
536 | zs.avail_in -= n; | |
537 | } | |
538 | ||
539 | BEAST_EXPECT(ec == expected); | |
540 | return out; | |
541 | } | |
542 | ||
543 | void testInflateErrors(IDecompressor& d) | |
544 | { | |
545 | check(d, {0x00, 0x00, 0x00, 0x00, 0x00}, | |
546 | error::invalid_stored_length); | |
547 | check(d, {0x03, 0x00}, | |
548 | error::end_of_stream); | |
549 | check(d, {0x06}, | |
550 | error::invalid_block_type); | |
551 | check(d, {0xfc, 0x00, 0x00}, | |
552 | error::too_many_symbols); | |
553 | check(d, {0x04, 0x00, 0xfe, 0xff}, | |
554 | error::incomplete_length_set); | |
555 | check(d, {0x04, 0x00, 0x24, 0x49, 0x00}, | |
556 | error::invalid_bit_length_repeat); | |
557 | check(d, {0x04, 0x00, 0x24, 0xe9, 0xff, 0xff}, | |
558 | error::invalid_bit_length_repeat); | |
559 | check(d, {0x04, 0x00, 0x24, 0xe9, 0xff, 0x6d}, | |
560 | error::missing_eob); | |
561 | check(d, {0x04, 0x80, 0x49, 0x92, 0x24, 0x49, 0x92, 0x24, | |
562 | 0x71, 0xff, 0xff, 0x93, 0x11, 0x00}, | |
563 | error::over_subscribed_length); | |
564 | check(d, {0x04, 0x80, 0x49, 0x92, 0x24, 0x0f, 0xb4, 0xff, | |
565 | 0xff, 0xc3, 0x84}, | |
566 | error::incomplete_length_set); | |
567 | check(d, {0x04, 0xc0, 0x81, 0x08, 0x00, 0x00, 0x00, 0x00, | |
568 | 0x20, 0x7f, 0xeb, 0x0b, 0x00, 0x00}, | |
569 | error::invalid_literal_length); | |
570 | check(d, {0x02, 0x7e, 0xff, 0xff}, | |
571 | error::invalid_distance_code); | |
572 | check(d, {0x0c, 0xc0, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, | |
573 | 0x90, 0xff, 0x6b, 0x04, 0x00}, | |
574 | error::invalid_distance); | |
575 | check(d, {0x05,0xe0, 0x81, 0x91, 0x24, 0xcb, 0xb2, 0x2c, | |
576 | 0x49, 0xe2, 0x0f, 0x2e, 0x8b, 0x9a, 0x47, 0x56, | |
577 | 0x9f, 0xfb, 0xfe, 0xec, 0xd2, 0xff, 0x1f}, | |
578 | error::end_of_stream); | |
579 | check(d, {0xed, 0xc0, 0x01, 0x01, 0x00, 0x00, 0x00, 0x40, | |
580 | 0x20, 0xff, 0x57, 0x1b, 0x42, 0x2c, 0x4f}, | |
581 | error::end_of_stream); | |
582 | check(d, {0x02, 0x08, 0x20, 0x80, 0x00, 0x03, 0x00}, | |
583 | error::end_of_stream); | |
584 | check(d, {0x78, 0x9c, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x78, 0x9c, 0xff}, | |
585 | error::invalid_stored_length); | |
586 | } | |
587 | ||
588 | void testInvalidSettings(IDecompressor& d) | |
589 | { | |
590 | except<std::domain_error>( | |
591 | [&]() | |
592 | { | |
593 | d.init(7); | |
594 | }); | |
595 | } | |
596 | ||
597 | void testFixedHuffmanFlushTrees(IDecompressor& d) | |
598 | { | |
599 | std::string out(5, 0); | |
600 | d.init(); | |
601 | boost::system::error_code ec; | |
602 | std::initializer_list<std::uint8_t> in = { | |
603 | 0xf2, 0x48, 0xcd, 0xc9, 0xc9, 0x07, 0x00, 0x00, | |
604 | 0x00, 0xff, 0xff}; | |
605 | d.next_in(&*in.begin()); | |
606 | d.next_out(&out[0]); | |
607 | d.avail_in(in.size()); | |
608 | d.avail_out(out.size()); | |
609 | ec = d.write(Flush::trees); | |
610 | BEAST_EXPECT(!ec); | |
611 | ec = d.write(Flush::sync); | |
612 | BEAST_EXPECT(!ec); | |
613 | BEAST_EXPECT(d.avail_out() == 0); | |
614 | BEAST_EXPECT(out == "Hello"); | |
615 | } | |
616 | ||
617 | void testUncompressedFlushTrees(IDecompressor& d) | |
618 | { | |
619 | std::string out(5, 0); | |
620 | d.init(); | |
621 | boost::system::error_code ec; | |
622 | std::initializer_list<std::uint8_t> in = { | |
623 | 0x00, 0x05, 0x00, 0xfa, 0xff, 0x48, 0x65, 0x6c, | |
624 | 0x6c, 0x6f, 0x00, 0x00}; | |
625 | d.next_in(&*in.begin()); | |
626 | d.next_out(&out[0]); | |
627 | d.avail_in(in.size()); | |
628 | d.avail_out(out.size()); | |
629 | ec = d.write(Flush::trees); | |
630 | BEAST_EXPECT(!ec); | |
631 | ec = d.write(Flush::sync); | |
632 | BEAST_EXPECT(!ec); | |
633 | BEAST_EXPECT(d.avail_out() == 0); | |
634 | BEAST_EXPECT(out == "Hello"); | |
7c673cae FG |
635 | } |
636 | ||
637 | void | |
638 | run() override | |
639 | { | |
640 | log << | |
641 | "sizeof(inflate_stream) == " << | |
642 | sizeof(inflate_stream) << std::endl; | |
92f5a8d4 TL |
643 | testInflate(zlib_decompressor); |
644 | testInflate(beast_decompressor); | |
645 | testInflateErrors(zlib_decompressor); | |
646 | testInflateErrors(beast_decompressor); | |
647 | testInvalidSettings(zlib_decompressor); | |
648 | testInvalidSettings(beast_decompressor); | |
649 | testFixedHuffmanFlushTrees(zlib_decompressor); | |
650 | testFixedHuffmanFlushTrees(beast_decompressor); | |
651 | testUncompressedFlushTrees(zlib_decompressor); | |
652 | testUncompressedFlushTrees(beast_decompressor); | |
7c673cae FG |
653 | } |
654 | }; | |
655 | ||
b32b8144 | 656 | BEAST_DEFINE_TESTSUITE(beast,zlib,inflate_stream); |
7c673cae FG |
657 | |
658 | } // zlib | |
659 | } // beast | |
b32b8144 | 660 | } // boost |