diff --git a/build/Jamfile b/build/Jamfile index 31273e6b..61e30a76 100644 --- a/build/Jamfile +++ b/build/Jamfile @@ -8,7 +8,6 @@ # import ac ; -import path ; import ../../config/checks/config : requires ; using zlib ; @@ -30,15 +29,13 @@ project boost/http_proto $(c11-requires) shared:BOOST_HTTP_PROTO_DYN_LINK=1 static:BOOST_HTTP_PROTO_STATIC_LINK=1 - [ ac.check-library /zlib//zlib : /zlib//zlib BOOST_HTTP_PROTO_HAS_ZLIB BOOST_HTTP_PROTO_ZLIB_SOURCE : ] - BOOST_HTTP_PROTO_SOURCE : usage-requirements shared:BOOST_HTTP_PROTO_DYN_LINK=1 static:BOOST_HTTP_PROTO_STATIC_LINK=1 : source-location $(HTTP_PROTO_ROOT) ; -alias http_proto_sources : [ path.glob-tree $(HTTP_PROTO_ROOT)/src : *.cpp ] ; +alias http_proto_sources : [ glob-tree-ex ./src : *.cpp ] ; explicit http_proto_sources ; @@ -47,24 +44,23 @@ lib boost_http_proto : requirements /boost//buffers /boost//url + BOOST_HTTP_PROTO_SOURCE : usage-requirements /boost//buffers /boost//url ; -alias http_proto_zlib_sources : [ path.glob-tree $(HTTP_PROTO_ROOT)/src_zlib : *.cpp ] ; +alias http_proto_zlib_sources : [ glob-tree-ex ./src_zlib : *.cpp ] ; explicit http_proto_zlib_sources ; lib boost_http_proto_zlib : http_proto_zlib_sources : requirements - /boost//url /boost/http_proto//boost_http_proto [ ac.check-library /zlib//zlib : /zlib//zlib : no ] + BOOST_HTTP_PROTO_ZLIB_SOURCE : usage-requirements - /boost//url - /boost/http_proto//boost_http_proto /zlib//zlib BOOST_HTTP_PROTO_HAS_ZLIB ; diff --git a/include/boost/http_proto/service/impl/zlib_service.hpp b/include/boost/http_proto/service/impl/zlib_service.hpp new file mode 100644 index 00000000..7e4cbbe6 --- /dev/null +++ b/include/boost/http_proto/service/impl/zlib_service.hpp @@ -0,0 +1,72 @@ +// +// Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2024 Mohammad Nejati +// +// 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/cppalliance/http_proto +// + +#ifndef BOOST_HTTP_PROTO_SERVICE_IMPL_ZLIB_SERVICE_HPP +#define BOOST_HTTP_PROTO_SERVICE_IMPL_ZLIB_SERVICE_HPP + +#include + +namespace boost { + +namespace system { +template<> +struct is_error_code_enum< + ::boost::http_proto::zlib::error> +{ + static bool const value = true; +}; +} // system + +namespace http_proto { +namespace zlib { + +namespace detail { + +struct BOOST_SYMBOL_VISIBLE + error_cat_type + : system::error_category +{ + BOOST_HTTP_PROTO_DECL const char* name( + ) const noexcept override; + BOOST_HTTP_PROTO_DECL bool failed( + int) const noexcept override; + BOOST_HTTP_PROTO_DECL std::string message( + int) const override; + BOOST_HTTP_PROTO_DECL char const* message( + int, char*, std::size_t + ) const noexcept override; + BOOST_SYSTEM_CONSTEXPR error_cat_type() + : error_category(0xe6c6d0215d1d6e22) + { + } +}; + +BOOST_HTTP_PROTO_DECL extern + error_cat_type error_cat; + +} // detail + +inline +BOOST_SYSTEM_CONSTEXPR +system::error_code +make_error_code( + error ev) noexcept +{ + return system::error_code{ + static_cast::type>(ev), + detail::error_cat}; +} + +} // zip +} // http_proto +} // boost + +#endif diff --git a/include/boost/http_proto/service/zlib_service.hpp b/include/boost/http_proto/service/zlib_service.hpp index a272b565..6e4f5eb5 100644 --- a/include/boost/http_proto/service/zlib_service.hpp +++ b/include/boost/http_proto/service/zlib_service.hpp @@ -1,6 +1,7 @@ // // Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com) // Copyright (c) 2024 Christian Mazakas +// Copyright (c) 2024 Mohammad Nejati // // 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) @@ -16,9 +17,6 @@ #include #include -#include -#include - namespace boost { namespace http_proto { namespace zlib { @@ -29,8 +27,6 @@ struct decoder_config unsigned mem_level = 8; }; -//------------------------------------------------ - constexpr inline std::size_t @@ -54,42 +50,101 @@ encoding_size_hint(decoder_config cfg = {}) noexcept (6 * 1024); } -struct BOOST_HTTP_PROTO_ZLIB_DECL +/** Error codes returned from compression/decompression functions. + + Negative values are errors, positive values are used + for special but normal events. +*/ +enum class error +{ + ok = 0, + stream_end = 1, + need_dict = 2, + errno_ = -1, + stream_err = -2, + data_err = -3, + mem_err = -4, + buf_err = -5, + version_err = -6 +}; + +/// Flush methods. +enum class flush +{ + none, + partial, + sync, + full, + finish, + block, + trees +}; + +/** Input and output buffers. + + The application must update `next_in` and `avail_in` when `avail_in` + has dropped to zero. It must update `next_out` and `avail_out` when + `avail_out` has dropped to zero. +*/ +struct params +{ + /// Next input byte + void const* next_in; + + /// Number of bytes available at `next_in` + std::size_t avail_in; + + /// Next output byte + void* next_out; + + /// Number of bytes remaining free at `next_out` + std::size_t avail_out; +}; + +/// Abstract interface for deflator/inflator streams. +struct stream +{ + /** Call the underling compression/decompression algorithm. + + @param p The input and output buffers. + + @param f The flush method. + + @return The result of operation that contains a value + of @ref error. + */ + virtual system::error_code + write(params& p, flush f) noexcept = 0; +}; + +/** Provides in-memory compression and decompression functions + using zlib underneath. +*/ +struct BOOST_HTTP_PROTO_DECL service : http_proto::service { - struct stream - { - enum class flush - { - none, - partial, - sync, - full, - finish, - block, - trees - }; - - struct results - { - std::size_t out_bytes; - std::size_t in_bytes; - system::error_code ec; - bool finished; - }; - - virtual results - write( - buffers::mutable_buffer out, - buffers::const_buffer in, - flush) noexcept = 0; - }; - virtual std::size_t space_needed() const noexcept = 0; + /** Create a deflator stream by calling zlib `deflateInit2()`. + + @param ws A reference to the workspace used for constructing the + deflator stream object and for storage used by zlib. + + @param level The compression level. + + @param window_bits The window size. + + @param mem_level Specifies how much memory should be allocated + for the internal compression state. + + @return A reference to the created deflator stream. + + @throws std::length_error If there is insufficient free space in + @ref `http_proto::detail::workspace`. + */ virtual stream& make_deflator( http_proto::detail::workspace& ws, @@ -97,17 +152,40 @@ struct BOOST_HTTP_PROTO_ZLIB_DECL int window_bits, int mem_level) const = 0; + /** Create an inflator stream by calling zlib `inflateInit2()`. + + @param ws A reference to the workspace used for constructing the + inflator stream object and for storage used by zlib. + + @param window_bits The window size. + + @return A reference to the created inflator stream. + + @throws std::length_error If there is insufficient free space in + @ref `http_proto::detail::workspace`. + */ virtual stream& make_inflator( http_proto::detail::workspace& ws, int window_bits) const = 0; }; -void BOOST_HTTP_PROTO_ZLIB_DECL +/** Installs a zlib service on the provided context. + + @param ctx A reference to the @ref context where the service + will be installed. + + @throw std::invalid_argument If the zlib service already + exist on the context. +*/ +BOOST_HTTP_PROTO_ZLIB_DECL +void install_service(context& ctx); } // zlib } // http_proto } // boost +#include + #endif diff --git a/include/boost/http_proto/src_brotli.hpp b/include/boost/http_proto/src_brotli.hpp deleted file mode 100644 index c616ab0c..00000000 --- a/include/boost/http_proto/src_brotli.hpp +++ /dev/null @@ -1,26 +0,0 @@ -// -// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.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/cppalliance/http_proto -// - -#ifndef BOOST_HTTP_PROTO_SRC_BROTLI_HPP -#define BOOST_HTTP_PROTO_SRC_BROTLI_HPP - -/* - -This file is meant to be included once, -in a translation unit of the program. - -*/ - -#ifndef BOOST_HTTP_PROTO_EXT_SOURCE -#define BOOST_HTTP_PROTO_EXT_SOURCE -#endif - -//#include - -#endif diff --git a/include/boost/http_proto/src_zlib.hpp b/include/boost/http_proto/src_zlib.hpp deleted file mode 100644 index db797db2..00000000 --- a/include/boost/http_proto/src_zlib.hpp +++ /dev/null @@ -1,26 +0,0 @@ -// -// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.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/cppalliance/http_proto -// - -#ifndef BOOST_HTTP_PROTO_SRC_ZLIB_HPP -#define BOOST_HTTP_PROTO_SRC_ZLIB_HPP - -/* - -This file is meant to be included once, -in a translation unit of the program. - -*/ - -#ifndef BOOST_HTTP_PROTO_ZLIB_SOURCE -#define BOOST_HTTP_PROTO_ZLIB_SOURCE -#endif - -#include - -#endif diff --git a/src/serializer.cpp b/src/serializer.cpp index b56a2480..d42ae57f 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -1,6 +1,7 @@ // // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) // Copyright (c) 2024 Christian Mazakas +// Copyright (c) 2024 Mohammad Nejati // // 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) @@ -12,13 +13,16 @@ #include #include #include + +#include "detail/filter.hpp" + #include #include #include #include + #include -#include "detail/filter.hpp" namespace boost { namespace http_proto { @@ -26,8 +30,7 @@ namespace { class deflator_filter : public http_proto::detail::filter { - using stream_t = zlib::service::stream; - stream_t& deflator_; + zlib::stream& deflator_; public: deflator_filter( @@ -46,30 +49,36 @@ class deflator_filter bool more) override { auto flush = - more ? stream_t::flush::none : stream_t::flush::finish; + more ? zlib::flush::none : zlib::flush::finish; filter::results results; for(;;) { - auto r = deflator_.write(out, in, flush); + auto params = zlib::params{in.data(), in.size(), + out.data(), out.size() }; + results.ec = deflator_.write(params, flush); - results.out_bytes += r.out_bytes; - results.in_bytes += r.in_bytes; - results.ec = r.ec; - results.finished = r.finished; + results.in_bytes += in.size() - params.avail_in; + results.out_bytes += out.size() - params.avail_out; - if(r.ec || r.finished) + if(results.ec.failed()) return results; - out = buffers::sans_prefix(out, r.out_bytes); - in = buffers::sans_prefix(in, r.in_bytes); + if(results.ec == zlib::error::stream_end) + { + results.finished = true; + return results; + } + + in = buffers::suffix(in, params.avail_in); + out = buffers::suffix(out, params.avail_out); if(in.size() == 0) { - if(r.out_bytes == 0) + // TODO: is this necessary? + if(results.out_bytes == 0) { - // TODO: is this necessary? - flush = stream_t::flush::sync; + flush = zlib::flush::sync; continue; } return results; diff --git a/src/service/zlib_service.cpp b/src/service/zlib_service.cpp new file mode 100644 index 00000000..7422090c --- /dev/null +++ b/src/service/zlib_service.cpp @@ -0,0 +1,82 @@ +// +// Copyright (c) 2024 Mohammad Nejati +// +// 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/cppalliance/http_proto +// + +#include + +namespace boost { +namespace http_proto { +namespace zlib { +namespace detail { + +const char* +error_cat_type:: +name() const noexcept +{ + return "boost.http.proto.zlib"; +} + +bool +error_cat_type:: +failed(int ev) const noexcept +{ + return ev < 0; +} + +std::string +error_cat_type:: +message(int ev) const +{ + return message(ev, nullptr, 0); +} + +char const* +error_cat_type:: +message( + int ev, + char*, + std::size_t) const noexcept +{ + switch(static_cast(ev)) + { + case error::ok: return "Z_OK"; + case error::stream_end: return "Z_STREAM_END"; + case error::need_dict: return "Z_NEED_DICT"; + case error::errno_: return "Z_ERRNO"; + case error::stream_err: return "Z_STREAM_ERROR"; + case error::data_err: return "Z_DATA_ERROR"; + case error::mem_err: return "Z_MEM_ERROR"; + case error::buf_err: return "Z_BUF_ERROR"; + case error::version_err: return "Z_VERSION_ERROR"; + default: + return "unknown"; + } +} + +// msvc 14.0 has a bug that warns about inability +// to use constexpr construction here, even though +// there's no constexpr construction +#if defined(_MSC_VER) && _MSC_VER <= 1900 +# pragma warning( push ) +# pragma warning( disable : 4592 ) +#endif + +#if defined(__cpp_constinit) && __cpp_constinit >= 201907L +constinit error_cat_type error_cat; +#else +error_cat_type error_cat; +#endif + +#if defined(_MSC_VER) && _MSC_VER <= 1900 +# pragma warning( pop ) +#endif + +} // detail +} // zlib +} // http_proto +} // boost diff --git a/src_zlib/service/zlib_service.cpp b/src_zlib/service/zlib_service.cpp index 946f5e1c..61a09494 100644 --- a/src_zlib/service/zlib_service.cpp +++ b/src_zlib/service/zlib_service.cpp @@ -1,5 +1,6 @@ // // Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2024 Mohammad Nejati // // 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) @@ -7,14 +8,9 @@ // Official repository: https://github.com/cppalliance/http_proto // -#ifndef BOOST_HTTP_PROTO_SERVICE_IMPL_ZLIB_SERVICE_IPP -#define BOOST_HTTP_PROTO_SERVICE_IMPL_ZLIB_SERVICE_IPP - -#include #include #include -#include #include #include #include @@ -24,95 +20,8 @@ namespace boost { namespace http_proto { namespace zlib { -namespace { - -/* - DEFLATE Compressed Data Format Specification version 1.3 - https://www.rfc-editor.org/rfc/rfc1951 -*/ -enum class error -{ - ok = 0, - stream_end = 1, - need_dict = 2, - errno_ = -1, - stream_err = -2, - data_err = -3, - mem_err = -4, - buf_err = -5, - version_err = -6 -}; - -} // namespace -} // zlib -} // http_proto -namespace system { -template<> -struct is_error_code_enum< - ::boost::http_proto::zlib::error> -{ - static bool const value = true; -}; -} // system -namespace http_proto { -namespace zlib { namespace { -struct error_cat_type - : system::error_category -{ - BOOST_SYSTEM_CONSTEXPR - error_cat_type() noexcept - : error_category( - 0xe6c6d0215d1d6e22) - { - } - - const char* - name() const noexcept override - { - return "boost.http.proto.zlib"; - } - - std::string - message( int ev ) const override - { - return message( ev, nullptr, 0 ); - } - - char const* - message( - int ev, - char*, - std::size_t) const noexcept override - { - switch(static_cast(ev)) - { - case error::ok: return "Z_OK"; - case error::stream_end: return "Z_STREAM_END"; - case error::need_dict: return "Z_NEED_DICT"; - case error::errno_: return "Z_ERRNO"; - case error::stream_err: return "Z_STREAM_ERROR"; - case error::data_err: return "Z_DATA_ERROR"; - case error::mem_err: return "Z_MEM_ERROR"; - case error::buf_err: return "Z_BUF_ERROR"; - case error::version_err: return "Z_VERSION_ERROR"; - default: - return "unknown"; - } - } -}; - -system::error_code -make_error_code( - error ev) noexcept -{ - static BOOST_SYSTEM_CONSTEXPR - error_cat_type cat{}; - return system::error_code{static_cast< - std::underlying_type< - error>::type>(ev), cat}; -} BOOST_NOINLINE BOOST_NORETURN void @@ -222,7 +131,7 @@ class probe std::size_t n_ = 0; }; -void* zalloc_impl( +void* zalloc( void* opaque, unsigned items, unsigned size) @@ -242,131 +151,117 @@ void* zalloc_impl( } } -void zfree_impl(void* /* opaque */, void* /* addr */) +void zfree(void* /* opaque */, void* /* addr */) { // we call ws_.clear() before the serializer is reused // so all the allocations are passively freed } -} // namespace -struct service_impl - : public service +static ::uInt +clamp(std::size_t x) noexcept { - using key_type = service; + if(x >= (std::numeric_limits<::uInt>::max)()) + return (std::numeric_limits<::uInt>::max)(); + return static_cast<::uInt>(x); +} + +void +sync(z_stream* zs, params const& p) noexcept +{ + zs->next_in = reinterpret_cast<::Bytef*>( + const_cast(p.next_in)); + zs->avail_in = clamp(p.avail_in); + zs->next_out = reinterpret_cast<::Bytef*>(p.next_out); + zs->avail_out = clamp(p.avail_out); +} + +void +sync(z_stream const& zs, params* p) noexcept +{ + p->next_in = zs.next_in; + p->avail_in -= clamp(p->avail_in) - zs.avail_in; + p->next_out = zs.next_out; + p->avail_out -= clamp(p->avail_out) - zs.avail_out; +} + +class deflator + : public stream +{ + z_stream zs_; - static ::uInt - clamp(std::size_t x) noexcept +public: + deflator( + http_proto::detail::workspace& ws, + int level, + int window_bits, + int mem_level) { - if(x >= (std::numeric_limits<::uInt>::max)()) - return (std::numeric_limits<::uInt>::max)(); - return static_cast<::uInt>(x); + zs_.zalloc = &zalloc; + zs_.zfree = &zfree; + zs_.opaque = &ws; + + auto ret = deflateInit2(&zs_, level, Z_DEFLATED, + window_bits, mem_level, Z_DEFAULT_STRATEGY); + if(ret != Z_OK) + throw_zlib_error(ret); } - static void - sync( - z_stream* zs, - buffers::mutable_buffer const& out, - buffers::const_buffer const& in) noexcept + system::error_code + write(params& p, flush f) noexcept override { - zs->next_in = reinterpret_cast< - unsigned char*>(const_cast(in.data())); - zs->avail_in = clamp(in.size()); - zs->next_out = reinterpret_cast< - unsigned char*>(out.data()); - zs->avail_out = clamp(out.size()); + sync(&zs_, p); + auto ret = deflate(&zs_, static_cast(f)); + sync(zs_, &p); + return static_cast(ret); } +}; + +class inflator + : public stream +{ + z_stream zs_; - static stream::results - make_results( - z_stream const& zs, - buffers::mutable_buffer const& out, - buffers::const_buffer const& in, - int ret) noexcept +public: + inflator( + http_proto::detail::workspace& ws, + int window_bits) { - return { - clamp(out.size()) - zs.avail_out, - clamp(in.size()) - zs.avail_in, - ret < 0 ? static_cast(ret) : system::error_code{}, - ret == Z_STREAM_END }; + zs_.zalloc = &zalloc; + zs_.zfree = &zfree; + zs_.opaque = &ws; + + auto ret = inflateInit2(&zs_, window_bits); + if(ret != Z_OK) + throw_zlib_error(ret); } - class deflator - : public stream - { - z_stream zs_; - - public: - deflator( - http_proto::detail::workspace& ws, - int level, - int window_bits, - int mem_level) - { - zs_.zalloc = &zalloc_impl; - zs_.zfree = &zfree_impl; - zs_.opaque = &ws; - - auto ret = deflateInit2(&zs_, level, Z_DEFLATED, - window_bits, mem_level, Z_DEFAULT_STRATEGY); - if(ret != Z_OK) - throw_zlib_error(ret); - } - - virtual results - write( - buffers::mutable_buffer out, - buffers::const_buffer in, - flush flush) noexcept override - { - sync(&zs_, out, in); - return make_results(zs_, out, in, - deflate(&zs_, static_cast(flush))); - } - }; - - class inflator - : public stream + system::error_code + write(params& p, flush f) noexcept override { - z_stream zs_; - - public: - inflator( - http_proto::detail::workspace& ws, - int window_bits) - { - zs_.zalloc = &zalloc_impl; - zs_.zfree = &zfree_impl; - zs_.opaque = &ws; - - auto ret = inflateInit2(&zs_, window_bits); - if(ret != Z_OK) - throw_zlib_error(ret); - } - - virtual results - write( - buffers::mutable_buffer out, - buffers::const_buffer in, - flush flush) noexcept override - { - sync(&zs_, out, in); - return make_results(zs_, out, in, - inflate(&zs_, static_cast(flush))); - } - }; + sync(&zs_, p); + auto ret = inflate(&zs_, static_cast(f)); + sync(zs_, &p); + return static_cast(ret); + } +}; + +struct service_impl + : public service +{ + using key_type = service; explicit service_impl(context&) noexcept { } - virtual std::size_t + std::size_t space_needed() const noexcept override { return 0; // TODO } - virtual stream& + stream& make_deflator( http_proto::detail::workspace& ws, int level, @@ -377,7 +272,7 @@ struct service_impl ws, level, window_bits, mem_level); } - virtual stream& + stream& make_inflator( http_proto::detail::workspace& ws, int window_bits) const override @@ -386,13 +281,14 @@ struct service_impl } }; -void BOOST_HTTP_PROTO_ZLIB_DECL +} // namespace + +void install_service(context& ctx) { ctx.make_service(); } + } // zlib } // http_proto } // boost - -#endif