diff --git a/stl/inc/algorithm b/stl/inc/algorithm index ed4466cfcd..66cb0f2773 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -4927,7 +4927,7 @@ _CONSTEXPR20 _OutIt reverse_copy(_BidIt _First, _BidIt _Last, _OutIt _Dest) { #if _HAS_IF_CONSTEXPR && _USE_STD_VECTOR_ALGORITHMS using _Elem = remove_pointer_t; using _DestElem = remove_pointer_t; - constexpr bool _Allow_vectorization = conjunction_v, remove_const_t<_DestElem>>, + constexpr bool _Allow_vectorization = conjunction_v, _DestElem>, is_pointer, is_trivially_copyable<_Elem>, negation>>; constexpr size_t _Nx = sizeof(_Elem); @@ -4970,6 +4970,95 @@ _FwdIt reverse_copy(_ExPo&&, _BidIt _First, _BidIt _Last, _FwdIt _Dest) noexcept _REQUIRE_PARALLEL_ITERATOR(_FwdIt); return _STD reverse_copy(_First, _Last, _Dest); } + +#ifdef __cpp_lib_concepts +namespace ranges { + // ALIAS TEMPLATE reverse_copy_result + template + using reverse_copy_result = in_out_result<_In, _Out>; + + // VARIABLE ranges::reverse_copy + class _Reverse_copy_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, weakly_incrementable _Out> + requires indirectly_copyable<_It, _Out> + constexpr reverse_copy_result<_It, _Out> operator()(_It _First, _Se _Last, _Out _Result) const { + // clang-format on + _Adl_verify_range(_First, _Last); + auto _UFirst = _Get_unwrapped(_STD move(_First)); + auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last)); + _Seek_wrapped(_First, _ULast); + _Result = _Reverse_copy_common(_STD move(_UFirst), _STD move(_ULast), _STD move(_Result)); + return {_STD move(_First), _STD move(_Result)}; + } + + // clang-format off + template + requires indirectly_copyable, _Out> + constexpr reverse_copy_result, _Out> operator()(_Rng&& _Range, _Out _Result) const { + // clang-format on + if constexpr (common_range<_Rng>) { + _Result = _Reverse_copy_common(_Ubegin(_Range), _Uend(_Range), _STD move(_Result)); + return {_RANGES end(_Range), _STD move(_Result)}; + } else { + auto _ULast = _Get_final_iterator_unwrapped(_Range); + _Result = _Reverse_copy_common(_Ubegin(_Range), _ULast, _STD move(_Result)); + return {_Rewrap_iterator(_Range, _STD move(_ULast)), _STD move(_Result)}; + } + } + + private: + template + _NODISCARD static constexpr _Out _Reverse_copy_common(const _It _First, _It _Last, _Out _Result) { + _STL_INTERNAL_STATIC_ASSERT(bidirectional_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_copyable<_It, _Out>); + +#if _USE_STD_VECTOR_ALGORITHMS + if constexpr (contiguous_iterator<_It> && contiguous_iterator<_Out>) { + using _Elem = remove_reference_t>; + using _DestElem = remove_reference_t>; + constexpr bool _Allow_vectorization = conjunction_v, _DestElem>, + is_trivially_copyable<_Elem>, negation>>; + constexpr size_t _Nx = sizeof(_Elem); + +#pragma warning(suppress : 6326) // Potential comparison of a constant with another constant + if constexpr (_Allow_vectorization && _Nx <= 8 && (_Nx & (_Nx - 1)) == 0) { + if (!_STD is_constant_evaluated()) { + _Elem* const _First_addr = _STD to_address(_First); + _Elem* const _Last_addr = _STD to_address(_Last); + _DestElem* const _Result_addr = _STD to_address(_Result); + if constexpr (_Nx == 1) { + __std_reverse_copy_trivially_copyable_1(_First_addr, _Last_addr, _Result_addr); + } else if constexpr (_Nx == 2) { + __std_reverse_copy_trivially_copyable_2(_First_addr, _Last_addr, _Result_addr); + } else if constexpr (_Nx == 4) { + __std_reverse_copy_trivially_copyable_4(_First_addr, _Last_addr, _Result_addr); + } else { + __std_reverse_copy_trivially_copyable_8(_First_addr, _Last_addr, _Result_addr); + } + + _Result += _Last - _First; + return _Result; + } + } + } +#endif // _USE_STD_VECTOR_ALGORITHMS + + for (; _First != _Last; ++_Result) { + *_Result = *--_Last; + } + + return _Result; + } + }; + + inline constexpr _Reverse_copy_fn reverse_copy{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts #endif // _HAS_CXX17 #ifdef __cpp_lib_concepts diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index 0e766db6fd..de785c1255 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -1063,6 +1063,16 @@ constexpr void test_fwd_write() { with_forward_ranges, Element1>::call(); } +template +constexpr void test_bidi_write() { + with_bidirectional_ranges, Element1>::call(); +} + +template +constexpr void test_contiguous_write() { + with_contiguous_ranges, Element1>::call(); +} + template constexpr void test_read() { with_input_iterators::call(); diff --git a/tests/std/test.lst b/tests/std/test.lst index 04b21077e1..b5475827f6 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -238,7 +238,6 @@ tests\P0769R2_shift_left_shift_right tests\P0784R7_library_support_for_more_constexpr_containers tests\P0811R3_midpoint_lerp tests\P0896R4_P1614R2_comparisons -tests\P0896R4_ranges_algorithm_machinery tests\P0896R4_ranges_alg_adjacent_find tests\P0896R4_ranges_alg_all_of tests\P0896R4_ranges_alg_any_of @@ -282,6 +281,7 @@ tests\P0896R4_ranges_alg_replace_copy tests\P0896R4_ranges_alg_replace_copy_if tests\P0896R4_ranges_alg_replace_if tests\P0896R4_ranges_alg_reverse +tests\P0896R4_ranges_alg_reverse_copy tests\P0896R4_ranges_alg_rotate tests\P0896R4_ranges_alg_rotate_copy tests\P0896R4_ranges_alg_sample @@ -297,6 +297,7 @@ tests\P0896R4_ranges_alg_transform_binary tests\P0896R4_ranges_alg_transform_unary tests\P0896R4_ranges_alg_unique tests\P0896R4_ranges_alg_unique_copy +tests\P0896R4_ranges_algorithm_machinery tests\P0896R4_ranges_iterator_machinery tests\P0896R4_ranges_range_machinery tests\P0896R4_ranges_subrange diff --git a/tests/std/tests/P0896R4_ranges_alg_reverse_copy/env.lst b/tests/std/tests/P0896R4_ranges_alg_reverse_copy/env.lst new file mode 100644 index 0000000000..f3ccc8613c --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_reverse_copy/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_reverse_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_reverse_copy/test.cpp new file mode 100644 index 0000000000..6a02203fdb --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_reverse_copy/test.cpp @@ -0,0 +1,163 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +// Validate that reverse_copy_result aliases in_out_result +STATIC_ASSERT(same_as, ranges::in_out_result>); + +// Validate dangling story +STATIC_ASSERT(same_as{}, nullptr_to)), + ranges::reverse_copy_result>); +STATIC_ASSERT(same_as{}, nullptr_to)), + ranges::reverse_copy_result>); + +struct nontrivial_int { + int val; + + constexpr nontrivial_int(int i = 0) noexcept : val{i} {} + constexpr nontrivial_int(const nontrivial_int& that) noexcept : val{that.val} {} + constexpr nontrivial_int& operator=(const nontrivial_int& that) noexcept { + val = that.val; + return *this; + } + + auto operator<=>(const nontrivial_int&) const = default; +}; + +struct instantiator { + static constexpr nontrivial_int input[] = {13, 42, 1367}; + static constexpr nontrivial_int expected[] = {1367, 42, 13}; + + template > Out> + static constexpr void call() { + if constexpr (!ranges::contiguous_range) { // the vectorized tests below have plenty of contiguous coverage + using ranges::reverse_copy, ranges::reverse_copy_result, ranges::begin, ranges::end, ranges::equal, + ranges::iterator_t; + + { // Validate iterator overload + nontrivial_int output[3]; + In wrapped_input{input}; + const same_as, Out>> auto result = + reverse_copy(wrapped_input.begin(), wrapped_input.end(), Out{output}); + assert(result.in == wrapped_input.end()); + assert(result.out.peek() == end(output)); + assert(equal(output, expected)); + } + { // Validate range overload + nontrivial_int output[3]; + In wrapped_input{input}; + const same_as, Out>> auto result = + reverse_copy(wrapped_input, Out{output}); + assert(result.in == wrapped_input.end()); + assert(result.out.peek() == end(output)); + assert(equal(output, expected)); + } + + { // Validate iterator overload, empty range + nontrivial_int output[3]; + In wrapped_input{}; + const same_as, Out>> auto result = + reverse_copy(wrapped_input.begin(), wrapped_input.end(), Out{output}); + assert(result.in == wrapped_input.end()); + assert(result.out.peek() == begin(output)); + } + { // Validate range overload, empty range + nontrivial_int output[3]; + In wrapped_input{}; + const same_as, Out>> auto result = + reverse_copy(wrapped_input, Out{output}); + assert(result.in == wrapped_input.end()); + assert(result.out.peek() == begin(output)); + } + } + } +}; + +template +struct bytes { + unsigned char storage[N]; + + constexpr bytes() { + ranges::fill(storage, static_cast(-1)); + } + + constexpr bytes(unsigned char base) { + iota(storage, storage + N, base); + } + + bool operator==(const bytes&) const = default; +}; + +struct test_vector { + template > Out> + static constexpr void call() { + using ranges::reverse_copy, ranges::reverse_copy_result, ranges::begin, ranges::end, ranges::equal, + ranges::iterator_t, ranges::range_value_t; + + const range_value_t input[3] = {0x10, 0x20, 0x30}; + const range_value_t expected[3] = {0x30, 0x20, 0x10}; + + { // Validate iterator overload, vectorizable + range_value_t output[3]; + In wrapped_input{input}; + const same_as, Out>> auto result = + reverse_copy(wrapped_input.begin(), wrapped_input.end(), Out{output}); + assert(result.in == wrapped_input.end()); + assert(result.out.peek() == end(output)); + assert(equal(output, expected)); + } + { // Validate range overload, vectorizable + range_value_t output[3]; + In wrapped_input{input}; + const same_as, Out>> auto result = + reverse_copy(wrapped_input, Out{output}); + assert(result.in == wrapped_input.end()); + assert(result.out.peek() == end(output)); + assert(equal(output, expected)); + } + + { // Validate iterator overload, vectorizable empty + range_value_t output[3]; + In wrapped_input{}; + const same_as, Out>> auto result = + reverse_copy(wrapped_input.begin(), wrapped_input.end(), Out{output}); + assert(result.in == wrapped_input.end()); + assert(result.out.peek() == begin(output)); + } + { // Validate range overload, vectorizable empty + range_value_t output[3]; + In wrapped_input{}; + const same_as, Out>> auto result = + reverse_copy(wrapped_input, Out{output}); + assert(result.in == wrapped_input.end()); + assert(result.out.peek() == begin(output)); + } + } +}; + +int main() { + STATIC_ASSERT((test_bidi_write(), true)); + test_bidi_write(); + + STATIC_ASSERT((test_contiguous_write, bytes<1>>(), true)); + STATIC_ASSERT((test_contiguous_write, bytes<2>>(), true)); + STATIC_ASSERT((test_contiguous_write, bytes<4>>(), true)); + STATIC_ASSERT((test_contiguous_write, bytes<8>>(), true)); + STATIC_ASSERT((test_contiguous_write, bytes<3>>(), true)); + test_contiguous_write, bytes<1>>(); + test_contiguous_write, bytes<2>>(); + test_contiguous_write, bytes<4>>(); + test_contiguous_write, bytes<8>>(); + test_contiguous_write, bytes<3>>(); +}