Files
boost_beast/test/beast/zlib/deflate_stream.cpp

392 lines
11 KiB
C++
Raw Normal View History

//
2019-02-21 07:00:31 -08:00
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// 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
//
// Test that header file is self-contained.
2017-07-20 13:40:34 -07:00
#include <boost/beast/zlib/deflate_stream.hpp>
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 {
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
static
2017-07-31 18:40:14 -07:00
std::string
corpus1(std::size_t n)
{
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)
{
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);
}
2017-07-31 18:40:14 -07:00
s.resize(n);
return s;
}
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)
{
2017-07-31 18:40:14 -07:00
int const strategy = Z_DEFAULT_STRATEGY;
int result;
2017-07-31 18:40:14 -07:00
z_stream zs;
memset(&zs, 0, sizeof(zs));
result = deflateInit2(
&zs,
level,
Z_DEFLATED,
-windowBits,
2017-07-31 18:40:14 -07:00
memLevel,
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;
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];
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
{
2017-07-31 18:40:14 -07:00
zs.next_in = (Bytef*)in.data();
zs.avail_in = static_cast<uInt>(in.size());
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;
}
2017-07-31 18:40:14 -07:00
out.resize(zs.total_out);
inflateEnd(&zs);
}
2017-07-31 18:40:14 -07:00
catch(...)
{
2017-07-31 18:40:14 -07:00
inflateEnd(&zs);
throw;
}
2017-07-31 18:40:14 -07:00
return out;
}
2017-07-31 18:40:14 -07:00
//--------------------------------------------------------------------------
using self = deflate_stream_test;
typedef void(self::*pmf_t)(
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;
}
}
void
doDeflate1_beast(
int level, int windowBits, int memLevel,
int strategy, std::string const& check)
{
std::string out;
z_params zs;
deflate_stream ds;
ds.reset(
level,
windowBits,
memLevel,
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()))
goto err;
2017-07-25 12:35:54 -07:00
if(! BEAST_EXPECT(progress))
goto err;
progress = false;
}
}
fin:
out.resize(zs.total_out);
2017-07-31 18:40:14 -07:00
BEAST_EXPECT(decompress(out) == check);
err:
;
}
//--------------------------------------------------------------------------
void
doDeflate2_beast(
int level, int windowBits, int memLevel,
int strategy, std::string const& check)
{
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,
memLevel,
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()))
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);
err:
;
}
}
}
//--------------------------------------------------------------------------
void
2017-07-31 18:40:14 -07:00
doMatrix(std::string const& check, pmf_t pmf)
{
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;
for(int strategy = 0; strategy <= 4; ++strategy)
{
for (int memLevel = 8; memLevel <= 9; ++memLevel)
{
(this->*pmf)(
level, windowBits, memLevel, strategy, check);
}
}
}
}
// Check default settings
(this->*pmf)(compression::default_size, 15, 8, 0, check);
}
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);
}
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);
});
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);
});
}
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);
}
void
run() override
{
log <<
"sizeof(deflate_stream) == " <<
sizeof(deflate_stream) << std::endl;
testDeflate();
testInvalidSettings();
testWriteAfterFinish();
}
};
2017-08-01 17:01:57 -07:00
BEAST_DEFINE_TESTSUITE(beast,zlib,deflate_stream);
} // zlib
} // beast
2017-07-20 13:40:34 -07:00
} // boost