2 // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
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)
7 // Official repository: https://github.com/boostorg/beast
10 // Test that header file is self-contained.
11 #include <boost/beast/zlib/inflate_stream.hpp>
13 #include <boost/beast/core/string.hpp>
14 #include <boost/beast/_experimental/unit_test/suite.hpp>
18 #include "zlib-1.2.11/zlib.h"
24 class inflate_stream_test
: public beast::unit_test::suite
26 struct IDecompressor
{
27 virtual void init() = 0;
28 virtual void init(int windowBits
) = 0;
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;
39 virtual error_code
write(Flush
) = 0;
40 virtual ~IDecompressor() = default;
42 class ZlibDecompressor
: public IDecompressor
{
46 ZlibDecompressor() = default;
47 void init(int windowBits
) override
51 const auto res
= inflateInit2(&zs
, windowBits
);
56 BOOST_THROW_EXCEPTION(std::runtime_error
{"zlib decompressor: no memory"});
58 BOOST_THROW_EXCEPTION(std::domain_error
{"zlib decompressor: bad arg"});
61 void init() override
{
64 const auto res
= inflateInit2(&zs
, -15);
69 BOOST_THROW_EXCEPTION(std::runtime_error
{"zlib decompressor: no memory"});
71 BOOST_THROW_EXCEPTION(std::domain_error
{"zlib decompressor: bad arg"});
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
; }
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
);
92 return error::end_of_stream
;
94 return error::need_dict
;
97 return error::stream_error
;
99 BOOST_THROW_EXCEPTION(std::bad_alloc
{});
101 return error::need_buffers
;
103 BOOST_THROW_EXCEPTION(std::runtime_error
{"zlib decompressor: impossible value"});
107 ~ZlibDecompressor() override
{
110 } zlib_decompressor
{};
111 class BeastCompressor
: public IDecompressor
{
116 BeastCompressor() = default;
118 void init(int windowBits
) override
122 is
.reset(windowBits
);
124 void init() override
{
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
; }
139 error_code
write(Flush flush
) override
{
141 is
.write(zp
, flush
, ec
);
145 ~BeastCompressor() override
= default;
146 } beast_decompressor
{};
148 // Lots of repeats, limited char range
151 corpus1(std::size_t n
)
153 static std::string
const alphabet
{
154 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
159 std::uniform_int_distribution
<std::size_t> d0
{
160 0, alphabet
.size() - 1};
161 std::uniform_int_distribution
<std::size_t> d1
{
165 auto const rep
= d1(g
);
166 auto const ch
= alphabet
[d0(g
)];
167 s
.insert(s
.end(), rep
, ch
);
176 corpus2(std::size_t n
)
181 std::uniform_int_distribution
<std::uint32_t> d0
{0, 255};
183 s
.push_back(static_cast<char>(d0(g
)));
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
198 memset(&zs
, 0, sizeof(zs
));
199 result
= deflateInit2(
207 throw std::logic_error
{"deflateInit2 failed"};
208 zs
.next_in
= (Bytef
*)in
.data();
209 zs
.avail_in
= static_cast<uInt
>(in
.size());
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
);
219 throw std::logic_error("deflate failed");
220 out
.resize(zs
.total_out
);
225 //--------------------------------------------------------------------------
241 Beast(Split in
, Split check
, Flush flush
= Flush::sync
)
251 std::string
const& in
,
252 std::string
const& check
,
253 unit_test::suite
& suite
) const
256 [&](std::size_t i
, std::size_t j
)
258 std::string
out(check
.size(), 0);
260 zs
.next_in
= in
.data();
261 zs
.next_out
= &out
[0];
266 bool bi
= ! (i
< in
.size());
267 bool bo
= ! (j
< check
.size());
271 is
.write(zs
, flush_
, ec
);
272 if( ec
== error::need_buffers
||
273 ec
== error::end_of_stream
)
275 out
.resize(zs
.total_out
);
276 suite
.expect(out
== check
, __FILE__
, __LINE__
);
281 suite
.fail(ec
.message(), __FILE__
, __LINE__
);
284 if(zs
.avail_in
== 0 && ! bi
)
287 zs
.avail_in
= in
.size() - i
;
289 if(zs
.avail_out
== 0 && ! bo
)
292 zs
.avail_out
= check
.size() - j
;
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;
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;
316 for(std::size_t i
= i0
; i
<= i1
; ++i
)
317 for(std::size_t j
= j0
; j
<= j1
; ++j
)
324 unit_test::suite
& suite_
;
332 Matrix(unit_test::suite
& suite
)
344 level(int from
, int to
)
357 window(int from
, int to
)
370 strategy(int from
, int to
)
379 strategy(what
, what
);
386 std::string
const& check
) const
388 for(auto level
= level_
[0];
389 level
<= level_
[1]; ++level
)
391 for(auto window
= window_
[0];
392 window
<= window_
[1]; ++window
)
394 for(auto strategy
= strategy_
[0];
395 strategy
<= strategy_
[1]; ++strategy
)
398 compress(check
, level
, window
, 4, strategy
),
407 testInflate(IDecompressor
& d
)
412 "{\n \"AutobahnPython/0.6.0\": {\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"
420 m(Beast
{half
, half
}, check
);
425 auto const check
= corpus1(5000);
426 m(Beast
{half
, half
}, check
);
430 auto const check
= corpus2(5000);
431 m(Beast
{half
, half
}, check
);
435 auto const check
= corpus1(1000);
438 m
.strategy(Z_DEFAULT_STRATEGY
);
439 m(Beast
{once
, full
}, check
);
443 auto const check
= corpus2(1000);
446 m
.strategy(Z_DEFAULT_STRATEGY
);
447 m(Beast
{once
, full
}, check
);
453 auto const check
= corpus1(200);
454 m(Beast
{full
, full
}, check
);
460 auto const check
= corpus2(500);
461 m(Beast
{full
, full
}, check
);
465 auto const check
= corpus2(1000);
468 m
.strategy(Z_DEFAULT_STRATEGY
);
469 m(Beast
{full
, once
, Flush::block
}, check
);
472 // Check a known string - this provides more stable coverage,
473 // independent of the RNG-generated strings.
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.";
488 m
.strategy(Z_DEFAULT_STRATEGY
);
489 m(Beast
{full
, full
}, check
);
492 // VFALCO Fails, but I'm unsure of what the correct
493 // behavior of Z_TREES/Flush::trees is.
497 auto const check
= corpus2(10000);
500 m
.strategy(Z_DEFAULT_STRATEGY
);
501 m(Beast
{full
, once
, Flush::trees
}, check
);
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}, {});
514 std::string
check(IDecompressor
& d
,
515 std::initializer_list
<std::uint8_t> const& in
,
517 std::size_t window_size
= 15,
518 std::size_t len
= -1)
520 std::string
out(1024, 0);
523 is
.reset(static_cast<int>(window_size
));
524 boost::system::error_code ec
;
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();
531 while (zs
.avail_in
> 0 && !ec
)
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
;
539 BEAST_EXPECT(ec
== expected
);
543 void testInflateErrors(IDecompressor
& d
)
545 check(d
, {0x00, 0x00, 0x00, 0x00, 0x00},
546 error::invalid_stored_length
);
547 check(d
, {0x03, 0x00},
548 error::end_of_stream
);
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},
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,
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
);
588 void testInvalidSettings(IDecompressor
& d
)
590 except
<std::domain_error
>(
597 void testFixedHuffmanFlushTrees(IDecompressor
& d
)
599 std::string
out(5, 0);
601 boost::system::error_code ec
;
602 std::initializer_list
<std::uint8_t> in
= {
603 0xf2, 0x48, 0xcd, 0xc9, 0xc9, 0x07, 0x00, 0x00,
605 d
.next_in(&*in
.begin());
607 d
.avail_in(in
.size());
608 d
.avail_out(out
.size());
609 ec
= d
.write(Flush::trees
);
611 ec
= d
.write(Flush::sync
);
613 BEAST_EXPECT(d
.avail_out() == 0);
614 BEAST_EXPECT(out
== "Hello");
617 void testUncompressedFlushTrees(IDecompressor
& d
)
619 std::string
out(5, 0);
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());
627 d
.avail_in(in
.size());
628 d
.avail_out(out
.size());
629 ec
= d
.write(Flush::trees
);
631 ec
= d
.write(Flush::sync
);
633 BEAST_EXPECT(d
.avail_out() == 0);
634 BEAST_EXPECT(out
== "Hello");
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
);
656 BEAST_DEFINE_TESTSUITE(beast
,zlib
,inflate_stream
);