Merge pull request #5055 from StephanTLavavej/merge-flat_map

Merge `main` to `feature/flat_map`
This commit is contained in:
Stephan T. Lavavej 2024-10-30 09:22:06 -07:00 коммит произвёл GitHub
Родитель 027f330670 aa91f3f446
Коммит fbe5394250
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
24 изменённых файлов: 1121 добавлений и 155 удалений

11
.mailmap Normal file
Просмотреть файл

@ -0,0 +1,11 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
# This file maps author and committer email addresses to canonical names and email
# addresses for display in the output of `git log` and `git blame`.
# Format:
# Canonical Name <canonical@example.com> <commit@example.com>
Amy Wishnousky <amyw@microsoft.com> <stwish@microsoft.com>
Anju del Moral Gonzalez <delMoralGonzalez.Anju@microsoft.com> <judelmor@microsoft.com>

Просмотреть файл

@ -433,6 +433,26 @@ build folder to your path:
set PATH=C:\STL\out\x64\out\bin\amd64;%PATH%
```
## Running Tests With Address Sanitizer (ASan)
You don't need any extra steps to run with test code and the code in STL headers instrumented with [ASan][].
The test matrices include both ASan and non-ASan configurations.
However, to instrument the separately-compiled code (the DLL, the satellites, the [Import Library][] - everything that's
in `/stl/src`), you need to build the STL with ASan. Change the build steps to add `-DSTL_ASAN_BUILD=ON`:
```
cmake --preset x64 -DSTL_ASAN_BUILD=ON
cmake --build --preset x64
```
ASan-instrumented STL binaries require that the executable be instrumented as well, so you'll have to skip the non-ASan
configurations by passing `-Dtags=ASAN` to `stl-lit.py`:
```
python tests\utils\stl-lit\stl-lit.py ..\..\tests\std\tests\VSO_0000000_vector_algorithms -Dtags=ASAN -v
```
# Benchmarking
For performance-sensitive code &ndash; containers, algorithms, and the like &ndash;
@ -590,3 +610,5 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
[lit result codes]: https://llvm.org/docs/CommandGuide/lit.html#test-status-results
[redistributables]: https://learn.microsoft.com/en-US/cpp/windows/latest-supported-vc-redist
[natvis documentation]: https://learn.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects
[ASan]: https://learn.microsoft.com/en-us/cpp/sanitizers/asan
[Import Library]: /docs/import_library.md

Просмотреть файл

@ -106,6 +106,7 @@ function(add_benchmark name)
target_link_libraries(benchmark-${name} PRIVATE benchmark::benchmark)
endfunction()
add_benchmark(adjacent_difference src/adjacent_difference.cpp)
add_benchmark(bitset_from_string src/bitset_from_string.cpp)
add_benchmark(bitset_to_string src/bitset_to_string.cpp)
add_benchmark(efficient_nonlocking_print src/efficient_nonlocking_print.cpp)

Просмотреть файл

@ -0,0 +1,76 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#pragma once
#include <cstddef>
#include <cstdlib>
#include <new>
template <class T, size_t Alignment, size_t Skew>
struct skewed_allocator {
using value_type = T;
static_assert(Alignment % alignof(T) == 0, "Chosen Alignment will produce unaligned T objects");
static_assert(Skew % alignof(T) == 0, "Chosen Skew will produce unaligned T objects");
template <class U>
struct rebind {
using other = skewed_allocator<U, Alignment, Skew>;
};
skewed_allocator() = default;
template <class U>
skewed_allocator(const skewed_allocator<U, Alignment, Skew>&) {}
template <class U>
bool operator==(const skewed_allocator<U, Alignment, Skew>&) const {
return true;
}
T* allocate(const size_t n) {
const auto p = static_cast<unsigned char*>(_aligned_malloc(n * sizeof(T) + Skew, Alignment));
if (!p) {
throw std::bad_alloc{};
}
return reinterpret_cast<T*>(p + Skew);
}
void deallocate(T* const p, size_t) {
if (p) {
_aligned_free(reinterpret_cast<unsigned char*>(p) - Skew);
}
}
};
// The purpose is to provide consistent behavior for benchmarks.
// 64 would be a reasonable alignment for practical perf uses,
// as it is both the cache line size and the maximum vector instruction size (on x64).
// However, aligning to the page size will provide even more consistency
// by ensuring that the same number of page boundaries is crossed each time.
inline constexpr size_t page_size = 4096;
// A realistic skew relative to allocation granularity, when a variable is placed
// next to a pointer in a structure or on the stack. Also corresponds to the default packing.
inline constexpr size_t realistic_skew = 8;
template <class T>
using highly_aligned_allocator = skewed_allocator<T, page_size, 0>;
template <class T>
using not_highly_aligned_allocator = skewed_allocator<T, page_size, realistic_skew>;
#pragma warning(push)
#pragma warning(disable : 4324) // structure was padded due to alignment specifier
template <class T>
struct alignas(page_size) highly_aligned {
T value;
};
template <class T>
struct alignas(page_size) not_highly_aligned {
char pad[realistic_skew];
T value;
};
#pragma warning(pop)

Просмотреть файл

@ -0,0 +1,57 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include <algorithm>
#include <benchmark/benchmark.h>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <numeric>
#include <random>
#include <type_traits>
#include <vector>
using namespace std;
template <class T>
void bm(benchmark::State& state) {
mt19937 gen(96337);
const size_t size = static_cast<size_t>(state.range(0));
vector<T> input(size);
vector<T> output(size);
if constexpr (is_floating_point_v<T>) {
normal_distribution<T> dis(-100.0, 100.0);
ranges::generate(input, [&] { return dis(gen); });
} else {
static_assert(is_unsigned_v<T>, "This avoids signed integers to avoid UB; they shouldn't perform differently");
uniform_int_distribution<conditional_t<sizeof(T) != 1, T, unsigned int>> dis(0, numeric_limits<T>::max());
ranges::generate(input, [&] { return static_cast<T>(dis(gen)); });
}
for (auto _ : state) {
benchmark::DoNotOptimize(input);
adjacent_difference(input.begin(), input.end(), output.begin());
benchmark::DoNotOptimize(output);
}
}
void common_args(auto bm) {
bm->Arg(2255);
}
#pragma warning(push)
#pragma warning(disable : 4244) // warning C4244: '=': conversion from 'int' to 'unsigned char', possible loss of data
BENCHMARK(bm<uint8_t>)->Apply(common_args);
BENCHMARK(bm<uint16_t>)->Apply(common_args);
#pragma warning(pop)
BENCHMARK(bm<uint32_t>)->Apply(common_args);
BENCHMARK(bm<uint64_t>)->Apply(common_args);
BENCHMARK(bm<float>)->Apply(common_args);
BENCHMARK(bm<double>)->Apply(common_args);
BENCHMARK_MAIN();

Просмотреть файл

@ -111,6 +111,22 @@ void search_default_searcher(benchmark::State& state) {
}
}
template <class T>
void member_find(benchmark::State& state) {
const auto& src_haystack = patterns[static_cast<size_t>(state.range())].data;
const auto& src_needle = patterns[static_cast<size_t>(state.range())].pattern;
const T haystack(src_haystack.begin(), src_haystack.end());
const T needle(src_needle.begin(), src_needle.end());
for (auto _ : state) {
benchmark::DoNotOptimize(haystack);
benchmark::DoNotOptimize(needle);
auto res = haystack.find(needle);
benchmark::DoNotOptimize(res);
}
}
template <class T>
void classic_find_end(benchmark::State& state) {
const auto& src_haystack = patterns[static_cast<size_t>(state.range())].data;
@ -158,6 +174,9 @@ BENCHMARK(ranges_search<std::uint16_t>)->Apply(common_args);
BENCHMARK(search_default_searcher<std::uint8_t>)->Apply(common_args);
BENCHMARK(search_default_searcher<std::uint16_t>)->Apply(common_args);
BENCHMARK(member_find<std::string>)->Apply(common_args);
BENCHMARK(member_find<std::wstring>)->Apply(common_args);
BENCHMARK(classic_find_end<std::uint8_t>)->Apply(common_args);
BENCHMARK(classic_find_end<std::uint16_t>)->Apply(common_args);

Просмотреть файл

@ -7,13 +7,17 @@
#include <cstdint>
#include <vector>
#include "skewed_allocator.hpp"
using namespace std;
template <size_t N, class T>
template <size_t N, class T, template <class> class Padder>
void std_swap(benchmark::State& state) {
T a[N];
Padder<T[N]> padded_a;
auto& a = padded_a.value;
memset(a, 'a', sizeof(a));
T b[N];
Padder<T[N]> padded_b;
auto& b = padded_b.value;
memset(b, 'b', sizeof(b));
for (auto _ : state) {
@ -23,10 +27,10 @@ void std_swap(benchmark::State& state) {
}
}
template <class T>
template <class T, template <class> class Alloc>
void std_swap_ranges(benchmark::State& state) {
vector<T> a(static_cast<size_t>(state.range(0)), T{'a'});
vector<T> b(static_cast<size_t>(state.range(0)), T{'b'});
vector<T, Alloc<T>> a(static_cast<size_t>(state.range(0)), T{'a'});
vector<T, Alloc<T>> b(static_cast<size_t>(state.range(0)), T{'b'});
for (auto _ : state) {
swap_ranges(a.begin(), a.end(), b.begin());
@ -35,18 +39,41 @@ void std_swap_ranges(benchmark::State& state) {
}
}
BENCHMARK(std_swap<1, uint8_t>);
BENCHMARK(std_swap<5, uint8_t>);
BENCHMARK(std_swap<15, uint8_t>);
BENCHMARK(std_swap<26, uint8_t>);
BENCHMARK(std_swap<38, uint8_t>);
BENCHMARK(std_swap<60, uint8_t>);
BENCHMARK(std_swap<125, uint8_t>);
BENCHMARK(std_swap<800, uint8_t>);
BENCHMARK(std_swap<3000, uint8_t>);
BENCHMARK(std_swap<9000, uint8_t>);
BENCHMARK(std_swap<1, uint8_t, highly_aligned>);
BENCHMARK(std_swap<5, uint8_t, highly_aligned>);
BENCHMARK(std_swap<15, uint8_t, highly_aligned>);
BENCHMARK(std_swap<26, uint8_t, highly_aligned>);
BENCHMARK(std_swap<38, uint8_t, highly_aligned>);
BENCHMARK(std_swap<60, uint8_t, highly_aligned>);
BENCHMARK(std_swap<125, uint8_t, highly_aligned>);
BENCHMARK(std_swap<800, uint8_t, highly_aligned>);
BENCHMARK(std_swap<3000, uint8_t, highly_aligned>);
BENCHMARK(std_swap<9000, uint8_t, highly_aligned>);
BENCHMARK(std_swap_ranges<uint8_t>)
BENCHMARK(std_swap<1, uint8_t, not_highly_aligned>);
BENCHMARK(std_swap<5, uint8_t, not_highly_aligned>);
BENCHMARK(std_swap<15, uint8_t, not_highly_aligned>);
BENCHMARK(std_swap<26, uint8_t, not_highly_aligned>);
BENCHMARK(std_swap<38, uint8_t, not_highly_aligned>);
BENCHMARK(std_swap<60, uint8_t, not_highly_aligned>);
BENCHMARK(std_swap<125, uint8_t, not_highly_aligned>);
BENCHMARK(std_swap<800, uint8_t, not_highly_aligned>);
BENCHMARK(std_swap<3000, uint8_t, not_highly_aligned>);
BENCHMARK(std_swap<9000, uint8_t, not_highly_aligned>);
BENCHMARK(std_swap_ranges<uint8_t, highly_aligned_allocator>)
->Arg(1)
->Arg(5)
->Arg(15)
->Arg(26)
->Arg(38)
->Arg(60)
->Arg(125)
->Arg(800)
->Arg(3000)
->Arg(9000);
BENCHMARK(std_swap_ranges<uint8_t, not_highly_aligned_allocator>)
->Arg(1)
->Arg(5)
->Arg(15)

@ -1 +1 @@
Subproject commit 886b76128fba5f995c8c8e24aaa2030b59dec01a
Subproject commit 69ead949d08ff0bb8cbbf4f7143aaa6687830f6b

Просмотреть файл

@ -628,6 +628,21 @@ constexpr size_t _Traits_find(_In_reads_(_Hay_size) const _Traits_ptr_t<_Traits>
return _Start_at;
}
#if _USE_STD_VECTOR_ALGORITHMS
if constexpr (_Is_implementation_handled_char_traits<_Traits> && sizeof(typename _Traits::char_type) <= 2) {
if (!_STD _Is_constant_evaluated()) {
const auto _End = _Haystack + _Hay_size;
const auto _Ptr = _STD _Search_vectorized(_Haystack + _Start_at, _End, _Needle, _Needle_size);
if (_Ptr != _End) {
return static_cast<size_t>(_Ptr - _Haystack);
} else {
return static_cast<size_t>(-1);
}
}
}
#endif // _USE_STD_VECTOR_ALGORITHMS
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<size_t>(_Possible_matches_end - _Match_try), *_Needle);
@ -1756,13 +1771,14 @@ _NODISCARD constexpr bool operator==(const basic_string_view<_Elem, _Traits> _Lh
return _Lhs._Equal(_Rhs);
}
template <class _Traits, class = void>
template <class _Traits>
struct _Get_comparison_category {
using type = weak_ordering;
};
template <class _Traits>
struct _Get_comparison_category<_Traits, void_t<typename _Traits::comparison_category>> {
requires requires { typename _Traits::comparison_category; }
struct _Get_comparison_category<_Traits> {
using type = _Traits::comparison_category;
static_assert(_Is_any_of_v<type, partial_ordering, weak_ordering, strong_ordering>,

Просмотреть файл

@ -33,13 +33,12 @@ struct _Stl_critical_section {
};
struct _Mtx_internal_imp_t {
#if defined(_CRT_WINDOWS) || defined(UNDOCKED_WINDOWS_UCRT) // for Windows-internal code
static constexpr size_t _Critical_section_size = 2 * sizeof(void*);
#elif defined(_WIN64) // ordinary 64-bit code
// TRANSITION, ABI: We should directly store _M_srw_lock above.
#ifdef _WIN64
static constexpr size_t _Critical_section_size = 64;
#else // vvv ordinary 32-bit code vvv
#else // ^^^ 64-bit / 32-bit vvv
static constexpr size_t _Critical_section_size = 36;
#endif // ^^^ ordinary 32-bit code ^^^
#endif // ^^^ 32-bit ^^^
int _Type{};
union {
@ -60,13 +59,12 @@ struct _Stl_condition_variable {
#pragma warning(push)
#pragma warning(disable : 26495) // Variable 'meow' is uninitialized. Always initialize a member variable (type.6).
struct _Cnd_internal_imp_t {
#if defined(_CRT_WINDOWS) // for Windows-internal code
static constexpr size_t _Cnd_internal_imp_size = 2 * sizeof(void*);
#elif defined(_WIN64) // ordinary 64-bit code
// TRANSITION, ABI: We should directly store _Win_cv above.
#ifdef _WIN64
static constexpr size_t _Cnd_internal_imp_size = 72;
#else // vvv ordinary 32-bit code vvv
#else // ^^^ 64-bit / 32-bit vvv
static constexpr size_t _Cnd_internal_imp_size = 40;
#endif // ^^^ ordinary 32-bit code ^^^
#endif // ^^^ 32-bit ^^^
union {
_Stl_condition_variable _Stl_cv{};

Просмотреть файл

@ -59,6 +59,11 @@ const void* __stdcall __std_find_last_trivial_2(const void* _First, const void*
const void* __stdcall __std_find_last_trivial_4(const void* _First, const void* _Last, uint32_t _Val) noexcept;
const void* __stdcall __std_find_last_trivial_8(const void* _First, const void* _Last, uint64_t _Val) noexcept;
const void* __stdcall __std_find_end_1(
const void* _First1, const void* _Last1, const void* _First2, size_t _Count2) noexcept;
const void* __stdcall __std_find_end_2(
const void* _First1, const void* _Last1, const void* _First2, size_t _Count2) noexcept;
__declspec(noalias) _Min_max_1i __stdcall __std_minmax_1i(const void* _First, const void* _Last) noexcept;
__declspec(noalias) _Min_max_1u __stdcall __std_minmax_1u(const void* _First, const void* _Last) noexcept;
__declspec(noalias) _Min_max_2i __stdcall __std_minmax_2i(const void* _First, const void* _Last) noexcept;
@ -189,6 +194,19 @@ _Ty* _Find_last_vectorized(_Ty* const _First, _Ty* const _Last, const _TVal _Val
}
}
template <class _Ty1, class _Ty2>
_Ty1* _Find_end_vectorized(
_Ty1* const _First1, _Ty1* const _Last1, _Ty2* const _First2, const size_t _Count2) noexcept {
_STL_INTERNAL_STATIC_ASSERT(sizeof(_Ty1) == sizeof(_Ty2));
if constexpr (sizeof(_Ty1) == 1) {
return const_cast<_Ty1*>(static_cast<const _Ty1*>(::__std_find_end_1(_First1, _Last1, _First2, _Count2)));
} else if constexpr (sizeof(_Ty1) == 2) {
return const_cast<_Ty1*>(static_cast<const _Ty1*>(::__std_find_end_2(_First1, _Last1, _First2, _Count2)));
} else {
_STL_INTERNAL_STATIC_ASSERT(false); // unexpected size
}
}
template <class _Ty, class _TVal1, class _TVal2>
__declspec(noalias) void _Replace_vectorized(
_Ty* const _First, _Ty* const _Last, const _TVal1 _Old_val, const _TVal2 _New_val) noexcept {
@ -3194,6 +3212,26 @@ _NODISCARD _CONSTEXPR20 _FwdIt1 find_end(
if constexpr (_Is_ranges_random_iter_v<_FwdIt1> && _Is_ranges_random_iter_v<_FwdIt2>) {
const _Iter_diff_t<_FwdIt2> _Count2 = _ULast2 - _UFirst2;
if (_Count2 > 0 && _Count2 <= _ULast1 - _UFirst1) {
#if _USE_STD_VECTOR_ALGORITHMS
if constexpr (_Vector_alg_in_search_is_safe<decltype(_UFirst1), decltype(_UFirst2), _Pr>) {
if (!_STD _Is_constant_evaluated()) {
const auto _Ptr1 = _STD _To_address(_UFirst1);
const auto _Ptr_res1 = _STD _Find_end_vectorized(
_Ptr1, _STD _To_address(_ULast1), _STD _To_address(_UFirst2), static_cast<size_t>(_Count2));
if constexpr (is_pointer_v<decltype(_UFirst1)>) {
_UFirst1 = _Ptr_res1;
} else {
_UFirst1 += _Ptr_res1 - _Ptr1;
}
_STD _Seek_wrapped(_First1, _UFirst1);
return _First1;
}
}
#endif // _USE_STD_VECTOR_ALGORITHMS
for (auto _UCandidate = _ULast1 - static_cast<_Iter_diff_t<_FwdIt1>>(_Count2);; --_UCandidate) {
if (_STD _Equal_rev_pred_unchecked(_UCandidate, _UFirst2, _ULast2, _STD _Pass_fn(_Pred))) {
_STD _Seek_wrapped(_First1, _UCandidate);
@ -3297,6 +3335,34 @@ namespace ranges {
if (_Count2 > 0 && _Count2 <= _Count1) {
const auto _Count2_as1 = static_cast<iter_difference_t<_It1>>(_Count2);
#if _USE_STD_VECTOR_ALGORITHMS
if constexpr (_Vector_alg_in_search_is_safe<_It1, _It2, _Pr> && is_same_v<_Pj1, identity>
&& is_same_v<_Pj2, identity>) {
if (!_STD is_constant_evaluated()) {
const auto _Ptr1 = _STD to_address(_First1);
const auto _Ptr2 = _STD to_address(_First2);
const auto _Ptr_last1 = _Ptr1 + _Count1;
const auto _Ptr_res1 =
_STD _Find_end_vectorized(_Ptr1, _Ptr_last1, _Ptr2, static_cast<size_t>(_Count2));
if constexpr (is_pointer_v<_It1>) {
if (_Ptr_res1 != _Ptr_last1) {
return {_Ptr_res1, _Ptr_res1 + _Count2};
} else {
return {_Ptr_res1, _Ptr_res1};
}
} else {
_First1 += _Ptr_res1 - _Ptr1;
if (_Ptr_res1 != _Ptr_last1) {
return {_First1, _First1 + _Count2_as1};
} else {
return {_First1, _First1};
}
}
}
}
#endif // _USE_STD_VECTOR_ALGORITHMS
for (auto _Candidate = _First1 + (_Count1 - _Count2_as1);; --_Candidate) {
auto _Match_and_mid1 =

Просмотреть файл

@ -4612,57 +4612,56 @@ namespace chrono {
minutes* _Offset;
};
template <class _CharT, class _Traits, class _Parsable, class... _Rest>
using _Has_from_stream =
decltype(static_cast<void>(from_stream(_STD declval<basic_istream<_CharT, _Traits>&>(),
_STD declval<const _CharT*>(), _STD declval<_Parsable&>(), _STD declval<_Rest>()...)),
0); // intentional ADL
template <class _Parsable, class _CharT, class _Traits, class... _Rest>
concept _Can_from_stream = requires(
basic_istream<_CharT, _Traits>& __istr, const _CharT* __s, _Parsable& __parsed, _Rest&&... __rest_args) {
from_stream(__istr, +__s, __parsed, _STD forward<_Rest>(__rest_args)...); // intentional ADL
};
_EXPORT_STD template <class _CharT, class _Parsable, _Has_from_stream<_CharT, char_traits<_CharT>, _Parsable> = 0>
_EXPORT_STD template <class _CharT, _Can_from_stream<_CharT, char_traits<_CharT>> _Parsable>
_NODISCARD auto parse(const _CharT* _Fmt, _Parsable& _Tp) {
return _Time_parse_iomanip_c_str<_CharT, char_traits<_CharT>, allocator<_CharT>, _Parsable>{_Fmt, _Tp};
}
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc, class _Parsable,
_Has_from_stream<_CharT, _Traits, _Parsable> = 0>
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc, _Can_from_stream<_CharT, _Traits> _Parsable>
_NODISCARD auto parse(const basic_string<_CharT, _Traits, _Alloc>& _Fmt, _Parsable& _Tp) {
return _Time_parse_iomanip{_Fmt, _Tp};
}
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc, class _Parsable,
_Has_from_stream<_CharT, _Traits, _Parsable, basic_string<_CharT, _Traits, _Alloc>*> = 0>
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc,
_Can_from_stream<_CharT, _Traits, basic_string<_CharT, _Traits, _Alloc>*> _Parsable>
_NODISCARD auto parse(const _CharT* _Fmt, _Parsable& _Tp, basic_string<_CharT, _Traits, _Alloc>& _Abbrev) {
return _Time_parse_iomanip_c_str{_Fmt, _Tp, _STD addressof(_Abbrev)};
}
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc, class _Parsable,
_Has_from_stream<_CharT, _Traits, _Parsable, basic_string<_CharT, _Traits, _Alloc>*> = 0>
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc,
_Can_from_stream<_CharT, _Traits, basic_string<_CharT, _Traits, _Alloc>*> _Parsable>
_NODISCARD auto parse(const basic_string<_CharT, _Traits, _Alloc>& _Fmt, _Parsable& _Tp,
basic_string<_CharT, _Traits, _Alloc>& _Abbrev) {
return _Time_parse_iomanip{_Fmt, _Tp, _STD addressof(_Abbrev)};
}
_EXPORT_STD template <class _CharT, class _Parsable,
_Has_from_stream<_CharT, char_traits<_CharT>, _Parsable, basic_string<_CharT>*, minutes*> = 0>
_EXPORT_STD template <class _CharT,
_Can_from_stream<_CharT, char_traits<_CharT>, basic_string<_CharT>*, minutes*> _Parsable>
_NODISCARD auto parse(const _CharT* _Fmt, _Parsable& _Tp, minutes& _Offset) {
return _Time_parse_iomanip_c_str{_Fmt, _Tp, static_cast<basic_string<_CharT>*>(nullptr), &_Offset};
}
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc, class _Parsable,
_Has_from_stream<_CharT, _Traits, _Parsable, basic_string<_CharT, _Traits, _Alloc>*, minutes*> = 0>
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc,
_Can_from_stream<_CharT, _Traits, basic_string<_CharT, _Traits, _Alloc>*, minutes*> _Parsable>
_NODISCARD auto parse(const basic_string<_CharT, _Traits, _Alloc>& _Fmt, _Parsable& _Tp, minutes& _Offset) {
return _Time_parse_iomanip{_Fmt, _Tp, static_cast<basic_string<_CharT, _Traits, _Alloc>*>(nullptr), &_Offset};
}
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc, class _Parsable,
_Has_from_stream<_CharT, _Traits, _Parsable, basic_string<_CharT, _Traits, _Alloc>*, minutes*> = 0>
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc,
_Can_from_stream<_CharT, _Traits, basic_string<_CharT, _Traits, _Alloc>*, minutes*> _Parsable>
_NODISCARD auto parse(
const _CharT* _Fmt, _Parsable& _Tp, basic_string<_CharT, _Traits, _Alloc>& _Abbrev, minutes& _Offset) {
return _Time_parse_iomanip_c_str{_Fmt, _Tp, _STD addressof(_Abbrev), &_Offset};
}
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc, class _Parsable,
_Has_from_stream<_CharT, _Traits, _Parsable, basic_string<_CharT, _Traits, _Alloc>*, minutes*> = 0>
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc,
_Can_from_stream<_CharT, _Traits, basic_string<_CharT, _Traits, _Alloc>*, minutes*> _Parsable>
_NODISCARD auto parse(const basic_string<_CharT, _Traits, _Alloc>& _Fmt, _Parsable& _Tp,
basic_string<_CharT, _Traits, _Alloc>& _Abbrev, minutes& _Offset) {
return _Time_parse_iomanip{_Fmt, _Tp, _STD addressof(_Abbrev), &_Offset};

Просмотреть файл

@ -461,6 +461,20 @@ _FwdIt2 transform_inclusive_scan(_ExPo&& _Exec, _FwdIt1 _First, _FwdIt1 _Last, _
_UnaryOp _Transform_op) noexcept; // terminates
#endif // _HAS_CXX17
template <class _TyDest, class _TySrc, class _BinOp>
void _Adjacent_difference_no_overlap(
_TyDest* const __restrict _Dest, _TySrc* const __restrict _Src, const ptrdiff_t _Count, _BinOp _Func) {
_Dest[0] = _Src[0];
for (ptrdiff_t _Ix = 1; _Ix != _Count; ++_Ix) {
#if _HAS_CXX20
_TySrc _Tmp = _Src[_Ix - 1];
_Dest[_Ix] = _Func(_Src[_Ix], _STD move(_Tmp));
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
_Dest[_Ix] = _Func(_Src[_Ix], _Src[_Ix - 1]);
#endif // ^^^ !_HAS_CXX20 ^^^
}
}
_EXPORT_STD template <class _InIt, class _OutIt, class _BinOp>
_CONSTEXPR20 _OutIt adjacent_difference(const _InIt _First, const _InIt _Last, _OutIt _Dest, _BinOp _Func) {
// compute adjacent differences into _Dest
@ -469,6 +483,32 @@ _CONSTEXPR20 _OutIt adjacent_difference(const _InIt _First, const _InIt _Last, _
const auto _ULast = _STD _Get_unwrapped(_Last);
auto _UDest = _STD _Get_unwrapped_n(_Dest, _STD _Idl_distance<_InIt>(_UFirst, _ULast));
if (_UFirst != _ULast) {
if constexpr (_Iterators_are_contiguous<decltype(_UFirst), decltype(_UDest)> && !_Iterator_is_volatile<_InIt>
&& is_trivially_copyable_v<_Iter_value_t<_InIt>>) {
#if _HAS_CXX20
if (!_STD is_constant_evaluated())
#endif
{
// Go with pointers and without loop-carried dependency to enable vectorization
const auto _First_ptr = _STD _To_address(_UFirst);
const auto _Last_ptr = _STD _To_address(_ULast);
const auto _Dest_ptr = _STD _To_address(_UDest);
const auto _Count = _Last_ptr - _First_ptr;
// Need to perform aliasing analysis.
// The vectorizer is generally able to do that on its own, and would guard the vectorized code with
// that, but when we eliminate the loop-carried dependency we change the semantics of the unvectorized
// code too. So we need to perform this check manually, and after that we can tell the compiler that
// there's no aliasing, to avoid it checking for that again.
if (reinterpret_cast<uintptr_t>(_Dest_ptr + _Count) <= reinterpret_cast<uintptr_t>(_First_ptr)
|| reinterpret_cast<uintptr_t>(_Last_ptr) <= reinterpret_cast<uintptr_t>(_Dest_ptr)) {
_STD _Adjacent_difference_no_overlap(_Dest_ptr, _First_ptr, _Count, _STD _Pass_fn(_Func));
_STD _Seek_wrapped(_Dest, _UDest + _Count);
return _Dest;
}
}
}
_Iter_value_t<_InIt> _Val(*_UFirst);
*_UDest = _Val;
while (++_UFirst != _ULast) { // compute another difference

Просмотреть файл

@ -605,18 +605,6 @@ bool _Is_word(_Elem _Ch) {
return _UCh <= static_cast<_UElem>('z') && _Is_word(static_cast<unsigned char>(_UCh));
}
#if _HAS_CXX20
template <class _Ty, class = void>
struct _Get_member_comparison_category {
using type = weak_ordering;
};
template <class _Ty>
struct _Get_member_comparison_category<_Ty, void_t<typename _Ty::comparison_category>> {
using type = _Ty::comparison_category;
};
#endif // _HAS_CXX20
_EXPORT_STD template <class _BidIt>
class sub_match : public pair<_BidIt, _BidIt> { // class to hold contents of a capture group
public:
@ -630,10 +618,6 @@ public:
// Note that _Size_type should always be std::size_t
using _Size_type = typename string_type::size_type;
#if _HAS_CXX20
using _Comparison_category = _Get_member_comparison_category<_Traits>::type;
#endif // _HAS_CXX20
constexpr sub_match() : _Mybase(), matched(false) {}
bool matched;
@ -744,7 +728,8 @@ _NODISCARD bool operator==(const sub_match<_BidIt>& _Left, const sub_match<_BidI
#if _HAS_CXX20
_EXPORT_STD template <class _BidIt>
_NODISCARD auto operator<=>(const sub_match<_BidIt>& _Left, const sub_match<_BidIt>& _Right) {
return static_cast<sub_match<_BidIt>::_Comparison_category>(_Left.compare(_Right) <=> 0);
using _Comparison_category = _Get_comparison_category_t<char_traits<_Iter_value_t<_BidIt>>>;
return static_cast<_Comparison_category>(_Left.compare(_Right) <=> 0);
}
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
template <class _BidIt>
@ -781,7 +766,8 @@ _NODISCARD bool operator==(const sub_match<_BidIt>& _Left, const _Iter_value_t<_
#if _HAS_CXX20
_EXPORT_STD template <class _BidIt>
_NODISCARD auto operator<=>(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) {
return static_cast<sub_match<_BidIt>::_Comparison_category>(_Left.compare(_Right) <=> 0);
using _Comparison_category = _Get_comparison_category_t<char_traits<_Iter_value_t<_BidIt>>>;
return static_cast<_Comparison_category>(_Left.compare(_Right) <=> 0);
}
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
template <class _BidIt>
@ -848,7 +834,8 @@ _NODISCARD bool operator==(const sub_match<_BidIt>& _Left, const _Iter_value_t<_
#if _HAS_CXX20
_EXPORT_STD template <class _BidIt>
_NODISCARD auto operator<=>(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) {
return static_cast<sub_match<_BidIt>::_Comparison_category>(_Left._Compare(_STD addressof(_Right), 1) <=> 0);
using _Comparison_category = _Get_comparison_category_t<char_traits<_Iter_value_t<_BidIt>>>;
return static_cast<_Comparison_category>(_Left._Compare(_STD addressof(_Right), 1) <=> 0);
}
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
template <class _BidIt>
@ -917,7 +904,8 @@ _NODISCARD bool operator==(
_EXPORT_STD template <class _BidIt, class _Traits, class _Alloc>
_NODISCARD auto operator<=>(
const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) {
return static_cast<sub_match<_BidIt>::_Comparison_category>(_Left._Compare(_Right.data(), _Right.size()) <=> 0);
using _Comparison_category = _Get_comparison_category_t<char_traits<_Iter_value_t<_BidIt>>>;
return static_cast<_Comparison_category>(_Left._Compare(_Right.data(), _Right.size()) <=> 0);
}
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
template <class _BidIt, class _Traits, class _Alloc>

Просмотреть файл

@ -590,7 +590,7 @@ public:
template <class... _Valtys>
conditional_t<_Multi, iterator, pair<iterator, bool>> emplace(_Valtys&&... _Vals) {
// try to insert value_type(_Vals...)
using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Remove_cvref_t<_Valtys>...>;
using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Valtys...>;
if constexpr (_Multi) {
_Check_max_size();
_List_node_emplace_op2<_Alnode> _Newnode(_List._Getal(), _STD forward<_Valtys>(_Vals)...);
@ -642,7 +642,7 @@ public:
template <class... _Valtys>
iterator emplace_hint(const_iterator _Hint, _Valtys&&... _Vals) { // try to insert value_type(_Vals...)
using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Remove_cvref_t<_Valtys>...>;
using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Valtys...>;
if constexpr (_Multi) {
_Check_max_size();
_List_node_emplace_op2<_Alnode> _Newnode(_List._Getal(), _STD forward<_Valtys>(_Vals)...);

Просмотреть файл

@ -2088,15 +2088,22 @@ _CXX17_DEPRECATE_TEMPORARY_BUFFER void return_temporary_buffer(_Ty* _Pbuf) {
}
#endif // _HAS_DEPRECATED_TEMPORARY_BUFFER
// assumes _Args have already been _Remove_cvref_t'd
// The key_type of an (unordered) associative container is cv-unqualified, and we can't bind const Key& to a
// volatile glvalue. Also, Cpp17CopyInsertable and Cpp17MoveInsertable don't require value-preservation for
// the construction from a volatile glvalue, so generally we can't perform this optimization for them.
// See N4993 [container.alloc.reqmts]/2.3, /2.4.
template <class _Ty>
using _Remove_const_ref_t = remove_const_t<remove_reference_t<_Ty>>;
// assumes _Args have already been _Remove_const_ref_t'd
template <class _Key, class... _Args>
struct _In_place_key_extract_set {
struct _In_place_key_extract_set_impl {
// by default we can't extract the key in the emplace family and must construct a node we might not use
static constexpr bool _Extractable = false;
};
template <class _Key>
struct _In_place_key_extract_set<_Key, _Key> {
struct _In_place_key_extract_set_impl<_Key, _Key> {
// we can extract the key in emplace if the emplaced type is identical to the key type
static constexpr bool _Extractable = true;
static const _Key& _Extract(const _Key& _Val) noexcept {
@ -2104,15 +2111,18 @@ struct _In_place_key_extract_set<_Key, _Key> {
}
};
// assumes _Args have already been _Remove_cvref_t'd
template <class... _Valtys>
using _In_place_key_extract_set = _In_place_key_extract_set_impl<_Remove_const_ref_t<_Valtys>...>;
// assumes _Args have already been _Remove_const_ref_t'd
template <class _Key, class... _Args>
struct _In_place_key_extract_map {
struct _In_place_key_extract_map_impl {
// by default we can't extract the key in the emplace family and must construct a node we might not use
static constexpr bool _Extractable = false;
};
template <class _Key, class _Second>
struct _In_place_key_extract_map<_Key, _Key, _Second> {
struct _In_place_key_extract_map_impl<_Key, _Key, _Second> {
// if we would call the pair(key, value) constructor family, we can use the first parameter as the key
static constexpr bool _Extractable = true;
static const _Key& _Extract(const _Key& _Val, const _Second&) noexcept {
@ -2121,14 +2131,49 @@ struct _In_place_key_extract_map<_Key, _Key, _Second> {
};
template <class _Key, class _First, class _Second>
struct _In_place_key_extract_map<_Key, pair<_First, _Second>> {
struct _In_place_key_extract_map_impl<_Key, pair<_First, _Second>> {
// if we would call the pair(pair<other, other>) constructor family, we can use the pair.first member as the key
static constexpr bool _Extractable = is_same_v<_Key, _Remove_cvref_t<_First>>;
static const _Key& _Extract(const pair<_First, _Second>& _Val) {
static constexpr bool _Extractable = is_same_v<_Key, _Remove_const_ref_t<_First>>;
static const _Key& _Extract(const pair<_First, _Second>& _Val) noexcept {
return _Val.first;
}
};
#if _HAS_CXX23
// if we would call the pair(pair-like) constructor family and the argument is not a subrange,
// we can use get<0>(pair-like) as the key
template <class _Key, class _Elem>
struct _In_place_key_extract_map_impl<_Key, array<_Elem, 2>> {
static constexpr bool _Extractable = is_same_v<_Key, remove_const_t<_Elem>>;
static const _Key& _Extract(const array<_Elem, 2>& _Val) noexcept {
return _Val[0];
}
};
template <class _Key, class _First, class _Second>
struct _In_place_key_extract_map_impl<_Key, tuple<_First, _Second>> {
static constexpr bool _Extractable = is_same_v<_Key, _Remove_const_ref_t<_First>>;
static const _Key& _Extract(const tuple<_First, _Second>& _Val) noexcept {
return _STD get<0>(_Val);
}
};
#endif // _HAS_CXX23
template <class _Key, class _First, class... _RestTypes>
struct _In_place_key_extract_map_impl<_Key, piecewise_construct_t, tuple<_First>, tuple<_RestTypes...>> {
// if we would call the piecewise_construct_t constructor and the first argument is a 1-tuple,
// we can use get<0>(first_tuple) as the key
static constexpr bool _Extractable = is_same_v<_Key, _Remove_const_ref_t<_First>>;
static const _Key& _Extract(
const piecewise_construct_t&, const tuple<_First>& _Tup_val, const tuple<_RestTypes...>&) noexcept {
return _STD get<0>(_Tup_val);
}
};
template <class... _Valtys>
using _In_place_key_extract_map = _In_place_key_extract_map_impl<_Remove_const_ref_t<_Valtys>...>;
#pragma warning(push)
#pragma warning(disable : 4624) // '%s': destructor was implicitly defined as deleted
template <class _Ty>

Просмотреть файл

@ -998,7 +998,7 @@ private:
protected:
template <class... _Valtys>
pair<_Nodeptr, bool> _Emplace(_Valtys&&... _Vals) {
using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Remove_cvref_t<_Valtys>...>;
using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Valtys...>;
const auto _Scary = _Get_scary();
_Tree_find_result<_Nodeptr> _Loc;
_Nodeptr _Inserted;
@ -1042,7 +1042,7 @@ public:
protected:
template <class... _Valtys>
_Nodeptr _Emplace_hint(const _Nodeptr _Hint, _Valtys&&... _Vals) {
using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Remove_cvref_t<_Valtys>...>;
using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Valtys...>;
const auto _Scary = _Get_scary();
_Tree_find_hint_result<_Nodeptr> _Loc;
_Nodeptr _Inserted;

Просмотреть файл

@ -3633,6 +3633,252 @@ namespace {
return _Last1;
}
}
template <class _Traits, class _Ty>
const void* __stdcall __std_find_end_impl(
const void* const _First1, const void* const _Last1, const void* const _First2, const size_t _Count2) noexcept {
if (_Count2 == 0) {
return _Last1;
}
if (_Count2 == 1) {
return __std_find_last_trivial_impl<_Traits>(_First1, _Last1, *static_cast<const _Ty*>(_First2));
}
const size_t _Size_bytes_1 = _Byte_length(_First1, _Last1);
const size_t _Size_bytes_2 = _Count2 * sizeof(_Ty);
if (_Size_bytes_1 < _Size_bytes_2) {
return _Last1;
}
#ifndef _M_ARM64EC
if (_Use_sse42() && _Size_bytes_1 >= 16) {
constexpr int _Op = (sizeof(_Ty) == 1 ? _SIDD_UBYTE_OPS : _SIDD_UWORD_OPS) | _SIDD_CMP_EQUAL_ORDERED;
constexpr int _Part_size_el = sizeof(_Ty) == 1 ? 16 : 8;
static constexpr int8_t _Low_part_mask[] = {//
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, //
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
if (_Size_bytes_2 <= 16) {
const int _Size_el_2 = static_cast<int>(_Count2);
constexpr unsigned int _Whole_mask = (1 << _Part_size_el) - 1;
const unsigned int _Needle_fit_mask = (1 << (_Part_size_el - _Size_el_2 + 1)) - 1;
const unsigned int _Needle_unfit_mask = _Whole_mask ^ _Needle_fit_mask;
const void* _Stop1 = _First1;
_Advance_bytes(_Stop1, _Size_bytes_1 & 0xF);
alignas(16) uint8_t _Tmp2[16];
memcpy(_Tmp2, _First2, _Size_bytes_2);
const __m128i _Data2 = _mm_load_si128(reinterpret_cast<const __m128i*>(_Tmp2));
const void* _Mid1 = _Last1;
_Rewind_bytes(_Mid1, 16);
const auto _Check_fit = [&_Mid1, _Needle_fit_mask](const unsigned int _Match) noexcept {
const unsigned int _Fit_match = _Match & _Needle_fit_mask;
if (_Fit_match != 0) {
unsigned long _Match_last_pos;
// CodeQL [SM02313] Result is always initialized: we just tested that _Fit_match is non-zero.
_BitScanReverse(&_Match_last_pos, _Fit_match);
_Advance_bytes(_Mid1, _Match_last_pos * sizeof(_Ty));
return true;
}
return false;
};
#pragma warning(push)
#pragma warning(disable : 4324) // structure was padded due to alignment specifier
const auto _Check_unfit = [=, &_Mid1](const unsigned int _Match) noexcept {
long _Unfit_match = _Match & _Needle_unfit_mask;
while (_Unfit_match != 0) {
const void* _Tmp1 = _Mid1;
unsigned long _Match_last_pos;
// CodeQL [SM02313] Result is always initialized: we just tested that _Unfit_match is non-zero.
_BitScanReverse(&_Match_last_pos, _Unfit_match);
_Advance_bytes(_Tmp1, _Match_last_pos * sizeof(_Ty));
const __m128i _Match_data = _mm_loadu_si128(reinterpret_cast<const __m128i*>(_Tmp1));
const __m128i _Cmp_result = _mm_xor_si128(_Data2, _Match_data);
const __m128i _Data_mask =
_mm_loadu_si128(reinterpret_cast<const __m128i*>(_Low_part_mask + 16 - _Size_bytes_2));
if (_mm_testz_si128(_Cmp_result, _Data_mask)) {
_Mid1 = _Tmp1;
return true;
}
_bittestandreset(&_Unfit_match, _Match_last_pos);
}
return false;
};
#pragma warning(pop)
// TRANSITION, DevCom-10689455, the code below could test with _mm_cmpestrc,
// if it has been fused with _mm_cmpestrm.
// The very last part, for any match needle should fit, otherwise false match
const __m128i _Data1_last = _mm_loadu_si128(reinterpret_cast<const __m128i*>(_Mid1));
const auto _Match_last = _mm_cmpestrm(_Data2, _Size_el_2, _Data1_last, _Part_size_el, _Op);
const unsigned int _Match_last_val = _mm_cvtsi128_si32(_Match_last);
if (_Check_fit(_Match_last_val)) {
return _Mid1;
}
// The middle part, fit and unfit needle
while (_Mid1 != _Stop1) {
_Rewind_bytes(_Mid1, 16);
const __m128i _Data1 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(_Mid1));
const auto _Match = _mm_cmpestrm(_Data2, _Size_el_2, _Data1, _Part_size_el, _Op);
const unsigned int _Match_val = _mm_cvtsi128_si32(_Match);
if (_Match_val != 0 && (_Check_unfit(_Match_val) || _Check_fit(_Match_val))) {
return _Mid1;
}
}
// The first part, fit and unfit needle, mask out already processed positions
if (const size_t _Tail_bytes_1 = _Size_bytes_1 & 0xF; _Tail_bytes_1 != 0) {
_Mid1 = _First1;
const __m128i _Data1 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(_Mid1));
const auto _Match = _mm_cmpestrm(_Data2, _Size_el_2, _Data1, _Part_size_el, _Op);
const unsigned int _Match_val = _mm_cvtsi128_si32(_Match) & ((1 << _Tail_bytes_1) - 1);
if (_Match_val != 0 && (_Check_unfit(_Match_val) || _Check_fit(_Match_val))) {
return _Mid1;
}
}
return _Last1;
} else {
const __m128i _Data2 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(_First2));
const void* _Tail2 = _First2;
_Advance_bytes(_Tail2, 16);
const void* _Mid1 = _Last1;
_Rewind_bytes(_Mid1, _Size_bytes_2);
const size_t _Size_diff_bytes = _Size_bytes_1 - _Size_bytes_2;
const void* _Stop1 = _First1;
_Advance_bytes(_Stop1, _Size_diff_bytes & 0xF);
#pragma warning(push)
#pragma warning(disable : 4324) // structure was padded due to alignment specifier
const auto _Check = [=, &_Mid1](long _Match) noexcept {
while (_Match != 0) {
const void* _Tmp1 = _Mid1;
unsigned long _Match_last_pos;
// CodeQL [SM02313] Result is always initialized: we just tested that _Match is non-zero.
_BitScanReverse(&_Match_last_pos, _Match);
bool _Match_1st_16 = true;
if (_Match_last_pos != 0) {
_Advance_bytes(_Tmp1, _Match_last_pos * sizeof(_Ty));
const __m128i _Match_data = _mm_loadu_si128(reinterpret_cast<const __m128i*>(_Tmp1));
const __m128i _Cmp_result = _mm_xor_si128(_Data2, _Match_data);
if (!_mm_testz_si128(_Cmp_result, _Cmp_result)) {
_Match_1st_16 = false;
}
}
if (_Match_1st_16) {
const void* _Tail1 = _Tmp1;
_Advance_bytes(_Tail1, 16);
if (memcmp(_Tail1, _Tail2, _Size_bytes_2 - 16) == 0) {
_Mid1 = _Tmp1;
return true;
}
}
_bittestandreset(&_Match, _Match_last_pos);
}
return false;
};
#pragma warning(pop)
// The very last part, just compare, as true match must start with first symbol
const __m128i _Data1_last = _mm_loadu_si128(reinterpret_cast<const __m128i*>(_Mid1));
const __m128i _Match_last = _mm_xor_si128(_Data2, _Data1_last);
if (_mm_testz_si128(_Match_last, _Match_last)) {
// Matched 16 bytes, check the rest
const void* _Tail1 = _Mid1;
_Advance_bytes(_Tail1, 16);
if (memcmp(_Tail1, _Tail2, _Size_bytes_2 - 16) == 0) {
return _Mid1;
}
}
// TRANSITION, DevCom-10689455, the code below could test with _mm_cmpestrc,
// if it has been fused with _mm_cmpestrm.
// The main part, match all characters
while (_Mid1 != _Stop1) {
_Rewind_bytes(_Mid1, 16);
const __m128i _Data1 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(_Mid1));
const auto _Match = _mm_cmpestrm(_Data2, _Part_size_el, _Data1, _Part_size_el, _Op);
const unsigned int _Match_val = _mm_cvtsi128_si32(_Match);
if (_Match_val != 0 && _Check(_Match_val)) {
return _Mid1;
}
}
// The first part, mask out already processed positions
if (const size_t _Tail_bytes_1 = _Size_diff_bytes & 0xF; _Tail_bytes_1 != 0) {
_Mid1 = _First1;
const __m128i _Data1 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(_Mid1));
const auto _Match = _mm_cmpestrm(_Data2, _Part_size_el, _Data1, _Part_size_el, _Op);
const unsigned int _Match_val = _mm_cvtsi128_si32(_Match) & ((1 << _Tail_bytes_1) - 1);
if (_Match_val != 0 && _Check(_Match_val)) {
return _Mid1;
}
}
return _Last1;
}
} else
#endif // !defined(_M_ARM64EC)
{
auto _Ptr1 = static_cast<const _Ty*>(_Last1) - _Count2;
const auto _Ptr2 = static_cast<const _Ty*>(_First2);
for (;;) {
if (*_Ptr1 == *_Ptr2) {
bool _Equal = true;
for (size_t _Idx = 1; _Idx != _Count2; ++_Idx) {
if (_Ptr1[_Idx] != _Ptr2[_Idx]) {
_Equal = false;
break;
}
}
if (_Equal) {
return _Ptr1;
}
}
if (_Ptr1 == _First1) {
return _Last1;
}
--_Ptr1;
}
}
}
} // unnamed namespace
extern "C" {
@ -3757,6 +4003,16 @@ const void* __stdcall __std_search_2(
return __std_search_impl<_Find_traits_2, uint16_t>(_First1, _Last1, _First2, _Count2);
}
const void* __stdcall __std_find_end_1(
const void* const _First1, const void* const _Last1, const void* const _First2, const size_t _Count2) noexcept {
return __std_find_end_impl<_Find_traits_1, uint8_t>(_First1, _Last1, _First2, _Count2);
}
const void* __stdcall __std_find_end_2(
const void* const _First1, const void* const _Last1, const void* const _First2, const size_t _Count2) noexcept {
return __std_find_end_impl<_Find_traits_2, uint16_t>(_First1, _Last1, _First2, _Count2);
}
__declspec(noalias) size_t __stdcall __std_mismatch_1(
const void* const _First1, const void* const _First2, const size_t _Count) noexcept {
return __std_mismatch_impl<_Find_traits_1, uint8_t>(_First1, _First2, _Count);

Просмотреть файл

@ -24,10 +24,6 @@ std/time/time.syn/formatter.year_month_weekday.pass.cpp:1 FAIL
std/time/time.syn/formatter.zoned_time.pass.cpp:0 FAIL
std/time/time.syn/formatter.zoned_time.pass.cpp:1 FAIL
# LLVM-74756: [libc++][test] overload_compare_iterator doesn't support its claimed iterator_category
std/utilities/memory/specialized.algorithms/uninitialized.copy/uninitialized_copy.pass.cpp FAIL
std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp FAIL
# LLVM-90196: [libc++][format] Formatting range with m range-type is incorrect
std/utilities/format/format.range/format.range.formatter/format.functions.format.pass.cpp FAIL
std/utilities/format/format.range/format.range.formatter/format.functions.vformat.pass.cpp FAIL
@ -37,12 +33,8 @@ std/utilities/format/format.range/format.range.fmtset/format.functions.vformat.p
# LLVM-100506: [libc++][test] Precondition violation in rand.dist.uni.real/param_ctor.pass.cpp
std/numerics/rand/rand.dist/rand.dist.uni/rand.dist.uni.real/param_ctor.pass.cpp FAIL
# LLVM-105878: [libc++][test] fp_compare.h includes non-portable <__config>
std/numerics/c.math/cmath.pass.cpp FAIL
std/numerics/numeric.ops/numeric.ops.midpoint/midpoint.float.pass.cpp FAIL
# LLVM-105966: [libc++][test] Fix is_always_lock_free test
std/atomics/atomics.lockfree/is_always_lock_free.cpp FAIL
# LLVM-113609: [libc++][test] Non-rebindable test_alloc in string.capacity/deallocate_size.pass.cpp
std/strings/basic.string/string.capacity/deallocate_size.pass.cpp FAIL
# Non-Standard regex behavior.
# "It seems likely that the test is still non-conforming due to how libc++ handles the 'w' character class."
@ -80,10 +72,9 @@ std/numerics/rand/rand.util/rand.util.canonical/generate_canonical.pass.cpp FAIL
# Test expects __cpp_lib_chrono to have the old value 201611L for P0505R0; we define the C++20 value 201907L for P1466R3.
std/language.support/support.limits/support.limits.general/chrono.version.compile.pass.cpp FAIL
# Tests expect __cpp_lib_ranges to have the old value 201811L for P0896R4; we define the C++20 value 201911L for P1716R3.
# Test expects __cpp_lib_freestanding_algorithm to not be defined before C++26; we define it unconditionally.
# Test expects __cpp_lib_shift to have the C++20 value 201806L for P0769R2; we define the C++23 value 202202L for P2440R1.
std/language.support/support.limits/support.limits.general/algorithm.version.compile.pass.cpp FAIL
std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp FAIL
std/language.support/support.limits/support.limits.general/iterator.version.compile.pass.cpp FAIL
# Test expects __cpp_lib_print to have the old value 202207L for P2093R14; we define the C++23 value 202406L for P3235R3.
std/language.support/support.limits/support.limits.general/ostream.version.compile.pass.cpp FAIL
@ -157,19 +148,6 @@ std/language.support/support.limits/support.limits.general/format.version.compil
# libc++ doesn't implement LWG-3670
std/ranges/range.factories/range.iota.view/iterator/member_typedefs.compile.pass.cpp FAIL
# libc++ doesn't implement LWG-3870
std/utilities/memory/specialized.algorithms/specialized.construct/ranges_construct_at.pass.cpp FAIL
std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct.pass.cpp FAIL
std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct_n.pass.cpp FAIL
std/utilities/memory/specialized.algorithms/uninitialized.construct.value/ranges_uninitialized_value_construct.pass.cpp FAIL
std/utilities/memory/specialized.algorithms/uninitialized.construct.value/ranges_uninitialized_value_construct_n.pass.cpp FAIL
std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy.pass.cpp FAIL
std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy_n.pass.cpp FAIL
std/utilities/memory/specialized.algorithms/uninitialized.fill/ranges_uninitialized_fill.pass.cpp FAIL
std/utilities/memory/specialized.algorithms/uninitialized.fill.n/ranges_uninitialized_fill_n.pass.cpp FAIL
std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move.pass.cpp FAIL
std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move_n.pass.cpp FAIL
# libc++ doesn't implement LWG-4013
std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/ctor.default.pass.cpp FAIL
std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/ctor.iter.pass.cpp FAIL
@ -237,6 +215,9 @@ std/language.support/support.limits/support.limits.general/cstdlib.version.compi
# P2255R2 "Type Traits To Detect References Binding To Temporaries"
std/language.support/support.limits/support.limits.general/type_traits.version.compile.pass.cpp FAIL
# P2674R1 is_implicit_lifetime
std/utilities/meta/meta.unary/meta.unary.prop/is_implicit_lifetime.pass.cpp FAIL
# *** MISSING COMPILER FEATURES ***
# P1169R4 static operator()
@ -247,15 +228,8 @@ std/thread/futures/futures.task/futures.task.members/ctad.static.compile.pass.cp
std/utilities/function.objects/func.wrap/func.wrap.func/func.wrap.func.con/ctad.static.compile.pass.cpp:0 FAIL
std/utilities/function.objects/func.wrap/func.wrap.func/func.wrap.func.con/ctad.static.compile.pass.cpp:1 FAIL
# P2128R6 Multidimensional Subscript Operator
std/containers/views/mdspan/mdspan/index_operator.pass.cpp:0 FAIL
std/containers/views/mdspan/mdspan/index_operator.pass.cpp:1 FAIL
# *** MISSING LWG ISSUE RESOLUTIONS ***
# LWG-2192 "Validity and return type of std::abs(0u) is unclear" (resolution is missing in UCRT, DevCom-10331466)
std/depr/depr.c.headers/stdlib_h.pass.cpp FAIL
# LWG-2503 "multiline option should be added to syntax_option_type"
std/re/re.const/re.matchflag/match_multiline.pass.cpp FAIL
std/re/re.const/re.matchflag/match_not_eol.pass.cpp FAIL
@ -276,11 +250,6 @@ std/thread/thread.condition/notify_all_at_thread_exit_lwg3343.pass.cpp SKIPPED
# *** C1XX COMPILER BUGS ***
# DevCom-409222 VSO-752709 "Constructing rvalue reference from non-reference-related lvalue reference"
# Reportedly fixed in VS 2019 16.10, test is still failing, need to investigate.
std/utilities/meta/meta.unary/meta.unary.prop/is_constructible.pass.cpp:0 FAIL
std/utilities/meta/meta.unary/meta.unary.prop/is_constructible.pass.cpp:1 FAIL
# VSO-1271673 "static analyzer doesn't know about short-circuiting"
# Note: The :1 (ASan) configuration doesn't run static analysis.
std/algorithms/alg.sorting/alg.sort/partial.sort/partial_sort.pass.cpp:0 FAIL
@ -747,9 +716,6 @@ std/re/re.alg/re.alg.search/basic.locale.pass.cpp FAIL
std/re/re.alg/re.alg.search/ecma.locale.pass.cpp FAIL
std/re/re.alg/re.alg.search/extended.locale.pass.cpp FAIL
# Not analyzed. Error mentions allocator<const T>.
std/utilities/memory/specialized.algorithms/specialized.construct/construct_at.pass.cpp FAIL
# Not analyzed. Seems to force a sign conversion error?
std/iterators/iterator.primitives/iterator.operations/advance.pass.cpp SKIPPED
@ -832,6 +798,8 @@ std/containers/sequences/vector/vector.cons/construct_size_value.pass.cpp:0 FAIL
std/containers/sequences/vector/vector.cons/construct_size_value.pass.cpp:1 FAIL
std/containers/sequences/vector/vector.cons/construct_size_value_alloc.pass.cpp:0 FAIL
std/containers/sequences/vector/vector.cons/construct_size_value_alloc.pass.cpp:1 FAIL
std/containers/views/mdspan/mdspan/index_operator.pass.cpp:0 FAIL
std/containers/views/mdspan/mdspan/index_operator.pass.cpp:1 FAIL
# Not analyzed. Looks like a test bug, assuming that hash<vector<bool>> is constexpr.
std/containers/sequences/vector.bool/enabled_hash.pass.cpp FAIL
@ -858,9 +826,6 @@ std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op
std/ranges/range.adaptors/range.lazy.split/ctor.copy_move.pass.cpp FAIL
std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/iter_swap.pass.cpp FAIL
# Not analyzed. Checking whether packaged_task is constructible from an allocator and a packaged_task of a different type.
std/thread/futures/futures.task/futures.task.members/ctor2.compile.pass.cpp FAIL
# Not analyzed.
# MSVC error C2131: expression did not evaluate to a constant
# MSVC note: failure was caused by a read of an uninitialized symbol

Просмотреть файл

@ -760,7 +760,7 @@ void test_hash_monostate() {
static_assert(std::is_copy_constructible<H>::value, "");
}
{
test_hash_enabled_for_type<std::monostate>();
test_hash_enabled<std::monostate>();
}
}
@ -795,18 +795,18 @@ namespace hash {
void test_hash_variant_enabled() {
{
#ifndef __EDG__ // TRANSITION, DevCom-10107834
test_hash_enabled_for_type<std::variant<int> >();
test_hash_enabled_for_type<std::variant<int*, long, double, const int> >();
test_hash_enabled<std::variant<int> >();
test_hash_enabled<std::variant<int*, long, double, const int> >();
#endif // ^^^ no workaround ^^^
}
{
test_hash_disabled_for_type<std::variant<int, A>>();
test_hash_disabled_for_type<std::variant<const A, void*>>();
test_hash_disabled<std::variant<int, A>>();
test_hash_disabled<std::variant<const A, void*>>();
}
{
#ifndef __EDG__ // TRANSITION, DevCom-10107834
test_hash_enabled_for_type<std::variant<int, B>>();
test_hash_enabled_for_type<std::variant<const B, int>>();
test_hash_enabled<std::variant<int, B>>();
test_hash_enabled<std::variant<const B, int>>();
#endif // ^^^ no workaround ^^^
}
}

Просмотреть файл

@ -701,7 +701,7 @@ int run_test()
struct TestConstexpr : public std::any {
constexpr TestConstexpr() : std::any() {}
};
static TEST_CONSTINIT std::any a;
TEST_CONSTINIT static std::any a;
(void)a;
}
#endif // ^^^ no workaround ^^^

Просмотреть файл

@ -862,18 +862,18 @@ int run_test()
}
{
#ifndef __EDG__ // TRANSITION, DevCom-10107834
test_hash_enabled_for_type<std::optional<int> >();
test_hash_enabled_for_type<std::optional<int*> >();
test_hash_enabled_for_type<std::optional<const int> >();
test_hash_enabled_for_type<std::optional<int* const> >();
test_hash_enabled<std::optional<int> >();
test_hash_enabled<std::optional<int*> >();
test_hash_enabled<std::optional<const int> >();
test_hash_enabled<std::optional<int* const> >();
#endif // ^^^ no workaround ^^^
test_hash_disabled_for_type<std::optional<A>>();
test_hash_disabled_for_type<std::optional<const A>>();
test_hash_disabled<std::optional<A>>();
test_hash_disabled<std::optional<const A>>();
#ifndef __EDG__ // TRANSITION, DevCom-10107834
test_hash_enabled_for_type<std::optional<B>>();
test_hash_enabled_for_type<std::optional<const B>>();
test_hash_enabled<std::optional<B>>();
test_hash_enabled<std::optional<const B>>();
#endif // ^^^ no workaround ^^^
}

Просмотреть файл

@ -12,6 +12,7 @@
#include <functional>
#include <limits>
#include <list>
#include <numeric>
#include <random>
#include <stdexcept>
#include <string>
@ -34,6 +35,74 @@ using namespace std;
#pragma clang diagnostic ignored "-Wc++17-extensions" // constexpr if is a C++17 extension
#endif // __clang__
template <class InIt, class OutIt, class BinOp>
OutIt last_known_good_adj_diff(InIt first, InIt last, OutIt dest, BinOp binop) {
if (first == last) {
return dest;
}
auto val = *first;
*dest = val;
for (++first, ++dest; first != last; ++first, ++dest) {
auto tmp = *first;
*dest = binop(tmp, val);
val = tmp;
}
return dest;
}
template <class T>
void test_case_adj_diff(const vector<T>& input, vector<T>& output_expected, vector<T>& output_actual) {
// Avoid truncation warnings:
const auto subtract = [](const T& left, const T& right) { return static_cast<T>(left - right); };
const auto expected = last_known_good_adj_diff(input.begin(), input.end(), output_expected.begin(), subtract);
const auto actual = adjacent_difference(input.begin(), input.end(), output_actual.begin(), subtract);
assert(actual - output_actual.begin() == expected - output_expected.begin());
assert(output_actual == output_expected);
}
template <class T>
void test_adjacent_difference(mt19937_64& gen) {
using Limits = numeric_limits<T>;
uniform_int_distribution<conditional_t<sizeof(T) == 1, int, T>> dis(
is_signed_v<T> ? static_cast<T>(Limits::min() / 2) : Limits::min(),
is_signed_v<T> ? static_cast<T>(Limits::max() / 2) : Limits::max());
vector<T> input;
vector<T> output_expected;
vector<T> output_actual;
for (const auto& v : {&input, &output_expected, &output_actual}) {
v->reserve(dataCount);
}
test_case_adj_diff(input, output_expected, output_actual);
for (size_t attempts = 0; attempts < dataCount; ++attempts) {
input.push_back(static_cast<T>(dis(gen)));
for (const auto& v : {&output_expected, &output_actual}) {
v->assign(input.size(), 0);
}
test_case_adj_diff(input, output_expected, output_actual);
}
}
void test_adjacent_difference_with_heterogeneous_types() {
const vector<unsigned char> input = {10, 70, 20, 90};
vector<int> output(4);
const auto result = adjacent_difference(input.begin(), input.end(), output.begin());
assert(result == output.end());
const vector<int> expected = {10, 60, -50, 70};
assert(output == expected);
}
template <class FwdIt, class T>
ptrdiff_t last_known_good_count(FwdIt first, FwdIt last, T v) {
ptrdiff_t result = 0;
@ -895,6 +964,18 @@ void test_swap_arrays(mt19937_64& gen) {
}
void test_vector_algorithms(mt19937_64& gen) {
test_adjacent_difference<char>(gen);
test_adjacent_difference<signed char>(gen);
test_adjacent_difference<unsigned char>(gen);
test_adjacent_difference<short>(gen);
test_adjacent_difference<unsigned short>(gen);
test_adjacent_difference<int>(gen);
test_adjacent_difference<unsigned int>(gen);
test_adjacent_difference<long long>(gen);
test_adjacent_difference<unsigned long long>(gen);
test_adjacent_difference_with_heterogeneous_types();
test_count<char>(gen);
test_count<signed char>(gen);
test_count<unsigned char>(gen);
@ -1247,22 +1328,57 @@ void test_case_string_find_last_of(const basic_string<T>& input_haystack, const
assert(expected == actual);
}
template <class T>
void test_case_string_find_str(const basic_string<T>& input_haystack, const basic_string<T>& input_needle) {
ptrdiff_t expected;
if (input_needle.empty()) {
expected = 0;
} else {
const auto expected_iter = last_known_good_search(
input_haystack.begin(), input_haystack.end(), input_needle.begin(), input_needle.end());
if (expected_iter != input_haystack.end()) {
expected = expected_iter - input_haystack.begin();
} else {
expected = -1;
}
}
const auto actual = static_cast<ptrdiff_t>(input_haystack.find(input_needle));
assert(expected == actual);
}
template <class T, class D>
void test_basic_string_dis(mt19937_64& gen, D& dis) {
basic_string<T> input_haystack;
basic_string<T> input_needle;
basic_string<T> temp;
input_haystack.reserve(haystackDataCount);
input_needle.reserve(needleDataCount);
temp.reserve(needleDataCount);
for (;;) {
input_needle.clear();
test_case_string_find_first_of(input_haystack, input_needle);
test_case_string_find_last_of(input_haystack, input_needle);
test_case_string_find_str(input_haystack, input_needle);
for (size_t attempts = 0; attempts < needleDataCount; ++attempts) {
input_needle.push_back(static_cast<T>(dis(gen)));
test_case_string_find_first_of(input_haystack, input_needle);
test_case_string_find_last_of(input_haystack, input_needle);
test_case_string_find_str(input_haystack, input_needle);
// For large needles the chance of a match is low, so test a guaranteed match
if (input_haystack.size() > input_needle.size() * 2) {
uniform_int_distribution<size_t> pos_dis(0, input_haystack.size() - input_needle.size());
const size_t pos = pos_dis(gen);
const auto overwritten_first = input_haystack.begin() + static_cast<ptrdiff_t>(pos);
temp.assign(overwritten_first, overwritten_first + static_cast<ptrdiff_t>(input_needle.size()));
copy(input_needle.begin(), input_needle.end(), overwritten_first);
test_case_string_find_str(input_haystack, input_needle);
copy(temp.begin(), temp.end(), overwritten_first);
}
}
if (input_haystack.size() == haystackDataCount) {

Просмотреть файл

@ -1,9 +1,14 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include <array>
#include <cassert>
#include <functional>
#include <map>
#include <memory>
#include <set>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <utility>
@ -142,23 +147,282 @@ void test_emplaces(FirstValue& val1, Values&... valn) {
}
}
int main() {
// This tests that unordered_(map|set) detect cases they can avoid allocating.
// It isn't strictly required by the standard, but we think it should be applicable to good implementations.
template <class SetContainer>
void test_emplace_for_unique_set() {
int lvalueInt{42};
test_emplaces<unordered_set<int, hash<int>, equal_to<>, TestAlloc<int>>>(lvalueInt);
using MapType = unordered_map<int, int, hash<int>, equal_to<>, TestAlloc<pair<const int, int>>>;
test_emplaces<MapType>(lvalueInt, lvalueInt);
test_emplaces<SetContainer>(lvalueInt);
}
template <class MapContainer>
void test_emplace_for_unique_map() {
int lvalueInt{42};
test_emplaces<MapContainer>(lvalueInt, lvalueInt);
pair<const int, int> lvalueConstPair{42, 65};
pair<int, int> lvaluePair{42, 65};
pair<const int&, int> lvalueConstRefPair{lvalueInt, 65};
pair<int&, int> lvalueRefPair{lvalueInt, 65};
pair<const int&&, int> lvalueConstRefRefPair{move(lvalueInt), 65};
pair<int&&, int> lvalueRefRefPair{move(lvalueInt), 65};
test_emplaces<MapType>(lvalueConstPair);
test_emplaces<MapType>(lvaluePair);
test_emplaces<MapType>(lvalueConstRefPair);
test_emplaces<MapType>(lvalueRefPair);
test_emplaces<MapType>(lvalueConstRefRefPair);
test_emplaces<MapType>(lvalueRefRefPair);
test_emplaces<MapContainer>(lvalueConstPair);
test_emplaces<MapContainer>(lvaluePair);
test_emplaces<MapContainer>(lvalueConstRefPair);
test_emplaces<MapContainer>(lvalueRefPair);
test_emplaces<MapContainer>(lvalueConstRefRefPair);
test_emplaces<MapContainer>(lvalueRefRefPair);
#if _HAS_CXX23
tuple<const int, int> lvalueConstTuple{42, 65};
tuple<int, int> lvalueTuple{42, 65};
tuple<const int&, int> lvalueConstRefTuple{lvalueInt, 65};
tuple<int&, int> lvalueRefTuple{lvalueInt, 65};
tuple<const int&&, int> lvalueConstRefRefTuple{move(lvalueInt), 65};
tuple<int&&, int> lvalueRefRefTuple{move(lvalueInt), 65};
test_emplaces<MapContainer>(lvalueConstTuple);
test_emplaces<MapContainer>(lvalueTuple);
test_emplaces<MapContainer>(lvalueConstRefTuple);
test_emplaces<MapContainer>(lvalueRefTuple);
test_emplaces<MapContainer>(lvalueConstRefRefTuple);
test_emplaces<MapContainer>(lvalueRefRefTuple);
array<int, 2> arr{42, 65};
array<const int, 2> constArr{42, 65};
test_emplaces<MapContainer>(arr);
test_emplaces<MapContainer>(constArr);
#endif // _HAS_CXX23
tuple<int> tupleIntSixtyFive{65};
tuple<const int> lvalueConstOneTuple{42};
tuple<int> lvalueOneTuple{42};
tuple<const int&> lvalueConstRefOneTuple{lvalueInt};
tuple<int&> lvalueRefOneTuple{lvalueInt};
test_emplaces<MapContainer>(piecewise_construct, lvalueConstOneTuple, tupleIntSixtyFive);
test_emplaces<MapContainer>(piecewise_construct, lvalueOneTuple, tupleIntSixtyFive);
test_emplaces<MapContainer>(piecewise_construct, lvalueConstRefOneTuple, tupleIntSixtyFive);
test_emplaces<MapContainer>(piecewise_construct, lvalueRefOneTuple, tupleIntSixtyFive);
}
// also test that the optimization strategy does not mishandle volatile arguments
template <class SetContainer>
void test_volatile_arguments_for_unique_set() {
using Key = typename SetContainer::value_type;
SetContainer s;
volatile Key x = 0;
const volatile Key& cx = x;
s.emplace(x);
x = 1;
s.emplace(move(x));
x = 2;
s.emplace(cx);
x = 3;
s.emplace(move(cx));
x = 4;
s.emplace_hint(s.end(), x);
x = 5;
s.emplace_hint(s.end(), move(x));
x = 6;
s.emplace_hint(s.end(), cx);
x = 7;
s.emplace_hint(s.end(), move(cx));
assert((s == SetContainer{0, 1, 2, 3, 4, 5, 6, 7}));
}
template <class MapContainer, class PairLike>
void test_pair_like_volatile_for_unique_map() {
using First = tuple_element_t<0, PairLike>;
using Second = tuple_element_t<1, PairLike>;
MapContainer m;
volatile remove_cv_t<remove_reference_t<First>> x = 0;
{
PairLike p{static_cast<First>(x), Second{}};
m.emplace(p);
}
x = 1;
{
PairLike p{static_cast<First>(x), Second{}};
m.emplace(move(p));
}
x = 2;
{
PairLike p{static_cast<First>(x), Second{}};
const auto& cp = p;
m.emplace(cp);
}
x = 3;
{
PairLike p{static_cast<First>(x), Second{}};
const auto& cp = p;
m.emplace(move(cp));
}
x = 4;
{
PairLike p{static_cast<First>(x), Second{}};
m.emplace_hint(m.end(), p);
}
x = 5;
{
PairLike p{static_cast<First>(x), Second{}};
m.emplace_hint(m.end(), move(p));
}
x = 6;
{
PairLike p{static_cast<First>(x), Second{}};
const auto& cp = p;
m.emplace_hint(m.end(), cp);
}
x = 7;
{
PairLike p{static_cast<First>(x), Second{}};
const auto& cp = p;
m.emplace_hint(m.end(), move(cp));
}
assert((m == MapContainer{{0, {}}, {1, {}}, {2, {}}, {3, {}}, {4, {}}, {5, {}}, {6, {}}, {7, {}}}));
}
template <class MapContainer, class Tuple>
void test_piecewise_volatile_for_unique_map() {
using First = tuple_element_t<0, Tuple>;
MapContainer m;
volatile remove_cv_t<remove_reference_t<First>> x = 0;
{
Tuple tp{static_cast<First>(x)};
m.emplace(piecewise_construct, tp, tuple<>{});
}
x = 1;
{
Tuple tp{static_cast<First>(x)};
m.emplace(piecewise_construct, move(tp), tuple<>{});
}
x = 2;
{
Tuple tp{static_cast<First>(x)};
const auto& ctp = tp;
m.emplace(piecewise_construct, ctp, tuple<>{});
}
x = 3;
{
Tuple tp{static_cast<First>(x)};
const auto& ctp = tp;
m.emplace(piecewise_construct, move(ctp), tuple<>{});
}
x = 4;
{
Tuple tp{static_cast<First>(x)};
m.emplace_hint(m.end(), piecewise_construct, tp, tuple<>{});
}
x = 5;
{
Tuple tp{static_cast<First>(x)};
m.emplace_hint(m.end(), piecewise_construct, move(tp), tuple<>{});
}
x = 6;
{
Tuple tp{static_cast<First>(x)};
const auto& ctp = tp;
m.emplace_hint(m.end(), piecewise_construct, ctp, tuple<>{});
}
x = 7;
{
Tuple tp{static_cast<First>(x)};
const auto& ctp = tp;
m.emplace_hint(m.end(), piecewise_construct, move(ctp), tuple<>{});
}
assert((m == MapContainer{{0, {}}, {1, {}}, {2, {}}, {3, {}}, {4, {}}, {5, {}}, {6, {}}, {7, {}}}));
}
template <class MapContainer>
void test_volatile_arguments_for_unique_map() {
using Key = typename MapContainer::key_type;
using Mapped = typename MapContainer::mapped_type;
{
volatile Key x = 0;
const volatile Key& cx = x;
MapContainer m;
m.emplace(x, Mapped{});
x = 1;
m.emplace(move(x), Mapped{});
x = 2;
m.emplace(cx, Mapped{});
x = 3;
m.emplace(move(cx), Mapped{});
x = 4;
m.emplace_hint(m.end(), x, Mapped{});
x = 5;
m.emplace_hint(m.end(), move(x), Mapped{});
x = 6;
m.emplace_hint(m.end(), cx, Mapped{});
x = 7;
m.emplace_hint(m.end(), move(cx), Mapped{});
assert((m == MapContainer{{0, {}}, {1, {}}, {2, {}}, {3, {}}, {4, {}}, {5, {}}, {6, {}}, {7, {}}}));
}
test_pair_like_volatile_for_unique_map<MapContainer, pair<volatile Key, Mapped>>();
test_pair_like_volatile_for_unique_map<MapContainer, pair<const volatile Key, Mapped>>();
test_pair_like_volatile_for_unique_map<MapContainer, pair<volatile Key&, Mapped>>();
test_pair_like_volatile_for_unique_map<MapContainer, pair<const volatile Key&, Mapped>>();
test_pair_like_volatile_for_unique_map<MapContainer, pair<volatile Key&&, Mapped>>();
test_pair_like_volatile_for_unique_map<MapContainer, pair<const volatile Key&&, Mapped>>();
#if _HAS_CXX23
test_pair_like_volatile_for_unique_map<MapContainer, tuple<volatile Key, Mapped>>();
test_pair_like_volatile_for_unique_map<MapContainer, tuple<const volatile Key, Mapped>>();
test_pair_like_volatile_for_unique_map<MapContainer, tuple<volatile Key&, Mapped>>();
test_pair_like_volatile_for_unique_map<MapContainer, tuple<const volatile Key&, Mapped>>();
test_pair_like_volatile_for_unique_map<MapContainer, tuple<volatile Key&&, Mapped>>();
test_pair_like_volatile_for_unique_map<MapContainer, tuple<const volatile Key&&, Mapped>>();
test_pair_like_volatile_for_unique_map<MapContainer, array<volatile Key, 2>>();
test_pair_like_volatile_for_unique_map<MapContainer, array<const volatile Key, 2>>();
#endif // _HAS_CXX23
test_piecewise_volatile_for_unique_map<MapContainer, tuple<volatile Key>>();
test_piecewise_volatile_for_unique_map<MapContainer, tuple<const volatile Key>>();
test_piecewise_volatile_for_unique_map<MapContainer, tuple<volatile Key&>>();
test_piecewise_volatile_for_unique_map<MapContainer, tuple<const volatile Key&>>();
}
int main() {
// This tests that unordered_(map|set) detect cases they can avoid allocating.
// It isn't strictly required by the standard, but we think it should be applicable to good implementations.
test_emplace_for_unique_set<set<int, less<>, TestAlloc<int>>>();
test_emplace_for_unique_set<unordered_set<int, hash<int>, equal_to<>, TestAlloc<int>>>();
test_emplace_for_unique_map<map<int, int, less<>, TestAlloc<pair<const int, int>>>>();
test_emplace_for_unique_map<unordered_map<int, int, hash<int>, equal_to<>, TestAlloc<pair<const int, int>>>>();
test_volatile_arguments_for_unique_set<set<int>>();
test_volatile_arguments_for_unique_set<unordered_set<int>>();
test_volatile_arguments_for_unique_map<map<int, long>>();
test_volatile_arguments_for_unique_map<unordered_map<int, long>>();
}