From f66aee4e7f39c185f7387c6da85c8c109233a594 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Tue, 11 Mar 2014 23:24:34 -0700 Subject: [PATCH] Add beast::buffer_view --- Builds/VisualStudio2013/RippleD.vcxproj | 1 + .../VisualStudio2013/RippleD.vcxproj.filters | 3 + SConstruct | 3 +- .../Builds/VisualStudio2013/beast.vcxproj | 7 + .../VisualStudio2013/beast.vcxproj.filters | 9 + src/beast/beast/container/Container.cpp | 3 + src/beast/beast/container/buffer_view.h | 519 ++++++++++++++++++ .../container/tests/buffer_view.test.cpp | 323 +++++++++++ 8 files changed, 867 insertions(+), 1 deletion(-) create mode 100644 src/beast/beast/container/buffer_view.h create mode 100644 src/beast/beast/container/tests/buffer_view.test.cpp diff --git a/Builds/VisualStudio2013/RippleD.vcxproj b/Builds/VisualStudio2013/RippleD.vcxproj index b6d61484bf..377831632e 100644 --- a/Builds/VisualStudio2013/RippleD.vcxproj +++ b/Builds/VisualStudio2013/RippleD.vcxproj @@ -2230,6 +2230,7 @@ + diff --git a/Builds/VisualStudio2013/RippleD.vcxproj.filters b/Builds/VisualStudio2013/RippleD.vcxproj.filters index eb8063f429..b04172fec9 100644 --- a/Builds/VisualStudio2013/RippleD.vcxproj.filters +++ b/Builds/VisualStudio2013/RippleD.vcxproj.filters @@ -3048,6 +3048,9 @@ [2] Old Ripple\ripple_app\transactors + + [1] Ripple\common + diff --git a/SConstruct b/SConstruct index 57904fdf71..d5c559b11c 100644 --- a/SConstruct +++ b/SConstruct @@ -60,7 +60,8 @@ if USING_CLANG: env.Append(LINKFLAGS='-stdlib=libstdc++') if OSX: - env.Append(CXXFLAGS = ['-std=c++11', '-stdlib=libc++']) + env.Append(CXXFLAGS = ['-std=c++11', '-stdlib=libc++', + '-Wno-deprecated-register']) env.Append(LINKFLAGS='-stdlib=libc++') env['FRAMEWORKS'] = ['AppKit','Foundation'] diff --git a/src/beast/Builds/VisualStudio2013/beast.vcxproj b/src/beast/Builds/VisualStudio2013/beast.vcxproj index b9ab9df0a5..2da17674fb 100644 --- a/src/beast/Builds/VisualStudio2013/beast.vcxproj +++ b/src/beast/Builds/VisualStudio2013/beast.vcxproj @@ -130,6 +130,7 @@ + @@ -468,6 +469,12 @@ true true + + true + true + true + true + true diff --git a/src/beast/Builds/VisualStudio2013/beast.vcxproj.filters b/src/beast/Builds/VisualStudio2013/beast.vcxproj.filters index b96a696fbe..6fb4764551 100644 --- a/src/beast/Builds/VisualStudio2013/beast.vcxproj.filters +++ b/src/beast/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/src/beast/beast/container/Container.cpp b/src/beast/beast/container/Container.cpp index 21948eb8f6..675854b171 100644 --- a/src/beast/beast/container/Container.cpp +++ b/src/beast/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/src/beast/beast/container/buffer_view.h b/src/beast/beast/container/buffer_view.h new file mode 100644 index 0000000000..f9e2ddeef0 --- /dev/null +++ b/src/beast/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/src/beast/beast/container/tests/buffer_view.test.cpp b/src/beast/beast/container/tests/buffer_view.test.cpp new file mode 100644 index 0000000000..3bc2172834 --- /dev/null +++ b/src/beast/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; + +}