summaryrefslogtreecommitdiff
path: root/libcxx/test/libcxx-03/strings
diff options
context:
space:
mode:
Diffstat (limited to 'libcxx/test/libcxx-03/strings')
-rw-r--r--libcxx/test/libcxx-03/strings/basic.string/sizeof.compile.pass.cpp145
-rw-r--r--libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp37
-rw-r--r--libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp120
-rw-r--r--libcxx/test/libcxx-03/strings/basic.string/string.cons/copy_shrunk_long.pass.cpp45
-rw-r--r--libcxx/test/libcxx-03/strings/basic.string/string.modifiers/resize_default_initialized.pass.cpp75
-rw-r--r--libcxx/test/libcxx-03/strings/c.strings/constexpr_memmove.pass.cpp155
-rw-r--r--libcxx/test/libcxx-03/strings/string.view/string.view.iterators/assert.iterator-indexing.pass.cpp158
7 files changed, 735 insertions, 0 deletions
diff --git a/libcxx/test/libcxx-03/strings/basic.string/sizeof.compile.pass.cpp b/libcxx/test/libcxx-03/strings/basic.string/sizeof.compile.pass.cpp
new file mode 100644
index 000000000000..31f1a94c7321
--- /dev/null
+++ b/libcxx/test/libcxx-03/strings/basic.string/sizeof.compile.pass.cpp
@@ -0,0 +1,145 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Ensure that we never change the size or alignment of `basic_string`
+
+#include <cstdint>
+#include <iterator>
+#include <string>
+
+#include "test_macros.h"
+#include "min_allocator.h"
+#include "test_allocator.h"
+
+template <class T>
+class small_pointer {
+public:
+ using value_type = T;
+ using difference_type = std::int16_t;
+ using pointer = small_pointer;
+ using reference = T&;
+ using iterator_category = std::random_access_iterator_tag;
+
+private:
+ std::uint16_t offset;
+};
+
+template <class T>
+class small_iter_allocator {
+public:
+ using value_type = T;
+ using pointer = small_pointer<T>;
+ using size_type = std::int16_t;
+ using difference_type = std::int16_t;
+
+ small_iter_allocator() TEST_NOEXCEPT {}
+
+ template <class U>
+ small_iter_allocator(small_iter_allocator<U>) TEST_NOEXCEPT {}
+
+ T* allocate(std::size_t n);
+ void deallocate(T* p, std::size_t);
+
+ friend bool operator==(small_iter_allocator, small_iter_allocator) { return true; }
+ friend bool operator!=(small_iter_allocator, small_iter_allocator) { return false; }
+};
+
+template <class CharT>
+using min_string = std::basic_string<CharT, std::char_traits<CharT>, min_allocator<CharT> >;
+
+template <class CharT>
+using test_string = std::basic_string<CharT, std::char_traits<CharT>, test_allocator<CharT> >;
+
+template <class CharT>
+using small_string = std::basic_string<CharT, std::char_traits<CharT>, small_iter_allocator<CharT> >;
+
+#if __SIZE_WIDTH__ == 64
+
+static_assert(sizeof(std::string) == 24, "");
+static_assert(sizeof(min_string<char>) == 24, "");
+static_assert(sizeof(test_string<char>) == 32, "");
+static_assert(sizeof(small_string<char>) == 6, "");
+
+# ifndef TEST_HAS_NO_WIDE_CHARACTERS
+# if __WCHAR_WIDTH__ == 32
+static_assert(sizeof(std::wstring) == 24, "");
+static_assert(sizeof(min_string<wchar_t>) == 24, "");
+static_assert(sizeof(test_string<wchar_t>) == 32, "");
+static_assert(sizeof(small_string<wchar_t>) == 12, "");
+# elif __WCHAR_WIDTH__ == 16
+static_assert(sizeof(std::wstring) == 24, "");
+static_assert(sizeof(min_string<wchar_t>) == 24, "");
+static_assert(sizeof(test_string<wchar_t>) == 32, "");
+static_assert(sizeof(small_string<wchar_t>) == 6, "");
+# else
+# error "Unexpected wchar_t width"
+# endif
+# endif
+
+# ifndef TEST_HAS_NO_CHAR8_T
+static_assert(sizeof(std::u8string) == 24, "");
+static_assert(sizeof(min_string<char8_t>) == 24, "");
+static_assert(sizeof(test_string<char8_t>) == 32, "");
+static_assert(sizeof(small_string<char8_t>) == 6, "");
+# endif
+
+# ifndef TEST_HAS_NO_UNICODE_CHARS
+static_assert(sizeof(std::u16string) == 24, "");
+static_assert(sizeof(std::u32string) == 24, "");
+static_assert(sizeof(min_string<char16_t>) == 24, "");
+static_assert(sizeof(min_string<char32_t>) == 24, "");
+static_assert(sizeof(test_string<char16_t>) == 32, "");
+static_assert(sizeof(test_string<char32_t>) == 32, "");
+static_assert(sizeof(small_string<char16_t>) == 6, "");
+static_assert(sizeof(small_string<char32_t>) == 12, "");
+# endif
+
+#elif __SIZE_WIDTH__ == 32
+
+static_assert(sizeof(std::string) == 12, "");
+static_assert(sizeof(min_string<char>) == 12, "");
+static_assert(sizeof(test_string<char>) == 24, "");
+static_assert(sizeof(small_string<char>) == 6, "");
+
+# ifndef TEST_HAS_NO_WIDE_CHARACTERS
+# if __WCHAR_WIDTH__ == 32
+static_assert(sizeof(std::wstring) == 12, "");
+static_assert(sizeof(min_string<wchar_t>) == 12, "");
+static_assert(sizeof(test_string<wchar_t>) == 24, "");
+static_assert(sizeof(small_string<wchar_t>) == 12, "");
+# elif __WCHAR_WIDTH__ == 16
+static_assert(sizeof(std::wstring) == 12, "");
+static_assert(sizeof(min_string<wchar_t>) == 12, "");
+static_assert(sizeof(test_string<wchar_t>) == 24, "");
+static_assert(sizeof(small_string<wchar_t>) == 6, "");
+# else
+# error "Unexpected wchar_t width"
+# endif
+# endif
+
+# ifndef TEST_HAS_NO_CHAR8_T
+static_assert(sizeof(std::u8string) == 12, "");
+static_assert(sizeof(min_string<char8_t>) == 12, "");
+static_assert(sizeof(test_string<char8_t>) == 24, "");
+static_assert(sizeof(small_string<char>) == 6, "");
+# endif
+
+# ifndef TEST_HAS_NO_UNICODE_CHARS
+static_assert(sizeof(std::u16string) == 12, "");
+static_assert(sizeof(std::u32string) == 12, "");
+static_assert(sizeof(min_string<char16_t>) == 12, "");
+static_assert(sizeof(min_string<char32_t>) == 12, "");
+static_assert(sizeof(test_string<char16_t>) == 24, "");
+static_assert(sizeof(test_string<char32_t>) == 24, "");
+static_assert(sizeof(small_string<char16_t>) == 6, "");
+static_assert(sizeof(small_string<char32_t>) == 12, "");
+# endif
+
+#else
+# error "std::size_t has an unexpected size"
+#endif
diff --git a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp
new file mode 100644
index 000000000000..77da29225957
--- /dev/null
+++ b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <string>
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <string>
+
+#include "test_macros.h"
+
+// alignment of the string heap buffer is hardcoded to 8
+const std::size_t alignment = 8;
+
+int main(int, char**) {
+ std::string input_string;
+ input_string.resize(64, 'a');
+
+ // Call a constructor which selects its size using __recommend.
+ std::string test_string(input_string.data());
+ const std::size_t expected_align8_size = 71;
+
+ // Demonstrate the lesser capacity/allocation size when the alignment requirement is 8.
+ if (alignment == 8) {
+ assert(test_string.capacity() == expected_align8_size);
+ } else {
+ assert(test_string.capacity() == expected_align8_size + 8);
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp
new file mode 100644
index 000000000000..6bfcb5d4bfcd
--- /dev/null
+++ b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp
@@ -0,0 +1,120 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <string>
+
+// This test ensures that the correct max_size() is returned depending on the platform.
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <string>
+
+#include "test_macros.h"
+
+// alignment of the string heap buffer is hardcoded to 8
+static const std::size_t alignment = 8;
+
+template <class = int>
+TEST_CONSTEXPR_CXX20 void full_size() {
+ std::string str;
+ assert(str.max_size() == std::numeric_limits<std::size_t>::max() - alignment - 1);
+
+#ifndef TEST_HAS_NO_CHAR8_T
+ std::u8string u8str;
+ assert(u8str.max_size() == std::numeric_limits<std::size_t>::max() - alignment - 1);
+#endif
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ std::wstring wstr;
+ assert(wstr.max_size() ==
+ ((std::numeric_limits<std::size_t>::max() / sizeof(wchar_t) - alignment) & ~std::size_t(1)) - 1);
+#endif
+
+ std::u16string u16str;
+ std::u32string u32str;
+ assert(u16str.max_size() == ((std::numeric_limits<std::size_t>::max() / 2 - alignment) & ~std::size_t(1)) - 1);
+ assert(u32str.max_size() == ((std::numeric_limits<std::size_t>::max() / 4 - alignment) & ~std::size_t(1)) - 1);
+}
+
+template <class = int>
+TEST_CONSTEXPR_CXX20 void half_size() {
+ std::string str;
+ assert(str.max_size() == std::numeric_limits<std::size_t>::max() / 2 - alignment - 1);
+
+#ifndef TEST_HAS_NO_CHAR8_T
+ std::u8string u8str;
+ assert(u8str.max_size() == std::numeric_limits<std::size_t>::max() / 2 - alignment - 1);
+#endif
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ std::wstring wstr;
+ assert(wstr.max_size() ==
+ std::numeric_limits<std::size_t>::max() / std::max<size_t>(2ul, sizeof(wchar_t)) - alignment - 1);
+#endif
+
+ std::u16string u16str;
+ std::u32string u32str;
+ assert(u16str.max_size() == std::numeric_limits<std::size_t>::max() / 2 - alignment - 1);
+ assert(u32str.max_size() == std::numeric_limits<std::size_t>::max() / 4 - alignment - 1);
+}
+
+TEST_CONSTEXPR_CXX20 bool test() {
+#if _LIBCPP_ABI_VERSION == 1
+
+# if defined(__x86_64__) || defined(__i386__)
+ full_size();
+# elif defined(__APPLE__) && defined(__aarch64__)
+ half_size();
+# elif defined(__arm__) || defined(__aarch64__)
+# ifdef __BIG_ENDIAN__
+ half_size();
+# else
+ full_size();
+# endif
+# elif defined(__powerpc__) || defined(__powerpc64__)
+# ifdef __BIG_ENDIAN__
+ half_size();
+# else
+ full_size();
+# endif
+# elif defined(__sparc64__)
+ half_size();
+# elif defined(__riscv)
+ full_size();
+# elif defined(_WIN32)
+ full_size();
+# else
+# error "Your target system seems to be unsupported."
+# endif
+
+#else
+
+# if defined(__arm__) || defined(__aarch64__)
+# ifdef __BIG_ENDIAN__
+ full_size();
+# else
+ half_size();
+# endif
+# else
+ half_size();
+# endif
+
+#endif
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+#if TEST_STD_VER > 17
+ static_assert(test());
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx-03/strings/basic.string/string.cons/copy_shrunk_long.pass.cpp b/libcxx/test/libcxx-03/strings/basic.string/string.cons/copy_shrunk_long.pass.cpp
new file mode 100644
index 000000000000..d4a0b318f36d
--- /dev/null
+++ b/libcxx/test/libcxx-03/strings/basic.string/string.cons/copy_shrunk_long.pass.cpp
@@ -0,0 +1,45 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <string>
+
+// basic_string(const basic_string<charT,traits,Allocator>& str);
+
+#include <string>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_allocator.h"
+#include "min_allocator.h"
+
+template <class S>
+TEST_CONSTEXPR_CXX20 bool test() {
+ // Tests that a long string holding a SSO size string results in
+ // an SSO copy constructed value.
+ S s1("1234567890123456789012345678901234567890123456789012345678901234567890");
+ s1.resize(7);
+ S s2(s1);
+ LIBCPP_ASSERT(s2.__invariants());
+ assert(s2 == s1);
+ assert(s2.capacity() < sizeof(S));
+
+ return true;
+}
+
+int main(int, char**) {
+ test<std::basic_string<char, std::char_traits<char>, test_allocator<char> > >();
+#if TEST_STD_VER >= 11
+ test<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+#endif
+#if TEST_STD_VER > 17
+ static_assert(test<std::basic_string<char, std::char_traits<char>, test_allocator<char>>>());
+ static_assert(test<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>());
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx-03/strings/basic.string/string.modifiers/resize_default_initialized.pass.cpp b/libcxx/test/libcxx-03/strings/basic.string/string.modifiers/resize_default_initialized.pass.cpp
new file mode 100644
index 000000000000..8e6e07d659c1
--- /dev/null
+++ b/libcxx/test/libcxx-03/strings/basic.string/string.modifiers/resize_default_initialized.pass.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <string>
+
+// __resize_default_init(size_type)
+
+#include <string>
+#include <cassert>
+
+#include "test_macros.h"
+
+TEST_CONSTEXPR_CXX20 void write_c_str(char* buf, int size) {
+ for (int i = 0; i < size; ++i) {
+ buf[i] = 'a';
+ }
+ buf[size] = '\0';
+}
+
+template <class S>
+TEST_CONSTEXPR_CXX20 void test_buffer_usage() {
+ {
+ unsigned buff_size = 125;
+ unsigned used_size = buff_size - 16;
+ S s;
+ s.__resize_default_init(buff_size);
+ write_c_str(&s[0], used_size);
+ assert(s.size() == buff_size);
+ assert(std::char_traits<char>().length(s.data()) == used_size);
+ s.__resize_default_init(used_size);
+ assert(s.size() == used_size);
+ assert(s.data()[used_size] == '\0');
+ for (unsigned i = 0; i < used_size; ++i) {
+ assert(s[i] == 'a');
+ }
+ }
+}
+
+template <class S>
+TEST_CONSTEXPR_CXX20 void test_basic() {
+ {
+ S s;
+ s.__resize_default_init(3);
+ assert(s.size() == 3);
+ assert(s.data()[3] == '\0');
+ for (int i = 0; i < 3; ++i)
+ s[i] = 'a' + i;
+ s.__resize_default_init(1);
+ assert(s[0] == 'a');
+ assert(s.data()[1] == '\0');
+ assert(s.size() == 1);
+ }
+}
+
+template <class S>
+TEST_CONSTEXPR_CXX20 bool test() {
+ test_basic<S>();
+ test_buffer_usage<S>();
+
+ return true;
+}
+
+int main(int, char**) {
+ test<std::string>();
+#if TEST_STD_VER > 17
+ static_assert(test<std::string>());
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx-03/strings/c.strings/constexpr_memmove.pass.cpp b/libcxx/test/libcxx-03/strings/c.strings/constexpr_memmove.pass.cpp
new file mode 100644
index 000000000000..d2ca5a265852
--- /dev/null
+++ b/libcxx/test/libcxx-03/strings/c.strings/constexpr_memmove.pass.cpp
@@ -0,0 +1,155 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// _Tp* __constexpr_memmove(_Tp* __dest, _Up* __src, __element_count __n);
+//
+// General tests for __constexpr_memmove.
+//
+// In particular, we try to ensure that __constexpr_memmove behaves like
+// __builtin_memmove as closely as possible. This means that it produces the
+// same effect, but also that it has the same type requirements.
+//
+// __builtin_memmove only requires that the types are TriviallyCopyable
+// (which is interestingly different from both is_trivially_XXX_constructible
+// and is_trivially_XXX_assignable), so we use some funky types to test these
+// corner cases.
+
+#include <__cxx03/__string/constexpr_c_functions.h>
+#include <cassert>
+#include <cstdint>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// The following types are all TriviallyCopyable, but they are not all
+// trivially_{copy,move}_{constructible,assignable}. TriviallyCopyable
+// guarantees that the type is *at least* one of the four, but no more
+// than that.
+struct CopyConstructible {
+ CopyConstructible() = default;
+ int value = 0;
+
+ CopyConstructible(const CopyConstructible&) = default;
+ CopyConstructible(CopyConstructible&&) = delete;
+ CopyConstructible& operator=(const CopyConstructible&) = delete;
+ CopyConstructible& operator=(CopyConstructible&&) = delete;
+};
+
+struct MoveConstructible {
+ MoveConstructible() = default;
+ int value = 0;
+
+ MoveConstructible(const MoveConstructible&) = delete;
+ MoveConstructible(MoveConstructible&&) = default;
+ MoveConstructible& operator=(const MoveConstructible&) = delete;
+ MoveConstructible& operator=(MoveConstructible&&) = delete;
+};
+
+struct CopyAssignable {
+ CopyAssignable() = default;
+ int value = 0;
+
+ CopyAssignable(const CopyAssignable&) = delete;
+ CopyAssignable(CopyAssignable&&) = delete;
+ CopyAssignable& operator=(const CopyAssignable&) = default;
+ CopyAssignable& operator=(CopyAssignable&&) = delete;
+};
+
+struct MoveAssignable {
+ MoveAssignable() = default;
+ int value = 0;
+
+ MoveAssignable(const MoveAssignable&) = delete;
+ MoveAssignable(MoveAssignable&&) = delete;
+ MoveAssignable& operator=(const MoveAssignable&) = delete;
+ MoveAssignable& operator=(MoveAssignable&&) = default;
+};
+
+template <class Source, class Dest>
+TEST_CONSTEXPR_CXX14 void test_user_defined_types() {
+ static_assert(std::is_trivially_copyable<Source>::value, "test the test");
+ static_assert(std::is_trivially_copyable<Dest>::value, "test the test");
+
+ // Note that we can't just initialize with an initializer list since some of the types we use here
+ // are not copy-constructible, which is required in pre-C++20 Standards for that syntax to work.
+ Source src[3];
+ src[0].value = 1;
+ src[1].value = 2;
+ src[2].value = 3;
+ Dest dst[3];
+ dst[0].value = 111;
+ dst[1].value = 111;
+ dst[2].value = 111;
+
+ Dest* result = std::__constexpr_memmove(dst, src, std::__element_count(3));
+ assert(result == dst);
+ assert(dst[0].value == 1);
+ assert(dst[1].value == 2);
+ assert(dst[2].value == 3);
+}
+
+template <class Source, class Dest>
+TEST_CONSTEXPR_CXX14 void test_builtin_types() {
+ Source src[3] = {1, 2, 3};
+ Dest dst[3] = {111, 111, 111};
+
+ Dest* result = std::__constexpr_memmove(dst, src, std::__element_count(3));
+ assert(result == dst);
+ assert(dst[0] == 1);
+ assert(dst[1] == 2);
+ assert(dst[2] == 3);
+}
+
+template <class SourcePtr, class DestPtr, class ObjectType>
+TEST_CONSTEXPR_CXX14 void test_pointer_types() {
+ ObjectType objs[3] = {1, 2, 3};
+
+ SourcePtr src[3] = {objs + 0, objs + 1, objs + 2};
+ DestPtr dst[3] = {nullptr, nullptr, nullptr};
+
+ DestPtr* result = std::__constexpr_memmove(dst, src, std::__element_count(3));
+ assert(result == dst);
+ assert(dst[0] == objs + 0);
+ assert(dst[1] == objs + 1);
+ assert(dst[2] == objs + 2);
+}
+
+TEST_CONSTEXPR_CXX14 bool test() {
+ test_user_defined_types<CopyConstructible, CopyConstructible>();
+ test_user_defined_types<MoveConstructible, MoveConstructible>();
+ test_user_defined_types<CopyAssignable, CopyAssignable>();
+ test_user_defined_types<MoveAssignable, MoveAssignable>();
+
+ test_builtin_types<char, char>();
+ test_builtin_types<short, short>();
+ test_builtin_types<int, int>();
+ test_builtin_types<long, long>();
+ test_builtin_types<long long, long long>();
+
+ // Cross-type
+ test_builtin_types<std::int16_t, std::uint16_t>();
+ test_builtin_types<std::int16_t, char16_t>();
+ test_builtin_types<std::int32_t, std::uint32_t>();
+ test_builtin_types<std::int32_t, char32_t>();
+
+ test_pointer_types<char*, char*, char>();
+ test_pointer_types<int*, int*, int>();
+ test_pointer_types<long*, long*, long>();
+ test_pointer_types<void*, void*, int>();
+ test_pointer_types<int* const, int*, int>();
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+#if TEST_STD_VER >= 14
+ static_assert(test(), "");
+#endif
+ return 0;
+}
diff --git a/libcxx/test/libcxx-03/strings/string.view/string.view.iterators/assert.iterator-indexing.pass.cpp b/libcxx/test/libcxx-03/strings/string.view/string.view.iterators/assert.iterator-indexing.pass.cpp
new file mode 100644
index 000000000000..5043a88cbc3d
--- /dev/null
+++ b/libcxx/test/libcxx-03/strings/string.view/string.view.iterators/assert.iterator-indexing.pass.cpp
@@ -0,0 +1,158 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Make sure that std::string_view's iterators check for OOB accesses when the debug mode is enabled.
+
+// REQUIRES: has-unix-headers, libcpp-has-abi-bounded-iterators
+// UNSUPPORTED: libcpp-hardening-mode=none
+
+#include <iterator>
+#include <string_view>
+
+#include "check_assertion.h"
+
+template <typename Iter>
+void test_iterator(Iter begin, Iter end, bool reverse) {
+ ptrdiff_t distance = std::distance(begin, end);
+
+ // Dereferencing an iterator at the end.
+ {
+ TEST_LIBCPP_ASSERT_FAILURE(
+ *end,
+ reverse ? "__bounded_iter::operator--: Attempt to rewind an iterator past the start"
+ : "__bounded_iter::operator*: Attempt to dereference an iterator at the end");
+#if _LIBCPP_STD_VER >= 20
+ // In C++20 mode, std::reverse_iterator implements operator->, but not operator*, with
+ // std::prev instead of operator--. std::prev ultimately calls operator+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ end.operator->(),
+ reverse ? "__bounded_iter::operator+=: Attempt to rewind an iterator past the start"
+ : "__bounded_iter::operator->: Attempt to dereference an iterator at the end");
+#else
+ TEST_LIBCPP_ASSERT_FAILURE(
+ end.operator->(),
+ reverse ? "__bounded_iter::operator--: Attempt to rewind an iterator past the start"
+ : "__bounded_iter::operator->: Attempt to dereference an iterator at the end");
+#endif
+ }
+
+ // Incrementing an iterator past the end.
+ {
+ [[maybe_unused]] const char* msg =
+ reverse ? "__bounded_iter::operator--: Attempt to rewind an iterator past the start"
+ : "__bounded_iter::operator++: Attempt to advance an iterator past the end";
+ auto it = end;
+ TEST_LIBCPP_ASSERT_FAILURE(it++, msg);
+ it = end;
+ TEST_LIBCPP_ASSERT_FAILURE(++it, msg);
+ }
+
+ // Decrementing an iterator past the start.
+ {
+ [[maybe_unused]] const char* msg =
+ reverse ? "__bounded_iter::operator++: Attempt to advance an iterator past the end"
+ : "__bounded_iter::operator--: Attempt to rewind an iterator past the start";
+ auto it = begin;
+ TEST_LIBCPP_ASSERT_FAILURE(it--, msg);
+ it = begin;
+ TEST_LIBCPP_ASSERT_FAILURE(--it, msg);
+ }
+
+ // Advancing past the end with operator+= and operator+.
+ {
+ [[maybe_unused]] const char* msg =
+ reverse ? "__bounded_iter::operator-=: Attempt to rewind an iterator past the start"
+ : "__bounded_iter::operator+=: Attempt to advance an iterator past the end";
+ auto it = end;
+ TEST_LIBCPP_ASSERT_FAILURE(it += 1, msg);
+ TEST_LIBCPP_ASSERT_FAILURE(end + 1, msg);
+ it = begin;
+ TEST_LIBCPP_ASSERT_FAILURE(it += (distance + 1), msg);
+ TEST_LIBCPP_ASSERT_FAILURE(begin + (distance + 1), msg);
+ }
+
+ // Advancing past the end with operator-= and operator-.
+ {
+ [[maybe_unused]] const char* msg =
+ reverse ? "__bounded_iter::operator+=: Attempt to rewind an iterator past the start"
+ : "__bounded_iter::operator-=: Attempt to advance an iterator past the end";
+ auto it = end;
+ TEST_LIBCPP_ASSERT_FAILURE(it -= (-1), msg);
+ TEST_LIBCPP_ASSERT_FAILURE(end - (-1), msg);
+ it = begin;
+ TEST_LIBCPP_ASSERT_FAILURE(it -= (-distance - 1), msg);
+ TEST_LIBCPP_ASSERT_FAILURE(begin - (-distance - 1), msg);
+ }
+
+ // Rewinding past the start with operator+= and operator+.
+ {
+ [[maybe_unused]] const char* msg =
+ reverse ? "__bounded_iter::operator-=: Attempt to advance an iterator past the end"
+ : "__bounded_iter::operator+=: Attempt to rewind an iterator past the start";
+ auto it = begin;
+ TEST_LIBCPP_ASSERT_FAILURE(it += (-1), msg);
+ TEST_LIBCPP_ASSERT_FAILURE(begin + (-1), msg);
+ it = end;
+ TEST_LIBCPP_ASSERT_FAILURE(it += (-distance - 1), msg);
+ TEST_LIBCPP_ASSERT_FAILURE(end + (-distance - 1), msg);
+ }
+
+ // Rewinding past the start with operator-= and operator-.
+ {
+ [[maybe_unused]] const char* msg =
+ reverse ? "__bounded_iter::operator+=: Attempt to advance an iterator past the end"
+ : "__bounded_iter::operator-=: Attempt to rewind an iterator past the start";
+ auto it = begin;
+ TEST_LIBCPP_ASSERT_FAILURE(it -= 1, msg);
+ TEST_LIBCPP_ASSERT_FAILURE(begin - 1, msg);
+ it = end;
+ TEST_LIBCPP_ASSERT_FAILURE(it -= (distance + 1), msg);
+ TEST_LIBCPP_ASSERT_FAILURE(end - (distance + 1), msg);
+ }
+
+ // Out-of-bounds operator[].
+ {
+ [[maybe_unused]] const char* end_msg =
+ reverse ? "__bounded_iter::operator--: Attempt to rewind an iterator past the start"
+ : "__bounded_iter::operator[]: Attempt to index an iterator at or past the end";
+ [[maybe_unused]] const char* past_end_msg =
+ reverse ? "__bounded_iter::operator-=: Attempt to rewind an iterator past the start"
+ : "__bounded_iter::operator[]: Attempt to index an iterator at or past the end";
+ [[maybe_unused]] const char* past_start_msg =
+ reverse ? "__bounded_iter::operator-=: Attempt to advance an iterator past the end"
+ : "__bounded_iter::operator[]: Attempt to index an iterator past the start";
+ TEST_LIBCPP_ASSERT_FAILURE(begin[distance], end_msg);
+ TEST_LIBCPP_ASSERT_FAILURE(begin[distance + 1], past_end_msg);
+ TEST_LIBCPP_ASSERT_FAILURE(begin[-1], past_start_msg);
+ TEST_LIBCPP_ASSERT_FAILURE(begin[-99], past_start_msg);
+
+ auto it = begin + 1;
+ TEST_LIBCPP_ASSERT_FAILURE(it[distance - 1], end_msg);
+ TEST_LIBCPP_ASSERT_FAILURE(it[distance], past_end_msg);
+ TEST_LIBCPP_ASSERT_FAILURE(it[-2], past_start_msg);
+ TEST_LIBCPP_ASSERT_FAILURE(it[-99], past_start_msg);
+ }
+}
+
+int main(int, char**) {
+ std::string_view const str("hello world");
+
+ // string_view::iterator
+ test_iterator(str.begin(), str.end(), /*reverse=*/false);
+
+ // string_view::const_iterator
+ test_iterator(str.cbegin(), str.cend(), /*reverse=*/false);
+
+ // string_view::reverse_iterator
+ test_iterator(str.rbegin(), str.rend(), /*reverse=*/true);
+
+ // string_view::const_reverse_iterator
+ test_iterator(str.crbegin(), str.crend(), /*reverse=*/true);
+
+ return 0;
+}