diff --git a/CHANGELOG.md b/CHANGELOG.md index 9319676c..ce8c6220 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +1.0.0-b38 + +* Refactor static_string + +-------------------------------------------------------------------------------- + 1.0.0-b37 * CMake hide command lines in .vcxproj Output windows" diff --git a/include/beast/core/detail/static_string.hpp b/include/beast/core/detail/static_string.hpp new file mode 100644 index 00000000..fe3ee177 --- /dev/null +++ b/include/beast/core/detail/static_string.hpp @@ -0,0 +1,64 @@ +// +// 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_DETAIL_STATIC_STRING_HPP +#define BEAST_DETAIL_STATIC_STRING_HPP + +#include +#include +#include + +namespace beast { +namespace detail { + +// Because k-ballo said so +template +using is_input_iterator = + std::integral_constant::value>; + +template +int +lexicographical_compare( + CharT const* s1, std::size_t n1, + CharT const* s2, std::size_t n2) +{ + if(n1 < n2) + return Traits::compare( + s1, s2, n1) <= 0 ? -1 : 1; + if(n1 > n2) + return Traits::compare( + s1, s2, n2) >= 0 ? 1 : -1; + return Traits::compare(s1, s2, n1); +} + +template +inline +int +lexicographical_compare( + boost::basic_string_ref s1, + CharT const* s2, std::size_t n2) +{ + return lexicographical_compare( + s1.data(), s1.size(), s2, n2); +} + +template +inline +int +lexicographical_compare( + boost::basic_string_ref s1, + boost::basic_string_ref s2) +{ + return lexicographical_compare( + s1.data(), s1.size(), s2.data(), s2.size()); +} + +} // detail +} // beast + +#endif diff --git a/include/beast/core/impl/static_string.ipp b/include/beast/core/impl/static_string.ipp new file mode 100644 index 00000000..14aa968a --- /dev/null +++ b/include/beast/core/impl/static_string.ipp @@ -0,0 +1,525 @@ +// +// 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_STRING_IPP +#define BEAST_IMPL_STATIC_STRING_IPP + +#include + +namespace beast { + +// +// (constructor) +// + +template +static_string:: +static_string() +{ + n_ = 0; + term(); +} + +template +static_string:: +static_string(size_type count, CharT ch) +{ + assign(count, ch); +} + +template +template +static_string:: +static_string(static_string const& other, + size_type pos) +{ + assign(other, pos); +} + +template +template +static_string:: +static_string(static_string const& other, + size_type pos, size_type count) +{ + assign(other, pos, count); +} + +template +static_string:: +static_string(CharT const* s, size_type count) +{ + assign(s, count); +} + +template +static_string:: +static_string(CharT const* s) +{ + assign(s); +} + +template +template +static_string:: +static_string(InputIt first, InputIt last) +{ + assign(first, last); +} + +template +static_string:: +static_string(static_string const& s) +{ + assign(s); +} + +template +template +static_string:: +static_string(static_string const& s) +{ + assign(s); +} + +template +static_string:: +static_string(std::initializer_list init) +{ + assign(init.begin(), init.end()); +} + +template +static_string:: +static_string(boost::basic_string_ref sv) +{ + assign(sv); +} + +template +template +static_string:: +static_string(T const& t, size_type pos, size_type n) +{ + assign(t, pos, n); +} + +// +// (assignment) +// + +template +auto +static_string:: +assign(size_type count, CharT ch) -> + static_string& +{ + if(count > max_size()) + throw detail::make_exception( + "count > max_size()", __FILE__, __LINE__); + n_ = count; + Traits::assign(&s_[0], n_, ch); + term(); + return *this; +} + +template +auto +static_string:: +assign(static_string const& str) -> + static_string& +{ + n_ = str.n_; + Traits::copy(&s_[0], &str.s_[0], n_ + 1); + return *this; +} + +template +template +auto +static_string:: +assign(static_string const& str, + size_type pos, size_type count) -> + static_string& +{ + auto const ss = str.substr(pos, count); + return assign(ss.data(), ss.size()); +} + +template +auto +static_string:: +assign(CharT const* s, size_type count) -> + static_string& +{ + if(count > max_size()) + throw detail::make_exception( + "count > max_size()", __FILE__, __LINE__); + n_ = count; + Traits::copy(&s_[0], s, n_); + term(); + return *this; +} + +template +template +auto +static_string:: +assign(InputIt first, InputIt last) -> + static_string& +{ + std::size_t const n = std::distance(first, last); + if(n > max_size()) + throw detail::make_exception( + "n > max_size()", __FILE__, __LINE__); + n_ = n; + for(auto it = &s_[0]; first != last; ++it, ++first) + Traits::assign(*it, *first); + term(); + return *this; +} + +template +template +auto +static_string:: +assign(T const& t, size_type pos, size_type count) -> + typename std::enable_if< + std::is_convertible>::value, static_string&>::type +{ + auto const sv = boost::basic_string_ref< + CharT, Traits>(t).substr(pos, count); + if(sv.size() > max_size()) + throw detail::make_exception( + "sv.size() > max_size()", __FILE__, __LINE__); + n_ = sv.size(); + Traits::copy(&s_[0], &sv[0], n_); + term(); + return *this; +} + +// +// Element access +// + +template +auto +static_string:: +at(size_type pos) -> + reference +{ + if(pos >= size()) + throw detail::make_exception( + "pos >= size()", __FILE__, __LINE__); + return s_[pos]; +} + +template +auto +static_string:: +at(size_type pos) const -> + const_reference +{ + if(pos >= size()) + throw detail::make_exception( + "pos >= size()", __FILE__, __LINE__); + return s_[pos]; +} + +// +// Capacity +// + +template +void +static_string:: +reserve(std::size_t n) +{ + if(n > max_size()) + throw detail::make_exception( + "n > max_size()", __FILE__, __LINE__); +} + +// +// Operations +// + +template +void +static_string:: +clear() +{ + n_ = 0; + term(); +} + +template +auto +static_string:: +insert(size_type index, size_type count, CharT ch) -> + static_string& +{ + if(index > size()) + throw detail::make_exception( + "index > size()", __FILE__, __LINE__); + insert(begin() + index, count, ch); + return *this; +} + +template +auto +static_string:: +insert(size_type index, CharT const* s, size_type count) -> + static_string& +{ + if(index > size()) + throw detail::make_exception( + "index > size()", __FILE__, __LINE__); + if(size() + count > max_size()) + throw detail::make_exception( + "size() + count > max_size()", __FILE__, __LINE__); + Traits::move( + &s_[index + count], &s_[index], size() - index); + n_ += count; + Traits::copy(&s_[index], s, count); + term(); + return *this; +} + +template +template +auto +static_string:: +insert(size_type index, + static_string const& str, + size_type index_str, size_type count) -> + static_string& +{ + auto const ss = str.substr(index_str, count); + return insert(index, ss.data(), ss.size()); +} + +template +auto +static_string:: +insert(const_iterator pos, size_type count, CharT ch) -> + iterator +{ + if(size() + count > max_size()) + throw detail::make_exception( + "size() + count() > max_size()", __FILE__, __LINE__); + auto const index = pos - &s_[0]; + Traits::move( + &s_[index + count], &s_[index], size() - index); + n_ += count; + Traits::assign(&s_[index], count, ch); + term(); + return &s_[index]; +} + +template +template +auto +static_string:: +insert(const_iterator pos, InputIt first, InputIt last) -> + typename std::enable_if< + detail::is_input_iterator::value, + iterator>::type +{ + std::size_t const count = std::distance(first, last); + if(size() + count > max_size()) + throw detail::make_exception( + "size() + count > max_size()", __FILE__, __LINE__); + std::size_t const index = pos - begin(); + Traits::move( + &s_[index + count], &s_[index], size() - index); + n_ += count; + for(auto it = begin() + index; + first != last; ++it, ++first) + Traits::assign(*it, *first); + term(); + return begin() + index; +} + +template +template +auto +static_string:: +insert(size_type index, const T& t, + size_type index_str, size_type count) -> + typename std::enable_if< + std::is_convertible>::value && + ! std::is_convertible::value, + static_string&>::type +{ + auto const str = boost::basic_string_ref< + CharT, Traits>(t).substr(index_str, count); + return insert(index, str.data(), str.size()); +} + +template +auto +static_string:: +erase(size_type index, size_type count) -> + static_string& +{ + if(index > size()) + throw detail::make_exception( + "index > size()", __FILE__, __LINE__); + auto const n = (std::min)(count, size() - index); + Traits::move( + &s_[index], &s_[index + n], size() - (index + n) + 1); + n_ -= n; + return *this; +} + +template +auto +static_string:: +erase(const_iterator pos) -> + iterator +{ + erase(pos - begin(), 1); + return begin() + (pos - begin()); +} + +template +auto +static_string:: +erase(const_iterator first, const_iterator last) -> + iterator +{ + erase(first - begin(), + std::distance(first, last)); + return begin() + (first - begin()); +} + +template +void +static_string:: +push_back(CharT ch) +{ + if(size() >= max_size()) + throw detail::make_exception( + "size() >= max_size()", __FILE__, __LINE__); + Traits::assign(s_[n_++], ch); + term(); +} + +template +template +auto +static_string:: +append(static_string const& str, + size_type pos, size_type count) -> + static_string& +{ + // Valid range is [0, size) + if(pos >= str.size()) + throw detail::make_exception( + "pos > str.size()", __FILE__, __LINE__); + boost::basic_string_ref const ss{ + &str.s_[pos], (std::min)(count, str.size() - pos)}; + insert(size(), ss.data(), ss.size()); + return *this; +} + +template +boost::basic_string_ref +static_string:: +substr(size_type pos, size_type count) const +{ + if(pos > size()) + throw detail::make_exception( + "pos > size()", __FILE__, __LINE__); + return{&s_[pos], (std::min)(count, size() - pos)}; +} + +template +auto +static_string:: +copy(CharT* dest, size_type count, size_type pos) const -> + size_type +{ + auto const str = substr(pos, count); + Traits::copy(dest, str.data(), str.size()); + return str.size(); +} + +template +void +static_string:: +resize(std::size_t n, CharT c) +{ + if(n > max_size()) + throw detail::make_exception( + "n > max_size()", __FILE__, __LINE__); + if(n > n_) + Traits::assign(&s_[n_], n - n_, c); + n_ = n; + term(); +} + +template +void +static_string:: +swap(static_string& str) +{ + static_string tmp(str); + str.n_ = n_; + Traits::copy(&str.s_[0], &s_[0], n_ + 1); + n_ = tmp.n_; + Traits::copy(&s_[0], &tmp.s_[0], n_ + 1); +} + +template +template +void +static_string:: +swap(static_string& str) +{ + if(size() > str.max_size()) + throw detail::make_exception( + "size() > str.max_size()", __FILE__, __LINE__); + if(str.size() > max_size()) + throw detail::make_exception( + "str.size() > max_size()", __FILE__, __LINE__); + static_string tmp(str); + str.n_ = n_; + Traits::copy(&str.s_[0], &s_[0], n_ + 1); + n_ = tmp.n_; + Traits::copy(&s_[0], &tmp.s_[0], n_ + 1); +} + + +template +auto +static_string:: +assign_char(CharT ch, std::true_type) -> + static_string& +{ + n_ = 1; + Traits::assign(s_[0], ch); + term(); + return *this; +} + +template +auto +static_string:: +assign_char(CharT ch, std::false_type) -> + static_string& +{ + throw detail::make_exception( + "max_size() == 0", __FILE__, __LINE__); +} + +} // beast + +#endif diff --git a/include/beast/core/static_string.hpp b/include/beast/core/static_string.hpp index 6ba5332c..73d051af 100644 --- a/include/beast/core/static_string.hpp +++ b/include/beast/core/static_string.hpp @@ -5,16 +5,20 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_WEBSOCKET_STATIC_STRING_HPP -#define BEAST_WEBSOCKET_STATIC_STRING_HPP +#ifndef BEAST_STATIC_STRING_HPP +#define BEAST_STATIC_STRING_HPP #include -#include -#include +#include +#include +#include #include +#include #include +#include #include #include +#include namespace beast { @@ -37,10 +41,20 @@ class static_string template friend class static_string; + void + term() + { + Traits::assign(s_[n_], 0); + } + std::size_t n_; - std::array s_; + CharT s_[N+1]; public: + // + // Member types + // + using traits_type = Traits; using value_type = typename Traits::char_type; using size_type = std::size_t; @@ -56,35 +70,203 @@ public: using const_reverse_iterator = std::reverse_iterator; - /** Default constructor. + // + // Constants + // - The string is initially empty, and null terminated. - */ + /// Maximum size of the string excluding the null terminator + static std::size_t constexpr max_size_n = N; + + /// A special index + static constexpr size_type npos = size_type(-1); + + // + // (constructor) + // + + /// Default constructor (empty string). static_string(); + /** Construct with count copies of character `ch`. + + The behavior is undefined if `count >= npos` + */ + static_string(size_type count, CharT ch); + + /// Construct with a substring (pos, other.size()) of `other`. + template + static_string(static_string const& other, + size_type pos); + + /// Construct with a substring (pos, count) of `other`. + template + static_string(static_string const& other, + size_type pos, size_type count); + + /// Construct with the first `count` characters of `s`, including nulls. + static_string(CharT const* s, size_type count); + + /// Construct from a null terminated string. + static_string(CharT const* s); + + /// Construct from a range of characters + template + static_string(InputIt first, InputIt last); + /// Copy constructor. - static_string(static_string const& s); + static_string(static_string const& other); /// Copy constructor. template - static_string(static_string const& s); + static_string(static_string const& other); + + /// Construct from an initializer list + static_string(std::initializer_list init); + + /// Construct from a `basic_string_ref` + explicit + static_string(boost::basic_string_ref sv); + + /** Construct from any object convertible to `basic_string_ref`. + + The range (pos, n) is extracted from the value + obtained by converting `t` to `basic_string_ref`, + and used to construct the string. + */ +#if BEAST_DOXYGEN + template +#else + template> ::value>::type> +#endif + static_string(T const& t, size_type pos, size_type n); + + // + // (assignment) + // /// Copy assignment. static_string& - operator=(static_string const& s); + operator=(static_string const& str) + { + return assign(str); + } /// Copy assignment. template static_string& - operator=(static_string const& s); + operator=(static_string const& str) + { + return assign(str); + } - /// Construct from string literal. - template - static_string(const CharT (&s)[M]); + /// Assign from null-terminated string. + static_string& + operator=(CharT const* s) + { + return assign(s); + } - /// Assign from string literal. + /// Assign from single character. + static_string& + operator=(CharT ch) + { + return assign_char(ch, + std::integral_constant0)>{}); + } + + /// Assign from initializer list. + static_string& + operator=(std::initializer_list init) + { + return assign(init); + } + + /// Assign from `basic_string_ref`. + static_string& + operator=(boost::basic_string_ref sv) + { + return assign(sv); + } + + /// Assign `count` copies of `ch`. + static_string& + assign(size_type count, CharT ch); + + /// Assign from another `static_string` + static_string& + assign(static_string const& str); + + // VFALCO NOTE this could come in two flavors, + // N>M and NM + + /// Assign from another `static_string` template - static_string& operator=(const CharT (&s)[M]); + static_string& + assign(static_string const& str) + { + return assign(str.data(), str.size()); + } + + /// Assign `count` characterss starting at `npos` from `other`. + template + static_string& + assign(static_string const& str, + size_type pos, size_type count = npos); + + /// Assign the first `count` characters of `s`, including nulls. + static_string& + assign(CharT const* s, size_type count); + + /// Assign a null terminated string. + static_string& + assign(CharT const* s) + { + return assign(s, Traits::length(s)); + } + + /// Assign from an iterator range of characters. + template + static_string& + assign(InputIt first, InputIt last); + + /// Assign from initializer list. + static_string& + assign(std::initializer_list init) + { + return assign(init.begin(), init.end()); + } + + /// Assign from `basic_string_ref`. + static_string& + assign(boost::basic_string_ref str) + { + return assign(str.data(), str.size()); + } + + /** Assign from any object convertible to `basic_string_ref`. + + The range (pos, n) is extracted from the value + obtained by converting `t` to `basic_string_ref`, + and used to assign the string. + */ + template +#if BEAST_DOXYGEN + static_string& +#else + typename std::enable_if< + std::is_convertible>::value, static_string&>::type +#endif + assign(T const& t, + size_type pos, size_type count = npos); + + // + // Element access + // /// Access specified character with bounds checking. reference @@ -154,9 +336,22 @@ public: CharT const* c_str() const { - return &s_[0]; + return data(); } + // VFALCO What about boost::string_view? + // + /// Convert a static string to a `static_string_ref` + operator boost::basic_string_ref() const + { + return boost::basic_string_ref< + CharT, Traits>{data(), size()}; + } + + // + // Iterators + // + /// Returns an iterator to the beginning. iterator begin() @@ -241,6 +436,10 @@ public: return const_reverse_iterator{cbegin()}; } + // + // Capacity + // + /// Returns `true` if the string is empty. bool empty() const @@ -255,6 +454,13 @@ public: return n_; } + /// Returns the number of characters, excluding the null terminator. + size_type + length() const + { + return size(); + } + /// Returns the maximum number of characters that can be stored, excluding the null terminator. size_type constexpr max_size() const @@ -262,26 +468,334 @@ public: return N; } + /** Reserves storage. + + This actually just throws an exception if `n > N`, + otherwise does nothing since the storage is fixed. + */ + void + reserve(std::size_t n); + /// Returns the number of characters that can be held in currently allocated storage. - size_type + size_type constexpr capacity() const { - return N; + return max_size(); } + + /** Reduces memory usage by freeing unused memory. + + This actually does nothing, since the storage is fixed. + */ + void + shrink_to_fit() + { + } + + // + // Operations + // /// Clears the contents. void - clear() + clear(); + + static_string& + insert(size_type index, size_type count, CharT ch); + + static_string& + insert(size_type index, CharT const* s) { - resize(0); + return insert(index, s, Traits::length(s)); } + static_string& + insert(size_type index, CharT const* s, size_type count); + + template + static_string& + insert(size_type index, + static_string const& str) + { + return insert(index, str.data(), str.size()); + } + + template + static_string& + insert(size_type index, + static_string const& str, + size_type index_str, size_type count = npos); + + iterator + insert(const_iterator pos, CharT ch) + { + return insert(pos, 1, ch); + } + + iterator + insert(const_iterator pos, size_type count, CharT ch); + + template +#if BEAST_DOXYGEN + iterator +#else + typename std::enable_if< + detail::is_input_iterator::value, + iterator>::type +#endif + insert(const_iterator pos, InputIt first, InputIt last); + + iterator + insert(const_iterator pos, std::initializer_list init) + { + return insert(pos, init.begin(), init.end()); + } + + static_string& + insert(size_type index, + boost::basic_string_ref str) + { + return insert(index, str.data(), str.size()); + } + + template +#if BEAST_DOXYGEN + static_string& +#else + typename std::enable_if< + std::is_convertible>::value && + ! std::is_convertible::value, + static_string&>::type +#endif + insert(size_type index, T const& t, + size_type index_str, size_type count = npos); + + static_string& + erase(size_type index = 0, size_type count = npos); + + iterator + erase(const_iterator pos); + + iterator + erase(const_iterator first, const_iterator last); + + void + push_back(CharT ch); + + void + pop_back() + { + Traits::assign(s_[--n_], 0); + } + + static_string& + append(size_type count, CharT ch) + { + insert(end(), count, ch); + return *this; + } + + template + static_string& + append(static_string const& str) + { + insert(size(), str); + return *this; + } + + template + static_string& + append(static_string const& str, + size_type pos, size_type count = npos); + + static_string& + append(CharT const* s, size_type count) + { + insert(size(), s, count); + return *this; + } + + static_string& + append(CharT const* s) + { + insert(size(), s); + return *this; + } + + template +#if BEAST_DOXYGEN + static_string& +#else + typename std::enable_if< + detail::is_input_iterator::value, + static_string&>::type +#endif + append(InputIt first, InputIt last) + { + insert(end(), first, last); + return *this; + } + + static_string& + append(std::initializer_list init) + { + insert(end(), init); + return *this; + } + + static_string& + append(boost::basic_string_ref sv) + { + insert(size(), sv); + return *this; + } + + template + typename std::enable_if< + std::is_convertible>::value && + ! std::is_convertible::value, + static_string&>::type + append(T const& t, size_type pos, size_type count = npos) + { + insert(size(), t, pos, count); + return *this; + } + + template + static_string& + operator+=(static_string const& str) + { + return append(str.data(), str.size()); + } + + static_string& + operator+=(CharT ch) + { + push_back(ch); + return *this; + } + + static_string& + operator+=(CharT const* s) + { + return append(s); + } + + static_string& + operator+=(std::initializer_list init) + { + return append(init); + } + + static_string& + operator+=(boost::basic_string_ref const& str) + { + return append(str); + } + + template + int + compare(static_string const& str) const + { + return detail::lexicographical_compare( + &s_[0], n_, &str.s_[0], str.n_); + } + + template + int + compare(size_type pos1, size_type count1, + static_string const& str) const + { + return detail::lexicographical_compare( + substr(pos1, count1), str.data(), str.size()); + } + + template + int + compare(size_type pos1, size_type count1, + static_string const& str, + size_type pos2, size_type count2 = npos) const + { + return detail::lexicographical_compare( + substr(pos1, count1), str.substr(pos2, count2)); + } + + int + compare(CharT const* s) const + { + return detail::lexicographical_compare( + &s_[0], n_, s, Traits::length(s)); + } + + int + compare(size_type pos1, size_type count1, + CharT const* s) const + { + return detail::lexicographical_compare( + substr(pos1, count1), s, Traits::length(s)); + } + + int + compare(size_type pos1, size_type count1, + CharT const*s, size_type count2) const + { + return detail::lexicographical_compare( + substr(pos1, count1), s, count2); + } + + int + compare(boost::basic_string_ref str) const + { + return detail::lexicographical_compare( + &s_[0], n_, str.data(), str.size()); + } + + int + compare(size_type pos1, size_type count1, + boost::basic_string_ref str) const + { + return detail::lexicographical_compare( + substr(pos1, count1), str); + } + + template +#if BEAST_DOXYGEN + int +#else + typename std::enable_if< + std::is_convertible>::value && + ! std::is_convertible::value, + int>::type +#endif + compare(size_type pos1, size_type count1, + T const& t, size_type pos2, + size_type count2 = npos) const + { + return compare(pos1, count1, + boost::basic_string_ref< + CharT, Traits>(t).substr(pos2, count2)); + } + + boost::basic_string_ref + substr(size_type pos = 0, size_type count = npos) const; + + /// Copy a substring (pos, pos+count) to character string pointed to by `dest`. + size_type + copy(CharT* dest, size_type count, size_type pos = 0) const; + /** Changes the number of characters stored. - @note No value-initialization is performed. + If the resulting string is larger, the new + characters are initialized to zero. */ void - resize(std::size_t n); + resize(std::size_t n) + { + resize(n, 0); + } /** Changes the number of characters stored. @@ -291,231 +805,79 @@ public: void resize(std::size_t n, CharT c); - /// Compare two character sequences. - template - int - compare(static_string const& rhs) const; + /// Exchange the contents of this string with another. + void + swap(static_string& str); - /// Return the characters as a `basic_string`. - std::basic_string - to_string() const - { - return std::basic_string< - CharT, Traits>{&s_[0], n_}; - } + /// Exchange the contents of this string with another. + template + void + swap(static_string& str); + + // + // Search + // private: - void - assign(CharT const* s); + static_string& + assign_char(CharT ch, std::true_type); + + static_string& + assign_char(CharT ch, std::false_type); }; -template -static_string:: -static_string() - : n_(0) -{ - s_[0] = 0; -} - -template -static_string:: -static_string(static_string const& s) - : n_(s.n_) -{ - Traits::copy(&s_[0], &s.s_[0], n_ + 1); -} - -template -template -static_string:: -static_string(static_string const& s) -{ - if(s.size() > N) - throw detail::make_exception( - "static_string overflow", __FILE__, __LINE__); - n_ = s.size(); - Traits::copy(&s_[0], &s.s_[0], n_ + 1); -} - -template -auto -static_string:: -operator=(static_string const& s) -> - static_string& -{ - n_ = s.n_; - Traits::copy(&s_[0], &s.s_[0], n_ + 1); - return *this; -} - -template -template -auto -static_string:: -operator=(static_string const& s) -> - static_string& -{ - if(s.size() > N) - throw detail::make_exception( - "static_string overflow", __FILE__, __LINE__); - n_ = s.size(); - Traits::copy(&s_[0], &s.s_[0], n_ + 1); - return *this; -} - -template -template -static_string:: -static_string(const CharT (&s)[M]) - : n_(M-1) -{ - static_assert(M-1 <= N, - "static_string overflow"); - Traits::copy(&s_[0], &s[0], M); -} - -template -template -auto -static_string:: -operator=(const CharT (&s)[M]) -> - static_string& -{ - static_assert(M-1 <= N, - "static_string overflow"); - n_ = M-1; - Traits::copy(&s_[0], &s[0], M); - return *this; -} - -template -auto -static_string:: -at(size_type pos) -> - reference -{ - if(pos >= n_) - throw detail::make_exception( - "invalid pos", __FILE__, __LINE__); - return s_[pos]; -} - -template -auto -static_string:: -at(size_type pos) const -> - const_reference -{ - if(pos >= n_) - throw detail::make_exception( - "static_string::at", __FILE__, __LINE__); - return s_[pos]; -} - -template -void -static_string:: -resize(std::size_t n) -{ - if(n > N) - throw detail::make_exception( - "static_string overflow", __FILE__, __LINE__); - n_ = n; - s_[n_] = 0; -} - -template -void -static_string:: -resize(std::size_t n, CharT c) -{ - if(n > N) - throw detail::make_exception( - "static_string overflow", __FILE__, __LINE__); - if(n > n_) - Traits::assign(&s_[n_], n - n_, c); - n_ = n; - s_[n_] = 0; -} - -template -template -int -static_string:: -compare(static_string const& rhs) const -{ - if(size() < rhs.size()) - { - auto const v = Traits::compare( - data(), rhs.data(), size()); - if(v == 0) - return -1; - return v; - } - else if(size() > rhs.size()) - { - auto const v = Traits::compare( - data(), rhs.data(), rhs.size()); - if(v == 0) - return 1; - return v; - } - return Traits::compare(data(), rhs.data(), size()); -} - -template -void -static_string:: -assign(CharT const* s) -{ - auto const n = Traits::length(s); - if(n > N) - throw detail::make_exception( - "too large", __FILE__, __LINE__); - n_ = n; - Traits::copy(&s_[0], s, n_ + 1); -} - -namespace detail { +// +// Non-member functions +// template -int -compare( - static_string const& lhs, - const CharT (&s)[M]) +void +operator+( + static_stringconst& lhs, + static_stringconst& rhs) { - if(lhs.size() < M-1) - { - auto const v = Traits::compare( - lhs.data(), &s[0], lhs.size()); - if(v == 0) - return -1; - return v; - } - else if(lhs.size() > M-1) - { - auto const v = Traits::compare( - lhs.data(), &s[0], M-1); - if(v == 0) - return 1; - return v; - } - return Traits::compare(lhs.data(), &s[0], lhs.size()); + static_assert(sizeof(CharT) == -1, + "operator+ is not available"); } -template -inline -int -compare( - const CharT (&s)[M], +template +void +operator+(CharT const* lhs, + static_stringconst& rhs ) +{ + static_assert(sizeof(CharT) == -1, + "operator+ is not available"); +} + +template +void +operator+(CharT lhs, static_string const& rhs) { - return -compare(rhs, s); + static_assert(sizeof(CharT) == -1, + "operator+ is not available"); } -} // detail +template +void +operator+(static_string const& lhs, + CharT const* rhs ) +{ + static_assert(sizeof(CharT) == -1, + "operator+ is not available"); +} -template +template +void +operator+(static_string const& lhs, + CharT rhs ) +{ + static_assert(sizeof(CharT) == -1, + "operator+ is not available"); +} + +template bool operator==( static_string const& lhs, @@ -524,7 +886,8 @@ operator==( return lhs.compare(rhs) == 0; } -template +template bool operator!=( static_string const& lhs, @@ -533,7 +896,8 @@ operator!=( return lhs.compare(rhs) != 0; } -template +template bool operator<( static_string const& lhs, @@ -542,7 +906,8 @@ operator<( return lhs.compare(rhs) < 0; } -template +template bool operator<=( static_string const& lhs, @@ -551,7 +916,8 @@ operator<=( return lhs.compare(rhs) <= 0; } -template +template bool operator>( static_string const& lhs, @@ -560,7 +926,8 @@ operator>( return lhs.compare(rhs) > 0; } -template +template bool operator>=( static_string const& lhs, @@ -569,116 +936,176 @@ operator>=( return lhs.compare(rhs) >= 0; } -//--- - -template +template bool operator==( - const CharT (&s)[N], - static_string const& rhs) + CharT const* lhs, + static_string const& rhs) { - return detail::compare(s, rhs) == 0; + return detail::lexicographical_compare( + lhs, Traits::length(lhs), + rhs.data(), rhs.size()) == 0; } -template +template bool operator==( static_string const& lhs, - const CharT (&s)[M]) + CharT const* rhs) { - return detail::compare(lhs, s) == 0; + return detail::lexicographical_compare( + lhs.data(), lhs.size(), + rhs, Traits::length(rhs)) == 0; } -template +template bool operator!=( - const CharT (&s)[N], - static_string const& rhs) + CharT const* lhs, + static_string const& rhs) { - return detail::compare(s, rhs) != 0; + return detail::lexicographical_compare( + lhs, Traits::length(lhs), + rhs.data(), rhs.size()) != 0; } -template +template bool operator!=( static_string const& lhs, - const CharT (&s)[M]) + CharT const* rhs) { - return detail::compare(lhs, s) != 0; + return detail::lexicographical_compare( + lhs.data(), lhs.size(), + rhs, Traits::length(rhs)) != 0; } -template +template bool operator<( - const CharT (&s)[N], - static_string const& rhs) + CharT const* lhs, + static_string const& rhs) { - return detail::compare(s, rhs) < 0; + return detail::lexicographical_compare( + lhs, Traits::length(lhs), + rhs.data(), rhs.size()) < 0; } -template +template bool operator<( static_string const& lhs, - const CharT (&s)[M]) + CharT const* rhs) { - return detail::compare(lhs, s) < 0; + return detail::lexicographical_compare( + lhs.data(), lhs.size(), + rhs, Traits::length(rhs)) < 0; } -template +template bool operator<=( - const CharT (&s)[N], - static_string const& rhs) + CharT const* lhs, + static_string const& rhs) { - return detail::compare(s, rhs) <= 0; + return detail::lexicographical_compare( + lhs, Traits::length(lhs), + rhs.data(), rhs.size()) <= 0; } -template +template bool operator<=( static_string const& lhs, - const CharT (&s)[M]) + CharT const* rhs) { - return detail::compare(lhs, s) <= 0; + return detail::lexicographical_compare( + lhs.data(), lhs.size(), + rhs, Traits::length(rhs)) <= 0; } -template +template bool operator>( - const CharT (&s)[N], - static_string const& rhs) + CharT const* lhs, + static_string const& rhs) { - return detail::compare(s, rhs) > 0; + return detail::lexicographical_compare( + lhs, Traits::length(lhs), + rhs.data(), rhs.size()) > 0; } -template +template bool operator>( static_string const& lhs, - const CharT (&s)[M]) + CharT const* rhs) { - return detail::compare(lhs, s) > 0; + return detail::lexicographical_compare( + lhs.data(), lhs.size(), + rhs, Traits::length(rhs)) > 0; } -template +template bool operator>=( - const CharT (&s)[N], - static_string const& rhs) + CharT const* lhs, + static_string const& rhs) { - return detail::compare(s, rhs) >= 0; + return detail::lexicographical_compare( + lhs, Traits::length(lhs), + rhs.data(), rhs.size()) >= 0; } -template +template bool operator>=( static_string const& lhs, - const CharT (&s)[M]) + CharT const* rhs) { - return detail::compare(lhs, s) >= 0; + return detail::lexicographical_compare( + lhs.data(), lhs.size(), + rhs, Traits::length(rhs)) >= 0; +} + +// +// swap +// + +template +void +swap( + static_string& lhs, + static_string& rhs) +{ + lhs.swap(rhs); +} + +template +void +swap( + static_string& lhs, + static_string& rhs) +{ + lhs.swap(rhs); +} + +// +// Input/Output +// + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, + static_string const& str) +{ + return os << static_cast< + boost::basic_string_ref>(str); } } // beast +#include + #endif diff --git a/test/core/static_string.cpp b/test/core/static_string.cpp index cfac29bf..bb907fae 100644 --- a/test/core/static_string.cpp +++ b/test/core/static_string.cpp @@ -15,150 +15,1133 @@ namespace beast { class static_string_test : public beast::unit_test::suite { public: - void testMembers() + void + testConstruct() { - using str1 = static_string<1>; - using str2 = static_string<2>; { - str1 s1; - BEAST_EXPECT(s1 == ""); - BEAST_EXPECT(s1.empty()); - BEAST_EXPECT(s1.size() == 0); - BEAST_EXPECT(s1.max_size() == 1); - BEAST_EXPECT(s1.capacity() == 1); - BEAST_EXPECT(s1.begin() == s1.end()); - BEAST_EXPECT(s1.cbegin() == s1.cend()); - BEAST_EXPECT(s1.rbegin() == s1.rend()); - BEAST_EXPECT(s1.crbegin() == s1.crend()); - try - { - BEAST_EXPECT(s1.at(0) == 0); - fail(); - } - catch(std::exception const&) - { - pass(); - } - BEAST_EXPECT(s1.data()[0] == 0); - BEAST_EXPECT(*s1.c_str() == 0); - BEAST_EXPECT(std::distance(s1.begin(), s1.end()) == 0); - BEAST_EXPECT(std::distance(s1.cbegin(), s1.cend()) == 0); - BEAST_EXPECT(std::distance(s1.rbegin(), s1.rend()) == 0); - BEAST_EXPECT(std::distance(s1.crbegin(), s1.crend()) == 0); - BEAST_EXPECT(s1.compare(s1) == 0); - BEAST_EXPECT(s1.to_string() == std::string{}); + static_string<1> s; + BEAST_EXPECT(s.empty()); + BEAST_EXPECT(s.size() == 0); + BEAST_EXPECT(s == ""); + BEAST_EXPECT(*s.end() == 0); } { - str1 const s1; - BEAST_EXPECT(s1 == ""); - BEAST_EXPECT(s1.empty()); - BEAST_EXPECT(s1.size() == 0); - BEAST_EXPECT(s1.max_size() == 1); - BEAST_EXPECT(s1.capacity() == 1); - BEAST_EXPECT(s1.begin() == s1.end()); - BEAST_EXPECT(s1.cbegin() == s1.cend()); - BEAST_EXPECT(s1.rbegin() == s1.rend()); - BEAST_EXPECT(s1.crbegin() == s1.crend()); + static_string<4> s1(3, 'x'); + BEAST_EXPECT(! s1.empty()); + BEAST_EXPECT(s1.size() == 3); + BEAST_EXPECT(s1 == "xxx"); + BEAST_EXPECT(*s1.end() == 0); try { - BEAST_EXPECT(s1.at(0) == 0); - fail(); - } - catch(std::exception const&) - { - pass(); - } - BEAST_EXPECT(s1.data()[0] == 0); - BEAST_EXPECT(*s1.c_str() == 0); - BEAST_EXPECT(std::distance(s1.begin(), s1.end()) == 0); - BEAST_EXPECT(std::distance(s1.cbegin(), s1.cend()) == 0); - BEAST_EXPECT(std::distance(s1.rbegin(), s1.rend()) == 0); - BEAST_EXPECT(std::distance(s1.crbegin(), s1.crend()) == 0); - BEAST_EXPECT(s1.compare(s1) == 0); - BEAST_EXPECT(s1.to_string() == std::string{}); - } - { - str1 s1; - str1 s2("x"); - BEAST_EXPECT(s2 == "x"); - BEAST_EXPECT(s2[0] == 'x'); - BEAST_EXPECT(s2.at(0) == 'x'); - BEAST_EXPECT(s2.front() == 'x'); - BEAST_EXPECT(s2.back() == 'x'); - str1 const s3(s2); - BEAST_EXPECT(s3 == "x"); - BEAST_EXPECT(s3[0] == 'x'); - BEAST_EXPECT(s3.at(0) == 'x'); - BEAST_EXPECT(s3.front() == 'x'); - BEAST_EXPECT(s3.back() == 'x'); - s2 = "y"; - BEAST_EXPECT(s2 == "y"); - BEAST_EXPECT(s3 == "x"); - s1 = s2; - BEAST_EXPECT(s1 == "y"); - s1.clear(); - BEAST_EXPECT(s1.empty()); - BEAST_EXPECT(s1.size() == 0); - } - { - str2 s1("x"); - str1 s2(s1); - BEAST_EXPECT(s2 == "x"); - str1 s3; - s3 = s2; - BEAST_EXPECT(s3 == "x"); - s1 = "xy"; - BEAST_EXPECT(s1.size() == 2); - BEAST_EXPECT(s1[0] == 'x'); - BEAST_EXPECT(s1[1] == 'y'); - BEAST_EXPECT(s1.at(0) == 'x'); - BEAST_EXPECT(s1.at(1) == 'y'); - BEAST_EXPECT(s1.front() == 'x'); - BEAST_EXPECT(s1.back() == 'y'); - auto const s4 = s1; - BEAST_EXPECT(s4[0] == 'x'); - BEAST_EXPECT(s4[1] == 'y'); - BEAST_EXPECT(s4.at(0) == 'x'); - BEAST_EXPECT(s4.at(1) == 'y'); - BEAST_EXPECT(s4.front() == 'x'); - BEAST_EXPECT(s4.back() == 'y'); - try - { - s3 = s1; - fail(); - } - catch(std::exception const&) - { - pass(); - } - try - { - str1 s5(s1); - fail(); - } - catch(std::exception const&) - { - pass(); - } - } - { - str1 s1("x"); - str2 s2; - s2 = s1; - try - { - s1.resize(2); - fail(); + static_string<2> s2(3, 'x'); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + static_string<5> s1("12345"); + BEAST_EXPECT(*s1.end() == 0); + static_string<3> s2(s1, 2); + BEAST_EXPECT(s2 == "345"); + BEAST_EXPECT(*s2.end() == 0); + static_string<0> s3(s1, 5); + BEAST_EXPECT(s3.empty()); + BEAST_EXPECT(s3.front() == 0); + BEAST_EXPECT(*s3.end() == 0); + } + { + static_string<5> s1("12345"); + static_string<2> s2(s1, 1, 2); + BEAST_EXPECT(s2 == "23"); + BEAST_EXPECT(*s2.end() == 0); + static_string<0> s3(s1, 5, 1); + BEAST_EXPECT(s3.empty()); + BEAST_EXPECT(s3.front() == 0); + BEAST_EXPECT(*s3.end() == 0); + try + { + static_string<5> s4(s1, 6); + fail("", __FILE__, __LINE__); + } + catch(std::out_of_range const&) + { + pass(); + } + } + { + static_string<5> s1("UVXYZ", 3); + BEAST_EXPECT(s1 == "UVX"); + BEAST_EXPECT(*s1.end() == 0); + static_string<5> s2("X\0""Y\0""Z", 3); + BEAST_EXPECT(std::memcmp( + s2.data(), "X\0""Y", 3) == 0); + BEAST_EXPECT(*s2.end() == 0); + } + { + static_string<5> s1("12345"); + static_string<3> s2( + s1.begin() + 1, s1.begin() + 3); + BEAST_EXPECT(s2 == "23"); + BEAST_EXPECT(*s2.end() == 0); + } + { + static_string<5> s1("12345"); + static_string<5> s2(s1); + BEAST_EXPECT(s2 == "12345"); + BEAST_EXPECT(*s2.end() == 0); + static_string<6> s3(s1); + BEAST_EXPECT(s3 == "12345"); + BEAST_EXPECT(*s3.end() == 0); + try + { + static_string<4> s4(s1); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + static_string<3> s1({'1', '2', '3'}); + BEAST_EXPECT(s1 == "123"); + BEAST_EXPECT(*s1.end() == 0); + BEAST_EXPECT( + static_string<0>({}) == static_string<0>()); + try + { + static_string<2> s2({'1', '2', '3'}); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + static_string<3> s1( + boost::string_ref("123")); + BEAST_EXPECT(s1 == "123"); + BEAST_EXPECT(*s1.end() == 0); + try + { + static_string<2> s2( + boost::string_ref("123")); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + static_string<5> s1( + std::string("12345"), 2, 2); + BEAST_EXPECT(s1 == "34"); + BEAST_EXPECT(*s1.end() == 0); + try + { + static_string<2> s2( + std::string("12345"), 1, 3); + fail("", __FILE__, __LINE__); } catch(std::length_error const&) { pass(); } } - pass(); } - void testCompare() + void + testAssign() + { + { + static_string<3> s1("123"); + static_string<3> s2; + s2 = s1; + BEAST_EXPECT(s2 == "123"); + BEAST_EXPECT(*s2.end() == 0); + } + { + static_string<3> s1("123"); + static_string<5> s2; + s2 = s1; + BEAST_EXPECT(s2 == "123"); + BEAST_EXPECT(*s2.end() == 0); + try + { + static_string<1> s3; + s3 = s1; + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + static_string<3> s1; + s1 = "123"; + BEAST_EXPECT(s1 == "123"); + BEAST_EXPECT(*s1.end() == 0); + try + { + static_string<1> s2; + s2 = "123"; + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + static_string<1> s1; + s1 = 'x'; + BEAST_EXPECT(s1 == "x"); + BEAST_EXPECT(*s1.end() == 0); + try + { + static_string<0> s2; + s2 = 'x'; + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + static_string<3> s1; + s1 = {'1', '2', '3'}; + BEAST_EXPECT(s1 == "123"); + BEAST_EXPECT(*s1.end() == 0); + try + { + static_string<1> s2; + s2 = {'1', '2', '3'}; + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + static_string<3> s1; + s1 = boost::string_ref("123"); + BEAST_EXPECT(s1 == "123"); + BEAST_EXPECT(*s1.end() == 0); + try + { + static_string<1> s2; + s2 = boost::string_ref("123"); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + + { + static_string<4> s1; + s1.assign(3, 'x'); + BEAST_EXPECT(s1 == "xxx"); + BEAST_EXPECT(*s1.end() == 0); + try + { + static_string<2> s2; + s2.assign(3, 'x'); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + static_string<5> s1("12345"); + BEAST_EXPECT(*s1.end() == 0); + static_string<5> s2; + s2.assign(s1); + BEAST_EXPECT(s2 == "12345"); + BEAST_EXPECT(*s2.end() == 0); + } + { + static_string<5> s1("12345"); + BEAST_EXPECT(*s1.end() == 0); + static_string<7> s2; + s2.assign(s1); + BEAST_EXPECT(s2 == "12345"); + BEAST_EXPECT(*s2.end() == 0); + try + { + static_string<3> s3; + s3.assign(s1); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + static_string<5> s1("12345"); + static_string<5> s2; + s2.assign(s1, 1); + BEAST_EXPECT(s2 == "2345"); + BEAST_EXPECT(*s2.end() == 0); + s2.assign(s1, 1, 2); + BEAST_EXPECT(s2 == "23"); + BEAST_EXPECT(*s2.end() == 0); + s2.assign(s1, 1, 100); + BEAST_EXPECT(s2 == "2345"); + BEAST_EXPECT(*s2.end() == 0); + try + { + s2.assign(s1, 6); + fail("", __FILE__, __LINE__); + } + catch(std::out_of_range const&) + { + pass(); + } + try + { + static_string<3> s3; + s3.assign(s1, 1); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + static_string<5> s1; + s1.assign("12"); + BEAST_EXPECT(s1 == "12"); + BEAST_EXPECT(*s1.end() == 0); + s1.assign("12345"); + BEAST_EXPECT(s1 == "12345"); + BEAST_EXPECT(*s1.end() == 0); + } + { + static_string<5> s1; + s1.assign("12345", 3); + BEAST_EXPECT(s1 == "123"); + BEAST_EXPECT(*s1.end() == 0); + } + { + static_string<5> s1("12345"); + static_string<3> s2; + s2.assign(s1.begin(), s1.begin() + 2); + BEAST_EXPECT(s2 == "12"); + BEAST_EXPECT(*s2.end() == 0); + try + { + s2.assign(s1.begin(), s1.end()); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + static_string<5> s1; + s1.assign({'1', '2', '3'}); + BEAST_EXPECT(s1 == "123"); + BEAST_EXPECT(*s1.end() == 0); + try + { + static_string<1> s2; + s2.assign({'1', '2', '3'}); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + static_string<5> s1; + s1.assign(boost::string_ref("123")); + BEAST_EXPECT(s1 == "123"); + BEAST_EXPECT(*s1.end() == 0); + s1.assign(boost::string_ref("12345")); + BEAST_EXPECT(s1 == "12345"); + BEAST_EXPECT(*s1.end() == 0); + try + { + s1.assign(boost::string_ref("1234567")); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + static_string<5> s1; + s1.assign(std::string("12345"), 2, 2); + BEAST_EXPECT(s1 == "34"); + BEAST_EXPECT(*s1.end() == 0); + s1.assign(std::string("12345"), 3); + BEAST_EXPECT(s1 == "45"); + BEAST_EXPECT(*s1.end() == 0); + try + { + static_string<2> s2; + s2.assign( + std::string("12345"), 1, 3); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + } + + void + testAccess() + { + { + static_string<5> s("12345"); + BEAST_EXPECT(s.at(1) == '2'); + BEAST_EXPECT(s.at(4) == '5'); + try + { + BEAST_EXPECT(s.at(5) == 0); + fail("", __FILE__, __LINE__); + } + catch(std::out_of_range const&) + { + pass(); + } + } + { + static_string<5> const s("12345"); + BEAST_EXPECT(s.at(1) == '2'); + BEAST_EXPECT(s.at(4) == '5'); + try + { + BEAST_EXPECT(s.at(5) == 0); + fail("", __FILE__, __LINE__); + } + catch(std::out_of_range const&) + { + pass(); + } + } + { + static_string<5> s("12345"); + BEAST_EXPECT(s[1] == '2'); + BEAST_EXPECT(s[4] == '5'); + s[1] = '_'; + BEAST_EXPECT(s == "1_345"); + } + { + static_string<5> const s("12345"); + BEAST_EXPECT(s[1] == '2'); + BEAST_EXPECT(s[4] == '5'); + BEAST_EXPECT(s[5] == 0); + } + { + static_string<3> s("123"); + BEAST_EXPECT(s.front() == '1'); + BEAST_EXPECT(s.back() == '3'); + s.front() = '_'; + BEAST_EXPECT(s == "_23"); + s.back() = '_'; + BEAST_EXPECT(s == "_2_"); + } + { + static_string<3> const s("123"); + BEAST_EXPECT(s.front() == '1'); + BEAST_EXPECT(s.back() == '3'); + } + { + static_string<3> s("123"); + BEAST_EXPECT(std::memcmp( + s.data(), "123", 3) == 0); + } + { + static_string<3> const s("123"); + BEAST_EXPECT(std::memcmp( + s.data(), "123", 3) == 0); + } + { + static_string<3> s("123"); + BEAST_EXPECT(std::memcmp( + s.c_str(), "123\0", 4) == 0); + } + { + static_string<3> s("123"); + boost::string_ref sv = s; + BEAST_EXPECT(static_string<5>(sv) == "123"); + } + } + + void + testIterators() + { + { + static_string<3> s; + BEAST_EXPECT(std::distance( + s.begin(), s.end()) == 0); + BEAST_EXPECT(std::distance( + s.rbegin(), s.rend()) == 0); + s = "123"; + BEAST_EXPECT(std::distance( + s.begin(), s.end()) == 3); + BEAST_EXPECT(std::distance( + s.rbegin(), s.rend()) == 3); + } + { + static_string<3> const s("123"); + BEAST_EXPECT(std::distance( + s.begin(), s.end()) == 3); + BEAST_EXPECT(std::distance( + s.cbegin(), s.cend()) == 3); + BEAST_EXPECT(std::distance( + s.rbegin(), s.rend()) == 3); + BEAST_EXPECT(std::distance( + s.crbegin(), s.crend()) == 3); + } + } + + void + testCapacity() + { + static_string<3> s; + BEAST_EXPECT(s.empty()); + BEAST_EXPECT(s.size() == 0); + BEAST_EXPECT(s.length() == 0); + BEAST_EXPECT(s.max_size() == 3); + BEAST_EXPECT(s.capacity() == 3); + s = "123"; + BEAST_EXPECT(! s.empty()); + BEAST_EXPECT(s.size() == 3); + BEAST_EXPECT(s.length() == 3); + s.reserve(0); + s.reserve(3); + try + { + s.reserve(4); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + s.shrink_to_fit(); + BEAST_EXPECT(! s.empty()); + BEAST_EXPECT(s.size() == 3); + BEAST_EXPECT(s.length() == 3); + BEAST_EXPECT(*s.end() == 0); + } + + void + testOperations() + { + // + // clear + // + + { + static_string<3> s("123"); + s.clear(); + BEAST_EXPECT(s.empty()); + BEAST_EXPECT(*s.end() == 0); + } + + // + // insert + // + + { + static_string<7> s1("12345"); + s1.insert(2, 2, '_'); + BEAST_EXPECT(s1 == "12__345"); + BEAST_EXPECT(*s1.end() == 0); + try + { + static_string<6> s2("12345"); + s2.insert(2, 2, '_'); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + try + { + static_string<6> s2("12345"); + s2.insert(6, 2, '_'); + fail("", __FILE__, __LINE__); + } + catch(std::out_of_range const&) + { + pass(); + } + } + { + static_string<7> s1("12345"); + s1.insert(2, "__"); + BEAST_EXPECT(s1 == "12__345"); + BEAST_EXPECT(*s1.end() == 0); + try + { + static_string<6> s2("12345"); + s2.insert(2, "__"); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + try + { + static_string<6> s2("12345"); + s2.insert(6, "__"); + fail("", __FILE__, __LINE__); + } + catch(std::out_of_range const&) + { + pass(); + } + } + { + static_string<7> s1("12345"); + s1.insert(2, "TUV", 2); + BEAST_EXPECT(s1 == "12TU345"); + BEAST_EXPECT(*s1.end() == 0); + try + { + static_string<6> s2("12345"); + s2.insert(2, "TUV", 2); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + try + { + static_string<6> s2("12345"); + s2.insert(6, "TUV", 2); + fail("", __FILE__, __LINE__); + } + catch(std::out_of_range const&) + { + pass(); + } + } + { + static_string<7> s1("12345"); + s1.insert(2, static_string<3>("TU")); + BEAST_EXPECT(s1 == "12TU345"); + BEAST_EXPECT(*s1.end() == 0); + try + { + static_string<6> s2("12345"); + s2.insert(2, static_string<3>("TUV")); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + try + { + static_string<6> s2("12345"); + s2.insert(6, static_string<3>("TUV")); + fail("", __FILE__, __LINE__); + } + catch(std::out_of_range const&) + { + pass(); + } + } + { + static_string<7> s1("12345"); + s1.insert(2, static_string<3>("TUV"), 1); + BEAST_EXPECT(s1 == "12UV345"); + BEAST_EXPECT(*s1.end() == 0); + s1 = "12345"; + s1.insert(2, static_string<3>("TUV"), 1, 1); + BEAST_EXPECT(s1 == "12U345"); + BEAST_EXPECT(*s1.end() == 0); + try + { + static_string<6> s2("12345"); + s2.insert(2, static_string<3>("TUV"), 1, 2); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + try + { + static_string<6> s2("12345"); + s2.insert(6, static_string<3>("TUV"), 1, 2); + fail("", __FILE__, __LINE__); + } + catch(std::out_of_range const&) + { + pass(); + } + } + { + static_string<4> s1("123"); + s1.insert(s1.begin() + 1, '_'); + BEAST_EXPECT(s1 == "1_23"); + BEAST_EXPECT(*s1.end() == 0); + try + { + static_string<3> s2("123"); + s2.insert(s2.begin() + 1, '_'); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + static_string<4> s1("12"); + s1.insert(s1.begin() + 1, 2, '_'); + BEAST_EXPECT(s1 == "1__2"); + BEAST_EXPECT(*s1.end() == 0); + try + { + static_string<4> s2("123"); + s2.insert(s2.begin() + 1, 2, ' '); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + static_string<3> s1("123"); + static_string<5> s2("UV"); + s2.insert(s2.begin() + 1, s1.begin(), s1.end()); + BEAST_EXPECT(s2 == "U123V"); + BEAST_EXPECT(*s2.end() == 0); + try + { + static_string<4> s3("UV"); + s3.insert(s3.begin() + 1, s1.begin(), s1.end()); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + static_string<5> s1("123"); + s1.insert(1, boost::string_ref("UV")); + BEAST_EXPECT(s1 == "1UV23"); + BEAST_EXPECT(*s1.end() == 0); + try + { + static_string<4> s2("123"); + s2.insert(1, boost::string_ref("UV")); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + try + { + static_string<5> s2("123"); + s2.insert(5, boost::string_ref("UV")); + fail("", __FILE__, __LINE__); + } + catch(std::out_of_range const&) + { + pass(); + } + } + { + static_string<5> s1("123"); + s1.insert(1, std::string("UV")); + BEAST_EXPECT(s1 == "1UV23"); + BEAST_EXPECT(*s1.end() == 0); + try + { + s1.insert(1, std::string("UV")); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + static_string<6> s1("123"); + s1.insert(1, std::string("UVX"), 1); + BEAST_EXPECT(s1 == "1VX23"); + BEAST_EXPECT(*s1.end() == 0); + s1.insert(4, std::string("PQR"), 1, 1); + BEAST_EXPECT(s1 == "1VX2Q3"); + BEAST_EXPECT(*s1.end() == 0); + try + { + s1.insert(4, std::string("PQR"), 1, 1); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + + // + // erase + // + + { + static_string<9> s1("123456789"); + BEAST_EXPECT(s1.erase(1, 1) == "13456789"); + BEAST_EXPECT(s1 == "13456789"); + BEAST_EXPECT(*s1.end() == 0); + BEAST_EXPECT(s1.erase(5) == "13456"); + BEAST_EXPECT(s1 == "13456"); + BEAST_EXPECT(*s1.end() == 0); + try + { + s1.erase(7); + fail("", __FILE__, __LINE__); + } + catch(std::out_of_range const&) + { + pass(); + } + } + { + static_string<9> s1("123456789"); + BEAST_EXPECT(*s1.erase(s1.begin() + 5) == '7'); + BEAST_EXPECT(s1 == "12345789"); + BEAST_EXPECT(*s1.end() == 0); + } + { + static_string<9> s1("123456789"); + BEAST_EXPECT(*s1.erase( + s1.begin() + 5, s1.begin() + 7) == '8'); + BEAST_EXPECT(s1 == "1234589"); + BEAST_EXPECT(*s1.end() == 0); + } + + // + // push_back + // + + { + static_string<3> s1("12"); + s1.push_back('3'); + BEAST_EXPECT(s1 == "123"); + try + { + s1.push_back('4'); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + static_string<0> s2; + try + { + s2.push_back('_'); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + + // + // pop_back + // + + { + static_string<3> s1("123"); + s1.pop_back(); + BEAST_EXPECT(s1 == "12"); + BEAST_EXPECT(*s1.end() == 0); + s1.pop_back(); + BEAST_EXPECT(s1 == "1"); + BEAST_EXPECT(*s1.end() == 0); + s1.pop_back(); + BEAST_EXPECT(s1.empty()); + BEAST_EXPECT(*s1.end() == 0); + } + + // + // append + // + + { + static_string<3> s1("1"); + s1.append(2, '_'); + BEAST_EXPECT(s1 == "1__"); + BEAST_EXPECT(*s1.end() == 0); + try + { + static_string<2> s2("1"); + s2.append(2, '_'); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + static_string<2> s1("__"); + static_string<3> s2("1"); + s2.append(s1); + BEAST_EXPECT(s2 == "1__"); + BEAST_EXPECT(*s2.end() == 0); + try + { + static_string<2> s3("1"); + s3.append(s1); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + static_string<3> s1("XYZ"); + static_string<4> s2("12"); + s2.append(s1, 1); + BEAST_EXPECT(s2 == "12YZ"); + BEAST_EXPECT(*s2.end() == 0); + static_string<3> s3("12"); + s3.append(s1, 1, 1); + BEAST_EXPECT(s3 == "12Y"); + BEAST_EXPECT(*s3.end() == 0); + try + { + static_string<3> s4("12"); + s4.append(s1, 3); + fail("", __FILE__, __LINE__); + } + catch(std::out_of_range const&) + { + pass(); + } + try + { + static_string<3> s4("12"); + s4.append(s1, 1); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + static_string<4> s1("12"); + s1.append("XYZ", 2); + BEAST_EXPECT(s1 == "12XY"); + BEAST_EXPECT(*s1.end() == 0); + try + { + static_string<3> s3("12"); + s3.append("XYZ", 2); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + static_string<5> s1("12"); + s1.append("XYZ"); + BEAST_EXPECT(s1 == "12XYZ"); + BEAST_EXPECT(*s1.end() == 0); + try + { + static_string<4> s2("12"); + s2.append("XYZ"); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + static_string<3> s1("XYZ"); + static_string<5> s2("12"); + s2.append(s1.begin(), s1.end()); + BEAST_EXPECT(s2 == "12XYZ"); + BEAST_EXPECT(*s2.end() == 0); + try + { + static_string<4> s3("12"); + s3.append(s1.begin(), s1.end()); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + static_string<5> s1("123"); + s1.append({'X', 'Y'}); + BEAST_EXPECT(s1 == "123XY"); + BEAST_EXPECT(*s1.end() == 0); + try + { + static_string<4> s2("123"); + s2.append({'X', 'Y'}); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + boost::string_ref s1("XYZ"); + static_string<5> s2("12"); + s2.append(s1); + BEAST_EXPECT(s2 == "12XYZ"); + BEAST_EXPECT(*s2.end() == 0); + try + { + static_string<4> s3("12"); + s3.append(s1); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + static_string<6> s1("123"); + s1.append(std::string("UVX"), 1); + BEAST_EXPECT(s1 == "123VX"); + BEAST_EXPECT(*s1.end() == 0); + s1.append(std::string("PQR"), 1, 1); + BEAST_EXPECT(s1 == "123VXQ"); + BEAST_EXPECT(*s1.end() == 0); + try + { + static_string<3> s2("123"); + s2.append(std::string("PQR"), 1, 1); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + + // + // operator+= + // + + { + static_string<2> s1("__"); + static_string<3> s2("1"); + s2 += s1; + BEAST_EXPECT(s2 == "1__"); + BEAST_EXPECT(*s2.end() == 0); + try + { + static_string<2> s3("1"); + s3 += s1; + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + static_string<3> s1("12"); + s1 += '3'; + BEAST_EXPECT(s1 == "123"); + try + { + s1 += '4'; + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + static_string<4> s1("12"); + s1 += "34"; + BEAST_EXPECT(s1 == "1234"); + try + { + s1 += "5"; + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + static_string<4> s1("12"); + s1 += {'3', '4'}; + BEAST_EXPECT(s1 == "1234"); + try + { + s1 += {'5'}; + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + { + boost::string_ref s1("34"); + static_string<4> s2("12"); + s2 += s1; + BEAST_EXPECT(s2 == "1234"); + try + { + s2 += s1; + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + } + + void + testCompare() { using str1 = static_string<1>; using str2 = static_string<2>; @@ -251,10 +1234,217 @@ public: } } + void + testSwap() + { + { + static_string<3> s1("123"); + static_string<3> s2("XYZ"); + swap(s1, s2); + BEAST_EXPECT(s1 == "XYZ"); + BEAST_EXPECT(*s1.end() == 0); + BEAST_EXPECT(s2 == "123"); + BEAST_EXPECT(*s2.end() == 0); + static_string<3> s3("UV"); + swap(s2, s3); + BEAST_EXPECT(s2 == "UV"); + BEAST_EXPECT(*s2.end() == 0); + BEAST_EXPECT(s3 == "123"); + BEAST_EXPECT(*s3.end() == 0); + } + { + static_string<5> s1("123"); + static_string<7> s2("XYZ"); + swap(s1, s2); + BEAST_EXPECT(s1 == "XYZ"); + BEAST_EXPECT(*s1.end() == 0); + BEAST_EXPECT(s2 == "123"); + BEAST_EXPECT(*s2.end() == 0); + static_string<3> s3("UV"); + swap(s2, s3); + BEAST_EXPECT(s2 == "UV"); + BEAST_EXPECT(*s2.end() == 0); + BEAST_EXPECT(s3 == "123"); + BEAST_EXPECT(*s3.end() == 0); + try + { + static_string<5> s4("12345"); + static_string<3> s5("XYZ"); + swap(s4, s5); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + try + { + static_string<3> s4("XYZ"); + static_string<5> s5("12345"); + swap(s4, s5); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + } + + void + testGeneral() + { + using str1 = static_string<1>; + using str2 = static_string<2>; + { + str1 s1; + BEAST_EXPECT(s1 == ""); + BEAST_EXPECT(s1.empty()); + BEAST_EXPECT(s1.size() == 0); + BEAST_EXPECT(s1.max_size() == 1); + BEAST_EXPECT(s1.capacity() == 1); + BEAST_EXPECT(s1.begin() == s1.end()); + BEAST_EXPECT(s1.cbegin() == s1.cend()); + BEAST_EXPECT(s1.rbegin() == s1.rend()); + BEAST_EXPECT(s1.crbegin() == s1.crend()); + try + { + BEAST_EXPECT(s1.at(0) == 0); + fail(); + } + catch(std::exception const&) + { + pass(); + } + BEAST_EXPECT(s1.data()[0] == 0); + BEAST_EXPECT(*s1.c_str() == 0); + BEAST_EXPECT(std::distance(s1.begin(), s1.end()) == 0); + BEAST_EXPECT(std::distance(s1.cbegin(), s1.cend()) == 0); + BEAST_EXPECT(std::distance(s1.rbegin(), s1.rend()) == 0); + BEAST_EXPECT(std::distance(s1.crbegin(), s1.crend()) == 0); + BEAST_EXPECT(s1.compare(s1) == 0); + } + { + str1 const s1; + BEAST_EXPECT(s1 == ""); + BEAST_EXPECT(s1.empty()); + BEAST_EXPECT(s1.size() == 0); + BEAST_EXPECT(s1.max_size() == 1); + BEAST_EXPECT(s1.capacity() == 1); + BEAST_EXPECT(s1.begin() == s1.end()); + BEAST_EXPECT(s1.cbegin() == s1.cend()); + BEAST_EXPECT(s1.rbegin() == s1.rend()); + BEAST_EXPECT(s1.crbegin() == s1.crend()); + try + { + BEAST_EXPECT(s1.at(0) == 0); + fail(); + } + catch(std::exception const&) + { + pass(); + } + BEAST_EXPECT(s1.data()[0] == 0); + BEAST_EXPECT(*s1.c_str() == 0); + BEAST_EXPECT(std::distance(s1.begin(), s1.end()) == 0); + BEAST_EXPECT(std::distance(s1.cbegin(), s1.cend()) == 0); + BEAST_EXPECT(std::distance(s1.rbegin(), s1.rend()) == 0); + BEAST_EXPECT(std::distance(s1.crbegin(), s1.crend()) == 0); + BEAST_EXPECT(s1.compare(s1) == 0); + } + { + str1 s1; + str1 s2("x"); + BEAST_EXPECT(s2 == "x"); + BEAST_EXPECT(s2[0] == 'x'); + BEAST_EXPECT(s2.at(0) == 'x'); + BEAST_EXPECT(s2.front() == 'x'); + BEAST_EXPECT(s2.back() == 'x'); + str1 const s3(s2); + BEAST_EXPECT(s3 == "x"); + BEAST_EXPECT(s3[0] == 'x'); + BEAST_EXPECT(s3.at(0) == 'x'); + BEAST_EXPECT(s3.front() == 'x'); + BEAST_EXPECT(s3.back() == 'x'); + s2 = "y"; + BEAST_EXPECT(s2 == "y"); + BEAST_EXPECT(s3 == "x"); + s1 = s2; + BEAST_EXPECT(s1 == "y"); + s1.clear(); + BEAST_EXPECT(s1.empty()); + BEAST_EXPECT(s1.size() == 0); + } + { + str2 s1("x"); + str1 s2(s1); + BEAST_EXPECT(s2 == "x"); + str1 s3; + s3 = s2; + BEAST_EXPECT(s3 == "x"); + s1 = "xy"; + BEAST_EXPECT(s1.size() == 2); + BEAST_EXPECT(s1[0] == 'x'); + BEAST_EXPECT(s1[1] == 'y'); + BEAST_EXPECT(s1.at(0) == 'x'); + BEAST_EXPECT(s1.at(1) == 'y'); + BEAST_EXPECT(s1.front() == 'x'); + BEAST_EXPECT(s1.back() == 'y'); + auto const s4 = s1; + BEAST_EXPECT(s4[0] == 'x'); + BEAST_EXPECT(s4[1] == 'y'); + BEAST_EXPECT(s4.at(0) == 'x'); + BEAST_EXPECT(s4.at(1) == 'y'); + BEAST_EXPECT(s4.front() == 'x'); + BEAST_EXPECT(s4.back() == 'y'); + try + { + s3 = s1; + fail(); + } + catch(std::exception const&) + { + pass(); + } + try + { + str1 s5(s1); + fail(); + } + catch(std::exception const&) + { + pass(); + } + } + { + str1 s1("x"); + str2 s2; + s2 = s1; + try + { + s1.resize(2); + fail(); + } + catch(std::length_error const&) + { + pass(); + } + } + pass(); + } + void run() override { - testMembers(); + testConstruct(); + testAssign(); + testAccess(); + testIterators(); + testCapacity(); + testOperations(); testCompare(); + testSwap(); + + testGeneral(); } };