From 36931253206931cafd66165c415fb47b2621012b Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Sat, 27 Apr 2024 19:11:08 +0800 Subject: [PATCH] Make `` slimmer by default The new header `<__msvc_string_view.hpp>` provides `char_traits` and `basic_string_view` and related utilities. `` shouldn't provide other utilities in ``. `` seemingly still need to include `` in C++23 due to `elements_of` whose default template argument depends on `allocator`. Escape hatch `_LEGACY_CODE_ASSUMES_STRING_VIEW_INCLUDES_XSTRING` is added to restore legacy inclusion. MSVC-internal changes are needed due to the new internal header. --- stl/CMakeLists.txt | 1 + stl/inc/__msvc_string_view.hpp | 1819 ++++++++++++++++++++++++++++++++ stl/inc/header-units.json | 1 + stl/inc/ranges | 1 + stl/inc/string_view | 5 + stl/inc/xstring | 1792 +------------------------------ 6 files changed, 1828 insertions(+), 1791 deletions(-) create mode 100644 stl/inc/__msvc_string_view.hpp diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index 81f9fd6525d..60bfcbd83df 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -18,6 +18,7 @@ set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_minmax.hpp ${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_print.hpp ${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_sanitizer_annotate_container.hpp + ${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_string_view.hpp ${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_system_error_abi.hpp ${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_threads_core.hpp ${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_tzdb.hpp diff --git a/stl/inc/__msvc_string_view.hpp b/stl/inc/__msvc_string_view.hpp new file mode 100644 index 00000000000..52daaedbaf5 --- /dev/null +++ b/stl/inc/__msvc_string_view.hpp @@ -0,0 +1,1819 @@ +// __msvc_string_view.hpp internal header + +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// ( without emitting non-C++17 warnings) + +#ifndef __MSVC_STRING_VIEW_HPP +#define __MSVC_STRING_VIEW_HPP +#include +#if _STL_COMPILER_PREPROCESSOR +#include +#include + +#pragma pack(push, _CRT_PACKING) +#pragma warning(push, _STL_WARNING_LEVEL) +#pragma warning(disable : _STL_DISABLED_WARNINGS) +_STL_DISABLE_CLANG_WARNINGS +#pragma push_macro("new") +#undef new + +_STD_BEGIN +#ifdef __clang__ +#define _HAS_MEMCPY_MEMMOVE_INTRINSICS 1 +#else // ^^^ use __builtin_memcpy and __builtin_memmove / use workaround vvv +#define _HAS_MEMCPY_MEMMOVE_INTRINSICS 0 // TRANSITION, DevCom-1046483 (MSVC) and VSO-1129974 (EDG) +#endif // ^^^ use workaround ^^^ + +template +struct _Char_traits { // properties of a string or stream element + using char_type = _Elem; + using int_type = _Int_type; + using pos_type = streampos; + using off_type = streamoff; + using state_type = _Mbstatet; +#if _HAS_CXX20 + using comparison_category = strong_ordering; +#endif // _HAS_CXX20 + + // For copy/move, we can uniformly call memcpy/memmove (or their builtin versions) for all element types. + + static _CONSTEXPR20 _Elem* copy(_Out_writes_all_(_Count) _Elem* const _First1, + _In_reads_(_Count) const _Elem* const _First2, const size_t _Count) noexcept /* strengthened */ { + // copy [_First2, _First2 + _Count) to [_First1, ...) +#if _HAS_MEMCPY_MEMMOVE_INTRINSICS + __builtin_memcpy(_First1, _First2, _Count * sizeof(_Elem)); +#else // ^^^ _HAS_MEMCPY_MEMMOVE_INTRINSICS / !_HAS_MEMCPY_MEMMOVE_INTRINSICS vvv +#if _HAS_CXX20 + if (_STD is_constant_evaluated()) { + // pre: [_First1, _First1 + _Count) and [_First2, _First2 + _Count) do not overlap + for (size_t _Idx = 0; _Idx != _Count; ++_Idx) { + _First1[_Idx] = _First2[_Idx]; + } + + return _First1; + } +#endif // _HAS_CXX20 + + _CSTD memcpy(_First1, _First2, _Count * sizeof(_Elem)); +#endif // ^^^ !_HAS_MEMCPY_MEMMOVE_INTRINSICS ^^^ + + return _First1; + } + + _Pre_satisfies_(_Dest_size >= _Count) static _CONSTEXPR20 _Elem* _Copy_s(_Out_writes_all_(_Dest_size) + _Elem* const _First1, + const size_t _Dest_size, _In_reads_(_Count) const _Elem* const _First2, const size_t _Count) noexcept { + // copy [_First2, _First2 + _Count) to [_First1, _First1 + _Dest_size) + _STL_VERIFY(_Count <= _Dest_size, "invalid argument"); + return copy(_First1, _First2, _Count); + } + + static _CONSTEXPR20 _Elem* move(_Out_writes_all_(_Count) _Elem* const _First1, + _In_reads_(_Count) const _Elem* const _First2, const size_t _Count) noexcept /* strengthened */ { + // copy [_First2, _First2 + _Count) to [_First1, ...), allowing overlap +#if _HAS_MEMCPY_MEMMOVE_INTRINSICS + __builtin_memmove(_First1, _First2, _Count * sizeof(_Elem)); +#else // ^^^ _HAS_MEMCPY_MEMMOVE_INTRINSICS / !_HAS_MEMCPY_MEMMOVE_INTRINSICS vvv +#if _HAS_CXX20 + if (_STD is_constant_evaluated()) { + // dest: [_First1, _First1 + _Count) + // src: [_First2, _First2 + _Count) + // We need to handle overlapping ranges. + // If _First1 is in the src range, we need a backward loop. + // Otherwise, the forward loop works (even if the back of dest overlaps the front of src). + + // Usually, we would compare pointers with less-than, even though they could belong to different arrays. + // However, we're not allowed to do that during constant evaluation, so we need a linear scan for equality. + bool _Loop_forward = true; + + for (const _Elem* _Src = _First2; _Src != _First2 + _Count; ++_Src) { + if (_First1 == _Src) { + _Loop_forward = false; + break; + } + } + + if (_Loop_forward) { + for (size_t _Idx = 0; _Idx != _Count; ++_Idx) { + _First1[_Idx] = _First2[_Idx]; + } + } else { + for (size_t _Idx = _Count; _Idx != 0; --_Idx) { + _First1[_Idx - 1] = _First2[_Idx - 1]; + } + } + + return _First1; + } +#endif // _HAS_CXX20 + + _CSTD memmove(_First1, _First2, _Count * sizeof(_Elem)); +#endif // ^^^ !_HAS_MEMCPY_MEMMOVE_INTRINSICS ^^^ + + return _First1; + } + + // For compare/length/find/assign, we can't uniformly call CRT functions (or their builtin versions). + // 8-bit: memcmp/strlen/memchr/memset; 16-bit: wmemcmp/wcslen/wmemchr/wmemset; 32-bit: nonexistent + + _NODISCARD static _CONSTEXPR17 int compare(_In_reads_(_Count) const _Elem* _First1, + _In_reads_(_Count) const _Elem* _First2, size_t _Count) noexcept /* strengthened */ { + // compare [_First1, _First1 + _Count) with [_First2, ...) + for (; 0 < _Count; --_Count, ++_First1, ++_First2) { + if (*_First1 != *_First2) { + return *_First1 < *_First2 ? -1 : +1; + } + } + + return 0; + } + + _NODISCARD static _CONSTEXPR17 size_t length(_In_z_ const _Elem* _First) noexcept /* strengthened */ { + // find length of null-terminated sequence + size_t _Count = 0; + while (*_First != _Elem()) { + ++_Count; + ++_First; + } + + return _Count; + } + + _NODISCARD static _CONSTEXPR17 const _Elem* find( + _In_reads_(_Count) const _Elem* _First, size_t _Count, const _Elem& _Ch) noexcept /* strengthened */ { + // look for _Ch in [_First, _First + _Count) + for (; 0 < _Count; --_Count, ++_First) { + if (*_First == _Ch) { + return _First; + } + } + + return nullptr; + } + + static _CONSTEXPR20 _Elem* assign( + _Out_writes_all_(_Count) _Elem* const _First, size_t _Count, const _Elem _Ch) noexcept /* strengthened */ { + // assign _Count * _Ch to [_First, ...) + for (_Elem* _Next = _First; _Count > 0; --_Count, ++_Next) { + *_Next = _Ch; + } + + return _First; + } + + static _CONSTEXPR17 void assign(_Elem& _Left, const _Elem& _Right) noexcept { + _Left = _Right; + } + + _NODISCARD static constexpr bool eq(const _Elem _Left, const _Elem _Right) noexcept { + return _Left == _Right; + } + + _NODISCARD static constexpr bool lt(const _Elem _Left, const _Elem _Right) noexcept { + return _Left < _Right; + } + + _NODISCARD static constexpr _Elem to_char_type(const int_type _Meta) noexcept { + return static_cast<_Elem>(_Meta); + } + + _NODISCARD static constexpr int_type to_int_type(const _Elem _Ch) noexcept { + return static_cast(_Ch); + } + + _NODISCARD static constexpr bool eq_int_type(const int_type _Left, const int_type _Right) noexcept { + return _Left == _Right; + } + + _NODISCARD static constexpr int_type not_eof(const int_type _Meta) noexcept { + return _Meta != eof() ? _Meta : !eof(); + } + + _NODISCARD static constexpr int_type eof() noexcept { + return static_cast(EOF); + } +}; + +template +struct _WChar_traits : private _Char_traits<_Elem, unsigned short> { + // char_traits for the char16_t-likes: char16_t, wchar_t, unsigned short +private: + using _Primary_char_traits = _Char_traits<_Elem, unsigned short>; + +public: + using char_type = _Elem; + using int_type = unsigned short; + using pos_type = streampos; + using off_type = streamoff; + using state_type = mbstate_t; +#if _HAS_CXX20 + using comparison_category = strong_ordering; +#endif // _HAS_CXX20 + + using _Primary_char_traits::_Copy_s; + using _Primary_char_traits::copy; + using _Primary_char_traits::move; + + _NODISCARD static _CONSTEXPR17 int compare(_In_reads_(_Count) const _Elem* const _First1, + _In_reads_(_Count) const _Elem* const _First2, const size_t _Count) noexcept /* strengthened */ { + // compare [_First1, _First1 + _Count) with [_First2, ...) +#if _HAS_CXX17 + if constexpr (is_same_v<_Elem, wchar_t>) { + return __builtin_wmemcmp(_First1, _First2, _Count); + } else { + return _Primary_char_traits::compare(_First1, _First2, _Count); + } +#else // ^^^ _HAS_CXX17 / !_HAS_CXX17 vvv + return _CSTD wmemcmp( + reinterpret_cast(_First1), reinterpret_cast(_First2), _Count); +#endif // ^^^ !_HAS_CXX17 ^^^ + } + + _NODISCARD static _CONSTEXPR17 size_t length(_In_z_ const _Elem* _First) noexcept /* strengthened */ { + // find length of null-terminated sequence +#if _HAS_CXX17 + if constexpr (is_same_v<_Elem, wchar_t>) { + return __builtin_wcslen(_First); + } else { + return _Primary_char_traits::length(_First); + } +#else // ^^^ _HAS_CXX17 / !_HAS_CXX17 vvv + return _CSTD wcslen(reinterpret_cast(_First)); +#endif // ^^^ !_HAS_CXX17 ^^^ + } + + _NODISCARD static _CONSTEXPR17 const _Elem* find( + _In_reads_(_Count) const _Elem* _First, const size_t _Count, const _Elem& _Ch) noexcept /* strengthened */ { + // look for _Ch in [_First, _First + _Count) +#if _HAS_CXX17 + if constexpr (is_same_v<_Elem, wchar_t>) { + return __builtin_wmemchr(_First, _Ch, _Count); + } else { + return _Primary_char_traits::find(_First, _Count, _Ch); + } +#else // ^^^ _HAS_CXX17 / !_HAS_CXX17 vvv + return reinterpret_cast(_CSTD wmemchr(reinterpret_cast(_First), _Ch, _Count)); +#endif // ^^^ !_HAS_CXX17 ^^^ + } + + static _CONSTEXPR20 _Elem* assign( + _Out_writes_all_(_Count) _Elem* const _First, size_t _Count, const _Elem _Ch) noexcept /* strengthened */ { + // assign _Count * _Ch to [_First, ...) +#if _HAS_CXX20 + if (_STD is_constant_evaluated()) { + return _Primary_char_traits::assign(_First, _Count, _Ch); + } +#endif // _HAS_CXX20 + + return reinterpret_cast<_Elem*>(_CSTD wmemset(reinterpret_cast(_First), _Ch, _Count)); + } + + static _CONSTEXPR17 void assign(_Elem& _Left, const _Elem& _Right) noexcept { +#if _HAS_CXX20 + if (_STD is_constant_evaluated()) { + return _Primary_char_traits::assign(_Left, _Right); + } +#endif // _HAS_CXX20 + _Left = _Right; + } + + _NODISCARD static constexpr bool eq(const _Elem _Left, const _Elem _Right) noexcept { + return _Left == _Right; + } + + _NODISCARD static constexpr bool lt(const _Elem _Left, const _Elem _Right) noexcept { + return _Left < _Right; + } + + _NODISCARD static constexpr _Elem to_char_type(const int_type _Meta) noexcept { + return _Meta; + } + + _NODISCARD static constexpr int_type to_int_type(const _Elem _Ch) noexcept { + return _Ch; + } + + _NODISCARD static constexpr bool eq_int_type(const int_type _Left, const int_type _Right) noexcept { + return _Left == _Right; + } + + _NODISCARD static constexpr int_type not_eof(const int_type _Meta) noexcept { + return _Meta != eof() ? _Meta : static_cast(!eof()); + } + + _NODISCARD static constexpr int_type eof() noexcept { + return WEOF; + } +}; + +_EXPORT_STD template +struct char_traits : _Char_traits<_Elem, long> {}; // properties of a string or stream unknown element + +template <> +struct char_traits : _WChar_traits {}; + +template <> +struct char_traits : _Char_traits {}; + +template <> +struct char_traits : _WChar_traits {}; + +#ifdef _CRTBLD +template <> +struct char_traits : _WChar_traits {}; +#endif // defined(_CRTBLD) + +#if defined(__cpp_char8_t) && !defined(__clang__) && !defined(__EDG__) +#define _HAS_U8_INTRINSICS 1 +#else // ^^^ Use intrinsics for char8_t / don't use said intrinsics vvv +#define _HAS_U8_INTRINSICS 0 +#endif // Detect u8 intrinsics + +template +struct _Narrow_char_traits : private _Char_traits<_Elem, _Int_type> { + // Implement char_traits for narrow character types char and char8_t +private: + using _Primary_char_traits = _Char_traits<_Elem, _Int_type>; + +public: + using char_type = _Elem; + using int_type = _Int_type; + using pos_type = streampos; + using off_type = streamoff; + using state_type = mbstate_t; +#if _HAS_CXX20 + using comparison_category = strong_ordering; +#endif // _HAS_CXX20 + + using _Primary_char_traits::_Copy_s; + using _Primary_char_traits::copy; + using _Primary_char_traits::move; + + _NODISCARD static _CONSTEXPR17 int compare(_In_reads_(_Count) const _Elem* const _First1, + _In_reads_(_Count) const _Elem* const _First2, const size_t _Count) noexcept /* strengthened */ { + // compare [_First1, _First1 + _Count) with [_First2, ...) +#if _HAS_CXX17 + return __builtin_memcmp(_First1, _First2, _Count); +#else // ^^^ _HAS_CXX17 / !_HAS_CXX17 vvv + return _CSTD memcmp(_First1, _First2, _Count); +#endif // ^^^ !_HAS_CXX17 ^^^ + } + + _NODISCARD static _CONSTEXPR17 size_t length(_In_z_ const _Elem* const _First) noexcept /* strengthened */ { + // find length of null-terminated string +#if _HAS_CXX17 +#ifdef __cpp_char8_t + if constexpr (is_same_v<_Elem, char8_t>) { +#if _HAS_U8_INTRINSICS + return __builtin_u8strlen(_First); +#else // ^^^ use u8 intrinsics / no u8 intrinsics vvv + return _Primary_char_traits::length(_First); +#endif // ^^^ no u8 intrinsics ^^^ + } else +#endif // defined(__cpp_char8_t) + { + return __builtin_strlen(_First); + } +#else // ^^^ _HAS_CXX17 / !_HAS_CXX17 vvv + return _CSTD strlen(reinterpret_cast(_First)); +#endif // ^^^ !_HAS_CXX17 ^^^ + } + + _NODISCARD static _CONSTEXPR17 const _Elem* find(_In_reads_(_Count) const _Elem* const _First, const size_t _Count, + const _Elem& _Ch) noexcept /* strengthened */ { + // look for _Ch in [_First, _First + _Count) +#if _HAS_CXX17 +#ifdef __cpp_char8_t + if constexpr (is_same_v<_Elem, char8_t>) { +#if _HAS_U8_INTRINSICS + return __builtin_u8memchr(_First, _Ch, _Count); +#else // ^^^ use u8 intrinsics / no u8 intrinsics vvv + return _Primary_char_traits::find(_First, _Count, _Ch); +#endif // ^^^ no u8 intrinsics ^^^ + } else +#endif // defined(__cpp_char8_t) + { + return __builtin_char_memchr(_First, _Ch, _Count); + } +#else // ^^^ _HAS_CXX17 / !_HAS_CXX17 vvv + return static_cast(_CSTD memchr(_First, _Ch, _Count)); +#endif // ^^^ !_HAS_CXX17 ^^^ + } + + static _CONSTEXPR20 _Elem* assign( + _Out_writes_all_(_Count) _Elem* const _First, size_t _Count, const _Elem _Ch) noexcept /* strengthened */ { + // assign _Count * _Ch to [_First, ...) +#if _HAS_CXX20 + if (_STD is_constant_evaluated()) { + return _Primary_char_traits::assign(_First, _Count, _Ch); + } +#endif // _HAS_CXX20 + + return static_cast<_Elem*>(_CSTD memset(_First, _Ch, _Count)); + } + + static _CONSTEXPR17 void assign(_Elem& _Left, const _Elem& _Right) noexcept { +#if _HAS_CXX20 + if (_STD is_constant_evaluated()) { + return _Primary_char_traits::assign(_Left, _Right); + } +#endif // _HAS_CXX20 + _Left = _Right; + } + + _NODISCARD static constexpr bool eq(const _Elem _Left, const _Elem _Right) noexcept { + return _Left == _Right; + } + + _NODISCARD static constexpr bool lt(const _Elem _Left, const _Elem _Right) noexcept { + return static_cast(_Left) < static_cast(_Right); + } + + _NODISCARD static constexpr _Elem to_char_type(const int_type _Meta) noexcept { + return static_cast<_Elem>(_Meta); + } + + _NODISCARD static constexpr int_type to_int_type(const _Elem _Ch) noexcept { + return static_cast(_Ch); + } + + _NODISCARD static constexpr bool eq_int_type(const int_type _Left, const int_type _Right) noexcept { + return _Left == _Right; + } + + _NODISCARD static constexpr int_type not_eof(const int_type _Meta) noexcept { + return _Meta != eof() ? _Meta : !eof(); + } + + _NODISCARD static constexpr int_type eof() noexcept { + return static_cast(EOF); + } +}; + +#undef _HAS_U8_INTRINSICS +#undef _HAS_MEMCPY_MEMMOVE_INTRINSICS + +template <> +struct char_traits : _Narrow_char_traits {}; // properties of a string or stream char element + +#ifdef __cpp_char8_t +template <> +struct char_traits : _Narrow_char_traits {}; +#endif // defined(__cpp_char8_t) + +template +basic_ostream<_Elem, _Traits>& _Insert_string( + basic_ostream<_Elem, _Traits>& _Ostr, const _Elem* const _Data, const _SizeT _Size) { + // insert a character-type sequence into _Ostr as if through a basic_string copy + using _Ostr_t = basic_ostream<_Elem, _Traits>; + typename _Ostr_t::iostate _State = _Ostr_t::goodbit; + + _SizeT _Pad; + if (_Ostr.width() <= 0 || static_cast<_SizeT>(_Ostr.width()) <= _Size) { + _Pad = 0; + } else { + _Pad = static_cast<_SizeT>(_Ostr.width()) - _Size; + } + + const typename _Ostr_t::sentry _Ok(_Ostr); + + if (!_Ok) { + _State |= _Ostr_t::badbit; + } else { // state okay, insert characters + _TRY_IO_BEGIN + if ((_Ostr.flags() & _Ostr_t::adjustfield) != _Ostr_t::left) { + for (; 0 < _Pad; --_Pad) { // pad on left + if (_Traits::eq_int_type(_Traits::eof(), _Ostr.rdbuf()->sputc(_Ostr.fill()))) { + _State |= _Ostr_t::badbit; // insertion failed, quit + break; + } + } + } + + if (_State == _Ostr_t::goodbit + && _Ostr.rdbuf()->sputn(_Data, static_cast(_Size)) != static_cast(_Size)) { + _State |= _Ostr_t::badbit; + } else { + for (; 0 < _Pad; --_Pad) { // pad on right + if (_Traits::eq_int_type(_Traits::eof(), _Ostr.rdbuf()->sputc(_Ostr.fill()))) { + _State |= _Ostr_t::badbit; // insertion failed, quit + break; + } + } + } + + _Ostr.width(0); + _CATCH_IO_(_Ostr_t, _Ostr) + } + + _Ostr.setstate(_State); + return _Ostr; +} + +template +using _Traits_ch_t = typename _Traits::char_type; + +template +using _Traits_ptr_t = const typename _Traits::char_type*; + +template +constexpr bool _Traits_equal(_In_reads_(_Left_size) const _Traits_ptr_t<_Traits> _Left, const size_t _Left_size, + _In_reads_(_Right_size) const _Traits_ptr_t<_Traits> _Right, const size_t _Right_size) noexcept { + // compare [_Left, _Left + _Left_size) to [_Right, _Right + _Right_size) for equality using _Traits + return _Left_size == _Right_size && _Traits::compare(_Left, _Right, _Left_size) == 0; +} + +template +constexpr int _Traits_compare(_In_reads_(_Left_size) const _Traits_ptr_t<_Traits> _Left, const size_t _Left_size, + _In_reads_(_Right_size) const _Traits_ptr_t<_Traits> _Right, const size_t _Right_size) noexcept { + // compare [_Left, _Left + _Left_size) to [_Right, _Right + _Right_size) using _Traits + const int _Ans = _Traits::compare(_Left, _Right, (_STD min)(_Left_size, _Right_size)); + + if (_Ans != 0) { + return _Ans; + } + + if (_Left_size < _Right_size) { + return -1; + } + + if (_Left_size > _Right_size) { + return 1; + } + + return 0; +} + +template +constexpr size_t _Traits_find(_In_reads_(_Hay_size) const _Traits_ptr_t<_Traits> _Haystack, const size_t _Hay_size, + const size_t _Start_at, _In_reads_(_Needle_size) const _Traits_ptr_t<_Traits> _Needle, + const size_t _Needle_size) noexcept { + // search [_Haystack, _Haystack + _Hay_size) for [_Needle, _Needle + _Needle_size), at/after _Start_at + if (_Needle_size > _Hay_size || _Start_at > _Hay_size - _Needle_size) { + // xpos cannot exist, report failure + // N4950 [string.view.find]/3 says: + // 1. _Start_at <= xpos + // 2. xpos + _Needle_size <= _Hay_size; + // therefore: + // 3. _Needle_size <= _Hay_size (by 2) (checked above) + // 4. _Start_at + _Needle_size <= _Hay_size (substitute 1 into 2) + // 5. _Start_at <= _Hay_size - _Needle_size (4, move _Needle_size to other side) (also checked above) + return static_cast(-1); + } + + if (_Needle_size == 0) { // empty string always matches if xpos is possible + return _Start_at; + } + + const auto _Possible_matches_end = _Haystack + (_Hay_size - _Needle_size) + 1; + for (auto _Match_try = _Haystack + _Start_at;; ++_Match_try) { + _Match_try = _Traits::find(_Match_try, static_cast(_Possible_matches_end - _Match_try), *_Needle); + if (!_Match_try) { // didn't find first character; report failure + return static_cast(-1); + } + + if (_Traits::compare(_Match_try, _Needle, _Needle_size) == 0) { // found match + return static_cast(_Match_try - _Haystack); + } + } +} + +template +constexpr size_t _Traits_find_ch(_In_reads_(_Hay_size) const _Traits_ptr_t<_Traits> _Haystack, const size_t _Hay_size, + const size_t _Start_at, const _Traits_ch_t<_Traits> _Ch) noexcept { + // search [_Haystack, _Haystack + _Hay_size) for _Ch, at/after _Start_at + if (_Start_at < _Hay_size) { + const auto _Found_at = _Traits::find(_Haystack + _Start_at, _Hay_size - _Start_at, _Ch); + if (_Found_at) { + return static_cast(_Found_at - _Haystack); + } + } + + return static_cast(-1); // (npos) no match +} + +template +constexpr size_t _Traits_rfind(_In_reads_(_Hay_size) const _Traits_ptr_t<_Traits> _Haystack, const size_t _Hay_size, + const size_t _Start_at, _In_reads_(_Needle_size) const _Traits_ptr_t<_Traits> _Needle, + const size_t _Needle_size) noexcept { + // search [_Haystack, _Haystack + _Hay_size) for [_Needle, _Needle + _Needle_size) beginning before _Start_at + if (_Needle_size == 0) { + return (_STD min)(_Start_at, _Hay_size); // empty string always matches + } + + if (_Needle_size <= _Hay_size) { // room for match, look for it + for (auto _Match_try = _Haystack + (_STD min)(_Start_at, _Hay_size - _Needle_size);; --_Match_try) { + if (_Traits::eq(*_Match_try, *_Needle) && _Traits::compare(_Match_try, _Needle, _Needle_size) == 0) { + return static_cast(_Match_try - _Haystack); // found a match + } + + if (_Match_try == _Haystack) { + break; // at beginning, no more chance for match + } + } + } + + return static_cast(-1); // no match +} + +template +constexpr size_t _Traits_rfind_ch(_In_reads_(_Hay_size) const _Traits_ptr_t<_Traits> _Haystack, const size_t _Hay_size, + const size_t _Start_at, const _Traits_ch_t<_Traits> _Ch) noexcept { + // search [_Haystack, _Haystack + _Hay_size) for _Ch before _Start_at + if (_Hay_size != 0) { // room for match, look for it + for (auto _Match_try = _Haystack + (_STD min)(_Start_at, _Hay_size - 1);; --_Match_try) { + if (_Traits::eq(*_Match_try, _Ch)) { + return static_cast(_Match_try - _Haystack); // found a match + } + + if (_Match_try == _Haystack) { + break; // at beginning, no more chance for match + } + } + } + + return static_cast(-1); // no match +} + +template ::value> +class _String_bitmap { // _String_bitmap for character types +public: + constexpr bool _Mark(const _Elem* _First, const _Elem* const _Last) noexcept { + // mark this bitmap such that the characters in [_First, _Last) are intended to match + // returns whether all inputs can be placed in the bitmap + for (; _First != _Last; ++_First) { + _Matches[static_cast(*_First)] = true; + } + + return true; + } + + constexpr bool _Match(const _Elem _Ch) const noexcept { // test if _Ch is in the bitmap + return _Matches[static_cast(_Ch)]; + } + +private: + bool _Matches[256] = {}; +}; + +template +class _String_bitmap<_Elem, false> { // _String_bitmap for wchar_t/unsigned short/char16_t/char32_t/etc. types +public: + static_assert(is_unsigned_v<_Elem>, + "Standard char_traits is only provided for char, wchar_t, char16_t, and char32_t. See N4950 [char.traits]. " + "Visual C++ accepts other unsigned integral types as an extension."); + + constexpr bool _Mark(const _Elem* _First, const _Elem* const _Last) noexcept { + // mark this bitmap such that the characters in [_First, _Last) are intended to match + // returns whether all inputs can be placed in the bitmap + for (; _First != _Last; ++_First) { + const auto _Ch = *_First; + if (_Ch >= 256U) { + return false; + } + + _Matches[static_cast(_Ch)] = true; + } + + return true; + } + + constexpr bool _Match(const _Elem _Ch) const noexcept { // test if _Ch is in the bitmap + return _Ch < 256U && _Matches[_Ch]; + } + +private: + bool _Matches[256] = {}; +}; + +template > +constexpr size_t _Traits_find_first_of(_In_reads_(_Hay_size) const _Traits_ptr_t<_Traits> _Haystack, + const size_t _Hay_size, const size_t _Start_at, _In_reads_(_Needle_size) const _Traits_ptr_t<_Traits> _Needle, + const size_t _Needle_size) noexcept { + // in [_Haystack, _Haystack + _Hay_size), look for one of [_Needle, _Needle + _Needle_size), at/after _Start_at + if (_Needle_size != 0 && _Start_at < _Hay_size) { // room for match, look for it + if constexpr (_Special) { + _String_bitmap _Matches; + if (!_Matches._Mark(_Needle, _Needle + _Needle_size)) { // couldn't put one of the characters into the + // bitmap, fall back to the serial algorithm + return _Traits_find_first_of<_Traits, false>(_Haystack, _Hay_size, _Start_at, _Needle, _Needle_size); + } + + const auto _End = _Haystack + _Hay_size; + for (auto _Match_try = _Haystack + _Start_at; _Match_try < _End; ++_Match_try) { + if (_Matches._Match(*_Match_try)) { + return static_cast(_Match_try - _Haystack); // found a match + } + } + } else { + const auto _End = _Haystack + _Hay_size; + for (auto _Match_try = _Haystack + _Start_at; _Match_try < _End; ++_Match_try) { + if (_Traits::find(_Needle, _Needle_size, *_Match_try)) { + return static_cast(_Match_try - _Haystack); // found a match + } + } + } + } + + return static_cast(-1); // no match +} + +template > +constexpr size_t _Traits_find_last_of(_In_reads_(_Hay_size) const _Traits_ptr_t<_Traits> _Haystack, + const size_t _Hay_size, const size_t _Start_at, _In_reads_(_Needle_size) const _Traits_ptr_t<_Traits> _Needle, + const size_t _Needle_size) noexcept { + // in [_Haystack, _Haystack + _Hay_size), look for last of [_Needle, _Needle + _Needle_size), before _Start_at + if (_Needle_size != 0 && _Hay_size != 0) { // worth searching, do it + if constexpr (_Special) { + _String_bitmap _Matches; + if (!_Matches._Mark(_Needle, _Needle + _Needle_size)) { // couldn't put one of the characters into the + // bitmap, fall back to the serial algorithm + return _Traits_find_last_of<_Traits, false>(_Haystack, _Hay_size, _Start_at, _Needle, _Needle_size); + } + + for (auto _Match_try = _Haystack + (_STD min)(_Start_at, _Hay_size - 1);; --_Match_try) { + if (_Matches._Match(*_Match_try)) { + return static_cast(_Match_try - _Haystack); // found a match + } + + if (_Match_try == _Haystack) { + break; // at beginning, no more chance for match + } + } + } else { + for (auto _Match_try = _Haystack + (_STD min)(_Start_at, _Hay_size - 1);; --_Match_try) { + if (_Traits::find(_Needle, _Needle_size, *_Match_try)) { + return static_cast(_Match_try - _Haystack); // found a match + } + + if (_Match_try == _Haystack) { + break; // at beginning, no more chance for match + } + } + } + } + + return static_cast(-1); // no match +} + +template > +constexpr size_t _Traits_find_first_not_of(_In_reads_(_Hay_size) const _Traits_ptr_t<_Traits> _Haystack, + const size_t _Hay_size, const size_t _Start_at, _In_reads_(_Needle_size) const _Traits_ptr_t<_Traits> _Needle, + const size_t _Needle_size) noexcept { + // in [_Haystack, _Haystack + _Hay_size), look for none of [_Needle, _Needle + _Needle_size), at/after _Start_at + if (_Start_at < _Hay_size) { // room for match, look for it + if constexpr (_Special) { + _String_bitmap _Matches; + if (!_Matches._Mark(_Needle, _Needle + _Needle_size)) { // couldn't put one of the characters into the + // bitmap, fall back to the serial algorithm + return _Traits_find_first_not_of<_Traits, false>( + _Haystack, _Hay_size, _Start_at, _Needle, _Needle_size); + } + + const auto _End = _Haystack + _Hay_size; + for (auto _Match_try = _Haystack + _Start_at; _Match_try < _End; ++_Match_try) { + if (!_Matches._Match(*_Match_try)) { + return static_cast(_Match_try - _Haystack); // found a match + } + } + } else { + const auto _End = _Haystack + _Hay_size; + for (auto _Match_try = _Haystack + _Start_at; _Match_try < _End; ++_Match_try) { + if (!_Traits::find(_Needle, _Needle_size, *_Match_try)) { + return static_cast(_Match_try - _Haystack); // found a match + } + } + } + } + + return static_cast(-1); // no match +} + +template +constexpr size_t _Traits_find_not_ch(_In_reads_(_Hay_size) const _Traits_ptr_t<_Traits> _Haystack, + const size_t _Hay_size, const size_t _Start_at, const _Traits_ch_t<_Traits> _Ch) noexcept { + // search [_Haystack, _Haystack + _Hay_size) for any value other than _Ch, at/after _Start_at + if (_Start_at < _Hay_size) { // room for match, look for it + const auto _End = _Haystack + _Hay_size; + for (auto _Match_try = _Haystack + _Start_at; _Match_try < _End; ++_Match_try) { + if (!_Traits::eq(*_Match_try, _Ch)) { + return static_cast(_Match_try - _Haystack); // found a match + } + } + } + + return static_cast(-1); // no match +} + +template > +constexpr size_t _Traits_find_last_not_of(_In_reads_(_Hay_size) const _Traits_ptr_t<_Traits> _Haystack, + const size_t _Hay_size, const size_t _Start_at, _In_reads_(_Needle_size) const _Traits_ptr_t<_Traits> _Needle, + const size_t _Needle_size) noexcept { + // in [_Haystack, _Haystack + _Hay_size), look for none of [_Needle, _Needle + _Needle_size), before _Start_at + if (_Hay_size != 0) { // worth searching, do it + if constexpr (_Special) { + _String_bitmap _Matches; + if (!_Matches._Mark(_Needle, _Needle + _Needle_size)) { // couldn't put one of the characters into the + // bitmap, fall back to the serial algorithm + return _Traits_find_last_not_of<_Traits, false>(_Haystack, _Hay_size, _Start_at, _Needle, _Needle_size); + } + + for (auto _Match_try = _Haystack + (_STD min)(_Start_at, _Hay_size - 1);; --_Match_try) { + if (!_Matches._Match(*_Match_try)) { + return static_cast(_Match_try - _Haystack); // found a match + } + + if (_Match_try == _Haystack) { + break; // at beginning, no more chance for match + } + } + } else { + for (auto _Match_try = _Haystack + (_STD min)(_Start_at, _Hay_size - 1);; --_Match_try) { + if (!_Traits::find(_Needle, _Needle_size, *_Match_try)) { + return static_cast(_Match_try - _Haystack); // found a match + } + + if (_Match_try == _Haystack) { + break; // at beginning, no more chance for match + } + } + } + } + + return static_cast(-1); // no match +} + +template +constexpr size_t _Traits_rfind_not_ch(_In_reads_(_Hay_size) const _Traits_ptr_t<_Traits> _Haystack, + const size_t _Hay_size, const size_t _Start_at, const _Traits_ch_t<_Traits> _Ch) noexcept { + // search [_Haystack, _Haystack + _Hay_size) for any value other than _Ch before _Start_at + if (_Hay_size != 0) { // room for match, look for it + for (auto _Match_try = _Haystack + (_STD min)(_Start_at, _Hay_size - 1);; --_Match_try) { + if (!_Traits::eq(*_Match_try, _Ch)) { + return static_cast(_Match_try - _Haystack); // found a match + } + + if (_Match_try == _Haystack) { + break; // at beginning, no more chance for match + } + } + } + + return static_cast(-1); // no match +} + +template +constexpr bool _Is_EcharT = _Is_any_of_v<_Ty, char, wchar_t, +#ifdef __cpp_char8_t + char8_t, +#endif // defined(__cpp_char8_t) + char16_t, char32_t>; + +#if _HAS_CXX17 +_EXPORT_STD template > +class basic_string_view; + +template +class _String_view_iterator { +public: +#if _HAS_CXX20 + using iterator_concept = contiguous_iterator_tag; +#endif // _HAS_CXX20 + using iterator_category = random_access_iterator_tag; + using value_type = typename _Traits::char_type; + using difference_type = ptrdiff_t; + using pointer = const value_type*; + using reference = const value_type&; + + constexpr _String_view_iterator() noexcept = default; + +private: + friend basic_string_view; + +#if _ITERATOR_DEBUG_LEVEL >= 1 + constexpr _String_view_iterator(const pointer _Data, const size_t _Size, const size_t _Off) noexcept + : _Mydata(_Data), _Mysize(_Size), _Myoff(_Off) {} +#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv + constexpr explicit _String_view_iterator(const pointer _Ptr) noexcept : _Myptr(_Ptr) {} +#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ + +public: + _NODISCARD constexpr reference operator*() const noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + _STL_VERIFY(_Mydata, "cannot dereference value-initialized string_view iterator"); + _STL_VERIFY(_Myoff < _Mysize, "cannot dereference end string_view iterator"); + return _Mydata[_Myoff]; +#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv + return *_Myptr; +#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ + } + + _NODISCARD constexpr pointer operator->() const noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + _STL_VERIFY(_Mydata, "cannot dereference value-initialized string_view iterator"); + _STL_VERIFY(_Myoff < _Mysize, "cannot dereference end string_view iterator"); + return _Mydata + _Myoff; +#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv + return _Myptr; +#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ + } + + constexpr _String_view_iterator& operator++() noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + _STL_VERIFY(_Mydata, "cannot increment value-initialized string_view iterator"); + _STL_VERIFY(_Myoff < _Mysize, "cannot increment string_view iterator past end"); + ++_Myoff; +#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv + ++_Myptr; +#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ + return *this; + } + + constexpr _String_view_iterator operator++(int) noexcept { + _String_view_iterator _Tmp{*this}; + ++*this; + return _Tmp; + } + + constexpr _String_view_iterator& operator--() noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + _STL_VERIFY(_Mydata, "cannot decrement value-initialized string_view iterator"); + _STL_VERIFY(_Myoff != 0, "cannot decrement string_view iterator before begin"); + --_Myoff; +#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv + --_Myptr; +#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ + return *this; + } + + constexpr _String_view_iterator operator--(int) noexcept { + _String_view_iterator _Tmp{*this}; + --*this; + return _Tmp; + } + + constexpr void _Verify_offset(const difference_type _Off) const noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + if (_Off != 0) { + _STL_VERIFY(_Mydata, "cannot seek value-initialized string_view iterator"); + } + + if (_Off < 0) { + _STL_VERIFY( + _Myoff >= size_t{0} - static_cast(_Off), "cannot seek string_view iterator before begin"); + } + + if (_Off > 0) { + _STL_VERIFY(_Mysize - _Myoff >= static_cast(_Off), "cannot seek string_view iterator after end"); + } +#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv + (void) _Off; +#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ + } + + constexpr _String_view_iterator& operator+=(const difference_type _Off) noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + _Verify_offset(_Off); + _Myoff += static_cast(_Off); +#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv + _Myptr += _Off; +#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ + + return *this; + } + + _NODISCARD constexpr _String_view_iterator operator+(const difference_type _Off) const noexcept { + _String_view_iterator _Tmp{*this}; + _Tmp += _Off; + return _Tmp; + } + + _NODISCARD_FRIEND constexpr _String_view_iterator operator+( + const difference_type _Off, _String_view_iterator _Right) noexcept { + _Right += _Off; + return _Right; + } + + constexpr _String_view_iterator& operator-=(const difference_type _Off) noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + if (_Off != 0) { + _STL_VERIFY(_Mydata, "cannot seek value-initialized string_view iterator"); + } + + if (_Off > 0) { + _STL_VERIFY(_Myoff >= static_cast(_Off), "cannot seek string_view iterator before begin"); + } + + if (_Off < 0) { + _STL_VERIFY(_Mysize - _Myoff >= size_t{0} - static_cast(_Off), + "cannot seek string_view iterator after end"); + } + + _Myoff -= static_cast(_Off); +#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv + _Myptr -= _Off; +#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ + + return *this; + } + + _NODISCARD constexpr _String_view_iterator operator-(const difference_type _Off) const noexcept { + _String_view_iterator _Tmp{*this}; + _Tmp -= _Off; + return _Tmp; + } + + _NODISCARD constexpr difference_type operator-(const _String_view_iterator& _Right) const noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + _STL_VERIFY(_Mydata == _Right._Mydata && _Mysize == _Right._Mysize, + "cannot subtract incompatible string_view iterators"); + return static_cast(_Myoff - _Right._Myoff); +#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv + return _Myptr - _Right._Myptr; +#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ + } + + _NODISCARD constexpr reference operator[](const difference_type _Off) const noexcept { + return *(*this + _Off); + } + + _NODISCARD constexpr bool operator==(const _String_view_iterator& _Right) const noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + _STL_VERIFY(_Mydata == _Right._Mydata && _Mysize == _Right._Mysize, + "cannot compare incompatible string_view iterators for equality"); + return _Myoff == _Right._Myoff; +#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv + return _Myptr == _Right._Myptr; +#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ + } + +#if _HAS_CXX20 + _NODISCARD constexpr strong_ordering operator<=>(const _String_view_iterator& _Right) const noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + _STL_VERIFY(_Mydata == _Right._Mydata && _Mysize == _Right._Mysize, + "cannot compare incompatible string_view iterators"); + return _Myoff <=> _Right._Myoff; +#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv + return _Myptr <=> _Right._Myptr; +#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ + } +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv + _NODISCARD constexpr bool operator!=(const _String_view_iterator& _Right) const noexcept { + return !(*this == _Right); + } + + _NODISCARD constexpr bool operator<(const _String_view_iterator& _Right) const noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + _STL_VERIFY(_Mydata == _Right._Mydata && _Mysize == _Right._Mysize, + "cannot compare incompatible string_view iterators"); + return _Myoff < _Right._Myoff; +#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv + return _Myptr < _Right._Myptr; +#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ + } + + _NODISCARD constexpr bool operator>(const _String_view_iterator& _Right) const noexcept { + return _Right < *this; + } + + _NODISCARD constexpr bool operator<=(const _String_view_iterator& _Right) const noexcept { + return !(_Right < *this); + } + + _NODISCARD constexpr bool operator>=(const _String_view_iterator& _Right) const noexcept { + return !(*this < _Right); + } +#endif // !_HAS_CXX20 + +#if _ITERATOR_DEBUG_LEVEL >= 1 + friend constexpr void _Verify_range(const _String_view_iterator& _First, const _String_view_iterator& _Last) { + _STL_VERIFY(_First._Mydata == _Last._Mydata && _First._Mysize == _Last._Mysize, + "string_view iterators in range are from different views"); + _STL_VERIFY(_First._Myoff <= _Last._Myoff, "string_view iterator range transposed"); + } +#endif // _ITERATOR_DEBUG_LEVEL >= 1 + + using _Prevent_inheriting_unwrap = _String_view_iterator; + + _NODISCARD constexpr pointer _Unwrapped() const noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + return _Mydata + _Myoff; +#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv + return _Myptr; +#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ + } + + static constexpr bool _Unwrap_when_unverified = _ITERATOR_DEBUG_LEVEL == 0; + + constexpr void _Seek_to(pointer _It) noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + _Myoff = static_cast(_It - _Mydata); +#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv + _Myptr = _It; +#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ + } + +private: +#if _ITERATOR_DEBUG_LEVEL >= 1 + pointer _Mydata = nullptr; + size_t _Mysize = 0; + size_t _Myoff = 0; +#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv + pointer _Myptr = nullptr; +#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ +}; + +#if _HAS_CXX20 +template +struct pointer_traits<_String_view_iterator<_Traits>> { + using pointer = _String_view_iterator<_Traits>; + using element_type = const pointer::value_type; + using difference_type = pointer::difference_type; + + _NODISCARD static constexpr element_type* to_address(const pointer& _Iter) noexcept { + return _Iter._Unwrapped(); + } +}; +#endif // _HAS_CXX20 + +#pragma warning(push) +// Invalid annotation: 'NullTerminated' property may only be used on buffers whose elements are of integral or pointer +// type +#pragma warning(disable : 6510) + +_EXPORT_STD template +class basic_string_view { // wrapper for any kind of contiguous character buffer +public: + static_assert(is_same_v<_Elem, typename _Traits::char_type>, + "Bad char_traits for basic_string_view; N4950 [string.view.template.general]/1 " + "\"The program is ill-formed if traits::char_type is not the same type as charT.\""); + + static_assert(!is_array_v<_Elem> && is_trivial_v<_Elem> && is_standard_layout_v<_Elem>, + "The character type of basic_string_view must be a non-array trivial standard-layout type. See N4950 " + "[strings.general]/1."); + + using traits_type = _Traits; + using value_type = _Elem; + using pointer = _Elem*; + using const_pointer = const _Elem*; + using reference = _Elem&; + using const_reference = const _Elem&; + using const_iterator = _String_view_iterator<_Traits>; + using iterator = const_iterator; + using const_reverse_iterator = _STD reverse_iterator; + using reverse_iterator = const_reverse_iterator; + using size_type = size_t; + using difference_type = ptrdiff_t; + + static constexpr auto npos{static_cast(-1)}; + + constexpr basic_string_view() noexcept : _Mydata(), _Mysize(0) {} + + constexpr basic_string_view(const basic_string_view&) noexcept = default; + constexpr basic_string_view& operator=(const basic_string_view&) noexcept = default; + + /* implicit */ constexpr basic_string_view(_In_z_ const const_pointer _Ntcts) noexcept // strengthened + : _Mydata(_Ntcts), _Mysize(_Traits::length(_Ntcts)) {} + +#if _HAS_CXX23 + basic_string_view(nullptr_t) = delete; +#endif // _HAS_CXX23 + + constexpr basic_string_view( + _In_reads_(_Count) const const_pointer _Cts, const size_type _Count) noexcept // strengthened + : _Mydata(_Cts), _Mysize(_Count) { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Count == 0 || _Cts, "non-zero size null string_view"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + } + +#if _HAS_CXX20 + template _Sent> + requires (is_same_v, _Elem> && !is_convertible_v<_Sent, size_type>) + constexpr basic_string_view(_Iter _First, _Sent _Last) noexcept(noexcept(_Last - _First)) // strengthened + : _Mydata(_STD to_address(_First)), _Mysize(static_cast(_Last - _First)) {} + +#if _HAS_CXX23 + // clang-format off + template + requires (!same_as, basic_string_view> + && _RANGES contiguous_range<_Range> + && _RANGES sized_range<_Range> + && same_as<_RANGES range_value_t<_Range>, _Elem> + && !is_convertible_v<_Range, const _Elem*> + && !requires(remove_cvref_t<_Range>& _Rng) { + _Rng.operator _STD basic_string_view<_Elem, _Traits>(); + }) + constexpr explicit basic_string_view(_Range&& _Rng) noexcept( + noexcept(_RANGES data(_Rng)) && noexcept(_RANGES size(_Rng))) // strengthened + : _Mydata(_RANGES data(_Rng)), _Mysize(static_cast(_RANGES size(_Rng))) {} + // clang-format on +#endif // _HAS_CXX23 +#endif // _HAS_CXX20 + + _NODISCARD constexpr const_iterator begin() const noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + return const_iterator(_Mydata, _Mysize, 0); +#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv + return const_iterator(_Mydata); +#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ + } + + _NODISCARD constexpr const_iterator end() const noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + return const_iterator(_Mydata, _Mysize, _Mysize); +#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv + return const_iterator(_Mydata + _Mysize); +#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ + } + + _NODISCARD constexpr const_iterator cbegin() const noexcept { + return begin(); + } + + _NODISCARD constexpr const_iterator cend() const noexcept { + return end(); + } + + _NODISCARD constexpr const_reverse_iterator rbegin() const noexcept { + return const_reverse_iterator{end()}; + } + + _NODISCARD constexpr const_reverse_iterator rend() const noexcept { + return const_reverse_iterator{begin()}; + } + + _NODISCARD constexpr const_reverse_iterator crbegin() const noexcept { + return rbegin(); + } + + _NODISCARD constexpr const_reverse_iterator crend() const noexcept { + return rend(); + } + + constexpr const_pointer _Unchecked_begin() const noexcept { + return _Mydata; + } + + constexpr const_pointer _Unchecked_end() const noexcept { + return _Mydata + _Mysize; + } + + _NODISCARD constexpr size_type size() const noexcept { + return _Mysize; + } + + _NODISCARD constexpr size_type length() const noexcept { + return _Mysize; + } + + _NODISCARD constexpr bool empty() const noexcept { + return _Mysize == 0; + } + + _NODISCARD constexpr const_pointer data() const noexcept { + return _Mydata; + } + + _NODISCARD constexpr size_type max_size() const noexcept { + // bound to PTRDIFF_MAX to make end() - begin() well defined (also makes room for npos) + // bound to static_cast(-1) / sizeof(_Elem) by address space limits + return (_STD min)(static_cast(PTRDIFF_MAX), static_cast(-1) / sizeof(_Elem)); + } + + _NODISCARD constexpr const_reference operator[](const size_type _Off) const noexcept /* strengthened */ { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Off < _Mysize, "string_view subscript out of range"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return _Mydata[_Off]; + } + + _NODISCARD constexpr const_reference at(const size_type _Off) const { + // get the character at _Off or throw if that is out of range + _Check_offset_exclusive(_Off); + return _Mydata[_Off]; + } + + _NODISCARD constexpr const_reference front() const noexcept /* strengthened */ { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Mysize != 0, "cannot call front on empty string_view"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return _Mydata[0]; + } + + _NODISCARD constexpr const_reference back() const noexcept /* strengthened */ { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Mysize != 0, "cannot call back on empty string_view"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return _Mydata[_Mysize - 1]; + } + + constexpr void remove_prefix(const size_type _Count) noexcept /* strengthened */ { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Mysize >= _Count, "cannot remove prefix longer than total size"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + _Mydata += _Count; + _Mysize -= _Count; + } + + constexpr void remove_suffix(const size_type _Count) noexcept /* strengthened */ { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Mysize >= _Count, "cannot remove suffix longer than total size"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + _Mysize -= _Count; + } + + constexpr void swap(basic_string_view& _Other) noexcept { + const basic_string_view _Tmp{_Other}; // note: std::swap is not constexpr before C++20 + _Other = *this; + *this = _Tmp; + } + + _CONSTEXPR20 size_type copy( + _Out_writes_(_Count) _Elem* const _Ptr, size_type _Count, const size_type _Off = 0) const { + // copy [_Off, _Off + Count) to [_Ptr, _Ptr + _Count) + _Check_offset(_Off); + _Count = _Clamp_suffix_size(_Off, _Count); + _Traits::copy(_Ptr, _Mydata + _Off, _Count); + return _Count; + } + + _Pre_satisfies_(_Dest_size >= _Count) _CONSTEXPR20 size_type + _Copy_s(_Out_writes_all_(_Dest_size) _Elem* const _Dest, const size_type _Dest_size, size_type _Count, + const size_type _Off = 0) const { + // copy [_Off, _Off + _Count) to [_Dest, _Dest + _Count) + _Check_offset(_Off); + _Count = _Clamp_suffix_size(_Off, _Count); + _Traits::_Copy_s(_Dest, _Dest_size, _Mydata + _Off, _Count); + return _Count; + } + + _NODISCARD constexpr basic_string_view substr(const size_type _Off = 0, size_type _Count = npos) const { + // return a new basic_string_view moved forward by _Off and trimmed to _Count elements + _Check_offset(_Off); + _Count = _Clamp_suffix_size(_Off, _Count); + return basic_string_view(_Mydata + _Off, _Count); + } + + constexpr bool _Equal(const basic_string_view _Right) const noexcept { + return _Traits_equal<_Traits>(_Mydata, _Mysize, _Right._Mydata, _Right._Mysize); + } + + _NODISCARD constexpr int compare(const basic_string_view _Right) const noexcept { + return _Traits_compare<_Traits>(_Mydata, _Mysize, _Right._Mydata, _Right._Mysize); + } + + _NODISCARD constexpr int compare(const size_type _Off, const size_type _Nx, const basic_string_view _Right) const { + // compare [_Off, _Off + _Nx) with _Right + return substr(_Off, _Nx).compare(_Right); + } + + _NODISCARD constexpr int compare(const size_type _Off, const size_type _Nx, const basic_string_view _Right, + const size_type _Roff, const size_type _Count) const { + // compare [_Off, _Off + _Nx) with _Right [_Roff, _Roff + _Count) + return substr(_Off, _Nx).compare(_Right.substr(_Roff, _Count)); + } + + _NODISCARD constexpr int compare(_In_z_ const _Elem* const _Ptr) const noexcept /* strengthened */ { + // compare [0, _Mysize) with [_Ptr, ) + return compare(basic_string_view(_Ptr)); + } + + _NODISCARD constexpr int compare(const size_type _Off, const size_type _Nx, _In_z_ const _Elem* const _Ptr) const { + // compare [_Off, _Off + _Nx) with [_Ptr, ) + return substr(_Off, _Nx).compare(basic_string_view(_Ptr)); + } + + _NODISCARD constexpr int compare(const size_type _Off, const size_type _Nx, + _In_reads_(_Count) const _Elem* const _Ptr, const size_type _Count) const { + // compare [_Off, _Off + _Nx) with [_Ptr, _Ptr + _Count) + return substr(_Off, _Nx).compare(basic_string_view(_Ptr, _Count)); + } + +#if _HAS_CXX20 + _NODISCARD constexpr bool starts_with(const basic_string_view _Right) const noexcept { + const auto _Rightsize = _Right._Mysize; + if (_Mysize < _Rightsize) { + return false; + } + return _Traits::compare(_Mydata, _Right._Mydata, _Rightsize) == 0; + } + + _NODISCARD constexpr bool starts_with(const _Elem _Right) const noexcept { + return !empty() && _Traits::eq(front(), _Right); + } + + _NODISCARD constexpr bool starts_with(const _Elem* const _Right) const noexcept /* strengthened */ { + return starts_with(basic_string_view(_Right)); + } + + _NODISCARD constexpr bool ends_with(const basic_string_view _Right) const noexcept { + const auto _Rightsize = _Right._Mysize; + if (_Mysize < _Rightsize) { + return false; + } + return _Traits::compare(_Mydata + (_Mysize - _Rightsize), _Right._Mydata, _Rightsize) == 0; + } + + _NODISCARD constexpr bool ends_with(const _Elem _Right) const noexcept { + return !empty() && _Traits::eq(back(), _Right); + } + + _NODISCARD constexpr bool ends_with(const _Elem* const _Right) const noexcept /* strengthened */ { + return ends_with(basic_string_view(_Right)); + } +#endif // _HAS_CXX20 + +#if _HAS_CXX23 + _NODISCARD constexpr bool contains(const basic_string_view _Right) const noexcept { + return find(_Right) != npos; + } + + _NODISCARD constexpr bool contains(const _Elem _Right) const noexcept { + return find(_Right) != npos; + } + + _NODISCARD constexpr bool contains(const _Elem* const _Right) const noexcept /* strengthened */ { + return find(_Right) != npos; + } +#endif // _HAS_CXX23 + + _NODISCARD constexpr size_type find(const basic_string_view _Right, const size_type _Off = 0) const noexcept { + // look for _Right beginning at or after _Off + return _Traits_find<_Traits>(_Mydata, _Mysize, _Off, _Right._Mydata, _Right._Mysize); + } + + _NODISCARD constexpr size_type find(const _Elem _Ch, const size_type _Off = 0) const noexcept { + // look for _Ch at or after _Off + return _Traits_find_ch<_Traits>(_Mydata, _Mysize, _Off, _Ch); + } + + _NODISCARD constexpr size_type find(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, + const size_type _Count) const noexcept /* strengthened */ { + // look for [_Ptr, _Ptr + _Count) beginning at or after _Off + return _Traits_find<_Traits>(_Mydata, _Mysize, _Off, _Ptr, _Count); + } + + _NODISCARD constexpr size_type find(_In_z_ const _Elem* const _Ptr, const size_type _Off = 0) const noexcept + /* strengthened */ { + // look for [_Ptr, ) beginning at or after _Off + return _Traits_find<_Traits>(_Mydata, _Mysize, _Off, _Ptr, _Traits::length(_Ptr)); + } + + _NODISCARD constexpr size_type rfind(const basic_string_view _Right, const size_type _Off = npos) const noexcept { + // look for _Right beginning before _Off + return _Traits_rfind<_Traits>(_Mydata, _Mysize, _Off, _Right._Mydata, _Right._Mysize); + } + + _NODISCARD constexpr size_type rfind(const _Elem _Ch, const size_type _Off = npos) const noexcept { + // look for _Ch before _Off + return _Traits_rfind_ch<_Traits>(_Mydata, _Mysize, _Off, _Ch); + } + + _NODISCARD constexpr size_type rfind(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, + const size_type _Count) const noexcept /* strengthened */ { + // look for [_Ptr, _Ptr + _Count) beginning before _Off + return _Traits_rfind<_Traits>(_Mydata, _Mysize, _Off, _Ptr, _Count); + } + + _NODISCARD constexpr size_type rfind(_In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept + /* strengthened */ { + // look for [_Ptr, ) beginning before _Off + return _Traits_rfind<_Traits>(_Mydata, _Mysize, _Off, _Ptr, _Traits::length(_Ptr)); + } + + _NODISCARD constexpr size_type find_first_of(const basic_string_view _Right, + const size_type _Off = 0) const noexcept { // look for one of _Right at or after _Off + return _Traits_find_first_of<_Traits>(_Mydata, _Mysize, _Off, _Right._Mydata, _Right._Mysize); + } + + _NODISCARD constexpr size_type find_first_of(const _Elem _Ch, const size_type _Off = 0) const noexcept { + // look for _Ch at or after _Off + return _Traits_find_ch<_Traits>(_Mydata, _Mysize, _Off, _Ch); + } + + _NODISCARD constexpr size_type find_first_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, + const size_type _Count) const noexcept /* strengthened */ { + // look for one of [_Ptr, _Ptr + _Count) at or after _Off + return _Traits_find_first_of<_Traits>(_Mydata, _Mysize, _Off, _Ptr, _Count); + } + + _NODISCARD constexpr size_type find_first_of( + _In_z_ const _Elem* const _Ptr, const size_type _Off = 0) const noexcept /* strengthened */ { + // look for one of [_Ptr, ) at or after _Off + return _Traits_find_first_of<_Traits>(_Mydata, _Mysize, _Off, _Ptr, _Traits::length(_Ptr)); + } + + _NODISCARD constexpr size_type find_last_of(const basic_string_view _Right, + const size_type _Off = npos) const noexcept { // look for one of _Right before _Off + return _Traits_find_last_of<_Traits>(_Mydata, _Mysize, _Off, _Right._Mydata, _Right._Mysize); + } + + _NODISCARD constexpr size_type find_last_of(const _Elem _Ch, const size_type _Off = npos) const noexcept { + // look for _Ch before _Off + return _Traits_rfind_ch<_Traits>(_Mydata, _Mysize, _Off, _Ch); + } + + _NODISCARD constexpr size_type find_last_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, + const size_type _Count) const noexcept /* strengthened */ { + // look for one of [_Ptr, _Ptr + _Count) before _Off + return _Traits_find_last_of<_Traits>(_Mydata, _Mysize, _Off, _Ptr, _Count); + } + + _NODISCARD constexpr size_type find_last_of( + _In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept /* strengthened */ { + // look for one of [_Ptr, ) before _Off + return _Traits_find_last_of<_Traits>(_Mydata, _Mysize, _Off, _Ptr, _Traits::length(_Ptr)); + } + + _NODISCARD constexpr size_type find_first_not_of(const basic_string_view _Right, + const size_type _Off = 0) const noexcept { // look for none of _Right at or after _Off + return _Traits_find_first_not_of<_Traits>(_Mydata, _Mysize, _Off, _Right._Mydata, _Right._Mysize); + } + + _NODISCARD constexpr size_type find_first_not_of(const _Elem _Ch, const size_type _Off = 0) const noexcept { + // look for any value other than _Ch at or after _Off + return _Traits_find_not_ch<_Traits>(_Mydata, _Mysize, _Off, _Ch); + } + + _NODISCARD constexpr size_type find_first_not_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, + const size_type _Count) const noexcept /* strengthened */ { + // look for none of [_Ptr, _Ptr + _Count) at or after _Off + return _Traits_find_first_not_of<_Traits>(_Mydata, _Mysize, _Off, _Ptr, _Count); + } + + _NODISCARD constexpr size_type find_first_not_of( + _In_z_ const _Elem* const _Ptr, const size_type _Off = 0) const noexcept /* strengthened */ { + // look for none of [_Ptr, ) at or after _Off + return _Traits_find_first_not_of<_Traits>(_Mydata, _Mysize, _Off, _Ptr, _Traits::length(_Ptr)); + } + + _NODISCARD constexpr size_type find_last_not_of(const basic_string_view _Right, + const size_type _Off = npos) const noexcept { // look for none of _Right before _Off + return _Traits_find_last_not_of<_Traits>(_Mydata, _Mysize, _Off, _Right._Mydata, _Right._Mysize); + } + + _NODISCARD constexpr size_type find_last_not_of(const _Elem _Ch, const size_type _Off = npos) const noexcept { + // look for any value other than _Ch before _Off + return _Traits_rfind_not_ch<_Traits>(_Mydata, _Mysize, _Off, _Ch); + } + + _NODISCARD constexpr size_type find_last_not_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, + const size_type _Count) const noexcept /* strengthened */ { + // look for none of [_Ptr, _Ptr + _Count) before _Off + return _Traits_find_last_not_of<_Traits>(_Mydata, _Mysize, _Off, _Ptr, _Count); + } + + _NODISCARD constexpr size_type find_last_not_of( + _In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept /* strengthened */ { + // look for none of [_Ptr, ) before _Off + return _Traits_find_last_not_of<_Traits>(_Mydata, _Mysize, _Off, _Ptr, _Traits::length(_Ptr)); + } + + _NODISCARD constexpr bool _Starts_with(const basic_string_view _View) const noexcept { + return _Mysize >= _View._Mysize && _Traits::compare(_Mydata, _View._Mydata, _View._Mysize) == 0; + } + +private: + constexpr void _Check_offset(const size_type _Off) const { // checks whether _Off is in the bounds of [0, size()] + if (_Mysize < _Off) { + _Xran(); + } + } + + constexpr void _Check_offset_exclusive(const size_type _Off) const { + // checks whether _Off is in the bounds of [0, size()) + if (_Mysize <= _Off) { + _Xran(); + } + } + + constexpr size_type _Clamp_suffix_size(const size_type _Off, const size_type _Size) const noexcept { + // trims _Size to the longest it can be assuming a string at/after _Off + return (_STD min)(_Size, _Mysize - _Off); + } + + [[noreturn]] static void _Xran() { + _Xout_of_range("invalid string_view position"); + } + + const_pointer _Mydata; + size_type _Mysize; +}; + +#pragma warning(pop) + +#if _HAS_CXX20 +template _Sent> +basic_string_view(_Iter, _Sent) -> basic_string_view>; + +#if _HAS_CXX23 +template <_RANGES contiguous_range _Range> +basic_string_view(_Range&&) -> basic_string_view<_RANGES range_value_t<_Range>>; +#endif // _HAS_CXX23 + +namespace ranges { + template + constexpr bool enable_view> = true; + template + constexpr bool enable_borrowed_range> = true; +} // namespace ranges +#endif // _HAS_CXX20 + +#if _HAS_CXX20 +_EXPORT_STD template +_NODISCARD constexpr bool operator==(const basic_string_view<_Elem, _Traits> _Lhs, + const type_identity_t> _Rhs) noexcept { + return _Lhs._Equal(_Rhs); +} + +template +struct _Get_comparison_category { + using type = weak_ordering; +}; + +template +struct _Get_comparison_category<_Traits, void_t> { + using type = _Traits::comparison_category; + + static_assert(_Is_any_of_v, + "N4971 [string.view.comparison]/4: Mandates: R denotes a comparison category type."); +}; + +template +using _Get_comparison_category_t = _Get_comparison_category<_Traits>::type; + +_EXPORT_STD template +_NODISCARD constexpr _Get_comparison_category_t<_Traits> operator<=>(const basic_string_view<_Elem, _Traits> _Lhs, + const type_identity_t> _Rhs) noexcept { + return static_cast<_Get_comparison_category_t<_Traits>>(_Lhs.compare(_Rhs) <=> 0); +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv +template +_NODISCARD constexpr bool operator==( + const basic_string_view<_Elem, _Traits> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { + return _Lhs._Equal(_Rhs); +} + +template // TRANSITION, VSO-409326 +_NODISCARD constexpr bool operator==( + const _Identity_t> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { + return _Lhs._Equal(_Rhs); +} + +template // TRANSITION, VSO-409326 +_NODISCARD constexpr bool operator==( + const basic_string_view<_Elem, _Traits> _Lhs, const _Identity_t> _Rhs) noexcept { + return _Lhs._Equal(_Rhs); +} + +template +_NODISCARD constexpr bool operator!=( + const basic_string_view<_Elem, _Traits> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { + return !_Lhs._Equal(_Rhs); +} + +template // TRANSITION, VSO-409326 +_NODISCARD constexpr bool operator!=( + const _Identity_t> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { + return !_Lhs._Equal(_Rhs); +} + +template // TRANSITION, VSO-409326 +_NODISCARD constexpr bool operator!=( + const basic_string_view<_Elem, _Traits> _Lhs, const _Identity_t> _Rhs) noexcept { + return !_Lhs._Equal(_Rhs); +} + +template +_NODISCARD constexpr bool operator<( + const basic_string_view<_Elem, _Traits> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { + return _Lhs.compare(_Rhs) < 0; +} + +template // TRANSITION, VSO-409326 +_NODISCARD constexpr bool operator<( + const _Identity_t> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { + return _Lhs.compare(_Rhs) < 0; +} + +template // TRANSITION, VSO-409326 +_NODISCARD constexpr bool operator<( + const basic_string_view<_Elem, _Traits> _Lhs, const _Identity_t> _Rhs) noexcept { + return _Lhs.compare(_Rhs) < 0; +} + +template +_NODISCARD constexpr bool operator>( + const basic_string_view<_Elem, _Traits> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { + return _Lhs.compare(_Rhs) > 0; +} + +template // TRANSITION, VSO-409326 +_NODISCARD constexpr bool operator>( + const _Identity_t> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { + return _Lhs.compare(_Rhs) > 0; +} + +template // TRANSITION, VSO-409326 +_NODISCARD constexpr bool operator>( + const basic_string_view<_Elem, _Traits> _Lhs, const _Identity_t> _Rhs) noexcept { + return _Lhs.compare(_Rhs) > 0; +} + +template +_NODISCARD constexpr bool operator<=( + const basic_string_view<_Elem, _Traits> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { + return _Lhs.compare(_Rhs) <= 0; +} + +template // TRANSITION, VSO-409326 +_NODISCARD constexpr bool operator<=( + const _Identity_t> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { + return _Lhs.compare(_Rhs) <= 0; +} + +template // TRANSITION, VSO-409326 +_NODISCARD constexpr bool operator<=( + const basic_string_view<_Elem, _Traits> _Lhs, const _Identity_t> _Rhs) noexcept { + return _Lhs.compare(_Rhs) <= 0; +} + +template +_NODISCARD constexpr bool operator>=( + const basic_string_view<_Elem, _Traits> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { + return _Lhs.compare(_Rhs) >= 0; +} + +template // TRANSITION, VSO-409326 +_NODISCARD constexpr bool operator>=( + const _Identity_t> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { + return _Lhs.compare(_Rhs) >= 0; +} + +template // TRANSITION, VSO-409326 +_NODISCARD constexpr bool operator>=( + const basic_string_view<_Elem, _Traits> _Lhs, const _Identity_t> _Rhs) noexcept { + return _Lhs.compare(_Rhs) >= 0; +} +#endif // ^^^ !_HAS_CXX20 ^^^ + +_EXPORT_STD using string_view = basic_string_view; +#ifdef __cpp_lib_char8_t +_EXPORT_STD using u8string_view = basic_string_view; +#endif // defined(__cpp_lib_char8_t) +_EXPORT_STD using u16string_view = basic_string_view; +_EXPORT_STD using u32string_view = basic_string_view; +_EXPORT_STD using wstring_view = basic_string_view; + +template +struct hash> : _Conditionally_enabled_hash, _Is_EcharT<_Elem>> { + _NODISCARD static size_t _Do_hash(const basic_string_view<_Elem> _Keyval) noexcept { + return _Hash_array_representation(_Keyval.data(), _Keyval.size()); + } +}; + +_EXPORT_STD template +basic_ostream<_Elem, _Traits>& operator<<( + basic_ostream<_Elem, _Traits>& _Ostr, const basic_string_view<_Elem, _Traits> _Str) { + return _Insert_string(_Ostr, _Str.data(), _Str.size()); +} + +inline namespace literals { + inline namespace string_view_literals { + _EXPORT_STD _NODISCARD constexpr string_view operator""sv(const char* _Str, size_t _Len) noexcept { + return string_view(_Str, _Len); + } + + _EXPORT_STD _NODISCARD constexpr wstring_view operator""sv(const wchar_t* _Str, size_t _Len) noexcept { + return wstring_view(_Str, _Len); + } + +#ifdef __cpp_char8_t + _EXPORT_STD _NODISCARD constexpr basic_string_view operator""sv( + const char8_t* _Str, size_t _Len) noexcept { + return basic_string_view(_Str, _Len); + } +#endif // defined(__cpp_char8_t) + + _EXPORT_STD _NODISCARD constexpr u16string_view operator""sv(const char16_t* _Str, size_t _Len) noexcept { + return u16string_view(_Str, _Len); + } + + _EXPORT_STD _NODISCARD constexpr u32string_view operator""sv(const char32_t* _Str, size_t _Len) noexcept { + return u32string_view(_Str, _Len); + } + } // namespace string_view_literals +} // namespace literals +#endif // _HAS_CXX17 +_STD_END + +#pragma pop_macro("new") +_STL_RESTORE_CLANG_WARNINGS +#pragma warning(pop) +#pragma pack(pop) + +#endif // _STL_COMPILER_PREPROCESSOR +#endif // __MSVC_STRING_VIEW_HPP diff --git a/stl/inc/header-units.json b/stl/inc/header-units.json index 52cd5bf0811..facf4398afa 100644 --- a/stl/inc/header-units.json +++ b/stl/inc/header-units.json @@ -16,6 +16,7 @@ "__msvc_minmax.hpp", "__msvc_print.hpp", "__msvc_sanitizer_annotate_container.hpp", + "__msvc_string_view.hpp", "__msvc_system_error_abi.hpp", "__msvc_threads_core.hpp", "__msvc_tzdb.hpp", diff --git a/stl/inc/ranges b/stl/inc/ranges index ec72cc0c8aa..46136fcba0f 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -21,6 +21,7 @@ _EMIT_STL_WARNING(STL4038, "The contents of are available only with C++ #if _HAS_CXX23 #include #include +#include #endif // _HAS_CXX23 #pragma pack(push, _CRT_PACKING) diff --git a/stl/inc/string_view b/stl/inc/string_view index f6fe952f223..ca465021816 100644 --- a/stl/inc/string_view +++ b/stl/inc/string_view @@ -11,7 +11,12 @@ #if !_HAS_CXX17 _EMIT_STL_WARNING(STL4038, "The contents of are available only with C++17 or later."); #else // ^^^ !_HAS_CXX17 / _HAS_CXX17 vvv +#ifdef _LEGACY_CODE_ASSUMES_STRING_VIEW_INCLUDES_XSTRING #include +#else // ^^^ defined(_LEGACY_CODE_ASSUMES_STRING_VIEW_INCLUDES_XSTRING) / + // !defined(_LEGACY_CODE_ASSUMES_STRING_VIEW_INCLUDES_XSTRING) vvv +#include <__msvc_string_view.hpp> +#endif // ^^^ !defined(_LEGACY_CODE_ASSUMES_STRING_VIEW_INCLUDES_XSTRING) ^^^ #endif // ^^^ _HAS_CXX17 ^^^ #endif // _STL_COMPILER_PREPROCESSOR #endif // _STRING_VIEW_ diff --git a/stl/inc/xstring b/stl/inc/xstring index 34503506c91..6ba65a1e976 100644 --- a/stl/inc/xstring +++ b/stl/inc/xstring @@ -3,13 +3,12 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// ( without emitting non-C++17 warnings) - #ifndef _XSTRING_ #define _XSTRING_ #include #if _STL_COMPILER_PREPROCESSOR #include <__msvc_sanitizer_annotate_container.hpp> +#include <__msvc_string_view.hpp> #include #include @@ -24,1796 +23,7 @@ _STL_DISABLE_CLANG_WARNINGS #pragma push_macro("new") #undef new -#ifdef __clang__ -#define _HAS_MEMCPY_MEMMOVE_INTRINSICS 1 -#else // ^^^ use __builtin_memcpy and __builtin_memmove / use workaround vvv -#define _HAS_MEMCPY_MEMMOVE_INTRINSICS 0 // TRANSITION, DevCom-1046483 (MSVC) and VSO-1129974 (EDG) -#endif // ^^^ use workaround ^^^ - _STD_BEGIN -template -struct _Char_traits { // properties of a string or stream element - using char_type = _Elem; - using int_type = _Int_type; - using pos_type = streampos; - using off_type = streamoff; - using state_type = _Mbstatet; -#if _HAS_CXX20 - using comparison_category = strong_ordering; -#endif // _HAS_CXX20 - - // For copy/move, we can uniformly call memcpy/memmove (or their builtin versions) for all element types. - - static _CONSTEXPR20 _Elem* copy(_Out_writes_all_(_Count) _Elem* const _First1, - _In_reads_(_Count) const _Elem* const _First2, const size_t _Count) noexcept /* strengthened */ { - // copy [_First2, _First2 + _Count) to [_First1, ...) -#if _HAS_MEMCPY_MEMMOVE_INTRINSICS - __builtin_memcpy(_First1, _First2, _Count * sizeof(_Elem)); -#else // ^^^ _HAS_MEMCPY_MEMMOVE_INTRINSICS / !_HAS_MEMCPY_MEMMOVE_INTRINSICS vvv -#if _HAS_CXX20 - if (_STD is_constant_evaluated()) { - // pre: [_First1, _First1 + _Count) and [_First2, _First2 + _Count) do not overlap - for (size_t _Idx = 0; _Idx != _Count; ++_Idx) { - _First1[_Idx] = _First2[_Idx]; - } - - return _First1; - } -#endif // _HAS_CXX20 - - _CSTD memcpy(_First1, _First2, _Count * sizeof(_Elem)); -#endif // ^^^ !_HAS_MEMCPY_MEMMOVE_INTRINSICS ^^^ - - return _First1; - } - - _Pre_satisfies_(_Dest_size >= _Count) static _CONSTEXPR20 _Elem* _Copy_s(_Out_writes_all_(_Dest_size) - _Elem* const _First1, - const size_t _Dest_size, _In_reads_(_Count) const _Elem* const _First2, const size_t _Count) noexcept { - // copy [_First2, _First2 + _Count) to [_First1, _First1 + _Dest_size) - _STL_VERIFY(_Count <= _Dest_size, "invalid argument"); - return copy(_First1, _First2, _Count); - } - - static _CONSTEXPR20 _Elem* move(_Out_writes_all_(_Count) _Elem* const _First1, - _In_reads_(_Count) const _Elem* const _First2, const size_t _Count) noexcept /* strengthened */ { - // copy [_First2, _First2 + _Count) to [_First1, ...), allowing overlap -#if _HAS_MEMCPY_MEMMOVE_INTRINSICS - __builtin_memmove(_First1, _First2, _Count * sizeof(_Elem)); -#else // ^^^ _HAS_MEMCPY_MEMMOVE_INTRINSICS / !_HAS_MEMCPY_MEMMOVE_INTRINSICS vvv -#if _HAS_CXX20 - if (_STD is_constant_evaluated()) { - // dest: [_First1, _First1 + _Count) - // src: [_First2, _First2 + _Count) - // We need to handle overlapping ranges. - // If _First1 is in the src range, we need a backward loop. - // Otherwise, the forward loop works (even if the back of dest overlaps the front of src). - - // Usually, we would compare pointers with less-than, even though they could belong to different arrays. - // However, we're not allowed to do that during constant evaluation, so we need a linear scan for equality. - bool _Loop_forward = true; - - for (const _Elem* _Src = _First2; _Src != _First2 + _Count; ++_Src) { - if (_First1 == _Src) { - _Loop_forward = false; - break; - } - } - - if (_Loop_forward) { - for (size_t _Idx = 0; _Idx != _Count; ++_Idx) { - _First1[_Idx] = _First2[_Idx]; - } - } else { - for (size_t _Idx = _Count; _Idx != 0; --_Idx) { - _First1[_Idx - 1] = _First2[_Idx - 1]; - } - } - - return _First1; - } -#endif // _HAS_CXX20 - - _CSTD memmove(_First1, _First2, _Count * sizeof(_Elem)); -#endif // ^^^ !_HAS_MEMCPY_MEMMOVE_INTRINSICS ^^^ - - return _First1; - } - - // For compare/length/find/assign, we can't uniformly call CRT functions (or their builtin versions). - // 8-bit: memcmp/strlen/memchr/memset; 16-bit: wmemcmp/wcslen/wmemchr/wmemset; 32-bit: nonexistent - - _NODISCARD static _CONSTEXPR17 int compare(_In_reads_(_Count) const _Elem* _First1, - _In_reads_(_Count) const _Elem* _First2, size_t _Count) noexcept /* strengthened */ { - // compare [_First1, _First1 + _Count) with [_First2, ...) - for (; 0 < _Count; --_Count, ++_First1, ++_First2) { - if (*_First1 != *_First2) { - return *_First1 < *_First2 ? -1 : +1; - } - } - - return 0; - } - - _NODISCARD static _CONSTEXPR17 size_t length(_In_z_ const _Elem* _First) noexcept /* strengthened */ { - // find length of null-terminated sequence - size_t _Count = 0; - while (*_First != _Elem()) { - ++_Count; - ++_First; - } - - return _Count; - } - - _NODISCARD static _CONSTEXPR17 const _Elem* find( - _In_reads_(_Count) const _Elem* _First, size_t _Count, const _Elem& _Ch) noexcept /* strengthened */ { - // look for _Ch in [_First, _First + _Count) - for (; 0 < _Count; --_Count, ++_First) { - if (*_First == _Ch) { - return _First; - } - } - - return nullptr; - } - - static _CONSTEXPR20 _Elem* assign( - _Out_writes_all_(_Count) _Elem* const _First, size_t _Count, const _Elem _Ch) noexcept /* strengthened */ { - // assign _Count * _Ch to [_First, ...) - for (_Elem* _Next = _First; _Count > 0; --_Count, ++_Next) { - *_Next = _Ch; - } - - return _First; - } - - static _CONSTEXPR17 void assign(_Elem& _Left, const _Elem& _Right) noexcept { - _Left = _Right; - } - - _NODISCARD static constexpr bool eq(const _Elem _Left, const _Elem _Right) noexcept { - return _Left == _Right; - } - - _NODISCARD static constexpr bool lt(const _Elem _Left, const _Elem _Right) noexcept { - return _Left < _Right; - } - - _NODISCARD static constexpr _Elem to_char_type(const int_type _Meta) noexcept { - return static_cast<_Elem>(_Meta); - } - - _NODISCARD static constexpr int_type to_int_type(const _Elem _Ch) noexcept { - return static_cast(_Ch); - } - - _NODISCARD static constexpr bool eq_int_type(const int_type _Left, const int_type _Right) noexcept { - return _Left == _Right; - } - - _NODISCARD static constexpr int_type not_eof(const int_type _Meta) noexcept { - return _Meta != eof() ? _Meta : !eof(); - } - - _NODISCARD static constexpr int_type eof() noexcept { - return static_cast(EOF); - } -}; - -template -struct _WChar_traits : private _Char_traits<_Elem, unsigned short> { - // char_traits for the char16_t-likes: char16_t, wchar_t, unsigned short -private: - using _Primary_char_traits = _Char_traits<_Elem, unsigned short>; - -public: - using char_type = _Elem; - using int_type = unsigned short; - using pos_type = streampos; - using off_type = streamoff; - using state_type = mbstate_t; -#if _HAS_CXX20 - using comparison_category = strong_ordering; -#endif // _HAS_CXX20 - - using _Primary_char_traits::_Copy_s; - using _Primary_char_traits::copy; - using _Primary_char_traits::move; - - _NODISCARD static _CONSTEXPR17 int compare(_In_reads_(_Count) const _Elem* const _First1, - _In_reads_(_Count) const _Elem* const _First2, const size_t _Count) noexcept /* strengthened */ { - // compare [_First1, _First1 + _Count) with [_First2, ...) -#if _HAS_CXX17 - if constexpr (is_same_v<_Elem, wchar_t>) { - return __builtin_wmemcmp(_First1, _First2, _Count); - } else { - return _Primary_char_traits::compare(_First1, _First2, _Count); - } -#else // ^^^ _HAS_CXX17 / !_HAS_CXX17 vvv - return _CSTD wmemcmp( - reinterpret_cast(_First1), reinterpret_cast(_First2), _Count); -#endif // ^^^ !_HAS_CXX17 ^^^ - } - - _NODISCARD static _CONSTEXPR17 size_t length(_In_z_ const _Elem* _First) noexcept /* strengthened */ { - // find length of null-terminated sequence -#if _HAS_CXX17 - if constexpr (is_same_v<_Elem, wchar_t>) { - return __builtin_wcslen(_First); - } else { - return _Primary_char_traits::length(_First); - } -#else // ^^^ _HAS_CXX17 / !_HAS_CXX17 vvv - return _CSTD wcslen(reinterpret_cast(_First)); -#endif // ^^^ !_HAS_CXX17 ^^^ - } - - _NODISCARD static _CONSTEXPR17 const _Elem* find( - _In_reads_(_Count) const _Elem* _First, const size_t _Count, const _Elem& _Ch) noexcept /* strengthened */ { - // look for _Ch in [_First, _First + _Count) -#if _HAS_CXX17 - if constexpr (is_same_v<_Elem, wchar_t>) { - return __builtin_wmemchr(_First, _Ch, _Count); - } else { - return _Primary_char_traits::find(_First, _Count, _Ch); - } -#else // ^^^ _HAS_CXX17 / !_HAS_CXX17 vvv - return reinterpret_cast(_CSTD wmemchr(reinterpret_cast(_First), _Ch, _Count)); -#endif // ^^^ !_HAS_CXX17 ^^^ - } - - static _CONSTEXPR20 _Elem* assign( - _Out_writes_all_(_Count) _Elem* const _First, size_t _Count, const _Elem _Ch) noexcept /* strengthened */ { - // assign _Count * _Ch to [_First, ...) -#if _HAS_CXX20 - if (_STD is_constant_evaluated()) { - return _Primary_char_traits::assign(_First, _Count, _Ch); - } -#endif // _HAS_CXX20 - - return reinterpret_cast<_Elem*>(_CSTD wmemset(reinterpret_cast(_First), _Ch, _Count)); - } - - static _CONSTEXPR17 void assign(_Elem& _Left, const _Elem& _Right) noexcept { -#if _HAS_CXX20 - if (_STD is_constant_evaluated()) { - return _Primary_char_traits::assign(_Left, _Right); - } -#endif // _HAS_CXX20 - _Left = _Right; - } - - _NODISCARD static constexpr bool eq(const _Elem _Left, const _Elem _Right) noexcept { - return _Left == _Right; - } - - _NODISCARD static constexpr bool lt(const _Elem _Left, const _Elem _Right) noexcept { - return _Left < _Right; - } - - _NODISCARD static constexpr _Elem to_char_type(const int_type _Meta) noexcept { - return _Meta; - } - - _NODISCARD static constexpr int_type to_int_type(const _Elem _Ch) noexcept { - return _Ch; - } - - _NODISCARD static constexpr bool eq_int_type(const int_type _Left, const int_type _Right) noexcept { - return _Left == _Right; - } - - _NODISCARD static constexpr int_type not_eof(const int_type _Meta) noexcept { - return _Meta != eof() ? _Meta : static_cast(!eof()); - } - - _NODISCARD static constexpr int_type eof() noexcept { - return WEOF; - } -}; - -_EXPORT_STD template -struct char_traits : _Char_traits<_Elem, long> {}; // properties of a string or stream unknown element - -template <> -struct char_traits : _WChar_traits {}; - -template <> -struct char_traits : _Char_traits {}; - -template <> -struct char_traits : _WChar_traits {}; - -#ifdef _CRTBLD -template <> -struct char_traits : _WChar_traits {}; -#endif // defined(_CRTBLD) - -#if defined(__cpp_char8_t) && !defined(__clang__) && !defined(__EDG__) -#define _HAS_U8_INTRINSICS 1 -#else // ^^^ Use intrinsics for char8_t / don't use said intrinsics vvv -#define _HAS_U8_INTRINSICS 0 -#endif // Detect u8 intrinsics - -template -struct _Narrow_char_traits : private _Char_traits<_Elem, _Int_type> { - // Implement char_traits for narrow character types char and char8_t -private: - using _Primary_char_traits = _Char_traits<_Elem, _Int_type>; - -public: - using char_type = _Elem; - using int_type = _Int_type; - using pos_type = streampos; - using off_type = streamoff; - using state_type = mbstate_t; -#if _HAS_CXX20 - using comparison_category = strong_ordering; -#endif // _HAS_CXX20 - - using _Primary_char_traits::_Copy_s; - using _Primary_char_traits::copy; - using _Primary_char_traits::move; - - _NODISCARD static _CONSTEXPR17 int compare(_In_reads_(_Count) const _Elem* const _First1, - _In_reads_(_Count) const _Elem* const _First2, const size_t _Count) noexcept /* strengthened */ { - // compare [_First1, _First1 + _Count) with [_First2, ...) -#if _HAS_CXX17 - return __builtin_memcmp(_First1, _First2, _Count); -#else // ^^^ _HAS_CXX17 / !_HAS_CXX17 vvv - return _CSTD memcmp(_First1, _First2, _Count); -#endif // ^^^ !_HAS_CXX17 ^^^ - } - - _NODISCARD static _CONSTEXPR17 size_t length(_In_z_ const _Elem* const _First) noexcept /* strengthened */ { - // find length of null-terminated string -#if _HAS_CXX17 -#ifdef __cpp_char8_t - if constexpr (is_same_v<_Elem, char8_t>) { -#if _HAS_U8_INTRINSICS - return __builtin_u8strlen(_First); -#else // ^^^ use u8 intrinsics / no u8 intrinsics vvv - return _Primary_char_traits::length(_First); -#endif // ^^^ no u8 intrinsics ^^^ - } else -#endif // defined(__cpp_char8_t) - { - return __builtin_strlen(_First); - } -#else // ^^^ _HAS_CXX17 / !_HAS_CXX17 vvv - return _CSTD strlen(reinterpret_cast(_First)); -#endif // ^^^ !_HAS_CXX17 ^^^ - } - - _NODISCARD static _CONSTEXPR17 const _Elem* find(_In_reads_(_Count) const _Elem* const _First, const size_t _Count, - const _Elem& _Ch) noexcept /* strengthened */ { - // look for _Ch in [_First, _First + _Count) -#if _HAS_CXX17 -#ifdef __cpp_char8_t - if constexpr (is_same_v<_Elem, char8_t>) { -#if _HAS_U8_INTRINSICS - return __builtin_u8memchr(_First, _Ch, _Count); -#else // ^^^ use u8 intrinsics / no u8 intrinsics vvv - return _Primary_char_traits::find(_First, _Count, _Ch); -#endif // ^^^ no u8 intrinsics ^^^ - } else -#endif // defined(__cpp_char8_t) - { - return __builtin_char_memchr(_First, _Ch, _Count); - } -#else // ^^^ _HAS_CXX17 / !_HAS_CXX17 vvv - return static_cast(_CSTD memchr(_First, _Ch, _Count)); -#endif // ^^^ !_HAS_CXX17 ^^^ - } - - static _CONSTEXPR20 _Elem* assign( - _Out_writes_all_(_Count) _Elem* const _First, size_t _Count, const _Elem _Ch) noexcept /* strengthened */ { - // assign _Count * _Ch to [_First, ...) -#if _HAS_CXX20 - if (_STD is_constant_evaluated()) { - return _Primary_char_traits::assign(_First, _Count, _Ch); - } -#endif // _HAS_CXX20 - - return static_cast<_Elem*>(_CSTD memset(_First, _Ch, _Count)); - } - - static _CONSTEXPR17 void assign(_Elem& _Left, const _Elem& _Right) noexcept { -#if _HAS_CXX20 - if (_STD is_constant_evaluated()) { - return _Primary_char_traits::assign(_Left, _Right); - } -#endif // _HAS_CXX20 - _Left = _Right; - } - - _NODISCARD static constexpr bool eq(const _Elem _Left, const _Elem _Right) noexcept { - return _Left == _Right; - } - - _NODISCARD static constexpr bool lt(const _Elem _Left, const _Elem _Right) noexcept { - return static_cast(_Left) < static_cast(_Right); - } - - _NODISCARD static constexpr _Elem to_char_type(const int_type _Meta) noexcept { - return static_cast<_Elem>(_Meta); - } - - _NODISCARD static constexpr int_type to_int_type(const _Elem _Ch) noexcept { - return static_cast(_Ch); - } - - _NODISCARD static constexpr bool eq_int_type(const int_type _Left, const int_type _Right) noexcept { - return _Left == _Right; - } - - _NODISCARD static constexpr int_type not_eof(const int_type _Meta) noexcept { - return _Meta != eof() ? _Meta : !eof(); - } - - _NODISCARD static constexpr int_type eof() noexcept { - return static_cast(EOF); - } -}; - -#undef _HAS_U8_INTRINSICS -#undef _HAS_MEMCPY_MEMMOVE_INTRINSICS - -template <> -struct char_traits : _Narrow_char_traits {}; // properties of a string or stream char element - -#ifdef __cpp_char8_t -template <> -struct char_traits : _Narrow_char_traits {}; -#endif // defined(__cpp_char8_t) - -template -basic_ostream<_Elem, _Traits>& _Insert_string( - basic_ostream<_Elem, _Traits>& _Ostr, const _Elem* const _Data, const _SizeT _Size) { - // insert a character-type sequence into _Ostr as if through a basic_string copy - using _Ostr_t = basic_ostream<_Elem, _Traits>; - typename _Ostr_t::iostate _State = _Ostr_t::goodbit; - - _SizeT _Pad; - if (_Ostr.width() <= 0 || static_cast<_SizeT>(_Ostr.width()) <= _Size) { - _Pad = 0; - } else { - _Pad = static_cast<_SizeT>(_Ostr.width()) - _Size; - } - - const typename _Ostr_t::sentry _Ok(_Ostr); - - if (!_Ok) { - _State |= _Ostr_t::badbit; - } else { // state okay, insert characters - _TRY_IO_BEGIN - if ((_Ostr.flags() & _Ostr_t::adjustfield) != _Ostr_t::left) { - for (; 0 < _Pad; --_Pad) { // pad on left - if (_Traits::eq_int_type(_Traits::eof(), _Ostr.rdbuf()->sputc(_Ostr.fill()))) { - _State |= _Ostr_t::badbit; // insertion failed, quit - break; - } - } - } - - if (_State == _Ostr_t::goodbit - && _Ostr.rdbuf()->sputn(_Data, static_cast(_Size)) != static_cast(_Size)) { - _State |= _Ostr_t::badbit; - } else { - for (; 0 < _Pad; --_Pad) { // pad on right - if (_Traits::eq_int_type(_Traits::eof(), _Ostr.rdbuf()->sputc(_Ostr.fill()))) { - _State |= _Ostr_t::badbit; // insertion failed, quit - break; - } - } - } - - _Ostr.width(0); - _CATCH_IO_(_Ostr_t, _Ostr) - } - - _Ostr.setstate(_State); - return _Ostr; -} - -template -using _Traits_ch_t = typename _Traits::char_type; - -template -using _Traits_ptr_t = const typename _Traits::char_type*; - -template -constexpr bool _Traits_equal(_In_reads_(_Left_size) const _Traits_ptr_t<_Traits> _Left, const size_t _Left_size, - _In_reads_(_Right_size) const _Traits_ptr_t<_Traits> _Right, const size_t _Right_size) noexcept { - // compare [_Left, _Left + _Left_size) to [_Right, _Right + _Right_size) for equality using _Traits - return _Left_size == _Right_size && _Traits::compare(_Left, _Right, _Left_size) == 0; -} - -template -constexpr int _Traits_compare(_In_reads_(_Left_size) const _Traits_ptr_t<_Traits> _Left, const size_t _Left_size, - _In_reads_(_Right_size) const _Traits_ptr_t<_Traits> _Right, const size_t _Right_size) noexcept { - // compare [_Left, _Left + _Left_size) to [_Right, _Right + _Right_size) using _Traits - const int _Ans = _Traits::compare(_Left, _Right, (_STD min)(_Left_size, _Right_size)); - - if (_Ans != 0) { - return _Ans; - } - - if (_Left_size < _Right_size) { - return -1; - } - - if (_Left_size > _Right_size) { - return 1; - } - - return 0; -} - -template -constexpr size_t _Traits_find(_In_reads_(_Hay_size) const _Traits_ptr_t<_Traits> _Haystack, const size_t _Hay_size, - const size_t _Start_at, _In_reads_(_Needle_size) const _Traits_ptr_t<_Traits> _Needle, - const size_t _Needle_size) noexcept { - // search [_Haystack, _Haystack + _Hay_size) for [_Needle, _Needle + _Needle_size), at/after _Start_at - if (_Needle_size > _Hay_size || _Start_at > _Hay_size - _Needle_size) { - // xpos cannot exist, report failure - // N4950 [string.view.find]/3 says: - // 1. _Start_at <= xpos - // 2. xpos + _Needle_size <= _Hay_size; - // therefore: - // 3. _Needle_size <= _Hay_size (by 2) (checked above) - // 4. _Start_at + _Needle_size <= _Hay_size (substitute 1 into 2) - // 5. _Start_at <= _Hay_size - _Needle_size (4, move _Needle_size to other side) (also checked above) - return static_cast(-1); - } - - if (_Needle_size == 0) { // empty string always matches if xpos is possible - return _Start_at; - } - - const auto _Possible_matches_end = _Haystack + (_Hay_size - _Needle_size) + 1; - for (auto _Match_try = _Haystack + _Start_at;; ++_Match_try) { - _Match_try = _Traits::find(_Match_try, static_cast(_Possible_matches_end - _Match_try), *_Needle); - if (!_Match_try) { // didn't find first character; report failure - return static_cast(-1); - } - - if (_Traits::compare(_Match_try, _Needle, _Needle_size) == 0) { // found match - return static_cast(_Match_try - _Haystack); - } - } -} - -template -constexpr size_t _Traits_find_ch(_In_reads_(_Hay_size) const _Traits_ptr_t<_Traits> _Haystack, const size_t _Hay_size, - const size_t _Start_at, const _Traits_ch_t<_Traits> _Ch) noexcept { - // search [_Haystack, _Haystack + _Hay_size) for _Ch, at/after _Start_at - if (_Start_at < _Hay_size) { - const auto _Found_at = _Traits::find(_Haystack + _Start_at, _Hay_size - _Start_at, _Ch); - if (_Found_at) { - return static_cast(_Found_at - _Haystack); - } - } - - return static_cast(-1); // (npos) no match -} - -template -constexpr size_t _Traits_rfind(_In_reads_(_Hay_size) const _Traits_ptr_t<_Traits> _Haystack, const size_t _Hay_size, - const size_t _Start_at, _In_reads_(_Needle_size) const _Traits_ptr_t<_Traits> _Needle, - const size_t _Needle_size) noexcept { - // search [_Haystack, _Haystack + _Hay_size) for [_Needle, _Needle + _Needle_size) beginning before _Start_at - if (_Needle_size == 0) { - return (_STD min)(_Start_at, _Hay_size); // empty string always matches - } - - if (_Needle_size <= _Hay_size) { // room for match, look for it - for (auto _Match_try = _Haystack + (_STD min)(_Start_at, _Hay_size - _Needle_size);; --_Match_try) { - if (_Traits::eq(*_Match_try, *_Needle) && _Traits::compare(_Match_try, _Needle, _Needle_size) == 0) { - return static_cast(_Match_try - _Haystack); // found a match - } - - if (_Match_try == _Haystack) { - break; // at beginning, no more chance for match - } - } - } - - return static_cast(-1); // no match -} - -template -constexpr size_t _Traits_rfind_ch(_In_reads_(_Hay_size) const _Traits_ptr_t<_Traits> _Haystack, const size_t _Hay_size, - const size_t _Start_at, const _Traits_ch_t<_Traits> _Ch) noexcept { - // search [_Haystack, _Haystack + _Hay_size) for _Ch before _Start_at - if (_Hay_size != 0) { // room for match, look for it - for (auto _Match_try = _Haystack + (_STD min)(_Start_at, _Hay_size - 1);; --_Match_try) { - if (_Traits::eq(*_Match_try, _Ch)) { - return static_cast(_Match_try - _Haystack); // found a match - } - - if (_Match_try == _Haystack) { - break; // at beginning, no more chance for match - } - } - } - - return static_cast(-1); // no match -} - -template ::value> -class _String_bitmap { // _String_bitmap for character types -public: - constexpr bool _Mark(const _Elem* _First, const _Elem* const _Last) noexcept { - // mark this bitmap such that the characters in [_First, _Last) are intended to match - // returns whether all inputs can be placed in the bitmap - for (; _First != _Last; ++_First) { - _Matches[static_cast(*_First)] = true; - } - - return true; - } - - constexpr bool _Match(const _Elem _Ch) const noexcept { // test if _Ch is in the bitmap - return _Matches[static_cast(_Ch)]; - } - -private: - bool _Matches[256] = {}; -}; - -template -class _String_bitmap<_Elem, false> { // _String_bitmap for wchar_t/unsigned short/char16_t/char32_t/etc. types -public: - static_assert(is_unsigned_v<_Elem>, - "Standard char_traits is only provided for char, wchar_t, char16_t, and char32_t. See N4950 [char.traits]. " - "Visual C++ accepts other unsigned integral types as an extension."); - - constexpr bool _Mark(const _Elem* _First, const _Elem* const _Last) noexcept { - // mark this bitmap such that the characters in [_First, _Last) are intended to match - // returns whether all inputs can be placed in the bitmap - for (; _First != _Last; ++_First) { - const auto _Ch = *_First; - if (_Ch >= 256U) { - return false; - } - - _Matches[static_cast(_Ch)] = true; - } - - return true; - } - - constexpr bool _Match(const _Elem _Ch) const noexcept { // test if _Ch is in the bitmap - return _Ch < 256U && _Matches[_Ch]; - } - -private: - bool _Matches[256] = {}; -}; - -template > -constexpr size_t _Traits_find_first_of(_In_reads_(_Hay_size) const _Traits_ptr_t<_Traits> _Haystack, - const size_t _Hay_size, const size_t _Start_at, _In_reads_(_Needle_size) const _Traits_ptr_t<_Traits> _Needle, - const size_t _Needle_size) noexcept { - // in [_Haystack, _Haystack + _Hay_size), look for one of [_Needle, _Needle + _Needle_size), at/after _Start_at - if (_Needle_size != 0 && _Start_at < _Hay_size) { // room for match, look for it - if constexpr (_Special) { - _String_bitmap _Matches; - if (!_Matches._Mark(_Needle, _Needle + _Needle_size)) { // couldn't put one of the characters into the - // bitmap, fall back to the serial algorithm - return _Traits_find_first_of<_Traits, false>(_Haystack, _Hay_size, _Start_at, _Needle, _Needle_size); - } - - const auto _End = _Haystack + _Hay_size; - for (auto _Match_try = _Haystack + _Start_at; _Match_try < _End; ++_Match_try) { - if (_Matches._Match(*_Match_try)) { - return static_cast(_Match_try - _Haystack); // found a match - } - } - } else { - const auto _End = _Haystack + _Hay_size; - for (auto _Match_try = _Haystack + _Start_at; _Match_try < _End; ++_Match_try) { - if (_Traits::find(_Needle, _Needle_size, *_Match_try)) { - return static_cast(_Match_try - _Haystack); // found a match - } - } - } - } - - return static_cast(-1); // no match -} - -template > -constexpr size_t _Traits_find_last_of(_In_reads_(_Hay_size) const _Traits_ptr_t<_Traits> _Haystack, - const size_t _Hay_size, const size_t _Start_at, _In_reads_(_Needle_size) const _Traits_ptr_t<_Traits> _Needle, - const size_t _Needle_size) noexcept { - // in [_Haystack, _Haystack + _Hay_size), look for last of [_Needle, _Needle + _Needle_size), before _Start_at - if (_Needle_size != 0 && _Hay_size != 0) { // worth searching, do it - if constexpr (_Special) { - _String_bitmap _Matches; - if (!_Matches._Mark(_Needle, _Needle + _Needle_size)) { // couldn't put one of the characters into the - // bitmap, fall back to the serial algorithm - return _Traits_find_last_of<_Traits, false>(_Haystack, _Hay_size, _Start_at, _Needle, _Needle_size); - } - - for (auto _Match_try = _Haystack + (_STD min)(_Start_at, _Hay_size - 1);; --_Match_try) { - if (_Matches._Match(*_Match_try)) { - return static_cast(_Match_try - _Haystack); // found a match - } - - if (_Match_try == _Haystack) { - break; // at beginning, no more chance for match - } - } - } else { - for (auto _Match_try = _Haystack + (_STD min)(_Start_at, _Hay_size - 1);; --_Match_try) { - if (_Traits::find(_Needle, _Needle_size, *_Match_try)) { - return static_cast(_Match_try - _Haystack); // found a match - } - - if (_Match_try == _Haystack) { - break; // at beginning, no more chance for match - } - } - } - } - - return static_cast(-1); // no match -} - -template > -constexpr size_t _Traits_find_first_not_of(_In_reads_(_Hay_size) const _Traits_ptr_t<_Traits> _Haystack, - const size_t _Hay_size, const size_t _Start_at, _In_reads_(_Needle_size) const _Traits_ptr_t<_Traits> _Needle, - const size_t _Needle_size) noexcept { - // in [_Haystack, _Haystack + _Hay_size), look for none of [_Needle, _Needle + _Needle_size), at/after _Start_at - if (_Start_at < _Hay_size) { // room for match, look for it - if constexpr (_Special) { - _String_bitmap _Matches; - if (!_Matches._Mark(_Needle, _Needle + _Needle_size)) { // couldn't put one of the characters into the - // bitmap, fall back to the serial algorithm - return _Traits_find_first_not_of<_Traits, false>( - _Haystack, _Hay_size, _Start_at, _Needle, _Needle_size); - } - - const auto _End = _Haystack + _Hay_size; - for (auto _Match_try = _Haystack + _Start_at; _Match_try < _End; ++_Match_try) { - if (!_Matches._Match(*_Match_try)) { - return static_cast(_Match_try - _Haystack); // found a match - } - } - } else { - const auto _End = _Haystack + _Hay_size; - for (auto _Match_try = _Haystack + _Start_at; _Match_try < _End; ++_Match_try) { - if (!_Traits::find(_Needle, _Needle_size, *_Match_try)) { - return static_cast(_Match_try - _Haystack); // found a match - } - } - } - } - - return static_cast(-1); // no match -} - -template -constexpr size_t _Traits_find_not_ch(_In_reads_(_Hay_size) const _Traits_ptr_t<_Traits> _Haystack, - const size_t _Hay_size, const size_t _Start_at, const _Traits_ch_t<_Traits> _Ch) noexcept { - // search [_Haystack, _Haystack + _Hay_size) for any value other than _Ch, at/after _Start_at - if (_Start_at < _Hay_size) { // room for match, look for it - const auto _End = _Haystack + _Hay_size; - for (auto _Match_try = _Haystack + _Start_at; _Match_try < _End; ++_Match_try) { - if (!_Traits::eq(*_Match_try, _Ch)) { - return static_cast(_Match_try - _Haystack); // found a match - } - } - } - - return static_cast(-1); // no match -} - -template > -constexpr size_t _Traits_find_last_not_of(_In_reads_(_Hay_size) const _Traits_ptr_t<_Traits> _Haystack, - const size_t _Hay_size, const size_t _Start_at, _In_reads_(_Needle_size) const _Traits_ptr_t<_Traits> _Needle, - const size_t _Needle_size) noexcept { - // in [_Haystack, _Haystack + _Hay_size), look for none of [_Needle, _Needle + _Needle_size), before _Start_at - if (_Hay_size != 0) { // worth searching, do it - if constexpr (_Special) { - _String_bitmap _Matches; - if (!_Matches._Mark(_Needle, _Needle + _Needle_size)) { // couldn't put one of the characters into the - // bitmap, fall back to the serial algorithm - return _Traits_find_last_not_of<_Traits, false>(_Haystack, _Hay_size, _Start_at, _Needle, _Needle_size); - } - - for (auto _Match_try = _Haystack + (_STD min)(_Start_at, _Hay_size - 1);; --_Match_try) { - if (!_Matches._Match(*_Match_try)) { - return static_cast(_Match_try - _Haystack); // found a match - } - - if (_Match_try == _Haystack) { - break; // at beginning, no more chance for match - } - } - } else { - for (auto _Match_try = _Haystack + (_STD min)(_Start_at, _Hay_size - 1);; --_Match_try) { - if (!_Traits::find(_Needle, _Needle_size, *_Match_try)) { - return static_cast(_Match_try - _Haystack); // found a match - } - - if (_Match_try == _Haystack) { - break; // at beginning, no more chance for match - } - } - } - } - - return static_cast(-1); // no match -} - -template -constexpr size_t _Traits_rfind_not_ch(_In_reads_(_Hay_size) const _Traits_ptr_t<_Traits> _Haystack, - const size_t _Hay_size, const size_t _Start_at, const _Traits_ch_t<_Traits> _Ch) noexcept { - // search [_Haystack, _Haystack + _Hay_size) for any value other than _Ch before _Start_at - if (_Hay_size != 0) { // room for match, look for it - for (auto _Match_try = _Haystack + (_STD min)(_Start_at, _Hay_size - 1);; --_Match_try) { - if (!_Traits::eq(*_Match_try, _Ch)) { - return static_cast(_Match_try - _Haystack); // found a match - } - - if (_Match_try == _Haystack) { - break; // at beginning, no more chance for match - } - } - } - - return static_cast(-1); // no match -} - -template -constexpr bool _Is_EcharT = _Is_any_of_v<_Ty, char, wchar_t, -#ifdef __cpp_char8_t - char8_t, -#endif // defined(__cpp_char8_t) - char16_t, char32_t>; - -#if _HAS_CXX17 -_EXPORT_STD template > -class basic_string_view; - -template -class _String_view_iterator { -public: -#if _HAS_CXX20 - using iterator_concept = contiguous_iterator_tag; -#endif // _HAS_CXX20 - using iterator_category = random_access_iterator_tag; - using value_type = typename _Traits::char_type; - using difference_type = ptrdiff_t; - using pointer = const value_type*; - using reference = const value_type&; - - constexpr _String_view_iterator() noexcept = default; - -private: - friend basic_string_view; - -#if _ITERATOR_DEBUG_LEVEL >= 1 - constexpr _String_view_iterator(const pointer _Data, const size_t _Size, const size_t _Off) noexcept - : _Mydata(_Data), _Mysize(_Size), _Myoff(_Off) {} -#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv - constexpr explicit _String_view_iterator(const pointer _Ptr) noexcept : _Myptr(_Ptr) {} -#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ - -public: - _NODISCARD constexpr reference operator*() const noexcept { -#if _ITERATOR_DEBUG_LEVEL >= 1 - _STL_VERIFY(_Mydata, "cannot dereference value-initialized string_view iterator"); - _STL_VERIFY(_Myoff < _Mysize, "cannot dereference end string_view iterator"); - return _Mydata[_Myoff]; -#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv - return *_Myptr; -#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ - } - - _NODISCARD constexpr pointer operator->() const noexcept { -#if _ITERATOR_DEBUG_LEVEL >= 1 - _STL_VERIFY(_Mydata, "cannot dereference value-initialized string_view iterator"); - _STL_VERIFY(_Myoff < _Mysize, "cannot dereference end string_view iterator"); - return _Mydata + _Myoff; -#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv - return _Myptr; -#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ - } - - constexpr _String_view_iterator& operator++() noexcept { -#if _ITERATOR_DEBUG_LEVEL >= 1 - _STL_VERIFY(_Mydata, "cannot increment value-initialized string_view iterator"); - _STL_VERIFY(_Myoff < _Mysize, "cannot increment string_view iterator past end"); - ++_Myoff; -#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv - ++_Myptr; -#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ - return *this; - } - - constexpr _String_view_iterator operator++(int) noexcept { - _String_view_iterator _Tmp{*this}; - ++*this; - return _Tmp; - } - - constexpr _String_view_iterator& operator--() noexcept { -#if _ITERATOR_DEBUG_LEVEL >= 1 - _STL_VERIFY(_Mydata, "cannot decrement value-initialized string_view iterator"); - _STL_VERIFY(_Myoff != 0, "cannot decrement string_view iterator before begin"); - --_Myoff; -#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv - --_Myptr; -#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ - return *this; - } - - constexpr _String_view_iterator operator--(int) noexcept { - _String_view_iterator _Tmp{*this}; - --*this; - return _Tmp; - } - - constexpr void _Verify_offset(const difference_type _Off) const noexcept { -#if _ITERATOR_DEBUG_LEVEL >= 1 - if (_Off != 0) { - _STL_VERIFY(_Mydata, "cannot seek value-initialized string_view iterator"); - } - - if (_Off < 0) { - _STL_VERIFY( - _Myoff >= size_t{0} - static_cast(_Off), "cannot seek string_view iterator before begin"); - } - - if (_Off > 0) { - _STL_VERIFY(_Mysize - _Myoff >= static_cast(_Off), "cannot seek string_view iterator after end"); - } -#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv - (void) _Off; -#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ - } - - constexpr _String_view_iterator& operator+=(const difference_type _Off) noexcept { -#if _ITERATOR_DEBUG_LEVEL >= 1 - _Verify_offset(_Off); - _Myoff += static_cast(_Off); -#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv - _Myptr += _Off; -#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ - - return *this; - } - - _NODISCARD constexpr _String_view_iterator operator+(const difference_type _Off) const noexcept { - _String_view_iterator _Tmp{*this}; - _Tmp += _Off; - return _Tmp; - } - - _NODISCARD_FRIEND constexpr _String_view_iterator operator+( - const difference_type _Off, _String_view_iterator _Right) noexcept { - _Right += _Off; - return _Right; - } - - constexpr _String_view_iterator& operator-=(const difference_type _Off) noexcept { -#if _ITERATOR_DEBUG_LEVEL >= 1 - if (_Off != 0) { - _STL_VERIFY(_Mydata, "cannot seek value-initialized string_view iterator"); - } - - if (_Off > 0) { - _STL_VERIFY(_Myoff >= static_cast(_Off), "cannot seek string_view iterator before begin"); - } - - if (_Off < 0) { - _STL_VERIFY(_Mysize - _Myoff >= size_t{0} - static_cast(_Off), - "cannot seek string_view iterator after end"); - } - - _Myoff -= static_cast(_Off); -#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv - _Myptr -= _Off; -#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ - - return *this; - } - - _NODISCARD constexpr _String_view_iterator operator-(const difference_type _Off) const noexcept { - _String_view_iterator _Tmp{*this}; - _Tmp -= _Off; - return _Tmp; - } - - _NODISCARD constexpr difference_type operator-(const _String_view_iterator& _Right) const noexcept { -#if _ITERATOR_DEBUG_LEVEL >= 1 - _STL_VERIFY(_Mydata == _Right._Mydata && _Mysize == _Right._Mysize, - "cannot subtract incompatible string_view iterators"); - return static_cast(_Myoff - _Right._Myoff); -#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv - return _Myptr - _Right._Myptr; -#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ - } - - _NODISCARD constexpr reference operator[](const difference_type _Off) const noexcept { - return *(*this + _Off); - } - - _NODISCARD constexpr bool operator==(const _String_view_iterator& _Right) const noexcept { -#if _ITERATOR_DEBUG_LEVEL >= 1 - _STL_VERIFY(_Mydata == _Right._Mydata && _Mysize == _Right._Mysize, - "cannot compare incompatible string_view iterators for equality"); - return _Myoff == _Right._Myoff; -#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv - return _Myptr == _Right._Myptr; -#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ - } - -#if _HAS_CXX20 - _NODISCARD constexpr strong_ordering operator<=>(const _String_view_iterator& _Right) const noexcept { -#if _ITERATOR_DEBUG_LEVEL >= 1 - _STL_VERIFY(_Mydata == _Right._Mydata && _Mysize == _Right._Mysize, - "cannot compare incompatible string_view iterators"); - return _Myoff <=> _Right._Myoff; -#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv - return _Myptr <=> _Right._Myptr; -#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ - } -#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv - _NODISCARD constexpr bool operator!=(const _String_view_iterator& _Right) const noexcept { - return !(*this == _Right); - } - - _NODISCARD constexpr bool operator<(const _String_view_iterator& _Right) const noexcept { -#if _ITERATOR_DEBUG_LEVEL >= 1 - _STL_VERIFY(_Mydata == _Right._Mydata && _Mysize == _Right._Mysize, - "cannot compare incompatible string_view iterators"); - return _Myoff < _Right._Myoff; -#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv - return _Myptr < _Right._Myptr; -#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ - } - - _NODISCARD constexpr bool operator>(const _String_view_iterator& _Right) const noexcept { - return _Right < *this; - } - - _NODISCARD constexpr bool operator<=(const _String_view_iterator& _Right) const noexcept { - return !(_Right < *this); - } - - _NODISCARD constexpr bool operator>=(const _String_view_iterator& _Right) const noexcept { - return !(*this < _Right); - } -#endif // !_HAS_CXX20 - -#if _ITERATOR_DEBUG_LEVEL >= 1 - friend constexpr void _Verify_range(const _String_view_iterator& _First, const _String_view_iterator& _Last) { - _STL_VERIFY(_First._Mydata == _Last._Mydata && _First._Mysize == _Last._Mysize, - "string_view iterators in range are from different views"); - _STL_VERIFY(_First._Myoff <= _Last._Myoff, "string_view iterator range transposed"); - } -#endif // _ITERATOR_DEBUG_LEVEL >= 1 - - using _Prevent_inheriting_unwrap = _String_view_iterator; - - _NODISCARD constexpr pointer _Unwrapped() const noexcept { -#if _ITERATOR_DEBUG_LEVEL >= 1 - return _Mydata + _Myoff; -#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv - return _Myptr; -#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ - } - - static constexpr bool _Unwrap_when_unverified = _ITERATOR_DEBUG_LEVEL == 0; - - constexpr void _Seek_to(pointer _It) noexcept { -#if _ITERATOR_DEBUG_LEVEL >= 1 - _Myoff = static_cast(_It - _Mydata); -#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv - _Myptr = _It; -#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ - } - -private: -#if _ITERATOR_DEBUG_LEVEL >= 1 - pointer _Mydata = nullptr; - size_t _Mysize = 0; - size_t _Myoff = 0; -#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv - pointer _Myptr = nullptr; -#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ -}; - -#if _HAS_CXX20 -template -struct pointer_traits<_String_view_iterator<_Traits>> { - using pointer = _String_view_iterator<_Traits>; - using element_type = const pointer::value_type; - using difference_type = pointer::difference_type; - - _NODISCARD static constexpr element_type* to_address(const pointer& _Iter) noexcept { - return _Iter._Unwrapped(); - } -}; -#endif // _HAS_CXX20 - -#pragma warning(push) -// Invalid annotation: 'NullTerminated' property may only be used on buffers whose elements are of integral or pointer -// type -#pragma warning(disable : 6510) - -_EXPORT_STD template -class basic_string_view { // wrapper for any kind of contiguous character buffer -public: - static_assert(is_same_v<_Elem, typename _Traits::char_type>, - "Bad char_traits for basic_string_view; N4950 [string.view.template.general]/1 " - "\"The program is ill-formed if traits::char_type is not the same type as charT.\""); - - static_assert(!is_array_v<_Elem> && is_trivial_v<_Elem> && is_standard_layout_v<_Elem>, - "The character type of basic_string_view must be a non-array trivial standard-layout type. See N4950 " - "[strings.general]/1."); - - using traits_type = _Traits; - using value_type = _Elem; - using pointer = _Elem*; - using const_pointer = const _Elem*; - using reference = _Elem&; - using const_reference = const _Elem&; - using const_iterator = _String_view_iterator<_Traits>; - using iterator = const_iterator; - using const_reverse_iterator = _STD reverse_iterator; - using reverse_iterator = const_reverse_iterator; - using size_type = size_t; - using difference_type = ptrdiff_t; - - static constexpr auto npos{static_cast(-1)}; - - constexpr basic_string_view() noexcept : _Mydata(), _Mysize(0) {} - - constexpr basic_string_view(const basic_string_view&) noexcept = default; - constexpr basic_string_view& operator=(const basic_string_view&) noexcept = default; - - /* implicit */ constexpr basic_string_view(_In_z_ const const_pointer _Ntcts) noexcept // strengthened - : _Mydata(_Ntcts), _Mysize(_Traits::length(_Ntcts)) {} - -#if _HAS_CXX23 - basic_string_view(nullptr_t) = delete; -#endif // _HAS_CXX23 - - constexpr basic_string_view( - _In_reads_(_Count) const const_pointer _Cts, const size_type _Count) noexcept // strengthened - : _Mydata(_Cts), _Mysize(_Count) { -#if _CONTAINER_DEBUG_LEVEL > 0 - _STL_VERIFY(_Count == 0 || _Cts, "non-zero size null string_view"); -#endif // _CONTAINER_DEBUG_LEVEL > 0 - } - -#if _HAS_CXX20 - template _Sent> - requires (is_same_v, _Elem> && !is_convertible_v<_Sent, size_type>) - constexpr basic_string_view(_Iter _First, _Sent _Last) noexcept(noexcept(_Last - _First)) // strengthened - : _Mydata(_STD to_address(_First)), _Mysize(static_cast(_Last - _First)) {} - -#if _HAS_CXX23 - // clang-format off - template - requires (!same_as, basic_string_view> - && _RANGES contiguous_range<_Range> - && _RANGES sized_range<_Range> - && same_as<_RANGES range_value_t<_Range>, _Elem> - && !is_convertible_v<_Range, const _Elem*> - && !requires(remove_cvref_t<_Range>& _Rng) { - _Rng.operator _STD basic_string_view<_Elem, _Traits>(); - }) - constexpr explicit basic_string_view(_Range&& _Rng) noexcept( - noexcept(_RANGES data(_Rng)) && noexcept(_RANGES size(_Rng))) // strengthened - : _Mydata(_RANGES data(_Rng)), _Mysize(static_cast(_RANGES size(_Rng))) {} - // clang-format on -#endif // _HAS_CXX23 -#endif // _HAS_CXX20 - - _NODISCARD constexpr const_iterator begin() const noexcept { -#if _ITERATOR_DEBUG_LEVEL >= 1 - return const_iterator(_Mydata, _Mysize, 0); -#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv - return const_iterator(_Mydata); -#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ - } - - _NODISCARD constexpr const_iterator end() const noexcept { -#if _ITERATOR_DEBUG_LEVEL >= 1 - return const_iterator(_Mydata, _Mysize, _Mysize); -#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv - return const_iterator(_Mydata + _Mysize); -#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ - } - - _NODISCARD constexpr const_iterator cbegin() const noexcept { - return begin(); - } - - _NODISCARD constexpr const_iterator cend() const noexcept { - return end(); - } - - _NODISCARD constexpr const_reverse_iterator rbegin() const noexcept { - return const_reverse_iterator{end()}; - } - - _NODISCARD constexpr const_reverse_iterator rend() const noexcept { - return const_reverse_iterator{begin()}; - } - - _NODISCARD constexpr const_reverse_iterator crbegin() const noexcept { - return rbegin(); - } - - _NODISCARD constexpr const_reverse_iterator crend() const noexcept { - return rend(); - } - - constexpr const_pointer _Unchecked_begin() const noexcept { - return _Mydata; - } - - constexpr const_pointer _Unchecked_end() const noexcept { - return _Mydata + _Mysize; - } - - _NODISCARD constexpr size_type size() const noexcept { - return _Mysize; - } - - _NODISCARD constexpr size_type length() const noexcept { - return _Mysize; - } - - _NODISCARD constexpr bool empty() const noexcept { - return _Mysize == 0; - } - - _NODISCARD constexpr const_pointer data() const noexcept { - return _Mydata; - } - - _NODISCARD constexpr size_type max_size() const noexcept { - // bound to PTRDIFF_MAX to make end() - begin() well defined (also makes room for npos) - // bound to static_cast(-1) / sizeof(_Elem) by address space limits - return (_STD min)(static_cast(PTRDIFF_MAX), static_cast(-1) / sizeof(_Elem)); - } - - _NODISCARD constexpr const_reference operator[](const size_type _Off) const noexcept /* strengthened */ { -#if _CONTAINER_DEBUG_LEVEL > 0 - _STL_VERIFY(_Off < _Mysize, "string_view subscript out of range"); -#endif // _CONTAINER_DEBUG_LEVEL > 0 - return _Mydata[_Off]; - } - - _NODISCARD constexpr const_reference at(const size_type _Off) const { - // get the character at _Off or throw if that is out of range - _Check_offset_exclusive(_Off); - return _Mydata[_Off]; - } - - _NODISCARD constexpr const_reference front() const noexcept /* strengthened */ { -#if _CONTAINER_DEBUG_LEVEL > 0 - _STL_VERIFY(_Mysize != 0, "cannot call front on empty string_view"); -#endif // _CONTAINER_DEBUG_LEVEL > 0 - return _Mydata[0]; - } - - _NODISCARD constexpr const_reference back() const noexcept /* strengthened */ { -#if _CONTAINER_DEBUG_LEVEL > 0 - _STL_VERIFY(_Mysize != 0, "cannot call back on empty string_view"); -#endif // _CONTAINER_DEBUG_LEVEL > 0 - return _Mydata[_Mysize - 1]; - } - - constexpr void remove_prefix(const size_type _Count) noexcept /* strengthened */ { -#if _CONTAINER_DEBUG_LEVEL > 0 - _STL_VERIFY(_Mysize >= _Count, "cannot remove prefix longer than total size"); -#endif // _CONTAINER_DEBUG_LEVEL > 0 - _Mydata += _Count; - _Mysize -= _Count; - } - - constexpr void remove_suffix(const size_type _Count) noexcept /* strengthened */ { -#if _CONTAINER_DEBUG_LEVEL > 0 - _STL_VERIFY(_Mysize >= _Count, "cannot remove suffix longer than total size"); -#endif // _CONTAINER_DEBUG_LEVEL > 0 - _Mysize -= _Count; - } - - constexpr void swap(basic_string_view& _Other) noexcept { - const basic_string_view _Tmp{_Other}; // note: std::swap is not constexpr before C++20 - _Other = *this; - *this = _Tmp; - } - - _CONSTEXPR20 size_type copy( - _Out_writes_(_Count) _Elem* const _Ptr, size_type _Count, const size_type _Off = 0) const { - // copy [_Off, _Off + Count) to [_Ptr, _Ptr + _Count) - _Check_offset(_Off); - _Count = _Clamp_suffix_size(_Off, _Count); - _Traits::copy(_Ptr, _Mydata + _Off, _Count); - return _Count; - } - - _Pre_satisfies_(_Dest_size >= _Count) _CONSTEXPR20 size_type - _Copy_s(_Out_writes_all_(_Dest_size) _Elem* const _Dest, const size_type _Dest_size, size_type _Count, - const size_type _Off = 0) const { - // copy [_Off, _Off + _Count) to [_Dest, _Dest + _Count) - _Check_offset(_Off); - _Count = _Clamp_suffix_size(_Off, _Count); - _Traits::_Copy_s(_Dest, _Dest_size, _Mydata + _Off, _Count); - return _Count; - } - - _NODISCARD constexpr basic_string_view substr(const size_type _Off = 0, size_type _Count = npos) const { - // return a new basic_string_view moved forward by _Off and trimmed to _Count elements - _Check_offset(_Off); - _Count = _Clamp_suffix_size(_Off, _Count); - return basic_string_view(_Mydata + _Off, _Count); - } - - constexpr bool _Equal(const basic_string_view _Right) const noexcept { - return _Traits_equal<_Traits>(_Mydata, _Mysize, _Right._Mydata, _Right._Mysize); - } - - _NODISCARD constexpr int compare(const basic_string_view _Right) const noexcept { - return _Traits_compare<_Traits>(_Mydata, _Mysize, _Right._Mydata, _Right._Mysize); - } - - _NODISCARD constexpr int compare(const size_type _Off, const size_type _Nx, const basic_string_view _Right) const { - // compare [_Off, _Off + _Nx) with _Right - return substr(_Off, _Nx).compare(_Right); - } - - _NODISCARD constexpr int compare(const size_type _Off, const size_type _Nx, const basic_string_view _Right, - const size_type _Roff, const size_type _Count) const { - // compare [_Off, _Off + _Nx) with _Right [_Roff, _Roff + _Count) - return substr(_Off, _Nx).compare(_Right.substr(_Roff, _Count)); - } - - _NODISCARD constexpr int compare(_In_z_ const _Elem* const _Ptr) const noexcept /* strengthened */ { - // compare [0, _Mysize) with [_Ptr, ) - return compare(basic_string_view(_Ptr)); - } - - _NODISCARD constexpr int compare(const size_type _Off, const size_type _Nx, _In_z_ const _Elem* const _Ptr) const { - // compare [_Off, _Off + _Nx) with [_Ptr, ) - return substr(_Off, _Nx).compare(basic_string_view(_Ptr)); - } - - _NODISCARD constexpr int compare(const size_type _Off, const size_type _Nx, - _In_reads_(_Count) const _Elem* const _Ptr, const size_type _Count) const { - // compare [_Off, _Off + _Nx) with [_Ptr, _Ptr + _Count) - return substr(_Off, _Nx).compare(basic_string_view(_Ptr, _Count)); - } - -#if _HAS_CXX20 - _NODISCARD constexpr bool starts_with(const basic_string_view _Right) const noexcept { - const auto _Rightsize = _Right._Mysize; - if (_Mysize < _Rightsize) { - return false; - } - return _Traits::compare(_Mydata, _Right._Mydata, _Rightsize) == 0; - } - - _NODISCARD constexpr bool starts_with(const _Elem _Right) const noexcept { - return !empty() && _Traits::eq(front(), _Right); - } - - _NODISCARD constexpr bool starts_with(const _Elem* const _Right) const noexcept /* strengthened */ { - return starts_with(basic_string_view(_Right)); - } - - _NODISCARD constexpr bool ends_with(const basic_string_view _Right) const noexcept { - const auto _Rightsize = _Right._Mysize; - if (_Mysize < _Rightsize) { - return false; - } - return _Traits::compare(_Mydata + (_Mysize - _Rightsize), _Right._Mydata, _Rightsize) == 0; - } - - _NODISCARD constexpr bool ends_with(const _Elem _Right) const noexcept { - return !empty() && _Traits::eq(back(), _Right); - } - - _NODISCARD constexpr bool ends_with(const _Elem* const _Right) const noexcept /* strengthened */ { - return ends_with(basic_string_view(_Right)); - } -#endif // _HAS_CXX20 - -#if _HAS_CXX23 - _NODISCARD constexpr bool contains(const basic_string_view _Right) const noexcept { - return find(_Right) != npos; - } - - _NODISCARD constexpr bool contains(const _Elem _Right) const noexcept { - return find(_Right) != npos; - } - - _NODISCARD constexpr bool contains(const _Elem* const _Right) const noexcept /* strengthened */ { - return find(_Right) != npos; - } -#endif // _HAS_CXX23 - - _NODISCARD constexpr size_type find(const basic_string_view _Right, const size_type _Off = 0) const noexcept { - // look for _Right beginning at or after _Off - return _Traits_find<_Traits>(_Mydata, _Mysize, _Off, _Right._Mydata, _Right._Mysize); - } - - _NODISCARD constexpr size_type find(const _Elem _Ch, const size_type _Off = 0) const noexcept { - // look for _Ch at or after _Off - return _Traits_find_ch<_Traits>(_Mydata, _Mysize, _Off, _Ch); - } - - _NODISCARD constexpr size_type find(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, - const size_type _Count) const noexcept /* strengthened */ { - // look for [_Ptr, _Ptr + _Count) beginning at or after _Off - return _Traits_find<_Traits>(_Mydata, _Mysize, _Off, _Ptr, _Count); - } - - _NODISCARD constexpr size_type find(_In_z_ const _Elem* const _Ptr, const size_type _Off = 0) const noexcept - /* strengthened */ { - // look for [_Ptr, ) beginning at or after _Off - return _Traits_find<_Traits>(_Mydata, _Mysize, _Off, _Ptr, _Traits::length(_Ptr)); - } - - _NODISCARD constexpr size_type rfind(const basic_string_view _Right, const size_type _Off = npos) const noexcept { - // look for _Right beginning before _Off - return _Traits_rfind<_Traits>(_Mydata, _Mysize, _Off, _Right._Mydata, _Right._Mysize); - } - - _NODISCARD constexpr size_type rfind(const _Elem _Ch, const size_type _Off = npos) const noexcept { - // look for _Ch before _Off - return _Traits_rfind_ch<_Traits>(_Mydata, _Mysize, _Off, _Ch); - } - - _NODISCARD constexpr size_type rfind(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, - const size_type _Count) const noexcept /* strengthened */ { - // look for [_Ptr, _Ptr + _Count) beginning before _Off - return _Traits_rfind<_Traits>(_Mydata, _Mysize, _Off, _Ptr, _Count); - } - - _NODISCARD constexpr size_type rfind(_In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept - /* strengthened */ { - // look for [_Ptr, ) beginning before _Off - return _Traits_rfind<_Traits>(_Mydata, _Mysize, _Off, _Ptr, _Traits::length(_Ptr)); - } - - _NODISCARD constexpr size_type find_first_of(const basic_string_view _Right, - const size_type _Off = 0) const noexcept { // look for one of _Right at or after _Off - return _Traits_find_first_of<_Traits>(_Mydata, _Mysize, _Off, _Right._Mydata, _Right._Mysize); - } - - _NODISCARD constexpr size_type find_first_of(const _Elem _Ch, const size_type _Off = 0) const noexcept { - // look for _Ch at or after _Off - return _Traits_find_ch<_Traits>(_Mydata, _Mysize, _Off, _Ch); - } - - _NODISCARD constexpr size_type find_first_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, - const size_type _Count) const noexcept /* strengthened */ { - // look for one of [_Ptr, _Ptr + _Count) at or after _Off - return _Traits_find_first_of<_Traits>(_Mydata, _Mysize, _Off, _Ptr, _Count); - } - - _NODISCARD constexpr size_type find_first_of( - _In_z_ const _Elem* const _Ptr, const size_type _Off = 0) const noexcept /* strengthened */ { - // look for one of [_Ptr, ) at or after _Off - return _Traits_find_first_of<_Traits>(_Mydata, _Mysize, _Off, _Ptr, _Traits::length(_Ptr)); - } - - _NODISCARD constexpr size_type find_last_of(const basic_string_view _Right, - const size_type _Off = npos) const noexcept { // look for one of _Right before _Off - return _Traits_find_last_of<_Traits>(_Mydata, _Mysize, _Off, _Right._Mydata, _Right._Mysize); - } - - _NODISCARD constexpr size_type find_last_of(const _Elem _Ch, const size_type _Off = npos) const noexcept { - // look for _Ch before _Off - return _Traits_rfind_ch<_Traits>(_Mydata, _Mysize, _Off, _Ch); - } - - _NODISCARD constexpr size_type find_last_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, - const size_type _Count) const noexcept /* strengthened */ { - // look for one of [_Ptr, _Ptr + _Count) before _Off - return _Traits_find_last_of<_Traits>(_Mydata, _Mysize, _Off, _Ptr, _Count); - } - - _NODISCARD constexpr size_type find_last_of( - _In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept /* strengthened */ { - // look for one of [_Ptr, ) before _Off - return _Traits_find_last_of<_Traits>(_Mydata, _Mysize, _Off, _Ptr, _Traits::length(_Ptr)); - } - - _NODISCARD constexpr size_type find_first_not_of(const basic_string_view _Right, - const size_type _Off = 0) const noexcept { // look for none of _Right at or after _Off - return _Traits_find_first_not_of<_Traits>(_Mydata, _Mysize, _Off, _Right._Mydata, _Right._Mysize); - } - - _NODISCARD constexpr size_type find_first_not_of(const _Elem _Ch, const size_type _Off = 0) const noexcept { - // look for any value other than _Ch at or after _Off - return _Traits_find_not_ch<_Traits>(_Mydata, _Mysize, _Off, _Ch); - } - - _NODISCARD constexpr size_type find_first_not_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, - const size_type _Count) const noexcept /* strengthened */ { - // look for none of [_Ptr, _Ptr + _Count) at or after _Off - return _Traits_find_first_not_of<_Traits>(_Mydata, _Mysize, _Off, _Ptr, _Count); - } - - _NODISCARD constexpr size_type find_first_not_of( - _In_z_ const _Elem* const _Ptr, const size_type _Off = 0) const noexcept /* strengthened */ { - // look for none of [_Ptr, ) at or after _Off - return _Traits_find_first_not_of<_Traits>(_Mydata, _Mysize, _Off, _Ptr, _Traits::length(_Ptr)); - } - - _NODISCARD constexpr size_type find_last_not_of(const basic_string_view _Right, - const size_type _Off = npos) const noexcept { // look for none of _Right before _Off - return _Traits_find_last_not_of<_Traits>(_Mydata, _Mysize, _Off, _Right._Mydata, _Right._Mysize); - } - - _NODISCARD constexpr size_type find_last_not_of(const _Elem _Ch, const size_type _Off = npos) const noexcept { - // look for any value other than _Ch before _Off - return _Traits_rfind_not_ch<_Traits>(_Mydata, _Mysize, _Off, _Ch); - } - - _NODISCARD constexpr size_type find_last_not_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, - const size_type _Count) const noexcept /* strengthened */ { - // look for none of [_Ptr, _Ptr + _Count) before _Off - return _Traits_find_last_not_of<_Traits>(_Mydata, _Mysize, _Off, _Ptr, _Count); - } - - _NODISCARD constexpr size_type find_last_not_of( - _In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept /* strengthened */ { - // look for none of [_Ptr, ) before _Off - return _Traits_find_last_not_of<_Traits>(_Mydata, _Mysize, _Off, _Ptr, _Traits::length(_Ptr)); - } - - _NODISCARD constexpr bool _Starts_with(const basic_string_view _View) const noexcept { - return _Mysize >= _View._Mysize && _Traits::compare(_Mydata, _View._Mydata, _View._Mysize) == 0; - } - -private: - constexpr void _Check_offset(const size_type _Off) const { // checks whether _Off is in the bounds of [0, size()] - if (_Mysize < _Off) { - _Xran(); - } - } - - constexpr void _Check_offset_exclusive(const size_type _Off) const { - // checks whether _Off is in the bounds of [0, size()) - if (_Mysize <= _Off) { - _Xran(); - } - } - - constexpr size_type _Clamp_suffix_size(const size_type _Off, const size_type _Size) const noexcept { - // trims _Size to the longest it can be assuming a string at/after _Off - return (_STD min)(_Size, _Mysize - _Off); - } - - [[noreturn]] static void _Xran() { - _Xout_of_range("invalid string_view position"); - } - - const_pointer _Mydata; - size_type _Mysize; -}; - -#pragma warning(pop) - -#if _HAS_CXX20 -template _Sent> -basic_string_view(_Iter, _Sent) -> basic_string_view>; - -#if _HAS_CXX23 -template <_RANGES contiguous_range _Range> -basic_string_view(_Range&&) -> basic_string_view<_RANGES range_value_t<_Range>>; -#endif // _HAS_CXX23 - -namespace ranges { - template - constexpr bool enable_view> = true; - template - constexpr bool enable_borrowed_range> = true; -} // namespace ranges -#endif // _HAS_CXX20 - -#if _HAS_CXX20 -_EXPORT_STD template -_NODISCARD constexpr bool operator==(const basic_string_view<_Elem, _Traits> _Lhs, - const type_identity_t> _Rhs) noexcept { - return _Lhs._Equal(_Rhs); -} - -template -struct _Get_comparison_category { - using type = weak_ordering; -}; - -template -struct _Get_comparison_category<_Traits, void_t> { - using type = _Traits::comparison_category; - - static_assert(_Is_any_of_v, - "N4971 [string.view.comparison]/4: Mandates: R denotes a comparison category type."); -}; - -template -using _Get_comparison_category_t = _Get_comparison_category<_Traits>::type; - -_EXPORT_STD template -_NODISCARD constexpr _Get_comparison_category_t<_Traits> operator<=>(const basic_string_view<_Elem, _Traits> _Lhs, - const type_identity_t> _Rhs) noexcept { - return static_cast<_Get_comparison_category_t<_Traits>>(_Lhs.compare(_Rhs) <=> 0); -} -#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv -template -_NODISCARD constexpr bool operator==( - const basic_string_view<_Elem, _Traits> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { - return _Lhs._Equal(_Rhs); -} - -template // TRANSITION, VSO-409326 -_NODISCARD constexpr bool operator==( - const _Identity_t> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { - return _Lhs._Equal(_Rhs); -} - -template // TRANSITION, VSO-409326 -_NODISCARD constexpr bool operator==( - const basic_string_view<_Elem, _Traits> _Lhs, const _Identity_t> _Rhs) noexcept { - return _Lhs._Equal(_Rhs); -} - -template -_NODISCARD constexpr bool operator!=( - const basic_string_view<_Elem, _Traits> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { - return !_Lhs._Equal(_Rhs); -} - -template // TRANSITION, VSO-409326 -_NODISCARD constexpr bool operator!=( - const _Identity_t> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { - return !_Lhs._Equal(_Rhs); -} - -template // TRANSITION, VSO-409326 -_NODISCARD constexpr bool operator!=( - const basic_string_view<_Elem, _Traits> _Lhs, const _Identity_t> _Rhs) noexcept { - return !_Lhs._Equal(_Rhs); -} - -template -_NODISCARD constexpr bool operator<( - const basic_string_view<_Elem, _Traits> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { - return _Lhs.compare(_Rhs) < 0; -} - -template // TRANSITION, VSO-409326 -_NODISCARD constexpr bool operator<( - const _Identity_t> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { - return _Lhs.compare(_Rhs) < 0; -} - -template // TRANSITION, VSO-409326 -_NODISCARD constexpr bool operator<( - const basic_string_view<_Elem, _Traits> _Lhs, const _Identity_t> _Rhs) noexcept { - return _Lhs.compare(_Rhs) < 0; -} - -template -_NODISCARD constexpr bool operator>( - const basic_string_view<_Elem, _Traits> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { - return _Lhs.compare(_Rhs) > 0; -} - -template // TRANSITION, VSO-409326 -_NODISCARD constexpr bool operator>( - const _Identity_t> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { - return _Lhs.compare(_Rhs) > 0; -} - -template // TRANSITION, VSO-409326 -_NODISCARD constexpr bool operator>( - const basic_string_view<_Elem, _Traits> _Lhs, const _Identity_t> _Rhs) noexcept { - return _Lhs.compare(_Rhs) > 0; -} - -template -_NODISCARD constexpr bool operator<=( - const basic_string_view<_Elem, _Traits> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { - return _Lhs.compare(_Rhs) <= 0; -} - -template // TRANSITION, VSO-409326 -_NODISCARD constexpr bool operator<=( - const _Identity_t> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { - return _Lhs.compare(_Rhs) <= 0; -} - -template // TRANSITION, VSO-409326 -_NODISCARD constexpr bool operator<=( - const basic_string_view<_Elem, _Traits> _Lhs, const _Identity_t> _Rhs) noexcept { - return _Lhs.compare(_Rhs) <= 0; -} - -template -_NODISCARD constexpr bool operator>=( - const basic_string_view<_Elem, _Traits> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { - return _Lhs.compare(_Rhs) >= 0; -} - -template // TRANSITION, VSO-409326 -_NODISCARD constexpr bool operator>=( - const _Identity_t> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { - return _Lhs.compare(_Rhs) >= 0; -} - -template // TRANSITION, VSO-409326 -_NODISCARD constexpr bool operator>=( - const basic_string_view<_Elem, _Traits> _Lhs, const _Identity_t> _Rhs) noexcept { - return _Lhs.compare(_Rhs) >= 0; -} -#endif // ^^^ !_HAS_CXX20 ^^^ - -_EXPORT_STD using string_view = basic_string_view; -#ifdef __cpp_lib_char8_t -_EXPORT_STD using u8string_view = basic_string_view; -#endif // defined(__cpp_lib_char8_t) -_EXPORT_STD using u16string_view = basic_string_view; -_EXPORT_STD using u32string_view = basic_string_view; -_EXPORT_STD using wstring_view = basic_string_view; - -template -struct hash> : _Conditionally_enabled_hash, _Is_EcharT<_Elem>> { - _NODISCARD static size_t _Do_hash(const basic_string_view<_Elem> _Keyval) noexcept { - return _Hash_array_representation(_Keyval.data(), _Keyval.size()); - } -}; - -_EXPORT_STD template -basic_ostream<_Elem, _Traits>& operator<<( - basic_ostream<_Elem, _Traits>& _Ostr, const basic_string_view<_Elem, _Traits> _Str) { - return _Insert_string(_Ostr, _Str.data(), _Str.size()); -} - -inline namespace literals { - inline namespace string_view_literals { - _EXPORT_STD _NODISCARD constexpr string_view operator""sv(const char* _Str, size_t _Len) noexcept { - return string_view(_Str, _Len); - } - - _EXPORT_STD _NODISCARD constexpr wstring_view operator""sv(const wchar_t* _Str, size_t _Len) noexcept { - return wstring_view(_Str, _Len); - } - -#ifdef __cpp_char8_t - _EXPORT_STD _NODISCARD constexpr basic_string_view operator""sv( - const char8_t* _Str, size_t _Len) noexcept { - return basic_string_view(_Str, _Len); - } -#endif // defined(__cpp_char8_t) - - _EXPORT_STD _NODISCARD constexpr u16string_view operator""sv(const char16_t* _Str, size_t _Len) noexcept { - return u16string_view(_Str, _Len); - } - - _EXPORT_STD _NODISCARD constexpr u32string_view operator""sv(const char32_t* _Str, size_t _Len) noexcept { - return u32string_view(_Str, _Len); - } - } // namespace string_view_literals -} // namespace literals -#endif // _HAS_CXX17 - template class _String_const_iterator : public _Iterator_base { public: