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/deflate_stream.hpp>
13 #include <boost/beast/core/string.hpp>
14 #include <boost/beast/_experimental/unit_test/suite.hpp>
20 #include "zlib-1.2.11/zlib.h"
26 class deflate_stream_test : public beast::unit_test::suite
29 virtual void init() = 0;
36 virtual std::size_t avail_in() const noexcept = 0;
37 virtual void avail_in(std::size_t) noexcept = 0;
38 virtual void const* next_in() const noexcept = 0;
39 virtual void next_in(const void*) noexcept = 0;
40 virtual std::size_t avail_out() const noexcept = 0;
41 virtual void avail_out(std::size_t) noexcept = 0;
42 virtual void* next_out() const noexcept = 0;
43 virtual void next_out(void*) noexcept = 0;
44 virtual std::size_t total_out() const noexcept = 0;
46 virtual std::size_t bound(std::size_t) = 0;
47 virtual error_code write(Flush) = 0;
48 virtual ~ICompressor() = default;
50 class ZlibCompressor : public ICompressor {
54 ZlibCompressor() = default;
55 void init() override {
58 const auto res = deflateInit2(&zs, -1, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
60 throw std::invalid_argument{"zlib compressor: failure"};
66 int strategy) override
70 const auto res = deflateInit2(&zs, level, Z_DEFLATED, -windowBits, memLevel, strategy);
72 BOOST_THROW_EXCEPTION(std::invalid_argument{"zlib compressor: 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; }
83 virtual std::size_t total_out() const noexcept override { return zs.total_out; }
85 std::size_t bound(std::size_t src_size) override {
86 return deflateBound(&zs, static_cast<uLong>(src_size));
88 error_code write(Flush flush) override {
89 constexpr static int zlib_flushes[] = {0, Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, Z_FULL_FLUSH, Z_FINISH, Z_TREES};
90 const auto zlib_flush = zlib_flushes[static_cast<int>(flush)];
91 if(zs.next_in == nullptr && zs.avail_in != 0)
92 BOOST_THROW_EXCEPTION(std::invalid_argument{"zlib compressor: invalid input"});
93 const auto res = deflate(&zs, zlib_flush);
98 return error::end_of_stream;
100 return error::stream_error;
102 return error::need_buffers;
108 ~ZlibCompressor() override {
112 class BeastCompressor : public ICompressor {
117 BeastCompressor() = default;
118 void init() override {
127 int strategy) override
135 toStrategy(strategy));
138 virtual std::size_t avail_in() const noexcept override { return zp.avail_in; }
139 virtual void avail_in(std::size_t n) noexcept override { zp.avail_in = n; }
140 virtual void const* next_in() const noexcept override { return zp.next_in; }
141 virtual void next_in(const void* ptr) noexcept override { zp.next_in = ptr; }
142 virtual std::size_t avail_out() const noexcept override { return zp.avail_out; }
143 virtual void avail_out(std::size_t n_out) noexcept override { zp.avail_out = n_out; }
144 virtual void* next_out() const noexcept override { return zp.next_out; }
145 virtual void next_out(void* ptr) noexcept override { zp.next_out = (Bytef*)ptr; }
146 virtual std::size_t total_out() const noexcept override { return zp.total_out; }
148 std::size_t bound(std::size_t src_size) override {
149 return ds.upper_bound(src_size);
151 error_code write(Flush flush) override {
153 ds.write(zp, flush, ec);
157 ~BeastCompressor() override = default;
161 // Lots of repeats, limited char range
164 corpus1(std::size_t n)
166 static std::string const alphabet{
167 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
172 std::uniform_int_distribution<std::size_t> d0{
173 0, alphabet.size() - 1};
174 std::uniform_int_distribution<std::size_t> d1{
178 auto const rep = d1(g);
179 auto const ch = alphabet[d0(g)];
180 s.insert(s.end(), rep, ch);
189 corpus2(std::size_t n)
194 std::uniform_int_distribution<std::uint32_t> d0{0, 255};
196 s.push_back(static_cast<char>(d0(g)));
203 string_view const& in,
204 int level, // 0=none, 1..9, -1=default
205 int windowBits, // 9..15
206 int memLevel) // 1..9 (8=default)
208 int const strategy = Z_DEFAULT_STRATEGY;
211 memset(&zs, 0, sizeof(zs));
212 result = deflateInit2(
220 throw std::logic_error{"deflateInit2 failed"};
221 zs.next_in = (Bytef*)in.data();
222 zs.avail_in = static_cast<uInt>(in.size());
224 out.resize(deflateBound(&zs,
225 static_cast<uLong>(in.size())));
226 zs.next_in = (Bytef*)in.data();
227 zs.avail_in = static_cast<uInt>(in.size());
228 zs.next_out = (Bytef*)&out[0];
229 zs.avail_out = static_cast<uInt>(out.size());
230 result = deflate(&zs, Z_FULL_FLUSH);
232 throw std::logic_error("deflate failed");
233 out.resize(zs.total_out);
240 decompress(string_view const& in)
245 memset(&zs, 0, sizeof(zs));
246 result = inflateInit2(&zs, -15);
248 throw std::logic_error{"inflateInit2 failed"};
251 zs.next_in = (Bytef*)in.data();
252 zs.avail_in = static_cast<uInt>(in.size());
255 out.resize(zs.total_out + 1024);
256 zs.next_out = (Bytef*)&out[zs.total_out];
257 zs.avail_out = static_cast<uInt>(
258 out.size() - zs.total_out);
259 result = inflate(&zs, Z_SYNC_FLUSH);
260 if( result == Z_NEED_DICT ||
261 result == Z_DATA_ERROR ||
262 result == Z_MEM_ERROR)
264 throw std::logic_error("inflate failed");
268 if(result == Z_STREAM_END)
271 out.resize(zs.total_out);
282 //--------------------------------------------------------------------------
284 using self = deflate_stream_test;
285 typedef void(self::*pmf_t)(
287 int level, int windowBits, int memLevel,
288 int strategy, std::string const&);
292 toStrategy(int strategy)
297 case 0: return Strategy::normal;
298 case 1: return Strategy::filtered;
299 case 2: return Strategy::huffman;
300 case 3: return Strategy::rle;
301 case 4: return Strategy::fixed;
308 int level, int windowBits, int memLevel,
309 int strategy, std::string const& check)
317 out.resize(c.bound(check.size()));
318 c.next_in(check.data());
319 c.avail_in(check.size());
320 c.next_out((void*)out.data());
321 c.avail_out(out.size());
323 bool progress = true;
326 error_code ec = c.write(Flush::full);
327 if( ec == error::need_buffers ||
328 ec == error::end_of_stream) // per zlib FAQ
330 if(! BEAST_EXPECTS(! ec, ec.message()))
332 if(! BEAST_EXPECT(progress))
339 out.resize(c.total_out());
340 BEAST_EXPECT(decompress(out) == check);
346 //--------------------------------------------------------------------------
351 int level, int windowBits, int memLevel,
352 int strategy, std::string const& check)
354 for(std::size_t i = 1; i < check.size(); ++i)
356 for(std::size_t j = 1;; ++j)
364 out.resize(c.bound(check.size()));
367 c.next_in((void*)check.data());
369 c.next_out((void*)out.data());
375 error_code ec = c.write(
376 bi ? Flush::full : Flush::none);
377 if( ec == error::need_buffers ||
378 ec == error::end_of_stream) // per zlib FAQ
380 if(! BEAST_EXPECTS(! ec, ec.message()))
382 if(c.avail_in() == 0 && ! bi)
385 c.avail_in(check.size() - i);
387 if(c.avail_out() == 0 && ! bo)
390 c.avail_out(out.size() - j);
395 out.resize(c.total_out());
396 BEAST_EXPECT(decompress(out) == check);
404 //--------------------------------------------------------------------------
407 doMatrix(ICompressor& c, std::string const& check, pmf_t pmf)
409 for(int level = 0; level <= 9; ++level)
411 for(int windowBits = 8; windowBits <= 9; ++windowBits)
413 // zlib has a bug with windowBits==8
416 for(int strategy = 0; strategy <= 4; ++strategy)
418 for (int memLevel = 8; memLevel <= 9; ++memLevel)
421 c, level, windowBits, memLevel, strategy, check);
427 // Check default settings
428 (this->*pmf)(c, compression::default_size, 15, 8, 0, check);
432 testDeflate(ICompressor& c)
434 doMatrix(c, "Hello, world!", &self::doDeflate1_beast);
435 doMatrix(c, "Hello, world!", &self::doDeflate2_beast);
436 log << "no-silence keepalive" << std::endl;
437 doMatrix(c, corpus1(56), &self::doDeflate2_beast);
438 doMatrix(c, corpus1(1024), &self::doDeflate1_beast);
441 void testInvalidSettings(ICompressor& c)
443 except<std::invalid_argument>(
446 c.init(-42, 15, 8, static_cast<int>(Strategy::normal));
448 except<std::invalid_argument>(
451 c.init(compression::default_size, -1, 8, static_cast<int>(Strategy::normal));
453 except<std::invalid_argument>(
456 c.init(compression::default_size, 15, -1, static_cast<int>(Strategy::normal));
458 except<std::invalid_argument>(
464 c.write(Flush::full);
469 testWriteAfterFinish(ICompressor& c)
474 string_view s = "Hello";
476 c.avail_in(s.size());
477 c.next_out(&out.front());
478 c.avail_out(out.size());
479 error_code ec = c.write(Flush::sync);
483 ec = c.write(Flush::finish);
484 BEAST_EXPECT(ec == error::end_of_stream);
486 c.avail_in(s.size());
487 c.next_out(&out.front());
488 c.avail_out(out.size());
489 ec = c.write(Flush::sync);
490 BEAST_EXPECT(ec == error::stream_error);
491 ec = c.write(Flush::finish);
492 BEAST_EXPECT(ec == error::need_buffers);
496 testFlushPartial(ICompressor& c)
501 string_view s = "Hello";
503 c.avail_in(s.size());
504 c.next_out(&out.front());
505 c.avail_out(out.size());
507 ec = c.write(Flush::none);
509 ec = c.write(Flush::partial);
514 testFlushAtLiteralBufferFull(ICompressor& c)
519 explicit fixture(ICompressor&c, std::size_t n, Strategy s) : c(c)
521 c.init(8, 15, 1, static_cast<int>(s));
522 std::iota(in.begin(), in.end(), std::uint8_t{0});
524 c.next_in(in.data());
525 c.avail_in(in.size());
526 c.next_out(&out.front());
527 c.avail_out(out.size());
530 std::array<std::uint8_t, 255> in;
534 for (auto s : {Strategy::huffman, Strategy::rle, Strategy::normal})
537 fixture f{c, 264, s};
538 error_code ec = c.write(Flush::finish);
539 BEAST_EXPECT(ec == error::end_of_stream);
540 BEAST_EXPECT(c.avail_out() == 1);
545 error_code ec = c.write(Flush::finish);
547 BEAST_EXPECT(c.avail_out() == 0);
552 error_code ec = c.write(Flush::sync);
560 testRLEMatchLengthExceedLookahead(ICompressor& c)
562 std::vector<std::uint8_t> in;
565 c.init(8, 15, 1, static_cast<int>(Strategy::rle));
566 std::fill_n(in.begin(), 4, 'a');
568 out.resize(in.size() * 2);
569 c.next_in(in.data());
570 c.avail_in(in.size());
571 c.next_out(&out.front());
572 c.avail_out(out.size());
575 ec = c.write(Flush::sync);
580 testFlushAfterDistMatch(ICompressor& c)
582 for (auto out_size : {144, 129})
584 std::array<std::uint8_t, 256> in{};
585 // 125 will mostly fill the lit buffer, so emitting a distance code
586 // results in a flush.
587 auto constexpr n = 125;
588 std::iota(in.begin(), in.begin() + n,
589 static_cast<std::uint8_t>(0));
590 std::iota(in.begin() + n, in.end(),
591 static_cast<std::uint8_t>(0));
593 c.init(8, 15, 1, static_cast<int>(Strategy::normal));
595 out.resize(out_size);
596 c.next_in(in.data());
597 c.avail_in(in.size());
598 c.next_out(&out.front());
599 c.avail_out(out.size());
602 ec = c.write(Flush::sync);
611 "sizeof(deflate_stream) == " <<
612 sizeof(deflate_stream) << std::endl;
614 testDeflate(zlib_compressor);
615 testDeflate(beast_compressor);
616 testInvalidSettings(zlib_compressor);
617 testInvalidSettings(beast_compressor);
618 testWriteAfterFinish(zlib_compressor);
619 testWriteAfterFinish(beast_compressor);
620 testFlushPartial(zlib_compressor);
621 testFlushPartial(beast_compressor);
622 testFlushAtLiteralBufferFull(zlib_compressor);
623 testFlushAtLiteralBufferFull(beast_compressor);
624 testRLEMatchLengthExceedLookahead(zlib_compressor);
625 testRLEMatchLengthExceedLookahead(beast_compressor);
626 testFlushAfterDistMatch(zlib_compressor);
627 testFlushAfterDistMatch(beast_compressor);
631 BEAST_DEFINE_TESTSUITE(beast,zlib,deflate_stream);