diff --git a/include/boost/static_string/config.hpp b/include/boost/static_string/config.hpp index 7e3fc16..c46ed57 100644 --- a/include/boost/static_string/config.hpp +++ b/include/boost/static_string/config.hpp @@ -119,11 +119,15 @@ #if (__cplusplus >= 201402L) && \ (__cplusplus < 201703L) && \ -defined(BOOST_STATIC_STRING_NULL_OPTIMIZATION) && \ defined(__clang__) && \ ((__clang_major__ == 4) || (__clang_major__ == 5)) -#error The null terminator optimization is not supported for clang 4.x and clang 5.x -#undef BOOST_STATIC_STRING_NULL_OPTIMIZATION +// This directive works on clang +#warning C++14 constexpr is not supported in clang 4.x and 5.x due to a compiler bug. +#ifdef BOOST_STATIC_STRING_CPP14_CONSTEXPR_USED +#undef BOOST_STATIC_STRING_CPP14_CONSTEXPR_USED +#endif +#undef BOOST_STATIC_STRING_CPP14_CONSTEXPR +#define BOOST_STATIC_STRING_CPP14_CONSTEXPR #endif namespace boost { diff --git a/include/boost/static_string/detail/static_string.hpp b/include/boost/static_string/detail/static_string.hpp index 958e45f..3c699a1 100644 --- a/include/boost/static_string/detail/static_string.hpp +++ b/include/boost/static_string/detail/static_string.hpp @@ -31,7 +31,8 @@ using static_string = basic_static_string>; template using static_wstring = basic_static_string>; -// At minimum an integral type shall not qualify as an iterator (Agustin Berge) +// At minimum an integral type shall not qualify as an iterator (Agustin Berge) +// Applicable standardese: http://eel.is/c++draft/containers#container.requirements.general-17 template using is_input_iterator = std::integral_constant::value>::type> : std::true_type { }; +// void_t for c++11 +template +using void_t = void; + +// Check if iterator can do subtraction, meaning its random access +template +struct is_difference_iter : std::false_type { }; + +template +struct is_difference_iter() - std::declval())>> : std::true_type { }; + +// constexpr distance for c++14 +template::value>::type* = nullptr> +BOOST_STATIC_STRING_CPP14_CONSTEXPR +typename std::iterator_traits::difference_type +distance(InputIt first, InputIt last) +{ + std::size_t dist = 0; + for (; first != last; ++first, ++dist); + return dist; +} + +template::value>::type* = nullptr> +BOOST_STATIC_STRING_CPP14_CONSTEXPR +typename std::iterator_traits::difference_type +distance(RandomIt first, RandomIt last) +{ + return last - first; +} + +// Copy using traits, respecting iterator rules +template +BOOST_STATIC_STRING_CPP14_CONSTEXPR +void +copy_with_traits(InputIt first, InputIt last, CharT* out) +{ + for (; first != last; ++first, ++out) + Traits::assign(*out, *first); +} + // Optimization for using the smallest possible type template class static_string_base_zero @@ -429,10 +470,14 @@ template< typename Traits, typename CharT, typename ForwardIterator> +BOOST_STATIC_STRING_CPP14_CONSTEXPR inline ForwardIterator find_not_of( - ForwardIterator first, ForwardIterator last, const CharT* str, std::size_t n) noexcept + ForwardIterator first, + ForwardIterator last, + const CharT* str, + std::size_t n) noexcept { for (; first != last; ++first) if (!Traits::find(str, n, *first)) @@ -445,3 +490,4 @@ find_not_of( } // boost #endif + diff --git a/include/boost/static_string/impl/static_string.hpp b/include/boost/static_string/impl/static_string.hpp index fa0e141..d403528 100644 --- a/include/boost/static_string/impl/static_string.hpp +++ b/include/boost/static_string/impl/static_string.hpp @@ -224,7 +224,7 @@ assign( detail::is_input_iterator::value, basic_static_string&>::type { - std::size_t const n = std::distance(first, last); + std::size_t const n = detail::distance(first, last); BOOST_STATIC_STRING_THROW_IF(n > max_size(), std::length_error{"n > max_size()"}); this->set_size(n); @@ -298,22 +298,6 @@ clear() noexcept //------------------------------------------------------------------------------ -template -BOOST_STATIC_STRING_CPP14_CONSTEXPR -auto -basic_static_string:: -insert( - size_type index, - size_type count, - CharT ch) BOOST_STATIC_STRING_COND_NOEXCEPT -> - basic_static_string& -{ - BOOST_STATIC_STRING_THROW_IF( - index > size(), std::out_of_range{"index > size()"}); - insert(begin() + index, count, ch); - return *this; -} - template BOOST_STATIC_STRING_CPP14_CONSTEXPR auto @@ -390,48 +374,60 @@ insert( detail::is_input_iterator< InputIterator>::value, iterator>::type { - const size_type index = pos - begin(); - return insert(index, &*first, std::distance(first, last)).begin() + index; + const auto curr_size = size(); + const auto curr_data = data(); + const auto count = detail::distance(first, last); + const auto index = pos - begin(); + const auto s = &*first; + BOOST_STATIC_STRING_THROW_IF( + index > curr_size, std::out_of_range{"index > size()"}); + BOOST_STATIC_STRING_THROW_IF( + count > max_size() - curr_size, std::length_error{"count > max_size() - size()"}); + const bool inside = s <= &curr_data[curr_size] && s >= curr_data; + if (!inside || (inside && ((s - curr_data) + count <= index))) + { + Traits::move(&curr_data[index + count], &curr_data[index], curr_size - index + 1); + detail::copy_with_traits(first, last, &curr_data[index]); + } + else + { + const size_type offset = s - curr_data; + Traits::move(&curr_data[index + count], &curr_data[index], curr_size - index + 1); + if (offset < index) + { + const size_type diff = index - offset; + Traits::copy(&curr_data[index], &curr_data[offset], diff); + Traits::copy(&curr_data[index + diff], &curr_data[index + count], count - diff); + } + else + { + Traits::copy(&curr_data[index], &curr_data[offset + count], count); + } + } + this->set_size(curr_size + count); + return begin() + index; } template -template BOOST_STATIC_STRING_CPP14_CONSTEXPR auto basic_static_string:: insert( - size_type index, - T const & t) BOOST_STATIC_STRING_COND_NOEXCEPT -> - typename std::enable_if< - std::is_convertible< - T const&, string_view_type>::value && - ! std::is_convertible< - T const&, CharT const*>::value, basic_static_string& - >::type + const_iterator pos, + std::initializer_list ilist) BOOST_STATIC_STRING_COND_NOEXCEPT -> + iterator { - return insert(index, t, 0, npos); -} - -template -template -BOOST_STATIC_STRING_CPP14_CONSTEXPR -auto -basic_static_string:: -insert( - size_type index, - T const & t, - size_type index_str, - size_type count) BOOST_STATIC_STRING_COND_NOEXCEPT -> - typename std::enable_if< - std::is_convertible< - T const&, string_view_type>::value && - ! std::is_convertible< - T const&, CharT const*>::value, basic_static_string& - >::type -{ - auto const s = - string_view_type(t).substr(index_str, count); - return insert(index, s.data(), s.size()); + const auto curr_size = size(); + const auto curr_data = data(); + const auto index = pos - begin(); + BOOST_STATIC_STRING_THROW_IF( + index > curr_size, std::out_of_range{"index > size()"}); + BOOST_STATIC_STRING_THROW_IF( + ilist.size() > max_size() - curr_size, std::length_error{"count > max_size() - size()"}); + Traits::move(&curr_data[index + ilist.size()], &curr_data[index], curr_size - index + 1); + detail::copy_with_traits(ilist.begin(), ilist.end(), &curr_data[index]); + this->set_size(curr_size + ilist.size()); + return curr_data + index; } //------------------------------------------------------------------------------ @@ -477,7 +473,7 @@ erase( iterator { erase(first - begin(), - std::distance(first, last)); + detail::distance(first, last)); return begin() + (first - begin()); } @@ -694,6 +690,97 @@ replace( return *this; } +template +template +BOOST_STATIC_STRING_CPP14_CONSTEXPR +auto +basic_static_string:: +replace( + const_iterator i1, + const_iterator i2, + InputIterator j1, + InputIterator j2) BOOST_STATIC_STRING_COND_NOEXCEPT -> + typename std::enable_if< + detail::is_input_iterator::value, + basic_static_string&>::type +{ + const auto curr_size = size(); + const auto curr_data = data(); + std::size_t n1 = std::distance(i1, i2); + const std::size_t n2 = detail::distance(j1, j2); + const std::size_t pos = i1 - begin(); + const auto s = &*j1; + BOOST_STATIC_STRING_THROW_IF( + pos > curr_size, std::out_of_range{"pos > size()"}); + BOOST_STATIC_STRING_THROW_IF( + curr_size - (std::min)(n1, curr_size - pos) >= max_size() - n2, + std::length_error{"replaced string exceeds max_size()"}); + if (pos + n1 >= curr_size) + n1 = curr_size - pos; + const bool inside = s <= &curr_data[curr_size] && s >= curr_data; + if (inside && size_type(s - curr_data) == pos && n1 == n2) + return *this; + if (!inside || (inside && ((s - curr_data) + n2 <= pos))) + { + // source outside + Traits::move(&curr_data[pos + n2], &curr_data[pos + n1], curr_size - pos - n1 + 1); + detail::copy_with_traits(j1, j2, &curr_data[pos]); + } + else + { + // source inside + const size_type offset = s - curr_data; + if (n2 >= n1) + { + // grow/unchanged + // shift all right of splice point by n2 - n1 to the right + Traits::move(&curr_data[pos + n2], &curr_data[pos + n1], curr_size - pos - n1 + 1); + const size_type diff = offset <= pos + n1 ? (std::min)((pos + n1) - offset, n2) : 0; + // copy all before splice point + Traits::move(&curr_data[pos], &curr_data[offset], diff); + // copy all after splice point + Traits::move(&curr_data[pos + diff], &curr_data[offset + (n2 - n1) + diff], n2 - diff); + } + else + { + // shrink + // copy all elements into place + Traits::move(&curr_data[pos], &curr_data[offset], n2); + // shift all elements after splice point left + Traits::move(&curr_data[pos + n2], &curr_data[pos + n1], curr_size - pos - n1 + 1); + } + } + this->set_size(curr_size + (n2 - n1)); + return *this; +} + +template +BOOST_STATIC_STRING_CPP14_CONSTEXPR +auto +basic_static_string:: +replace( + const_iterator i1, + const_iterator i2, + std::initializer_list il) BOOST_STATIC_STRING_COND_NOEXCEPT -> + basic_static_string& +{ + const auto curr_size = size(); + const auto curr_data = data(); + std::size_t n1 = detail::distance(i1, i2); + const std::size_t pos = i1 - begin(); + BOOST_STATIC_STRING_THROW_IF( + pos > curr_size, std::out_of_range{"pos > size()"}); + BOOST_STATIC_STRING_THROW_IF( + curr_size - (std::min)(n1, curr_size - pos) >= max_size() - il.size(), + std::length_error{"replaced string exceeds max_size()"}); + if (pos + n1 >= curr_size) + n1 = curr_size - pos; + Traits::move(&curr_data[pos + il.size()], &curr_data[pos + n1], curr_size - pos - n1 + 1); + detail::copy_with_traits(il.begin(), il.end(), &curr_data[pos]); + this->set_size(curr_size + (il.size() - n1)); + return *this; +} + template BOOST_STATIC_STRING_CPP14_CONSTEXPR auto @@ -710,7 +797,7 @@ find( if (!n) return pos; const auto res = std::search(&data()[pos], &data()[curr_size], s, &s[n], Traits::eq); - return res == end() ? npos : std::distance(data(), res); + return res == end() ? npos : detail::distance(data(), res); } template @@ -733,7 +820,7 @@ rfind( return pos; for (auto sub = &curr_data[pos]; sub >= curr_data; --sub) if (!Traits::compare(sub, s, n)) - return std::distance(curr_data, sub); + return detail::distance(curr_data, sub); return npos; } @@ -751,7 +838,7 @@ find_first_of( if (pos >= size() || !n) return npos; const auto res = std::find_first_of(&curr_data[pos], &curr_data[size()], s, &s[n], Traits::eq); - return res == end() ? npos : std::distance(curr_data, res); + return res == end() ? npos : detail::distance(curr_data, res); } template @@ -772,7 +859,7 @@ find_last_of( else pos = curr_size - (pos + 1); const auto res = std::find_first_of(rbegin() + pos, rend(), s, &s[n], Traits::eq); - return res == rend() ? npos : curr_size - 1 - std::distance(rbegin(), res); + return res == rend() ? npos : curr_size - 1 - detail::distance(rbegin(), res); } template @@ -790,7 +877,7 @@ find_first_not_of( if (!n) return pos; const auto res = detail::find_not_of(&data()[pos], &data()[size()], s, n); - return res == end() ? npos : std::distance(data(), res); + return res == end() ? npos : detail::distance(data(), res); } template @@ -810,7 +897,7 @@ find_last_not_of( return pos; pos = curr_size - (pos + 1); const auto res = detail::find_not_of(rbegin() + pos, rend(), s, n); - return res == rend() ? npos : curr_size - 1 - std::distance(rbegin(), res); + return res == rend() ? npos : curr_size - 1 - detail::distance(rbegin(), res); } template diff --git a/include/boost/static_string/static_string.hpp b/include/boost/static_string/static_string.hpp index 0b9ca12..332ff71 100644 --- a/include/boost/static_string/static_string.hpp +++ b/include/boost/static_string/static_string.hpp @@ -425,6 +425,7 @@ public: @throw std::length_error if `ilist.size() > max_size()` @return `*this` */ + BOOST_STATIC_STRING_CPP14_CONSTEXPR basic_static_string& assign( std::initializer_list ilist) BOOST_STATIC_STRING_COND_NOEXCEPT @@ -802,7 +803,13 @@ public: insert( size_type index, size_type count, - CharT ch) BOOST_STATIC_STRING_COND_NOEXCEPT; + CharT ch) BOOST_STATIC_STRING_COND_NOEXCEPT + { + BOOST_STATIC_STRING_THROW_IF( + index > size(), std::out_of_range{"index > size()"}); + insert(begin() + index, count, ch); + return *this; + } /** Insert into the string. @@ -947,13 +954,10 @@ public: @return An iterator to the first inserted character or pos if no insertion took place */ BOOST_STATIC_STRING_CPP14_CONSTEXPR - iterator - insert( + iterator + insert( const_iterator pos, - std::initializer_list ilist) BOOST_STATIC_STRING_COND_NOEXCEPT - { - return insert(pos, ilist.begin(), ilist.end()); - } + std::initializer_list ilist) BOOST_STATIC_STRING_COND_NOEXCEPT; /** Insert into the string. @@ -981,7 +985,10 @@ public: #endif insert( size_type index, - T const& t) BOOST_STATIC_STRING_COND_NOEXCEPT; + T const& t) BOOST_STATIC_STRING_COND_NOEXCEPT + { + return insert(index, t, 0, npos); + } /** Insert into the string. @@ -1010,7 +1017,11 @@ public: size_type index, T const& t, size_type index_str, - size_type count = npos) BOOST_STATIC_STRING_COND_NOEXCEPT; + size_type count = npos) BOOST_STATIC_STRING_COND_NOEXCEPT + { + auto const s = string_view_type(t).substr(index_str, count); + return insert(index, s.data(), s.size()); + } /** Removes `min(count, size() - index)` characters starting at `index` @@ -1852,10 +1863,7 @@ public: const_iterator i1, const_iterator i2, InputIterator j1, - InputIterator j2) BOOST_STATIC_STRING_COND_NOEXCEPT - { - return replace(i1, i2, basic_static_string(j1, j2)); - } + InputIterator j2) BOOST_STATIC_STRING_COND_NOEXCEPT; /** Replace a subset of the string. @@ -1866,14 +1874,11 @@ public: @return `*this` */ BOOST_STATIC_STRING_CPP14_CONSTEXPR - basic_static_string& - replace( + basic_static_string& + replace( const_iterator i1, const_iterator i2, - std::initializer_list il) BOOST_STATIC_STRING_COND_NOEXCEPT - { - return replace(i1, i2, il.begin(), il.size()); - } + std::initializer_list il) BOOST_STATIC_STRING_COND_NOEXCEPT; //-------------------------------------------------------------------------- //