From e831e8b7ee62c0893a4147d6216427642a3f4c28 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Sun, 17 Feb 2019 13:22:14 -0800 Subject: [PATCH] Add detail::decorator --- .../beast/websocket/detail/decorator.hpp | 225 ++++++++++++++++++ include/boost/beast/websocket/rfc6455.hpp | 8 + include/boost/beast/websocket/stream.hpp | 6 - test/beast/websocket/CMakeLists.txt | 1 + test/beast/websocket/Jamfile | 1 + test/beast/websocket/_detail_decorator.cpp | 133 +++++++++++ 6 files changed, 368 insertions(+), 6 deletions(-) create mode 100644 include/boost/beast/websocket/detail/decorator.hpp create mode 100644 test/beast/websocket/_detail_decorator.cpp diff --git a/include/boost/beast/websocket/detail/decorator.hpp b/include/boost/beast/websocket/detail/decorator.hpp new file mode 100644 index 00000000..d399c8b3 --- /dev/null +++ b/include/boost/beast/websocket/detail/decorator.hpp @@ -0,0 +1,225 @@ +// +// Copyright (c) 2016-2017 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_DECORATOR_HPP +#define BOOST_BEAST_WEBSOCKET_DETAIL_DECORATOR_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace websocket { +namespace detail { + +class decorator +{ + struct incomplete; + + static std::size_t constexpr Bytes = + beast::detail::max_sizeof< + void*, + void const*, + void(*)(), + void(incomplete::*)(), + decltype(std::bind( + std::declval(), + std::declval>())) + >(); + + struct none + { + }; + + struct base + { + virtual ~base() = default; + + virtual + base* + move(void* to) = 0; + + virtual + void + invoke(request_type&) = 0; + + virtual + void + invoke(response_type&) = 0; + }; + + template + struct is_req_op : std::false_type + { + }; + + template + struct is_req_op()(std::declval()) + )>> : std::true_type + { + }; + + template + struct is_res_op : std::false_type + { + }; + + template + struct is_res_op()(std::declval()) + )>> : std::true_type + { + }; + + template + struct impl : base, + boost::empty_value + { + impl(impl&&) = default; + + template + explicit + impl(F_&& f) + : boost::empty_value( + boost::empty_init_t{}, + std::forward(f)) + { + } + + base* + move(void* to) override + { + return ::new(to) impl( + std::move(this->get())); + } + + void + invoke(request_type& req) override + { + this->invoke(req, is_req_op{}); + } + + void + invoke(request_type& req, std::true_type) + { + this->get()(req); + } + + void + invoke(request_type&, std::false_type) + { + } + + void + invoke(response_type& res) override + { + this->invoke(res, is_res_op{}); + } + + void + invoke(response_type& res, std::true_type) + { + this->get()(res); + } + + void + invoke(response_type&, std::false_type) + { + } + }; + + using type = typename + std::aligned_storage::type; + + type buf_; + base* base_; + +public: + decorator() + : decorator(none{}) + { + } + + decorator( + decorator&& other) + { + if(other.is_inline()) + { + base_ = other.base_->move(&buf_); + other.base_->~base(); + } + else + { + base_ = other.base_; + } + other.base_ = ::new(&other.buf_) + impl(none{}); + } + + template + explicit + decorator(F&& f) + : base_(sizeof(F) <= sizeof(buf_) ? + ::new(&buf_) impl< + typename std::decay::type>( + std::forward(f)) : + new impl< + typename std::decay::type>( + std::forward(f))) + { + } + + ~decorator() + { + if(is_inline()) + base_->~base(); + else + delete base_; + } + + bool + is_inline() const noexcept + { + return + ! std::less()( + reinterpret_cast(base_), + reinterpret_cast(&buf_)) && + std::less()( + reinterpret_cast(base_), + reinterpret_cast(&buf_ + 1)); + } + + void + operator()(request_type& req) + { + base_->invoke(req); + } + + void + operator()(response_type& res) + { + base_->invoke(res); + } +}; + +} // detail +} // websocket +} // beast +} // boost + +#endif diff --git a/include/boost/beast/websocket/rfc6455.hpp b/include/boost/beast/websocket/rfc6455.hpp index 4cad2ecc..c0dfd056 100644 --- a/include/boost/beast/websocket/rfc6455.hpp +++ b/include/boost/beast/websocket/rfc6455.hpp @@ -13,7 +13,9 @@ #include #include #include +#include #include +#include #include #include @@ -21,6 +23,12 @@ namespace boost { namespace beast { namespace websocket { +/// The type of object holding HTTP Upgrade requests +using request_type = http::request; + +/// The type of object holding HTTP Upgrade responses +using response_type = http::response; + /** Returns `true` if the specified HTTP request is a WebSocket Upgrade. This function returns `true` when the passed HTTP Request diff --git a/include/boost/beast/websocket/stream.hpp b/include/boost/beast/websocket/stream.hpp index 208cbaae..a7c441ad 100644 --- a/include/boost/beast/websocket/stream.hpp +++ b/include/boost/beast/websocket/stream.hpp @@ -37,12 +37,6 @@ namespace boost { namespace beast { namespace websocket { -/// The type of object holding HTTP Upgrade requests -using request_type = http::request; - -/// The type of object holding HTTP Upgrade responses -using response_type = http::response; - /** The type of received control frame. Values of this type are passed to the control frame diff --git a/test/beast/websocket/CMakeLists.txt b/test/beast/websocket/CMakeLists.txt index 9f633b23..4f643038 100644 --- a/test/beast/websocket/CMakeLists.txt +++ b/test/beast/websocket/CMakeLists.txt @@ -16,6 +16,7 @@ add_executable (tests-beast-websocket ${EXTRAS_FILES} ${TEST_MAIN} Jamfile + _detail_decorator.cpp _detail_prng.cpp _detail_impl_base.cpp test.hpp diff --git a/test/beast/websocket/Jamfile b/test/beast/websocket/Jamfile index 5dcdad4c..70de6247 100644 --- a/test/beast/websocket/Jamfile +++ b/test/beast/websocket/Jamfile @@ -8,6 +8,7 @@ # local SOURCES = + _detail_decorator.cpp _detail_impl_base.cpp _detail_prng.cpp accept.cpp diff --git a/test/beast/websocket/_detail_decorator.cpp b/test/beast/websocket/_detail_decorator.cpp new file mode 100644 index 00000000..c666cb01 --- /dev/null +++ b/test/beast/websocket/_detail_decorator.cpp @@ -0,0 +1,133 @@ +// +// Copyright (c) 2016-2017 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 +// + +// Test that header file is self-contained. +#include + +#include + +namespace boost { +namespace beast { +namespace websocket { +namespace detail { + +class decorator_test : public unit_test::suite +{ +public: + struct req_t + { + bool pass_ = false; + + req_t() = default; + + ~req_t() + { + BEAST_EXPECT(pass_); + } + + req_t(req_t&& other) + : pass_(boost::exchange(other.pass_, true)) + { + } + + void + operator()(request_type&) + { + BEAST_EXPECT(! pass_); + pass_ = true; + } + }; + + struct res_t + { + bool pass_ = false; + + res_t() = default; + + ~res_t() + { + BEAST_EXPECT(pass_); + } + + res_t(res_t&& other) + : pass_(boost::exchange(other.pass_, true)) + { + } + + void + operator()(response_type&) + { + BEAST_EXPECT(! pass_); + pass_ = true; + } + }; + + struct both_t : res_t , req_t + { + }; + + struct big_t : both_t + { + char buf[2048]; + }; + + void + dec_req(request_type&) + { + } + + void + testDecorator() + { + request_type req; + response_type res; + + decorator{}(req); + decorator{}(res); + decorator{req_t{}}(req); + decorator{res_t{}}(res); + + { + decorator d(both_t{}); + d(req); + d(res); + } + + { + decorator d(big_t{}); + d(req); + d(res); + } + + { + decorator d1{req_t{}}; + decorator d2{std::move(d1)}; + d2(req); + } + + { + decorator d(std::bind( + &decorator_test::dec_req, this)); + BEAST_EXPECT(d.is_inline()); + } + } + + void + run() override + { + testDecorator(); + } +}; + +BEAST_DEFINE_TESTSUITE(beast,websocket,decorator); + +} // detail +} // websocket +} // beast +} // boost