diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e19eac6..cf10e33a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ Version 218: * detect_ssl, async_detect_ssl are public interfaces * Add OpenSSL installation/setup instructions +* Enable split Beast compilation for tests -------------------------------------------------------------------------------- diff --git a/Jamfile b/Jamfile index e1764cb2..ed4d16b1 100644 --- a/Jamfile +++ b/Jamfile @@ -52,7 +52,7 @@ variant beast_ubasan path-constant TEST_MAIN : include/boost/beast/_experimental/unit_test/main.cpp ; lib static_asio - : test/asio.cpp + : test/lib_asio.cpp : requirements [ requires cxx11_constexpr @@ -61,14 +61,39 @@ lib static_asio cxx11_template_aliases cxx11_variadic_templates ] + BOOST_ASIO_SEPARATE_COMPILATION BOOST_ASIO_NO_DEPRECATED=1 BOOST_ASIO_DISABLE_BOOST_ARRAY=1 BOOST_ASIO_DISABLE_BOOST_BIND=1 BOOST_ASIO_DISABLE_BOOST_DATE_TIME=1 BOOST_ASIO_DISABLE_BOOST_REGEX=1 - BOOST_ASIO_SEPARATE_COMPILATION BOOST_COROUTINES_NO_DEPRECATION_WARNING=1 - windows:_WIN32_WINNT=0x0501 + windows:_WIN32_WINNT=0x0601 + static + ; + +lib static_beast + : test/lib_beast.cpp + : requirements + [ requires + cxx11_constexpr + cxx11_decltype + cxx11_hdr_tuple + cxx11_template_aliases + cxx11_variadic_templates + ] + BOOST_BEAST_SPLIT_COMPILATION + BOOST_ASIO_NO_DEPRECATED=1 + BOOST_ASIO_DISABLE_BOOST_ARRAY=1 + BOOST_ASIO_DISABLE_BOOST_BIND=1 + BOOST_ASIO_DISABLE_BOOST_DATE_TIME=1 + BOOST_ASIO_DISABLE_BOOST_REGEX=1 + BOOST_COROUTINES_NO_DEPRECATION_WARNING=1 + msvc:_SCL_SECURE_NO_WARNINGS=1 + msvc:_CRT_SECURE_NO_WARNINGS=1 + msvc:_SILENCE_CXX17_ALLOCATOR_VOID_DEPRECATION_WARNING + msvc:_SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING + windows:_WIN32_WINNT=0x0601 static ; @@ -79,13 +104,13 @@ project /boost/beast /boost/coroutine//boost_coroutine /boost/filesystem//boost_filesystem static_asio + static_beast /boost//headers multi on shared [ ac.check-library /boost/beast//ssl : /boost/beast//ssl : no ] /boost/beast//crypto - BOOST_ALL_NO_LIB=1 BOOST_ASIO_NO_DEPRECATED=1 BOOST_ASIO_DISABLE_BOOST_ARRAY=1 @@ -93,14 +118,10 @@ project /boost/beast BOOST_ASIO_DISABLE_BOOST_DATE_TIME=1 BOOST_ASIO_DISABLE_BOOST_REGEX=1 BOOST_COROUTINES_NO_DEPRECATION_WARNING=1 - - # workaround for asio defect - BOOST_ASIO_DISABLE_WINDOWS_RANDOM_ACCESS_HANDLE=1 - BOOST_ASIO_DISABLE_WINDOWS_STREAM_HANDLE=1 - - # VFALCO FIXME Need this for recent asio changes - BOOST_BEAST_NO_FILE_BODY_WIN32=1 - + #BOOST_ASIO_SEPARATE_COMPILATION + BOOST_ASIO_DISABLE_WINDOWS_RANDOM_ACCESS_HANDLE=1 + BOOST_ASIO_DISABLE_WINDOWS_STREAM_HANDLE=1 + #BOOST_BEAST_SEPARATE_COMPILATION msvc:"/bigobj" msvc-14.1:"/permissive-" msvc:_SCL_SECURE_NO_WARNINGS=1 @@ -108,7 +129,6 @@ project /boost/beast msvc:_SILENCE_CXX17_ALLOCATOR_VOID_DEPRECATION_WARNING msvc:_SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING msvc,release:"/Ob2 /Oi /Ot" - linux:_XOPEN_SOURCE=600 linux:_GNU_SOURCE=1 solaris:_XOPEN_SOURCE=500 @@ -128,3 +148,4 @@ project /boost/beast : usage-requirements ; + diff --git a/doc/qbk/release_notes.qbk b/doc/qbk/release_notes.qbk index 28060012..b5189893 100644 --- a/doc/qbk/release_notes.qbk +++ b/doc/qbk/release_notes.qbk @@ -64,7 +64,15 @@ * All asynchronous operations use Asio's [@boost:/doc/html/boost_asio/reference/async_initiate.html `async_initiate`] for efficient integration with Coroutines TS. - * OpenSSL is now required to build tests and examples + * ''' + ⚡ + ''' + [*['faster compilation]], + ''' + ⚡ + ''' + define `BOOST_BEAST_SPLIT_COMPILATION` and include + [@../../include/boost/beast/src.hpp src.hpp] in one of your .cpp files! * See the full [link beast.release_notes [*Release Notes]] for a complete list of changes. diff --git a/include/boost/beast/_experimental/test/error.hpp b/include/boost/beast/_experimental/test/error.hpp index 61b26fde..d583bf93 100644 --- a/include/boost/beast/_experimental/test/error.hpp +++ b/include/boost/beast/_experimental/test/error.hpp @@ -11,6 +11,7 @@ #define BOOST_BEAST_TEST_ERROR_HPP #include +#include namespace boost { namespace beast { @@ -21,8 +22,8 @@ enum class error { /** The test stream generated a simulated testing error - This error is returned by the test @ref stream when it - generates a simulated error. + This error is returned by a @ref fail_count object + when it generates a simulated error. */ test_failure = 1 }; diff --git a/include/boost/beast/_experimental/test/fail_count.hpp b/include/boost/beast/_experimental/test/fail_count.hpp index d09ad440..4f8881a0 100644 --- a/include/boost/beast/_experimental/test/fail_count.hpp +++ b/include/boost/beast/_experimental/test/fail_count.hpp @@ -11,7 +11,6 @@ #define BOOST_BEAST_TEST_FAIL_COUNT_HPP #include -#include #include #include @@ -47,7 +46,7 @@ public: explicit fail_count( std::size_t n, - error_code ev = make_error_code(error::test_failure)); + error_code ev = error::test_failure); /// Throw an exception on the Nth failure BOOST_BEAST_DECL diff --git a/include/boost/beast/_experimental/test/impl/error.hpp b/include/boost/beast/_experimental/test/impl/error.hpp index cfe0eac0..e7d8bd8b 100644 --- a/include/boost/beast/_experimental/test/impl/error.hpp +++ b/include/boost/beast/_experimental/test/impl/error.hpp @@ -29,26 +29,6 @@ namespace boost { namespace beast { namespace test { -namespace detail { - -class error_codes : public error_category -{ -public: - BOOST_BEAST_DECL - const char* - name() const noexcept override; - - BOOST_BEAST_DECL - std::string - message(int ev) const override; - - BOOST_BEAST_DECL - error_condition - default_error_condition(int ev) const noexcept override; -}; - -} // detail - BOOST_BEAST_DECL error_code make_error_code(error e) noexcept; @@ -57,6 +37,8 @@ make_error_code(error e) noexcept; } // beast } // boost -#include +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif #endif diff --git a/include/boost/beast/_experimental/test/impl/error.ipp b/include/boost/beast/_experimental/test/impl/error.ipp new file mode 100644 index 00000000..f4447137 --- /dev/null +++ b/include/boost/beast/_experimental/test/impl/error.ipp @@ -0,0 +1,65 @@ +// +// 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_TEST_IMPL_ERROR_IPP +#define BOOST_BEAST_TEST_IMPL_ERROR_IPP + +#include + +namespace boost { +namespace beast { +namespace test { + +namespace detail { + +class error_codes : public error_category +{ +public: + BOOST_BEAST_DECL + const char* + name() const noexcept override + { + return "boost.beast.test"; + } + + BOOST_BEAST_DECL + std::string + message(int ev) const override + { + switch(static_cast(ev)) + { + default: + case error::test_failure: return + "An automatic unit test failure occurred"; + } + } + + BOOST_BEAST_DECL + error_condition + default_error_condition(int ev) const noexcept override + { + return error_condition{ev, *this}; + } +}; + +} // detail + +error_code +make_error_code(error e) noexcept +{ + static detail::error_codes const cat{}; + return error_code{static_cast< + std::underlying_type::type>(e), cat}; +} + +} // test +} // beast +} // boost + +#endif diff --git a/include/boost/beast/_experimental/test/impl/impl/error.hpp b/include/boost/beast/_experimental/test/impl/impl/error.hpp deleted file mode 100644 index 0456788b..00000000 --- a/include/boost/beast/_experimental/test/impl/impl/error.hpp +++ /dev/null @@ -1,59 +0,0 @@ -// -// 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) -// -// Official repository: https://github.com/boostorg/beast -// - -#ifndef BOOST_BEAST_TEST_IMPL_IMPL_ERROR_HPP -#define BOOST_BEAST_TEST_IMPL_IMPL_ERROR_HPP - -namespace boost { -namespace beast { -namespace test { - -namespace detail { - -const char* -error_codes:: -name() const noexcept -{ - return "boost.beast.test"; -} - -std::string -error_codes:: -message(int ev) const -{ - switch(static_cast(ev)) - { - default: - case error::test_failure: return - "The test stream generated a simulated error"; - } -} - -error_condition -error_codes:: -default_error_condition(int ev) const noexcept -{ - return error_condition{ev, *this}; -} - -} // detail - -error_code -make_error_code(error e) noexcept -{ - static detail::error_codes const cat{}; - return error_code{static_cast< - std::underlying_type::type>(e), cat}; -} - -} // test -} // beast -} // boost - -#endif diff --git a/include/boost/beast/_experimental/test/impl/stream.hpp b/include/boost/beast/_experimental/test/impl/stream.hpp index 1edb3a53..79c10c44 100644 --- a/include/boost/beast/_experimental/test/impl/stream.hpp +++ b/include/boost/beast/_experimental/test/impl/stream.hpp @@ -236,7 +236,7 @@ struct stream::run_write_op return; } - // A request to read 0 bytes from a stream is a no-op. + // A request to write 0 bytes to a stream is a no-op. if(buffer_size(buffers) == 0) { net::post( @@ -251,11 +251,12 @@ struct stream::run_write_op auto out = out_.lock(); if(! out) { + ec = net::error::connection_reset; net::post( in_->ioc.get_executor(), beast::bind_front_handler( std::move(h), - net::error::connection_reset, + ec, std::size_t{0})); return; } @@ -269,209 +270,18 @@ struct stream::run_write_op out->b.commit(n); out->notify_read(); } + BOOST_ASSERT(! ec); net::post( in_->ioc.get_executor(), beast::bind_front_handler( std::move(h), - error_code{}, + ec, n)); } }; //------------------------------------------------------------------------------ -stream:: -state:: -state( - net::io_context& ioc_, - fail_count* fc_) - : ioc(ioc_) - , fc(fc_) -{ -} - -stream:: -state:: -~state() -{ - // cancel outstanding read - if(op != nullptr) - (*op)(true); -} - -void -stream:: -state:: -notify_read() -{ - if(op) - { - auto op_ = std::move(op); - op_->operator()(); - } - else - { - cv.notify_all(); - } -} - -//------------------------------------------------------------------------------ - -stream:: -~stream() -{ - close(); -} - -stream:: -stream(stream&& other) -{ - auto in = std::make_shared( - other.in_->ioc, other.in_->fc); - in_ = std::move(other.in_); - out_ = std::move(other.out_); - other.in_ = in; -} - -stream& -stream:: -operator=(stream&& other) -{ - close(); - auto in = std::make_shared( - other.in_->ioc, other.in_->fc); - in_ = std::move(other.in_); - out_ = std::move(other.out_); - other.in_ = in; - return *this; -} - -//------------------------------------------------------------------------------ - -stream:: -stream(net::io_context& ioc) - : in_(std::make_shared(ioc, nullptr)) -{ -} - -stream:: -stream( - net::io_context& ioc, - fail_count& fc) - : in_(std::make_shared(ioc, &fc)) -{ -} - -stream:: -stream( - net::io_context& ioc, - string_view s) - : in_(std::make_shared(ioc, nullptr)) -{ - in_->b.commit(net::buffer_copy( - in_->b.prepare(s.size()), - net::buffer(s.data(), s.size()))); -} - -stream:: -stream( - net::io_context& ioc, - fail_count& fc, - string_view s) - : in_(std::make_shared(ioc, &fc)) -{ - in_->b.commit(net::buffer_copy( - in_->b.prepare(s.size()), - net::buffer(s.data(), s.size()))); -} - -void -stream:: -connect(stream& remote) -{ - BOOST_ASSERT(! out_.lock()); - BOOST_ASSERT(! remote.out_.lock()); - out_ = remote.in_; - remote.out_ = in_; - in_->code = status::ok; - remote.in_->code = status::ok; -} - -string_view -stream:: -str() const -{ - auto const bs = in_->b.data(); - if(buffer_size(bs) == 0) - return {}; - auto const b = beast::buffers_front(bs); - return {static_cast(b.data()), b.size()}; -} - -void -stream:: -append(string_view s) -{ - std::lock_guard lock{in_->m}; - in_->b.commit(net::buffer_copy( - in_->b.prepare(s.size()), - net::buffer(s.data(), s.size()))); -} - -void -stream:: -clear() -{ - std::lock_guard lock{in_->m}; - in_->b.consume(in_->b.size()); -} - -void -stream:: -close() -{ - // cancel outstanding read - { - std::unique_ptr op; - { - std::lock_guard lock(in_->m); - in_->code = status::eof; - op = std::move(in_->op); - } - if(op != nullptr) - (*op)(true); - } - - // disconnect - { - auto out = out_.lock(); - out_.reset(); - - // notify peer - if(out) - { - std::lock_guard lock(out->m); - if(out->code == status::ok) - { - out->code = status::eof; - out->notify_read(); - } - } - } -} - -void -stream:: -close_remote() -{ - std::lock_guard lock{in_->m}; - if(in_->code == status::ok) - { - in_->code = status::eof; - in_->notify_read(); - } -} - template std::size_t stream:: @@ -637,24 +447,7 @@ async_write_some( buffers); } -void -teardown( - websocket::role_type, - stream& s, - boost::system::error_code& ec) -{ - if( s.in_->fc && - s.in_->fc->fail(ec)) - return; - - s.close(); - - if( s.in_->fc && - s.in_->fc->fail(ec)) - ec = net::error::eof; - else - ec = {}; -} +//------------------------------------------------------------------------------ template void @@ -683,19 +476,7 @@ async_teardown( std::move(handler), ec)); } -stream -connect(stream& to) -{ - stream from{to.get_executor().context()}; - from.connect(to); - return from; -} - -void -connect(stream& s1, stream& s2) -{ - s1.connect(s2); -} +//------------------------------------------------------------------------------ template stream @@ -712,4 +493,8 @@ connect(stream& to, Arg1&& arg1, ArgN&&... argn) } // beast } // boost +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif + #endif diff --git a/include/boost/beast/_experimental/test/impl/stream.ipp b/include/boost/beast/_experimental/test/impl/stream.ipp new file mode 100644 index 00000000..a0e37438 --- /dev/null +++ b/include/boost/beast/_experimental/test/impl/stream.ipp @@ -0,0 +1,256 @@ +// +// 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_TEST_IMPL_STREAM_IPP +#define BOOST_BEAST_TEST_IMPL_STREAM_IPP + +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace test { + +//------------------------------------------------------------------------------ + +stream:: +state:: +state( + net::io_context& ioc_, + fail_count* fc_) + : ioc(ioc_) + , fc(fc_) +{ +} + +stream:: +state:: +~state() +{ + // cancel outstanding read + if(op != nullptr) + (*op)(true); +} + +void +stream:: +state:: +notify_read() +{ + if(op) + { + auto op_ = std::move(op); + op_->operator()(); + } + else + { + cv.notify_all(); + } +} + +//------------------------------------------------------------------------------ + +stream:: +~stream() +{ + close(); +} + +stream:: +stream(stream&& other) +{ + auto in = std::make_shared( + other.in_->ioc, other.in_->fc); + in_ = std::move(other.in_); + out_ = std::move(other.out_); + other.in_ = in; +} + +stream& +stream:: +operator=(stream&& other) +{ + close(); + auto in = std::make_shared( + other.in_->ioc, other.in_->fc); + in_ = std::move(other.in_); + out_ = std::move(other.out_); + other.in_ = in; + return *this; +} + +//------------------------------------------------------------------------------ + +stream:: +stream(net::io_context& ioc) + : in_(std::make_shared(ioc, nullptr)) +{ +} + +stream:: +stream( + net::io_context& ioc, + fail_count& fc) + : in_(std::make_shared(ioc, &fc)) +{ +} + +stream:: +stream( + net::io_context& ioc, + string_view s) + : in_(std::make_shared(ioc, nullptr)) +{ + in_->b.commit(net::buffer_copy( + in_->b.prepare(s.size()), + net::buffer(s.data(), s.size()))); +} + +stream:: +stream( + net::io_context& ioc, + fail_count& fc, + string_view s) + : in_(std::make_shared(ioc, &fc)) +{ + in_->b.commit(net::buffer_copy( + in_->b.prepare(s.size()), + net::buffer(s.data(), s.size()))); +} + +void +stream:: +connect(stream& remote) +{ + BOOST_ASSERT(! out_.lock()); + BOOST_ASSERT(! remote.out_.lock()); + out_ = remote.in_; + remote.out_ = in_; + in_->code = status::ok; + remote.in_->code = status::ok; +} + +string_view +stream:: +str() const +{ + auto const bs = in_->b.data(); + if(buffer_size(bs) == 0) + return {}; + auto const b = beast::buffers_front(bs); + return {static_cast(b.data()), b.size()}; +} + +void +stream:: +append(string_view s) +{ + std::lock_guard lock{in_->m}; + in_->b.commit(net::buffer_copy( + in_->b.prepare(s.size()), + net::buffer(s.data(), s.size()))); +} + +void +stream:: +clear() +{ + std::lock_guard lock{in_->m}; + in_->b.consume(in_->b.size()); +} + +void +stream:: +close() +{ + // cancel outstanding read + { + std::unique_ptr op; + { + std::lock_guard lock(in_->m); + in_->code = status::eof; + op = std::move(in_->op); + } + if(op != nullptr) + (*op)(true); + } + + // disconnect + { + auto out = out_.lock(); + out_.reset(); + + // notify peer + if(out) + { + std::lock_guard lock(out->m); + if(out->code == status::ok) + { + out->code = status::eof; + out->notify_read(); + } + } + } +} + +void +stream:: +close_remote() +{ + std::lock_guard lock{in_->m}; + if(in_->code == status::ok) + { + in_->code = status::eof; + in_->notify_read(); + } +} + +void +teardown( + websocket::role_type, + stream& s, + boost::system::error_code& ec) +{ + if( s.in_->fc && + s.in_->fc->fail(ec)) + return; + + s.close(); + + if( s.in_->fc && + s.in_->fc->fail(ec)) + ec = net::error::eof; + else + ec = {}; +} + +//------------------------------------------------------------------------------ + +stream +connect(stream& to) +{ + stream from{to.get_executor().context()}; + from.connect(to); + return from; +} + +void +connect(stream& s1, stream& s2) +{ + s1.connect(s2); +} + +} // test +} // beast +} // boost + +#endif diff --git a/include/boost/beast/core/detail/base64.hpp b/include/boost/beast/core/detail/base64.hpp index 459577a7..7f91c047 100644 --- a/include/boost/beast/core/detail/base64.hpp +++ b/include/boost/beast/core/detail/base64.hpp @@ -7,35 +7,6 @@ // Official repository: https://github.com/boostorg/beast // -/* - Portions from http://www.adp-gmbh.ch/cpp/common/base64.html - Copyright notice: - - base64.cpp and base64.h - - Copyright (C) 2004-2008 Rene Nyffenegger - - This source code is provided 'as-is', without any express or implied - warranty. In no event will the author be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this source code must not be misrepresented; you must not - claim that you wrote the original source code. If you use this source code - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original source code. - - 3. This notice may not be removed or altered from any source distribution. - - Rene Nyffenegger rene.nyffenegger@adp-gmbh.ch -*/ - #ifndef BOOST_BEAST_DETAIL_BASE64_HPP #define BOOST_BEAST_DETAIL_BASE64_HPP @@ -50,47 +21,16 @@ namespace detail { namespace base64 { -inline +BOOST_BEAST_DECL char const* -get_alphabet() -{ - static char constexpr tab[] = { - "ABCDEFGHIJKLMNOP" - "QRSTUVWXYZabcdef" - "ghijklmnopqrstuv" - "wxyz0123456789+/" - }; - return &tab[0]; -} +get_alphabet(); -inline +BOOST_BEAST_DECL signed char const* -get_inverse() -{ - static signed char constexpr tab[] = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0-15 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16-31 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, // 32-47 - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, // 48-63 - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 64-79 - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, // 80-95 - -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 96-111 - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, // 112-127 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128-143 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144-159 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160-175 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176-191 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192-207 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208-223 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224-239 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 240-255 - }; - return &tab[0]; -} - +get_inverse(); /// Returns max chars needed to encode a base64 string -inline +BOOST_BEAST_DECL std::size_t constexpr encoded_size(std::size_t n) { @@ -103,7 +43,6 @@ std::size_t constexpr decoded_size(std::size_t n) { return n / 4 * 3; // requires n&3==0, smaller - //return 3 * n / 4; } /** Encode a series of octets as a padded, base64 string. @@ -118,45 +57,9 @@ decoded_size(std::size_t n) @return The number of characters written to `out`. This will exclude any null termination. */ -inline +BOOST_BEAST_DECL std::size_t -encode(void* dest, void const* src, std::size_t len) -{ - char* out = static_cast(dest); - char const* in = static_cast(src); - auto const tab = base64::get_alphabet(); - - for(auto n = len / 3; n--;) - { - *out++ = tab[ (in[0] & 0xfc) >> 2]; - *out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)]; - *out++ = tab[((in[2] & 0xc0) >> 6) + ((in[1] & 0x0f) << 2)]; - *out++ = tab[ in[2] & 0x3f]; - in += 3; - } - - switch(len % 3) - { - case 2: - *out++ = tab[ (in[0] & 0xfc) >> 2]; - *out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)]; - *out++ = tab[ (in[1] & 0x0f) << 2]; - *out++ = '='; - break; - - case 1: - *out++ = tab[ (in[0] & 0xfc) >> 2]; - *out++ = tab[((in[0] & 0x03) << 4)]; - *out++ = '='; - *out++ = '='; - break; - - case 0: - break; - } - - return out - static_cast(dest); -} +encode(void* dest, void const* src, std::size_t len); /** Decode a padded base64 string into a series of octets. @@ -169,72 +72,19 @@ encode(void* dest, void const* src, std::size_t len) the number of characters read from the input string, expressed as a pair. */ -inline +BOOST_BEAST_DECL std::pair -decode(void* dest, char const* src, std::size_t len) -{ - char* out = static_cast(dest); - auto in = reinterpret_cast(src); - unsigned char c3[3], c4[4]; - int i = 0; - int j = 0; - - auto const inverse = base64::get_inverse(); - - while(len-- && *in != '=') - { - auto const v = inverse[*in]; - if(v == -1) - break; - ++in; - c4[i] = v; - if(++i == 4) - { - c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4); - c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2); - c3[2] = ((c4[2] & 0x3) << 6) + c4[3]; - - for(i = 0; i < 3; i++) - *out++ = c3[i]; - i = 0; - } - } - - if(i) - { - c3[0] = ( c4[0] << 2) + ((c4[1] & 0x30) >> 4); - c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2); - c3[2] = ((c4[2] & 0x3) << 6) + c4[3]; - - for(j = 0; j < i - 1; j++) - *out++ = c3[j]; - } - - return {out - static_cast(dest), - in - reinterpret_cast(src)}; -} +decode(void* dest, char const* src, std::size_t len); } // base64 -inline +BOOST_BEAST_DECL std::string -base64_encode( - std::uint8_t const* data, - std::size_t len) -{ - std::string dest; - dest.resize(base64::encoded_size(len)); - dest.resize(base64::encode(&dest[0], data, len)); - return dest; -} +base64_encode(std::uint8_t const* data, std::size_t len); -inline +BOOST_BEAST_DECL std::string -base64_encode(string_view s) -{ - return base64_encode (reinterpret_cast < - std::uint8_t const*> (s.data()), s.size()); -} +base64_encode(string_view s); template std::string @@ -252,4 +102,8 @@ base64_decode(string_view data) } // beast } // boost +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif + #endif diff --git a/include/boost/beast/core/detail/base64.ipp b/include/boost/beast/core/detail/base64.ipp new file mode 100644 index 00000000..7e11d188 --- /dev/null +++ b/include/boost/beast/core/detail/base64.ipp @@ -0,0 +1,220 @@ +// +// 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) +// +// Official repository: https://github.com/boostorg/beast +// + +/* + Portions from http://www.adp-gmbh.ch/cpp/common/base64.html + Copyright notice: + + base64.cpp and base64.h + + Copyright (C) 2004-2008 Rene Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + + 3. This notice may not be removed or altered from any source distribution. + + Rene Nyffenegger rene.nyffenegger@adp-gmbh.ch +*/ + +#ifndef BOOST_BEAST_DETAIL_BASE64_IPP +#define BOOST_BEAST_DETAIL_BASE64_IPP + +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace detail { + +namespace base64 { + +char const* +get_alphabet() +{ + static char constexpr tab[] = { + "ABCDEFGHIJKLMNOP" + "QRSTUVWXYZabcdef" + "ghijklmnopqrstuv" + "wxyz0123456789+/" + }; + return &tab[0]; +} + +signed char const* +get_inverse() +{ + static signed char constexpr tab[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0-15 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16-31 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, // 32-47 + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, // 48-63 + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 64-79 + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, // 80-95 + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 96-111 + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, // 112-127 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128-143 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144-159 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160-175 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176-191 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192-207 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208-223 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224-239 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 240-255 + }; + return &tab[0]; +} + +/** Encode a series of octets as a padded, base64 string. + + The resulting string will not be null terminated. + + @par Requires + + The memory pointed to by `out` points to valid memory + of at least `encoded_size(len)` bytes. + + @return The number of characters written to `out`. This + will exclude any null termination. +*/ +std::size_t +encode(void* dest, void const* src, std::size_t len) +{ + char* out = static_cast(dest); + char const* in = static_cast(src); + auto const tab = base64::get_alphabet(); + + for(auto n = len / 3; n--;) + { + *out++ = tab[ (in[0] & 0xfc) >> 2]; + *out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)]; + *out++ = tab[((in[2] & 0xc0) >> 6) + ((in[1] & 0x0f) << 2)]; + *out++ = tab[ in[2] & 0x3f]; + in += 3; + } + + switch(len % 3) + { + case 2: + *out++ = tab[ (in[0] & 0xfc) >> 2]; + *out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)]; + *out++ = tab[ (in[1] & 0x0f) << 2]; + *out++ = '='; + break; + + case 1: + *out++ = tab[ (in[0] & 0xfc) >> 2]; + *out++ = tab[((in[0] & 0x03) << 4)]; + *out++ = '='; + *out++ = '='; + break; + + case 0: + break; + } + + return out - static_cast(dest); +} + +/** Decode a padded base64 string into a series of octets. + + @par Requires + + The memory pointed to by `out` points to valid memory + of at least `decoded_size(len)` bytes. + + @return The number of octets written to `out`, and + the number of characters read from the input string, + expressed as a pair. +*/ +std::pair +decode(void* dest, char const* src, std::size_t len) +{ + char* out = static_cast(dest); + auto in = reinterpret_cast(src); + unsigned char c3[3], c4[4]; + int i = 0; + int j = 0; + + auto const inverse = base64::get_inverse(); + + while(len-- && *in != '=') + { + auto const v = inverse[*in]; + if(v == -1) + break; + ++in; + c4[i] = v; + if(++i == 4) + { + c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4); + c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2); + c3[2] = ((c4[2] & 0x3) << 6) + c4[3]; + + for(i = 0; i < 3; i++) + *out++ = c3[i]; + i = 0; + } + } + + if(i) + { + c3[0] = ( c4[0] << 2) + ((c4[1] & 0x30) >> 4); + c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2); + c3[2] = ((c4[2] & 0x3) << 6) + c4[3]; + + for(j = 0; j < i - 1; j++) + *out++ = c3[j]; + } + + return {out - static_cast(dest), + in - reinterpret_cast(src)}; +} + +} // base64 + +std::string +base64_encode( + std::uint8_t const* data, + std::size_t len) +{ + std::string dest; + dest.resize(base64::encoded_size(len)); + dest.resize(base64::encode(&dest[0], data, len)); + return dest; +} + +std::string +base64_encode(string_view s) +{ + return base64_encode (reinterpret_cast < + std::uint8_t const*> (s.data()), s.size()); +} + +} // detail +} // beast +} // boost + +#endif diff --git a/include/boost/beast/core/detail/config.hpp b/include/boost/beast/core/detail/config.hpp index b9e28ac3..19ef3242 100644 --- a/include/boost/beast/core/detail/config.hpp +++ b/include/boost/beast/core/detail/config.hpp @@ -67,12 +67,6 @@ namespace net = boost::asio; # endif #endif -#if BOOST_BEAST_DOXYGEN -# define BOOST_BEAST_DECL -#else -# define BOOST_BEAST_DECL inline -#endif - #define BOOST_BEAST_INLINE_VARIABLE(name, type) \ namespace \ { \ @@ -80,4 +74,20 @@ namespace net = boost::asio; ::boost::beast::detail::static_const::value; \ } +// Default to a header-only implementation. The user must specifically +// request separate compilation by defining BOOST_BEAST_SPLIT_COMPILATION +#ifndef BOOST_BEAST_HEADER_ONLY +# ifndef BOOST_BEAST_SPLIT_COMPILATION +# define BOOST_BEAST_HEADER_ONLY 1 +# endif +#endif + +#if BOOST_BEAST_DOXYGEN +# define BOOST_BEAST_DECL +#elif defined(BOOST_BEAST_HEADER_ONLY) +# define BOOST_BEAST_DECL inline +#else +# define BOOST_BEAST_DECL +#endif + #endif diff --git a/include/boost/beast/core/detail/sha1.hpp b/include/boost/beast/core/detail/sha1.hpp index 76284e01..abcc3074 100644 --- a/include/boost/beast/core/detail/sha1.hpp +++ b/include/boost/beast/core/detail/sha1.hpp @@ -10,6 +10,8 @@ #ifndef BOOST_BEAST_DETAIL_SHA1_HPP #define BOOST_BEAST_DETAIL_SHA1_HPP +#include + #include #include #include @@ -34,189 +36,6 @@ static std::size_t constexpr BLOCK_INTS = 16; static std::size_t constexpr BLOCK_BYTES = 64; static std::size_t constexpr DIGEST_BYTES = 20; -inline -std::uint32_t -rol(std::uint32_t value, std::size_t bits) -{ - return (value << bits) | (value >> (32 - bits)); -} - -inline -std::uint32_t -blk(std::uint32_t block[BLOCK_INTS], std::size_t i) -{ - return rol( - block[(i+13)&15] ^ block[(i+8)&15] ^ - block[(i+2)&15] ^ block[i], 1); -} - -inline -void -R0(std::uint32_t block[BLOCK_INTS], std::uint32_t v, - std::uint32_t &w, std::uint32_t x, std::uint32_t y, - std::uint32_t &z, std::size_t i) -{ - z += ((w&(x^y))^y) + block[i] + 0x5a827999 + rol(v, 5); - w = rol(w, 30); -} - - -inline -void -R1(std::uint32_t block[BLOCK_INTS], std::uint32_t v, - std::uint32_t &w, std::uint32_t x, std::uint32_t y, - std::uint32_t &z, std::size_t i) -{ - block[i] = blk(block, i); - z += ((w&(x^y))^y) + block[i] + 0x5a827999 + rol(v, 5); - w = rol(w, 30); -} - -inline -void -R2(std::uint32_t block[BLOCK_INTS], std::uint32_t v, - std::uint32_t &w, std::uint32_t x, std::uint32_t y, - std::uint32_t &z, std::size_t i) -{ - block[i] = blk(block, i); - z += (w^x^y) + block[i] + 0x6ed9eba1 + rol(v, 5); - w = rol(w, 30); -} - -inline -void -R3(std::uint32_t block[BLOCK_INTS], std::uint32_t v, - std::uint32_t &w, std::uint32_t x, std::uint32_t y, - std::uint32_t &z, std::size_t i) -{ - block[i] = blk(block, i); - z += (((w|x)&y)|(w&x)) + block[i] + 0x8f1bbcdc + rol(v, 5); - w = rol(w, 30); -} - -inline -void -R4(std::uint32_t block[BLOCK_INTS], std::uint32_t v, - std::uint32_t &w, std::uint32_t x, std::uint32_t y, - std::uint32_t &z, std::size_t i) -{ - block[i] = blk(block, i); - z += (w^x^y) + block[i] + 0xca62c1d6 + rol(v, 5); - w = rol(w, 30); -} - -inline -void -make_block(std::uint8_t const* p, - std::uint32_t block[BLOCK_INTS]) -{ - for(std::size_t i = 0; i < BLOCK_INTS; i++) - block[i] = - (static_cast(p[4*i+3])) | - (static_cast(p[4*i+2]))<< 8 | - (static_cast(p[4*i+1]))<<16 | - (static_cast(p[4*i+0]))<<24; -} - -template -void -transform( - std::uint32_t digest[], std::uint32_t block[BLOCK_INTS]) -{ - std::uint32_t a = digest[0]; - std::uint32_t b = digest[1]; - std::uint32_t c = digest[2]; - std::uint32_t d = digest[3]; - std::uint32_t e = digest[4]; - - R0(block, a, b, c, d, e, 0); - R0(block, e, a, b, c, d, 1); - R0(block, d, e, a, b, c, 2); - R0(block, c, d, e, a, b, 3); - R0(block, b, c, d, e, a, 4); - R0(block, a, b, c, d, e, 5); - R0(block, e, a, b, c, d, 6); - R0(block, d, e, a, b, c, 7); - R0(block, c, d, e, a, b, 8); - R0(block, b, c, d, e, a, 9); - R0(block, a, b, c, d, e, 10); - R0(block, e, a, b, c, d, 11); - R0(block, d, e, a, b, c, 12); - R0(block, c, d, e, a, b, 13); - R0(block, b, c, d, e, a, 14); - R0(block, a, b, c, d, e, 15); - R1(block, e, a, b, c, d, 0); - R1(block, d, e, a, b, c, 1); - R1(block, c, d, e, a, b, 2); - R1(block, b, c, d, e, a, 3); - R2(block, a, b, c, d, e, 4); - R2(block, e, a, b, c, d, 5); - R2(block, d, e, a, b, c, 6); - R2(block, c, d, e, a, b, 7); - R2(block, b, c, d, e, a, 8); - R2(block, a, b, c, d, e, 9); - R2(block, e, a, b, c, d, 10); - R2(block, d, e, a, b, c, 11); - R2(block, c, d, e, a, b, 12); - R2(block, b, c, d, e, a, 13); - R2(block, a, b, c, d, e, 14); - R2(block, e, a, b, c, d, 15); - R2(block, d, e, a, b, c, 0); - R2(block, c, d, e, a, b, 1); - R2(block, b, c, d, e, a, 2); - R2(block, a, b, c, d, e, 3); - R2(block, e, a, b, c, d, 4); - R2(block, d, e, a, b, c, 5); - R2(block, c, d, e, a, b, 6); - R2(block, b, c, d, e, a, 7); - R3(block, a, b, c, d, e, 8); - R3(block, e, a, b, c, d, 9); - R3(block, d, e, a, b, c, 10); - R3(block, c, d, e, a, b, 11); - R3(block, b, c, d, e, a, 12); - R3(block, a, b, c, d, e, 13); - R3(block, e, a, b, c, d, 14); - R3(block, d, e, a, b, c, 15); - R3(block, c, d, e, a, b, 0); - R3(block, b, c, d, e, a, 1); - R3(block, a, b, c, d, e, 2); - R3(block, e, a, b, c, d, 3); - R3(block, d, e, a, b, c, 4); - R3(block, c, d, e, a, b, 5); - R3(block, b, c, d, e, a, 6); - R3(block, a, b, c, d, e, 7); - R3(block, e, a, b, c, d, 8); - R3(block, d, e, a, b, c, 9); - R3(block, c, d, e, a, b, 10); - R3(block, b, c, d, e, a, 11); - R4(block, a, b, c, d, e, 12); - R4(block, e, a, b, c, d, 13); - R4(block, d, e, a, b, c, 14); - R4(block, c, d, e, a, b, 15); - R4(block, b, c, d, e, a, 0); - R4(block, a, b, c, d, e, 1); - R4(block, e, a, b, c, d, 2); - R4(block, d, e, a, b, c, 3); - R4(block, c, d, e, a, b, 4); - R4(block, b, c, d, e, a, 5); - R4(block, a, b, c, d, e, 6); - R4(block, e, a, b, c, d, 7); - R4(block, d, e, a, b, c, 8); - R4(block, c, d, e, a, b, 9); - R4(block, b, c, d, e, a, 10); - R4(block, a, b, c, d, e, 11); - R4(block, e, a, b, c, d, 12); - R4(block, d, e, a, b, c, 13); - R4(block, c, d, e, a, b, 14); - R4(block, b, c, d, e, a, 15); - - digest[0] += a; - digest[1] += b; - digest[2] += c; - digest[3] += d; - digest[4] += e; -} - } // sha1 struct sha1_context @@ -230,84 +49,29 @@ struct sha1_context std::uint8_t buf[block_size]; }; -template +BOOST_BEAST_DECL void -init(sha1_context& ctx) noexcept -{ - ctx.buflen = 0; - ctx.blocks = 0; - ctx.digest[0] = 0x67452301; - ctx.digest[1] = 0xefcdab89; - ctx.digest[2] = 0x98badcfe; - ctx.digest[3] = 0x10325476; - ctx.digest[4] = 0xc3d2e1f0; -} +init(sha1_context& ctx) noexcept; -template +BOOST_BEAST_DECL void -update(sha1_context& ctx, - void const* message, std::size_t size) noexcept -{ - auto p = static_cast< - std::uint8_t const*>(message); - for(;;) - { - auto const n = (std::min)( - size, sizeof(ctx.buf) - ctx.buflen); - std::memcpy(ctx.buf + ctx.buflen, p, n); - ctx.buflen += n; - if(ctx.buflen != 64) - return; - p += n; - size -= n; - ctx.buflen = 0; - std::uint32_t block[sha1::BLOCK_INTS]; - sha1::make_block(ctx.buf, block); - sha1::transform(ctx.digest, block); - ++ctx.blocks; - } -} +update( + sha1_context& ctx, + void const* message, + std::size_t size) noexcept; -template +BOOST_BEAST_DECL void -finish(sha1_context& ctx, void* digest) noexcept -{ - using sha1::BLOCK_INTS; - using sha1::BLOCK_BYTES; - - std::uint64_t total_bits = - (ctx.blocks*64 + ctx.buflen) * 8; - // pad - ctx.buf[ctx.buflen++] = 0x80; - auto const buflen = ctx.buflen; - while(ctx.buflen < 64) - ctx.buf[ctx.buflen++] = 0x00; - std::uint32_t block[BLOCK_INTS]; - sha1::make_block(ctx.buf, block); - if(buflen > BLOCK_BYTES - 8) - { - sha1::transform(ctx.digest, block); - for(size_t i = 0; i < BLOCK_INTS - 2; i++) - block[i] = 0; - } - - /* Append total_bits, split this uint64_t into two uint32_t */ - block[BLOCK_INTS - 1] = total_bits & 0xffffffff; - block[BLOCK_INTS - 2] = (total_bits >> 32); - sha1::transform(ctx.digest, block); - for(std::size_t i = 0; i < sha1::DIGEST_BYTES/4; i++) - { - std::uint8_t* d = - static_cast(digest) + 4 * i; - d[3] = ctx.digest[i] & 0xff; - d[2] = (ctx.digest[i] >> 8) & 0xff; - d[1] = (ctx.digest[i] >> 16) & 0xff; - d[0] = (ctx.digest[i] >> 24) & 0xff; - } -} +finish( + sha1_context& ctx, + void* digest) noexcept; } // detail } // beast } // boost +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif + #endif diff --git a/include/boost/beast/core/detail/sha1.ipp b/include/boost/beast/core/detail/sha1.ipp new file mode 100644 index 00000000..8d739f34 --- /dev/null +++ b/include/boost/beast/core/detail/sha1.ipp @@ -0,0 +1,301 @@ +// +// 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_DETAIL_SHA1_IPP +#define BOOST_BEAST_DETAIL_SHA1_IPP + +#include + +#include +#include +#include + +// Based on https://github.com/vog/sha1 +/* + Original authors: + Steve Reid (Original C Code) + Bruce Guenter (Small changes to fit into bglibs) + Volker Grabsch (Translation to simpler C++ Code) + Eugene Hopkinson (Safety improvements) + Vincent Falco (beast adaptation) +*/ + +namespace boost { +namespace beast { +namespace detail { + +namespace sha1 { + +inline +std::uint32_t +rol(std::uint32_t value, std::size_t bits) +{ + return (value << bits) | (value >> (32 - bits)); +} + +inline +std::uint32_t +blk(std::uint32_t block[BLOCK_INTS], std::size_t i) +{ + return rol( + block[(i+13)&15] ^ block[(i+8)&15] ^ + block[(i+2)&15] ^ block[i], 1); +} + +inline +void +R0(std::uint32_t block[BLOCK_INTS], std::uint32_t v, + std::uint32_t &w, std::uint32_t x, std::uint32_t y, + std::uint32_t &z, std::size_t i) +{ + z += ((w&(x^y))^y) + block[i] + 0x5a827999 + rol(v, 5); + w = rol(w, 30); +} + + +inline +void +R1(std::uint32_t block[BLOCK_INTS], std::uint32_t v, + std::uint32_t &w, std::uint32_t x, std::uint32_t y, + std::uint32_t &z, std::size_t i) +{ + block[i] = blk(block, i); + z += ((w&(x^y))^y) + block[i] + 0x5a827999 + rol(v, 5); + w = rol(w, 30); +} + +inline +void +R2(std::uint32_t block[BLOCK_INTS], std::uint32_t v, + std::uint32_t &w, std::uint32_t x, std::uint32_t y, + std::uint32_t &z, std::size_t i) +{ + block[i] = blk(block, i); + z += (w^x^y) + block[i] + 0x6ed9eba1 + rol(v, 5); + w = rol(w, 30); +} + +inline +void +R3(std::uint32_t block[BLOCK_INTS], std::uint32_t v, + std::uint32_t &w, std::uint32_t x, std::uint32_t y, + std::uint32_t &z, std::size_t i) +{ + block[i] = blk(block, i); + z += (((w|x)&y)|(w&x)) + block[i] + 0x8f1bbcdc + rol(v, 5); + w = rol(w, 30); +} + +inline +void +R4(std::uint32_t block[BLOCK_INTS], std::uint32_t v, + std::uint32_t &w, std::uint32_t x, std::uint32_t y, + std::uint32_t &z, std::size_t i) +{ + block[i] = blk(block, i); + z += (w^x^y) + block[i] + 0xca62c1d6 + rol(v, 5); + w = rol(w, 30); +} + +inline +void +make_block(std::uint8_t const* p, + std::uint32_t block[BLOCK_INTS]) +{ + for(std::size_t i = 0; i < BLOCK_INTS; i++) + block[i] = + (static_cast(p[4*i+3])) | + (static_cast(p[4*i+2]))<< 8 | + (static_cast(p[4*i+1]))<<16 | + (static_cast(p[4*i+0]))<<24; +} + +inline +void +transform( + std::uint32_t digest[], std::uint32_t block[BLOCK_INTS]) +{ + std::uint32_t a = digest[0]; + std::uint32_t b = digest[1]; + std::uint32_t c = digest[2]; + std::uint32_t d = digest[3]; + std::uint32_t e = digest[4]; + + R0(block, a, b, c, d, e, 0); + R0(block, e, a, b, c, d, 1); + R0(block, d, e, a, b, c, 2); + R0(block, c, d, e, a, b, 3); + R0(block, b, c, d, e, a, 4); + R0(block, a, b, c, d, e, 5); + R0(block, e, a, b, c, d, 6); + R0(block, d, e, a, b, c, 7); + R0(block, c, d, e, a, b, 8); + R0(block, b, c, d, e, a, 9); + R0(block, a, b, c, d, e, 10); + R0(block, e, a, b, c, d, 11); + R0(block, d, e, a, b, c, 12); + R0(block, c, d, e, a, b, 13); + R0(block, b, c, d, e, a, 14); + R0(block, a, b, c, d, e, 15); + R1(block, e, a, b, c, d, 0); + R1(block, d, e, a, b, c, 1); + R1(block, c, d, e, a, b, 2); + R1(block, b, c, d, e, a, 3); + R2(block, a, b, c, d, e, 4); + R2(block, e, a, b, c, d, 5); + R2(block, d, e, a, b, c, 6); + R2(block, c, d, e, a, b, 7); + R2(block, b, c, d, e, a, 8); + R2(block, a, b, c, d, e, 9); + R2(block, e, a, b, c, d, 10); + R2(block, d, e, a, b, c, 11); + R2(block, c, d, e, a, b, 12); + R2(block, b, c, d, e, a, 13); + R2(block, a, b, c, d, e, 14); + R2(block, e, a, b, c, d, 15); + R2(block, d, e, a, b, c, 0); + R2(block, c, d, e, a, b, 1); + R2(block, b, c, d, e, a, 2); + R2(block, a, b, c, d, e, 3); + R2(block, e, a, b, c, d, 4); + R2(block, d, e, a, b, c, 5); + R2(block, c, d, e, a, b, 6); + R2(block, b, c, d, e, a, 7); + R3(block, a, b, c, d, e, 8); + R3(block, e, a, b, c, d, 9); + R3(block, d, e, a, b, c, 10); + R3(block, c, d, e, a, b, 11); + R3(block, b, c, d, e, a, 12); + R3(block, a, b, c, d, e, 13); + R3(block, e, a, b, c, d, 14); + R3(block, d, e, a, b, c, 15); + R3(block, c, d, e, a, b, 0); + R3(block, b, c, d, e, a, 1); + R3(block, a, b, c, d, e, 2); + R3(block, e, a, b, c, d, 3); + R3(block, d, e, a, b, c, 4); + R3(block, c, d, e, a, b, 5); + R3(block, b, c, d, e, a, 6); + R3(block, a, b, c, d, e, 7); + R3(block, e, a, b, c, d, 8); + R3(block, d, e, a, b, c, 9); + R3(block, c, d, e, a, b, 10); + R3(block, b, c, d, e, a, 11); + R4(block, a, b, c, d, e, 12); + R4(block, e, a, b, c, d, 13); + R4(block, d, e, a, b, c, 14); + R4(block, c, d, e, a, b, 15); + R4(block, b, c, d, e, a, 0); + R4(block, a, b, c, d, e, 1); + R4(block, e, a, b, c, d, 2); + R4(block, d, e, a, b, c, 3); + R4(block, c, d, e, a, b, 4); + R4(block, b, c, d, e, a, 5); + R4(block, a, b, c, d, e, 6); + R4(block, e, a, b, c, d, 7); + R4(block, d, e, a, b, c, 8); + R4(block, c, d, e, a, b, 9); + R4(block, b, c, d, e, a, 10); + R4(block, a, b, c, d, e, 11); + R4(block, e, a, b, c, d, 12); + R4(block, d, e, a, b, c, 13); + R4(block, c, d, e, a, b, 14); + R4(block, b, c, d, e, a, 15); + + digest[0] += a; + digest[1] += b; + digest[2] += c; + digest[3] += d; + digest[4] += e; +} + +} // sha1 + +void +init(sha1_context& ctx) noexcept +{ + ctx.buflen = 0; + ctx.blocks = 0; + ctx.digest[0] = 0x67452301; + ctx.digest[1] = 0xefcdab89; + ctx.digest[2] = 0x98badcfe; + ctx.digest[3] = 0x10325476; + ctx.digest[4] = 0xc3d2e1f0; +} + +void +update( + sha1_context& ctx, + void const* message, + std::size_t size) noexcept +{ + auto p = static_cast< + std::uint8_t const*>(message); + for(;;) + { + auto const n = (std::min)( + size, sizeof(ctx.buf) - ctx.buflen); + std::memcpy(ctx.buf + ctx.buflen, p, n); + ctx.buflen += n; + if(ctx.buflen != 64) + return; + p += n; + size -= n; + ctx.buflen = 0; + std::uint32_t block[sha1::BLOCK_INTS]; + sha1::make_block(ctx.buf, block); + sha1::transform(ctx.digest, block); + ++ctx.blocks; + } +} + +void +finish( + sha1_context& ctx, + void* digest) noexcept +{ + using sha1::BLOCK_INTS; + using sha1::BLOCK_BYTES; + + std::uint64_t total_bits = + (ctx.blocks*64 + ctx.buflen) * 8; + // pad + ctx.buf[ctx.buflen++] = 0x80; + auto const buflen = ctx.buflen; + while(ctx.buflen < 64) + ctx.buf[ctx.buflen++] = 0x00; + std::uint32_t block[BLOCK_INTS]; + sha1::make_block(ctx.buf, block); + if(buflen > BLOCK_BYTES - 8) + { + sha1::transform(ctx.digest, block); + for(size_t i = 0; i < BLOCK_INTS - 2; i++) + block[i] = 0; + } + + /* Append total_bits, split this uint64_t into two uint32_t */ + block[BLOCK_INTS - 1] = total_bits & 0xffffffff; + block[BLOCK_INTS - 2] = (total_bits >> 32); + sha1::transform(ctx.digest, block); + for(std::size_t i = 0; i < sha1::DIGEST_BYTES/4; i++) + { + std::uint8_t* d = + static_cast(digest) + 4 * i; + d[3] = ctx.digest[i] & 0xff; + d[2] = (ctx.digest[i] >> 8) & 0xff; + d[1] = (ctx.digest[i] >> 16) & 0xff; + d[0] = (ctx.digest[i] >> 24) & 0xff; + } +} + +} // detail +} // beast +} // boost + +#endif diff --git a/include/boost/beast/core/file_posix.hpp b/include/boost/beast/core/file_posix.hpp index ef2bf25b..86e53523 100644 --- a/include/boost/beast/core/file_posix.hpp +++ b/include/boost/beast/core/file_posix.hpp @@ -184,7 +184,9 @@ public: } // beast } // boost -#include +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif #endif diff --git a/include/boost/beast/core/file_stdio.hpp b/include/boost/beast/core/file_stdio.hpp index 836905b7..860c1e5e 100644 --- a/include/boost/beast/core/file_stdio.hpp +++ b/include/boost/beast/core/file_stdio.hpp @@ -164,6 +164,8 @@ public: } // beast } // boost -#include +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif #endif diff --git a/include/boost/beast/core/file_win32.hpp b/include/boost/beast/core/file_win32.hpp index 5a5c6ab0..4cdc312e 100644 --- a/include/boost/beast/core/file_win32.hpp +++ b/include/boost/beast/core/file_win32.hpp @@ -181,7 +181,9 @@ public: } // beast } // boost -#include +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif #endif diff --git a/include/boost/beast/core/flat_static_buffer.hpp b/include/boost/beast/core/flat_static_buffer.hpp index ba7f636e..f83b7e31 100644 --- a/include/boost/beast/core/flat_static_buffer.hpp +++ b/include/boost/beast/core/flat_static_buffer.hpp @@ -329,6 +329,8 @@ public: } // beast } // boost -#include +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif #endif \ No newline at end of file diff --git a/include/boost/beast/core/impl/error.hpp b/include/boost/beast/core/impl/error.hpp index a2244925..ec677f0e 100644 --- a/include/boost/beast/core/impl/error.hpp +++ b/include/boost/beast/core/impl/error.hpp @@ -15,53 +15,20 @@ namespace boost { namespace system { template<> -struct is_error_code_enum +struct is_error_code_enum<::boost::beast::error> { static bool const value = true; }; - template<> -struct is_error_condition_enum +struct is_error_condition_enum<::boost::beast::condition> { static bool const value = true; }; - } // system } // boost namespace boost { namespace beast { -namespace detail { - -class error_codes : public error_category -{ -public: - BOOST_BEAST_DECL - const char* - name() const noexcept override; - - BOOST_BEAST_DECL - std::string - message(int ev) const override; - - BOOST_BEAST_DECL - error_condition - default_error_condition(int ev) const noexcept override; -}; - -class error_conditions : public error_category -{ -public: - BOOST_BEAST_DECL - const char* - name() const noexcept override; - - BOOST_BEAST_DECL - std::string - message(int cv) const override; -}; - -} // detail BOOST_BEAST_DECL error_code @@ -74,6 +41,8 @@ make_error_condition(condition c); } // beast } // boost +#ifdef BOOST_BEAST_HEADER_ONLY #include +#endif #endif diff --git a/include/boost/beast/core/impl/error.ipp b/include/boost/beast/core/impl/error.ipp index 57eeba2e..0b301577 100644 --- a/include/boost/beast/core/impl/error.ipp +++ b/include/boost/beast/core/impl/error.ipp @@ -10,63 +10,70 @@ #ifndef BOOST_BEAST_IMPL_ERROR_IPP #define BOOST_BEAST_IMPL_ERROR_IPP +#include + namespace boost { namespace beast { namespace detail { -const char* -error_codes:: -name() const noexcept +class error_codes : public error_category { - return "boost.beast"; -} - -std::string -error_codes:: -message(int ev) const -{ - switch(static_cast(ev)) +public: + const char* + name() const noexcept override { - default: - case error::timeout: return - "The socket was closed due to a timeout"; + return "boost.beast"; } -} -error_condition -error_codes:: -default_error_condition(int ev) const noexcept -{ - switch(static_cast(ev)) + BOOST_BEAST_DECL + std::string + message(int ev) const override { - default: -// return {ev, *this}; - case error::timeout: - return condition::timeout; + switch(static_cast(ev)) + { + default: + case error::timeout: return + "The socket was closed due to a timeout"; + } } -} -//------------------------------------------------------------------------------ - -const char* -error_conditions:: -name() const noexcept -{ - return "boost.beast"; -} - -std::string -error_conditions:: -message(int cv) const -{ - switch(static_cast(cv)) + BOOST_BEAST_DECL + error_condition + default_error_condition(int ev) const noexcept override { - default: - case condition::timeout: - return "The operation timed out"; + switch(static_cast(ev)) + { + default: + // return {ev, *this}; + case error::timeout: + return condition::timeout; + } } -} +}; + +class error_conditions : public error_category +{ +public: + BOOST_BEAST_DECL + const char* + name() const noexcept override + { + return "boost.beast"; + } + + BOOST_BEAST_DECL + std::string + message(int cv) const override + { + switch(static_cast(cv)) + { + default: + case condition::timeout: + return "The operation timed out"; + } + } +}; } // detail diff --git a/include/boost/beast/core/impl/file_posix.hpp b/include/boost/beast/core/impl/file_posix.ipp similarity index 97% rename from include/boost/beast/core/impl/file_posix.hpp rename to include/boost/beast/core/impl/file_posix.ipp index 5113fbab..6d22503d 100644 --- a/include/boost/beast/core/impl/file_posix.hpp +++ b/include/boost/beast/core/impl/file_posix.ipp @@ -7,8 +7,21 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_CORE_IMPL_FILE_POSIX_HPP -#define BOOST_BEAST_CORE_IMPL_FILE_POSIX_HPP +#ifndef BOOST_BEAST_CORE_IMPL_FILE_POSIX_IPP +#define BOOST_BEAST_CORE_IMPL_FILE_POSIX_IPP + +#include + +#if BOOST_BEAST_USE_POSIX_FILE + +#include +#include +#include +#include +#include +#include +#include +#include #if ! defined(BOOST_BEAST_NO_POSIX_FADVISE) # if defined(__APPLE__) || (defined(ANDROID) && (__ANDROID_API__ < 21)) @@ -24,15 +37,6 @@ # endif #endif -#include -#include -#include -#include -#include -#include -#include -#include - namespace boost { namespace beast { @@ -323,3 +327,5 @@ write(void const* buffer, std::size_t n, error_code& ec) } // boost #endif + +#endif diff --git a/include/boost/beast/core/impl/file_stdio.hpp b/include/boost/beast/core/impl/file_stdio.ipp similarity index 97% rename from include/boost/beast/core/impl/file_stdio.hpp rename to include/boost/beast/core/impl/file_stdio.ipp index bd7f3f7a..46897f9c 100644 --- a/include/boost/beast/core/impl/file_stdio.hpp +++ b/include/boost/beast/core/impl/file_stdio.ipp @@ -7,9 +7,10 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_CORE_IMPL_FILE_STDIO_HPP -#define BOOST_BEAST_CORE_IMPL_FILE_STDIO_HPP +#ifndef BOOST_BEAST_CORE_IMPL_FILE_STDIO_IPP +#define BOOST_BEAST_CORE_IMPL_FILE_STDIO_IPP +#include #include #include #include diff --git a/include/boost/beast/core/impl/file_win32.hpp b/include/boost/beast/core/impl/file_win32.ipp similarity index 98% rename from include/boost/beast/core/impl/file_win32.hpp rename to include/boost/beast/core/impl/file_win32.ipp index 2d9631df..cd0939c1 100644 --- a/include/boost/beast/core/impl/file_win32.hpp +++ b/include/boost/beast/core/impl/file_win32.ipp @@ -7,8 +7,12 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_CORE_IMPL_FILE_WIN32_HPP -#define BOOST_BEAST_CORE_IMPL_FILE_WIN32_HPP +#ifndef BOOST_BEAST_CORE_IMPL_FILE_WIN32_IPP +#define BOOST_BEAST_CORE_IMPL_FILE_WIN32_IPP + +#include + +#if BOOST_BEAST_USE_WIN32_FILE #include #include @@ -25,7 +29,7 @@ namespace detail { // VFALCO Can't seem to get boost/detail/winapi to work with // this so use the non-Ex version for now. -inline +BOOST_BEAST_DECL boost::winapi::BOOL_ set_file_pointer_ex( boost::winapi::HANDLE_ hFile, @@ -345,3 +349,5 @@ write(void const* buffer, std::size_t n, error_code& ec) } // boost #endif + +#endif diff --git a/include/boost/beast/core/impl/flat_static_buffer.hpp b/include/boost/beast/core/impl/flat_static_buffer.ipp similarity index 92% rename from include/boost/beast/core/impl/flat_static_buffer.hpp rename to include/boost/beast/core/impl/flat_static_buffer.ipp index 08f8d442..694b2fb8 100644 --- a/include/boost/beast/core/impl/flat_static_buffer.hpp +++ b/include/boost/beast/core/impl/flat_static_buffer.ipp @@ -7,15 +7,15 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_IMPL_FLAT_STATIC_BUFFER_HPP -#define BOOST_BEAST_IMPL_FLAT_STATIC_BUFFER_HPP +#ifndef BOOST_BEAST_IMPL_FLAT_STATIC_BUFFER_IPP +#define BOOST_BEAST_IMPL_FLAT_STATIC_BUFFER_IPP -#include -#include +#include #include #include #include #include +#include #include namespace boost { diff --git a/include/boost/beast/core/impl/static_buffer.hpp b/include/boost/beast/core/impl/static_buffer.hpp index aa147018..dee13bfb 100644 --- a/include/boost/beast/core/impl/static_buffer.hpp +++ b/include/boost/beast/core/impl/static_buffer.hpp @@ -21,113 +21,6 @@ namespace boost { namespace beast { -static_buffer_base:: -static_buffer_base( - void* p, std::size_t size) noexcept - : begin_(static_cast(p)) - , capacity_(size) -{ -} - -void -static_buffer_base:: -clear() noexcept -{ - in_off_ = 0; - in_size_ = 0; - out_size_ = 0; -} - -auto -static_buffer_base:: -data() const noexcept -> - const_buffers_type -{ - if(in_off_ + in_size_ <= capacity_) - return { - net::const_buffer{ - begin_ + in_off_, in_size_}, - net::const_buffer{ - begin_, 0}}; - return { - net::const_buffer{ - begin_ + in_off_, capacity_ - in_off_}, - net::const_buffer{ - begin_, in_size_ - (capacity_ - in_off_)}}; -} - -auto -static_buffer_base:: -data() noexcept -> - mutable_data_type -{ - if(in_off_ + in_size_ <= capacity_) - return { - net::mutable_buffer{ - begin_ + in_off_, in_size_}, - net::mutable_buffer{ - begin_, 0}}; - return { - net::mutable_buffer{ - begin_ + in_off_, capacity_ - in_off_}, - net::mutable_buffer{ - begin_, in_size_ - (capacity_ - in_off_)}}; -} - -auto -static_buffer_base:: -prepare(std::size_t n) -> - mutable_buffers_type -{ - using net::mutable_buffer; - if(n > capacity_ - in_size_) - BOOST_THROW_EXCEPTION(std::length_error{ - "static_buffer overflow"}); - out_size_ = n; - auto const out_off = - (in_off_ + in_size_) % capacity_; - if(out_off + out_size_ <= capacity_ ) - return { - net::mutable_buffer{ - begin_ + out_off, out_size_}, - net::mutable_buffer{ - begin_, 0}}; - return { - net::mutable_buffer{ - begin_ + out_off, capacity_ - out_off}, - net::mutable_buffer{ - begin_, out_size_ - (capacity_ - out_off)}}; -} - -void -static_buffer_base:: -commit(std::size_t n) noexcept -{ - in_size_ += (std::min)(n, out_size_); - out_size_ = 0; -} - -void -static_buffer_base:: -consume(std::size_t n) noexcept -{ - if(n < in_size_) - { - in_off_ = (in_off_ + n) % capacity_; - in_size_ -= n; - } - else - { - // rewind the offset, so the next call to prepare - // can have a longer contiguous segment. this helps - // algorithms optimized for larger buffers. - in_off_ = 0; - in_size_ = 0; - } -} - -//------------------------------------------------------------------------------ - template static_buffer:: static_buffer(static_buffer const& other) noexcept diff --git a/include/boost/beast/core/impl/static_buffer.ipp b/include/boost/beast/core/impl/static_buffer.ipp new file mode 100644 index 00000000..c26ff429 --- /dev/null +++ b/include/boost/beast/core/impl/static_buffer.ipp @@ -0,0 +1,133 @@ +// +// 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_IMPL_STATIC_BUFFER_IPP +#define BOOST_BEAST_IMPL_STATIC_BUFFER_IPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { + +static_buffer_base:: +static_buffer_base( + void* p, std::size_t size) noexcept + : begin_(static_cast(p)) + , capacity_(size) +{ +} + +void +static_buffer_base:: +clear() noexcept +{ + in_off_ = 0; + in_size_ = 0; + out_size_ = 0; +} + +auto +static_buffer_base:: +data() const noexcept -> + const_buffers_type +{ + if(in_off_ + in_size_ <= capacity_) + return { + net::const_buffer{ + begin_ + in_off_, in_size_}, + net::const_buffer{ + begin_, 0}}; + return { + net::const_buffer{ + begin_ + in_off_, capacity_ - in_off_}, + net::const_buffer{ + begin_, in_size_ - (capacity_ - in_off_)}}; +} + +auto +static_buffer_base:: +data() noexcept -> + mutable_data_type +{ + if(in_off_ + in_size_ <= capacity_) + return { + net::mutable_buffer{ + begin_ + in_off_, in_size_}, + net::mutable_buffer{ + begin_, 0}}; + return { + net::mutable_buffer{ + begin_ + in_off_, capacity_ - in_off_}, + net::mutable_buffer{ + begin_, in_size_ - (capacity_ - in_off_)}}; +} + +auto +static_buffer_base:: +prepare(std::size_t n) -> + mutable_buffers_type +{ + using net::mutable_buffer; + if(n > capacity_ - in_size_) + BOOST_THROW_EXCEPTION(std::length_error{ + "static_buffer overflow"}); + out_size_ = n; + auto const out_off = + (in_off_ + in_size_) % capacity_; + if(out_off + out_size_ <= capacity_ ) + return { + net::mutable_buffer{ + begin_ + out_off, out_size_}, + net::mutable_buffer{ + begin_, 0}}; + return { + net::mutable_buffer{ + begin_ + out_off, capacity_ - out_off}, + net::mutable_buffer{ + begin_, out_size_ - (capacity_ - out_off)}}; +} + +void +static_buffer_base:: +commit(std::size_t n) noexcept +{ + in_size_ += (std::min)(n, out_size_); + out_size_ = 0; +} + +void +static_buffer_base:: +consume(std::size_t n) noexcept +{ + if(n < in_size_) + { + in_off_ = (in_off_ + n) % capacity_; + in_size_ -= n; + } + else + { + // rewind the offset, so the next call to prepare + // can have a longer contiguous segment. this helps + // algorithms optimized for larger buffers. + in_off_ = 0; + in_size_ = 0; + } +} + +} // beast +} // boost + +#endif diff --git a/include/boost/beast/core/rate_policy.hpp b/include/boost/beast/core/rate_policy.hpp index b0a3e19e..47c4a120 100644 --- a/include/boost/beast/core/rate_policy.hpp +++ b/include/boost/beast/core/rate_policy.hpp @@ -173,7 +173,7 @@ class simple_rate_policy } void - transfer_read_bytes(std::size_t n) + transfer_read_bytes(std::size_t n) noexcept { if( rd_remain_ != all) rd_remain_ = @@ -181,7 +181,7 @@ class simple_rate_policy } void - transfer_write_bytes(std::size_t n) + transfer_write_bytes(std::size_t n) noexcept { if( wr_remain_ != all) wr_remain_ = @@ -189,7 +189,7 @@ class simple_rate_policy } void - on_timer() + on_timer() noexcept { rd_remain_ = rd_limit_; wr_remain_ = wr_limit_; @@ -198,7 +198,7 @@ class simple_rate_policy public: /// Set the limit of bytes per second to read void - read_limit(std::size_t bytes_per_second) + read_limit(std::size_t bytes_per_second) noexcept { rd_limit_ = bytes_per_second; if( rd_remain_ > bytes_per_second) @@ -207,7 +207,7 @@ public: /// Set the limit of bytes per second to write void - write_limit(std::size_t bytes_per_second) + write_limit(std::size_t bytes_per_second) noexcept { wr_limit_ = bytes_per_second; if( wr_remain_ > bytes_per_second) diff --git a/include/boost/beast/core/static_buffer.hpp b/include/boost/beast/core/static_buffer.hpp index b1aee8aa..d36fd868 100644 --- a/include/boost/beast/core/static_buffer.hpp +++ b/include/boost/beast/core/static_buffer.hpp @@ -294,5 +294,8 @@ public: } // boost #include +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif #endif diff --git a/include/boost/beast/http/basic_parser.hpp b/include/boost/beast/http/basic_parser.hpp index 92508b24..edd01371 100644 --- a/include/boost/beast/http/basic_parser.hpp +++ b/include/boost/beast/http/basic_parser.hpp @@ -616,6 +616,6 @@ private: } // beast } // boost -#include +#include #endif diff --git a/include/boost/beast/http/chunk_encode.hpp b/include/boost/beast/http/chunk_encode.hpp index 6399fedc..8c1b804a 100644 --- a/include/boost/beast/http/chunk_encode.hpp +++ b/include/boost/beast/http/chunk_encode.hpp @@ -732,6 +732,6 @@ make_chunk_last( } // beast } // boost -#include +#include #endif diff --git a/include/boost/beast/http/error.hpp b/include/boost/beast/http/error.hpp index 8cca7853..7a003bc5 100644 --- a/include/boost/beast/http/error.hpp +++ b/include/boost/beast/http/error.hpp @@ -151,6 +151,6 @@ enum class error } // beast } // boost -#include +#include #endif diff --git a/include/boost/beast/http/field.hpp b/include/boost/beast/http/field.hpp index 44c43855..69eb253f 100644 --- a/include/boost/beast/http/field.hpp +++ b/include/boost/beast/http/field.hpp @@ -380,6 +380,7 @@ enum class field : unsigned short @param f The field to convert */ +BOOST_BEAST_DECL string_view to_string(field f); @@ -390,6 +391,7 @@ to_string(field f); @return The corresponding field, or @ref field::unknown if no known field matches. */ +BOOST_BEAST_DECL field string_to_field(string_view s); @@ -405,6 +407,8 @@ operator<<(std::ostream& os, field f) } // beast } // boost +#ifdef BOOST_BEAST_HEADER_ONLY #include +#endif #endif diff --git a/include/boost/beast/http/fields.hpp b/include/boost/beast/http/fields.hpp index 00772729..f0f980b8 100644 --- a/include/boost/beast/http/fields.hpp +++ b/include/boost/beast/http/fields.hpp @@ -768,6 +768,6 @@ using fields = basic_fields>; } // beast } // boost -#include +#include #endif diff --git a/include/boost/beast/http/file_body.hpp b/include/boost/beast/http/file_body.hpp index cf1d6f23..a78d7901 100644 --- a/include/boost/beast/http/file_body.hpp +++ b/include/boost/beast/http/file_body.hpp @@ -31,7 +31,7 @@ using file_body = basic_file_body; } // boost #ifndef BOOST_BEAST_NO_FILE_BODY_WIN32 -#include +#include #endif #endif diff --git a/include/boost/beast/http/impl/basic_parser.ipp b/include/boost/beast/http/impl/basic_parser.hpp similarity index 99% rename from include/boost/beast/http/impl/basic_parser.ipp rename to include/boost/beast/http/impl/basic_parser.hpp index c915be24..5192113f 100644 --- a/include/boost/beast/http/impl/basic_parser.ipp +++ b/include/boost/beast/http/impl/basic_parser.hpp @@ -7,8 +7,8 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_IPP -#define BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_IPP +#ifndef BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_HPP +#define BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_HPP #include #include diff --git a/include/boost/beast/http/impl/chunk_encode.ipp b/include/boost/beast/http/impl/chunk_encode.hpp similarity index 99% rename from include/boost/beast/http/impl/chunk_encode.ipp rename to include/boost/beast/http/impl/chunk_encode.hpp index 34b4b076..032b3121 100644 --- a/include/boost/beast/http/impl/chunk_encode.ipp +++ b/include/boost/beast/http/impl/chunk_encode.hpp @@ -7,8 +7,8 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_HTTP_IMPL_CHUNK_ENCODE_IPP -#define BOOST_BEAST_HTTP_IMPL_CHUNK_ENCODE_IPP +#ifndef BOOST_BEAST_HTTP_IMPL_CHUNK_ENCODE_HPP +#define BOOST_BEAST_HTTP_IMPL_CHUNK_ENCODE_HPP #include #include @@ -681,7 +681,6 @@ insert(string_view name, string_view value) } template -inline auto basic_chunk_extensions:: begin() const -> @@ -691,7 +690,6 @@ begin() const -> } template -inline auto basic_chunk_extensions:: end() const -> diff --git a/include/boost/beast/http/impl/error.hpp b/include/boost/beast/http/impl/error.hpp new file mode 100644 index 00000000..0ffef3fd --- /dev/null +++ b/include/boost/beast/http/impl/error.hpp @@ -0,0 +1,41 @@ +// +// 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_HTTP_IMPL_ERROR_HPP +#define BOOST_BEAST_HTTP_IMPL_ERROR_HPP + +#include + +namespace boost { +namespace system { +template<> +struct is_error_code_enum<::boost::beast::http::error> +{ + static bool const value = true; +}; +} // system +} // boost + +namespace boost { +namespace beast { +namespace http { + +BOOST_BEAST_DECL +error_code +make_error_code(error ev); + +} // http +} // beast +} // boost + +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif + +#endif diff --git a/include/boost/beast/http/impl/error.ipp b/include/boost/beast/http/impl/error.ipp index 71e0f1a8..bf15c690 100644 --- a/include/boost/beast/http/impl/error.ipp +++ b/include/boost/beast/http/impl/error.ipp @@ -10,18 +10,10 @@ #ifndef BOOST_BEAST_HTTP_IMPL_ERROR_IPP #define BOOST_BEAST_HTTP_IMPL_ERROR_IPP +#include #include namespace boost { - -namespace system { -template<> -struct is_error_code_enum -{ - static bool const value = true; -}; -} // system - namespace beast { namespace http { namespace detail { @@ -96,7 +88,6 @@ public: } // detail -inline error_code make_error_code(error ev) { diff --git a/include/boost/beast/http/impl/field.ipp b/include/boost/beast/http/impl/field.ipp index fc355b0d..46af6f85 100644 --- a/include/boost/beast/http/impl/field.ipp +++ b/include/boost/beast/http/impl/field.ipp @@ -10,11 +10,9 @@ #ifndef BOOST_BEAST_HTTP_IMPL_FIELD_IPP #define BOOST_BEAST_HTTP_IMPL_FIELD_IPP -#include +#include #include #include -#include -#include #include #include @@ -516,7 +514,7 @@ struct field_table } }; -inline +BOOST_BEAST_DECL field_table const& get_field_table() { @@ -524,7 +522,7 @@ get_field_table() return tab; } -template +BOOST_BEAST_DECL string_view to_string(field f) { @@ -535,14 +533,12 @@ to_string(field f) } // detail -inline string_view to_string(field f) { return detail::to_string(f); } -inline field string_to_field(string_view s) { diff --git a/include/boost/beast/http/impl/fields.ipp b/include/boost/beast/http/impl/fields.hpp similarity index 99% rename from include/boost/beast/http/impl/fields.ipp rename to include/boost/beast/http/impl/fields.hpp index c648d5ac..784e5115 100644 --- a/include/boost/beast/http/impl/fields.ipp +++ b/include/boost/beast/http/impl/fields.hpp @@ -7,8 +7,8 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_HTTP_IMPL_FIELDS_IPP -#define BOOST_BEAST_HTTP_IMPL_FIELDS_IPP +#ifndef BOOST_BEAST_HTTP_IMPL_FIELDS_HPP +#define BOOST_BEAST_HTTP_IMPL_FIELDS_HPP #include #include diff --git a/include/boost/beast/http/impl/file_body_win32.ipp b/include/boost/beast/http/impl/file_body_win32.hpp similarity index 99% rename from include/boost/beast/http/impl/file_body_win32.ipp rename to include/boost/beast/http/impl/file_body_win32.hpp index c428c370..6bb797bb 100644 --- a/include/boost/beast/http/impl/file_body_win32.ipp +++ b/include/boost/beast/http/impl/file_body_win32.hpp @@ -7,8 +7,8 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_HTTP_IMPL_FILE_BODY_WIN32_IPP -#define BOOST_BEAST_HTTP_IMPL_FILE_BODY_WIN32_IPP +#ifndef BOOST_BEAST_HTTP_IMPL_FILE_BODY_WIN32_HPP +#define BOOST_BEAST_HTTP_IMPL_FILE_BODY_WIN32_HPP #if BOOST_BEAST_USE_WIN32_FILE diff --git a/include/boost/beast/http/impl/message.ipp b/include/boost/beast/http/impl/message.hpp similarity index 98% rename from include/boost/beast/http/impl/message.ipp rename to include/boost/beast/http/impl/message.hpp index 1d4a84d8..805d01f0 100644 --- a/include/boost/beast/http/impl/message.ipp +++ b/include/boost/beast/http/impl/message.hpp @@ -7,8 +7,8 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_HTTP_IMPL_MESSAGE_IPP -#define BOOST_BEAST_HTTP_IMPL_MESSAGE_IPP +#ifndef BOOST_BEAST_HTTP_IMPL_MESSAGE_HPP +#define BOOST_BEAST_HTTP_IMPL_MESSAGE_HPP #include #include @@ -30,7 +30,6 @@ header(Arg1&& arg1, ArgN&&... argn) } template -inline verb header:: method() const @@ -73,7 +72,6 @@ method_string(string_view s) } template -inline string_view header:: target() const @@ -82,7 +80,6 @@ target() const } template -inline void header:: target(string_view s) @@ -116,7 +113,6 @@ header(Arg1&& arg1, ArgN&&... argn) } template -inline status header:: result() const @@ -126,7 +122,6 @@ result() const } template -inline void header:: result(status v) @@ -135,7 +130,6 @@ result(status v) } template -inline void header:: result(unsigned v) @@ -148,7 +142,6 @@ result(unsigned v) } template -inline unsigned header:: result_int() const @@ -168,7 +161,6 @@ reason() const } template -inline void header:: reason(string_view s) diff --git a/include/boost/beast/http/impl/parser.ipp b/include/boost/beast/http/impl/parser.hpp similarity index 94% rename from include/boost/beast/http/impl/parser.ipp rename to include/boost/beast/http/impl/parser.hpp index 45d7a5c6..18996cce 100644 --- a/include/boost/beast/http/impl/parser.ipp +++ b/include/boost/beast/http/impl/parser.hpp @@ -7,8 +7,8 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_HTTP_IMPL_PARSER_IPP -#define BOOST_BEAST_HTTP_IMPL_PARSER_IPP +#ifndef BOOST_BEAST_HTTP_IMPL_PARSER_HPP +#define BOOST_BEAST_HTTP_IMPL_PARSER_HPP #include #include diff --git a/include/boost/beast/http/impl/read.ipp b/include/boost/beast/http/impl/read.hpp similarity index 99% rename from include/boost/beast/http/impl/read.ipp rename to include/boost/beast/http/impl/read.hpp index 7831ca90..eeff7977 100644 --- a/include/boost/beast/http/impl/read.ipp +++ b/include/boost/beast/http/impl/read.hpp @@ -7,8 +7,8 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_HTTP_IMPL_READ_IPP -#define BOOST_BEAST_HTTP_IMPL_READ_IPP +#ifndef BOOST_BEAST_HTTP_IMPL_READ_HPP +#define BOOST_BEAST_HTTP_IMPL_READ_HPP #include #include diff --git a/include/boost/beast/http/impl/rfc7230.ipp b/include/boost/beast/http/impl/rfc7230.hpp similarity index 99% rename from include/boost/beast/http/impl/rfc7230.ipp rename to include/boost/beast/http/impl/rfc7230.hpp index 7b2ffd3e..0556eeae 100644 --- a/include/boost/beast/http/impl/rfc7230.ipp +++ b/include/boost/beast/http/impl/rfc7230.hpp @@ -7,8 +7,8 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_HTTP_IMPL_RFC7230_IPP -#define BOOST_BEAST_HTTP_IMPL_RFC7230_IPP +#ifndef BOOST_BEAST_HTTP_IMPL_RFC7230_HPP +#define BOOST_BEAST_HTTP_IMPL_RFC7230_HPP #include #include diff --git a/include/boost/beast/http/impl/serializer.ipp b/include/boost/beast/http/impl/serializer.hpp similarity index 99% rename from include/boost/beast/http/impl/serializer.ipp rename to include/boost/beast/http/impl/serializer.hpp index 4c5df2bd..e059c027 100644 --- a/include/boost/beast/http/impl/serializer.ipp +++ b/include/boost/beast/http/impl/serializer.hpp @@ -7,8 +7,8 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_HTTP_IMPL_SERIALIZER_IPP -#define BOOST_BEAST_HTTP_IMPL_SERIALIZER_IPP +#ifndef BOOST_BEAST_HTTP_IMPL_SERIALIZER_HPP +#define BOOST_BEAST_HTTP_IMPL_SERIALIZER_HPP #include #include diff --git a/include/boost/beast/http/impl/status.ipp b/include/boost/beast/http/impl/status.ipp index 3438aaa3..2a61acd5 100644 --- a/include/boost/beast/http/impl/status.ipp +++ b/include/boost/beast/http/impl/status.ipp @@ -10,15 +10,13 @@ #ifndef BOOST_BEAST_HTTP_IMPL_STATUS_IPP #define BOOST_BEAST_HTTP_IMPL_STATUS_IPP -#include +#include #include namespace boost { namespace beast { namespace http { -namespace detail { -template status int_to_status(unsigned v) { @@ -107,9 +105,30 @@ int_to_status(unsigned v) return status::unknown; } -template +status_class +to_status_class(unsigned v) +{ + switch(v / 100) + { + case 1: return status_class::informational; + case 2: return status_class::successful; + case 3: return status_class::redirection; + case 4: return status_class::client_error; + case 5: return status_class::server_error; + default: + break; + } + return status_class::unknown; +} + +status_class +to_status_class(status v) +{ + return to_status_class(static_cast(v)); +} + string_view -status_to_string(unsigned v) +obsolete_reason(status v) { switch(static_cast(v)) { @@ -190,55 +209,6 @@ status_to_string(unsigned v) return ""; } -template -status_class -to_status_class(unsigned v) -{ - switch(v / 100) - { - case 1: return status_class::informational; - case 2: return status_class::successful; - case 3: return status_class::redirection; - case 4: return status_class::client_error; - case 5: return status_class::server_error; - default: - break; - } - return status_class::unknown; -} - -} // detail - -inline -status -int_to_status(unsigned v) -{ - return detail::int_to_status(v); -} - -inline -status_class -to_status_class(unsigned v) -{ - return detail::to_status_class(v); -} - -inline -status_class -to_status_class(status v) -{ - return to_status_class(static_cast(v)); -} - -inline -string_view -obsolete_reason(status v) -{ - return detail::status_to_string( - static_cast(v)); -} - -inline std::ostream& operator<<(std::ostream& os, status v) { diff --git a/include/boost/beast/http/impl/verb.ipp b/include/boost/beast/http/impl/verb.ipp index 34f534cc..20ef47c5 100644 --- a/include/boost/beast/http/impl/verb.ipp +++ b/include/boost/beast/http/impl/verb.ipp @@ -10,7 +10,7 @@ #ifndef BOOST_BEAST_HTTP_IMPL_VERB_IPP #define BOOST_BEAST_HTTP_IMPL_VERB_IPP -#include +#include #include #include @@ -18,12 +18,8 @@ namespace boost { namespace beast { namespace http { -namespace detail { - -template -inline string_view -verb_to_string(verb v) +to_string(verb v) { switch(v) { @@ -74,7 +70,6 @@ verb_to_string(verb v) BOOST_THROW_EXCEPTION(std::invalid_argument{"unknown verb"}); } -template verb string_to_verb(string_view v) { @@ -314,22 +309,6 @@ string_to_verb(string_view v) return verb::unknown; } -} // detail - -inline -string_view -to_string(verb v) -{ - return detail::verb_to_string(v); -} - -inline -verb -string_to_verb(string_view s) -{ - return detail::string_to_verb(s); -} - } // http } // beast } // boost diff --git a/include/boost/beast/http/impl/write.ipp b/include/boost/beast/http/impl/write.hpp similarity index 99% rename from include/boost/beast/http/impl/write.ipp rename to include/boost/beast/http/impl/write.hpp index 58913972..10a9dd8c 100644 --- a/include/boost/beast/http/impl/write.ipp +++ b/include/boost/beast/http/impl/write.hpp @@ -7,8 +7,8 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_HTTP_IMPL_WRITE_IPP -#define BOOST_BEAST_HTTP_IMPL_WRITE_IPP +#ifndef BOOST_BEAST_HTTP_IMPL_WRITE_HPP +#define BOOST_BEAST_HTTP_IMPL_WRITE_HPP #include #include diff --git a/include/boost/beast/http/message.hpp b/include/boost/beast/http/message.hpp index 7f30e7f4..d4dd79ea 100644 --- a/include/boost/beast/http/message.hpp +++ b/include/boost/beast/http/message.hpp @@ -1001,6 +1001,6 @@ swap( } // beast } // boost -#include +#include #endif diff --git a/include/boost/beast/http/parser.hpp b/include/boost/beast/http/parser.hpp index fc144253..30bb1800 100644 --- a/include/boost/beast/http/parser.hpp +++ b/include/boost/beast/http/parser.hpp @@ -460,6 +460,6 @@ using response_parser = parser; } // beast } // boost -#include +#include #endif diff --git a/include/boost/beast/http/read.hpp b/include/boost/beast/http/read.hpp index f7ea51f6..76554b82 100644 --- a/include/boost/beast/http/read.hpp +++ b/include/boost/beast/http/read.hpp @@ -812,6 +812,6 @@ async_read( } // beast } // boost -#include +#include #endif diff --git a/include/boost/beast/http/rfc7230.hpp b/include/boost/beast/http/rfc7230.hpp index 62f720eb..bc2498db 100644 --- a/include/boost/beast/http/rfc7230.hpp +++ b/include/boost/beast/http/rfc7230.hpp @@ -324,6 +324,6 @@ validate_list(detail::basic_parsed_list< } // beast } // boost -#include +#include #endif diff --git a/include/boost/beast/http/serializer.hpp b/include/boost/beast/http/serializer.hpp index f8429f2a..d7982e4d 100644 --- a/include/boost/beast/http/serializer.hpp +++ b/include/boost/beast/http/serializer.hpp @@ -365,6 +365,6 @@ using response_serializer = serializer; } // beast } // boost -#include +#include #endif diff --git a/include/boost/beast/http/status.hpp b/include/boost/beast/http/status.hpp index 859f358d..2e9b7da7 100644 --- a/include/boost/beast/http/status.hpp +++ b/include/boost/beast/http/status.hpp @@ -134,6 +134,7 @@ enum class status_class : unsigned If the integer does not match a known status code, @ref status::unknown is returned. */ +BOOST_BEAST_DECL status int_to_status(unsigned v); @@ -144,6 +145,7 @@ int_to_status(unsigned v); @return The status class. If the integer does not match a known status class, @ref status_class::unknown is returned. */ +BOOST_BEAST_DECL status_class to_status_class(unsigned v); @@ -153,6 +155,7 @@ to_status_class(unsigned v); @return The status class. */ +BOOST_BEAST_DECL status_class to_status_class(status v); @@ -160,10 +163,12 @@ to_status_class(status v); @param v The status code to use. */ +BOOST_BEAST_DECL string_view obsolete_reason(status v); /// Outputs the standard reason phrase of a status code to a stream. +BOOST_BEAST_DECL std::ostream& operator<<(std::ostream&, status); @@ -171,6 +176,8 @@ operator<<(std::ostream&, status); } // beast } // boost +#ifdef BOOST_BEAST_HEADER_ONLY #include +#endif #endif diff --git a/include/boost/beast/http/verb.hpp b/include/boost/beast/http/verb.hpp index fea21d5f..5352851a 100644 --- a/include/boost/beast/http/verb.hpp +++ b/include/boost/beast/http/verb.hpp @@ -133,10 +133,12 @@ enum class verb If the string does not match a known request method, @ref verb::unknown is returned. */ +BOOST_BEAST_DECL verb string_to_verb(string_view s); /// Returns the text representation of a request method verb. +BOOST_BEAST_DECL string_view to_string(verb v); @@ -152,6 +154,8 @@ operator<<(std::ostream& os, verb v) } // beast } // boost +#ifdef BOOST_BEAST_HEADER_ONLY #include +#endif #endif diff --git a/include/boost/beast/http/write.hpp b/include/boost/beast/http/write.hpp index 9590b6cd..1d517033 100644 --- a/include/boost/beast/http/write.hpp +++ b/include/boost/beast/http/write.hpp @@ -754,6 +754,6 @@ operator<<(std::ostream& os, } // beast } // boost -#include +#include #endif diff --git a/include/boost/beast/src.hpp b/include/boost/beast/src.hpp new file mode 100644 index 00000000..c7e5ab88 --- /dev/null +++ b/include/boost/beast/src.hpp @@ -0,0 +1,51 @@ +// +// 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_SRC_HPP +#define BOOST_BEAST_SRC_HPP + +/* + +This file is meant to be included once, in a translation unit of +the program, with the macro BOOST_BEAST_SPLIT_COMPILATION defined. + +*/ + +#define BOOST_BEAST_SOURCE + +#include + +#if defined(BOOST_BEAST_HEADER_ONLY) +# error Do not compile Beast library source with BOOST_BEAST_HEADER_ONLY defined +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#endif diff --git a/include/boost/beast/websocket/detail/error.hpp b/include/boost/beast/websocket/detail/error.hpp deleted file mode 100644 index c38d9fa5..00000000 --- a/include/boost/beast/websocket/detail/error.hpp +++ /dev/null @@ -1,78 +0,0 @@ -// -// 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) -// -// Official repository: https://github.com/boostorg/beast -// - -#ifndef BOOST_BEAST_WEBSOCKET_DETAIL_ERROR_HPP -#define BOOST_BEAST_WEBSOCKET_DETAIL_ERROR_HPP - -#include -#include - -namespace boost { - -namespace beast { -namespace websocket { -enum class error; -enum class condition; -} // websocket -} // beast - -namespace system { -template<> -struct is_error_code_enum -{ - static bool const value = true; -}; -template<> -struct is_error_condition_enum -{ - static bool const value = true; -}; -} // system - -namespace beast { -namespace websocket { -namespace detail { - -class error_codes : public error_category -{ -public: - const char* - name() const noexcept override; - - std::string - message(int ev) const override; - - error_condition - default_error_condition(int ev) const noexcept override; -}; - -class error_conditions : public error_category -{ -public: - const char* - name() const noexcept override; - - std::string - message(int cv) const override; -}; - -} // detail - -error_code -make_error_code(error e); - -error_condition -make_error_condition(condition c); - -} // websocket -} // beast - -} // boost - -#endif diff --git a/include/boost/beast/websocket/detail/prng.hpp b/include/boost/beast/websocket/detail/prng.hpp index a35bcb4a..35409b58 100644 --- a/include/boost/beast/websocket/detail/prng.hpp +++ b/include/boost/beast/websocket/detail/prng.hpp @@ -116,6 +116,8 @@ make_prng(bool secure); } // beast } // boost -#include +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif #endif diff --git a/include/boost/beast/websocket/detail/impl/prng.ipp b/include/boost/beast/websocket/detail/prng.ipp similarity index 97% rename from include/boost/beast/websocket/detail/impl/prng.ipp rename to include/boost/beast/websocket/detail/prng.ipp index e2578c41..2807ca96 100644 --- a/include/boost/beast/websocket/detail/impl/prng.ipp +++ b/include/boost/beast/websocket/detail/prng.ipp @@ -7,9 +7,10 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_WEBSOCKET_DETAIL_IMPL_PRNG_IPP -#define BOOST_BEAST_WEBSOCKET_DETAIL_IMPL_PRNG_IPP +#ifndef BOOST_BEAST_WEBSOCKET_DETAIL_PRNG_IPP +#define BOOST_BEAST_WEBSOCKET_DETAIL_PRNG_IPP +#include #include #include #include diff --git a/include/boost/beast/websocket/error.hpp b/include/boost/beast/websocket/error.hpp index ee22cdbd..4bb7d14b 100644 --- a/include/boost/beast/websocket/error.hpp +++ b/include/boost/beast/websocket/error.hpp @@ -10,7 +10,6 @@ #ifndef BOOST_BEAST_WEBSOCKET_ERROR_HPP #define BOOST_BEAST_WEBSOCKET_ERROR_HPP -#include #include #include diff --git a/include/boost/beast/websocket/impl/error.hpp b/include/boost/beast/websocket/impl/error.hpp index 90fcab93..3cc99472 100644 --- a/include/boost/beast/websocket/impl/error.hpp +++ b/include/boost/beast/websocket/impl/error.hpp @@ -10,153 +10,39 @@ #ifndef BOOST_BEAST_WEBSOCKET_IMPL_ERROR_HPP #define BOOST_BEAST_WEBSOCKET_IMPL_ERROR_HPP +namespace boost { +namespace system { +template<> +struct is_error_code_enum<::boost::beast::websocket::error> +{ + static bool const value = true; +}; +template<> +struct is_error_condition_enum<::boost::beast::websocket::condition> +{ + static bool const value = true; +}; +} // system +} // boost + namespace boost { namespace beast { namespace websocket { -namespace detail { - -BOOST_BEAST_DECL -const char* -error_codes:: -name() const noexcept -{ - return "boost.beast.websocket"; -} - -BOOST_BEAST_DECL -std::string -error_codes:: -message(int ev) const -{ - switch(static_cast(ev)) - { - default: - case error::closed: return "The WebSocket stream was gracefully closed at both endpoints"; - case error::buffer_overflow: return "The WebSocket operation caused a dynamic buffer overflow"; - case error::partial_deflate_block: return "The WebSocket stream produced an incomplete deflate block"; - case error::message_too_big: return "The WebSocket message exceeded the locally configured limit"; - - case error::bad_http_version: return "The WebSocket handshake was not HTTP/1.1"; - case error::bad_method: return "The WebSocket handshake method was not GET"; - case error::no_host: return "The WebSocket handshake Host field is missing"; - case error::no_connection: return "The WebSocket handshake Connection field is missing"; - case error::no_connection_upgrade: return "The WebSocket handshake Connection field is missing the upgrade token"; - case error::no_upgrade: return "The WebSocket handshake Upgrade field is missing"; - case error::no_upgrade_websocket: return "The WebSocket handshake Upgrade field is missing the websocket token"; - case error::no_sec_key: return "The WebSocket handshake Sec-WebSocket-Key field is missing"; - case error::bad_sec_key: return "The WebSocket handshake Sec-WebSocket-Key field is invalid"; - case error::no_sec_version: return "The WebSocket handshake Sec-WebSocket-Version field is missing"; - case error::bad_sec_version: return "The WebSocket handshake Sec-WebSocket-Version field is invalid"; - case error::no_sec_accept: return "The WebSocket handshake Sec-WebSocket-Accept field is missing"; - case error::bad_sec_accept: return "The WebSocket handshake Sec-WebSocket-Accept field is invalid"; - case error::upgrade_declined: return "The WebSocket handshake was declined by the remote peer"; - - case error::bad_opcode: return "The WebSocket frame contained an illegal opcode"; - case error::bad_data_frame: return "The WebSocket data frame was unexpected"; - case error::bad_continuation: return "The WebSocket continuation frame was unexpected"; - case error::bad_reserved_bits: return "The WebSocket frame contained illegal reserved bits"; - case error::bad_control_fragment: return "The WebSocket control frame was fragmented"; - case error::bad_control_size: return "The WebSocket control frame size was invalid"; - case error::bad_unmasked_frame: return "The WebSocket frame was unmasked"; - case error::bad_masked_frame: return "The WebSocket frame was masked"; - case error::bad_size: return "The WebSocket frame size was not canonical"; - case error::bad_frame_payload: return "The WebSocket frame payload was not valid utf8"; - case error::bad_close_code: return "The WebSocket close frame reason code was invalid"; - case error::bad_close_size: return "The WebSocket close frame payload size was invalid"; - case error::bad_close_payload: return "The WebSocket close frame payload was not valid utf8"; - } -} - -BOOST_BEAST_DECL -error_condition -error_codes:: -default_error_condition(int ev) const noexcept -{ - switch(static_cast(ev)) - { - default: - case error::closed: - case error::buffer_overflow: - case error::partial_deflate_block: - case error::message_too_big: - return {ev, *this}; - - case error::bad_http_version: - case error::bad_method: - case error::no_host: - case error::no_connection: - case error::no_connection_upgrade: - case error::no_upgrade: - case error::no_upgrade_websocket: - case error::no_sec_key: - case error::bad_sec_key: - case error::no_sec_version: - case error::bad_sec_version: - case error::no_sec_accept: - case error::bad_sec_accept: - case error::upgrade_declined: - return condition::handshake_failed; - - case error::bad_opcode: - case error::bad_data_frame: - case error::bad_continuation: - case error::bad_reserved_bits: - case error::bad_control_fragment: - case error::bad_control_size: - case error::bad_unmasked_frame: - case error::bad_masked_frame: - case error::bad_size: - case error::bad_frame_payload: - case error::bad_close_code: - case error::bad_close_size: - case error::bad_close_payload: - return condition::protocol_violation; - } -} - -BOOST_BEAST_DECL -const char* -error_conditions:: -name() const noexcept -{ - return "boost.beast.websocket"; -} - -BOOST_BEAST_DECL -std::string -error_conditions:: -message(int cv) const -{ - switch(static_cast(cv)) - { - default: - case condition::handshake_failed: return "The WebSocket handshake failed"; - case condition::protocol_violation: return "A WebSocket protocol violation occurred"; - } -} - -} // detail BOOST_BEAST_DECL error_code -make_error_code(error e) -{ - static detail::error_codes const cat{}; - return error_code{static_cast< - std::underlying_type::type>(e), cat}; -} +make_error_code(error e); BOOST_BEAST_DECL error_condition -make_error_condition(condition c) -{ - static detail::error_conditions const cat{}; - return error_condition{static_cast< - std::underlying_type::type>(c), cat}; -} +make_error_condition(condition c); } // websocket } // beast } // boost +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif + #endif diff --git a/include/boost/beast/websocket/impl/error.ipp b/include/boost/beast/websocket/impl/error.ipp new file mode 100644 index 00000000..68296dc4 --- /dev/null +++ b/include/boost/beast/websocket/impl/error.ipp @@ -0,0 +1,160 @@ +// +// 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_WEBSOCKET_IMPL_ERROR_IPP +#define BOOST_BEAST_WEBSOCKET_IMPL_ERROR_IPP + +#include + +namespace boost { +namespace beast { +namespace websocket { +namespace detail { + +class error_codes : public error_category +{ +public: + const char* + name() const noexcept override + { + return "boost.beast.websocket"; + } + + std::string + message(int ev) const override + { + switch(static_cast(ev)) + { + default: + case error::closed: return "The WebSocket stream was gracefully closed at both endpoints"; + case error::buffer_overflow: return "The WebSocket operation caused a dynamic buffer overflow"; + case error::partial_deflate_block: return "The WebSocket stream produced an incomplete deflate block"; + case error::message_too_big: return "The WebSocket message exceeded the locally configured limit"; + + case error::bad_http_version: return "The WebSocket handshake was not HTTP/1.1"; + case error::bad_method: return "The WebSocket handshake method was not GET"; + case error::no_host: return "The WebSocket handshake Host field is missing"; + case error::no_connection: return "The WebSocket handshake Connection field is missing"; + case error::no_connection_upgrade: return "The WebSocket handshake Connection field is missing the upgrade token"; + case error::no_upgrade: return "The WebSocket handshake Upgrade field is missing"; + case error::no_upgrade_websocket: return "The WebSocket handshake Upgrade field is missing the websocket token"; + case error::no_sec_key: return "The WebSocket handshake Sec-WebSocket-Key field is missing"; + case error::bad_sec_key: return "The WebSocket handshake Sec-WebSocket-Key field is invalid"; + case error::no_sec_version: return "The WebSocket handshake Sec-WebSocket-Version field is missing"; + case error::bad_sec_version: return "The WebSocket handshake Sec-WebSocket-Version field is invalid"; + case error::no_sec_accept: return "The WebSocket handshake Sec-WebSocket-Accept field is missing"; + case error::bad_sec_accept: return "The WebSocket handshake Sec-WebSocket-Accept field is invalid"; + case error::upgrade_declined: return "The WebSocket handshake was declined by the remote peer"; + + case error::bad_opcode: return "The WebSocket frame contained an illegal opcode"; + case error::bad_data_frame: return "The WebSocket data frame was unexpected"; + case error::bad_continuation: return "The WebSocket continuation frame was unexpected"; + case error::bad_reserved_bits: return "The WebSocket frame contained illegal reserved bits"; + case error::bad_control_fragment: return "The WebSocket control frame was fragmented"; + case error::bad_control_size: return "The WebSocket control frame size was invalid"; + case error::bad_unmasked_frame: return "The WebSocket frame was unmasked"; + case error::bad_masked_frame: return "The WebSocket frame was masked"; + case error::bad_size: return "The WebSocket frame size was not canonical"; + case error::bad_frame_payload: return "The WebSocket frame payload was not valid utf8"; + case error::bad_close_code: return "The WebSocket close frame reason code was invalid"; + case error::bad_close_size: return "The WebSocket close frame payload size was invalid"; + case error::bad_close_payload: return "The WebSocket close frame payload was not valid utf8"; + } + } + + error_condition + default_error_condition(int ev) const noexcept override + { + switch(static_cast(ev)) + { + default: + case error::closed: + case error::buffer_overflow: + case error::partial_deflate_block: + case error::message_too_big: + return {ev, *this}; + + case error::bad_http_version: + case error::bad_method: + case error::no_host: + case error::no_connection: + case error::no_connection_upgrade: + case error::no_upgrade: + case error::no_upgrade_websocket: + case error::no_sec_key: + case error::bad_sec_key: + case error::no_sec_version: + case error::bad_sec_version: + case error::no_sec_accept: + case error::bad_sec_accept: + case error::upgrade_declined: + return condition::handshake_failed; + + case error::bad_opcode: + case error::bad_data_frame: + case error::bad_continuation: + case error::bad_reserved_bits: + case error::bad_control_fragment: + case error::bad_control_size: + case error::bad_unmasked_frame: + case error::bad_masked_frame: + case error::bad_size: + case error::bad_frame_payload: + case error::bad_close_code: + case error::bad_close_size: + case error::bad_close_payload: + return condition::protocol_violation; + } + } +}; + +class error_conditions : public error_category +{ +public: + const char* + name() const noexcept override + { + return "boost.beast.websocket"; + } + + std::string + message(int cv) const override + { + switch(static_cast(cv)) + { + default: + case condition::handshake_failed: return "The WebSocket handshake failed"; + case condition::protocol_violation: return "A WebSocket protocol violation occurred"; + } + } +}; + +} // detail + +error_code +make_error_code(error e) +{ + static detail::error_codes const cat{}; + return error_code{static_cast< + std::underlying_type::type>(e), cat}; +} + +error_condition +make_error_condition(condition c) +{ + static detail::error_conditions const cat{}; + return error_condition{static_cast< + std::underlying_type::type>(c), cat}; +} + +} // websocket +} // beast +} // boost + +#endif diff --git a/include/boost/beast/zlib/detail/bitstream.hpp b/include/boost/beast/zlib/detail/bitstream.hpp index b755085d..1ac310cc 100644 --- a/include/boost/beast/zlib/detail/bitstream.hpp +++ b/include/boost/beast/zlib/detail/bitstream.hpp @@ -124,7 +124,6 @@ public: }; template -inline bool bitstream:: fill(std::size_t n, FwdIt& first, FwdIt const& last) @@ -140,7 +139,6 @@ fill(std::size_t n, FwdIt& first, FwdIt const& last) } template -inline void bitstream:: fill_8(FwdIt& it) @@ -150,7 +148,6 @@ fill_8(FwdIt& it) } template -inline void bitstream:: fill_16(FwdIt& it) @@ -162,7 +159,6 @@ fill_16(FwdIt& it) } template -inline void bitstream:: peek(Unsigned& value, std::size_t n) @@ -174,7 +170,6 @@ peek(Unsigned& value, std::size_t n) } template -inline void bitstream:: read(Unsigned& value, std::size_t n) @@ -188,7 +183,6 @@ read(Unsigned& value, std::size_t n) } template -inline void bitstream:: rewind(BidirIt& it) diff --git a/include/boost/beast/zlib/detail/deflate_stream.hpp b/include/boost/beast/zlib/detail/deflate_stream.hpp index dac4c2a7..65632afa 100644 --- a/include/boost/beast/zlib/detail/deflate_stream.hpp +++ b/include/boost/beast/zlib/detail/deflate_stream.hpp @@ -37,6 +37,7 @@ #ifndef BOOST_BEAST_ZLIB_DETAIL_DEFLATE_STREAM_HPP #define BOOST_BEAST_ZLIB_DETAIL_DEFLATE_STREAM_HPP +#include #include #include #include @@ -57,56 +58,6 @@ namespace beast { namespace zlib { namespace detail { -/* - * ALGORITHM - * - * The "deflation" process depends on being able to identify portions - * of the input text which are identical to earlier input (within a - * sliding window trailing behind the input currently being processed). - * - * Each code tree is stored in a compressed form which is itself - * a Huffman encoding of the lengths of all the code strings (in - * ascending order by source values). The actual code strings are - * reconstructed from the lengths in the inflate process, as described - * in the deflate specification. - * - * The most straightforward technique turns out to be the fastest for - * most input files: try all possible matches and select the longest. - * The key feature of this algorithm is that insertions into the string - * dictionary are very simple and thus fast, and deletions are avoided - * completely. Insertions are performed at each input character, whereas - * string matches are performed only when the previous match ends. So it - * is preferable to spend more time in matches to allow very fast string - * insertions and avoid deletions. The matching algorithm for small - * strings is inspired from that of Rabin & Karp. A brute force approach - * is used to find longer strings when a small match has been found. - * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze - * (by Leonid Broukhis). - * A previous version of this file used a more sophisticated algorithm - * (by Fiala and Greene) which is guaranteed to run in linear amortized - * time, but has a larger average cost, uses more memory and is patented. - * However the F&G algorithm may be faster for some highly redundant - * files if the parameter max_chain_length (described below) is too large. - * - * ACKNOWLEDGEMENTS - * - * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and - * I found it in 'freeze' written by Leonid Broukhis. - * Thanks to many people for bug reports and testing. - * - * REFERENCES - * - * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". - * Available in http://tools.ietf.org/html/rfc1951 - * - * A description of the Rabin and Karp algorithm is given in the book - * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. - * - * Fiala,E.R., and Greene,D.H. - * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 - * - */ - class deflate_stream { protected: @@ -656,63 +607,63 @@ protected: Unsigned bi_reverse(Unsigned code, unsigned len); - template + BOOST_BEAST_DECL static void gen_codes(ct_data *tree, int max_code, std::uint16_t *bl_count); - template + BOOST_BEAST_DECL static lut_type const& get_lut(); - template void doReset (int level, int windowBits, int memLevel, Strategy strategy); - template void doReset (); - template void doClear (); - template std::size_t doUpperBound (std::size_t sourceLen) const; - template void doTune (int good_length, int max_lazy, int nice_length, int max_chain); - template void doParams (z_params& zs, int level, Strategy strategy, error_code& ec); - template void doWrite (z_params& zs, boost::optional flush, error_code& ec); - template void doDictionary (Byte const* dict, uInt dictLength, error_code& ec); - template void doPrime (int bits, int value, error_code& ec); - template void doPending (unsigned* value, int* bits); + BOOST_BEAST_DECL void doReset (int level, int windowBits, int memLevel, Strategy strategy); + BOOST_BEAST_DECL void doReset (); + BOOST_BEAST_DECL void doClear (); + BOOST_BEAST_DECL std::size_t doUpperBound (std::size_t sourceLen) const; + BOOST_BEAST_DECL void doTune (int good_length, int max_lazy, int nice_length, int max_chain); + BOOST_BEAST_DECL void doParams (z_params& zs, int level, Strategy strategy, error_code& ec); + BOOST_BEAST_DECL void doWrite (z_params& zs, boost::optional flush, error_code& ec); + BOOST_BEAST_DECL void doDictionary (Byte const* dict, uInt dictLength, error_code& ec); + BOOST_BEAST_DECL void doPrime (int bits, int value, error_code& ec); + BOOST_BEAST_DECL void doPending (unsigned* value, int* bits); - template void init (); - template void lm_init (); - template void init_block (); - template void pqdownheap (ct_data const* tree, int k); - template void pqremove (ct_data const* tree, int& top); - template void gen_bitlen (tree_desc *desc); - template void build_tree (tree_desc *desc); - template void scan_tree (ct_data *tree, int max_code); - template void send_tree (ct_data *tree, int max_code); - template int build_bl_tree (); - template void send_all_trees (int lcodes, int dcodes, int blcodes); - template void compress_block (ct_data const* ltree, ct_data const* dtree); - template int detect_data_type (); - template void bi_windup (); - template void bi_flush (); - template void copy_block (char *buf, unsigned len, int header); + BOOST_BEAST_DECL void init (); + BOOST_BEAST_DECL void lm_init (); + BOOST_BEAST_DECL void init_block (); + BOOST_BEAST_DECL void pqdownheap (ct_data const* tree, int k); + BOOST_BEAST_DECL void pqremove (ct_data const* tree, int& top); + BOOST_BEAST_DECL void gen_bitlen (tree_desc *desc); + BOOST_BEAST_DECL void build_tree (tree_desc *desc); + BOOST_BEAST_DECL void scan_tree (ct_data *tree, int max_code); + BOOST_BEAST_DECL void send_tree (ct_data *tree, int max_code); + BOOST_BEAST_DECL int build_bl_tree (); + BOOST_BEAST_DECL void send_all_trees (int lcodes, int dcodes, int blcodes); + BOOST_BEAST_DECL void compress_block (ct_data const* ltree, ct_data const* dtree); + BOOST_BEAST_DECL int detect_data_type (); + BOOST_BEAST_DECL void bi_windup (); + BOOST_BEAST_DECL void bi_flush (); + BOOST_BEAST_DECL void copy_block (char *buf, unsigned len, int header); - template void tr_init (); - template void tr_align (); - template void tr_flush_bits (); - template void tr_stored_block (char *bu, std::uint32_t stored_len, int last); - template void tr_tally_dist (std::uint16_t dist, std::uint8_t len, bool& flush); - template void tr_tally_lit (std::uint8_t c, bool& flush); + BOOST_BEAST_DECL void tr_init (); + BOOST_BEAST_DECL void tr_align (); + BOOST_BEAST_DECL void tr_flush_bits (); + BOOST_BEAST_DECL void tr_stored_block (char *bu, std::uint32_t stored_len, int last); + BOOST_BEAST_DECL void tr_tally_dist (std::uint16_t dist, std::uint8_t len, bool& flush); + BOOST_BEAST_DECL void tr_tally_lit (std::uint8_t c, bool& flush); - template void tr_flush_block (z_params& zs, char *buf, std::uint32_t stored_len, int last); - template void fill_window (z_params& zs); - template void flush_pending (z_params& zs); - template void flush_block (z_params& zs, bool last); - template int read_buf (z_params& zs, Byte *buf, unsigned size); - template uInt longest_match (IPos cur_match); + BOOST_BEAST_DECL void tr_flush_block (z_params& zs, char *buf, std::uint32_t stored_len, int last); + BOOST_BEAST_DECL void fill_window (z_params& zs); + BOOST_BEAST_DECL void flush_pending (z_params& zs); + BOOST_BEAST_DECL void flush_block (z_params& zs, bool last); + BOOST_BEAST_DECL int read_buf (z_params& zs, Byte *buf, unsigned size); + BOOST_BEAST_DECL uInt longest_match (IPos cur_match); - template block_state f_stored (z_params& zs, Flush flush); - template block_state f_fast (z_params& zs, Flush flush); - template block_state f_slow (z_params& zs, Flush flush); - template block_state f_rle (z_params& zs, Flush flush); - template block_state f_huff (z_params& zs, Flush flush); + BOOST_BEAST_DECL block_state f_stored (z_params& zs, Flush flush); + BOOST_BEAST_DECL block_state f_fast (z_params& zs, Flush flush); + BOOST_BEAST_DECL block_state f_slow (z_params& zs, Flush flush); + BOOST_BEAST_DECL block_state f_rle (z_params& zs, Flush flush); + BOOST_BEAST_DECL block_state f_huff (z_params& zs, Flush flush); block_state deflate_stored(z_params& zs, Flush flush) @@ -749,7 +700,6 @@ protected: // Reverse the first len bits of a code template -inline Unsigned deflate_stream:: bi_reverse(Unsigned code, unsigned len) @@ -767,2240 +717,13 @@ bi_reverse(Unsigned code, unsigned len) return res >> 1; } -/* Generate the codes for a given tree and bit counts (which need not be optimal). - IN assertion: the array bl_count contains the bit length statistics for - the given tree and the field len is set for all tree elements. - OUT assertion: the field code is set for all tree elements of non - zero code length. -*/ -template -void -deflate_stream:: -gen_codes(ct_data *tree, int max_code, std::uint16_t *bl_count) -{ - std::uint16_t next_code[maxBits+1]; /* next code value for each bit length */ - std::uint16_t code = 0; /* running code value */ - int bits; /* bit index */ - int n; /* code index */ - - // The distribution counts are first used to - // generate the code values without bit reversal. - for(bits = 1; bits <= maxBits; bits++) - { - code = (code + bl_count[bits-1]) << 1; - next_code[bits] = code; - } - // Check that the bit counts in bl_count are consistent. - // The last code must be all ones. - BOOST_ASSERT(code + bl_count[maxBits]-1 == (1< -auto -deflate_stream::get_lut() -> - lut_type const& -{ - struct init - { - lut_type tables; - - init() - { - // number of codes at each bit length for an optimal tree - //std::uint16_t bl_count[maxBits+1]; - - // Initialize the mapping length (0..255) -> length code (0..28) - std::uint8_t length = 0; - for(std::uint8_t code = 0; code < lengthCodes-1; ++code) - { - tables.base_length[code] = length; - auto const run = 1U << tables.extra_lbits[code]; - for(unsigned n = 0; n < run; ++n) - tables.length_code[length++] = code; - } - BOOST_ASSERT(length == 0); - // Note that the length 255 (match length 258) can be represented - // in two different ways: code 284 + 5 bits or code 285, so we - // overwrite length_code[255] to use the best encoding: - tables.length_code[255] = lengthCodes-1; - - // Initialize the mapping dist (0..32K) -> dist code (0..29) - { - std::uint8_t code; - std::uint16_t dist = 0; - for(code = 0; code < 16; code++) - { - tables.base_dist[code] = dist; - auto const run = 1U << tables.extra_dbits[code]; - for(unsigned n = 0; n < run; ++n) - tables.dist_code[dist++] = code; - } - BOOST_ASSERT(dist == 256); - // from now on, all distances are divided by 128 - dist >>= 7; - for(; code < dCodes; ++code) - { - tables.base_dist[code] = dist << 7; - auto const run = 1U << (tables.extra_dbits[code]-7); - for(std::size_t n = 0; n < run; ++n) - tables.dist_code[256 + dist++] = code; - } - BOOST_ASSERT(dist == 256); - } - - // Construct the codes of the static literal tree - std::uint16_t bl_count[maxBits+1]; - std::memset(bl_count, 0, sizeof(bl_count)); - unsigned n = 0; - while (n <= 143) - tables.ltree[n++].dl = 8; - bl_count[8] += 144; - while (n <= 255) - tables.ltree[n++].dl = 9; - bl_count[9] += 112; - while (n <= 279) - tables.ltree[n++].dl = 7; - bl_count[7] += 24; - while (n <= 287) - tables.ltree[n++].dl = 8; - bl_count[8] += 8; - // Codes 286 and 287 do not exist, but we must include them in the tree - // construction to get a canonical Huffman tree (longest code all ones) - gen_codes(tables.ltree, lCodes+1, bl_count); - - for(n = 0; n < dCodes; ++n) - { - tables.dtree[n].dl = 5; - tables.dtree[n].fc = - static_cast(bi_reverse(n, 5)); - } - } - }; - static init const data; - return data.tables; -} - -template -void -deflate_stream:: -doReset( - int level, - int windowBits, - int memLevel, - Strategy strategy) -{ - if(level == default_size) - level = 6; - - // VFALCO What do we do about this? - // until 256-byte window bug fixed - if(windowBits == 8) - windowBits = 9; - - if(level < 0 || level > 9) - BOOST_THROW_EXCEPTION(std::invalid_argument{ - "invalid level"}); - - if(windowBits < 8 || windowBits > 15) - BOOST_THROW_EXCEPTION(std::invalid_argument{ - "invalid windowBits"}); - - if(memLevel < 1 || memLevel > max_mem_level) - BOOST_THROW_EXCEPTION(std::invalid_argument{ - "invalid memLevel"}); - - w_bits_ = windowBits; - - hash_bits_ = memLevel + 7; - - // 16K elements by default - lit_bufsize_ = 1 << (memLevel + 6); - - level_ = level; - strategy_ = strategy; - inited_ = false; -} - -template -void -deflate_stream:: -doReset() -{ - inited_ = false; -} - -template -void -deflate_stream:: -doClear() -{ - inited_ = false; - buf_.reset(); -} - -template -std::size_t -deflate_stream:: -doUpperBound(std::size_t sourceLen) const -{ - std::size_t complen; - std::size_t wraplen; - - /* conservative upper bound for compressed data */ - complen = sourceLen + - ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5; - - /* compute wrapper length */ - wraplen = 0; - - /* if not default parameters, return conservative bound */ - if(w_bits_ != 15 || hash_bits_ != 8 + 7) - return complen + wraplen; - - /* default settings: return tight bound for that case */ - return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + - (sourceLen >> 25) + 13 - 6 + wraplen; -} - -template -void -deflate_stream:: -doTune( - int good_length, - int max_lazy, - int nice_length, - int max_chain) -{ - good_match_ = good_length; - nice_match_ = nice_length; - max_lazy_match_ = max_lazy; - max_chain_length_ = max_chain; -} - -template -void -deflate_stream:: -doParams(z_params& zs, int level, Strategy strategy, error_code& ec) -{ - compress_func func; - - if(level == default_size) - level = 6; - if(level < 0 || level > 9) - { - ec = error::stream_error; - return; - } - func = get_config(level_).func; - - if((strategy != strategy_ || func != get_config(level).func) && - zs.total_in != 0) - { - // Flush the last buffer: - doWrite(zs, Flush::block, ec); - if(ec == error::need_buffers && pending_ == 0) - ec = {}; - } - if(level_ != level) - { - level_ = level; - max_lazy_match_ = get_config(level).max_lazy; - good_match_ = get_config(level).good_length; - nice_match_ = get_config(level).nice_length; - max_chain_length_ = get_config(level).max_chain; - } - strategy_ = strategy; -} - -// VFALCO boost::optional param is a workaround for -// gcc "maybe uninitialized" warning -// https://github.com/boostorg/beast/issues/532 -// -template -void -deflate_stream:: -doWrite(z_params& zs, boost::optional flush, error_code& ec) -{ - maybe_init(); - - if(zs.next_out == 0 || (zs.next_in == 0 && zs.avail_in != 0) || - (status_ == FINISH_STATE && flush != Flush::finish)) - { - ec = error::stream_error; - return; - } - if(zs.avail_out == 0) - { - ec = error::need_buffers; - return; - } - - // value of flush param for previous deflate call - auto old_flush = boost::make_optional( - last_flush_.is_initialized(), - last_flush_ ? *last_flush_ : Flush::none); - - last_flush_ = flush; - - // Flush as much pending output as possible - if(pending_ != 0) - { - flush_pending(zs); - if(zs.avail_out == 0) - { - /* Since avail_out is 0, deflate will be called again with - * more output space, but possibly with both pending and - * avail_in equal to zero. There won't be anything to do, - * but this is not an error situation so make sure we - * return OK instead of BUF_ERROR at next call of deflate: - */ - last_flush_ = boost::none; - return; - } - } - else if(zs.avail_in == 0 && ( - old_flush && flush <= *old_flush - ) && flush != Flush::finish) - { - /* Make sure there is something to do and avoid duplicate consecutive - * flushes. For repeated and useless calls with Flush::finish, we keep - * returning Z_STREAM_END instead of Z_BUF_ERROR. - */ - ec = error::need_buffers; - return; - } - - // User must not provide more input after the first FINISH: - if(status_ == FINISH_STATE && zs.avail_in != 0) - { - ec = error::need_buffers; - return; - } - - /* Start a new block or continue the current one. - */ - if(zs.avail_in != 0 || lookahead_ != 0 || - (flush != Flush::none && status_ != FINISH_STATE)) - { - block_state bstate; - - switch(strategy_) - { - case Strategy::huffman: - bstate = deflate_huff(zs, flush.get()); - break; - case Strategy::rle: - bstate = deflate_rle(zs, flush.get()); - break; - default: - { - bstate = (this->*(get_config(level_).func))(zs, flush.get()); - break; - } - } - - if(bstate == finish_started || bstate == finish_done) - { - status_ = FINISH_STATE; - } - if(bstate == need_more || bstate == finish_started) - { - if(zs.avail_out == 0) - { - last_flush_ = boost::none; /* avoid BUF_ERROR next call, see above */ - } - return; - /* If flush != Flush::none && avail_out == 0, the next call - of deflate should use the same flush parameter to make sure - that the flush is complete. So we don't have to output an - empty block here, this will be done at next call. This also - ensures that for a very small output buffer, we emit at most - one empty block. - */ - } - if(bstate == block_done) - { - if(flush == Flush::partial) - { - tr_align(); - } - else if(flush != Flush::block) - { - /* FULL_FLUSH or SYNC_FLUSH */ - tr_stored_block((char*)0, 0L, 0); - /* For a full flush, this empty block will be recognized - * as a special marker by inflate_sync(). - */ - if(flush == Flush::full) - { - clear_hash(); // forget history - if(lookahead_ == 0) - { - strstart_ = 0; - block_start_ = 0L; - insert_ = 0; - } - } - } - flush_pending(zs); - if(zs.avail_out == 0) - { - last_flush_ = boost::none; /* avoid BUF_ERROR at next call, see above */ - return; - } - } - } - - if(flush == Flush::finish) - { - ec = error::end_of_stream; - return; - } -} - -// VFALCO Warning: untested -template -void -deflate_stream:: -doDictionary(Byte const* dict, uInt dictLength, error_code& ec) -{ - if(lookahead_) - { - ec = error::stream_error; - return; - } - - maybe_init(); - - /* if dict would fill window, just replace the history */ - if(dictLength >= w_size_) - { - clear_hash(); - strstart_ = 0; - block_start_ = 0L; - insert_ = 0; - dict += dictLength - w_size_; /* use the tail */ - dictLength = w_size_; - } - - /* insert dict into window and hash */ - z_params zs; - zs.avail_in = dictLength; - zs.next_in = (const Byte *)dict; - zs.avail_out = 0; - zs.next_out = 0; - fill_window(zs); - while(lookahead_ >= minMatch) - { - uInt str = strstart_; - uInt n = lookahead_ - (minMatch-1); - do - { - update_hash(ins_h_, window_[str + minMatch-1]); - prev_[str & w_mask_] = head_[ins_h_]; - head_[ins_h_] = (std::uint16_t)str; - str++; - } - while(--n); - strstart_ = str; - lookahead_ = minMatch-1; - fill_window(zs); - } - strstart_ += lookahead_; - block_start_ = (long)strstart_; - insert_ = lookahead_; - lookahead_ = 0; - match_length_ = prev_length_ = minMatch-1; - match_available_ = 0; -} - -template -void -deflate_stream:: -doPrime(int bits, int value, error_code& ec) -{ - maybe_init(); - - if((Byte *)(d_buf_) < pending_out_ + ((Buf_size + 7) >> 3)) - { - ec = error::need_buffers; - return; - } - - do - { - int put = Buf_size - bi_valid_; - if(put > bits) - put = bits; - bi_buf_ |= (std::uint16_t)((value & ((1 << put) - 1)) << bi_valid_); - bi_valid_ += put; - tr_flush_bits(); - value >>= put; - bits -= put; - } - while(bits); -} - -template -void -deflate_stream:: -doPending(unsigned* value, int* bits) -{ - if(value != 0) - *value = pending_; - if(bits != 0) - *bits = bi_valid_; -} - -//-------------------------------------------------------------------------- - -// Do lazy initialization -template -void -deflate_stream:: -init() -{ - // Caller must set these: - // w_bits_ - // hash_bits_ - // lit_bufsize_ - // level_ - // strategy_ - - w_size_ = 1 << w_bits_; - w_mask_ = w_size_ - 1; - - hash_size_ = 1 << hash_bits_; - hash_mask_ = hash_size_ - 1; - hash_shift_ = ((hash_bits_+minMatch-1)/minMatch); - - auto const nwindow = w_size_ * 2*sizeof(Byte); - auto const nprev = w_size_ * sizeof(std::uint16_t); - auto const nhead = hash_size_ * sizeof(std::uint16_t); - auto const noverlay = lit_bufsize_ * (sizeof(std::uint16_t)+2); - auto const needed = nwindow + nprev + nhead + noverlay; - - if(! buf_ || buf_size_ != needed) - { - buf_ = boost::make_unique_noinit< - std::uint8_t[]>(needed); - buf_size_ = needed; - } - - window_ = reinterpret_cast(buf_.get()); - prev_ = reinterpret_cast(buf_.get() + nwindow); - head_ = reinterpret_cast(buf_.get() + nwindow + nprev); - - /* We overlay pending_buf_ and d_buf_ + l_buf_. This works - since the average output size for(length, distance) - codes is <= 24 bits. - */ - auto overlay = reinterpret_cast( - buf_.get() + nwindow + nprev + nhead); - - // nothing written to window_ yet - high_water_ = 0; - - pending_buf_ = - reinterpret_cast(overlay); - pending_buf_size_ = - static_cast(lit_bufsize_) * - (sizeof(std::uint16_t) + 2L); - - d_buf_ = overlay + lit_bufsize_ / sizeof(std::uint16_t); - l_buf_ = pending_buf_ + (1 + sizeof(std::uint16_t)) * lit_bufsize_; - - pending_ = 0; - pending_out_ = pending_buf_; - - status_ = BUSY_STATE; - last_flush_ = Flush::none; - - tr_init(); - lm_init(); - - inited_ = true; -} - -/* Initialize the "longest match" routines for a new zlib stream -*/ -template -void -deflate_stream:: -lm_init() -{ - window_size_ = (std::uint32_t)2L*w_size_; - - clear_hash(); - - /* Set the default configuration parameters: - */ - // VFALCO TODO just copy the config struct - max_lazy_match_ = get_config(level_).max_lazy; - good_match_ = get_config(level_).good_length; - nice_match_ = get_config(level_).nice_length; - max_chain_length_ = get_config(level_).max_chain; - - strstart_ = 0; - block_start_ = 0L; - lookahead_ = 0; - insert_ = 0; - match_length_ = prev_length_ = minMatch-1; - match_available_ = 0; - ins_h_ = 0; -} - -// Initialize a new block. -// -template -void -deflate_stream:: -init_block() -{ - for(int n = 0; n < lCodes; n++) - dyn_ltree_[n].fc = 0; - for(int n = 0; n < dCodes; n++) - dyn_dtree_[n].fc = 0; - for(int n = 0; n < blCodes; n++) - bl_tree_[n].fc = 0; - dyn_ltree_[END_BLOCK].fc = 1; - opt_len_ = 0L; - static_len_ = 0L; - last_lit_ = 0; - matches_ = 0; -} - -/* Restore the heap property by moving down the tree starting at node k, - exchanging a node with the smallest of its two sons if necessary, - stopping when the heap property is re-established (each father smaller - than its two sons). -*/ -template -void -deflate_stream:: -pqdownheap( - ct_data const* tree, // the tree to restore - int k) // node to move down -{ - int v = heap_[k]; - int j = k << 1; // left son of k - while(j <= heap_len_) - { - // Set j to the smallest of the two sons: - if(j < heap_len_ && - smaller(tree, heap_[j+1], heap_[j])) - j++; - // Exit if v is smaller than both sons - if(smaller(tree, v, heap_[j])) - break; - - // Exchange v with the smallest son - heap_[k] = heap_[j]; - k = j; - - // And continue down the tree, - // setting j to the left son of k - j <<= 1; - } - heap_[k] = v; -} - -/* Remove the smallest element from the heap and recreate the heap - with one less element. Updates heap and heap_len. -*/ -template -inline -void -deflate_stream:: -pqremove(ct_data const* tree, int& top) -{ - top = heap_[kSmallest]; - heap_[kSmallest] = heap_[heap_len_--]; - pqdownheap(tree, kSmallest); -} - -/* Compute the optimal bit lengths for a tree and update the total bit length - for the current block. - IN assertion: the fields freq and dad are set, heap[heap_max] and - above are the tree nodes sorted by increasing frequency. - OUT assertions: the field len is set to the optimal bit length, the - array bl_count contains the frequencies for each bit length. - The length opt_len is updated; static_len is also updated if stree is - not null. -*/ -template -void -deflate_stream:: -gen_bitlen(tree_desc *desc) -{ - ct_data *tree = desc->dyn_tree; - int max_code = desc->max_code; - ct_data const* stree = desc->stat_desc->static_tree; - std::uint8_t const *extra = desc->stat_desc->extra_bits; - int base = desc->stat_desc->extra_base; - int max_length = desc->stat_desc->max_length; - int h; // heap index - int n, m; // iterate over the tree elements - int bits; // bit length - int xbits; // extra bits - std::uint16_t f; // frequency - int overflow = 0; // number of elements with bit length too large - - std::fill(&bl_count_[0], &bl_count_[maxBits+1], std::uint16_t{0}); - - /* In a first pass, compute the optimal bit lengths (which may - * overflow in the case of the bit length tree). - */ - tree[heap_[heap_max_]].dl = 0; // root of the heap - - for(h = heap_max_+1; h < HEAP_SIZE; h++) { - n = heap_[h]; - bits = tree[tree[n].dl].dl + 1; - if(bits > max_length) bits = max_length, overflow++; - // We overwrite tree[n].dl which is no longer needed - tree[n].dl = (std::uint16_t)bits; - - if(n > max_code) - continue; // not a leaf node - - bl_count_[bits]++; - xbits = 0; - if(n >= base) - xbits = extra[n-base]; - f = tree[n].fc; - opt_len_ += (std::uint32_t)f * (bits + xbits); - if(stree) - static_len_ += (std::uint32_t)f * (stree[n].dl + xbits); - } - if(overflow == 0) - return; - - // Find the first bit length which could increase: - do - { - bits = max_length-1; - while(bl_count_[bits] == 0) - bits--; - bl_count_[bits]--; // move one leaf down the tree - bl_count_[bits+1] += 2; // move one overflow item as its brother - bl_count_[max_length]--; - /* The brother of the overflow item also moves one step up, - * but this does not affect bl_count[max_length] - */ - overflow -= 2; - } - while(overflow > 0); - - /* Now recompute all bit lengths, scanning in increasing frequency. - * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all - * lengths instead of fixing only the wrong ones. This idea is taken - * from 'ar' written by Haruhiko Okumura.) - */ - for(bits = max_length; bits != 0; bits--) - { - n = bl_count_[bits]; - while(n != 0) - { - m = heap_[--h]; - if(m > max_code) - continue; - if((unsigned) tree[m].dl != (unsigned) bits) - { - opt_len_ += ((long)bits - (long)tree[m].dl) *(long)tree[m].fc; - tree[m].dl = (std::uint16_t)bits; - } - n--; - } - } -} - -/* Construct one Huffman tree and assigns the code bit strings and lengths. - Update the total bit length for the current block. - IN assertion: the field freq is set for all tree elements. - OUT assertions: the fields len and code are set to the optimal bit length - and corresponding code. The length opt_len is updated; static_len is - also updated if stree is not null. The field max_code is set. -*/ -template -void -deflate_stream:: -build_tree(tree_desc *desc) -{ - ct_data *tree = desc->dyn_tree; - ct_data const* stree = desc->stat_desc->static_tree; - int elems = desc->stat_desc->elems; - int n, m; // iterate over heap elements - int max_code = -1; // largest code with non zero frequency - int node; // new node being created - - /* Construct the initial heap, with least frequent element in - * heap[kSmallest]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. - * heap[0] is not used. - */ - heap_len_ = 0; - heap_max_ = HEAP_SIZE; - - for(n = 0; n < elems; n++) - { - if(tree[n].fc != 0) - { - heap_[++(heap_len_)] = max_code = n; - depth_[n] = 0; - } - else - { - tree[n].dl = 0; - } - } - - /* The pkzip format requires that at least one distance code exists, - * and that at least one bit should be sent even if there is only one - * possible code. So to avoid special checks later on we force at least - * two codes of non zero frequency. - */ - while(heap_len_ < 2) - { - node = heap_[++(heap_len_)] = (max_code < 2 ? ++max_code : 0); - tree[node].fc = 1; - depth_[node] = 0; - opt_len_--; - if(stree) - static_len_ -= stree[node].dl; - // node is 0 or 1 so it does not have extra bits - } - desc->max_code = max_code; - - /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, - * establish sub-heaps of increasing lengths: - */ - for(n = heap_len_/2; n >= 1; n--) - pqdownheap(tree, n); - - /* Construct the Huffman tree by repeatedly combining the least two - * frequent nodes. - */ - node = elems; /* next internal node of the tree */ - do - { - pqremove(tree, n); /* n = node of least frequency */ - m = heap_[kSmallest]; /* m = node of next least frequency */ - - heap_[--(heap_max_)] = n; /* keep the nodes sorted by frequency */ - heap_[--(heap_max_)] = m; - - /* Create a new node father of n and m */ - tree[node].fc = tree[n].fc + tree[m].fc; - depth_[node] = (std::uint8_t)((depth_[n] >= depth_[m] ? - depth_[n] : depth_[m]) + 1); - tree[n].dl = tree[m].dl = (std::uint16_t)node; - /* and insert the new node in the heap */ - heap_[kSmallest] = node++; - pqdownheap(tree, kSmallest); - - } - while(heap_len_ >= 2); - - heap_[--(heap_max_)] = heap_[kSmallest]; - - /* At this point, the fields freq and dad are set. We can now - * generate the bit lengths. - */ - gen_bitlen((tree_desc *)desc); - - /* The field len is now set, we can generate the bit codes */ - gen_codes(tree, max_code, bl_count_); -} - -/* Scan a literal or distance tree to determine the frequencies - of the codes in the bit length tree. -*/ -template -void -deflate_stream:: -scan_tree( - ct_data *tree, // the tree to be scanned - int max_code) // and its largest code of non zero frequency -{ - int n; // iterates over all tree elements - int prevlen = -1; // last emitted length - int curlen; // length of current code - int nextlen = tree[0].dl; // length of next code - std::uint16_t count = 0; // repeat count of the current code - int max_count = 7; // max repeat count - int min_count = 4; // min repeat count - - if(nextlen == 0) - { - max_count = 138; - min_count = 3; - } - tree[max_code+1].dl = (std::uint16_t)0xffff; // guard - - for(n = 0; n <= max_code; n++) - { - curlen = nextlen; nextlen = tree[n+1].dl; - if(++count < max_count && curlen == nextlen) - { - continue; - } - else if(count < min_count) - { - bl_tree_[curlen].fc += count; - } - else if(curlen != 0) - { - if(curlen != prevlen) bl_tree_[curlen].fc++; - bl_tree_[REP_3_6].fc++; - } - else if(count <= 10) - { - bl_tree_[REPZ_3_10].fc++; - } - else - { - bl_tree_[REPZ_11_138].fc++; - } - count = 0; - prevlen = curlen; - if(nextlen == 0) - { - max_count = 138; - min_count = 3; - } - else if(curlen == nextlen) - { - max_count = 6; - min_count = 3; - } - else - { - max_count = 7; - min_count = 4; - } - } -} - -/* Send a literal or distance tree in compressed form, - using the codes in bl_tree. -*/ -template -void -deflate_stream:: -send_tree( - ct_data *tree, // the tree to be scanned - int max_code) // and its largest code of non zero frequency -{ - int n; // iterates over all tree elements - int prevlen = -1; // last emitted length - int curlen; // length of current code - int nextlen = tree[0].dl; // length of next code - int count = 0; // repeat count of the current code - int max_count = 7; // max repeat count - int min_count = 4; // min repeat count - - // tree[max_code+1].dl = -1; // guard already set - if(nextlen == 0) - { - max_count = 138; - min_count = 3; - } - - for(n = 0; n <= max_code; n++) - { - curlen = nextlen; - nextlen = tree[n+1].dl; - if(++count < max_count && curlen == nextlen) - { - continue; - } - else if(count < min_count) - { - do - { - send_code(curlen, bl_tree_); - } - while (--count != 0); - } - else if(curlen != 0) - { - if(curlen != prevlen) - { - send_code(curlen, bl_tree_); - count--; - } - BOOST_ASSERT(count >= 3 && count <= 6); - send_code(REP_3_6, bl_tree_); - send_bits(count-3, 2); - } - else if(count <= 10) - { - send_code(REPZ_3_10, bl_tree_); - send_bits(count-3, 3); - } - else - { - send_code(REPZ_11_138, bl_tree_); - send_bits(count-11, 7); - } - count = 0; - prevlen = curlen; - if(nextlen == 0) - { - max_count = 138; - min_count = 3; - } - else if(curlen == nextlen) - { - max_count = 6; - min_count = 3; - } - else - { - max_count = 7; - min_count = 4; - } - } -} - -/* Construct the Huffman tree for the bit lengths and return - the index in bl_order of the last bit length code to send. -*/ -template -int -deflate_stream:: -build_bl_tree() -{ - int max_blindex; // index of last bit length code of non zero freq - - // Determine the bit length frequencies for literal and distance trees - scan_tree((ct_data *)dyn_ltree_, l_desc_.max_code); - scan_tree((ct_data *)dyn_dtree_, d_desc_.max_code); - - // Build the bit length tree: - build_tree((tree_desc *)(&(bl_desc_))); - /* opt_len now includes the length of the tree representations, except - * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. - */ - - /* Determine the number of bit length codes to send. The pkzip format - * requires that at least 4 bit length codes be sent. (appnote.txt says - * 3 but the actual value used is 4.) - */ - for(max_blindex = blCodes-1; max_blindex >= 3; max_blindex--) - { - if(bl_tree_[lut_.bl_order[max_blindex]].dl != 0) - break; - } - // Update opt_len to include the bit length tree and counts - opt_len_ += 3*(max_blindex+1) + 5+5+4; - return max_blindex; -} - -/* Send the header for a block using dynamic Huffman trees: the counts, - the lengths of the bit length codes, the literal tree and the distance - tree. - IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. -*/ -template -void -deflate_stream:: -send_all_trees( - int lcodes, - int dcodes, - int blcodes) // number of codes for each tree -{ - int rank; // index in bl_order - - BOOST_ASSERT(lcodes >= 257 && dcodes >= 1 && blcodes >= 4); - BOOST_ASSERT(lcodes <= lCodes && dcodes <= dCodes && blcodes <= blCodes); - send_bits(lcodes-257, 5); // not +255 as stated in appnote.txt - send_bits(dcodes-1, 5); - send_bits(blcodes-4, 4); // not -3 as stated in appnote.txt - for(rank = 0; rank < blcodes; rank++) - send_bits(bl_tree_[lut_.bl_order[rank]].dl, 3); - send_tree((ct_data *)dyn_ltree_, lcodes-1); // literal tree - send_tree((ct_data *)dyn_dtree_, dcodes-1); // distance tree -} - -/* Send the block data compressed using the given Huffman trees -*/ -template -void -deflate_stream:: -compress_block( - ct_data const* ltree, // literal tree - ct_data const* dtree) // distance tree -{ - unsigned dist; /* distance of matched string */ - int lc; /* match length or unmatched char (if dist == 0) */ - unsigned lx = 0; /* running index in l_buf */ - unsigned code; /* the code to send */ - int extra; /* number of extra bits to send */ - - if(last_lit_ != 0) - { - do - { - dist = d_buf_[lx]; - lc = l_buf_[lx++]; - if(dist == 0) - { - send_code(lc, ltree); /* send a literal byte */ - } - else - { - /* Here, lc is the match length - minMatch */ - code = lut_.length_code[lc]; - send_code(code+literals+1, ltree); /* send the length code */ - extra = lut_.extra_lbits[code]; - if(extra != 0) - { - lc -= lut_.base_length[code]; - send_bits(lc, extra); /* send the extra length bits */ - } - dist--; /* dist is now the match distance - 1 */ - code = d_code(dist); - BOOST_ASSERT(code < dCodes); - - send_code(code, dtree); /* send the distance code */ - extra = lut_.extra_dbits[code]; - if(extra != 0) - { - dist -= lut_.base_dist[code]; - send_bits(dist, extra); /* send the extra distance bits */ - } - } /* literal or match pair ? */ - - /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ - BOOST_ASSERT((uInt)(pending_) < lit_bufsize_ + 2*lx); - } - while(lx < last_lit_); - } - - send_code(END_BLOCK, ltree); -} - -/* Check if the data type is TEXT or BINARY, using the following algorithm: - - TEXT if the two conditions below are satisfied: - a) There are no non-portable control characters belonging to the - "black list" (0..6, 14..25, 28..31). - b) There is at least one printable character belonging to the - "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). - - BINARY otherwise. - - The following partially-portable control characters form a - "gray list" that is ignored in this detection algorithm: - (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). - IN assertion: the fields fc of dyn_ltree are set. -*/ -template -int -deflate_stream:: -detect_data_type() -{ - /* black_mask is the bit mask of black-listed bytes - * set bits 0..6, 14..25, and 28..31 - * 0xf3ffc07f = binary 11110011111111111100000001111111 - */ - unsigned long black_mask = 0xf3ffc07fUL; - int n; - - // Check for non-textual ("black-listed") bytes. - for(n = 0; n <= 31; n++, black_mask >>= 1) - if((black_mask & 1) && (dyn_ltree_[n].fc != 0)) - return binary; - - // Check for textual ("white-listed") bytes. */ - if(dyn_ltree_[9].fc != 0 || dyn_ltree_[10].fc != 0 - || dyn_ltree_[13].fc != 0) - return text; - for(n = 32; n < literals; n++) - if(dyn_ltree_[n].fc != 0) - return text; - - /* There are no "black-listed" or "white-listed" bytes: - * this stream either is empty or has tolerated ("gray-listed") bytes only. - */ - return binary; -} - -/* Flush the bit buffer and align the output on a byte boundary -*/ -template -void -deflate_stream:: -bi_windup() -{ - if(bi_valid_ > 8) - put_short(bi_buf_); - else if(bi_valid_ > 0) - put_byte((Byte)bi_buf_); - bi_buf_ = 0; - bi_valid_ = 0; -} - -/* Flush the bit buffer, keeping at most 7 bits in it. -*/ -template -void -deflate_stream:: -bi_flush() -{ - if(bi_valid_ == 16) - { - put_short(bi_buf_); - bi_buf_ = 0; - bi_valid_ = 0; - } - else if(bi_valid_ >= 8) - { - put_byte((Byte)bi_buf_); - bi_buf_ >>= 8; - bi_valid_ -= 8; - } -} - -/* Copy a stored block, storing first the length and its - one's complement if requested. -*/ -template -void -deflate_stream:: -copy_block( - char *buf, // the input data - unsigned len, // its length - int header) // true if block header must be written -{ - bi_windup(); // align on byte boundary - - if(header) - { - put_short((std::uint16_t)len); - put_short((std::uint16_t)~len); - } - // VFALCO Use memcpy? - while (len--) - put_byte(*buf++); -} - -//------------------------------------------------------------------------------ - -/* Initialize the tree data structures for a new zlib stream. -*/ -template -void -deflate_stream:: -tr_init() -{ - l_desc_.dyn_tree = dyn_ltree_; - l_desc_.stat_desc = &lut_.l_desc; - - d_desc_.dyn_tree = dyn_dtree_; - d_desc_.stat_desc = &lut_.d_desc; - - bl_desc_.dyn_tree = bl_tree_; - bl_desc_.stat_desc = &lut_.bl_desc; - - bi_buf_ = 0; - bi_valid_ = 0; - - // Initialize the first block of the first file: - init_block(); -} - -/* Send one empty static block to give enough lookahead for inflate. - This takes 10 bits, of which 7 may remain in the bit buffer. -*/ -template -void -deflate_stream:: -tr_align() -{ - send_bits(STATIC_TREES<<1, 3); - send_code(END_BLOCK, lut_.ltree); - bi_flush(); -} - -/* Flush the bits in the bit buffer to pending output (leaves at most 7 bits) -*/ -template -void -deflate_stream:: -tr_flush_bits() -{ - bi_flush(); -} - -/* Send a stored block -*/ -template -void -deflate_stream:: -tr_stored_block( - char *buf, // input block - std::uint32_t stored_len, // length of input block - int last) // one if this is the last block for a file -{ - send_bits((STORED_BLOCK<<1)+last, 3); // send block type - copy_block(buf, (unsigned)stored_len, 1); // with header -} - -template -inline -void -deflate_stream:: -tr_tally_dist(std::uint16_t dist, std::uint8_t len, bool& flush) -{ - d_buf_[last_lit_] = dist; - l_buf_[last_lit_++] = len; - dist--; - dyn_ltree_[lut_.length_code[len]+literals+1].fc++; - dyn_dtree_[d_code(dist)].fc++; - flush = (last_lit_ == lit_bufsize_-1); -} - -template -inline -void -deflate_stream:: -tr_tally_lit(std::uint8_t c, bool& flush) -{ - d_buf_[last_lit_] = 0; - l_buf_[last_lit_++] = c; - dyn_ltree_[c].fc++; - flush = (last_lit_ == lit_bufsize_-1); -} - -//------------------------------------------------------------------------------ - -/* Determine the best encoding for the current block: dynamic trees, - static trees or store, and output the encoded block to the zip file. -*/ -template -void -deflate_stream:: -tr_flush_block( - z_params& zs, - char *buf, // input block, or NULL if too old - std::uint32_t stored_len, // length of input block - int last) // one if this is the last block for a file -{ - std::uint32_t opt_lenb; - std::uint32_t static_lenb; // opt_len and static_len in bytes - int max_blindex = 0; // index of last bit length code of non zero freq - - // Build the Huffman trees unless a stored block is forced - if(level_ > 0) - { - // Check if the file is binary or text - if(zs.data_type == unknown) - zs.data_type = detect_data_type(); - - // Construct the literal and distance trees - build_tree((tree_desc *)(&(l_desc_))); - - build_tree((tree_desc *)(&(d_desc_))); - /* At this point, opt_len and static_len are the total bit lengths of - * the compressed block data, excluding the tree representations. - */ - - /* Build the bit length tree for the above two trees, and get the index - * in bl_order of the last bit length code to send. - */ - max_blindex = build_bl_tree(); - - /* Determine the best encoding. Compute the block lengths in bytes. */ - opt_lenb = (opt_len_+3+7)>>3; - static_lenb = (static_len_+3+7)>>3; - - if(static_lenb <= opt_lenb) - opt_lenb = static_lenb; - } - else - { - // VFALCO This assertion fails even in the original ZLib, - // happens with strategy == Z_HUFFMAN_ONLY, see: - // https://github.com/madler/zlib/issues/172 - - #if 0 - BOOST_ASSERT(buf); - #endif - opt_lenb = static_lenb = stored_len + 5; // force a stored block - } - -#ifdef FORCE_STORED - if(buf != (char*)0) { /* force stored block */ -#else - if(stored_len+4 <= opt_lenb && buf != (char*)0) { - /* 4: two words for the lengths */ -#endif - /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. - * Otherwise we can't have processed more than WSIZE input bytes since - * the last block flush, because compression would have been - * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to - * transform a block into a stored block. - */ - tr_stored_block(buf, stored_len, last); - -#ifdef FORCE_STATIC - } - else if(static_lenb >= 0) - { - // force static trees -#else - } - else if(strategy_ == Strategy::fixed || static_lenb == opt_lenb) - { -#endif - send_bits((STATIC_TREES<<1)+last, 3); - compress_block(lut_.ltree, lut_.dtree); - } - else - { - send_bits((DYN_TREES<<1)+last, 3); - send_all_trees(l_desc_.max_code+1, d_desc_.max_code+1, - max_blindex+1); - compress_block((const ct_data *)dyn_ltree_, - (const ct_data *)dyn_dtree_); - } - /* The above check is made mod 2^32, for files larger than 512 MB - * and std::size_t implemented on 32 bits. - */ - init_block(); - - if(last) - bi_windup(); -} - -template -void -deflate_stream:: -fill_window(z_params& zs) -{ - unsigned n, m; - unsigned more; // Amount of free space at the end of the window. - std::uint16_t *p; - uInt wsize = w_size_; - - do - { - more = (unsigned)(window_size_ - - (std::uint32_t)lookahead_ -(std::uint32_t)strstart_); - - // VFALCO We don't support systems below 32-bit - #if 0 - // Deal with !@#$% 64K limit: - if(sizeof(int) <= 2) - { - if(more == 0 && strstart_ == 0 && lookahead_ == 0) - { - more = wsize; - } - else if(more == (unsigned)(-1)) - { - /* Very unlikely, but possible on 16 bit machine if - * strstart == 0 && lookahead == 1 (input done a byte at time) - */ - more--; - } - } - #endif - - /* If the window is almost full and there is insufficient lookahead, - move the upper half to the lower one to make room in the upper half. - */ - if(strstart_ >= wsize+max_dist()) - { - std::memcpy(window_, window_+wsize, (unsigned)wsize); - match_start_ -= wsize; - strstart_ -= wsize; // we now have strstart >= max_dist - block_start_ -= (long) wsize; - - /* Slide the hash table (could be avoided with 32 bit values - at the expense of memory usage). We slide even when level == 0 - to keep the hash table consistent if we switch back to level > 0 - later. (Using level 0 permanently is not an optimal usage of - zlib, so we don't care about this pathological case.) - */ - n = hash_size_; - p = &head_[n]; - do - { - m = *--p; - *p = (std::uint16_t)(m >= wsize ? m-wsize : 0); - } - while(--n); - - n = wsize; - p = &prev_[n]; - do - { - m = *--p; - *p = (std::uint16_t)(m >= wsize ? m-wsize : 0); - /* If n is not on any hash chain, prev[n] is garbage but - its value will never be used. - */ - } - while(--n); - more += wsize; - } - if(zs.avail_in == 0) - break; - - /* If there was no sliding: - strstart <= WSIZE+max_dist-1 && lookahead <= kMinLookahead - 1 && - more == window_size - lookahead - strstart - => more >= window_size - (kMinLookahead-1 + WSIZE + max_dist-1) - => more >= window_size - 2*WSIZE + 2 - In the BIG_MEM or MMAP case (not yet supported), - window_size == input_size + kMinLookahead && - strstart + lookahead_ <= input_size => more >= kMinLookahead. - Otherwise, window_size == 2*WSIZE so more >= 2. - If there was sliding, more >= WSIZE. So in all cases, more >= 2. - */ - n = read_buf(zs, window_ + strstart_ + lookahead_, more); - lookahead_ += n; - - // Initialize the hash value now that we have some input: - if(lookahead_ + insert_ >= minMatch) - { - uInt str = strstart_ - insert_; - ins_h_ = window_[str]; - update_hash(ins_h_, window_[str + 1]); - while(insert_) - { - update_hash(ins_h_, window_[str + minMatch-1]); - prev_[str & w_mask_] = head_[ins_h_]; - head_[ins_h_] = (std::uint16_t)str; - str++; - insert_--; - if(lookahead_ + insert_ < minMatch) - break; - } - } - /* If the whole input has less than minMatch bytes, ins_h is garbage, - but this is not important since only literal bytes will be emitted. - */ - } - while(lookahead_ < kMinLookahead && zs.avail_in != 0); - - /* If the kWinInit bytes after the end of the current data have never been - written, then zero those bytes in order to avoid memory check reports of - the use of uninitialized (or uninitialised as Julian writes) bytes by - the longest match routines. Update the high water mark for the next - time through here. kWinInit is set to maxMatch since the longest match - routines allow scanning to strstart + maxMatch, ignoring lookahead. - */ - if(high_water_ < window_size_) - { - std::uint32_t curr = strstart_ + (std::uint32_t)(lookahead_); - std::uint32_t winit; - - if(high_water_ < curr) - { - /* Previous high water mark below current data -- zero kWinInit - bytes or up to end of window, whichever is less. - */ - winit = window_size_ - curr; - if(winit > kWinInit) - winit = kWinInit; - std::memset(window_ + curr, 0, (unsigned)winit); - high_water_ = curr + winit; - } - else if(high_water_ < (std::uint32_t)curr + kWinInit) - { - /* High water mark at or above current data, but below current data - plus kWinInit -- zero out to current data plus kWinInit, or up - to end of window, whichever is less. - */ - winit = (std::uint32_t)curr + kWinInit - high_water_; - if(winit > window_size_ - high_water_) - winit = window_size_ - high_water_; - std::memset(window_ + high_water_, 0, (unsigned)winit); - high_water_ += winit; - } - } -} - -/* Flush as much pending output as possible. All write() output goes - through this function so some applications may wish to modify it - to avoid allocating a large strm->next_out buffer and copying into it. - (See also read_buf()). -*/ -template -void -deflate_stream:: -flush_pending(z_params& zs) -{ - tr_flush_bits(); - auto len = clamp(pending_, zs.avail_out); - if(len == 0) - return; - - std::memcpy(zs.next_out, pending_out_, len); - zs.next_out = - static_cast(zs.next_out) + len; - pending_out_ += len; - zs.total_out += len; - zs.avail_out -= len; - pending_ -= len; - if(pending_ == 0) - pending_out_ = pending_buf_; -} - -/* Flush the current block, with given end-of-file flag. - IN assertion: strstart is set to the end of the current match. -*/ -template -inline -void -deflate_stream:: -flush_block(z_params& zs, bool last) -{ - tr_flush_block(zs, - (block_start_ >= 0L ? - (char *)&window_[(unsigned)block_start_] : - (char *)0), - (std::uint32_t)((long)strstart_ - block_start_), - last); - block_start_ = strstart_; - flush_pending(zs); -} - -/* Read a new buffer from the current input stream, update the adler32 - and total number of bytes read. All write() input goes through - this function so some applications may wish to modify it to avoid - allocating a large strm->next_in buffer and copying from it. - (See also flush_pending()). -*/ -template -int -deflate_stream:: -read_buf(z_params& zs, Byte *buf, unsigned size) -{ - auto len = clamp(zs.avail_in, size); - if(len == 0) - return 0; - - zs.avail_in -= len; - - std::memcpy(buf, zs.next_in, len); - zs.next_in = static_cast< - std::uint8_t const*>(zs.next_in) + len; - zs.total_in += len; - return (int)len; -} - -/* Set match_start to the longest match starting at the given string and - return its length. Matches shorter or equal to prev_length are discarded, - in which case the result is equal to prev_length and match_start is - garbage. - IN assertions: cur_match is the head of the hash chain for the current - string (strstart) and its distance is <= max_dist, and prev_length >= 1 - OUT assertion: the match length is not greater than s->lookahead_. - - For 80x86 and 680x0, an optimized version will be provided in match.asm or - match.S. The code will be functionally equivalent. -*/ -template -uInt -deflate_stream:: -longest_match(IPos cur_match) -{ - unsigned chain_length = max_chain_length_;/* max hash chain length */ - Byte *scan = window_ + strstart_; /* current string */ - Byte *match; /* matched string */ - int len; /* length of current match */ - int best_len = prev_length_; /* best match length so far */ - int nice_match = nice_match_; /* stop if match long enough */ - IPos limit = strstart_ > (IPos)max_dist() ? - strstart_ - (IPos)max_dist() : 0; - /* Stop when cur_match becomes <= limit. To simplify the code, - * we prevent matches with the string of window index 0. - */ - std::uint16_t *prev = prev_; - uInt wmask = w_mask_; - - Byte *strend = window_ + strstart_ + maxMatch; - Byte scan_end1 = scan[best_len-1]; - Byte scan_end = scan[best_len]; - - /* The code is optimized for HASH_BITS >= 8 and maxMatch-2 multiple of 16. - * It is easy to get rid of this optimization if necessary. - */ - BOOST_ASSERT(hash_bits_ >= 8 && maxMatch == 258); - - /* Do not waste too much time if we already have a good match: */ - if(prev_length_ >= good_match_) { - chain_length >>= 2; - } - /* Do not look for matches beyond the end of the input. This is necessary - * to make deflate deterministic. - */ - if((uInt)nice_match > lookahead_) - nice_match = lookahead_; - - BOOST_ASSERT((std::uint32_t)strstart_ <= window_size_-kMinLookahead); - - do { - BOOST_ASSERT(cur_match < strstart_); - match = window_ + cur_match; - - /* Skip to next match if the match length cannot increase - * or if the match length is less than 2. Note that the checks below - * for insufficient lookahead only occur occasionally for performance - * reasons. Therefore uninitialized memory will be accessed, and - * conditional jumps will be made that depend on those values. - * However the length of the match is limited to the lookahead, so - * the output of deflate is not affected by the uninitialized values. - */ - if( match[best_len] != scan_end || - match[best_len-1] != scan_end1 || - *match != *scan || - *++match != scan[1]) - continue; - - /* The check at best_len-1 can be removed because it will be made - * again later. (This heuristic is not always a win.) - * It is not necessary to compare scan[2] and match[2] since they - * are always equal when the other bytes match, given that - * the hash keys are equal and that HASH_BITS >= 8. - */ - scan += 2, match++; - BOOST_ASSERT(*scan == *match); - - /* We check for insufficient lookahead only every 8th comparison; - * the 256th check will be made at strstart+258. - */ - do - { - } - while( *++scan == *++match && *++scan == *++match && - *++scan == *++match && *++scan == *++match && - *++scan == *++match && *++scan == *++match && - *++scan == *++match && *++scan == *++match && - scan < strend); - - BOOST_ASSERT(scan <= window_+(unsigned)(window_size_-1)); - - len = maxMatch - (int)(strend - scan); - scan = strend - maxMatch; - - if(len > best_len) { - match_start_ = cur_match; - best_len = len; - if(len >= nice_match) break; - scan_end1 = scan[best_len-1]; - scan_end = scan[best_len]; - } - } - while((cur_match = prev[cur_match & wmask]) > limit - && --chain_length != 0); - - if((uInt)best_len <= lookahead_) - return (uInt)best_len; - return lookahead_; -} - -//------------------------------------------------------------------------------ - -/* Copy without compression as much as possible from the input stream, return - the current block state. - This function does not insert new strings in the dictionary since - uncompressible data is probably not useful. This function is used - only for the level=0 compression option. - NOTE: this function should be optimized to avoid extra copying from - window to pending_buf. -*/ -template -inline -auto -deflate_stream:: -f_stored(z_params& zs, Flush flush) -> - block_state -{ - /* Stored blocks are limited to 0xffff bytes, pending_buf is limited - * to pending_buf_size, and each stored block has a 5 byte header: - */ - std::uint32_t max_block_size = 0xffff; - std::uint32_t max_start; - - if(max_block_size > pending_buf_size_ - 5) { - max_block_size = pending_buf_size_ - 5; - } - - /* Copy as much as possible from input to output: */ - for(;;) { - /* Fill the window as much as possible: */ - if(lookahead_ <= 1) { - - BOOST_ASSERT(strstart_ < w_size_+max_dist() || - block_start_ >= (long)w_size_); - - fill_window(zs); - if(lookahead_ == 0 && flush == Flush::none) - return need_more; - - if(lookahead_ == 0) break; /* flush the current block */ - } - BOOST_ASSERT(block_start_ >= 0L); - - strstart_ += lookahead_; - lookahead_ = 0; - - /* Emit a stored block if pending_buf will be full: */ - max_start = block_start_ + max_block_size; - if(strstart_ == 0 || (std::uint32_t)strstart_ >= max_start) { - /* strstart == 0 is possible when wraparound on 16-bit machine */ - lookahead_ = (uInt)(strstart_ - max_start); - strstart_ = (uInt)max_start; - flush_block(zs, false); - if(zs.avail_out == 0) - return need_more; - } - /* Flush if we may have to slide, otherwise block_start may become - * negative and the data will be gone: - */ - if(strstart_ - (uInt)block_start_ >= max_dist()) { - flush_block(zs, false); - if(zs.avail_out == 0) - return need_more; - } - } - insert_ = 0; - if(flush == Flush::finish) - { - flush_block(zs, true); - if(zs.avail_out == 0) - return finish_started; - return finish_done; - } - if((long)strstart_ > block_start_) - { - flush_block(zs, false); - if(zs.avail_out == 0) - return need_more; - } - return block_done; -} - -/* Compress as much as possible from the input stream, return the current - block state. - This function does not perform lazy evaluation of matches and inserts - new strings in the dictionary only for unmatched strings or for short - matches. It is used only for the fast compression options. -*/ -template -inline -auto -deflate_stream:: -f_fast(z_params& zs, Flush flush) -> - block_state -{ - IPos hash_head; /* head of the hash chain */ - bool bflush; /* set if current block must be flushed */ - - for(;;) - { - /* Make sure that we always have enough lookahead, except - * at the end of the input file. We need maxMatch bytes - * for the next match, plus minMatch bytes to insert the - * string following the next match. - */ - if(lookahead_ < kMinLookahead) - { - fill_window(zs); - if(lookahead_ < kMinLookahead && flush == Flush::none) - return need_more; - if(lookahead_ == 0) - break; /* flush the current block */ - } - - /* Insert the string window[strstart .. strstart+2] in the - * dictionary, and set hash_head to the head of the hash chain: - */ - hash_head = 0; - if(lookahead_ >= minMatch) { - insert_string(hash_head); - } - - /* Find the longest match, discarding those <= prev_length. - * At this point we have always match_length < minMatch - */ - if(hash_head != 0 && strstart_ - hash_head <= max_dist()) { - /* To simplify the code, we prevent matches with the string - * of window index 0 (in particular we have to avoid a match - * of the string with itself at the start of the input file). - */ - match_length_ = longest_match (hash_head); - /* longest_match() sets match_start */ - } - if(match_length_ >= minMatch) - { - tr_tally_dist(static_cast(strstart_ - match_start_), - static_cast(match_length_ - minMatch), bflush); - - lookahead_ -= match_length_; - - /* Insert new strings in the hash table only if the match length - * is not too large. This saves time but degrades compression. - */ - if(match_length_ <= max_lazy_match_ && - lookahead_ >= minMatch) { - match_length_--; /* string at strstart already in table */ - do - { - strstart_++; - insert_string(hash_head); - /* strstart never exceeds WSIZE-maxMatch, so there are - * always minMatch bytes ahead. - */ - } - while(--match_length_ != 0); - strstart_++; - } - else - { - strstart_ += match_length_; - match_length_ = 0; - ins_h_ = window_[strstart_]; - update_hash(ins_h_, window_[strstart_+1]); - /* If lookahead < minMatch, ins_h is garbage, but it does not - * matter since it will be recomputed at next deflate call. - */ - } - } - else - { - /* No match, output a literal byte */ - tr_tally_lit(window_[strstart_], bflush); - lookahead_--; - strstart_++; - } - if(bflush) - { - flush_block(zs, false); - if(zs.avail_out == 0) - return need_more; - } - } - insert_ = strstart_ < minMatch-1 ? strstart_ : minMatch-1; - if(flush == Flush::finish) - { - flush_block(zs, true); - if(zs.avail_out == 0) - return finish_started; - return finish_done; - } - if(last_lit_) - { - flush_block(zs, false); - if(zs.avail_out == 0) - return need_more; - } - return block_done; -} - -/* Same as above, but achieves better compression. We use a lazy - evaluation for matches: a match is finally adopted only if there is - no better match at the next window position. -*/ -template -inline -auto -deflate_stream:: -f_slow(z_params& zs, Flush flush) -> - block_state -{ - IPos hash_head; /* head of hash chain */ - bool bflush; /* set if current block must be flushed */ - - /* Process the input block. */ - for(;;) - { - /* Make sure that we always have enough lookahead, except - * at the end of the input file. We need maxMatch bytes - * for the next match, plus minMatch bytes to insert the - * string following the next match. - */ - if(lookahead_ < kMinLookahead) - { - fill_window(zs); - if(lookahead_ < kMinLookahead && flush == Flush::none) - return need_more; - if(lookahead_ == 0) - break; /* flush the current block */ - } - - /* Insert the string window[strstart .. strstart+2] in the - * dictionary, and set hash_head to the head of the hash chain: - */ - hash_head = 0; - if(lookahead_ >= minMatch) - insert_string(hash_head); - - /* Find the longest match, discarding those <= prev_length. - */ - prev_length_ = match_length_, prev_match_ = match_start_; - match_length_ = minMatch-1; - - if(hash_head != 0 && prev_length_ < max_lazy_match_ && - strstart_ - hash_head <= max_dist()) - { - /* To simplify the code, we prevent matches with the string - * of window index 0 (in particular we have to avoid a match - * of the string with itself at the start of the input file). - */ - match_length_ = longest_match(hash_head); - /* longest_match() sets match_start */ - - if(match_length_ <= 5 && (strategy_ == Strategy::filtered - || (match_length_ == minMatch && - strstart_ - match_start_ > kTooFar) - )) - { - /* If prev_match is also minMatch, match_start is garbage - * but we will ignore the current match anyway. - */ - match_length_ = minMatch-1; - } - } - /* If there was a match at the previous step and the current - * match is not better, output the previous match: - */ - if(prev_length_ >= minMatch && match_length_ <= prev_length_) - { - /* Do not insert strings in hash table beyond this. */ - uInt max_insert = strstart_ + lookahead_ - minMatch; - - tr_tally_dist( - static_cast(strstart_ -1 - prev_match_), - static_cast(prev_length_ - minMatch), bflush); - - /* Insert in hash table all strings up to the end of the match. - * strstart-1 and strstart are already inserted. If there is not - * enough lookahead, the last two strings are not inserted in - * the hash table. - */ - lookahead_ -= prev_length_-1; - prev_length_ -= 2; - do { - if(++strstart_ <= max_insert) - insert_string(hash_head); - } - while(--prev_length_ != 0); - match_available_ = 0; - match_length_ = minMatch-1; - strstart_++; - - if(bflush) - { - flush_block(zs, false); - if(zs.avail_out == 0) - return need_more; - } - - } - else if(match_available_) - { - /* If there was no match at the previous position, output a - * single literal. If there was a match but the current match - * is longer, truncate the previous match to a single literal. - */ - tr_tally_lit(window_[strstart_-1], bflush); - if(bflush) - flush_block(zs, false); - strstart_++; - lookahead_--; - if(zs.avail_out == 0) - return need_more; - } - else - { - /* There is no previous match to compare with, wait for - * the next step to decide. - */ - match_available_ = 1; - strstart_++; - lookahead_--; - } - } - BOOST_ASSERT(flush != Flush::none); - if(match_available_) - { - tr_tally_lit(window_[strstart_-1], bflush); - match_available_ = 0; - } - insert_ = strstart_ < minMatch-1 ? strstart_ : minMatch-1; - if(flush == Flush::finish) - { - flush_block(zs, true); - if(zs.avail_out == 0) - return finish_started; - return finish_done; - } - if(last_lit_) - { - flush_block(zs, false); - if(zs.avail_out == 0) - return need_more; - } - return block_done; -} - -/* For Strategy::rle, simply look for runs of bytes, generate matches only of distance - one. Do not maintain a hash table. (It will be regenerated if this run of - deflate switches away from Strategy::rle.) -*/ -template -inline -auto -deflate_stream:: -f_rle(z_params& zs, Flush flush) -> - block_state -{ - bool bflush; // set if current block must be flushed - uInt prev; // byte at distance one to match - Byte *scan, *strend; // scan goes up to strend for length of run - - for(;;) - { - /* Make sure that we always have enough lookahead, except - * at the end of the input file. We need maxMatch bytes - * for the longest run, plus one for the unrolled loop. - */ - if(lookahead_ <= maxMatch) { - fill_window(zs); - if(lookahead_ <= maxMatch && flush == Flush::none) { - return need_more; - } - if(lookahead_ == 0) break; /* flush the current block */ - } - - /* See how many times the previous byte repeats */ - match_length_ = 0; - if(lookahead_ >= minMatch && strstart_ > 0) { - scan = window_ + strstart_ - 1; - prev = *scan; - if(prev == *++scan && prev == *++scan && prev == *++scan) { - strend = window_ + strstart_ + maxMatch; - do { - } while(prev == *++scan && prev == *++scan && - prev == *++scan && prev == *++scan && - prev == *++scan && prev == *++scan && - prev == *++scan && prev == *++scan && - scan < strend); - match_length_ = maxMatch - (int)(strend - scan); - if(match_length_ > lookahead_) - match_length_ = lookahead_; - } - BOOST_ASSERT(scan <= window_+(uInt)(window_size_-1)); - } - - /* Emit match if have run of minMatch or longer, else emit literal */ - if(match_length_ >= minMatch) { - tr_tally_dist(std::uint16_t{1}, - static_cast(match_length_ - minMatch), - bflush); - - lookahead_ -= match_length_; - strstart_ += match_length_; - match_length_ = 0; - } else { - /* No match, output a literal byte */ - tr_tally_lit(window_[strstart_], bflush); - lookahead_--; - strstart_++; - } - if(bflush) - { - flush_block(zs, false); - if(zs.avail_out == 0) - return need_more; - } - } - insert_ = 0; - if(flush == Flush::finish) - { - flush_block(zs, true); - if(zs.avail_out == 0) - return finish_started; - return finish_done; - } - if(last_lit_) - { - flush_block(zs, false); - if(zs.avail_out == 0) - return need_more; - } - return block_done; -} - -/* =========================================================================== - * For Strategy::huffman, do not look for matches. Do not maintain a hash table. - * (It will be regenerated if this run of deflate switches away from Huffman.) - */ -template -inline -auto -deflate_stream:: -f_huff(z_params& zs, Flush flush) -> - block_state -{ - bool bflush; // set if current block must be flushed - - for(;;) - { - // Make sure that we have a literal to write. - if(lookahead_ == 0) - { - fill_window(zs); - if(lookahead_ == 0) - { - if(flush == Flush::none) - return need_more; - break; // flush the current block - } - } - - // Output a literal byte - match_length_ = 0; - tr_tally_lit(window_[strstart_], bflush); - lookahead_--; - strstart_++; - if(bflush) - { - flush_block(zs, false); - if(zs.avail_out == 0) - return need_more; - } - } - insert_ = 0; - if(flush == Flush::finish) - { - flush_block(zs, true); - if(zs.avail_out == 0) - return finish_started; - return finish_done; - } - if(last_lit_) - { - flush_block(zs, false); - if(zs.avail_out == 0) - return need_more; - } - return block_done; -} - } // detail } // zlib } // beast } // boost +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif + #endif diff --git a/include/boost/beast/zlib/detail/deflate_stream.ipp b/include/boost/beast/zlib/detail/deflate_stream.ipp new file mode 100644 index 00000000..4641697c --- /dev/null +++ b/include/boost/beast/zlib/detail/deflate_stream.ipp @@ -0,0 +1,2292 @@ +// +// 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) +// +// Official repository: https://github.com/boostorg/beast +// +// This is a derivative work based on Zlib, copyright below: +/* + Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 + (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). +*/ + +#ifndef BOOST_BEAST_ZLIB_DETAIL_DEFLATE_STREAM_IPP +#define BOOST_BEAST_ZLIB_DETAIL_DEFLATE_STREAM_IPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace zlib { +namespace detail { + +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in http://tools.ietf.org/html/rfc1951 + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* Generate the codes for a given tree and bit counts (which need not be optimal). + IN assertion: the array bl_count contains the bit length statistics for + the given tree and the field len is set for all tree elements. + OUT assertion: the field code is set for all tree elements of non + zero code length. +*/ +void +deflate_stream:: +gen_codes(ct_data *tree, int max_code, std::uint16_t *bl_count) +{ + std::uint16_t next_code[maxBits+1]; /* next code value for each bit length */ + std::uint16_t code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + // The distribution counts are first used to + // generate the code values without bit reversal. + for(bits = 1; bits <= maxBits; bits++) + { + code = (code + bl_count[bits-1]) << 1; + next_code[bits] = code; + } + // Check that the bit counts in bl_count are consistent. + // The last code must be all ones. + BOOST_ASSERT(code + bl_count[maxBits]-1 == (1< + lut_type const& +{ + struct init + { + lut_type tables; + + init() + { + // number of codes at each bit length for an optimal tree + //std::uint16_t bl_count[maxBits+1]; + + // Initialize the mapping length (0..255) -> length code (0..28) + std::uint8_t length = 0; + for(std::uint8_t code = 0; code < lengthCodes-1; ++code) + { + tables.base_length[code] = length; + auto const run = 1U << tables.extra_lbits[code]; + for(unsigned n = 0; n < run; ++n) + tables.length_code[length++] = code; + } + BOOST_ASSERT(length == 0); + // Note that the length 255 (match length 258) can be represented + // in two different ways: code 284 + 5 bits or code 285, so we + // overwrite length_code[255] to use the best encoding: + tables.length_code[255] = lengthCodes-1; + + // Initialize the mapping dist (0..32K) -> dist code (0..29) + { + std::uint8_t code; + std::uint16_t dist = 0; + for(code = 0; code < 16; code++) + { + tables.base_dist[code] = dist; + auto const run = 1U << tables.extra_dbits[code]; + for(unsigned n = 0; n < run; ++n) + tables.dist_code[dist++] = code; + } + BOOST_ASSERT(dist == 256); + // from now on, all distances are divided by 128 + dist >>= 7; + for(; code < dCodes; ++code) + { + tables.base_dist[code] = dist << 7; + auto const run = 1U << (tables.extra_dbits[code]-7); + for(std::size_t n = 0; n < run; ++n) + tables.dist_code[256 + dist++] = code; + } + BOOST_ASSERT(dist == 256); + } + + // Construct the codes of the static literal tree + std::uint16_t bl_count[maxBits+1]; + std::memset(bl_count, 0, sizeof(bl_count)); + unsigned n = 0; + while (n <= 143) + tables.ltree[n++].dl = 8; + bl_count[8] += 144; + while (n <= 255) + tables.ltree[n++].dl = 9; + bl_count[9] += 112; + while (n <= 279) + tables.ltree[n++].dl = 7; + bl_count[7] += 24; + while (n <= 287) + tables.ltree[n++].dl = 8; + bl_count[8] += 8; + // Codes 286 and 287 do not exist, but we must include them in the tree + // construction to get a canonical Huffman tree (longest code all ones) + gen_codes(tables.ltree, lCodes+1, bl_count); + + for(n = 0; n < dCodes; ++n) + { + tables.dtree[n].dl = 5; + tables.dtree[n].fc = + static_cast(bi_reverse(n, 5)); + } + } + }; + static init const data; + return data.tables; +} + +void +deflate_stream:: +doReset( + int level, + int windowBits, + int memLevel, + Strategy strategy) +{ + if(level == default_size) + level = 6; + + // VFALCO What do we do about this? + // until 256-byte window bug fixed + if(windowBits == 8) + windowBits = 9; + + if(level < 0 || level > 9) + BOOST_THROW_EXCEPTION(std::invalid_argument{ + "invalid level"}); + + if(windowBits < 8 || windowBits > 15) + BOOST_THROW_EXCEPTION(std::invalid_argument{ + "invalid windowBits"}); + + if(memLevel < 1 || memLevel > max_mem_level) + BOOST_THROW_EXCEPTION(std::invalid_argument{ + "invalid memLevel"}); + + w_bits_ = windowBits; + + hash_bits_ = memLevel + 7; + + // 16K elements by default + lit_bufsize_ = 1 << (memLevel + 6); + + level_ = level; + strategy_ = strategy; + inited_ = false; +} + +void +deflate_stream:: +doReset() +{ + inited_ = false; +} + +void +deflate_stream:: +doClear() +{ + inited_ = false; + buf_.reset(); +} + +std::size_t +deflate_stream:: +doUpperBound(std::size_t sourceLen) const +{ + std::size_t complen; + std::size_t wraplen; + + /* conservative upper bound for compressed data */ + complen = sourceLen + + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5; + + /* compute wrapper length */ + wraplen = 0; + + /* if not default parameters, return conservative bound */ + if(w_bits_ != 15 || hash_bits_ != 8 + 7) + return complen + wraplen; + + /* default settings: return tight bound for that case */ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + + (sourceLen >> 25) + 13 - 6 + wraplen; +} + +void +deflate_stream:: +doTune( + int good_length, + int max_lazy, + int nice_length, + int max_chain) +{ + good_match_ = good_length; + nice_match_ = nice_length; + max_lazy_match_ = max_lazy; + max_chain_length_ = max_chain; +} + +void +deflate_stream:: +doParams(z_params& zs, int level, Strategy strategy, error_code& ec) +{ + compress_func func; + + if(level == default_size) + level = 6; + if(level < 0 || level > 9) + { + ec = error::stream_error; + return; + } + func = get_config(level_).func; + + if((strategy != strategy_ || func != get_config(level).func) && + zs.total_in != 0) + { + // Flush the last buffer: + doWrite(zs, Flush::block, ec); + if(ec == error::need_buffers && pending_ == 0) + ec = {}; + } + if(level_ != level) + { + level_ = level; + max_lazy_match_ = get_config(level).max_lazy; + good_match_ = get_config(level).good_length; + nice_match_ = get_config(level).nice_length; + max_chain_length_ = get_config(level).max_chain; + } + strategy_ = strategy; +} + +// VFALCO boost::optional param is a workaround for +// gcc "maybe uninitialized" warning +// https://github.com/boostorg/beast/issues/532 +// +void +deflate_stream:: +doWrite(z_params& zs, boost::optional flush, error_code& ec) +{ + maybe_init(); + + if(zs.next_out == 0 || (zs.next_in == 0 && zs.avail_in != 0) || + (status_ == FINISH_STATE && flush != Flush::finish)) + { + ec = error::stream_error; + return; + } + if(zs.avail_out == 0) + { + ec = error::need_buffers; + return; + } + + // value of flush param for previous deflate call + auto old_flush = boost::make_optional( + last_flush_.is_initialized(), + last_flush_ ? *last_flush_ : Flush::none); + + last_flush_ = flush; + + // Flush as much pending output as possible + if(pending_ != 0) + { + flush_pending(zs); + if(zs.avail_out == 0) + { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + last_flush_ = boost::none; + return; + } + } + else if(zs.avail_in == 0 && ( + old_flush && flush <= *old_flush + ) && flush != Flush::finish) + { + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Flush::finish, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + ec = error::need_buffers; + return; + } + + // User must not provide more input after the first FINISH: + if(status_ == FINISH_STATE && zs.avail_in != 0) + { + ec = error::need_buffers; + return; + } + + /* Start a new block or continue the current one. + */ + if(zs.avail_in != 0 || lookahead_ != 0 || + (flush != Flush::none && status_ != FINISH_STATE)) + { + block_state bstate; + + switch(strategy_) + { + case Strategy::huffman: + bstate = deflate_huff(zs, flush.get()); + break; + case Strategy::rle: + bstate = deflate_rle(zs, flush.get()); + break; + default: + { + bstate = (this->*(get_config(level_).func))(zs, flush.get()); + break; + } + } + + if(bstate == finish_started || bstate == finish_done) + { + status_ = FINISH_STATE; + } + if(bstate == need_more || bstate == finish_started) + { + if(zs.avail_out == 0) + { + last_flush_ = boost::none; /* avoid BUF_ERROR next call, see above */ + } + return; + /* If flush != Flush::none && avail_out == 0, the next call + of deflate should use the same flush parameter to make sure + that the flush is complete. So we don't have to output an + empty block here, this will be done at next call. This also + ensures that for a very small output buffer, we emit at most + one empty block. + */ + } + if(bstate == block_done) + { + if(flush == Flush::partial) + { + tr_align(); + } + else if(flush != Flush::block) + { + /* FULL_FLUSH or SYNC_FLUSH */ + tr_stored_block((char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if(flush == Flush::full) + { + clear_hash(); // forget history + if(lookahead_ == 0) + { + strstart_ = 0; + block_start_ = 0L; + insert_ = 0; + } + } + } + flush_pending(zs); + if(zs.avail_out == 0) + { + last_flush_ = boost::none; /* avoid BUF_ERROR at next call, see above */ + return; + } + } + } + + if(flush == Flush::finish) + { + ec = error::end_of_stream; + return; + } +} + +// VFALCO Warning: untested +void +deflate_stream:: +doDictionary(Byte const* dict, uInt dictLength, error_code& ec) +{ + if(lookahead_) + { + ec = error::stream_error; + return; + } + + maybe_init(); + + /* if dict would fill window, just replace the history */ + if(dictLength >= w_size_) + { + clear_hash(); + strstart_ = 0; + block_start_ = 0L; + insert_ = 0; + dict += dictLength - w_size_; /* use the tail */ + dictLength = w_size_; + } + + /* insert dict into window and hash */ + z_params zs; + zs.avail_in = dictLength; + zs.next_in = (const Byte *)dict; + zs.avail_out = 0; + zs.next_out = 0; + fill_window(zs); + while(lookahead_ >= minMatch) + { + uInt str = strstart_; + uInt n = lookahead_ - (minMatch-1); + do + { + update_hash(ins_h_, window_[str + minMatch-1]); + prev_[str & w_mask_] = head_[ins_h_]; + head_[ins_h_] = (std::uint16_t)str; + str++; + } + while(--n); + strstart_ = str; + lookahead_ = minMatch-1; + fill_window(zs); + } + strstart_ += lookahead_; + block_start_ = (long)strstart_; + insert_ = lookahead_; + lookahead_ = 0; + match_length_ = prev_length_ = minMatch-1; + match_available_ = 0; +} + +void +deflate_stream:: +doPrime(int bits, int value, error_code& ec) +{ + maybe_init(); + + if((Byte *)(d_buf_) < pending_out_ + ((Buf_size + 7) >> 3)) + { + ec = error::need_buffers; + return; + } + + do + { + int put = Buf_size - bi_valid_; + if(put > bits) + put = bits; + bi_buf_ |= (std::uint16_t)((value & ((1 << put) - 1)) << bi_valid_); + bi_valid_ += put; + tr_flush_bits(); + value >>= put; + bits -= put; + } + while(bits); +} + +void +deflate_stream:: +doPending(unsigned* value, int* bits) +{ + if(value != 0) + *value = pending_; + if(bits != 0) + *bits = bi_valid_; +} + +//-------------------------------------------------------------------------- + +// Do lazy initialization +void +deflate_stream:: +init() +{ + // Caller must set these: + // w_bits_ + // hash_bits_ + // lit_bufsize_ + // level_ + // strategy_ + + w_size_ = 1 << w_bits_; + w_mask_ = w_size_ - 1; + + hash_size_ = 1 << hash_bits_; + hash_mask_ = hash_size_ - 1; + hash_shift_ = ((hash_bits_+minMatch-1)/minMatch); + + auto const nwindow = w_size_ * 2*sizeof(Byte); + auto const nprev = w_size_ * sizeof(std::uint16_t); + auto const nhead = hash_size_ * sizeof(std::uint16_t); + auto const noverlay = lit_bufsize_ * (sizeof(std::uint16_t)+2); + auto const needed = nwindow + nprev + nhead + noverlay; + + if(! buf_ || buf_size_ != needed) + { + buf_ = boost::make_unique_noinit< + std::uint8_t[]>(needed); + buf_size_ = needed; + } + + window_ = reinterpret_cast(buf_.get()); + prev_ = reinterpret_cast(buf_.get() + nwindow); + head_ = reinterpret_cast(buf_.get() + nwindow + nprev); + + /* We overlay pending_buf_ and d_buf_ + l_buf_. This works + since the average output size for(length, distance) + codes is <= 24 bits. + */ + auto overlay = reinterpret_cast( + buf_.get() + nwindow + nprev + nhead); + + // nothing written to window_ yet + high_water_ = 0; + + pending_buf_ = + reinterpret_cast(overlay); + pending_buf_size_ = + static_cast(lit_bufsize_) * + (sizeof(std::uint16_t) + 2L); + + d_buf_ = overlay + lit_bufsize_ / sizeof(std::uint16_t); + l_buf_ = pending_buf_ + (1 + sizeof(std::uint16_t)) * lit_bufsize_; + + pending_ = 0; + pending_out_ = pending_buf_; + + status_ = BUSY_STATE; + last_flush_ = Flush::none; + + tr_init(); + lm_init(); + + inited_ = true; +} + +/* Initialize the "longest match" routines for a new zlib stream +*/ +void +deflate_stream:: +lm_init() +{ + window_size_ = (std::uint32_t)2L*w_size_; + + clear_hash(); + + /* Set the default configuration parameters: + */ + // VFALCO TODO just copy the config struct + max_lazy_match_ = get_config(level_).max_lazy; + good_match_ = get_config(level_).good_length; + nice_match_ = get_config(level_).nice_length; + max_chain_length_ = get_config(level_).max_chain; + + strstart_ = 0; + block_start_ = 0L; + lookahead_ = 0; + insert_ = 0; + match_length_ = prev_length_ = minMatch-1; + match_available_ = 0; + ins_h_ = 0; +} + +// Initialize a new block. +// +void +deflate_stream:: +init_block() +{ + for(int n = 0; n < lCodes; n++) + dyn_ltree_[n].fc = 0; + for(int n = 0; n < dCodes; n++) + dyn_dtree_[n].fc = 0; + for(int n = 0; n < blCodes; n++) + bl_tree_[n].fc = 0; + dyn_ltree_[END_BLOCK].fc = 1; + opt_len_ = 0L; + static_len_ = 0L; + last_lit_ = 0; + matches_ = 0; +} + +/* Restore the heap property by moving down the tree starting at node k, + exchanging a node with the smallest of its two sons if necessary, + stopping when the heap property is re-established (each father smaller + than its two sons). +*/ +void +deflate_stream:: +pqdownheap( + ct_data const* tree, // the tree to restore + int k) // node to move down +{ + int v = heap_[k]; + int j = k << 1; // left son of k + while(j <= heap_len_) + { + // Set j to the smallest of the two sons: + if(j < heap_len_ && + smaller(tree, heap_[j+1], heap_[j])) + j++; + // Exit if v is smaller than both sons + if(smaller(tree, v, heap_[j])) + break; + + // Exchange v with the smallest son + heap_[k] = heap_[j]; + k = j; + + // And continue down the tree, + // setting j to the left son of k + j <<= 1; + } + heap_[k] = v; +} + +/* Remove the smallest element from the heap and recreate the heap + with one less element. Updates heap and heap_len. +*/ +void +deflate_stream:: +pqremove(ct_data const* tree, int& top) +{ + top = heap_[kSmallest]; + heap_[kSmallest] = heap_[heap_len_--]; + pqdownheap(tree, kSmallest); +} + +/* Compute the optimal bit lengths for a tree and update the total bit length + for the current block. + IN assertion: the fields freq and dad are set, heap[heap_max] and + above are the tree nodes sorted by increasing frequency. + OUT assertions: the field len is set to the optimal bit length, the + array bl_count contains the frequencies for each bit length. + The length opt_len is updated; static_len is also updated if stree is + not null. +*/ +void +deflate_stream:: +gen_bitlen(tree_desc *desc) +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + ct_data const* stree = desc->stat_desc->static_tree; + std::uint8_t const *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; // heap index + int n, m; // iterate over the tree elements + int bits; // bit length + int xbits; // extra bits + std::uint16_t f; // frequency + int overflow = 0; // number of elements with bit length too large + + std::fill(&bl_count_[0], &bl_count_[maxBits+1], std::uint16_t{0}); + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[heap_[heap_max_]].dl = 0; // root of the heap + + for(h = heap_max_+1; h < HEAP_SIZE; h++) { + n = heap_[h]; + bits = tree[tree[n].dl].dl + 1; + if(bits > max_length) bits = max_length, overflow++; + // We overwrite tree[n].dl which is no longer needed + tree[n].dl = (std::uint16_t)bits; + + if(n > max_code) + continue; // not a leaf node + + bl_count_[bits]++; + xbits = 0; + if(n >= base) + xbits = extra[n-base]; + f = tree[n].fc; + opt_len_ += (std::uint32_t)f * (bits + xbits); + if(stree) + static_len_ += (std::uint32_t)f * (stree[n].dl + xbits); + } + if(overflow == 0) + return; + + // Find the first bit length which could increase: + do + { + bits = max_length-1; + while(bl_count_[bits] == 0) + bits--; + bl_count_[bits]--; // move one leaf down the tree + bl_count_[bits+1] += 2; // move one overflow item as its brother + bl_count_[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } + while(overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for(bits = max_length; bits != 0; bits--) + { + n = bl_count_[bits]; + while(n != 0) + { + m = heap_[--h]; + if(m > max_code) + continue; + if((unsigned) tree[m].dl != (unsigned) bits) + { + opt_len_ += ((long)bits - (long)tree[m].dl) *(long)tree[m].fc; + tree[m].dl = (std::uint16_t)bits; + } + n--; + } + } +} + +/* Construct one Huffman tree and assigns the code bit strings and lengths. + Update the total bit length for the current block. + IN assertion: the field freq is set for all tree elements. + OUT assertions: the fields len and code are set to the optimal bit length + and corresponding code. The length opt_len is updated; static_len is + also updated if stree is not null. The field max_code is set. +*/ +void +deflate_stream:: +build_tree(tree_desc *desc) +{ + ct_data *tree = desc->dyn_tree; + ct_data const* stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; // iterate over heap elements + int max_code = -1; // largest code with non zero frequency + int node; // new node being created + + /* Construct the initial heap, with least frequent element in + * heap[kSmallest]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + heap_len_ = 0; + heap_max_ = HEAP_SIZE; + + for(n = 0; n < elems; n++) + { + if(tree[n].fc != 0) + { + heap_[++(heap_len_)] = max_code = n; + depth_[n] = 0; + } + else + { + tree[n].dl = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while(heap_len_ < 2) + { + node = heap_[++(heap_len_)] = (max_code < 2 ? ++max_code : 0); + tree[node].fc = 1; + depth_[node] = 0; + opt_len_--; + if(stree) + static_len_ -= stree[node].dl; + // node is 0 or 1 so it does not have extra bits + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for(n = heap_len_/2; n >= 1; n--) + pqdownheap(tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do + { + pqremove(tree, n); /* n = node of least frequency */ + m = heap_[kSmallest]; /* m = node of next least frequency */ + + heap_[--(heap_max_)] = n; /* keep the nodes sorted by frequency */ + heap_[--(heap_max_)] = m; + + /* Create a new node father of n and m */ + tree[node].fc = tree[n].fc + tree[m].fc; + depth_[node] = (std::uint8_t)((depth_[n] >= depth_[m] ? + depth_[n] : depth_[m]) + 1); + tree[n].dl = tree[m].dl = (std::uint16_t)node; + /* and insert the new node in the heap */ + heap_[kSmallest] = node++; + pqdownheap(tree, kSmallest); + + } + while(heap_len_ >= 2); + + heap_[--(heap_max_)] = heap_[kSmallest]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen((tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes(tree, max_code, bl_count_); +} + +/* Scan a literal or distance tree to determine the frequencies + of the codes in the bit length tree. +*/ +void +deflate_stream:: +scan_tree( + ct_data *tree, // the tree to be scanned + int max_code) // and its largest code of non zero frequency +{ + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0].dl; // length of next code + std::uint16_t count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if(nextlen == 0) + { + max_count = 138; + min_count = 3; + } + tree[max_code+1].dl = (std::uint16_t)0xffff; // guard + + for(n = 0; n <= max_code; n++) + { + curlen = nextlen; nextlen = tree[n+1].dl; + if(++count < max_count && curlen == nextlen) + { + continue; + } + else if(count < min_count) + { + bl_tree_[curlen].fc += count; + } + else if(curlen != 0) + { + if(curlen != prevlen) bl_tree_[curlen].fc++; + bl_tree_[REP_3_6].fc++; + } + else if(count <= 10) + { + bl_tree_[REPZ_3_10].fc++; + } + else + { + bl_tree_[REPZ_11_138].fc++; + } + count = 0; + prevlen = curlen; + if(nextlen == 0) + { + max_count = 138; + min_count = 3; + } + else if(curlen == nextlen) + { + max_count = 6; + min_count = 3; + } + else + { + max_count = 7; + min_count = 4; + } + } +} + +/* Send a literal or distance tree in compressed form, + using the codes in bl_tree. +*/ +void +deflate_stream:: +send_tree( + ct_data *tree, // the tree to be scanned + int max_code) // and its largest code of non zero frequency +{ + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0].dl; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + // tree[max_code+1].dl = -1; // guard already set + if(nextlen == 0) + { + max_count = 138; + min_count = 3; + } + + for(n = 0; n <= max_code; n++) + { + curlen = nextlen; + nextlen = tree[n+1].dl; + if(++count < max_count && curlen == nextlen) + { + continue; + } + else if(count < min_count) + { + do + { + send_code(curlen, bl_tree_); + } + while (--count != 0); + } + else if(curlen != 0) + { + if(curlen != prevlen) + { + send_code(curlen, bl_tree_); + count--; + } + BOOST_ASSERT(count >= 3 && count <= 6); + send_code(REP_3_6, bl_tree_); + send_bits(count-3, 2); + } + else if(count <= 10) + { + send_code(REPZ_3_10, bl_tree_); + send_bits(count-3, 3); + } + else + { + send_code(REPZ_11_138, bl_tree_); + send_bits(count-11, 7); + } + count = 0; + prevlen = curlen; + if(nextlen == 0) + { + max_count = 138; + min_count = 3; + } + else if(curlen == nextlen) + { + max_count = 6; + min_count = 3; + } + else + { + max_count = 7; + min_count = 4; + } + } +} + +/* Construct the Huffman tree for the bit lengths and return + the index in bl_order of the last bit length code to send. +*/ +int +deflate_stream:: +build_bl_tree() +{ + int max_blindex; // index of last bit length code of non zero freq + + // Determine the bit length frequencies for literal and distance trees + scan_tree((ct_data *)dyn_ltree_, l_desc_.max_code); + scan_tree((ct_data *)dyn_dtree_, d_desc_.max_code); + + // Build the bit length tree: + build_tree((tree_desc *)(&(bl_desc_))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for(max_blindex = blCodes-1; max_blindex >= 3; max_blindex--) + { + if(bl_tree_[lut_.bl_order[max_blindex]].dl != 0) + break; + } + // Update opt_len to include the bit length tree and counts + opt_len_ += 3*(max_blindex+1) + 5+5+4; + return max_blindex; +} + +/* Send the header for a block using dynamic Huffman trees: the counts, + the lengths of the bit length codes, the literal tree and the distance + tree. + IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. +*/ +void +deflate_stream:: +send_all_trees( + int lcodes, + int dcodes, + int blcodes) // number of codes for each tree +{ + int rank; // index in bl_order + + BOOST_ASSERT(lcodes >= 257 && dcodes >= 1 && blcodes >= 4); + BOOST_ASSERT(lcodes <= lCodes && dcodes <= dCodes && blcodes <= blCodes); + send_bits(lcodes-257, 5); // not +255 as stated in appnote.txt + send_bits(dcodes-1, 5); + send_bits(blcodes-4, 4); // not -3 as stated in appnote.txt + for(rank = 0; rank < blcodes; rank++) + send_bits(bl_tree_[lut_.bl_order[rank]].dl, 3); + send_tree((ct_data *)dyn_ltree_, lcodes-1); // literal tree + send_tree((ct_data *)dyn_dtree_, dcodes-1); // distance tree +} + +/* Send the block data compressed using the given Huffman trees +*/ +void +deflate_stream:: +compress_block( + ct_data const* ltree, // literal tree + ct_data const* dtree) // distance tree +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if(last_lit_ != 0) + { + do + { + dist = d_buf_[lx]; + lc = l_buf_[lx++]; + if(dist == 0) + { + send_code(lc, ltree); /* send a literal byte */ + } + else + { + /* Here, lc is the match length - minMatch */ + code = lut_.length_code[lc]; + send_code(code+literals+1, ltree); /* send the length code */ + extra = lut_.extra_lbits[code]; + if(extra != 0) + { + lc -= lut_.base_length[code]; + send_bits(lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + BOOST_ASSERT(code < dCodes); + + send_code(code, dtree); /* send the distance code */ + extra = lut_.extra_dbits[code]; + if(extra != 0) + { + dist -= lut_.base_dist[code]; + send_bits(dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + BOOST_ASSERT((uInt)(pending_) < lit_bufsize_ + 2*lx); + } + while(lx < last_lit_); + } + + send_code(END_BLOCK, ltree); +} + +/* Check if the data type is TEXT or BINARY, using the following algorithm: + - TEXT if the two conditions below are satisfied: + a) There are no non-portable control characters belonging to the + "black list" (0..6, 14..25, 28..31). + b) There is at least one printable character belonging to the + "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). + - BINARY otherwise. + - The following partially-portable control characters form a + "gray list" that is ignored in this detection algorithm: + (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). + IN assertion: the fields fc of dyn_ltree are set. +*/ +int +deflate_stream:: +detect_data_type() +{ + /* black_mask is the bit mask of black-listed bytes + * set bits 0..6, 14..25, and 28..31 + * 0xf3ffc07f = binary 11110011111111111100000001111111 + */ + unsigned long black_mask = 0xf3ffc07fUL; + int n; + + // Check for non-textual ("black-listed") bytes. + for(n = 0; n <= 31; n++, black_mask >>= 1) + if((black_mask & 1) && (dyn_ltree_[n].fc != 0)) + return binary; + + // Check for textual ("white-listed") bytes. */ + if(dyn_ltree_[9].fc != 0 || dyn_ltree_[10].fc != 0 + || dyn_ltree_[13].fc != 0) + return text; + for(n = 32; n < literals; n++) + if(dyn_ltree_[n].fc != 0) + return text; + + /* There are no "black-listed" or "white-listed" bytes: + * this stream either is empty or has tolerated ("gray-listed") bytes only. + */ + return binary; +} + +/* Flush the bit buffer and align the output on a byte boundary +*/ +void +deflate_stream:: +bi_windup() +{ + if(bi_valid_ > 8) + put_short(bi_buf_); + else if(bi_valid_ > 0) + put_byte((Byte)bi_buf_); + bi_buf_ = 0; + bi_valid_ = 0; +} + +/* Flush the bit buffer, keeping at most 7 bits in it. +*/ +void +deflate_stream:: +bi_flush() +{ + if(bi_valid_ == 16) + { + put_short(bi_buf_); + bi_buf_ = 0; + bi_valid_ = 0; + } + else if(bi_valid_ >= 8) + { + put_byte((Byte)bi_buf_); + bi_buf_ >>= 8; + bi_valid_ -= 8; + } +} + +/* Copy a stored block, storing first the length and its + one's complement if requested. +*/ +void +deflate_stream:: +copy_block( + char *buf, // the input data + unsigned len, // its length + int header) // true if block header must be written +{ + bi_windup(); // align on byte boundary + + if(header) + { + put_short((std::uint16_t)len); + put_short((std::uint16_t)~len); + } + // VFALCO Use memcpy? + while (len--) + put_byte(*buf++); +} + +//------------------------------------------------------------------------------ + +/* Initialize the tree data structures for a new zlib stream. +*/ +void +deflate_stream:: +tr_init() +{ + l_desc_.dyn_tree = dyn_ltree_; + l_desc_.stat_desc = &lut_.l_desc; + + d_desc_.dyn_tree = dyn_dtree_; + d_desc_.stat_desc = &lut_.d_desc; + + bl_desc_.dyn_tree = bl_tree_; + bl_desc_.stat_desc = &lut_.bl_desc; + + bi_buf_ = 0; + bi_valid_ = 0; + + // Initialize the first block of the first file: + init_block(); +} + +/* Send one empty static block to give enough lookahead for inflate. + This takes 10 bits, of which 7 may remain in the bit buffer. +*/ +void +deflate_stream:: +tr_align() +{ + send_bits(STATIC_TREES<<1, 3); + send_code(END_BLOCK, lut_.ltree); + bi_flush(); +} + +/* Flush the bits in the bit buffer to pending output (leaves at most 7 bits) +*/ +void +deflate_stream:: +tr_flush_bits() +{ + bi_flush(); +} + +/* Send a stored block +*/ +void +deflate_stream:: +tr_stored_block( + char *buf, // input block + std::uint32_t stored_len, // length of input block + int last) // one if this is the last block for a file +{ + send_bits((STORED_BLOCK<<1)+last, 3); // send block type + copy_block(buf, (unsigned)stored_len, 1); // with header +} + +void +deflate_stream:: +tr_tally_dist(std::uint16_t dist, std::uint8_t len, bool& flush) +{ + d_buf_[last_lit_] = dist; + l_buf_[last_lit_++] = len; + dist--; + dyn_ltree_[lut_.length_code[len]+literals+1].fc++; + dyn_dtree_[d_code(dist)].fc++; + flush = (last_lit_ == lit_bufsize_-1); +} + +void +deflate_stream:: +tr_tally_lit(std::uint8_t c, bool& flush) +{ + d_buf_[last_lit_] = 0; + l_buf_[last_lit_++] = c; + dyn_ltree_[c].fc++; + flush = (last_lit_ == lit_bufsize_-1); +} + +//------------------------------------------------------------------------------ + +/* Determine the best encoding for the current block: dynamic trees, + static trees or store, and output the encoded block to the zip file. +*/ +void +deflate_stream:: +tr_flush_block( + z_params& zs, + char *buf, // input block, or NULL if too old + std::uint32_t stored_len, // length of input block + int last) // one if this is the last block for a file +{ + std::uint32_t opt_lenb; + std::uint32_t static_lenb; // opt_len and static_len in bytes + int max_blindex = 0; // index of last bit length code of non zero freq + + // Build the Huffman trees unless a stored block is forced + if(level_ > 0) + { + // Check if the file is binary or text + if(zs.data_type == unknown) + zs.data_type = detect_data_type(); + + // Construct the literal and distance trees + build_tree((tree_desc *)(&(l_desc_))); + + build_tree((tree_desc *)(&(d_desc_))); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (opt_len_+3+7)>>3; + static_lenb = (static_len_+3+7)>>3; + + if(static_lenb <= opt_lenb) + opt_lenb = static_lenb; + } + else + { + // VFALCO This assertion fails even in the original ZLib, + // happens with strategy == Z_HUFFMAN_ONLY, see: + // https://github.com/madler/zlib/issues/172 + + #if 0 + BOOST_ASSERT(buf); + #endif + opt_lenb = static_lenb = stored_len + 5; // force a stored block + } + +#ifdef FORCE_STORED + if(buf != (char*)0) { /* force stored block */ +#else + if(stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + tr_stored_block(buf, stored_len, last); + +#ifdef FORCE_STATIC + } + else if(static_lenb >= 0) + { + // force static trees +#else + } + else if(strategy_ == Strategy::fixed || static_lenb == opt_lenb) + { +#endif + send_bits((STATIC_TREES<<1)+last, 3); + compress_block(lut_.ltree, lut_.dtree); + } + else + { + send_bits((DYN_TREES<<1)+last, 3); + send_all_trees(l_desc_.max_code+1, d_desc_.max_code+1, + max_blindex+1); + compress_block((const ct_data *)dyn_ltree_, + (const ct_data *)dyn_dtree_); + } + /* The above check is made mod 2^32, for files larger than 512 MB + * and std::size_t implemented on 32 bits. + */ + init_block(); + + if(last) + bi_windup(); +} + +void +deflate_stream:: +fill_window(z_params& zs) +{ + unsigned n, m; + unsigned more; // Amount of free space at the end of the window. + std::uint16_t *p; + uInt wsize = w_size_; + + do + { + more = (unsigned)(window_size_ - + (std::uint32_t)lookahead_ -(std::uint32_t)strstart_); + + // VFALCO We don't support systems below 32-bit + #if 0 + // Deal with !@#$% 64K limit: + if(sizeof(int) <= 2) + { + if(more == 0 && strstart_ == 0 && lookahead_ == 0) + { + more = wsize; + } + else if(more == (unsigned)(-1)) + { + /* Very unlikely, but possible on 16 bit machine if + * strstart == 0 && lookahead == 1 (input done a byte at time) + */ + more--; + } + } + #endif + + /* If the window is almost full and there is insufficient lookahead, + move the upper half to the lower one to make room in the upper half. + */ + if(strstart_ >= wsize+max_dist()) + { + std::memcpy(window_, window_+wsize, (unsigned)wsize); + match_start_ -= wsize; + strstart_ -= wsize; // we now have strstart >= max_dist + block_start_ -= (long) wsize; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + n = hash_size_; + p = &head_[n]; + do + { + m = *--p; + *p = (std::uint16_t)(m >= wsize ? m-wsize : 0); + } + while(--n); + + n = wsize; + p = &prev_[n]; + do + { + m = *--p; + *p = (std::uint16_t)(m >= wsize ? m-wsize : 0); + /* If n is not on any hash chain, prev[n] is garbage but + its value will never be used. + */ + } + while(--n); + more += wsize; + } + if(zs.avail_in == 0) + break; + + /* If there was no sliding: + strstart <= WSIZE+max_dist-1 && lookahead <= kMinLookahead - 1 && + more == window_size - lookahead - strstart + => more >= window_size - (kMinLookahead-1 + WSIZE + max_dist-1) + => more >= window_size - 2*WSIZE + 2 + In the BIG_MEM or MMAP case (not yet supported), + window_size == input_size + kMinLookahead && + strstart + lookahead_ <= input_size => more >= kMinLookahead. + Otherwise, window_size == 2*WSIZE so more >= 2. + If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + n = read_buf(zs, window_ + strstart_ + lookahead_, more); + lookahead_ += n; + + // Initialize the hash value now that we have some input: + if(lookahead_ + insert_ >= minMatch) + { + uInt str = strstart_ - insert_; + ins_h_ = window_[str]; + update_hash(ins_h_, window_[str + 1]); + while(insert_) + { + update_hash(ins_h_, window_[str + minMatch-1]); + prev_[str & w_mask_] = head_[ins_h_]; + head_[ins_h_] = (std::uint16_t)str; + str++; + insert_--; + if(lookahead_ + insert_ < minMatch) + break; + } + } + /* If the whole input has less than minMatch bytes, ins_h is garbage, + but this is not important since only literal bytes will be emitted. + */ + } + while(lookahead_ < kMinLookahead && zs.avail_in != 0); + + /* If the kWinInit bytes after the end of the current data have never been + written, then zero those bytes in order to avoid memory check reports of + the use of uninitialized (or uninitialised as Julian writes) bytes by + the longest match routines. Update the high water mark for the next + time through here. kWinInit is set to maxMatch since the longest match + routines allow scanning to strstart + maxMatch, ignoring lookahead. + */ + if(high_water_ < window_size_) + { + std::uint32_t curr = strstart_ + (std::uint32_t)(lookahead_); + std::uint32_t winit; + + if(high_water_ < curr) + { + /* Previous high water mark below current data -- zero kWinInit + bytes or up to end of window, whichever is less. + */ + winit = window_size_ - curr; + if(winit > kWinInit) + winit = kWinInit; + std::memset(window_ + curr, 0, (unsigned)winit); + high_water_ = curr + winit; + } + else if(high_water_ < (std::uint32_t)curr + kWinInit) + { + /* High water mark at or above current data, but below current data + plus kWinInit -- zero out to current data plus kWinInit, or up + to end of window, whichever is less. + */ + winit = (std::uint32_t)curr + kWinInit - high_water_; + if(winit > window_size_ - high_water_) + winit = window_size_ - high_water_; + std::memset(window_ + high_water_, 0, (unsigned)winit); + high_water_ += winit; + } + } +} + +/* Flush as much pending output as possible. All write() output goes + through this function so some applications may wish to modify it + to avoid allocating a large strm->next_out buffer and copying into it. + (See also read_buf()). +*/ +void +deflate_stream:: +flush_pending(z_params& zs) +{ + tr_flush_bits(); + auto len = clamp(pending_, zs.avail_out); + if(len == 0) + return; + + std::memcpy(zs.next_out, pending_out_, len); + zs.next_out = + static_cast(zs.next_out) + len; + pending_out_ += len; + zs.total_out += len; + zs.avail_out -= len; + pending_ -= len; + if(pending_ == 0) + pending_out_ = pending_buf_; +} + +/* Flush the current block, with given end-of-file flag. + IN assertion: strstart is set to the end of the current match. +*/ +void +deflate_stream:: +flush_block(z_params& zs, bool last) +{ + tr_flush_block(zs, + (block_start_ >= 0L ? + (char *)&window_[(unsigned)block_start_] : + (char *)0), + (std::uint32_t)((long)strstart_ - block_start_), + last); + block_start_ = strstart_; + flush_pending(zs); +} + +/* Read a new buffer from the current input stream, update the adler32 + and total number of bytes read. All write() input goes through + this function so some applications may wish to modify it to avoid + allocating a large strm->next_in buffer and copying from it. + (See also flush_pending()). +*/ +int +deflate_stream:: +read_buf(z_params& zs, Byte *buf, unsigned size) +{ + auto len = clamp(zs.avail_in, size); + if(len == 0) + return 0; + + zs.avail_in -= len; + + std::memcpy(buf, zs.next_in, len); + zs.next_in = static_cast< + std::uint8_t const*>(zs.next_in) + len; + zs.total_in += len; + return (int)len; +} + +/* Set match_start to the longest match starting at the given string and + return its length. Matches shorter or equal to prev_length are discarded, + in which case the result is equal to prev_length and match_start is + garbage. + IN assertions: cur_match is the head of the hash chain for the current + string (strstart) and its distance is <= max_dist, and prev_length >= 1 + OUT assertion: the match length is not greater than s->lookahead_. + + For 80x86 and 680x0, an optimized version will be provided in match.asm or + match.S. The code will be functionally equivalent. +*/ +uInt +deflate_stream:: +longest_match(IPos cur_match) +{ + unsigned chain_length = max_chain_length_;/* max hash chain length */ + Byte *scan = window_ + strstart_; /* current string */ + Byte *match; /* matched string */ + int len; /* length of current match */ + int best_len = prev_length_; /* best match length so far */ + int nice_match = nice_match_; /* stop if match long enough */ + IPos limit = strstart_ > (IPos)max_dist() ? + strstart_ - (IPos)max_dist() : 0; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + std::uint16_t *prev = prev_; + uInt wmask = w_mask_; + + Byte *strend = window_ + strstart_ + maxMatch; + Byte scan_end1 = scan[best_len-1]; + Byte scan_end = scan[best_len]; + + /* The code is optimized for HASH_BITS >= 8 and maxMatch-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + BOOST_ASSERT(hash_bits_ >= 8 && maxMatch == 258); + + /* Do not waste too much time if we already have a good match: */ + if(prev_length_ >= good_match_) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if((uInt)nice_match > lookahead_) + nice_match = lookahead_; + + BOOST_ASSERT((std::uint32_t)strstart_ <= window_size_-kMinLookahead); + + do { + BOOST_ASSERT(cur_match < strstart_); + match = window_ + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2. Note that the checks below + * for insufficient lookahead only occur occasionally for performance + * reasons. Therefore uninitialized memory will be accessed, and + * conditional jumps will be made that depend on those values. + * However the length of the match is limited to the lookahead, so + * the output of deflate is not affected by the uninitialized values. + */ + if( match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) + continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + BOOST_ASSERT(*scan == *match); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do + { + } + while( *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + BOOST_ASSERT(scan <= window_+(unsigned)(window_size_-1)); + + len = maxMatch - (int)(strend - scan); + scan = strend - maxMatch; + + if(len > best_len) { + match_start_ = cur_match; + best_len = len; + if(len >= nice_match) break; + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; + } + } + while((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if((uInt)best_len <= lookahead_) + return (uInt)best_len; + return lookahead_; +} + +//------------------------------------------------------------------------------ + +/* Copy without compression as much as possible from the input stream, return + the current block state. + This function does not insert new strings in the dictionary since + uncompressible data is probably not useful. This function is used + only for the level=0 compression option. + NOTE: this function should be optimized to avoid extra copying from + window to pending_buf. +*/ +auto +deflate_stream:: +f_stored(z_params& zs, Flush flush) -> + block_state +{ + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + std::uint32_t max_block_size = 0xffff; + std::uint32_t max_start; + + if(max_block_size > pending_buf_size_ - 5) { + max_block_size = pending_buf_size_ - 5; + } + + /* Copy as much as possible from input to output: */ + for(;;) { + /* Fill the window as much as possible: */ + if(lookahead_ <= 1) { + + BOOST_ASSERT(strstart_ < w_size_+max_dist() || + block_start_ >= (long)w_size_); + + fill_window(zs); + if(lookahead_ == 0 && flush == Flush::none) + return need_more; + + if(lookahead_ == 0) break; /* flush the current block */ + } + BOOST_ASSERT(block_start_ >= 0L); + + strstart_ += lookahead_; + lookahead_ = 0; + + /* Emit a stored block if pending_buf will be full: */ + max_start = block_start_ + max_block_size; + if(strstart_ == 0 || (std::uint32_t)strstart_ >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + lookahead_ = (uInt)(strstart_ - max_start); + strstart_ = (uInt)max_start; + flush_block(zs, false); + if(zs.avail_out == 0) + return need_more; + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if(strstart_ - (uInt)block_start_ >= max_dist()) { + flush_block(zs, false); + if(zs.avail_out == 0) + return need_more; + } + } + insert_ = 0; + if(flush == Flush::finish) + { + flush_block(zs, true); + if(zs.avail_out == 0) + return finish_started; + return finish_done; + } + if((long)strstart_ > block_start_) + { + flush_block(zs, false); + if(zs.avail_out == 0) + return need_more; + } + return block_done; +} + +/* Compress as much as possible from the input stream, return the current + block state. + This function does not perform lazy evaluation of matches and inserts + new strings in the dictionary only for unmatched strings or for short + matches. It is used only for the fast compression options. +*/ +auto +deflate_stream:: +f_fast(z_params& zs, Flush flush) -> + block_state +{ + IPos hash_head; /* head of the hash chain */ + bool bflush; /* set if current block must be flushed */ + + for(;;) + { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need maxMatch bytes + * for the next match, plus minMatch bytes to insert the + * string following the next match. + */ + if(lookahead_ < kMinLookahead) + { + fill_window(zs); + if(lookahead_ < kMinLookahead && flush == Flush::none) + return need_more; + if(lookahead_ == 0) + break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + hash_head = 0; + if(lookahead_ >= minMatch) { + insert_string(hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < minMatch + */ + if(hash_head != 0 && strstart_ - hash_head <= max_dist()) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + match_length_ = longest_match (hash_head); + /* longest_match() sets match_start */ + } + if(match_length_ >= minMatch) + { + tr_tally_dist(static_cast(strstart_ - match_start_), + static_cast(match_length_ - minMatch), bflush); + + lookahead_ -= match_length_; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ + if(match_length_ <= max_lazy_match_ && + lookahead_ >= minMatch) { + match_length_--; /* string at strstart already in table */ + do + { + strstart_++; + insert_string(hash_head); + /* strstart never exceeds WSIZE-maxMatch, so there are + * always minMatch bytes ahead. + */ + } + while(--match_length_ != 0); + strstart_++; + } + else + { + strstart_ += match_length_; + match_length_ = 0; + ins_h_ = window_[strstart_]; + update_hash(ins_h_, window_[strstart_+1]); + /* If lookahead < minMatch, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } + else + { + /* No match, output a literal byte */ + tr_tally_lit(window_[strstart_], bflush); + lookahead_--; + strstart_++; + } + if(bflush) + { + flush_block(zs, false); + if(zs.avail_out == 0) + return need_more; + } + } + insert_ = strstart_ < minMatch-1 ? strstart_ : minMatch-1; + if(flush == Flush::finish) + { + flush_block(zs, true); + if(zs.avail_out == 0) + return finish_started; + return finish_done; + } + if(last_lit_) + { + flush_block(zs, false); + if(zs.avail_out == 0) + return need_more; + } + return block_done; +} + +/* Same as above, but achieves better compression. We use a lazy + evaluation for matches: a match is finally adopted only if there is + no better match at the next window position. +*/ +auto +deflate_stream:: +f_slow(z_params& zs, Flush flush) -> + block_state +{ + IPos hash_head; /* head of hash chain */ + bool bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for(;;) + { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need maxMatch bytes + * for the next match, plus minMatch bytes to insert the + * string following the next match. + */ + if(lookahead_ < kMinLookahead) + { + fill_window(zs); + if(lookahead_ < kMinLookahead && flush == Flush::none) + return need_more; + if(lookahead_ == 0) + break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + hash_head = 0; + if(lookahead_ >= minMatch) + insert_string(hash_head); + + /* Find the longest match, discarding those <= prev_length. + */ + prev_length_ = match_length_, prev_match_ = match_start_; + match_length_ = minMatch-1; + + if(hash_head != 0 && prev_length_ < max_lazy_match_ && + strstart_ - hash_head <= max_dist()) + { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + match_length_ = longest_match(hash_head); + /* longest_match() sets match_start */ + + if(match_length_ <= 5 && (strategy_ == Strategy::filtered + || (match_length_ == minMatch && + strstart_ - match_start_ > kTooFar) + )) + { + /* If prev_match is also minMatch, match_start is garbage + * but we will ignore the current match anyway. + */ + match_length_ = minMatch-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if(prev_length_ >= minMatch && match_length_ <= prev_length_) + { + /* Do not insert strings in hash table beyond this. */ + uInt max_insert = strstart_ + lookahead_ - minMatch; + + tr_tally_dist( + static_cast(strstart_ -1 - prev_match_), + static_cast(prev_length_ - minMatch), bflush); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + lookahead_ -= prev_length_-1; + prev_length_ -= 2; + do { + if(++strstart_ <= max_insert) + insert_string(hash_head); + } + while(--prev_length_ != 0); + match_available_ = 0; + match_length_ = minMatch-1; + strstart_++; + + if(bflush) + { + flush_block(zs, false); + if(zs.avail_out == 0) + return need_more; + } + + } + else if(match_available_) + { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + tr_tally_lit(window_[strstart_-1], bflush); + if(bflush) + flush_block(zs, false); + strstart_++; + lookahead_--; + if(zs.avail_out == 0) + return need_more; + } + else + { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + match_available_ = 1; + strstart_++; + lookahead_--; + } + } + BOOST_ASSERT(flush != Flush::none); + if(match_available_) + { + tr_tally_lit(window_[strstart_-1], bflush); + match_available_ = 0; + } + insert_ = strstart_ < minMatch-1 ? strstart_ : minMatch-1; + if(flush == Flush::finish) + { + flush_block(zs, true); + if(zs.avail_out == 0) + return finish_started; + return finish_done; + } + if(last_lit_) + { + flush_block(zs, false); + if(zs.avail_out == 0) + return need_more; + } + return block_done; +} + +/* For Strategy::rle, simply look for runs of bytes, generate matches only of distance + one. Do not maintain a hash table. (It will be regenerated if this run of + deflate switches away from Strategy::rle.) +*/ +auto +deflate_stream:: +f_rle(z_params& zs, Flush flush) -> + block_state +{ + bool bflush; // set if current block must be flushed + uInt prev; // byte at distance one to match + Byte *scan, *strend; // scan goes up to strend for length of run + + for(;;) + { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need maxMatch bytes + * for the longest run, plus one for the unrolled loop. + */ + if(lookahead_ <= maxMatch) { + fill_window(zs); + if(lookahead_ <= maxMatch && flush == Flush::none) { + return need_more; + } + if(lookahead_ == 0) break; /* flush the current block */ + } + + /* See how many times the previous byte repeats */ + match_length_ = 0; + if(lookahead_ >= minMatch && strstart_ > 0) { + scan = window_ + strstart_ - 1; + prev = *scan; + if(prev == *++scan && prev == *++scan && prev == *++scan) { + strend = window_ + strstart_ + maxMatch; + do { + } while(prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + scan < strend); + match_length_ = maxMatch - (int)(strend - scan); + if(match_length_ > lookahead_) + match_length_ = lookahead_; + } + BOOST_ASSERT(scan <= window_+(uInt)(window_size_-1)); + } + + /* Emit match if have run of minMatch or longer, else emit literal */ + if(match_length_ >= minMatch) { + tr_tally_dist(std::uint16_t{1}, + static_cast(match_length_ - minMatch), + bflush); + + lookahead_ -= match_length_; + strstart_ += match_length_; + match_length_ = 0; + } else { + /* No match, output a literal byte */ + tr_tally_lit(window_[strstart_], bflush); + lookahead_--; + strstart_++; + } + if(bflush) + { + flush_block(zs, false); + if(zs.avail_out == 0) + return need_more; + } + } + insert_ = 0; + if(flush == Flush::finish) + { + flush_block(zs, true); + if(zs.avail_out == 0) + return finish_started; + return finish_done; + } + if(last_lit_) + { + flush_block(zs, false); + if(zs.avail_out == 0) + return need_more; + } + return block_done; +} + +/* =========================================================================== + * For Strategy::huffman, do not look for matches. Do not maintain a hash table. + * (It will be regenerated if this run of deflate switches away from Huffman.) + */ +auto +deflate_stream:: +f_huff(z_params& zs, Flush flush) -> + block_state +{ + bool bflush; // set if current block must be flushed + + for(;;) + { + // Make sure that we have a literal to write. + if(lookahead_ == 0) + { + fill_window(zs); + if(lookahead_ == 0) + { + if(flush == Flush::none) + return need_more; + break; // flush the current block + } + } + + // Output a literal byte + match_length_ = 0; + tr_tally_lit(window_[strstart_], bflush); + lookahead_--; + strstart_++; + if(bflush) + { + flush_block(zs, false); + if(zs.avail_out == 0) + return need_more; + } + } + insert_ = 0; + if(flush == Flush::finish) + { + flush_block(zs, true); + if(zs.avail_out == 0) + return finish_started; + return finish_done; + } + if(last_lit_) + { + flush_block(zs, false); + if(zs.avail_out == 0) + return need_more; + } + return block_done; +} + +} // detail +} // zlib +} // beast +} // boost + +#endif diff --git a/include/boost/beast/zlib/detail/inflate_stream.hpp b/include/boost/beast/zlib/detail/inflate_stream.hpp index 931088ec..9fdd2ef2 100644 --- a/include/boost/beast/zlib/detail/inflate_stream.hpp +++ b/include/boost/beast/zlib/detail/inflate_stream.hpp @@ -42,6 +42,7 @@ #include #include #include +#if 0 #include #include #include @@ -49,6 +50,7 @@ #include #include #include +#endif namespace boost { namespace beast { @@ -63,9 +65,17 @@ protected: w_.reset(15); } - template void doClear(); - template void doReset(int windowBits); - template void doWrite(z_params& zs, Flush flush, error_code& ec); + BOOST_BEAST_DECL + void + doClear(); + + BOOST_BEAST_DECL + void + doReset(int windowBits); + + BOOST_BEAST_DECL + void + doWrite(z_params& zs, Flush flush, error_code& ec); void doReset() @@ -169,7 +179,7 @@ private: dists }; - template + BOOST_BEAST_DECL static void inflate_table( @@ -181,16 +191,16 @@ private: std::uint16_t* work, error_code& ec); - template + BOOST_BEAST_DECL static codes const& get_fixed_tables(); - template + BOOST_BEAST_DECL void fixedTables(); - template + BOOST_BEAST_DECL void inflate_fast(ranges& r, error_code& ec); @@ -229,1082 +239,13 @@ private: unsigned distbits_; // index bits for distcode }; -//------------------------------------------------------------------------------ - -template -void -inflate_stream:: -doReset(int windowBits) -{ - if(windowBits < 8 || windowBits > 15) - BOOST_THROW_EXCEPTION(std::domain_error{ - "windowBits out of range"}); - w_.reset(windowBits); - - bi_.flush(); - mode_ = HEAD; - last_ = 0; - dmax_ = 32768U; - lencode_ = codes_; - distcode_ = codes_; - next_ = codes_; - back_ = -1; -} - -template -void -inflate_stream:: -doClear() -{ -} - -template -void -inflate_stream:: -doWrite(z_params& zs, Flush flush, error_code& ec) -{ - ranges r; - r.in.first = static_cast< - std::uint8_t const*>(zs.next_in); - r.in.last = r.in.first + zs.avail_in; - r.in.next = r.in.first; - r.out.first = static_cast< - std::uint8_t*>(zs.next_out); - r.out.last = r.out.first + zs.avail_out; - r.out.next = r.out.first; - - auto const done = - [&] - { - /* - Return from inflate(), updating the total counts and the check value. - If there was no progress during the inflate() call, return a buffer - error. Call updatewindow() to create and/or update the window state. - Note: a memory error from inflate() is non-recoverable. - */ - - - // VFALCO TODO Don't allocate update the window unless necessary - if(/*wsize_ ||*/ (r.out.used() && mode_ < BAD && - (mode_ < CHECK || flush != Flush::finish))) - w_.write(r.out.first, r.out.used()); - - zs.next_in = r.in.next; - zs.avail_in = r.in.avail(); - zs.next_out = r.out.next; - zs.avail_out = r.out.avail(); - zs.total_in += r.in.used(); - zs.total_out += r.out.used(); - zs.data_type = bi_.size() + (last_ ? 64 : 0) + - (mode_ == TYPE ? 128 : 0) + - (mode_ == LEN_ || mode_ == COPY_ ? 256 : 0); - - if(((! r.in.used() && ! r.out.used()) || - flush == Flush::finish) && ! ec) - ec = error::need_buffers; - }; - auto const err = - [&](error e) - { - ec = e; - mode_ = BAD; - }; - - if(mode_ == TYPE) - mode_ = TYPEDO; - - for(;;) - { - switch(mode_) - { - case HEAD: - mode_ = TYPEDO; - break; - - case TYPE: - if(flush == Flush::block || flush == Flush::trees) - return done(); - // fall through - - case TYPEDO: - { - if(last_) - { - bi_.flush_byte(); - mode_ = CHECK; - break; - } - if(! bi_.fill(3, r.in.next, r.in.last)) - return done(); - std::uint8_t v; - bi_.read(v, 1); - last_ = v != 0; - bi_.read(v, 2); - switch(v) - { - case 0: - // uncompressed block - mode_ = STORED; - break; - case 1: - // fixed Huffman table - fixedTables(); - mode_ = LEN_; /* decode codes */ - if(flush == Flush::trees) - return done(); - break; - case 2: - // dynamic Huffman table - mode_ = TABLE; - break; - - default: - return err(error::invalid_block_type); - } - break; - } - - case STORED: - { - bi_.flush_byte(); - std::uint32_t v; - if(! bi_.fill(32, r.in.next, r.in.last)) - return done(); - bi_.peek(v, 32); - length_ = v & 0xffff; - if(length_ != ((v >> 16) ^ 0xffff)) - return err(error::invalid_stored_length); - // flush instead of read, otherwise - // undefined right shift behavior. - bi_.flush(); - mode_ = COPY_; - if(flush == Flush::trees) - return done(); - BOOST_FALLTHROUGH; - } - - case COPY_: - mode_ = COPY; - BOOST_FALLTHROUGH; - - case COPY: - { - auto copy = length_; - if(copy == 0) - { - mode_ = TYPE; - break; - } - copy = clamp(copy, r.in.avail()); - copy = clamp(copy, r.out.avail()); - if(copy == 0) - return done(); - std::memcpy(r.out.next, r.in.next, copy); - r.in.next += copy; - r.out.next += copy; - length_ -= copy; - break; - } - - case TABLE: - if(! bi_.fill(5 + 5 + 4, r.in.next, r.in.last)) - return done(); - bi_.read(nlen_, 5); - nlen_ += 257; - bi_.read(ndist_, 5); - ndist_ += 1; - bi_.read(ncode_, 4); - ncode_ += 4; - if(nlen_ > 286 || ndist_ > 30) - return err(error::too_many_symbols); - have_ = 0; - mode_ = LENLENS; - BOOST_FALLTHROUGH; - - case LENLENS: - { - static std::array constexpr order = {{ - 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}}; - while(have_ < ncode_) - { - if(! bi_.fill(3, r.in.next, r.in.last)) - return done(); - bi_.read(lens_[order[have_]], 3); - ++have_; - } - while(have_ < order.size()) - lens_[order[have_++]] = 0; - - next_ = &codes_[0]; - lencode_ = next_; - lenbits_ = 7; - inflate_table(build::codes, &lens_[0], - order.size(), &next_, &lenbits_, work_, ec); - if(ec) - { - mode_ = BAD; - break; - } - have_ = 0; - mode_ = CODELENS; - BOOST_FALLTHROUGH; - } - - case CODELENS: - { - while(have_ < nlen_ + ndist_) - { - std::uint16_t v; - if(! bi_.fill(lenbits_, r.in.next, r.in.last)) - return done(); - bi_.peek(v, lenbits_); - auto cp = &lencode_[v]; - if(cp->val < 16) - { - bi_.drop(cp->bits); - lens_[have_++] = cp->val; - } - else - { - std::uint16_t len; - std::uint16_t copy; - if(cp->val == 16) - { - if(! bi_.fill(cp->bits + 2, r.in.next, r.in.last)) - return done(); - bi_.drop(cp->bits); - if(have_ == 0) - return err(error::invalid_bit_length_repeat); - bi_.read(copy, 2); - len = lens_[have_ - 1]; - copy += 3; - - } - else if(cp->val == 17) - { - if(! bi_.fill(cp->bits + 3, r.in.next, r.in.last)) - return done(); - bi_.drop(cp->bits); - bi_.read(copy, 3); - len = 0; - copy += 3; - } - else - { - if(! bi_.fill(cp->bits + 7, r.in.next, r.in.last)) - return done(); - bi_.drop(cp->bits); - bi_.read(copy, 7); - len = 0; - copy += 11; - } - if(have_ + copy > nlen_ + ndist_) - return err(error::invalid_bit_length_repeat); - std::fill(&lens_[have_], &lens_[have_ + copy], len); - have_ += copy; - copy = 0; - } - } - // handle error breaks in while - if(mode_ == BAD) - break; - // check for end-of-block code (better have one) - if(lens_[256] == 0) - return err(error::missing_eob); - /* build code tables -- note: do not change the lenbits or distbits - values here (9 and 6) without reading the comments in inftrees.hpp - concerning the kEnough constants, which depend on those values */ - next_ = &codes_[0]; - lencode_ = next_; - lenbits_ = 9; - inflate_table(build::lens, &lens_[0], - nlen_, &next_, &lenbits_, work_, ec); - if(ec) - { - mode_ = BAD; - return; - } - distcode_ = next_; - distbits_ = 6; - inflate_table(build::dists, lens_ + nlen_, - ndist_, &next_, &distbits_, work_, ec); - if(ec) - { - mode_ = BAD; - return; - } - mode_ = LEN_; - if(flush == Flush::trees) - return done(); - BOOST_FALLTHROUGH; - } - - case LEN_: - mode_ = LEN; - BOOST_FALLTHROUGH; - - case LEN: - { - if(r.in.avail() >= 6 && r.out.avail() >= 258) - { - inflate_fast(r, ec); - if(ec) - { - mode_ = BAD; - return; - } - if(mode_ == TYPE) - back_ = -1; - break; - } - if(! bi_.fill(lenbits_, r.in.next, r.in.last)) - return done(); - std::uint16_t v; - back_ = 0; - bi_.peek(v, lenbits_); - auto cp = &lencode_[v]; - if(cp->op && (cp->op & 0xf0) == 0) - { - auto prev = cp; - if(! bi_.fill(prev->bits + prev->op, r.in.next, r.in.last)) - return done(); - bi_.peek(v, prev->bits + prev->op); - cp = &lencode_[prev->val + (v >> prev->bits)]; - bi_.drop(prev->bits + cp->bits); - back_ += prev->bits + cp->bits; - } - else - { - bi_.drop(cp->bits); - back_ += cp->bits; - } - length_ = cp->val; - if(cp->op == 0) - { - mode_ = LIT; - break; - } - if(cp->op & 32) - { - back_ = -1; - mode_ = TYPE; - break; - } - if(cp->op & 64) - return err(error::invalid_literal_length); - extra_ = cp->op & 15; - mode_ = LENEXT; - BOOST_FALLTHROUGH; - } - - case LENEXT: - if(extra_) - { - if(! bi_.fill(extra_, r.in.next, r.in.last)) - return done(); - std::uint16_t v; - bi_.read(v, extra_); - length_ += v; - back_ += extra_; - } - was_ = length_; - mode_ = DIST; - BOOST_FALLTHROUGH; - - case DIST: - { - if(! bi_.fill(distbits_, r.in.next, r.in.last)) - return done(); - std::uint16_t v; - bi_.peek(v, distbits_); - auto cp = &distcode_[v]; - if((cp->op & 0xf0) == 0) - { - auto prev = cp; - if(! bi_.fill(prev->bits + prev->op, r.in.next, r.in.last)) - return done(); - bi_.peek(v, prev->bits + prev->op); - cp = &distcode_[prev->val + (v >> prev->bits)]; - bi_.drop(prev->bits + cp->bits); - back_ += prev->bits + cp->bits; - } - else - { - bi_.drop(cp->bits); - back_ += cp->bits; - } - if(cp->op & 64) - return err(error::invalid_distance_code); - offset_ = cp->val; - extra_ = cp->op & 15; - mode_ = DISTEXT; - BOOST_FALLTHROUGH; - } - - case DISTEXT: - if(extra_) - { - std::uint16_t v; - if(! bi_.fill(extra_, r.in.next, r.in.last)) - return done(); - bi_.read(v, extra_); - offset_ += v; - back_ += extra_; - } -#ifdef INFLATE_STRICT - if(offset_ > dmax_) - return err(error::invalid_distance); -#endif - mode_ = MATCH; - BOOST_FALLTHROUGH; - - case MATCH: - { - if(! r.out.avail()) - return done(); - if(offset_ > r.out.used()) - { - // copy from window - auto offset = static_cast( - offset_ - r.out.used()); - if(offset > w_.size()) - return err(error::invalid_distance); - auto const n = clamp(clamp( - length_, offset), r.out.avail()); - w_.read(r.out.next, offset, n); - r.out.next += n; - length_ -= n; - } - else - { - // copy from output - auto in = r.out.next - offset_; - auto n = clamp(length_, r.out.avail()); - length_ -= n; - while(n--) - *r.out.next++ = *in++; - } - if(length_ == 0) - mode_ = LEN; - break; - } - - case LIT: - { - if(! r.out.avail()) - return done(); - auto const v = static_cast(length_); - *r.out.next++ = v; - mode_ = LEN; - break; - } - - case CHECK: - mode_ = DONE; - BOOST_FALLTHROUGH; - - case DONE: - ec = error::end_of_stream; - return done(); - - case BAD: - return done(); - - case SYNC: - default: - BOOST_THROW_EXCEPTION(std::logic_error{ - "stream error"}); - } - } -} - -//------------------------------------------------------------------------------ - -/* Build a set of tables to decode the provided canonical Huffman code. - The code lengths are lens[0..codes-1]. The result starts at *table, - whose indices are 0..2^bits-1. work is a writable array of at least - lens shorts, which is used as a work area. type is the type of code - to be generated, build::codes, build::lens, or build::dists. On - return, zero is success, -1 is an invalid code, and +1 means that - kEnough isn't enough. table on return points to the next available - entry's address. bits is the requested root table index bits, and - on return it is the actual root table index bits. It will differ if - the request is greater than the longest code or if it is less than - the shortest code. -*/ -template -void -inflate_stream:: -inflate_table( - build type, - std::uint16_t* lens, - std::size_t codes, - code** table, - unsigned *bits, - std::uint16_t* work, - error_code& ec) -{ - unsigned len; // a code's length in bits - unsigned sym; // index of code symbols - unsigned min, max; // minimum and maximum code lengths - unsigned root; // number of index bits for root table - unsigned curr; // number of index bits for current table - unsigned drop; // code bits to drop for sub-table - int left; // number of prefix codes available - unsigned used; // code entries in table used - unsigned huff; // Huffman code - unsigned incr; // for incrementing code, index - unsigned fill; // index for replicating entries - unsigned low; // low bits for current root entry - unsigned mask; // mask for low root bits - code here; // table entry for duplication - code *next; // next available space in table - std::uint16_t const* base; // base value table to use - std::uint16_t const* extra; // extra bits table to use - int end; // use base and extra for symbol > end - std::uint16_t count[15+1]; // number of codes of each length - std::uint16_t offs[15+1]; // offsets in table for each length - - // Length codes 257..285 base - static std::uint16_t constexpr lbase[31] = { - 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, - 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; - - // Length codes 257..285 extra - static std::uint16_t constexpr lext[31] = { - 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, - 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78}; - - // Distance codes 0..29 base - static std::uint16_t constexpr dbase[32] = { - 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, - 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, - 8193, 12289, 16385, 24577, 0, 0}; - - // Distance codes 0..29 extra - static std::uint16_t constexpr dext[32] = { - 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, - 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, - 28, 28, 29, 29, 64, 64}; - - /* - Process a set of code lengths to create a canonical Huffman code. The - code lengths are lens[0..codes-1]. Each length corresponds to the - symbols 0..codes-1. The Huffman code is generated by first sorting the - symbols by length from short to long, and retaining the symbol order - for codes with equal lengths. Then the code starts with all zero bits - for the first code of the shortest length, and the codes are integer - increments for the same length, and zeros are appended as the length - increases. For the deflate format, these bits are stored backwards - from their more natural integer increment ordering, and so when the - decoding tables are built in the large loop below, the integer codes - are incremented backwards. - - This routine assumes, but does not check, that all of the entries in - lens[] are in the range 0..15. The caller must assure this. - 1..15 is interpreted as that code length. zero means that that - symbol does not occur in this code. - - The codes are sorted by computing a count of codes for each length, - creating from that a table of starting indices for each length in the - sorted table, and then entering the symbols in order in the sorted - table. The sorted table is work[], with that space being provided by - the caller. - - The length counts are used for other purposes as well, i.e. finding - the minimum and maximum length codes, determining if there are any - codes at all, checking for a valid set of lengths, and looking ahead - at length counts to determine sub-table sizes when building the - decoding tables. - */ - - /* accumulate lengths for codes (assumes lens[] all in 0..15) */ - for (len = 0; len <= 15; len++) - count[len] = 0; - for (sym = 0; sym < codes; sym++) - count[lens[sym]]++; - - /* bound code lengths, force root to be within code lengths */ - root = *bits; - for (max = 15; max >= 1; max--) - if (count[max] != 0) - break; - if (root > max) - root = max; - if (max == 0) - { /* no symbols to code at all */ - here.op = (std::uint8_t)64; /* invalid code marker */ - here.bits = (std::uint8_t)1; - here.val = (std::uint16_t)0; - *(*table)++ = here; /* make a table to force an error */ - *(*table)++ = here; - *bits = 1; - return; /* no symbols, but wait for decoding to report error */ - } - for (min = 1; min < max; min++) - if (count[min] != 0) - break; - if (root < min) - root = min; - - /* check for an over-subscribed or incomplete set of lengths */ - left = 1; - for (len = 1; len <= 15; len++) - { - left <<= 1; - left -= count[len]; - if (left < 0) - { - ec = error::over_subscribed_length; - return; - } - } - if (left > 0 && (type == build::codes || max != 1)) - { - ec = error::incomplete_length_set; - return; - } - - /* generate offsets into symbol table for each length for sorting */ - offs[1] = 0; - for (len = 1; len < 15; len++) - offs[len + 1] = offs[len] + count[len]; - - /* sort symbols by length, by symbol order within each length */ - for (sym = 0; sym < codes; sym++) - if (lens[sym] != 0) - work[offs[lens[sym]]++] = (std::uint16_t)sym; - - /* - Create and fill in decoding tables. In this loop, the table being - filled is at next and has curr index bits. The code being used is huff - with length len. That code is converted to an index by dropping drop - bits off of the bottom. For codes where len is less than drop + curr, - those top drop + curr - len bits are incremented through all values to - fill the table with replicated entries. - - root is the number of index bits for the root table. When len exceeds - root, sub-tables are created pointed to by the root entry with an index - of the low root bits of huff. This is saved in low to check for when a - new sub-table should be started. drop is zero when the root table is - being filled, and drop is root when sub-tables are being filled. - - When a new sub-table is needed, it is necessary to look ahead in the - code lengths to determine what size sub-table is needed. The length - counts are used for this, and so count[] is decremented as codes are - entered in the tables. - - used keeps track of how many table entries have been allocated from the - provided *table space. It is checked for build::lens and DIST tables against - the constants kEnoughLens and kEnoughDists to guard against changes in - the initial root table size constants. See the comments in inftrees.hpp - for more information. - - sym increments through all symbols, and the loop terminates when - all codes of length max, i.e. all codes, have been processed. This - routine permits incomplete codes, so another loop after this one fills - in the rest of the decoding tables with invalid code markers. - */ - - /* set up for code type */ - switch (type) - { - case build::codes: - base = extra = work; /* dummy value--not used */ - end = 19; - break; - case build::lens: - base = lbase; - base -= 257; - extra = lext; - extra -= 257; - end = 256; - break; - default: /* build::dists */ - base = dbase; - extra = dext; - end = -1; - } - - /* initialize state for loop */ - huff = 0; /* starting code */ - sym = 0; /* starting code symbol */ - len = min; /* starting code length */ - next = *table; /* current table to fill in */ - curr = root; /* current table index bits */ - drop = 0; /* current bits to drop from code for index */ - low = (unsigned)(-1); /* trigger new sub-table when len > root */ - used = 1U << root; /* use root table entries */ - mask = used - 1; /* mask for comparing low */ - - auto const not_enough = [] - { - BOOST_THROW_EXCEPTION(std::logic_error{ - "insufficient output size when inflating tables"}); - }; - - // check available table space - if ((type == build::lens && used > kEnoughLens) || - (type == build::dists && used > kEnoughDists)) - return not_enough(); - - /* process all codes and make table entries */ - for (;;) - { - /* create table entry */ - here.bits = (std::uint8_t)(len - drop); - if ((int)(work[sym]) < end) - { - here.op = (std::uint8_t)0; - here.val = work[sym]; - } - else if ((int)(work[sym]) > end) - { - here.op = (std::uint8_t)(extra[work[sym]]); - here.val = base[work[sym]]; - } - else - { - here.op = (std::uint8_t)(32 + 64); /* end of block */ - here.val = 0; - } - - /* replicate for those indices with low len bits equal to huff */ - incr = 1U << (len - drop); - fill = 1U << curr; - min = fill; /* save offset to next table */ - do - { - fill -= incr; - next[(huff >> drop) + fill] = here; - } while (fill != 0); - - /* backwards increment the len-bit code huff */ - incr = 1U << (len - 1); - while (huff & incr) - incr >>= 1; - if (incr != 0) - { - huff &= incr - 1; - huff += incr; - } - else - huff = 0; - - /* go to next symbol, update count, len */ - sym++; - if (--(count[len]) == 0) - { - if (len == max) break; - len = lens[work[sym]]; - } - - /* create new sub-table if needed */ - if (len > root && (huff & mask) != low) - { - /* if first time, transition to sub-tables */ - if (drop == 0) - drop = root; - - /* increment past last table */ - next += min; /* here min is 1 << curr */ - - /* determine length of next table */ - curr = len - drop; - left = (int)(1 << curr); - while (curr + drop < max) - { - left -= count[curr + drop]; - if (left <= 0) break; - curr++; - left <<= 1; - } - - /* check for enough space */ - used += 1U << curr; - if ((type == build::lens && used > kEnoughLens) || - (type == build::dists && used > kEnoughDists)) - return not_enough(); - - /* point entry in root table to sub-table */ - low = huff & mask; - (*table)[low].op = (std::uint8_t)curr; - (*table)[low].bits = (std::uint8_t)root; - (*table)[low].val = (std::uint16_t)(next - *table); - } - } - - /* fill in remaining table entry if code is incomplete (guaranteed to have - at most one remaining entry, since if the code is incomplete, the - maximum code length that was allowed to get this far is one bit) */ - if (huff != 0) - { - here.op = 64; // invalid code marker - here.bits = (std::uint8_t)(len - drop); - here.val = 0; - next[huff] = here; - } - - *table += used; - *bits = root; -} - -template -auto -inflate_stream:: -get_fixed_tables() -> - codes const& -{ - struct fixed_codes : codes - { - code len_[512]; - code dist_[32]; - - fixed_codes() - { - lencode = len_; - lenbits = 9; - distcode = dist_; - distbits = 5; - - std::uint16_t lens[320]; - std::uint16_t work[288]; - - std::fill(&lens[ 0], &lens[144], std::uint16_t{8}); - std::fill(&lens[144], &lens[256], std::uint16_t{9}); - std::fill(&lens[256], &lens[280], std::uint16_t{7}); - std::fill(&lens[280], &lens[288], std::uint16_t{8}); - - { - error_code ec; - auto next = &len_[0]; - inflate_table(build::lens, - lens, 288, &next, &lenbits, work, ec); - if(ec) - BOOST_THROW_EXCEPTION(std::logic_error{ec.message()}); - } - - // VFALCO These fixups are from ZLib - len_[ 99].op = 64; - len_[227].op = 64; - len_[355].op = 64; - len_[483].op = 64; - - { - error_code ec; - auto next = &dist_[0]; - std::fill(&lens[0], &lens[32], std::uint16_t{5}); - inflate_table(build::dists, - lens, 32, &next, &distbits, work, ec); - if(ec) - BOOST_THROW_EXCEPTION(std::logic_error{ec.message()}); - } - } - }; - - static fixed_codes const fc; - return fc; -} - -template -void -inflate_stream:: -fixedTables() -{ - auto const fc = get_fixed_tables(); - lencode_ = fc.lencode; - lenbits_ = fc.lenbits; - distcode_ = fc.distcode; - distbits_ = fc.distbits; -} - -/* - Decode literal, length, and distance codes and write out the resulting - literal and match bytes until either not enough input or output is - available, an end-of-block is encountered, or a data error is encountered. - When large enough input and output buffers are supplied to inflate(), for - example, a 16K input buffer and a 64K output buffer, more than 95% of the - inflate execution time is spent in this routine. - - Entry assumptions: - - state->mode_ == LEN - zs.avail_in >= 6 - zs.avail_out >= 258 - start >= zs.avail_out - state->bits_ < 8 - - On return, state->mode_ is one of: - - LEN -- ran out of enough output space or enough available input - TYPE -- reached end of block code, inflate() to interpret next block - BAD -- error in block data - - Notes: - - - The maximum input bits used by a length/distance pair is 15 bits for the - length code, 5 bits for the length extra, 15 bits for the distance code, - and 13 bits for the distance extra. This totals 48 bits, or six bytes. - Therefore if zs.avail_in >= 6, then there is enough input to avoid - checking for available input while decoding. - - - The maximum bytes that a single length/distance pair can output is 258 - bytes, which is the maximum length that can be coded. inflate_fast() - requires zs.avail_out >= 258 for each loop to avoid checking for - output space. - - inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): - - Using bit fields for code structure - - Different op definition to avoid & for extra bits (do & for table bits) - - Three separate decoding do-loops for direct, window, and wnext == 0 - - Special case for distance > 1 copies to do overlapped load and store copy - - Explicit branch predictions (based on measured branch probabilities) - - Deferring match copy and interspersed it with decoding subsequent codes - - Swapping literal/length else - - Swapping window/direct else - - Larger unrolled copy loops (three is about right) - - Moving len -= 3 statement into middle of loop - */ -template -void -inflate_stream:: -inflate_fast(ranges& r, error_code& ec) -{ - unsigned char const* last; // have enough input while in < last - unsigned char *end; // while out < end, enough space available - std::size_t op; // code bits, operation, extra bits, or window position, window bytes to copy - unsigned len; // match length, unused bytes - unsigned dist; // match distance - unsigned const lmask = - (1U << lenbits_) - 1; // mask for first level of length codes - unsigned const dmask = - (1U << distbits_) - 1; // mask for first level of distance codes - - last = r.in.next + (r.in.avail() - 5); - end = r.out.next + (r.out.avail() - 257); - - /* decode literals and length/distances until end-of-block or not enough - input data or output space */ - do - { - if(bi_.size() < 15) - bi_.fill_16(r.in.next); - auto cp = &lencode_[bi_.peek_fast() & lmask]; - dolen: - bi_.drop(cp->bits); - op = (unsigned)(cp->op); - if(op == 0) - { - // literal - *r.out.next++ = (unsigned char)(cp->val); - } - else if(op & 16) - { - // length base - len = (unsigned)(cp->val); - op &= 15; // number of extra bits - if(op) - { - if(bi_.size() < op) - bi_.fill_8(r.in.next); - len += (unsigned)bi_.peek_fast() & ((1U << op) - 1); - bi_.drop(op); - } - if(bi_.size() < 15) - bi_.fill_16(r.in.next); - cp = &distcode_[bi_.peek_fast() & dmask]; - dodist: - bi_.drop(cp->bits); - op = (unsigned)(cp->op); - if(op & 16) - { - // distance base - dist = (unsigned)(cp->val); - op &= 15; // number of extra bits - if(bi_.size() < op) - { - bi_.fill_8(r.in.next); - if(bi_.size() < op) - bi_.fill_8(r.in.next); - } - dist += (unsigned)bi_.peek_fast() & ((1U << op) - 1); -#ifdef INFLATE_STRICT - if(dist > dmax_) - { - ec = error::invalid_distance; - mode_ = BAD; - break; - } -#endif - bi_.drop(op); - - op = r.out.used(); - if(dist > op) - { - // copy from window - op = dist - op; // distance back in window - if(op > w_.size()) - { - ec = error::invalid_distance; - mode_ = BAD; - break; - } - auto const n = clamp(len, op); - w_.read(r.out.next, op, n); - r.out.next += n; - len -= n; - } - if(len > 0) - { - // copy from output - auto in = r.out.next - dist; - auto n = clamp(len, r.out.avail()); - len -= n; - while(n--) - *r.out.next++ = *in++; - } - } - else if((op & 64) == 0) - { - // 2nd level distance code - cp = &distcode_[cp->val + (bi_.peek_fast() & ((1U << op) - 1))]; - goto dodist; - } - else - { - ec = error::invalid_distance_code; - mode_ = BAD; - break; - } - } - else if((op & 64) == 0) - { - // 2nd level length code - cp = &lencode_[cp->val + (bi_.peek_fast() & ((1U << op) - 1))]; - goto dolen; - } - else if(op & 32) - { - // end-of-block - mode_ = TYPE; - break; - } - else - { - ec = error::invalid_literal_length; - mode_ = BAD; - break; - } - } - while(r.in.next < last && r.out.next < end); - - // return unused bytes (on entry, bits < 8, so in won't go too far back) - bi_.rewind(r.in.next); -} - } // detail } // zlib } // beast } // boost +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif + #endif diff --git a/include/boost/beast/zlib/detail/inflate_stream.ipp b/include/boost/beast/zlib/detail/inflate_stream.ipp new file mode 100644 index 00000000..37955b9c --- /dev/null +++ b/include/boost/beast/zlib/detail/inflate_stream.ipp @@ -0,0 +1,1118 @@ +// +// 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) +// +// Official repository: https://github.com/boostorg/beast +// +// This is a derivative work based on Zlib, copyright below: +/* + Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 + (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). +*/ + +#ifndef BOOST_BEAST_ZLIB_DETAIL_INFLATE_STREAM_IPP +#define BOOST_BEAST_ZLIB_DETAIL_INFLATE_STREAM_IPP + +#include +#include +#include + +namespace boost { +namespace beast { +namespace zlib { +namespace detail { + +void +inflate_stream:: +doClear() +{ +} + +void +inflate_stream:: +doReset(int windowBits) +{ + if(windowBits < 8 || windowBits > 15) + BOOST_THROW_EXCEPTION(std::domain_error{ + "windowBits out of range"}); + w_.reset(windowBits); + + bi_.flush(); + mode_ = HEAD; + last_ = 0; + dmax_ = 32768U; + lencode_ = codes_; + distcode_ = codes_; + next_ = codes_; + back_ = -1; +} + +void +inflate_stream:: +doWrite(z_params& zs, Flush flush, error_code& ec) +{ + ranges r; + r.in.first = static_cast< + std::uint8_t const*>(zs.next_in); + r.in.last = r.in.first + zs.avail_in; + r.in.next = r.in.first; + r.out.first = static_cast< + std::uint8_t*>(zs.next_out); + r.out.last = r.out.first + zs.avail_out; + r.out.next = r.out.first; + + auto const done = + [&] + { + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + + + // VFALCO TODO Don't allocate update the window unless necessary + if(/*wsize_ ||*/ (r.out.used() && mode_ < BAD && + (mode_ < CHECK || flush != Flush::finish))) + w_.write(r.out.first, r.out.used()); + + zs.next_in = r.in.next; + zs.avail_in = r.in.avail(); + zs.next_out = r.out.next; + zs.avail_out = r.out.avail(); + zs.total_in += r.in.used(); + zs.total_out += r.out.used(); + zs.data_type = bi_.size() + (last_ ? 64 : 0) + + (mode_ == TYPE ? 128 : 0) + + (mode_ == LEN_ || mode_ == COPY_ ? 256 : 0); + + if(((! r.in.used() && ! r.out.used()) || + flush == Flush::finish) && ! ec) + ec = error::need_buffers; + }; + auto const err = + [&](error e) + { + ec = e; + mode_ = BAD; + }; + + if(mode_ == TYPE) + mode_ = TYPEDO; + + for(;;) + { + switch(mode_) + { + case HEAD: + mode_ = TYPEDO; + break; + + case TYPE: + if(flush == Flush::block || flush == Flush::trees) + return done(); + // fall through + + case TYPEDO: + { + if(last_) + { + bi_.flush_byte(); + mode_ = CHECK; + break; + } + if(! bi_.fill(3, r.in.next, r.in.last)) + return done(); + std::uint8_t v; + bi_.read(v, 1); + last_ = v != 0; + bi_.read(v, 2); + switch(v) + { + case 0: + // uncompressed block + mode_ = STORED; + break; + case 1: + // fixed Huffman table + fixedTables(); + mode_ = LEN_; /* decode codes */ + if(flush == Flush::trees) + return done(); + break; + case 2: + // dynamic Huffman table + mode_ = TABLE; + break; + + default: + return err(error::invalid_block_type); + } + break; + } + + case STORED: + { + bi_.flush_byte(); + std::uint32_t v; + if(! bi_.fill(32, r.in.next, r.in.last)) + return done(); + bi_.peek(v, 32); + length_ = v & 0xffff; + if(length_ != ((v >> 16) ^ 0xffff)) + return err(error::invalid_stored_length); + // flush instead of read, otherwise + // undefined right shift behavior. + bi_.flush(); + mode_ = COPY_; + if(flush == Flush::trees) + return done(); + BOOST_FALLTHROUGH; + } + + case COPY_: + mode_ = COPY; + BOOST_FALLTHROUGH; + + case COPY: + { + auto copy = length_; + if(copy == 0) + { + mode_ = TYPE; + break; + } + copy = clamp(copy, r.in.avail()); + copy = clamp(copy, r.out.avail()); + if(copy == 0) + return done(); + std::memcpy(r.out.next, r.in.next, copy); + r.in.next += copy; + r.out.next += copy; + length_ -= copy; + break; + } + + case TABLE: + if(! bi_.fill(5 + 5 + 4, r.in.next, r.in.last)) + return done(); + bi_.read(nlen_, 5); + nlen_ += 257; + bi_.read(ndist_, 5); + ndist_ += 1; + bi_.read(ncode_, 4); + ncode_ += 4; + if(nlen_ > 286 || ndist_ > 30) + return err(error::too_many_symbols); + have_ = 0; + mode_ = LENLENS; + BOOST_FALLTHROUGH; + + case LENLENS: + { + static std::array constexpr order = {{ + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}}; + while(have_ < ncode_) + { + if(! bi_.fill(3, r.in.next, r.in.last)) + return done(); + bi_.read(lens_[order[have_]], 3); + ++have_; + } + while(have_ < order.size()) + lens_[order[have_++]] = 0; + + next_ = &codes_[0]; + lencode_ = next_; + lenbits_ = 7; + inflate_table(build::codes, &lens_[0], + order.size(), &next_, &lenbits_, work_, ec); + if(ec) + { + mode_ = BAD; + break; + } + have_ = 0; + mode_ = CODELENS; + BOOST_FALLTHROUGH; + } + + case CODELENS: + { + while(have_ < nlen_ + ndist_) + { + std::uint16_t v; + if(! bi_.fill(lenbits_, r.in.next, r.in.last)) + return done(); + bi_.peek(v, lenbits_); + auto cp = &lencode_[v]; + if(cp->val < 16) + { + bi_.drop(cp->bits); + lens_[have_++] = cp->val; + } + else + { + std::uint16_t len; + std::uint16_t copy; + if(cp->val == 16) + { + if(! bi_.fill(cp->bits + 2, r.in.next, r.in.last)) + return done(); + bi_.drop(cp->bits); + if(have_ == 0) + return err(error::invalid_bit_length_repeat); + bi_.read(copy, 2); + len = lens_[have_ - 1]; + copy += 3; + + } + else if(cp->val == 17) + { + if(! bi_.fill(cp->bits + 3, r.in.next, r.in.last)) + return done(); + bi_.drop(cp->bits); + bi_.read(copy, 3); + len = 0; + copy += 3; + } + else + { + if(! bi_.fill(cp->bits + 7, r.in.next, r.in.last)) + return done(); + bi_.drop(cp->bits); + bi_.read(copy, 7); + len = 0; + copy += 11; + } + if(have_ + copy > nlen_ + ndist_) + return err(error::invalid_bit_length_repeat); + std::fill(&lens_[have_], &lens_[have_ + copy], len); + have_ += copy; + copy = 0; + } + } + // handle error breaks in while + if(mode_ == BAD) + break; + // check for end-of-block code (better have one) + if(lens_[256] == 0) + return err(error::missing_eob); + /* build code tables -- note: do not change the lenbits or distbits + values here (9 and 6) without reading the comments in inftrees.hpp + concerning the kEnough constants, which depend on those values */ + next_ = &codes_[0]; + lencode_ = next_; + lenbits_ = 9; + inflate_table(build::lens, &lens_[0], + nlen_, &next_, &lenbits_, work_, ec); + if(ec) + { + mode_ = BAD; + return; + } + distcode_ = next_; + distbits_ = 6; + inflate_table(build::dists, lens_ + nlen_, + ndist_, &next_, &distbits_, work_, ec); + if(ec) + { + mode_ = BAD; + return; + } + mode_ = LEN_; + if(flush == Flush::trees) + return done(); + BOOST_FALLTHROUGH; + } + + case LEN_: + mode_ = LEN; + BOOST_FALLTHROUGH; + + case LEN: + { + if(r.in.avail() >= 6 && r.out.avail() >= 258) + { + inflate_fast(r, ec); + if(ec) + { + mode_ = BAD; + return; + } + if(mode_ == TYPE) + back_ = -1; + break; + } + if(! bi_.fill(lenbits_, r.in.next, r.in.last)) + return done(); + std::uint16_t v; + back_ = 0; + bi_.peek(v, lenbits_); + auto cp = &lencode_[v]; + if(cp->op && (cp->op & 0xf0) == 0) + { + auto prev = cp; + if(! bi_.fill(prev->bits + prev->op, r.in.next, r.in.last)) + return done(); + bi_.peek(v, prev->bits + prev->op); + cp = &lencode_[prev->val + (v >> prev->bits)]; + bi_.drop(prev->bits + cp->bits); + back_ += prev->bits + cp->bits; + } + else + { + bi_.drop(cp->bits); + back_ += cp->bits; + } + length_ = cp->val; + if(cp->op == 0) + { + mode_ = LIT; + break; + } + if(cp->op & 32) + { + back_ = -1; + mode_ = TYPE; + break; + } + if(cp->op & 64) + return err(error::invalid_literal_length); + extra_ = cp->op & 15; + mode_ = LENEXT; + BOOST_FALLTHROUGH; + } + + case LENEXT: + if(extra_) + { + if(! bi_.fill(extra_, r.in.next, r.in.last)) + return done(); + std::uint16_t v; + bi_.read(v, extra_); + length_ += v; + back_ += extra_; + } + was_ = length_; + mode_ = DIST; + BOOST_FALLTHROUGH; + + case DIST: + { + if(! bi_.fill(distbits_, r.in.next, r.in.last)) + return done(); + std::uint16_t v; + bi_.peek(v, distbits_); + auto cp = &distcode_[v]; + if((cp->op & 0xf0) == 0) + { + auto prev = cp; + if(! bi_.fill(prev->bits + prev->op, r.in.next, r.in.last)) + return done(); + bi_.peek(v, prev->bits + prev->op); + cp = &distcode_[prev->val + (v >> prev->bits)]; + bi_.drop(prev->bits + cp->bits); + back_ += prev->bits + cp->bits; + } + else + { + bi_.drop(cp->bits); + back_ += cp->bits; + } + if(cp->op & 64) + return err(error::invalid_distance_code); + offset_ = cp->val; + extra_ = cp->op & 15; + mode_ = DISTEXT; + BOOST_FALLTHROUGH; + } + + case DISTEXT: + if(extra_) + { + std::uint16_t v; + if(! bi_.fill(extra_, r.in.next, r.in.last)) + return done(); + bi_.read(v, extra_); + offset_ += v; + back_ += extra_; + } +#ifdef INFLATE_STRICT + if(offset_ > dmax_) + return err(error::invalid_distance); +#endif + mode_ = MATCH; + BOOST_FALLTHROUGH; + + case MATCH: + { + if(! r.out.avail()) + return done(); + if(offset_ > r.out.used()) + { + // copy from window + auto offset = static_cast( + offset_ - r.out.used()); + if(offset > w_.size()) + return err(error::invalid_distance); + auto const n = clamp(clamp( + length_, offset), r.out.avail()); + w_.read(r.out.next, offset, n); + r.out.next += n; + length_ -= n; + } + else + { + // copy from output + auto in = r.out.next - offset_; + auto n = clamp(length_, r.out.avail()); + length_ -= n; + while(n--) + *r.out.next++ = *in++; + } + if(length_ == 0) + mode_ = LEN; + break; + } + + case LIT: + { + if(! r.out.avail()) + return done(); + auto const v = static_cast(length_); + *r.out.next++ = v; + mode_ = LEN; + break; + } + + case CHECK: + mode_ = DONE; + BOOST_FALLTHROUGH; + + case DONE: + ec = error::end_of_stream; + return done(); + + case BAD: + return done(); + + case SYNC: + default: + BOOST_THROW_EXCEPTION(std::logic_error{ + "stream error"}); + } + } +} + +//------------------------------------------------------------------------------ + +/* Build a set of tables to decode the provided canonical Huffman code. + The code lengths are lens[0..codes-1]. The result starts at *table, + whose indices are 0..2^bits-1. work is a writable array of at least + lens shorts, which is used as a work area. type is the type of code + to be generated, build::codes, build::lens, or build::dists. On + return, zero is success, -1 is an invalid code, and +1 means that + kEnough isn't enough. table on return points to the next available + entry's address. bits is the requested root table index bits, and + on return it is the actual root table index bits. It will differ if + the request is greater than the longest code or if it is less than + the shortest code. +*/ +void +inflate_stream:: +inflate_table( + build type, + std::uint16_t* lens, + std::size_t codes, + code** table, + unsigned *bits, + std::uint16_t* work, + error_code& ec) +{ + unsigned len; // a code's length in bits + unsigned sym; // index of code symbols + unsigned min, max; // minimum and maximum code lengths + unsigned root; // number of index bits for root table + unsigned curr; // number of index bits for current table + unsigned drop; // code bits to drop for sub-table + int left; // number of prefix codes available + unsigned used; // code entries in table used + unsigned huff; // Huffman code + unsigned incr; // for incrementing code, index + unsigned fill; // index for replicating entries + unsigned low; // low bits for current root entry + unsigned mask; // mask for low root bits + code here; // table entry for duplication + code *next; // next available space in table + std::uint16_t const* base; // base value table to use + std::uint16_t const* extra; // extra bits table to use + int end; // use base and extra for symbol > end + std::uint16_t count[15+1]; // number of codes of each length + std::uint16_t offs[15+1]; // offsets in table for each length + + // Length codes 257..285 base + static std::uint16_t constexpr lbase[31] = { + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + + // Length codes 257..285 extra + static std::uint16_t constexpr lext[31] = { + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78}; + + // Distance codes 0..29 base + static std::uint16_t constexpr dbase[32] = { + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0}; + + // Distance codes 0..29 extra + static std::uint16_t constexpr dext[32] = { + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64}; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..15. The caller must assure this. + 1..15 is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..15) */ + for (len = 0; len <= 15; len++) + count[len] = 0; + for (sym = 0; sym < codes; sym++) + count[lens[sym]]++; + + /* bound code lengths, force root to be within code lengths */ + root = *bits; + for (max = 15; max >= 1; max--) + if (count[max] != 0) + break; + if (root > max) + root = max; + if (max == 0) + { /* no symbols to code at all */ + here.op = (std::uint8_t)64; /* invalid code marker */ + here.bits = (std::uint8_t)1; + here.val = (std::uint16_t)0; + *(*table)++ = here; /* make a table to force an error */ + *(*table)++ = here; + *bits = 1; + return; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min < max; min++) + if (count[min] != 0) + break; + if (root < min) + root = min; + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= 15; len++) + { + left <<= 1; + left -= count[len]; + if (left < 0) + { + ec = error::over_subscribed_length; + return; + } + } + if (left > 0 && (type == build::codes || max != 1)) + { + ec = error::incomplete_length_set; + return; + } + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < 15; len++) + offs[len + 1] = offs[len] + count[len]; + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) + if (lens[sym] != 0) + work[offs[lens[sym]]++] = (std::uint16_t)sym; + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked for build::lens and DIST tables against + the constants kEnoughLens and kEnoughDists to guard against changes in + the initial root table size constants. See the comments in inftrees.hpp + for more information. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + switch (type) + { + case build::codes: + base = extra = work; /* dummy value--not used */ + end = 19; + break; + case build::lens: + base = lbase; + base -= 257; + extra = lext; + extra -= 257; + end = 256; + break; + default: /* build::dists */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize state for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = *table; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = (unsigned)(-1); /* trigger new sub-table when len > root */ + used = 1U << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + auto const not_enough = [] + { + BOOST_THROW_EXCEPTION(std::logic_error{ + "insufficient output size when inflating tables"}); + }; + + // check available table space + if ((type == build::lens && used > kEnoughLens) || + (type == build::dists && used > kEnoughDists)) + return not_enough(); + + /* process all codes and make table entries */ + for (;;) + { + /* create table entry */ + here.bits = (std::uint8_t)(len - drop); + if ((int)(work[sym]) < end) + { + here.op = (std::uint8_t)0; + here.val = work[sym]; + } + else if ((int)(work[sym]) > end) + { + here.op = (std::uint8_t)(extra[work[sym]]); + here.val = base[work[sym]]; + } + else + { + here.op = (std::uint8_t)(32 + 64); /* end of block */ + here.val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1U << (len - drop); + fill = 1U << curr; + min = fill; /* save offset to next table */ + do + { + fill -= incr; + next[(huff >> drop) + fill] = here; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) + { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + + /* go to next symbol, update count, len */ + sym++; + if (--(count[len]) == 0) + { + if (len == max) break; + len = lens[work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) + { + /* if first time, transition to sub-tables */ + if (drop == 0) + drop = root; + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = (int)(1 << curr); + while (curr + drop < max) + { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1U << curr; + if ((type == build::lens && used > kEnoughLens) || + (type == build::dists && used > kEnoughDists)) + return not_enough(); + + /* point entry in root table to sub-table */ + low = huff & mask; + (*table)[low].op = (std::uint8_t)curr; + (*table)[low].bits = (std::uint8_t)root; + (*table)[low].val = (std::uint16_t)(next - *table); + } + } + + /* fill in remaining table entry if code is incomplete (guaranteed to have + at most one remaining entry, since if the code is incomplete, the + maximum code length that was allowed to get this far is one bit) */ + if (huff != 0) + { + here.op = 64; // invalid code marker + here.bits = (std::uint8_t)(len - drop); + here.val = 0; + next[huff] = here; + } + + *table += used; + *bits = root; +} + +auto +inflate_stream:: +get_fixed_tables() -> + codes const& +{ + struct fixed_codes : codes + { + code len_[512]; + code dist_[32]; + + fixed_codes() + { + lencode = len_; + lenbits = 9; + distcode = dist_; + distbits = 5; + + std::uint16_t lens[320]; + std::uint16_t work[288]; + + std::fill(&lens[ 0], &lens[144], std::uint16_t{8}); + std::fill(&lens[144], &lens[256], std::uint16_t{9}); + std::fill(&lens[256], &lens[280], std::uint16_t{7}); + std::fill(&lens[280], &lens[288], std::uint16_t{8}); + + { + error_code ec; + auto next = &len_[0]; + inflate_table(build::lens, + lens, 288, &next, &lenbits, work, ec); + if(ec) + BOOST_THROW_EXCEPTION(std::logic_error{ec.message()}); + } + + // VFALCO These fixups are from ZLib + len_[ 99].op = 64; + len_[227].op = 64; + len_[355].op = 64; + len_[483].op = 64; + + { + error_code ec; + auto next = &dist_[0]; + std::fill(&lens[0], &lens[32], std::uint16_t{5}); + inflate_table(build::dists, + lens, 32, &next, &distbits, work, ec); + if(ec) + BOOST_THROW_EXCEPTION(std::logic_error{ec.message()}); + } + } + }; + + static fixed_codes const fc; + return fc; +} + +void +inflate_stream:: +fixedTables() +{ + auto const fc = get_fixed_tables(); + lencode_ = fc.lencode; + lenbits_ = fc.lenbits; + distcode_ = fc.distcode; + distbits_ = fc.distbits; +} + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode_ == LEN + zs.avail_in >= 6 + zs.avail_out >= 258 + start >= zs.avail_out + state->bits_ < 8 + + On return, state->mode_ is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if zs.avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires zs.avail_out >= 258 for each loop to avoid checking for + output space. + + inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): + - Using bit fields for code structure + - Different op definition to avoid & for extra bits (do & for table bits) + - Three separate decoding do-loops for direct, window, and wnext == 0 + - Special case for distance > 1 copies to do overlapped load and store copy + - Explicit branch predictions (based on measured branch probabilities) + - Deferring match copy and interspersed it with decoding subsequent codes + - Swapping literal/length else + - Swapping window/direct else + - Larger unrolled copy loops (three is about right) + - Moving len -= 3 statement into middle of loop + */ +void +inflate_stream:: +inflate_fast(ranges& r, error_code& ec) +{ + unsigned char const* last; // have enough input while in < last + unsigned char *end; // while out < end, enough space available + std::size_t op; // code bits, operation, extra bits, or window position, window bytes to copy + unsigned len; // match length, unused bytes + unsigned dist; // match distance + unsigned const lmask = + (1U << lenbits_) - 1; // mask for first level of length codes + unsigned const dmask = + (1U << distbits_) - 1; // mask for first level of distance codes + + last = r.in.next + (r.in.avail() - 5); + end = r.out.next + (r.out.avail() - 257); + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + do + { + if(bi_.size() < 15) + bi_.fill_16(r.in.next); + auto cp = &lencode_[bi_.peek_fast() & lmask]; + dolen: + bi_.drop(cp->bits); + op = (unsigned)(cp->op); + if(op == 0) + { + // literal + *r.out.next++ = (unsigned char)(cp->val); + } + else if(op & 16) + { + // length base + len = (unsigned)(cp->val); + op &= 15; // number of extra bits + if(op) + { + if(bi_.size() < op) + bi_.fill_8(r.in.next); + len += (unsigned)bi_.peek_fast() & ((1U << op) - 1); + bi_.drop(op); + } + if(bi_.size() < 15) + bi_.fill_16(r.in.next); + cp = &distcode_[bi_.peek_fast() & dmask]; + dodist: + bi_.drop(cp->bits); + op = (unsigned)(cp->op); + if(op & 16) + { + // distance base + dist = (unsigned)(cp->val); + op &= 15; // number of extra bits + if(bi_.size() < op) + { + bi_.fill_8(r.in.next); + if(bi_.size() < op) + bi_.fill_8(r.in.next); + } + dist += (unsigned)bi_.peek_fast() & ((1U << op) - 1); +#ifdef INFLATE_STRICT + if(dist > dmax_) + { + ec = error::invalid_distance; + mode_ = BAD; + break; + } +#endif + bi_.drop(op); + + op = r.out.used(); + if(dist > op) + { + // copy from window + op = dist - op; // distance back in window + if(op > w_.size()) + { + ec = error::invalid_distance; + mode_ = BAD; + break; + } + auto const n = clamp(len, op); + w_.read(r.out.next, op, n); + r.out.next += n; + len -= n; + } + if(len > 0) + { + // copy from output + auto in = r.out.next - dist; + auto n = clamp(len, r.out.avail()); + len -= n; + while(n--) + *r.out.next++ = *in++; + } + } + else if((op & 64) == 0) + { + // 2nd level distance code + cp = &distcode_[cp->val + (bi_.peek_fast() & ((1U << op) - 1))]; + goto dodist; + } + else + { + ec = error::invalid_distance_code; + mode_ = BAD; + break; + } + } + else if((op & 64) == 0) + { + // 2nd level length code + cp = &lencode_[cp->val + (bi_.peek_fast() & ((1U << op) - 1))]; + goto dolen; + } + else if(op & 32) + { + // end-of-block + mode_ = TYPE; + break; + } + else + { + ec = error::invalid_literal_length; + mode_ = BAD; + break; + } + } + while(r.in.next < last && r.out.next < end); + + // return unused bytes (on entry, bits < 8, so in won't go too far back) + bi_.rewind(r.in.next); +} + +} // detail +} // zlib +} // beast +} // boost + +#endif diff --git a/include/boost/beast/zlib/detail/ranges.hpp b/include/boost/beast/zlib/detail/ranges.hpp index c309dfa6..d9d1e94a 100644 --- a/include/boost/beast/zlib/detail/ranges.hpp +++ b/include/boost/beast/zlib/detail/ranges.hpp @@ -87,9 +87,7 @@ struct ranges // Clamp u to v where u and v are different types template -inline -U -clamp(U u, V v) +U clamp(U u, V v) { if(u > v) u = static_cast(v); diff --git a/include/boost/beast/zlib/detail/window.hpp b/include/boost/beast/zlib/detail/window.hpp index 4aa9e6ad..44495de0 100644 --- a/include/boost/beast/zlib/detail/window.hpp +++ b/include/boost/beast/zlib/detail/window.hpp @@ -76,88 +76,72 @@ public: } void - reset(int bits); - - void - read(std::uint8_t* out, std::size_t pos, std::size_t n); - - template - void - write(std::uint8_t const* in, std::size_t n); -}; - -inline -void -window:: -reset(int bits) -{ - if(bits_ != bits) - { - p_.reset(); - bits_ = static_cast(bits); - capacity_ = 1U << bits_; - } - i_ = 0; - size_ = 0; -} - -inline -void -window:: -read(std::uint8_t* out, std::size_t pos, std::size_t n) -{ - if(i_ >= size_) - { - // window is contiguous - std::memcpy(out, &p_[i_ - pos], n); - return; - } - auto i = ((i_ - pos) + capacity_) % capacity_; - auto m = capacity_ - i; - if(n <= m) - { - std::memcpy(out, &p_[i], n); - return; - } - std::memcpy(out, &p_[i], m); - out += m; - std::memcpy(out, &p_[0], n - m); -} - -template -void -window:: -write(std::uint8_t const* in, std::size_t n) -{ - if(! p_) - p_ = boost::make_unique< - std::uint8_t[]>(capacity_); - if(n >= capacity_) + reset(int bits) { + if(bits_ != bits) + { + p_.reset(); + bits_ = static_cast(bits); + capacity_ = 1U << bits_; + } i_ = 0; - size_ = capacity_; - std::memcpy(&p_[0], in + (n - size_), size_); - return; + size_ = 0; } - if(i_ + n <= capacity_) - { - std::memcpy(&p_[i_], in, n); - if(size_ >= capacity_ - n) - size_ = capacity_; - else - size_ = static_cast(size_ + n); - i_ = static_cast( - (i_ + n) % capacity_); - return; + void + read(std::uint8_t* out, std::size_t pos, std::size_t n) + { + if(i_ >= size_) + { + // window is contiguous + std::memcpy(out, &p_[i_ - pos], n); + return; + } + auto i = ((i_ - pos) + capacity_) % capacity_; + auto m = capacity_ - i; + if(n <= m) + { + std::memcpy(out, &p_[i], n); + return; + } + std::memcpy(out, &p_[i], m); + out += m; + std::memcpy(out, &p_[0], n - m); } - auto m = capacity_ - i_; - std::memcpy(&p_[i_], in, m); - in += m; - i_ = static_cast(n - m); - std::memcpy(&p_[0], in, i_); - size_ = capacity_; -} + + void + write(std::uint8_t const* in, std::size_t n) + { + if(! p_) + p_ = boost::make_unique< + std::uint8_t[]>(capacity_); + if(n >= capacity_) + { + i_ = 0; + size_ = capacity_; + std::memcpy(&p_[0], in + (n - size_), size_); + return; + } + if(i_ + n <= capacity_) + { + std::memcpy(&p_[i_], in, n); + if(size_ >= capacity_ - n) + size_ = capacity_; + else + size_ = static_cast(size_ + n); + + i_ = static_cast( + (i_ + n) % capacity_); + return; + } + auto m = capacity_ - i_; + std::memcpy(&p_[i_], in, m); + in += m; + i_ = static_cast(n - m); + std::memcpy(&p_[0], in, i_); + size_ = capacity_; + } +}; } // detail } // zlib diff --git a/include/boost/beast/zlib/error.hpp b/include/boost/beast/zlib/error.hpp index 40d1a9e8..43790560 100644 --- a/include/boost/beast/zlib/error.hpp +++ b/include/boost/beast/zlib/error.hpp @@ -6,17 +6,6 @@ // // Official repository: https://github.com/boostorg/beast // - -#ifndef BOOST_BEAST_ZLIB_ERROR_HPP -#define BOOST_BEAST_ZLIB_ERROR_HPP - -#include -#include - -namespace boost { -namespace beast { -namespace zlib { - // This is a derivative work based on Zlib, copyright below: /* Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler @@ -45,7 +34,17 @@ namespace zlib { (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). */ -/** Error codes returned by the codec. +#ifndef BOOST_BEAST_ZLIB_ERROR_HPP +#define BOOST_BEAST_ZLIB_ERROR_HPP + +#include +#include + +namespace boost { +namespace beast { +namespace zlib { + +/** Error codes returned by the deflate codecs. */ enum class error { @@ -133,7 +132,7 @@ enum class error } // beast } // boost -#include +#include #endif diff --git a/include/boost/beast/zlib/impl/error.hpp b/include/boost/beast/zlib/impl/error.hpp new file mode 100644 index 00000000..03cf0db2 --- /dev/null +++ b/include/boost/beast/zlib/impl/error.hpp @@ -0,0 +1,67 @@ +// +// 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) +// +// Official repository: https://github.com/boostorg/beast +// + +// This is a derivative work based on Zlib, copyright below: +/* + Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 + (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). +*/ + +#ifndef BOOST_BEAST_ZLIB_IMPL_ERROR_HPP +#define BOOST_BEAST_ZLIB_IMPL_ERROR_HPP + +namespace boost { +namespace system { +template<> +struct is_error_code_enum<::boost::beast::zlib::error> +{ + static bool const value = true; +}; +} // system +} // boost + +namespace boost { +namespace beast { +namespace zlib { + +BOOST_BEAST_DECL +error_code +make_error_code(error ev); + +} // zlib +} // beast +} // boost + +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif + +#endif diff --git a/include/boost/beast/zlib/impl/error.ipp b/include/boost/beast/zlib/impl/error.ipp index 655615a1..75310bfc 100644 --- a/include/boost/beast/zlib/impl/error.ipp +++ b/include/boost/beast/zlib/impl/error.ipp @@ -6,6 +6,7 @@ // // Official repository: https://github.com/boostorg/beast // + // This is a derivative work based on Zlib, copyright below: /* Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler @@ -37,30 +38,21 @@ #ifndef BOOST_BEAST_ZLIB_IMPL_ERROR_IPP #define BOOST_BEAST_ZLIB_IMPL_ERROR_IPP -#include +#include #include namespace boost { - -namespace system { -template<> -struct is_error_code_enum -{ - static bool const value = true; -}; -} // system - namespace beast { namespace zlib { namespace detail { -class zlib_error_category : public error_category +class error_codes : public error_category { public: const char* name() const noexcept override { - return "beast.zlib"; + return "boost.beast.zlib"; } std::string @@ -114,23 +106,14 @@ public: } }; -inline -error_category const& -get_error_category() -{ - static zlib_error_category const cat{}; - return cat; -} - } // detail -inline error_code make_error_code(error ev) { - return error_code{ - static_cast::type>(ev), - detail::get_error_category()}; + static detail::error_codes const cat{}; + return error_code{static_cast< + std::underlying_type::type>(ev), cat}; } } // zlib diff --git a/include/boost/beast/zlib/inflate_stream.hpp b/include/boost/beast/zlib/inflate_stream.hpp index ee112a75..11c842e0 100644 --- a/include/boost/beast/zlib/inflate_stream.hpp +++ b/include/boost/beast/zlib/inflate_stream.hpp @@ -6,13 +6,6 @@ // // Official repository: https://github.com/boostorg/beast // - -#ifndef BOOST_BEAST_ZLIB_INFLATE_STREAM_HPP -#define BOOST_BEAST_ZLIB_INFLATE_STREAM_HPP - -#include -#include - // This is a derivative work based on Zlib, copyright below: /* Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler @@ -41,6 +34,12 @@ (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). */ +#ifndef BOOST_BEAST_ZLIB_INFLATE_STREAM_HPP +#define BOOST_BEAST_ZLIB_INFLATE_STREAM_HPP + +#include +#include + namespace boost { namespace beast { namespace zlib { diff --git a/include/boost/beast/zlib/zlib.hpp b/include/boost/beast/zlib/zlib.hpp index 8a7e44de..aa19a223 100644 --- a/include/boost/beast/zlib/zlib.hpp +++ b/include/boost/beast/zlib/zlib.hpp @@ -6,14 +6,6 @@ // // Official repository: https://github.com/boostorg/beast // - -#ifndef BOOST_BEAST_ZLIB_ZLIB_HPP -#define BOOST_BEAST_ZLIB_ZLIB_HPP - -#include -#include -#include - // This is a derivative work based on Zlib, copyright below: /* Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler @@ -42,6 +34,13 @@ (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). */ +#ifndef BOOST_BEAST_ZLIB_ZLIB_HPP +#define BOOST_BEAST_ZLIB_ZLIB_HPP + +#include +#include +#include + namespace boost { namespace beast { namespace zlib { diff --git a/test/beast/core/_detail_sha1.cpp b/test/beast/core/_detail_sha1.cpp index 3c93d586..165ddf71 100644 --- a/test/beast/core/_detail_sha1.cpp +++ b/test/beast/core/_detail_sha1.cpp @@ -7,6 +7,7 @@ // Official repository: https://github.com/boostorg/beast // +#include #include #include #include diff --git a/test/beast/core/basic_stream.cpp b/test/beast/core/basic_stream.cpp index 2f2b50dc..7a7c88b1 100644 --- a/test/beast/core/basic_stream.cpp +++ b/test/beast/core/basic_stream.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -24,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -1218,7 +1220,6 @@ public: { return {}; } - void process_http_1 (tcp_stream& stream, net::yield_context yield) { flat_buffer buffer; diff --git a/test/beast/http/error.cpp b/test/beast/http/error.cpp index 3b1c9c6d..ed71bb63 100644 --- a/test/beast/http/error.cpp +++ b/test/beast/http/error.cpp @@ -24,11 +24,12 @@ public: check(char const* name, error ev) { auto const ec = make_error_code(ev); - auto const& cat = - make_error_code(static_cast(0)).category(); + auto const& cat = make_error_code( + static_cast(0)).category(); BEAST_EXPECT(std::string(ec.category().name()) == name); BEAST_EXPECT(! ec.message().empty()); - BEAST_EXPECT(std::addressof(ec.category()) == std::addressof(cat)); + BEAST_EXPECT( + std::addressof(ec.category()) == std::addressof(cat)); BEAST_EXPECT(cat.equivalent( static_cast::type>(ev), ec.category().default_error_condition( diff --git a/test/beast/websocket/read2.cpp b/test/beast/websocket/read2.cpp index 5d7b06f1..0b1bb230 100644 --- a/test/beast/websocket/read2.cpp +++ b/test/beast/websocket/read2.cpp @@ -470,8 +470,9 @@ public: if(se.code() == test::error::test_failure) throw; BEAST_EXPECTS(se.code().category() == - zlib::detail::get_error_category(), - se.code().message()); + make_error_code(static_cast< + zlib::error>(0)).category(), + se.code().message()); } catch(...) { diff --git a/test/beast/zlib/error.cpp b/test/beast/zlib/error.cpp index 96167316..8d290551 100644 --- a/test/beast/zlib/error.cpp +++ b/test/beast/zlib/error.cpp @@ -23,38 +23,40 @@ public: void check(char const* name, error ev) { auto const ec = make_error_code(ev); + auto const& cat = make_error_code( + static_cast(0)).category(); BEAST_EXPECT(std::string{ec.category().name()} == name); BEAST_EXPECT(! ec.message().empty()); - BEAST_EXPECT(std::addressof(ec.category()) == - std::addressof(detail::get_error_category())); - BEAST_EXPECT(detail::get_error_category().equivalent( + BEAST_EXPECT( + std::addressof(ec.category()) == std::addressof(cat)); + BEAST_EXPECT(cat.equivalent( static_cast::type>(ev), ec.category().default_error_condition( static_cast::type>(ev)))); - BEAST_EXPECT(detail::get_error_category().equivalent( - ec, static_cast::type>(ev))); + BEAST_EXPECT(cat.equivalent(ec, + static_cast::type>(ev))); } void run() override { - check("beast.zlib", error::need_buffers); - check("beast.zlib", error::end_of_stream); - check("beast.zlib", error::stream_error); + check("boost.beast.zlib", error::need_buffers); + check("boost.beast.zlib", error::end_of_stream); + check("boost.beast.zlib", error::stream_error); - check("beast.zlib", error::invalid_block_type); - check("beast.zlib", error::invalid_stored_length); - check("beast.zlib", error::too_many_symbols); - check("beast.zlib", error::invalid_code_lenths); - check("beast.zlib", error::invalid_bit_length_repeat); - check("beast.zlib", error::missing_eob); - check("beast.zlib", error::invalid_literal_length); - check("beast.zlib", error::invalid_distance_code); - check("beast.zlib", error::invalid_distance); + check("boost.beast.zlib", error::invalid_block_type); + check("boost.beast.zlib", error::invalid_stored_length); + check("boost.beast.zlib", error::too_many_symbols); + check("boost.beast.zlib", error::invalid_code_lenths); + check("boost.beast.zlib", error::invalid_bit_length_repeat); + check("boost.beast.zlib", error::missing_eob); + check("boost.beast.zlib", error::invalid_literal_length); + check("boost.beast.zlib", error::invalid_distance_code); + check("boost.beast.zlib", error::invalid_distance); - check("beast.zlib", error::over_subscribed_length); - check("beast.zlib", error::incomplete_length_set); + check("boost.beast.zlib", error::over_subscribed_length); + check("boost.beast.zlib", error::incomplete_length_set); - check("beast.zlib", error::general); + check("boost.beast.zlib", error::general); } }; diff --git a/test/asio.cpp b/test/lib_asio.cpp similarity index 85% rename from test/asio.cpp rename to test/lib_asio.cpp index 24b52d2c..b89c37d7 100644 --- a/test/asio.cpp +++ b/test/lib_asio.cpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) diff --git a/test/lib_beast.cpp b/test/lib_beast.cpp new file mode 100644 index 00000000..4b377eff --- /dev/null +++ b/test/lib_beast.cpp @@ -0,0 +1,15 @@ +// +// 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) +// +// Official repository: https://github.com/boostorg/beast +// + +// This file is used to build the asio stbeastatic library, +// used with BOOST_BEAST_SPLIT_COMPILATION which helps +// reduce compilation time + +#include +//#include