]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/boost/libs/beast/test/beast/zlib/inflate_stream.cpp
import new upstream nautilus stable release 14.2.8
[ceph.git] / ceph / src / boost / libs / beast / test / beast / zlib / inflate_stream.cpp
index c76df14447d24ad812575d86950137830405af01..66f53c92431f2ca676bf39ac1bc1f4f53ad77f31 100644 (file)
@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2016-2017 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)
@@ -11,7 +11,7 @@
 #include <boost/beast/zlib/inflate_stream.hpp>
 
 #include <boost/beast/core/string.hpp>
-#include <boost/beast/unit_test/suite.hpp>
+#include <boost/beast/_experimental/unit_test/suite.hpp>
 #include <chrono>
 #include <random>
 
@@ -23,6 +23,127 @@ namespace zlib {
 
 class inflate_stream_test : public beast::unit_test::suite
 {
+    struct IDecompressor {
+        virtual void init() = 0;
+        virtual void init(int windowBits) = 0;
+
+        virtual std::size_t avail_in() const noexcept = 0;
+        virtual void avail_in(std::size_t) noexcept = 0;
+        virtual void const* next_in() const noexcept = 0;
+        virtual void next_in(const void*) noexcept = 0;
+        virtual std::size_t avail_out() const noexcept = 0;
+        virtual void avail_out(std::size_t) noexcept = 0;
+        virtual void* next_out() const noexcept = 0;
+        virtual void next_out(void*) noexcept = 0;
+
+        virtual error_code write(Flush) = 0;
+        virtual ~IDecompressor() = default;
+    };
+    class ZlibDecompressor : public IDecompressor {
+       z_stream zs;
+
+    public:
+        ZlibDecompressor() = default;
+        void init(int windowBits) override
+        {
+            inflateEnd(&zs);
+            zs = {};
+            const auto res = inflateInit2(&zs, windowBits);
+            switch(res){
+            case Z_OK:
+               break;
+            case Z_MEM_ERROR:
+               BOOST_THROW_EXCEPTION(std::runtime_error{"zlib decompressor: no memory"});
+            case Z_STREAM_ERROR:
+               BOOST_THROW_EXCEPTION(std::domain_error{"zlib decompressor: bad arg"});
+            }
+        }
+        void init() override {
+            inflateEnd(&zs);
+            zs = {};
+            const auto res = inflateInit2(&zs, -15);
+            switch(res){
+            case Z_OK:
+                break;
+            case Z_MEM_ERROR:
+                BOOST_THROW_EXCEPTION(std::runtime_error{"zlib decompressor: no memory"});
+            case Z_STREAM_ERROR:
+               BOOST_THROW_EXCEPTION(std::domain_error{"zlib decompressor: bad arg"});
+            }
+        }
+
+        virtual std::size_t avail_in() const noexcept override  { return zs.avail_in; }
+        virtual void avail_in(std::size_t n) noexcept override { zs.avail_in = n; }
+        virtual void const* next_in() const noexcept override { return zs.next_in; }
+        virtual void next_in(const void* ptr) noexcept override { zs.next_in = const_cast<Bytef*>(static_cast<const Bytef*>(ptr)); }
+        virtual std::size_t avail_out() const noexcept override { return zs.avail_out; }
+        virtual void avail_out(std::size_t n_out) noexcept override { zs.avail_out = n_out; }
+        virtual void* next_out() const noexcept override { return zs.next_out; }
+        virtual void next_out(void* ptr) noexcept override { zs.next_out = (Bytef*)ptr; }
+
+        error_code write(Flush flush) override {
+            constexpr static int zlib_flushes[] = {0, Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, Z_FULL_FLUSH, Z_FINISH, Z_TREES};
+            const auto zlib_flush = zlib_flushes[static_cast<int>(flush)];
+            const auto res = inflate(&zs, zlib_flush);
+            switch(res){
+            case Z_OK:
+              return {};
+            case Z_STREAM_END:
+              return error::end_of_stream;
+            case Z_NEED_DICT:
+              return error::need_dict;
+            case Z_DATA_ERROR:
+            case Z_STREAM_ERROR:
+              return error::stream_error;
+            case Z_MEM_ERROR:
+              BOOST_THROW_EXCEPTION(std::bad_alloc{});
+            case Z_BUF_ERROR:
+              return error::need_buffers;
+            default:
+              BOOST_THROW_EXCEPTION(std::runtime_error{"zlib decompressor: impossible value"});
+            }
+        }
+
+        ~ZlibDecompressor() override {
+          inflateEnd(&zs);
+        }
+    } zlib_decompressor{};
+    class BeastCompressor : public IDecompressor {
+        z_params zp;
+        inflate_stream is;
+
+    public:
+        BeastCompressor() = default;
+
+        void init(int windowBits) override
+        {
+            zp = {};
+            is.clear();
+            is.reset(windowBits);
+        }
+        void init() override {
+          zp = {};
+          is.clear();
+          is.reset();
+        }
+
+        virtual std::size_t avail_in() const noexcept override  { return zp.avail_in; }
+        virtual void avail_in(std::size_t n) noexcept override { zp.avail_in = n; }
+        virtual void const* next_in() const noexcept override { return zp.next_in; }
+        virtual void next_in(const void* ptr) noexcept override { zp.next_in = ptr; }
+        virtual std::size_t avail_out() const noexcept override { return zp.avail_out; }
+        virtual void avail_out(std::size_t n_out) noexcept override { zp.avail_out = n_out; }
+        virtual void* next_out() const noexcept override { return zp.next_out; }
+        virtual void next_out(void* ptr) noexcept override { zp.next_out = (Bytef*)ptr; }
+
+        error_code write(Flush flush) override {
+            error_code ec{};
+            is.write(zp, flush, ec);
+            return ec;
+        }
+
+        ~BeastCompressor() override = default;
+    } beast_decompressor{};
 public:
     // Lots of repeats, limited char range
     static
@@ -283,7 +404,7 @@ public:
     };
 
     void
-    testInflate()
+    testInflate(IDecompressor& d)
     {
         {
             Matrix m{*this};
@@ -347,6 +468,26 @@ public:
             m.strategy(Z_DEFAULT_STRATEGY);
             m(Beast{full, once, Flush::block}, check);
         }
+        {
+            // Check a known string - this provides more stable coverage,
+            // independent of the RNG-generated strings.
+            Matrix m{*this};
+            auto const check =
+                "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do "
+                "eiusmod tempor incididunt ut labore et dolore magna aliqua. "
+                "Ultricies mi eget mauris pharetra et ultrices neque ornare. Eget est "
+                "lorem ipsum dolor. Dui faucibus in ornare quam viverra orci "
+                "sagittis. Lorem mollis aliquam ut porttitor. Pretium quam vulputate "
+                "dignissim suspendisse in est ante in. Tempus egestas sed sed risus "
+                "pretium quam vulputate dignissim. Pellentesque dignissim enim sit "
+                "amet venenatis urna. Eleifend quam adipiscing vitae proin sagittis "
+                "nisl rhoncus. Aliquam etiam erat velit scelerisque in. Accumsan in "
+                "nisl nisi scelerisque eu ultrices vitae auctor eu.";
+            m.level(6);
+            m.window(9);
+            m.strategy(Z_DEFAULT_STRATEGY);
+            m(Beast{full, full}, check);
+        }
 
         // VFALCO Fails, but I'm unsure of what the correct
         //        behavior of Z_TREES/Flush::trees is.
@@ -360,6 +501,137 @@ public:
             m(Beast{full, once, Flush::trees}, check);
         }
 #endif
+
+        check(d, {0x63, 0x18, 0x05, 0x40, 0x0c, 0x00}, {}, 8,  3);
+        check(d, {0xed, 0xc0, 0x81, 0x00, 0x00, 0x00, 0x00, 0x80,
+               0xa0, 0xfd, 0xa9, 0x17, 0xa9, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, {});
+    }
+
+    std::string check(IDecompressor& d,
+        std::initializer_list<std::uint8_t> const& in,
+        error_code expected,
+        std::size_t window_size = 15,
+        std::size_t len = -1)
+    {
+        std::string out(1024, 0);
+        z_params zs;
+        inflate_stream is;
+        is.reset(static_cast<int>(window_size));
+        boost::system::error_code ec;
+
+        zs.next_in = &*in.begin();
+        zs.next_out = &out[0];
+        zs.avail_in = std::min(in.size(), len);
+        zs.avail_out = out.size();
+
+        while (zs.avail_in > 0 && !ec)
+        {
+            is.write(zs, Flush::sync, ec);
+            auto n = std::min(zs.avail_in, len);
+            zs.next_in = static_cast<char const*>(zs.next_in) + n;
+            zs.avail_in -= n;
+        }
+
+        BEAST_EXPECT(ec == expected);
+        return out;
+    }
+
+    void testInflateErrors(IDecompressor& d)
+    {
+        check(d, {0x00, 0x00, 0x00, 0x00, 0x00},
+            error::invalid_stored_length);
+        check(d, {0x03, 0x00},
+            error::end_of_stream);
+        check(d, {0x06},
+            error::invalid_block_type);
+        check(d, {0xfc, 0x00, 0x00},
+            error::too_many_symbols);
+        check(d, {0x04, 0x00, 0xfe, 0xff},
+            error::incomplete_length_set);
+        check(d, {0x04, 0x00, 0x24, 0x49, 0x00},
+            error::invalid_bit_length_repeat);
+        check(d, {0x04, 0x00, 0x24, 0xe9, 0xff, 0xff},
+            error::invalid_bit_length_repeat);
+        check(d, {0x04, 0x00, 0x24, 0xe9, 0xff, 0x6d},
+            error::missing_eob);
+        check(d, {0x04, 0x80, 0x49, 0x92, 0x24, 0x49, 0x92, 0x24,
+               0x71, 0xff, 0xff, 0x93, 0x11, 0x00},
+            error::over_subscribed_length);
+        check(d, {0x04, 0x80, 0x49, 0x92, 0x24, 0x0f, 0xb4, 0xff,
+               0xff, 0xc3, 0x84},
+            error::incomplete_length_set);
+        check(d, {0x04, 0xc0, 0x81, 0x08, 0x00, 0x00, 0x00, 0x00,
+               0x20, 0x7f, 0xeb, 0x0b, 0x00, 0x00},
+            error::invalid_literal_length);
+        check(d, {0x02, 0x7e, 0xff, 0xff},
+            error::invalid_distance_code);
+        check(d, {0x0c, 0xc0, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x90, 0xff, 0x6b, 0x04, 0x00},
+            error::invalid_distance);
+        check(d, {0x05,0xe0, 0x81, 0x91, 0x24, 0xcb, 0xb2, 0x2c,
+               0x49, 0xe2, 0x0f, 0x2e, 0x8b, 0x9a, 0x47, 0x56,
+               0x9f, 0xfb, 0xfe, 0xec, 0xd2, 0xff, 0x1f},
+            error::end_of_stream);
+        check(d, {0xed, 0xc0, 0x01, 0x01, 0x00, 0x00, 0x00, 0x40,
+               0x20, 0xff, 0x57, 0x1b, 0x42, 0x2c, 0x4f},
+            error::end_of_stream);
+        check(d, {0x02, 0x08, 0x20, 0x80, 0x00, 0x03, 0x00},
+            error::end_of_stream);
+        check(d, {0x78, 0x9c, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x78, 0x9c, 0xff},
+            error::invalid_stored_length);
+    }
+
+    void testInvalidSettings(IDecompressor& d)
+    {
+        except<std::domain_error>(
+            [&]()
+            {
+                d.init(7);
+            });
+    }
+
+    void testFixedHuffmanFlushTrees(IDecompressor& d)
+    {
+        std::string out(5, 0);
+        d.init();
+        boost::system::error_code ec;
+        std::initializer_list<std::uint8_t> in = {
+            0xf2, 0x48, 0xcd, 0xc9, 0xc9, 0x07, 0x00, 0x00,
+            0x00, 0xff, 0xff};
+        d.next_in(&*in.begin());
+        d.next_out(&out[0]);
+        d.avail_in(in.size());
+        d.avail_out(out.size());
+        ec = d.write(Flush::trees);
+        BEAST_EXPECT(!ec);
+        ec = d.write(Flush::sync);
+        BEAST_EXPECT(!ec);
+        BEAST_EXPECT(d.avail_out() == 0);
+        BEAST_EXPECT(out == "Hello");
+    }
+
+    void testUncompressedFlushTrees(IDecompressor& d)
+    {
+        std::string out(5, 0);
+        d.init();
+        boost::system::error_code ec;
+        std::initializer_list<std::uint8_t> in = {
+            0x00, 0x05, 0x00, 0xfa, 0xff, 0x48, 0x65, 0x6c,
+            0x6c, 0x6f, 0x00, 0x00};
+        d.next_in(&*in.begin());
+        d.next_out(&out[0]);
+        d.avail_in(in.size());
+        d.avail_out(out.size());
+        ec = d.write(Flush::trees);
+        BEAST_EXPECT(!ec);
+        ec = d.write(Flush::sync);
+        BEAST_EXPECT(!ec);
+        BEAST_EXPECT(d.avail_out() == 0);
+        BEAST_EXPECT(out == "Hello");
     }
 
     void
@@ -368,7 +640,16 @@ public:
         log <<
             "sizeof(inflate_stream) == " <<
             sizeof(inflate_stream) << std::endl;
-        testInflate();
+        testInflate(zlib_decompressor);
+        testInflate(beast_decompressor);
+        testInflateErrors(zlib_decompressor);
+        testInflateErrors(beast_decompressor);
+        testInvalidSettings(zlib_decompressor);
+        testInvalidSettings(beast_decompressor);
+        testFixedHuffmanFlushTrees(zlib_decompressor);
+        testFixedHuffmanFlushTrees(beast_decompressor);
+        testUncompressedFlushTrees(zlib_decompressor);
+        testUncompressedFlushTrees(beast_decompressor);
     }
 };