diff --git a/libcxx/include/filesystem b/libcxx/include/filesystem --- a/libcxx/include/filesystem +++ b/libcxx/include/filesystem @@ -792,7 +792,7 @@ template static typename enable_if<__is_cpp17_forward_iterator<_Iter>::value>::type __append_range(__path_string& __dest, _Iter __b, _Iter __e) { - __dest.__append_forward_unsafe(__b, __e); + __dest.append(__b, __e); } template diff --git a/libcxx/include/string b/libcxx/include/string --- a/libcxx/include/string +++ b/libcxx/include/string @@ -514,6 +514,7 @@ */ #include <__config> +#include #include #include #include @@ -632,30 +633,6 @@ _LIBCPP_EXTERN_TEMPLATE(class _LIBCPP_EXTERN_TEMPLATE_TYPE_VIS __basic_string_common) -#ifdef _LIBCPP_NO_EXCEPTIONS -template -struct __libcpp_string_gets_noexcept_iterator_impl : public true_type {}; -#elif defined(_LIBCPP_HAS_NO_NOEXCEPT) -template -struct __libcpp_string_gets_noexcept_iterator_impl : public false_type {}; -#else -template ::value> -struct __libcpp_string_gets_noexcept_iterator_impl : public _LIBCPP_BOOL_CONSTANT(( - noexcept(++(declval<_Iter&>())) && - is_nothrow_assignable<_Iter&, _Iter>::value && - noexcept(declval<_Iter>() == declval<_Iter>()) && - noexcept(*declval<_Iter>()) -)) {}; - -template -struct __libcpp_string_gets_noexcept_iterator_impl<_Iter, false> : public false_type {}; -#endif - - -template -struct __libcpp_string_gets_noexcept_iterator - : public _LIBCPP_BOOL_CONSTANT(__libcpp_is_trivial_iterator<_Iter>::value || __libcpp_string_gets_noexcept_iterator_impl<_Iter>::value) {}; - template struct __can_be_converted_to_string_view : public _BoolConstant< is_convertible >::value && @@ -1048,15 +1025,11 @@ _LIBCPP_INLINE_VISIBILITY void __append_default_init(size_type __n); - template - _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS - basic_string& __append_forward_unsafe(_ForwardIterator, _ForwardIterator); template _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS _EnableIf < - __is_exactly_cpp17_input_iterator<_InputIterator>::value - || !__libcpp_string_gets_noexcept_iterator<_InputIterator>::value, + __is_exactly_cpp17_input_iterator<_InputIterator>::value, basic_string& > _LIBCPP_INLINE_VISIBILITY @@ -1069,14 +1042,11 @@ _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS _EnableIf < - __is_cpp17_forward_iterator<_ForwardIterator>::value - && __libcpp_string_gets_noexcept_iterator<_ForwardIterator>::value, + __is_cpp17_forward_iterator<_ForwardIterator>::value, basic_string& > _LIBCPP_INLINE_VISIBILITY - append(_ForwardIterator __first, _ForwardIterator __last) { - return __append_forward_unsafe(__first, __last); - } + append(_ForwardIterator __first, _ForwardIterator __last); #ifndef _LIBCPP_CXX03_LANG _LIBCPP_INLINE_VISIBILITY @@ -1124,8 +1094,8 @@ _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS _EnableIf < - __is_exactly_cpp17_input_iterator<_InputIterator>::value - || !__libcpp_string_gets_noexcept_iterator<_InputIterator>::value, + __is_exactly_cpp17_input_iterator<_InputIterator>::value + || !__libcpp_is_trivial_iterator<_InputIterator>::value, basic_string& > assign(_InputIterator __first, _InputIterator __last); @@ -1134,7 +1104,7 @@ _EnableIf < __is_cpp17_forward_iterator<_ForwardIterator>::value - && __libcpp_string_gets_noexcept_iterator<_ForwardIterator>::value, + && __libcpp_is_trivial_iterator<_ForwardIterator>::value, basic_string& > assign(_ForwardIterator __first, _ForwardIterator __last); @@ -1175,8 +1145,8 @@ _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS _EnableIf < - __is_exactly_cpp17_input_iterator<_InputIterator>::value - || !__libcpp_string_gets_noexcept_iterator<_InputIterator>::value, + __is_exactly_cpp17_input_iterator<_InputIterator>::value + || !__libcpp_is_trivial_iterator<_InputIterator>::value, iterator > insert(const_iterator __pos, _InputIterator __first, _InputIterator __last); @@ -1185,7 +1155,7 @@ _EnableIf < __is_cpp17_forward_iterator<_ForwardIterator>::value - && __libcpp_string_gets_noexcept_iterator<_ForwardIterator>::value, + && __libcpp_is_trivial_iterator<_ForwardIterator>::value, iterator > insert(const_iterator __pos, _ForwardIterator __first, _ForwardIterator __last); @@ -1721,6 +1691,22 @@ _LIBCPP_INLINE_VISIBILITY void __invalidate_all_iterators(); _LIBCPP_INLINE_VISIBILITY void __invalidate_iterators_past(size_type); + struct __null_terminator_guard { + explicit __null_terminator_guard(value_type *__p) : + __p_(__p) {} + + void __release_guard() { + __p_ = nullptr; + } + + ~__null_terminator_guard() { + if (__p_) + *__p_ = value_type(); + } + private: + value_type *__p_; + }; + friend basic_string operator+<>(const basic_string&, const basic_string&); friend basic_string operator+<>(const value_type*, const basic_string&); friend basic_string operator+<>(value_type, const basic_string&); @@ -2470,8 +2456,8 @@ template _EnableIf < - __is_exactly_cpp17_input_iterator <_InputIterator>::value - || !__libcpp_string_gets_noexcept_iterator<_InputIterator>::value, + __is_exactly_cpp17_input_iterator<_InputIterator>::value + || !__libcpp_is_trivial_iterator<_InputIterator>::value, basic_string<_CharT, _Traits, _Allocator>& > basic_string<_CharT, _Traits, _Allocator>::assign(_InputIterator __first, _InputIterator __last) @@ -2486,7 +2472,7 @@ _EnableIf < __is_cpp17_forward_iterator<_ForwardIterator>::value - && __libcpp_string_gets_noexcept_iterator<_ForwardIterator>::value, + && __libcpp_is_trivial_iterator<_ForwardIterator>::value, basic_string<_CharT, _Traits, _Allocator>& > basic_string<_CharT, _Traits, _Allocator>::assign(_ForwardIterator __first, _ForwardIterator __last) @@ -2665,12 +2651,14 @@ template template -basic_string<_CharT, _Traits, _Allocator>& -basic_string<_CharT, _Traits, _Allocator>::__append_forward_unsafe( +_EnableIf +< + __is_cpp17_forward_iterator<_ForwardIterator>::value, + basic_string<_CharT, _Traits, _Allocator>& +> +basic_string<_CharT, _Traits, _Allocator>::append( _ForwardIterator __first, _ForwardIterator __last) { - static_assert(__is_cpp17_forward_iterator<_ForwardIterator>::value, - "function requires a ForwardIterator"); size_type __sz = size(); size_type __cap = capacity(); size_type __n = static_cast(_VSTD::distance(__first, __last)); @@ -2678,9 +2666,13 @@ { typedef typename iterator_traits<_ForwardIterator>::reference _CharRef; _CharRef __tmp_ref = *__first; - if (__ptr_in_range(_VSTD::addressof(__tmp_ref), data(), data() + size())) + bool __needs_grow = (__cap - __sz < __n); + if (__needs_grow && (!__libcpp_is_trivial_iterator<_ForwardIterator>::value || + _VSTD::__ptr_in_range(_VSTD::addressof(__tmp_ref), data(), data() + size()))) { - const basic_string __temp (__first, __last, __alloc()); + // We may need to finish iterating the original range + // before reallocating this string. + const basic_string __temp(__first, __last, __alloc()); append(__temp.data(), __temp.size()); } else @@ -2688,8 +2680,10 @@ if (__cap - __sz < __n) __grow_by(__cap, __sz + __n - __cap, __sz, __sz, 0); pointer __p = __get_pointer() + __sz; + __null_terminator_guard __ntr(_VSTD::addressof(*__p)); for (; __first != __last; ++__p, ++__first) traits_type::assign(*__p, *__first); + __ntr.__release_guard(); traits_type::assign(*__p, value_type()); __set_size(__sz + __n); } @@ -2808,9 +2802,9 @@ template _EnableIf < - __is_exactly_cpp17_input_iterator<_InputIterator>::value - || !__libcpp_string_gets_noexcept_iterator<_InputIterator>::value, - typename basic_string<_CharT, _Traits, _Allocator>::iterator + __is_exactly_cpp17_input_iterator<_InputIterator>::value + || !__libcpp_is_trivial_iterator<_InputIterator>::value, + typename basic_string<_CharT, _Traits, _Allocator>::iterator > basic_string<_CharT, _Traits, _Allocator>::insert(const_iterator __pos, _InputIterator __first, _InputIterator __last) { @@ -2828,7 +2822,7 @@ _EnableIf < __is_cpp17_forward_iterator<_ForwardIterator>::value - && __libcpp_string_gets_noexcept_iterator<_ForwardIterator>::value, + && __libcpp_is_trivial_iterator<_ForwardIterator>::value, typename basic_string<_CharT, _Traits, _Allocator>::iterator > basic_string<_CharT, _Traits, _Allocator>::insert(const_iterator __pos, _ForwardIterator __first, _ForwardIterator __last) @@ -2844,7 +2838,7 @@ { typedef typename iterator_traits<_ForwardIterator>::reference _CharRef; _CharRef __tmp_char = *__first; - if (__ptr_in_range(_VSTD::addressof(__tmp_char), data(), data() + size())) + if (_VSTD::__ptr_in_range(_VSTD::addressof(__tmp_char), data(), data() + size())) { const basic_string __temp(__first, __last, __alloc()); return insert(__pos, __temp.data(), __temp.data() + __temp.size()); diff --git a/libcxx/test/libcxx/strings/iterators.exceptions.pass.cpp b/libcxx/test/libcxx/strings/iterators.exceptions.pass.cpp deleted file mode 100644 --- a/libcxx/test/libcxx/strings/iterators.exceptions.pass.cpp +++ /dev/null @@ -1,89 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// - -// __libcpp_is_trivial_iterator - -// __libcpp_string_gets_noexcept_iterator determines if an iterator can be used -// w/o worrying about whether or not certain operations can throw. -// This gives us a "fast path for string operations" -// - -#include -#include -#include -#include -#include - -#include "test_macros.h" -#include "test_iterators.h" - -#ifndef TEST_HAS_NO_EXCEPTIONS -static const bool expected = false; -#else -// Under -fno-exceptions all noexcept expressions are trivially true, so -// any check for a noexcept returning false must actually check for it being -// true. -static const bool expected = true; -#endif - -int main(int, char**) -{ -// basic tests - static_assert(( std::__libcpp_string_gets_noexcept_iterator::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::value), ""); - - static_assert(( std::__libcpp_string_gets_noexcept_iterator > ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator >::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator > ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator >::value), ""); - - static_assert(( std::__libcpp_string_gets_noexcept_iterator > ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator >::value), ""); - - static_assert(( std::__libcpp_string_gets_noexcept_iterator > > ::value), ""); - -// iterators in the libc++ test suite - static_assert(std::__libcpp_string_gets_noexcept_iterator >::value == expected, ""); - static_assert(std::__libcpp_string_gets_noexcept_iterator >::value == expected, ""); - static_assert(std::__libcpp_string_gets_noexcept_iterator >::value == expected, ""); - static_assert(std::__libcpp_string_gets_noexcept_iterator >::value == expected, ""); - static_assert(std::__libcpp_string_gets_noexcept_iterator >::value == expected, ""); - static_assert(std::__libcpp_string_gets_noexcept_iterator >::value == expected, ""); - -#if TEST_STD_VER >= 11 - static_assert(( std::__libcpp_string_gets_noexcept_iterator >::value), ""); -#else - static_assert(std::__libcpp_string_gets_noexcept_iterator >::value == expected, ""); -#endif - -// -// iterators from libc++'s containers -// - -// string - static_assert(( std::__libcpp_string_gets_noexcept_iterator::iterator> ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::const_iterator> ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::reverse_iterator> ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::const_reverse_iterator>::value), ""); - -// vector - static_assert(( std::__libcpp_string_gets_noexcept_iterator::iterator> ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::const_iterator> ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::reverse_iterator> ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::const_reverse_iterator>::value), ""); - -#if TEST_STD_VER >= 11 -// Initializer list (which has no reverse iterators) - static_assert(( std::__libcpp_string_gets_noexcept_iterator::iterator> ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::const_iterator> ::value), ""); -#endif - - return 0; -} diff --git a/libcxx/test/libcxx/strings/iterators.noexcept.pass.cpp b/libcxx/test/libcxx/strings/iterators.noexcept.pass.cpp deleted file mode 100644 --- a/libcxx/test/libcxx/strings/iterators.noexcept.pass.cpp +++ /dev/null @@ -1,81 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// - -// - -// __libcpp_is_trivial_iterator - -// __libcpp_string_gets_noexcept_iterator determines if an iterator can be used -// w/o worrying about whether or not certain operations can throw. -// This gives us a "fast path for string operations". -// -// When exceptions are disabled, all iterators should get this "fast path" -// - -// ADDITIONAL_COMPILE_FLAGS: -fno-exceptions - -#include -#include -#include -#include -#include - -#include "test_macros.h" -#include "test_iterators.h" - -int main(int, char**) -{ -// basic tests - static_assert(( std::__libcpp_string_gets_noexcept_iterator::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::value), ""); - - static_assert(( std::__libcpp_string_gets_noexcept_iterator > ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator >::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator > ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator >::value), ""); - - static_assert(( std::__libcpp_string_gets_noexcept_iterator > ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator >::value), ""); - - static_assert(( std::__libcpp_string_gets_noexcept_iterator > > ::value), ""); - -// iterators in the libc++ test suite - static_assert(( std::__libcpp_string_gets_noexcept_iterator >::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator >::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator >::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator >::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator >::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator >::value), ""); - - static_assert(( std::__libcpp_string_gets_noexcept_iterator >::value), ""); - -// -// iterators from libc++'s containers -// - -// string - static_assert(( std::__libcpp_string_gets_noexcept_iterator::iterator> ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::const_iterator> ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::reverse_iterator> ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::const_reverse_iterator>::value), ""); - -// vector - static_assert(( std::__libcpp_string_gets_noexcept_iterator::iterator> ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::const_iterator> ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::reverse_iterator> ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::const_reverse_iterator>::value), ""); - -#if TEST_STD_VER >= 11 -// Initializer list (which has no reverse iterators) - static_assert(( std::__libcpp_string_gets_noexcept_iterator::iterator> ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::const_iterator> ::value), ""); -#endif - - return 0; -} diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/robust_against_adl.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/robust_against_adl.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/robust_against_adl.pass.cpp @@ -0,0 +1,33 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +#include +#include + +struct Incomplete; +template struct Holder { T t; }; + +template +struct Charlike { + char ch_; + Charlike(char ch) : ch_(ch) {} + operator char() const { return ch_; } +}; + +int main(int, char**) +{ + std::string s; + Charlike > a[] = {'m', 'a', 'h', 'i'}; + s.append(a, a+4); + s.insert(s.begin(), a, a+4); + assert(s == "mahimahi"); + + return 0; +} diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/strong_guarantee.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/strong_guarantee.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/strong_guarantee.pass.cpp @@ -0,0 +1,98 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03 +// UNSUPPORTED: no-exceptions + +// + +// template +// basic_string& append(InputIterator first, InputIterator last); + +#include +#include + +#include "test_macros.h" + +int count; + +struct Iterator { + using value_type = char; + using difference_type = int; + using iterator_category = std::forward_iterator_tag; + using reference = const char&; + using pointer = const char*; + + const char *ptr_ = nullptr; + + explicit Iterator() = default; + explicit Iterator(const char *p) : ptr_(p) {} + Iterator operator++(int) { Iterator copy(*this); ++*this; return copy; } + Iterator& operator++() noexcept { ++ptr_; return *this; } + const char& operator*() const noexcept { return *ptr_; } + friend bool operator==(Iterator a, Iterator b) noexcept { return a.ptr_ == b.ptr_; } + friend bool operator!=(Iterator a, Iterator b) noexcept(false) { + if (--count == 0) throw "oops"; + return a.ptr_ != b.ptr_; + } +}; + +void test_one_count(size_t first_size, size_t first_capacity_remaining, size_t second_size) +{ + // Make a string "aaaa..." with 'remaining' remaining capacity. + std::string s(first_size, 'a'); + assert(s.capacity() >= first_capacity_remaining); + s.resize(s.capacity() - first_capacity_remaining); + for (char& ch : s) { + ch = 'a'; + } + std::string original = s; + + std::string::iterator begin = s.begin(); + std::string::iterator end = s.end(); + const char *data = s.data(); + try { + std::string needle(second_size, 'b'); + Iterator first(needle.data()); + Iterator last(needle.data() + needle.size()); + s.append(first, last); + LIBCPP_ASSERT(s.__invariants()); + assert(s == original + needle); + } catch (...) { + // Part of "no effects" is that iterators and pointers + // into the string must not have been invalidated. + LIBCPP_ASSERT(s.__invariants()); + assert(s == original); + assert(s.begin() == begin); + assert(s.end() == end); + assert(s.data() == data); + } +} + +void test_all_counts(size_t first_size, size_t first_capacity_remaining, size_t second_size) +{ + for (int i=1; true; ++i) { + count = i; + test_one_count(first_size, first_capacity_remaining, second_size); + if (count != 0) break; // in this case no exception was thrown + } +} + +int main(int, char**) +{ + test_all_counts(10, 0, 2); + test_all_counts(10, 5, 4); + test_all_counts(10, 5, 5); + test_all_counts(10, 5, 6); + test_all_counts(100, 0, 2); + test_all_counts(100, 5, 4); + test_all_counts(100, 5, 5); + test_all_counts(100, 5, 6); + + return 0; +} diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/strong_guarantee.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/strong_guarantee.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/strong_guarantee.pass.cpp @@ -0,0 +1,93 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03 +// UNSUPPORTED: no-exceptions + +// + +// template +// iterator insert(const_iterator pos, InputIterator first, InputIterator last); + +#include +#include + +#include "test_macros.h" + +int count; + +struct Iterator { + using value_type = char; + using difference_type = int; + using iterator_category = std::forward_iterator_tag; + using reference = const char&; + using pointer = const char*; + + const char *ptr_ = nullptr; + + explicit Iterator() = default; + explicit Iterator(const char *p) : ptr_(p) {} + Iterator operator++(int) { Iterator copy(*this); ++*this; return copy; } + Iterator& operator++() noexcept { ++ptr_; return *this; } + const char& operator*() const noexcept { return *ptr_; } + friend bool operator==(Iterator a, Iterator b) noexcept { return a.ptr_ == b.ptr_; } + friend bool operator!=(Iterator a, Iterator b) noexcept(false) { + if (--count == 0) throw "oops"; + return a.ptr_ != b.ptr_; + } +}; + +void test_one_count(const std::string& original, const std::string& needle, int pos) +{ + std::string expected = original; + expected.insert(expected.begin() + pos, needle.begin(), needle.end()); + std::string s = original; + std::string::iterator begin = s.begin(); + std::string::iterator end = s.end(); + const char *data = s.data(); + try { + Iterator first(needle.data()); + Iterator last(needle.data() + needle.size()); + s.insert(s.begin() + pos, first, last); + LIBCPP_ASSERT(s.__invariants()); + assert(s == expected); + } catch (...) { + // Part of "no effects" is that iterators and pointers + // into the string must not have been invalidated. + LIBCPP_ASSERT(s.__invariants()); + assert(s == original); + assert(s.begin() == begin); + assert(s.end() == end); + assert(s.data() == data); + } +} + +void test_all_counts(const std::string& original, const std::string& needle, int pos) +{ + for (int i=1; true; ++i) { + count = i; + test_one_count(original, needle, pos); + if (count != 0) break; // in this case no exception was thrown + } +} + +int main(int, char**) +{ + test_all_counts("", "xy", 0); + test_all_counts("aaa", "xy", 0); + test_all_counts("aaa", "xy", 1); + test_all_counts("aaa", "xy", 2); + test_all_counts("aaa", "xy", 3); + test_all_counts("a very long string that defies short string optimization", "short", 0); + test_all_counts("a very long string that defies short string optimization", "short", 2); + test_all_counts("a very long string that defies short string optimization", "short", 25); + test_all_counts("a very long string that defies short string optimization", "short", 53); + test_all_counts("a very long string that defies short string optimization", "short", 56); + + return 0; +}