diff --git a/Builds/VisualStudio2013/beast.vcxproj b/Builds/VisualStudio2013/beast.vcxproj
index b9ab9df0a..2da17674f 100644
--- a/Builds/VisualStudio2013/beast.vcxproj
+++ b/Builds/VisualStudio2013/beast.vcxproj
@@ -130,6 +130,7 @@
+
@@ -468,6 +469,12 @@
true
true
+
+ true
+ true
+ true
+ true
+
true
diff --git a/Builds/VisualStudio2013/beast.vcxproj.filters b/Builds/VisualStudio2013/beast.vcxproj.filters
index b96a696fb..6fb476455 100644
--- a/Builds/VisualStudio2013/beast.vcxproj.filters
+++ b/Builds/VisualStudio2013/beast.vcxproj.filters
@@ -306,6 +306,9 @@
{5745a887-7df8-4059-87ea-e0c7eea77a9b}
+
+ {4b468051-9e97-4548-a5f0-469425c3b603}
+
@@ -1326,6 +1329,9 @@
beast\cxx14
+
+ beast\container
+
@@ -1910,6 +1916,9 @@
beast\container\impl
+
+ beast\container\tests
+
diff --git a/beast/container/Container.cpp b/beast/container/Container.cpp
index 21948eb8f..675854b17 100644
--- a/beast/container/Container.cpp
+++ b/beast/container/Container.cpp
@@ -20,3 +20,6 @@
#include "BeastConfig.h"
#include "impl/aged_associative_container.cpp"
+
+#include "tests/buffer_view.test.cpp"
+
diff --git a/beast/container/buffer_view.h b/beast/container/buffer_view.h
new file mode 100644
index 000000000..f9e2ddeef
--- /dev/null
+++ b/beast/container/buffer_view.h
@@ -0,0 +1,519 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#ifndef BEAST_CONTAINER_BUFFER_VIEW_H_INCLUDED
+#define BEAST_CONTAINER_BUFFER_VIEW_H_INCLUDED
+
+#include "../Config.h"
+
+#include "../cxx14/algorithm.h" //
+#include
+#include
+#include
+#include
+#include
+#include "../cxx14/type_traits.h" //
+
+namespace beast {
+
+namespace detail {
+
+template >::value>
+struct apply_const
+{
+ typedef U type;
+};
+
+template
+struct apply_const
+{
+ typedef const U type;
+};
+
+// is_contiguous is true if C is a contiguous container
+template
+struct is_contiguous
+ : public std::false_type
+{
+};
+
+template
+struct is_contiguous
+ : public is_contiguous
+{
+};
+
+template
+struct is_contiguous >
+ : public std::true_type
+{
+};
+
+template
+struct is_contiguous >
+ : public std::true_type
+{
+};
+
+template
+struct is_contiguous >
+ : public std::true_type
+{
+};
+
+// True if T is const or U is not const
+template
+struct buffer_view_const_compatible : std::integral_constant ::value || ! std::is_const::value
+>
+{
+};
+
+// True if T and U are the same or differ only in const, or
+// if T and U are equally sized integral types.
+template
+struct buffer_view_ptr_compatible : std::integral_constant , std::remove_const >::value) ||
+ (std::is_integral ::value && std::is_integral ::value &&
+ sizeof (U) == sizeof (T))
+>
+{
+};
+
+// Determine if buffer_view is constructible from U*
+template
+struct buffer_view_convertible : std::integral_constant ::value &&
+ buffer_view_ptr_compatible ::value
+>
+{
+};
+
+// True if C is a container that can be used to construct a buffer_view
+template
+struct buffer_view_container_compatible : std::integral_constant ::value && buffer_view_convertible ::type>::value
+>
+{
+};
+
+} // detail
+
+struct buffer_view_default_tag
+{
+};
+
+//------------------------------------------------------------------------------
+
+/** A view into a range of contiguous container elements.
+
+ The size of the view is determined at the time of construction.
+ This tries to emulate the interface of std::vector as closely as possible,
+ with the constraint that the size of the container cannot be changed.
+
+ @tparam T The underlying element type. If T is const, member functions
+ which can modify elements are removed from the interface.
+
+ @tparam Tag A type used to prevent two views with the same T from being
+ comparable or assignable.
+*/
+template <
+ class T,
+ class Tag = buffer_view_default_tag
+>
+class buffer_view
+{
+private:
+ T* m_base;
+ std::size_t m_size;
+
+ static_assert (std::is_same >::value,
+ "T may not be a reference type");
+
+ static_assert (! std::is_same ::value,
+ "T may not be void");
+
+ static_assert (std::is_same ,
+ std::remove_reference_t const>::value,
+ "Expected std::add_const to produce T const");
+
+ template
+ void
+ assign (Iter first, Iter last) noexcept
+ {
+ typedef typename std::iterator_traits ::value_type U;
+
+ static_assert (detail::buffer_view_const_compatible ::value,
+ "Cannot convert from 'U const' to 'T', "
+ "conversion loses const qualifiers");
+
+ static_assert (detail::buffer_view_ptr_compatible ::value,
+ "Cannot convert from 'U*' to 'T*, "
+ "types are incompatible");
+
+ if (first == last)
+ {
+ m_base = nullptr;
+ m_size = 0;
+ }
+ else
+ {
+ #if 0
+ // fails on gcc
+ m_base = reinterpret_cast (
+ std::addressof (*first));
+ #else
+ m_base = reinterpret_cast (&*first);
+ #endif
+ m_size = std::distance (first, last);
+ }
+ }
+
+public:
+ typedef T value_type;
+ typedef std::size_t size_type;
+ typedef std::ptrdiff_t difference_type;
+ typedef T& reference;
+ typedef T const& const_reference;
+ typedef T* pointer;
+ typedef T const* const_pointer;
+ typedef T* iterator;
+ typedef T const* const_iterator;
+ typedef std::reverse_iterator reverse_iterator;
+ typedef std::reverse_iterator const_reverse_iterator;
+
+ // default construct
+ buffer_view () noexcept
+ : m_base (nullptr)
+ , m_size (0)
+ {
+ }
+
+ // copy construct
+ template ::value>
+ >
+ buffer_view (buffer_view v) noexcept
+ {
+ assign (v.begin(), v.end());
+ }
+
+ // construct from container
+ template ::value
+ >
+ >
+ buffer_view (C& c) noexcept
+ {
+ assign (c.begin(), c.end());
+ }
+
+ // construct from pointer range
+ template ::value>
+ >
+ buffer_view (U* first, U* last) noexcept
+ {
+ assign (first, last);
+ }
+
+ // construct from base and size
+ template ::value>
+ >
+ buffer_view (U* u, std::size_t n) noexcept
+ : m_base (u)
+ , m_size (n)
+ {
+ }
+
+ // assign from container
+ template ::value
+ >
+ >
+ buffer_view&
+ operator= (C& c) noexcept
+ {
+ assign (c.begin(), c.end());
+ return *this;
+ }
+
+ //
+ // Element access
+ //
+
+ reference
+ at (size_type pos)
+ {
+ if (! (pos < size()))
+ throw std::out_of_range ("bad array index");
+ return m_base [pos];
+ }
+
+ const_reference
+ at (size_type pos) const
+ {
+ if (! (pos < size()))
+ throw std::out_of_range ("bad array index");
+ return m_base [pos];
+ }
+
+ reference
+ operator[] (size_type pos) noexcept
+ {
+ return m_base [pos];
+ }
+
+ const_reference
+ operator[] (size_type pos) const noexcept
+ {
+ return m_base [pos];
+ }
+
+ reference
+ back() noexcept
+ {
+ return m_base [m_size - 1];
+ }
+
+ const_reference
+ back() const noexcept
+ {
+ return m_base [m_size - 1];
+ }
+
+ reference
+ front() noexcept
+ {
+ return *m_base;
+ }
+
+ const_reference
+ front() const noexcept
+ {
+ return *m_base;
+ }
+
+ pointer
+ data() noexcept
+ {
+ return m_base;
+ }
+
+ const_pointer
+ data() const noexcept
+ {
+ return m_base;
+ }
+
+ //
+ // Iterators
+ //
+
+ iterator
+ begin() noexcept
+ {
+ return m_base;
+ }
+
+ const_iterator
+ begin() const noexcept
+ {
+ return m_base;
+ }
+
+ const_iterator
+ cbegin() const noexcept
+ {
+ return m_base;
+ }
+
+ iterator
+ end() noexcept
+ {
+ return m_base + m_size;
+ }
+
+ const_iterator
+ end() const noexcept
+ {
+ return m_base + m_size;
+ }
+
+ const_iterator
+ cend() const noexcept
+ {
+ return m_base + m_size;
+ }
+
+ reverse_iterator
+ rbegin() noexcept
+ {
+ return reverse_iterator (end());
+ }
+
+ const_reverse_iterator
+ rbegin() const noexcept
+ {
+ return const_reverse_iterator (cend());
+ }
+
+ const_reverse_iterator
+ crbegin() const noexcept
+ {
+ return const_reverse_iterator (cend());
+ }
+
+ reverse_iterator
+ rend() noexcept
+ {
+ return reverse_iterator (begin());
+ }
+
+ const_reverse_iterator
+ rend() const noexcept
+ {
+ return const_reverse_iterator (cbegin());
+ }
+
+ const_reverse_iterator
+ crend() const noexcept
+ {
+ return const_reverse_iterator (cbegin());
+ }
+
+ //
+ // Capacity
+ //
+
+ bool
+ empty() const noexcept
+ {
+ return m_size == 0;
+ }
+
+ size_type
+ size() const noexcept
+ {
+ return m_size;
+ }
+
+ size_type
+ max_size() const noexcept
+ {
+ return size();
+ }
+
+ size_type
+ capacity() const noexcept
+ {
+ return size();
+ }
+
+ //
+ // Modifiers
+ //
+
+ template
+ friend void swap (buffer_view & lhs,
+ buffer_view & rhs) noexcept;
+};
+
+//------------------------------------------------------------------------------
+
+template
+inline
+bool
+operator== (buffer_view lhs, buffer_view rhs)
+{
+ return std::equal (
+ lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend());
+}
+
+template
+inline
+bool
+operator!= (buffer_view lhs, buffer_view rhs)
+{
+ return ! (lhs == rhs);
+}
+
+template
+inline
+bool
+operator< (buffer_view lhs, buffer_view rhs)
+{
+ return std::lexicographical_compare (
+ lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend());
+}
+
+template
+inline
+bool
+operator>= (buffer_view lhs, buffer_view rhs)
+{
+ return ! (lhs < rhs);
+}
+
+template
+inline
+bool
+operator> (buffer_view lhs, buffer_view rhs)
+{
+ return rhs < lhs;
+}
+
+template
+inline
+bool
+operator<= (buffer_view lhs, buffer_view rhs)
+{
+ return ! (rhs < lhs);
+}
+
+template
+inline
+void
+swap (buffer_view & lhs, buffer_view & rhs) noexcept
+{
+ std::swap (lhs.m_base, rhs.m_base);
+ std::swap (lhs.m_size, rhs.m_size);
+}
+
+//------------------------------------------------------------------------------
+
+template <
+ class T,
+ class Tag = buffer_view_default_tag
+>
+using const_buffer_view = buffer_view <
+ std::add_const_t , Tag>;
+
+}
+
+#endif
diff --git a/beast/container/tests/buffer_view.test.cpp b/beast/container/tests/buffer_view.test.cpp
new file mode 100644
index 000000000..3bc217283
--- /dev/null
+++ b/beast/container/tests/buffer_view.test.cpp
@@ -0,0 +1,323 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#include "../../../modules/beast_core/beast_core.h" // for UnitTest
+#include "../buffer_view.h"
+
+#include "../../cxx14/algorithm.h" //
+
+namespace beast {
+
+class buffer_view_Tests : public UnitTest
+{
+public:
+ // Returns `true` if the iterator distance matches the size
+ template
+ static bool eq_dist (FwdIt first, FwdIt last, Size size)
+ {
+ auto const dist (std::distance (first, last));
+
+ static_assert (std::is_signed ::value,
+ "dist must be signed");
+
+ if (dist < 0)
+ return false;
+
+ return static_cast (dist) == size;
+ }
+
+ // Check the contents of a buffer_view against the container
+ template
+ void check (C const& c, buffer_view v)
+ {
+ expect (! v.empty() || c.empty());
+ expect (v.size() == c.size());
+ expect (v.max_size() == v.size());
+ expect (v.capacity() == v.size());
+
+ expect (eq_dist (v.begin(), v.end(), v.size()));
+ expect (eq_dist (v.cbegin(), v.cend(), v.size()));
+ expect (eq_dist (v.rbegin(), v.rend(), v.size()));
+ expect (eq_dist (v.crbegin(), v.crend(), v.size()));
+
+ expect (std::equal (
+ c.cbegin(), c.cend(), v.cbegin(), v.cend()));
+
+ expect (std::equal (
+ c.crbegin(), c.crend(), v.crbegin(), v.crend()));
+
+ if (v.size() == c.size())
+ {
+ if (! v.empty())
+ {
+ expect (v.front() == c.front());
+ expect (v.back() == c.back());
+ }
+
+ for (std::size_t i (0); i < v.size(); ++i)
+ expect (v[i] == c[i]);
+ }
+ }
+
+ //--------------------------------------------------------------------------
+
+ // Call at() with an invalid index
+ template
+ void checkBadIndex (V& v,
+ std::enable_if_t <
+ std::is_const ::value>* = 0)
+ {
+ try
+ {
+ v.at(0);
+ fail();
+ }
+ catch (std::out_of_range e)
+ {
+ pass();
+ }
+ catch (...)
+ {
+ fail();
+ }
+ }
+
+ // Call at() with an invalid index
+ template
+ void checkBadIndex (V& v,
+ std::enable_if_t <
+ ! std::is_const ::value>* = 0)
+ {
+ try
+ {
+ v.at(0);
+ fail();
+ }
+ catch (std::out_of_range e)
+ {
+ pass();
+ }
+ catch (...)
+ {
+ fail();
+ }
+
+ try
+ {
+ v.at(0) = 1;
+ fail();
+ }
+ catch (std::out_of_range e)
+ {
+ pass();
+ }
+ catch (...)
+ {
+ fail();
+ }
+ }
+
+ // Checks invariants for an empty buffer_view
+ template
+ void checkEmpty (V& v)
+ {
+ expect (v.empty());
+ expect (v.size() == 0);
+ expect (v.max_size() == v.size());
+ expect (v.capacity() == v.size());
+ expect (v.begin() == v.end());
+ expect (v.cbegin() == v.cend());
+ expect (v.begin() == v.cend());
+ expect (v.rbegin() == v.rend());
+ expect (v.crbegin() == v.rend());
+
+ checkBadIndex (v);
+ }
+
+ // Test empty containers
+ void testEmpty()
+ {
+ beginTestCase ("empty");
+
+ buffer_view v1;
+ checkEmpty (v1);
+
+ buffer_view v2;
+ swap (v1, v2);
+ checkEmpty (v1);
+ checkEmpty (v2);
+
+ buffer_view v3 (v2);
+ checkEmpty (v3);
+ }
+
+ //--------------------------------------------------------------------------
+
+ // Construct const views from a container
+ template
+ void testConstructConst (C const& c)
+ {
+ typedef buffer_view > V;
+
+ {
+ // construct from container
+ V v (c);
+ check (c, v);
+
+ // construct from buffer_view
+ V v2 (v);
+ check (c, v2);
+ }
+
+ if (! c.empty())
+ {
+ {
+ // construct from const pointer range
+ V v (&c.front(), &c.back()+1);
+ check (c, v);
+
+ // construct from pointer and size
+ V v2 (&c.front(), c.size());
+ check (v, v2);
+ }
+
+ {
+ // construct from non const pointer range
+ C cp (c);
+ V v (&cp.front(), &cp.back()+1);
+ check (cp, v);
+
+ // construct from pointer and size
+ V v2 (&cp.front(), cp.size());
+ check (v, v2);
+
+ // construct from data and size
+ V v3 (v2.data(), v2.size());
+ check (c, v3);
+ }
+ }
+ }
+
+ // Construct view from a container
+ template
+ void testConstruct (C const& c)
+ {
+ static_assert (! std::is_const ::value,
+ "Container value_type cannot be const");
+
+ testConstructConst (c);
+
+ typedef buffer_view V;
+
+ C cp (c);
+ V v (cp);
+ check (cp, v);
+
+ std::reverse (v.begin(), v.end());
+ check (cp, v);
+
+ expect (std::equal (v.rbegin(), v.rend(),
+ c.begin(), c.end()));
+ }
+
+ void testConstruct()
+ {
+ beginTestCase ("std::vector ");
+ testConstruct (
+ std::vector ({'h', 'e', 'l', 'l', 'o'}));
+
+ beginTestCase ("std::string ");
+ testConstruct (
+ std::basic_string ("hello"));
+ }
+
+ //--------------------------------------------------------------------------
+
+ void testCoerce()
+ {
+ beginTestCase ("coerce");
+
+ std::string const s ("hello");
+ const_buffer_view v (s);
+
+ pass();
+ }
+
+ //--------------------------------------------------------------------------
+
+ void testAssign()
+ {
+ beginTestCase ("testAssign");
+ std::vector v1({1, 2, 3});
+ buffer_view r1(v1);
+ std::vector v2({4, 5, 6, 7});
+ buffer_view r2(v2);
+ r1 = r2;
+ expect (std::equal (r1.begin(), r1.end(), v2.begin(), v2.end()));
+ }
+
+ //--------------------------------------------------------------------------
+
+ static_assert (std::is_constructible ,
+ std::vector &>::value, "");
+
+ static_assert (!std::is_constructible ,
+ std::vector const&>::value, "");
+
+ static_assert (std::is_constructible ,
+ std::vector &>::value, "");
+
+ static_assert (std::is_constructible ,
+ std::vector const&>::value, "");
+
+ static_assert (std::is_nothrow_default_constructible <
+ buffer_view >::value, "");
+
+ static_assert (std::is_nothrow_destructible <
+ buffer_view >::value, "");
+
+ static_assert (std::is_nothrow_copy_constructible <
+ buffer_view >::value, "");
+
+ static_assert (std::is_nothrow_copy_assignable <
+ buffer_view>::value, "");
+
+ static_assert (std::is_nothrow_move_constructible <
+ buffer_view >::value, "");
+
+ static_assert (std::is_nothrow_move_assignable <
+ buffer_view >::value, "");
+
+ void runTest()
+ {
+ testEmpty();
+ testConstruct();
+ testCoerce();
+ testAssign();
+ }
+
+ buffer_view_Tests() : UnitTest ("buffer_view", "beast")
+ {
+ }
+};
+
+static buffer_view_Tests buffer_view_tests;
+
+}