diff --git a/CHANGELOG.md b/CHANGELOG.md
index 54b0ee67..dd2bb89a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@ Version 211:
* close_socket is in stream_traits.hpp
* Improvements to test::stream
* Add stranded_stream
+* Add flat_stream
--------------------------------------------------------------------------------
diff --git a/doc/qbk/03_core/2_streams.qbk b/doc/qbk/03_core/2_streams.qbk
index 6b2683a5..6221b4af 100644
--- a/doc/qbk/03_core/2_streams.qbk
+++ b/doc/qbk/03_core/2_streams.qbk
@@ -7,6 +7,8 @@
Official repository: https://github.com/boostorg/beast
]
+
+
[section Stream Types]
A __Stream__ is a communication channel where data is reliably transferred as
@@ -111,4 +113,30 @@ synchronous stream may check its argument:
[snippet_core_3]
+
+
+[heading Stream Interfaces]
+
+To facilitiate stream algorithms, these types provide enhanced functionality
+above what is offered by networking:
+
+[table Stream Interfaces
+[[Name][Description]]
+[[
+ [link beast.ref.boost__beast__basic_timeout_stream `basic_timeout_stream`]
+ [link beast.ref.boost__beast__timeout_stream `timeout_stream`]
+][
+ Objects of this type are designed to act as a replacement for the
+ traditional networking socket. They
+]]
+[[
+ [link beast.ref.boost__beast__flat_stream `flat_stream`]
+][
+ This stream wrapper improves performance by working around a limitation
+ of networking's `ssl::stream` to improve performance.
+]]
+]
+
+
+
[endsect]
diff --git a/doc/qbk/quickref.xml b/doc/qbk/quickref.xml
index 178bf619..0548c6bd 100644
--- a/doc/qbk/quickref.xml
+++ b/doc/qbk/quickref.xml
@@ -33,6 +33,7 @@
flat_buffer
flat_static_buffer
flat_static_buffer_base
+ flat_stream 🞲
handler_ptr
iequal
iless
@@ -354,7 +355,6 @@
Classes
- flat_stream
ssl_stream
http::icy_stream
test::fail_count
diff --git a/include/boost/beast/_experimental/core/ssl_stream.hpp b/include/boost/beast/_experimental/core/ssl_stream.hpp
index 9cbc5af3..7a2a06a5 100644
--- a/include/boost/beast/_experimental/core/ssl_stream.hpp
+++ b/include/boost/beast/_experimental/core/ssl_stream.hpp
@@ -15,7 +15,7 @@
// This include is necessary to work with `ssl::stream` and `boost::beast::websocket::stream`
#include
-#include
+#include
#include
#include
#include
diff --git a/include/boost/beast/core.hpp b/include/boost/beast/core.hpp
index 27e18b2a..5bf8d46c 100644
--- a/include/boost/beast/core.hpp
+++ b/include/boost/beast/core.hpp
@@ -32,6 +32,7 @@
#include
#include
#include
+#include
#include
#include
#include
diff --git a/include/boost/beast/_experimental/core/detail/flat_stream.hpp b/include/boost/beast/core/detail/flat_stream.hpp
similarity index 100%
rename from include/boost/beast/_experimental/core/detail/flat_stream.hpp
rename to include/boost/beast/core/detail/flat_stream.hpp
diff --git a/include/boost/beast/_experimental/core/flat_stream.hpp b/include/boost/beast/core/flat_stream.hpp
similarity index 94%
rename from include/boost/beast/_experimental/core/flat_stream.hpp
rename to include/boost/beast/core/flat_stream.hpp
index 0b8f27f8..4dea1354 100644
--- a/include/boost/beast/_experimental/core/flat_stream.hpp
+++ b/include/boost/beast/core/flat_stream.hpp
@@ -12,7 +12,9 @@
#include
#include
-#include
+#include
+#include
+#include
#include
#include
#include
@@ -73,12 +75,12 @@ namespace beast {
`net::ssl::stream`.
@par Concepts
- @b AsyncStream
- @b SyncStream
+ @li SyncStream
+ @li AsyncStream
@see
- @li https://github.com/boostorg/beast/issues/1108
@li https://github.com/boostorg/asio/issues/100
+ @li https://github.com/boostorg/beast/issues/1108
@li https://stackoverflow.com/questions/38198638/openssl-ssl-write-from-multiple-buffers-ssl-writev
@li https://stackoverflow.com/questions/50026167/performance-drop-on-port-from-beast-1-0-0-b66-to-boost-1-67-0-beast
*/
@@ -92,9 +94,22 @@ class flat_stream
// 16KB is the upper limit on reasonably sized HTTP messages.
static std::size_t constexpr max_size = 16 * 1024;
- template class write_op;
+ // Largest stack we will use to flatten
+ static std::size_t constexpr max_stack = 16 * 1024;
+
+ template class write_op;
NextLayer stream_;
+ flat_buffer buffer_;
+
+ BOOST_STATIC_ASSERT(has_get_executor::value);
+
+ template
+ std::size_t
+ stack_write_some(
+ std::size_t size,
+ ConstBufferSequence const& buffers,
+ error_code& ec);
public:
/// The type of the next layer.
@@ -102,7 +117,7 @@ public:
typename std::remove_reference::type;
/// The type of the executor associated with the object.
- using executor_type = typename next_layer_type::executor_type;
+ using executor_type = beast::executor_type;
flat_stream(flat_stream&&) = default;
flat_stream(flat_stream const&) = default;
@@ -322,6 +337,6 @@ public:
} // beast
} // boost
-#include
+#include
#endif
diff --git a/include/boost/beast/_experimental/core/impl/flat_stream.hpp b/include/boost/beast/core/impl/flat_stream.hpp
similarity index 65%
rename from include/boost/beast/_experimental/core/impl/flat_stream.hpp
rename to include/boost/beast/core/impl/flat_stream.hpp
index 93ea5c5a..f1f6cd71 100644
--- a/include/boost/beast/_experimental/core/impl/flat_stream.hpp
+++ b/include/boost/beast/core/impl/flat_stream.hpp
@@ -12,6 +12,7 @@
#include
#include
+#include
#include
#include
#include
@@ -22,39 +23,16 @@ namespace boost {
namespace beast {
template
-template
+template
class flat_stream::write_op
: public async_op_base>
-
, public net::coroutine
{
- using alloc_type = std::allocator;
-
- struct deleter
- {
- alloc_type alloc;
- std::size_t size = 0;
-
- explicit
- deleter(alloc_type const& alloc_)
- : alloc(alloc_)
- {
- }
-
- void
- operator()(char* p)
- {
- alloc.deallocate(p, size);
- }
- };
-
- flat_stream& s_;
- ConstBufferSequence b_;
- std::unique_ptr p_;
-
public:
- template
+ template<
+ class ConstBufferSequence,
+ class Handler_>
write_op(
flat_stream& s,
ConstBufferSequence const& b,
@@ -63,11 +41,26 @@ public:
beast::executor_type>(
std::forward(h),
s.get_executor())
- , s_(s)
- , b_(b)
- , p_(nullptr, deleter{alloc_type{}})
{
- (*this)({}, 0);
+ auto const result =
+ coalesce(b, coalesce_limit);
+ if(result.needs_coalescing)
+ {
+ s.buffer_.clear();
+ s.buffer_.commit(net::buffer_copy(
+ s.buffer_.prepare(result.size),
+ b, result.size));
+ s.stream_.async_write_some(
+ s.buffer_.data(), std::move(*this));
+ }
+ else
+ {
+ s.buffer_.clear();
+ s.buffer_.shrink_to_fit();
+ s.stream_.async_write_some(
+ beast::buffers_prefix(
+ result.size, b), std::move(*this));
+ }
}
void
@@ -75,36 +68,7 @@ public:
boost::system::error_code ec,
std::size_t bytes_transferred)
{
- BOOST_ASIO_CORO_REENTER(*this)
- {
- BOOST_ASIO_CORO_YIELD
- {
- auto const result =
- coalesce(b_, coalesce_limit);
- if(result.needs_coalescing)
- {
- p_.get_deleter().size = result.size;
- p_.reset(p_.get_deleter().alloc.allocate(
- p_.get_deleter().size));
- net::buffer_copy(
- net::buffer(
- p_.get(), p_.get_deleter().size),
- b_, result.size);
- s_.stream_.async_write_some(
- net::buffer(
- p_.get(), p_.get_deleter().size),
- std::move(*this));
- }
- else
- {
- s_.stream_.async_write_some(
- boost::beast::buffers_prefix(
- result.size, b_), std::move(*this));
- }
- }
- p_.reset();
- this->invoke(ec, bytes_transferred);
- }
+ this->invoke(ec, bytes_transferred);
}
};
@@ -128,7 +92,7 @@ read_some(MutableBufferSequence const& buffers)
"SyncReadStream requirements not met");
static_assert(net::is_mutable_buffer_sequence<
MutableBufferSequence>::value,
- "MutableBufferSequence requirements not met");
+ "MutableBufferSequence requirements not met");
error_code ec;
auto n = read_some(buffers, ec);
if(ec)
@@ -142,6 +106,11 @@ std::size_t
flat_stream::
read_some(MutableBufferSequence const& buffers, error_code& ec)
{
+ static_assert(boost::beast::is_sync_read_stream::value,
+ "SyncReadStream requirements not met");
+ static_assert(net::is_mutable_buffer_sequence<
+ MutableBufferSequence>::value,
+ "MutableBufferSequence requirements not met");
return stream_.read_some(buffers, ec);
}
@@ -159,7 +128,7 @@ async_read_some(
static_assert(boost::beast::is_async_read_stream::value,
"AsyncReadStream requirements not met");
static_assert(net::is_mutable_buffer_sequence<
- MutableBufferSequence >::value,
+ MutableBufferSequence >::value,
"MutableBufferSequence requirements not met");
return stream_.async_read_some(
buffers, std::forward(handler));
@@ -176,16 +145,26 @@ write_some(ConstBufferSequence const& buffers)
static_assert(net::is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
- auto const result = coalesce(buffers, coalesce_limit);
- if(result.needs_coalescing)
- {
- std::unique_ptr p{new char[result.size]};
- auto const b = net::buffer(p.get(), result.size);
- net::buffer_copy(b, buffers);
- return stream_.write_some(b);
- }
- return stream_.write_some(
- boost::beast::buffers_prefix(result.size, buffers));
+ error_code ec;
+ auto n = write_some(buffers, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(boost::system::system_error{ec});
+ return n;
+}
+
+template
+template
+std::size_t
+flat_stream::
+stack_write_some(
+ std::size_t size,
+ ConstBufferSequence const& buffers,
+ error_code& ec)
+{
+ static_buffer b;
+ b.commit(net::buffer_copy(
+ b.prepare(size), buffers));
+ return stream_.write_some(b.data(), ec);
}
template
@@ -198,15 +177,21 @@ write_some(ConstBufferSequence const& buffers, error_code& ec)
"SyncWriteStream requirements not met");
static_assert(net::is_const_buffer_sequence<
ConstBufferSequence>::value,
- "ConstBufferSequence requirements not met");
+ "ConstBufferSequence requirements not met");
auto const result = coalesce(buffers, coalesce_limit);
if(result.needs_coalescing)
{
- std::unique_ptr p{new char[result.size]};
- auto const b = net::buffer(p.get(), result.size);
- net::buffer_copy(b, buffers);
- return stream_.write_some(b, ec);
+ if(result.size > max_stack)
+ return stack_write_some(result.size, buffers, ec);
+
+ buffer_.clear();
+ buffer_.commit(net::buffer_copy(
+ buffer_.prepare(result.size),
+ buffers));
+ return stream_.write_some(buffer_.data(), ec);
}
+ buffer_.clear();
+ buffer_.shrink_to_fit();
return stream_.write_some(
boost::beast::buffers_prefix(result.size, buffers), ec);
}
@@ -225,13 +210,13 @@ async_write_some(
static_assert(boost::beast::is_async_write_stream::value,
"AsyncWriteStream requirements not met");
static_assert(net::is_const_buffer_sequence<
- ConstBufferSequence>::value,
+ ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
BOOST_BEAST_HANDLER_INIT(
WriteHandler, void(error_code, std::size_t));
- write_op{
- *this, buffers, std::move(init.completion_handler)};
+ write_op(
+ *this, buffers, std::move(init.completion_handler));
return init.result.get();
}
diff --git a/test/beast/core/CMakeLists.txt b/test/beast/core/CMakeLists.txt
index d9890450..d481211c 100644
--- a/test/beast/core/CMakeLists.txt
+++ b/test/beast/core/CMakeLists.txt
@@ -51,6 +51,7 @@ add_executable (tests-beast-core
file_win32.cpp
flat_buffer.cpp
flat_static_buffer.cpp
+ flat_stream.cpp
handler_ptr.cpp
make_printable.cpp
multi_buffer.cpp
diff --git a/test/beast/core/Jamfile b/test/beast/core/Jamfile
index c53ce101..c63b3ff8 100644
--- a/test/beast/core/Jamfile
+++ b/test/beast/core/Jamfile
@@ -39,6 +39,7 @@ local SOURCES =
file_win32.cpp
flat_buffer.cpp
flat_static_buffer.cpp
+ flat_stream.cpp
handler_ptr.cpp
make_printable.cpp
multi_buffer.cpp
diff --git a/test/beast/experimental/flat_stream.cpp b/test/beast/core/flat_stream.cpp
similarity index 89%
rename from test/beast/experimental/flat_stream.cpp
rename to test/beast/core/flat_stream.cpp
index 1220e639..8687c5e4 100644
--- a/test/beast/experimental/flat_stream.cpp
+++ b/test/beast/core/flat_stream.cpp
@@ -8,11 +8,14 @@
//
// Test that header file is self-contained.
-#include
+#include
+
+#include "stream_tests.hpp"
#include
#include
#include
+#include
#include
#include
@@ -24,6 +27,13 @@ class flat_stream_test
, public test::enable_yield_to
{
public:
+ void
+ testStream()
+ {
+ test_sync_stream>();
+ test_async_stream>();
+ }
+
void
testSplit()
{
@@ -81,7 +91,7 @@ public:
test::ws_echo_server es{log};
net::io_context ioc;
websocket::stream> ws{ioc};
- ws.next_layer().next_layer().connect(es.stream());
+ get_lowest_layer(ws).connect(es.stream());
ws.async_handshake("localhost", "/",
[&](error_code)
{
@@ -97,6 +107,7 @@ public:
void
run() override
{
+ testStream();
testSplit();
testHttp();
testWebsocket();
diff --git a/test/beast/experimental/CMakeLists.txt b/test/beast/experimental/CMakeLists.txt
index 2397c684..5b870f5a 100644
--- a/test/beast/experimental/CMakeLists.txt
+++ b/test/beast/experimental/CMakeLists.txt
@@ -17,7 +17,6 @@ add_executable (tests-beast-experimental
${TEST_MAIN}
Jamfile
error.cpp
- flat_stream.cpp
icy_stream.cpp
ssl_stream.cpp
stream.cpp
diff --git a/test/beast/experimental/Jamfile b/test/beast/experimental/Jamfile
index a4d05381..ff7d2e4d 100644
--- a/test/beast/experimental/Jamfile
+++ b/test/beast/experimental/Jamfile
@@ -9,7 +9,6 @@
local SOURCES =
error.cpp
- flat_stream.cpp
icy_stream.cpp
ssl_stream.cpp
stream.cpp