Imported Upstream version 1.72.0
[platform/upstream/boost.git] / 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