2016-10-18 19:43:36 -04:00
|
|
|
//
|
2019-02-21 07:00:31 -08:00
|
|
|
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
|
2016-10-18 19:43:36 -04:00
|
|
|
//
|
|
|
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
|
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
//
|
2017-07-20 13:40:34 -07:00
|
|
|
// Official repository: https://github.com/boostorg/beast
|
|
|
|
//
|
2016-10-18 19:43:36 -04:00
|
|
|
|
|
|
|
// Test that header file is self-contained.
|
2017-07-20 13:40:34 -07:00
|
|
|
#include <boost/beast/zlib/deflate_stream.hpp>
|
2016-10-18 19:43:36 -04:00
|
|
|
|
2017-07-31 18:40:14 -07:00
|
|
|
#include <boost/beast/core/string.hpp>
|
2018-11-11 14:07:55 -08:00
|
|
|
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
2017-07-31 18:40:14 -07:00
|
|
|
#include <cstdint>
|
|
|
|
#include <random>
|
|
|
|
|
|
|
|
#include "zlib-1.2.11/zlib.h"
|
|
|
|
|
2017-07-20 13:40:34 -07:00
|
|
|
namespace boost {
|
2016-10-18 19:43:36 -04:00
|
|
|
namespace beast {
|
|
|
|
namespace zlib {
|
|
|
|
|
|
|
|
class deflate_stream_test : public beast::unit_test::suite
|
|
|
|
{
|
|
|
|
public:
|
2017-07-31 18:40:14 -07:00
|
|
|
// Lots of repeats, limited char range
|
2016-10-18 19:43:36 -04:00
|
|
|
static
|
2017-07-31 18:40:14 -07:00
|
|
|
std::string
|
|
|
|
corpus1(std::size_t n)
|
2016-10-18 19:43:36 -04:00
|
|
|
{
|
2017-07-31 18:40:14 -07:00
|
|
|
static std::string const alphabet{
|
|
|
|
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
|
|
|
};
|
|
|
|
std::string s;
|
|
|
|
s.reserve(n + 5);
|
|
|
|
std::mt19937 g;
|
|
|
|
std::uniform_int_distribution<std::size_t> d0{
|
|
|
|
0, alphabet.size() - 1};
|
|
|
|
std::uniform_int_distribution<std::size_t> d1{
|
|
|
|
1, 5};
|
|
|
|
while(s.size() < n)
|
2016-10-18 19:43:36 -04:00
|
|
|
{
|
2017-07-31 18:40:14 -07:00
|
|
|
auto const rep = d1(g);
|
|
|
|
auto const ch = alphabet[d0(g)];
|
|
|
|
s.insert(s.end(), rep, ch);
|
2016-10-18 19:43:36 -04:00
|
|
|
}
|
2017-07-31 18:40:14 -07:00
|
|
|
s.resize(n);
|
|
|
|
return s;
|
2016-10-18 19:43:36 -04:00
|
|
|
}
|
|
|
|
|
2017-07-31 18:40:14 -07:00
|
|
|
// Random data
|
|
|
|
static
|
|
|
|
std::string
|
|
|
|
corpus2(std::size_t n)
|
|
|
|
{
|
|
|
|
std::string s;
|
|
|
|
s.reserve(n);
|
|
|
|
std::mt19937 g;
|
|
|
|
std::uniform_int_distribution<std::uint32_t> d0{0, 255};
|
|
|
|
while(n--)
|
|
|
|
s.push_back(static_cast<char>(d0(g)));
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
std::string
|
|
|
|
compress(
|
|
|
|
string_view const& in,
|
|
|
|
int level, // 0=none, 1..9, -1=default
|
|
|
|
int windowBits, // 9..15
|
|
|
|
int memLevel) // 1..9 (8=default)
|
2016-10-18 19:43:36 -04:00
|
|
|
{
|
2017-07-31 18:40:14 -07:00
|
|
|
int const strategy = Z_DEFAULT_STRATEGY;
|
2016-10-18 19:43:36 -04:00
|
|
|
int result;
|
2017-07-31 18:40:14 -07:00
|
|
|
z_stream zs;
|
|
|
|
memset(&zs, 0, sizeof(zs));
|
|
|
|
result = deflateInit2(
|
|
|
|
&zs,
|
2016-10-18 19:43:36 -04:00
|
|
|
level,
|
|
|
|
Z_DEFLATED,
|
|
|
|
-windowBits,
|
2017-07-31 18:40:14 -07:00
|
|
|
memLevel,
|
2016-10-18 19:43:36 -04:00
|
|
|
strategy);
|
2017-07-31 18:40:14 -07:00
|
|
|
if(result != Z_OK)
|
|
|
|
throw std::logic_error{"deflateInit2 failed"};
|
|
|
|
zs.next_in = (Bytef*)in.data();
|
|
|
|
zs.avail_in = static_cast<uInt>(in.size());
|
|
|
|
std::string out;
|
2016-10-18 19:43:36 -04:00
|
|
|
out.resize(deflateBound(&zs,
|
2017-07-31 18:40:14 -07:00
|
|
|
static_cast<uLong>(in.size())));
|
|
|
|
zs.next_in = (Bytef*)in.data();
|
|
|
|
zs.avail_in = static_cast<uInt>(in.size());
|
|
|
|
zs.next_out = (Bytef*)&out[0];
|
2016-10-18 19:43:36 -04:00
|
|
|
zs.avail_out = static_cast<uInt>(out.size());
|
2017-07-31 18:40:14 -07:00
|
|
|
result = deflate(&zs, Z_FULL_FLUSH);
|
|
|
|
if(result != Z_OK)
|
|
|
|
throw std::logic_error("deflate failed");
|
|
|
|
out.resize(zs.total_out);
|
|
|
|
deflateEnd(&zs);
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
std::string
|
|
|
|
decompress(string_view const& in)
|
|
|
|
{
|
|
|
|
int result;
|
|
|
|
std::string out;
|
|
|
|
z_stream zs;
|
|
|
|
memset(&zs, 0, sizeof(zs));
|
|
|
|
result = inflateInit2(&zs, -15);
|
|
|
|
try
|
2016-10-18 19:43:36 -04:00
|
|
|
{
|
2017-07-31 18:40:14 -07:00
|
|
|
zs.next_in = (Bytef*)in.data();
|
|
|
|
zs.avail_in = static_cast<uInt>(in.size());
|
2016-10-18 19:43:36 -04:00
|
|
|
for(;;)
|
|
|
|
{
|
2017-07-31 18:40:14 -07:00
|
|
|
out.resize(zs.total_out + 1024);
|
|
|
|
zs.next_out = (Bytef*)&out[zs.total_out];
|
|
|
|
zs.avail_out = static_cast<uInt>(
|
|
|
|
out.size() - zs.total_out);
|
|
|
|
result = inflate(&zs, Z_SYNC_FLUSH);
|
|
|
|
if( result == Z_NEED_DICT ||
|
|
|
|
result == Z_DATA_ERROR ||
|
|
|
|
result == Z_MEM_ERROR)
|
|
|
|
{
|
|
|
|
throw std::logic_error("inflate failed");
|
|
|
|
}
|
|
|
|
if(zs.avail_out > 0)
|
|
|
|
break;
|
|
|
|
if(result == Z_STREAM_END)
|
|
|
|
break;
|
2016-10-18 19:43:36 -04:00
|
|
|
}
|
2017-07-31 18:40:14 -07:00
|
|
|
out.resize(zs.total_out);
|
|
|
|
inflateEnd(&zs);
|
2016-10-18 19:43:36 -04:00
|
|
|
}
|
2017-07-31 18:40:14 -07:00
|
|
|
catch(...)
|
2016-10-18 19:43:36 -04:00
|
|
|
{
|
2017-07-31 18:40:14 -07:00
|
|
|
inflateEnd(&zs);
|
|
|
|
throw;
|
2016-10-18 19:43:36 -04:00
|
|
|
}
|
2017-07-31 18:40:14 -07:00
|
|
|
return out;
|
|
|
|
}
|
2016-10-18 19:43:36 -04:00
|
|
|
|
2017-07-31 18:40:14 -07:00
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
|
|
using self = deflate_stream_test;
|
|
|
|
typedef void(self::*pmf_t)(
|
2019-08-19 23:08:16 +02:00
|
|
|
int level, int windowBits, int memLevel,
|
|
|
|
int strategy, std::string const&);
|
2017-07-31 18:40:14 -07:00
|
|
|
|
|
|
|
static
|
|
|
|
Strategy
|
|
|
|
toStrategy(int strategy)
|
|
|
|
{
|
|
|
|
switch(strategy)
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
case 0: return Strategy::normal;
|
|
|
|
case 1: return Strategy::filtered;
|
|
|
|
case 2: return Strategy::huffman;
|
|
|
|
case 3: return Strategy::rle;
|
|
|
|
case 4: return Strategy::fixed;
|
|
|
|
}
|
2016-10-18 19:43:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
doDeflate1_beast(
|
2019-08-19 23:08:16 +02:00
|
|
|
int level, int windowBits, int memLevel,
|
|
|
|
int strategy, std::string const& check)
|
2016-10-18 19:43:36 -04:00
|
|
|
{
|
|
|
|
std::string out;
|
|
|
|
z_params zs;
|
|
|
|
deflate_stream ds;
|
|
|
|
ds.reset(
|
|
|
|
level,
|
|
|
|
windowBits,
|
2019-08-19 23:08:16 +02:00
|
|
|
memLevel,
|
2016-10-18 19:43:36 -04:00
|
|
|
toStrategy(strategy));
|
|
|
|
out.resize(ds.upper_bound(
|
|
|
|
static_cast<uLong>(check.size())));
|
|
|
|
zs.next_in = (Bytef*)check.data();
|
|
|
|
zs.avail_in = static_cast<uInt>(check.size());
|
|
|
|
zs.next_out = (Bytef*)out.data();
|
|
|
|
zs.avail_out = static_cast<uInt>(out.size());
|
|
|
|
{
|
|
|
|
bool progress = true;
|
|
|
|
for(;;)
|
|
|
|
{
|
|
|
|
error_code ec;
|
|
|
|
ds.write(zs, Flush::full, ec);
|
|
|
|
if( ec == error::need_buffers ||
|
|
|
|
ec == error::end_of_stream) // per zlib FAQ
|
|
|
|
goto fin;
|
2017-07-25 12:35:54 -07:00
|
|
|
if(! BEAST_EXPECTS(! ec, ec.message()))
|
2016-10-18 19:43:36 -04:00
|
|
|
goto err;
|
2017-07-25 12:35:54 -07:00
|
|
|
if(! BEAST_EXPECT(progress))
|
2016-10-18 19:43:36 -04:00
|
|
|
goto err;
|
|
|
|
progress = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fin:
|
|
|
|
out.resize(zs.total_out);
|
2017-07-31 18:40:14 -07:00
|
|
|
BEAST_EXPECT(decompress(out) == check);
|
2016-10-18 19:43:36 -04:00
|
|
|
|
|
|
|
err:
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void
|
|
|
|
doDeflate2_beast(
|
2019-08-19 23:08:16 +02:00
|
|
|
int level, int windowBits, int memLevel,
|
|
|
|
int strategy, std::string const& check)
|
2016-10-18 19:43:36 -04:00
|
|
|
{
|
|
|
|
for(std::size_t i = 1; i < check.size(); ++i)
|
|
|
|
{
|
|
|
|
for(std::size_t j = 1;; ++j)
|
|
|
|
{
|
|
|
|
z_params zs;
|
|
|
|
deflate_stream ds;
|
|
|
|
ds.reset(
|
|
|
|
level,
|
|
|
|
windowBits,
|
2019-08-19 23:08:16 +02:00
|
|
|
memLevel,
|
2016-10-18 19:43:36 -04:00
|
|
|
toStrategy(strategy));
|
|
|
|
std::string out;
|
|
|
|
out.resize(ds.upper_bound(
|
|
|
|
static_cast<uLong>(check.size())));
|
|
|
|
if(j >= out.size())
|
|
|
|
break;
|
|
|
|
zs.next_in = (Bytef*)check.data();
|
|
|
|
zs.avail_in = static_cast<uInt>(i);
|
|
|
|
zs.next_out = (Bytef*)out.data();
|
|
|
|
zs.avail_out = static_cast<uInt>(j);
|
|
|
|
bool bi = false;
|
|
|
|
bool bo = false;
|
|
|
|
for(;;)
|
|
|
|
{
|
|
|
|
error_code ec;
|
|
|
|
ds.write(zs,
|
|
|
|
bi ? Flush::full : Flush::none, ec);
|
|
|
|
if( ec == error::need_buffers ||
|
|
|
|
ec == error::end_of_stream) // per zlib FAQ
|
|
|
|
goto fin;
|
2017-07-25 12:35:54 -07:00
|
|
|
if(! BEAST_EXPECTS(! ec, ec.message()))
|
2016-10-18 19:43:36 -04:00
|
|
|
goto err;
|
|
|
|
if(zs.avail_in == 0 && ! bi)
|
|
|
|
{
|
|
|
|
bi = true;
|
|
|
|
zs.avail_in =
|
|
|
|
static_cast<uInt>(check.size() - i);
|
|
|
|
}
|
|
|
|
if(zs.avail_out == 0 && ! bo)
|
|
|
|
{
|
|
|
|
bo = true;
|
|
|
|
zs.avail_out =
|
|
|
|
static_cast<uInt>(out.size() - j);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fin:
|
|
|
|
out.resize(zs.total_out);
|
2017-07-31 18:40:14 -07:00
|
|
|
BEAST_EXPECT(decompress(out) == check);
|
2016-10-18 19:43:36 -04:00
|
|
|
|
|
|
|
err:
|
|
|
|
;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void
|
2017-07-31 18:40:14 -07:00
|
|
|
doMatrix(std::string const& check, pmf_t pmf)
|
2016-10-18 19:43:36 -04:00
|
|
|
{
|
|
|
|
for(int level = 0; level <= 9; ++level)
|
|
|
|
{
|
|
|
|
for(int windowBits = 8; windowBits <= 9; ++windowBits)
|
|
|
|
{
|
2017-07-12 14:22:15 -07:00
|
|
|
// zlib has a bug with windowBits==8
|
|
|
|
if(windowBits == 8)
|
|
|
|
continue;
|
2016-10-18 19:43:36 -04:00
|
|
|
for(int strategy = 0; strategy <= 4; ++strategy)
|
|
|
|
{
|
2019-08-19 23:08:16 +02:00
|
|
|
for (int memLevel = 8; memLevel <= 9; ++memLevel)
|
|
|
|
{
|
|
|
|
(this->*pmf)(
|
|
|
|
level, windowBits, memLevel, strategy, check);
|
|
|
|
}
|
2016-10-18 19:43:36 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-08-19 23:08:16 +02:00
|
|
|
|
|
|
|
// Check default settings
|
|
|
|
(this->*pmf)(compression::default_size, 15, 8, 0, check);
|
2016-10-18 19:43:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
testDeflate()
|
|
|
|
{
|
2017-07-31 18:40:14 -07:00
|
|
|
doMatrix("Hello, world!", &self::doDeflate1_beast);
|
|
|
|
doMatrix("Hello, world!", &self::doDeflate2_beast);
|
|
|
|
doMatrix(corpus1(56), &self::doDeflate2_beast);
|
|
|
|
doMatrix(corpus1(1024), &self::doDeflate1_beast);
|
2016-10-18 19:43:36 -04:00
|
|
|
}
|
|
|
|
|
2019-07-29 20:24:33 +02:00
|
|
|
void testInvalidSettings()
|
|
|
|
{
|
|
|
|
except<std::invalid_argument>(
|
|
|
|
[]()
|
|
|
|
{
|
|
|
|
deflate_stream ds;
|
|
|
|
ds.reset(-42, 15, 8, Strategy::normal);
|
|
|
|
});
|
|
|
|
except<std::invalid_argument>(
|
|
|
|
[]()
|
|
|
|
{
|
|
|
|
deflate_stream ds;
|
|
|
|
ds.reset(compression::default_size, -1, 8, Strategy::normal);
|
|
|
|
});
|
|
|
|
except<std::invalid_argument>(
|
|
|
|
[]()
|
|
|
|
{
|
|
|
|
deflate_stream ds;
|
|
|
|
ds.reset(compression::default_size, 15, -1, Strategy::normal);
|
|
|
|
});
|
2019-08-20 00:23:19 +02:00
|
|
|
except<std::invalid_argument>(
|
|
|
|
[]()
|
|
|
|
{
|
|
|
|
deflate_stream ds;
|
|
|
|
ds.reset();
|
|
|
|
z_params zp{};
|
|
|
|
zp.avail_in = 1;
|
|
|
|
zp.next_in = nullptr;
|
|
|
|
error_code ec;
|
|
|
|
ds.write(zp, Flush::full, ec);
|
|
|
|
});
|
2019-07-29 20:24:33 +02:00
|
|
|
}
|
|
|
|
|
2019-09-09 02:53:56 +02:00
|
|
|
void
|
|
|
|
testWriteAfterFinish()
|
|
|
|
{
|
|
|
|
z_params zp;
|
|
|
|
deflate_stream ds;
|
|
|
|
ds.reset();
|
|
|
|
std::string out;
|
|
|
|
out.resize(1024);
|
|
|
|
string_view s = "Hello";
|
|
|
|
zp.next_in = s.data();
|
|
|
|
zp.avail_in = s.size();
|
|
|
|
zp.next_out = &out.front();
|
|
|
|
zp.avail_out = out.size();
|
|
|
|
error_code ec;
|
|
|
|
ds.write(zp, Flush::sync, ec);
|
|
|
|
BEAST_EXPECT(!ec);
|
|
|
|
zp.next_in = nullptr;
|
|
|
|
zp.avail_in = 0;
|
|
|
|
ds.write(zp, Flush::finish, ec);
|
|
|
|
BEAST_EXPECT(ec == error::end_of_stream);
|
|
|
|
zp.next_in = s.data();
|
|
|
|
zp.avail_in = s.size();
|
|
|
|
zp.next_out = &out.front();
|
|
|
|
zp.avail_out = out.size();
|
|
|
|
ds.write(zp, Flush::sync, ec);
|
|
|
|
BEAST_EXPECT(ec == error::stream_error);
|
|
|
|
ds.write(zp, Flush::finish, ec);
|
|
|
|
BEAST_EXPECT(ec == error::need_buffers);
|
|
|
|
}
|
|
|
|
|
2016-10-18 19:43:36 -04:00
|
|
|
void
|
|
|
|
run() override
|
|
|
|
{
|
|
|
|
log <<
|
|
|
|
"sizeof(deflate_stream) == " <<
|
|
|
|
sizeof(deflate_stream) << std::endl;
|
|
|
|
|
|
|
|
testDeflate();
|
2019-07-29 20:24:33 +02:00
|
|
|
testInvalidSettings();
|
2019-09-09 02:53:56 +02:00
|
|
|
testWriteAfterFinish();
|
2016-10-18 19:43:36 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-08-01 17:01:57 -07:00
|
|
|
BEAST_DEFINE_TESTSUITE(beast,zlib,deflate_stream);
|
2016-10-18 19:43:36 -04:00
|
|
|
|
|
|
|
} // zlib
|
|
|
|
} // beast
|
2017-07-20 13:40:34 -07:00
|
|
|
} // boost
|