LCOV - code coverage report
Current view: top level - libs/http_proto/src_zlib/service/zlib_service.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 80.4 % 153 123
Test Date: 2024-09-13 19:44:06 Functions: 77.3 % 22 17

            Line data    Source code
       1              : //
       2              : // Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com)
       3              : //
       4              : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       5              : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       6              : //
       7              : // Official repository: https://github.com/cppalliance/http_proto
       8              : //
       9              : 
      10              : #ifndef BOOST_HTTP_PROTO_SERVICE_IMPL_ZLIB_SERVICE_IPP
      11              : #define BOOST_HTTP_PROTO_SERVICE_IMPL_ZLIB_SERVICE_IPP
      12              : 
      13              : #include <boost/http_proto/service/zlib_service.hpp>
      14              : 
      15              : #include <boost/http_proto/metadata.hpp>
      16              : 
      17              : #include <boost/assert/source_location.hpp>
      18              : #include <boost/buffers/circular_buffer.hpp>
      19              : #include <boost/config.hpp>
      20              : #include <boost/system/result.hpp>
      21              : #include <boost/throw_exception.hpp>
      22              : 
      23              : #include <zlib.h>
      24              : 
      25              : #include "../../src/zlib_service.hpp"
      26              : 
      27              : namespace boost {
      28              : namespace http_proto {
      29              : namespace zlib {
      30              : namespace detail {
      31              : 
      32              : /*
      33              :     DEFLATE Compressed Data Format Specification version 1.3
      34              :     https://www.rfc-editor.org/rfc/rfc1951
      35              : */
      36              : 
      37              : //------------------------------------------------
      38              : 
      39              : enum class error
      40              : {
      41              :     ok          =  0,
      42              :     stream_end  =  1,
      43              :     need_dict   =  2,
      44              :     errno_      = -1,
      45              :     stream_err  = -2,
      46              :     data_err    = -3,
      47              :     mem_err     = -4,
      48              :     buf_err     = -5,
      49              :     version_err = -6
      50              : };
      51              : 
      52              : //------------------------------------------------
      53              : } // detail
      54              : } // zlib
      55              : } // http_proto
      56              : namespace system {
      57              : template<>
      58              : struct is_error_code_enum<
      59              :     ::boost::http_proto::zlib::detail::error>
      60              : {
      61              :     static bool const value = true;
      62              : };
      63              : } // system
      64              : namespace http_proto {
      65              : namespace zlib {
      66              : namespace detail {
      67              : //------------------------------------------------
      68              : 
      69              : struct error_cat_type
      70              :     : system::error_category
      71              : {
      72              :     BOOST_SYSTEM_CONSTEXPR
      73            3 :     error_cat_type() noexcept
      74            3 :         : error_category(
      75            3 :             0xe6c6d0215d1d6e22)
      76              :     {
      77            3 :     }
      78              : 
      79              :     const char*
      80            0 :     name() const noexcept override
      81              :     {
      82            0 :         return "boost.http.proto.zlib";
      83              :     }
      84              : 
      85              :     std::string
      86            0 :     message( int ev ) const override
      87              :     {
      88            0 :         return message( ev, nullptr, 0 );
      89              :     }
      90              : 
      91              :     char const*
      92            0 :     message(
      93              :         int ev,
      94              :         char*,
      95              :         std::size_t) const noexcept override
      96              :     {
      97            0 :         switch(static_cast<error>(ev))
      98              :         {
      99            0 :         case error::ok: return "Z_OK";
     100            0 :         case error::stream_end: return "Z_STREAM_END";
     101            0 :         case error::need_dict: return "Z_NEED_DICT";
     102            0 :         case error::errno_: return "Z_ERRNO";
     103            0 :         case error::stream_err: return "Z_STREAM_ERROR";
     104            0 :         case error::data_err: return "Z_DATA_ERROR";
     105            0 :         case error::mem_err: return "Z_MEM_ERROR";
     106            0 :         case error::buf_err: return "Z_BUF_ERROR";
     107            0 :         case error::version_err: return "Z_VERSION_ERROR";
     108            0 :         default:
     109            0 :             return "unknown";
     110              :         }
     111              :     }
     112              : };
     113              : 
     114              : system::error_code
     115          104 : make_error_code(
     116              :     error ev) noexcept
     117              : {
     118              :     static BOOST_SYSTEM_CONSTEXPR
     119          104 :         error_cat_type cat{};
     120              :     return system::error_code{static_cast<
     121              :         std::underlying_type<
     122          104 :             error>::type>(ev), cat};
     123              : }
     124              : 
     125              : BOOST_NOINLINE BOOST_NORETURN
     126              : void
     127            0 : throw_zlib_error(
     128              :     int e,
     129              :     source_location const& loc = BOOST_CURRENT_LOCATION)
     130              : {
     131            0 :     throw_exception(
     132            0 :         system::system_error(static_cast<error>(e)), loc);
     133              : }
     134              : 
     135              : //------------------------------------------------
     136              : 
     137              : // probes memory usage for a config
     138              : class probe
     139              : {
     140              : public:
     141              :     explicit
     142           26 :     probe() noexcept
     143           26 :     {
     144           26 :         zs_.zalloc = &zalloc;
     145           26 :         zs_.zfree = &zfree;
     146           26 :         zs_.opaque = this;
     147           26 :     }
     148              : 
     149              :     system::result<std::size_t>
     150           26 :     deflate_init(
     151              :         int level)
     152              :     {
     153           26 :         n_ = 0;
     154           26 :         system::error_code ec;
     155              :         ec = static_cast<error>(
     156           26 :             deflateInit(&zs_, level));
     157           26 :         if(ec.failed())
     158            0 :             return ec;
     159           26 :         Bytef tmp[24]{};
     160           26 :         zs_.next_in = &tmp[0];
     161           26 :         zs_.avail_in = 1;
     162           26 :         zs_.next_out = &tmp[1];
     163           26 :         zs_.avail_out = 23;
     164              :         ec = static_cast<error>(
     165           26 :             deflate(&zs_,
     166           26 :                 Z_FINISH));
     167           52 :         if( ec.failed() &&
     168           52 :             ec != error::stream_end)
     169            0 :             return ec;
     170              :         ec = static_cast<error>(
     171           26 :             deflateEnd(&zs_));
     172           26 :         if(ec.failed())
     173            0 :             return ec;
     174           26 :         return n_;
     175              :     }
     176              : 
     177              :     system::result<std::size_t>
     178              :     deflate_init2(
     179              :         int level,
     180              :         int method,
     181              :         int windowBits,
     182              :         int memLevel,
     183              :         int strategy)
     184              :     {
     185              :         n_ = 0;
     186              :         system::error_code ec;
     187              :         ec = static_cast<error>(
     188              :             deflateInit2(&zs_,
     189              :                 level,
     190              :                 method,
     191              :                 windowBits,
     192              :                 memLevel,
     193              :                 strategy));
     194              :         if(ec.failed())
     195              :             return ec;
     196              :         Bytef tmp[2];
     197              :         zs_.next_in = &tmp[0];
     198              :         zs_.avail_in = 0;
     199              :         zs_.next_out = &tmp[1];
     200              :         zs_.avail_out = 0;
     201              :         ec = static_cast<error>(
     202              :             deflate(&zs_,
     203              :                 Z_FULL_FLUSH));
     204              :         if(ec.failed())
     205              :             return ec;
     206              :         ec = static_cast<error>(
     207              :             deflateEnd(&zs_));
     208              :         if(ec.failed())
     209              :             return ec;
     210              :         return n_;
     211              :     }
     212              : 
     213              : private:
     214          130 :     static void* zalloc(void* opaque,
     215              :         uInt num, uInt size)
     216              :     {
     217          130 :         auto& self =
     218              :             *reinterpret_cast<
     219              :                 probe*>(opaque);
     220          130 :         self.n_ += num * size;
     221          130 :         return new char[num * size];
     222              :     }
     223              : 
     224          130 :     static void zfree(
     225              :         void*, void* address)
     226              :     {
     227          130 :         delete[] reinterpret_cast<
     228          130 :             char*>(address);
     229          130 :     }
     230              : 
     231              :     z_stream_s zs_{};
     232              :     std::size_t n_ = 0;
     233              : };
     234              : 
     235              : //------------------------------------------------
     236              : 
     237              : 
     238              : namespace {
     239          240 : void* zalloc_impl(
     240              :     void* opaque,
     241              :     unsigned items,
     242              :     unsigned size)
     243              : {
     244              :     try
     245              :     {
     246          240 :         auto n = items * size;
     247          240 :         auto* ws =
     248              :             reinterpret_cast<
     249              :                 http_proto::detail::workspace*>(opaque);
     250              : 
     251          240 :         return ws->reserve_front(n);
     252              :     }
     253            0 :     catch(std::length_error const&) // represents OOM
     254              :     {
     255            0 :         return Z_NULL;
     256            0 :     }
     257              : }
     258              : 
     259          240 : void zfree_impl(void* /* opaque */, void* /* addr */)
     260              : {
     261              :     // we call ws_.clear() before the serializer is reused
     262              :     // so all the allocations are passively freed
     263          240 : }
     264              : 
     265              : } // namespace
     266              : 
     267              : class deflate_filter final
     268              :     : public http_proto::detail::filter
     269              : {
     270              : private:
     271              :     z_stream stream_;
     272              :     http_proto::detail::workspace& ws_;
     273              : 
     274              :     void init(bool use_gzip);
     275              : 
     276              : public:
     277              :     deflate_filter(
     278              :         http_proto::detail::workspace& ws,
     279              :         bool use_gzip = false);
     280              :     ~deflate_filter();
     281              : 
     282              :     deflate_filter(deflate_filter const&) = delete;
     283              :     deflate_filter& operator=(
     284              :         deflate_filter const&) = delete;
     285              : 
     286              :     filter::results
     287              :     on_process(
     288              :         buffers::mutable_buffer out,
     289              :         buffers::const_buffer in,
     290              :         bool more) override;
     291              : };
     292              : 
     293           48 : deflate_filter::
     294              : deflate_filter(
     295              :     http_proto::detail::workspace& ws,
     296           48 :     bool use_gzip)
     297           48 :     : ws_(ws)
     298              : {
     299           48 :     stream_.zalloc = &zalloc_impl;
     300           48 :     stream_.zfree = &zfree_impl;
     301           48 :     stream_.opaque = &ws_;
     302           48 :     init(use_gzip);
     303           48 : }
     304              : 
     305           48 : deflate_filter::
     306           48 : ~deflate_filter()
     307              : {
     308           48 :     deflateEnd(&stream_);
     309           48 : }
     310              : 
     311              : void
     312           48 : deflate_filter::
     313              : init(bool use_gzip)
     314              : {
     315           48 :     int ret = -1;
     316              : 
     317           48 :     int window_bits = 15;
     318           48 :     if( use_gzip )
     319           24 :         window_bits += 16;
     320              : 
     321           48 :     int mem_level = 8;
     322              : 
     323           48 :     ret = deflateInit2(
     324              :         &stream_, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
     325              :         window_bits, mem_level, Z_DEFAULT_STRATEGY);
     326              : 
     327           48 :     if( ret != Z_OK )
     328            0 :         throw_zlib_error(ret);
     329              : 
     330           48 :     stream_.next_out = nullptr;
     331           48 :     stream_.avail_out = 0;
     332              : 
     333           48 :     stream_.next_in = nullptr;
     334           48 :     stream_.avail_in = 0;
     335           48 : }
     336              : 
     337              : http_proto::detail::filter::results
     338        23724 : deflate_filter::
     339              : on_process(
     340              :     buffers::mutable_buffer out,
     341              :     buffers::const_buffer in,
     342              :     bool more)
     343              : {
     344        23724 :     auto& zstream = stream_;
     345              : 
     346        23724 :     auto flush = more ? Z_NO_FLUSH : Z_FINISH;
     347        23724 :     int ret = -1;
     348        23724 :     filter::results results;
     349              : 
     350              :     for(;;)
     351              :     {
     352        45988 :         zstream.next_in =
     353              :             reinterpret_cast<unsigned char*>(
     354        45988 :                 const_cast<void*>(in.data()));
     355        45988 :         zstream.avail_in = static_cast<unsigned>(
     356        45988 :             in.size());
     357              : 
     358        45988 :         zstream.next_out =
     359              :             reinterpret_cast<unsigned char*>(
     360        45988 :                 out.data());
     361        45988 :         zstream.avail_out =
     362        45988 :             static_cast<unsigned>(out.size());
     363              : 
     364        45988 :         auto n1 = zstream.avail_in;
     365        45988 :         auto n2 = zstream.avail_out;
     366        45988 :         ret = deflate(&zstream, flush);
     367              : 
     368        45988 :         in += (n1 - zstream.avail_in);
     369        45988 :         out += (n2 - zstream.avail_out);
     370              : 
     371        45988 :         results.in_bytes += (n1 - zstream.avail_in);
     372        45988 :         results.out_bytes += (n2 - zstream.avail_out);
     373              : 
     374        45988 :         auto is_empty = (in.size() == 0);
     375              : 
     376        45988 :         if( ret != Z_OK &&
     377           96 :             ret != Z_BUF_ERROR &&
     378              :             ret != Z_STREAM_END )
     379            0 :             throw_zlib_error(ret);
     380              : 
     381        45988 :         if( is_empty &&
     382        44648 :             n2 == zstream.avail_out &&
     383              :             ret == Z_OK )
     384              :         {
     385        11104 :             flush = Z_SYNC_FLUSH;
     386        11104 :             continue;
     387              :         }
     388              : 
     389        34884 :         if( ret == Z_STREAM_END )
     390           96 :             results.finished = true;
     391              : 
     392        34884 :         if( ret == Z_BUF_ERROR )
     393        22104 :             break;
     394              : 
     395        12780 :         if( ret == Z_STREAM_END )
     396           96 :             break;
     397              : 
     398        25368 :         if( ret == Z_OK &&
     399        12684 :             out.size() == 0 )
     400         1524 :             break;
     401        22264 :     }
     402        23724 :     return results;
     403              : }
     404              : 
     405              : //------------------------------------------------
     406              : 
     407              : struct
     408              :     deflate_decoder_service_impl
     409              :     : deflate_decoder_service
     410              : {
     411              :     using key_type =
     412              :         deflate_decoder_service;
     413              : 
     414              :     explicit
     415           26 :     deflate_decoder_service_impl(
     416              :         context& ctx,
     417              :         config const& cfg)
     418           26 :         : cfg_(cfg)
     419              :     {
     420              :         (void)ctx;
     421           26 :         probe p;
     422           26 :         auto n0 = p.deflate_init(
     423           26 :             Z_DEFAULT_COMPRESSION).value();
     424              :         (void)n0;
     425           26 :     }
     426              : 
     427              : private:
     428              :     config cfg_;
     429              : 
     430              :     config const&
     431            0 :     get_config() const noexcept override
     432              :     {
     433            0 :         return cfg_;
     434              :     }
     435              : 
     436              :     std::size_t
     437            1 :     space_needed() const noexcept override
     438              :     {
     439            1 :         return 0;
     440              :     }
     441              : 
     442              :     http_proto::detail::filter&
     443           24 :     make_deflate_filter(
     444              :         http_proto::detail::workspace& ws) const override
     445              :     {
     446           24 :         return ws.emplace<deflate_filter>(ws, false);
     447              :     }
     448              : 
     449              :     http_proto::detail::filter&
     450           24 :     make_gzip_filter(
     451              :         http_proto::detail::workspace& ws) const override
     452              :     {
     453           24 :         return ws.emplace<deflate_filter>(ws, true);
     454              :     }
     455              : };
     456              : 
     457              : } // detail
     458              : 
     459              : void BOOST_HTTP_PROTO_ZLIB_DECL
     460           26 : install_deflate_encoder(context& ctx)
     461              : {
     462           26 :     detail::deflate_decoder_service::config cfg;
     463              :     ctx.make_service<
     464           26 :         detail::deflate_decoder_service_impl>(cfg);
     465           26 : }
     466              : 
     467              : } // zlib
     468              : } // http_proto
     469              : } // boost
     470              : 
     471              : #endif
        

Generated by: LCOV version 2.1