]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/beast/test/beast/zlib/inflate_stream.cpp
import new upstream nautilus stable release 14.2.8
[ceph.git] / ceph / src / boost / libs / beast / test / beast / zlib / inflate_stream.cpp
1 //
2 // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot 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/boostorg/beast
8 //
9
10 // Test that header file is self-contained.
11 #include <boost/beast/zlib/inflate_stream.hpp>
12
13 #include <boost/beast/core/string.hpp>
14 #include <boost/beast/_experimental/unit_test/suite.hpp>
15 #include <chrono>
16 #include <random>
17
18 #include "zlib-1.2.11/zlib.h"
19
20 namespace boost {
21 namespace beast {
22 namespace zlib {
23
24 class inflate_stream_test : public beast::unit_test::suite
25 {
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:
56 BOOST_THROW_EXCEPTION(std::runtime_error{"zlib decompressor: no memory"});
57 case Z_STREAM_ERROR:
58 BOOST_THROW_EXCEPTION(std::domain_error{"zlib decompressor: bad arg"});
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:
69 BOOST_THROW_EXCEPTION(std::runtime_error{"zlib decompressor: no memory"});
70 case Z_STREAM_ERROR:
71 BOOST_THROW_EXCEPTION(std::domain_error{"zlib decompressor: bad arg"});
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:
99 BOOST_THROW_EXCEPTION(std::bad_alloc{});
100 case Z_BUF_ERROR:
101 return error::need_buffers;
102 default:
103 BOOST_THROW_EXCEPTION(std::runtime_error{"zlib decompressor: impossible value"});
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{};
147 public:
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
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
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;
337 window_[0] = 9;
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()(
385 F const& f,
386 std::string const& check) const
387 {
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)
396 f(
397 window,
398 compress(check, level, window, 4, strategy),
399 check,
400 suite_);
401 }
402 }
403 }
404 };
405
406 void
407 testInflate(IDecompressor& d)
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 ;
420 m(Beast{half, half}, check);
421 }
422
423 {
424 Matrix m{*this};
425 auto const check = corpus1(5000);
426 m(Beast{half, half}, check);
427 }
428 {
429 Matrix m{*this};
430 auto const check = corpus2(5000);
431 m(Beast{half, half}, check);
432 }
433 {
434 Matrix m{*this};
435 auto const check = corpus1(1000);
436 m.level(6);
437 m.window(9);
438 m.strategy(Z_DEFAULT_STRATEGY);
439 m(Beast{once, full}, check);
440 }
441 {
442 Matrix m{*this};
443 auto const check = corpus2(1000);
444 m.level(6);
445 m.window(9);
446 m.strategy(Z_DEFAULT_STRATEGY);
447 m(Beast{once, full}, check);
448 }
449 {
450 Matrix m{*this};
451 m.level(6);
452 m.window(9);
453 auto const check = corpus1(200);
454 m(Beast{full, full}, check);
455 }
456 {
457 Matrix m{*this};
458 m.level(6);
459 m.window(9);
460 auto const check = corpus2(500);
461 m(Beast{full, full}, check);
462 }
463 {
464 Matrix m{*this};
465 auto const check = corpus2(1000);
466 m.level(6);
467 m.window(9);
468 m.strategy(Z_DEFAULT_STRATEGY);
469 m(Beast{full, once, Flush::block}, check);
470 }
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 }
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);
501 m(Beast{full, once, Flush::trees}, check);
502 }
503 #endif
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");
635 }
636
637 void
638 run() override
639 {
640 log <<
641 "sizeof(inflate_stream) == " <<
642 sizeof(inflate_stream) << std::endl;
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);
653 }
654 };
655
656 BEAST_DEFINE_TESTSUITE(beast,zlib,inflate_stream);
657
658 } // zlib
659 } // beast
660 } // boost