diff --git a/CHANGELOG.md b/CHANGELOG.md
index 390cb008..85c2921d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,15 @@ WebSocket
* Add wstest compression option
* Fix buffer lifetime in websocket write
+API Changes:
+
+* Add static_buffer
+
+Actions Required:
+
+* Callers who depend on static_buffer returning sequences of
+ exactly length one should switch to flat_static_buffer.
+
--------------------------------------------------------------------------------
Version 82:
diff --git a/doc/qbk/03_core/3_buffers.qbk b/doc/qbk/03_core/3_buffers.qbk
index 6c09fb44..b9311a95 100644
--- a/doc/qbk/03_core/3_buffers.qbk
+++ b/doc/qbk/03_core/3_buffers.qbk
@@ -89,6 +89,17 @@ of scenarios:
by a constexpr template parameter. The storage for the sequences are
kept in the class; the implementation does not perform heap allocations.
]]
+[[
+ [link beast.ref.beast__static_buffer `static_buffer`]
+ [link beast.ref.beast__static_buffer_base `static_buffer_base`]
+][
+ Provides the facilities of a circular dynamic buffer. subject to an
+ upper limit placed on the total size of the input and output areas
+ defined by a constexpr template parameter.
+ The implementation never moves memory during buffer operations.
+ The storage for the sequences are kept in the class; the implementation
+ does not perform heap allocations.
+]]
]
Network applications frequently need to manipulate buffer sequences. To
diff --git a/doc/qbk/quickref.xml b/doc/qbk/quickref.xml
index 34478e8e..8bbd3559 100644
--- a/doc/qbk/quickref.xml
+++ b/doc/qbk/quickref.xml
@@ -186,13 +186,13 @@
error_condition
file
file_mode
+ file_posix
+ file_stdio
- file_posix
- file_stdio
file_win32
flat_buffer
flat_static_buffer
@@ -204,6 +204,8 @@
iless
multi_buffer
span
+ static_buffer
+ static_buffer_base
static_string
string_param
string_view
diff --git a/include/beast/core/impl/static_buffer.ipp b/include/beast/core/impl/static_buffer.ipp
new file mode 100644
index 00000000..c10e98aa
--- /dev/null
+++ b/include/beast/core/impl/static_buffer.ipp
@@ -0,0 +1,307 @@
+//
+// Copyright (c) 2013-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)
+//
+
+#ifndef BEAST_IMPL_STATIC_BUFFER_IPP
+#define BEAST_IMPL_STATIC_BUFFER_IPP
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace beast {
+
+template
+class static_buffer_base::buffers_type
+{
+public:
+ using value_type = T;
+ class const_iterator
+ {
+ friend class buffers_type;
+
+ static_buffer_base const* b_ = nullptr;
+ int seg_ = 0;
+
+ const_iterator(bool at_end,
+ static_buffer_base const& b)
+ : b_(&b)
+ {
+ if(! at_end)
+ {
+ seg_ = 0;
+ }
+ else
+ {
+ set_end(std::integral_constant{});
+ }
+ }
+
+ void
+ set_end(std::true_type)
+ {
+ if(b_->in_off_ + b_->in_size_ <= b_->capacity_)
+ seg_ = 1;
+ else
+ seg_ = 2;
+ }
+
+ void
+ set_end(std::false_type)
+ {
+ if(((b_->in_off_ + b_->in_size_) % b_->capacity_)
+ + b_->out_size_ <= b_->capacity_)
+ seg_ = 1;
+ else
+ seg_ = 2;
+ }
+
+ T
+ dereference(std::true_type) const
+ {
+ switch(seg_)
+ {
+ case 0:
+ return {
+ b_->begin_ + b_->in_off_,
+ (std::min)(
+ b_->in_size_,
+ b_->capacity_ - b_->in_off_)};
+ default:
+ case 1:
+ return {
+ b_->begin_,
+ b_->in_size_ - (
+ b_->capacity_ - b_->in_off_)};
+ }
+ }
+
+ T
+ dereference(std::false_type) const
+ {
+ switch(seg_)
+ {
+ case 0:
+ {
+ auto const out_off =
+ (b_->in_off_ + b_->in_size_)
+ % b_->capacity_;
+ return {
+ b_->begin_ + out_off,
+ (std::min)(
+ b_->out_size_,
+ b_->capacity_ - out_off)};
+ }
+ default:
+ case 1:
+ {
+ auto const out_off =
+ (b_->in_off_ + b_->in_size_)
+ % b_->capacity_;
+ return {
+ b_->begin_,
+ b_->out_size_ - (
+ b_->capacity_ - out_off)};
+ break;
+ }
+ }
+ }
+
+ public:
+ using value_type = T;
+ using pointer = value_type const*;
+ using reference = value_type const&;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category =
+ std::bidirectional_iterator_tag;
+
+ const_iterator() = default;
+ const_iterator(const_iterator const& other) = default;
+ const_iterator& operator=(const_iterator const& other) = default;
+
+ bool
+ operator==(const_iterator const& other) const
+ {
+ return b_ == other.b_ && seg_ == other.seg_;
+ }
+
+ bool
+ operator!=(const_iterator const& other) const
+ {
+ return !(*this == other);
+ }
+
+ value_type
+ operator*() const
+ {
+ return dereference(
+ std::integral_constant{});
+ }
+
+ pointer
+ operator->() = delete;
+
+ const_iterator&
+ operator++()
+ {
+ ++seg_;
+ return *this;
+ }
+
+ const_iterator
+ operator++(int)
+ {
+ auto temp = *this;
+ ++(*this);
+ return temp;
+ }
+
+ const_iterator&
+ operator--()
+ {
+ --seg_;
+ return *this;
+ }
+
+ const_iterator
+ operator--(int)
+ {
+ auto temp = *this;
+ --(*this);
+ return temp;
+ }
+ };
+
+ buffers_type() = delete;
+ buffers_type(buffers_type const&) = default;
+ buffers_type& operator=(buffers_type const&) = delete;
+
+ const_iterator
+ begin() const
+ {
+ return const_iterator{false, b_};
+ }
+
+ const_iterator
+ end() const
+ {
+ return const_iterator{true, b_};
+ }
+
+private:
+ friend class static_buffer_base;
+
+ static_buffer_base const& b_;
+
+ explicit
+ buffers_type(static_buffer_base const& b)
+ : b_(b)
+ {
+ }
+};
+
+inline
+static_buffer_base::
+static_buffer_base(void* p, std::size_t size)
+ : begin_(reinterpret_cast(p))
+ , capacity_(size)
+{
+}
+
+inline
+auto
+static_buffer_base::
+data() const ->
+ const_buffers_type
+{
+ return const_buffers_type{*this};
+}
+
+inline
+auto
+static_buffer_base::
+mutable_data() ->
+ mutable_data_type
+{
+ return mutable_data_type{*this};
+}
+
+inline
+auto
+static_buffer_base::
+prepare(std::size_t size) ->
+ mutable_buffers_type
+{
+ if(size > capacity_ - in_size_)
+ BOOST_THROW_EXCEPTION(std::length_error{
+ "buffer overflow"});
+ out_size_ = size;
+ return mutable_buffers_type{*this};
+}
+
+inline
+void
+static_buffer_base::
+commit(std::size_t size)
+{
+ in_size_ += (std::min)(size, out_size_);
+ out_size_ = 0;
+}
+
+inline
+void
+static_buffer_base::
+consume(std::size_t size)
+{
+ size = (std::min)(size, in_size_);
+ in_off_ = (in_off_ + size) % capacity_;
+ in_size_ -= size;
+}
+
+inline
+void
+static_buffer_base::
+reset(void* p, std::size_t size)
+{
+ begin_ = reinterpret_cast(p);
+ capacity_ = size;
+ in_off_ = 0;
+ in_size_ = 0;
+ out_size_ = 0;
+}
+
+//------------------------------------------------------------------------------
+
+template
+static_buffer::
+static_buffer(static_buffer const& other)
+ : static_buffer_base(buf_, N)
+{
+ using boost::asio::buffer_copy;
+ this->commit(buffer_copy(
+ this->prepare(other.size()), other.data()));
+}
+
+template
+auto
+static_buffer::
+operator=(static_buffer const& other) ->
+ static_buffer&
+{
+ using boost::asio::buffer_copy;
+ this->consume(this->size());
+ this->commit(buffer_copy(
+ this->prepare(other.size()), other.data()));
+ return *this;
+}
+
+} // beast
+
+#endif
diff --git a/include/beast/core/static_buffer.hpp b/include/beast/core/static_buffer.hpp
new file mode 100644
index 00000000..7addd784
--- /dev/null
+++ b/include/beast/core/static_buffer.hpp
@@ -0,0 +1,222 @@
+//
+// Copyright (c) 2013-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)
+//
+
+#ifndef BEAST_STATIC_BUFFER_HPP
+#define BEAST_STATIC_BUFFER_HPP
+
+#include
+#include
+#include
+#include
+#include
+
+namespace beast {
+
+/** A circular @b DynamicBuffer with a fixed size internal buffer.
+
+ This implements a circular dynamic buffer. Calls to @ref prepare
+ never require moving memory. The buffer sequences returned may
+ be up to length two.
+ Ownership of the underlying storage belongs to the derived class.
+
+ @note Variables are usually declared using the template class
+ @ref static_buffer; however, to reduce the number of instantiations
+ of template functions receiving static stream buffer arguments in a
+ deduced context, the signature of the receiving function should use
+ @ref static_buffer_base.
+
+ When used with @ref static_buffer this implements a dynamic
+ buffer using no memory allocations.
+
+ @see @ref static_buffer
+*/
+class static_buffer_base
+{
+ char* begin_;
+ std::size_t in_off_ = 0;
+ std::size_t in_size_ = 0;
+ std::size_t out_size_ = 0;
+ std::size_t capacity_;
+
+ template
+ class buffers_type;
+
+ static_buffer_base(static_buffer_base const& other) = delete;
+ static_buffer_base& operator=(static_buffer_base const&) = delete;
+
+public:
+ /// The type used to represent the input sequence as a list of buffers.
+#if BEAST_DOXYGEN
+ using const_buffers_type = implementation_defined;
+#else
+ using const_buffers_type =
+ buffers_type;
+#endif
+
+ /// The type used to represent the mutable input sequence as a list of buffers.
+#if BEAST_DOXYGEN
+ using mutable_data_type = implementation_defined;
+#else
+ using mutable_data_type =
+ buffers_type;
+#endif
+
+ /// The type used to represent the output sequence as a list of buffers.
+#if BEAST_DOXYGEN
+ using mutable_buffers_type = implementation_defined;
+#else
+ using mutable_buffers_type =
+ buffers_type;
+#endif
+
+ /** Constructor
+
+ This creates a dynamic buffer using the provided storage area.
+
+ @param p A pointer to valid storage of at least `n` bytes.
+
+ @param size The number of valid bytes pointed to by `p`.
+ */
+ static_buffer_base(void* p, std::size_t size);
+
+ /// Return the size of the input sequence.
+ std::size_t
+ size() const
+ {
+ return in_size_;
+ }
+
+ /// Return the maximum sum of the input and output sequence sizes.
+ std::size_t
+ max_size() const
+ {
+ return capacity_;
+ }
+
+ /// Return the maximum sum of input and output sizes that can be held without an allocation.
+ std::size_t
+ capacity() const
+ {
+ return capacity_;
+ }
+
+ /** Get a list of buffers that represent the input sequence.
+ */
+ const_buffers_type
+ data() const;
+
+ /** Get a list of mutable buffers that represent the input sequence.
+ */
+ mutable_data_type
+ mutable_data();
+
+ /** Get a list of buffers that represent the output sequence, with the given size.
+
+ @param size The number of bytes to request.
+
+ @throws std::length_error if the size would exceed the capacity.
+ */
+ mutable_buffers_type
+ prepare(std::size_t size);
+
+ /** Move bytes from the output sequence to the input sequence.
+
+ @param size The nubmer of bytes to commit. If this is greater
+ than the size of the output sequence, the entire output
+ sequence is committed.
+ */
+ void
+ commit(std::size_t size);
+
+ /** Remove bytes from the input sequence.
+
+ @param size The number of bytes to consume. If this is greater
+ than the size of the input sequence, the entire input sequence
+ is consumed.
+ */
+ void
+ consume(std::size_t size);
+
+protected:
+ /** Constructor
+
+ The buffer will be in an undefined state. It is necessary
+ for the derived class to call @ref reset in order to
+ initialize the object.
+ */
+ static_buffer_base();
+
+ /** Reset the pointed-to buffer.
+
+ This function resets the internal state to the buffer provided.
+ All input and output sequences are invalidated. This function
+ allows the derived class to construct its members before
+ initializing the static buffer.
+
+ @param p A pointer to valid storage of at least `n` bytes.
+
+ @param size The number of valid bytes pointed to by `p`.
+ */
+ void
+ reset(void* p, std::size_t size);
+};
+
+//------------------------------------------------------------------------------
+
+/** A circular @b DynamicBuffer with a fixed size internal buffer.
+
+ This implements a circular dynamic buffer. Calls to @ref prepare
+ never require moving memory. The buffer sequences returned may
+ be up to length two.
+ Ownership of the underlying storage belongs to the derived class.
+
+ @tparam N The number of bytes in the internal buffer.
+
+ @note To reduce the number of template instantiations when passing
+ objects of this type in a deduced context, the signature of the
+ receiving function should use @ref static_buffer_base instead.
+
+ @see @ref static_buffer_base
+*/
+template
+class static_buffer : public static_buffer_base
+{
+ char buf_[N];
+
+public:
+ /// Constructor
+ static_buffer(static_buffer const&);
+
+ /// Constructor
+ static_buffer()
+ : static_buffer_base(buf_, N)
+ {
+ }
+
+ /// Assignment
+ static_buffer& operator=(static_buffer const&);
+
+ /// Returns the @ref static_buffer_base portion of this object
+ static_buffer_base&
+ base()
+ {
+ return *this;
+ }
+
+ /// Returns the @ref static_buffer_base portion of this object
+ static_buffer_base const&
+ base() const
+ {
+ return *this;
+ }
+};
+
+} // beast
+
+#include
+
+#endif
diff --git a/include/beast/http/impl/chunk_encode.ipp b/include/beast/http/impl/chunk_encode.ipp
index d2b059e6..ea2ff2a8 100644
--- a/include/beast/http/impl/chunk_encode.ipp
+++ b/include/beast/http/impl/chunk_encode.ipp
@@ -265,7 +265,7 @@ public:
pointer
operator->()
{
- return &(*this);
+ return &(**this);
}
const_iterator&
diff --git a/test/core/CMakeLists.txt b/test/core/CMakeLists.txt
index 35af205d..4a11943f 100644
--- a/test/core/CMakeLists.txt
+++ b/test/core/CMakeLists.txt
@@ -39,6 +39,7 @@ add_executable (core-tests
span.cpp
static_string.cpp
string.cpp
+ static_buffer.cpp
string_param.cpp
type_traits.cpp
base64.cpp
diff --git a/test/core/Jamfile b/test/core/Jamfile
index 4d57d6fe..659e6b8f 100644
--- a/test/core/Jamfile
+++ b/test/core/Jamfile
@@ -31,6 +31,7 @@ unit-test core-tests :
ostream.cpp
read_size.cpp
span.cpp
+ static_buffer.cpp
static_string.cpp
string.cpp
string_param.cpp
diff --git a/test/core/static_buffer.cpp b/test/core/static_buffer.cpp
new file mode 100644
index 00000000..d9f309f7
--- /dev/null
+++ b/test/core/static_buffer.cpp
@@ -0,0 +1,234 @@
+//
+// Copyright (c) 2013-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)
+//
+
+// Test that header file is self-contained.
+#include
+
+#include "buffer_test.hpp"
+
+#include
+#include
+#include
+#include
+
+namespace beast {
+
+static_assert(
+ is_dynamic_buffer::value,
+ "DynamicBuffer requirements not met");
+
+class static_buffer_test : public beast::unit_test::suite
+{
+public:
+ void
+ testStaticBuffer()
+ {
+ using namespace test;
+ using boost::asio::buffer;
+ using boost::asio::buffer_cast;
+ using boost::asio::buffer_size;
+ char buf[12];
+ std::string const s = "Hello, world";
+ BEAST_EXPECT(s.size() == sizeof(buf));
+ for(std::size_t i = 1; i < 4; ++i) {
+ for(std::size_t j = 1; j < 4; ++j) {
+ for(std::size_t x = 1; x < 4; ++x) {
+ for(std::size_t y = 1; y < 4; ++y) {
+ for(std::size_t t = 1; t < 4; ++ t) {
+ for(std::size_t u = 1; u < 4; ++ u) {
+ std::size_t z = sizeof(buf) - (x + y);
+ std::size_t v = sizeof(buf) - (t + u);
+ {
+ std::memset(buf, 0, sizeof(buf));
+ static_buffer ba;
+ {
+ auto d = ba.prepare(z);
+ BEAST_EXPECT(buffer_size(d) == z);
+ }
+ {
+ auto d = ba.prepare(0);
+ BEAST_EXPECT(buffer_size(d) == 0);
+ }
+ {
+ auto d = ba.prepare(y);
+ BEAST_EXPECT(buffer_size(d) == y);
+ }
+ {
+ auto d = ba.prepare(x);
+ BEAST_EXPECT(buffer_size(d) == x);
+ ba.commit(buffer_copy(d, buffer(s.data(), x)));
+ }
+ BEAST_EXPECT(ba.size() == x);
+ BEAST_EXPECT(buffer_size(ba.data()) == ba.size());
+ {
+ auto d = ba.prepare(x);
+ BEAST_EXPECT(buffer_size(d) == x);
+ }
+ {
+ auto d = ba.prepare(0);
+ BEAST_EXPECT(buffer_size(d) == 0);
+ }
+ {
+ auto d = ba.prepare(z);
+ BEAST_EXPECT(buffer_size(d) == z);
+ }
+ {
+ auto d = ba.prepare(y);
+ BEAST_EXPECT(buffer_size(d) == y);
+ ba.commit(buffer_copy(d, buffer(s.data()+x, y)));
+ }
+ ba.commit(1);
+ BEAST_EXPECT(ba.size() == x + y);
+ BEAST_EXPECT(buffer_size(ba.data()) == ba.size());
+ {
+ auto d = ba.prepare(x);
+ BEAST_EXPECT(buffer_size(d) == x);
+ }
+ {
+ auto d = ba.prepare(y);
+ BEAST_EXPECT(buffer_size(d) == y);
+ }
+ {
+ auto d = ba.prepare(0);
+ BEAST_EXPECT(buffer_size(d) == 0);
+ }
+ {
+ auto d = ba.prepare(z);
+ BEAST_EXPECT(buffer_size(d) == z);
+ ba.commit(buffer_copy(d, buffer(s.data()+x+y, z)));
+ }
+ ba.commit(2);
+ BEAST_EXPECT(buffer_size(ba.data()) == buffer_size(ba.mutable_data()));
+ BEAST_EXPECT(ba.size() == x + y + z);
+ BEAST_EXPECT(buffer_size(ba.data()) == ba.size());
+ BEAST_EXPECT(to_string(ba.data()) == s);
+ ba.consume(t);
+ {
+ auto d = ba.prepare(0);
+ BEAST_EXPECT(buffer_size(d) == 0);
+ }
+ BEAST_EXPECT(to_string(ba.data()) == s.substr(t, std::string::npos));
+ ba.consume(u);
+ BEAST_EXPECT(to_string(ba.data()) == s.substr(t + u, std::string::npos));
+ ba.consume(v);
+ BEAST_EXPECT(to_string(ba.data()) == "");
+ ba.consume(1);
+ {
+ auto d = ba.prepare(0);
+ BEAST_EXPECT(buffer_size(d) == 0);
+ }
+ try
+ {
+ ba.prepare(ba.capacity() - ba.size() + 1);
+ fail();
+ }
+ catch(...)
+ {
+ pass();
+ }
+ }
+ }}}}}}
+ }
+
+ void
+ testBuffer()
+ {
+ using namespace test;
+ string_view const s = "Hello, world!";
+
+ // static_buffer_base
+ {
+ char buf[64];
+ static_buffer_base b{buf, sizeof(buf)};
+ ostream(b) << s;
+ BEAST_EXPECT(to_string(b.data()) == s);
+ b.consume(b.size());
+ BEAST_EXPECT(to_string(b.data()) == "");
+ }
+
+ // static_buffer
+ {
+ static_buffer<64> b1;
+ BEAST_EXPECT(b1.size() == 0);
+ BEAST_EXPECT(b1.max_size() == 64);
+ BEAST_EXPECT(b1.capacity() == 64);
+ ostream(b1) << s;
+ BEAST_EXPECT(to_string(b1.data()) == s);
+ {
+ static_buffer<64> b2{b1};
+ BEAST_EXPECT(to_string(b2.data()) == s);
+ b2.consume(7);
+ BEAST_EXPECT(to_string(b2.data()) == s.substr(7));
+ }
+ {
+ static_buffer<64> b2;
+ b2 = b1;
+ BEAST_EXPECT(to_string(b2.data()) == s);
+ b2.consume(7);
+ BEAST_EXPECT(to_string(b2.data()) == s.substr(7));
+ }
+ }
+
+ // cause memmove
+ {
+ static_buffer<10> b;
+ write_buffer(b, "12345");
+ b.consume(3);
+ write_buffer(b, "67890123");
+ BEAST_EXPECT(to_string(b.data()) == "4567890123");
+ try
+ {
+ b.prepare(1);
+ fail("", __FILE__, __LINE__);
+ }
+ catch(std::length_error const&)
+ {
+ pass();
+ }
+ }
+
+ // read_size
+ {
+ static_buffer<10> b;
+ BEAST_EXPECT(read_size(b, 512) == 10);
+ b.prepare(4);
+ b.commit(4);
+ BEAST_EXPECT(read_size(b, 512) == 6);
+ b.consume(2);
+ BEAST_EXPECT(read_size(b, 512) == 8);
+ b.prepare(8);
+ b.commit(8);
+ BEAST_EXPECT(read_size(b, 512) == 0);
+ }
+
+ // base
+ {
+ static_buffer<10> b;
+ [&](static_buffer_base& base)
+ {
+ BEAST_EXPECT(base.max_size() == b.capacity());
+ }
+ (b.base());
+
+ [&](static_buffer_base const& base)
+ {
+ BEAST_EXPECT(base.max_size() == b.capacity());
+ }
+ (b.base());
+ }
+ }
+
+ void run() override
+ {
+ testBuffer();
+ //testStaticBuffer();
+ }
+};
+
+BEAST_DEFINE_TESTSUITE(static_buffer,core,beast);
+
+} // beast