Skip to content
2 changes: 2 additions & 0 deletions include/oneapi/dpl/pstl/glue_algorithm_ranges_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ struct __replace_fn;
struct __reverse_fn;
struct __reverse_copy_fn;
struct __mismatch_fn;
struct __starts_with_fn;
struct __ends_with_fn;
struct __remove_if_fn;
struct __remove_fn;
struct __unique_fn;
Expand Down
50 changes: 50 additions & 0 deletions include/oneapi/dpl/pstl/glue_algorithm_ranges_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1025,6 +1025,56 @@ struct __internal::__mismatch_fn
}; //__mismatch_fn
inline constexpr __internal::__mismatch_fn mismatch;

// [alg.starts.with]

struct __internal::__starts_with_fn
{
template <typename _ExecutionPolicy, std::ranges::random_access_range _R1, std::ranges::random_access_range _R2,
typename _Pred = std::ranges::equal_to, typename _Proj1 = std::identity, typename _Proj2 = std::identity>
requires oneapi::dpl::is_execution_policy_v<std::remove_cvref_t<_ExecutionPolicy>> &&
std::ranges::sized_range<_R1> && std::ranges::sized_range<_R2> &&
std::indirectly_comparable<std::ranges::iterator_t<_R1>, std::ranges::iterator_t<_R2>,
_Pred, _Proj1, _Proj2>
bool
operator()(_ExecutionPolicy&& __exec, _R1&& __r1, _R2&& __r2, _Pred __pred = {}, _Proj1 __proj1 = {},
_Proj2 __proj2 = {}) const
{
// To ensure no dangling iterator is returned, __r2 may not be forwarded
return std::ranges::end(__r2) == oneapi::dpl::ranges::mismatch(std::forward<_ExecutionPolicy>(__exec), __r1,
__r2, __pred, __proj1, __proj2).in2;
}
};
inline constexpr __internal::__starts_with_fn starts_with;

// [alg.ends.with]

struct __internal::__ends_with_fn
{
template <typename _ExecutionPolicy, std::ranges::random_access_range _R1, std::ranges::random_access_range _R2,
typename _Pred = std::ranges::equal_to, typename _Proj1 = std::identity, typename _Proj2 = std::identity>
requires oneapi::dpl::is_execution_policy_v<std::remove_cvref_t<_ExecutionPolicy>> &&
std::ranges::sized_range<_R1> && std::ranges::sized_range<_R2> &&
std::indirectly_comparable<std::ranges::iterator_t<_R1>, std::ranges::iterator_t<_R2>,
_Pred, _Proj1, _Proj2>
bool
operator()(_ExecutionPolicy&& __exec, _R1&& __r1, _R2&& __r2, _Pred __pred = {}, _Proj1 __proj1 = {},
_Proj2 __proj2 = {}) const
{
auto __size_diff = std::ranges::distance(__r1) - std::ranges::distance(__r2);
if (__size_diff < 0)
return false;

#if _ONEDPL_CPP20_RANGES_ADVANCE_SYCL_INCOMPATIBLE
oneapi::dpl::__ranges::drop_view_simple __r1_dropped {std::views::all(__r1), __size_diff};
#else
auto __r1_dropped = std::views::all(__r1) | std::views::drop(__size_diff);
Comment thread
dmitriy-sobolev marked this conversation as resolved.
#endif
return oneapi::dpl::ranges::equal(std::forward<_ExecutionPolicy>(__exec), std::move(__r1_dropped),
std::forward<_R2>(__r2), __pred, __proj1, __proj2);
}
};
inline constexpr __internal::__ends_with_fn ends_with;

// [alg.remove]

struct __internal::__remove_if_fn
Expand Down
4 changes: 4 additions & 0 deletions include/oneapi/dpl/pstl/onedpl_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,10 @@
# define _ONEDPL_CPP20_OWNING_VIEW_PRESENT (_ONEDPL___cplusplus >= 202302L)
#endif

// The implementation of std::ranges::advance in libstdc++ 10 uses throw expressions.
// That prevents its use in SYCL kernels, as well as the use of std::ranges::next, std::views::drop, etc.
#define _ONEDPL_CPP20_RANGES_ADVANCE_SYCL_INCOMPATIBLE (_ONEDPL_BACKEND_SYCL && _GLIBCXX_RELEASE == 10)

// When C++20 concepts are available, we must use std::tuple as a proxy reference to satisfy iterator concepts, which
// requires the changes to std::tuple in P2321R2 and the tuple-like basic_common_reference specialization in P2165R4.
#define _ONEDPL_CAN_USE_STD_TUPLE_PROXY_ITERATOR \
Expand Down
17 changes: 17 additions & 0 deletions include/oneapi/dpl/pstl/utils_ranges.h
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,18 @@ struct drop_view_simple
assert(__n >= 0 && __n <= oneapi::dpl::__ranges::__size(__r));
}

auto
begin() const
{
return __begin(__r) + __n;
}

auto
end() const
{
return __end(__r);
}

//TODO: to be consistent with C++ standard, this Idx should be changed to diff_type of underlying range
template <typename Idx>
auto operator[](Idx __i) const -> decltype(__r[__i])
Expand Down Expand Up @@ -838,4 +850,9 @@ __get_subscription_view(_View&& __view)
} // namespace dpl
} // namespace oneapi

#if _ONEDPL_CPP20_RANGES_PRESENT
template <typename _R, typename _Size>
constexpr bool std::ranges::enable_view<oneapi::dpl::__ranges::drop_view_simple<_R, _Size>> = true;
#endif

#endif // _ONEDPL_UTILS_RANGES_H
52 changes: 52 additions & 0 deletions test/parallel_api/ranges/std_ranges_ends_with.pass.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Copyright (C) 2026 UXL Foundation Contributors
//
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "std_ranges_test.h"

#if _ENABLE_STD_RANGES_TESTING
template<int call_id, typename T, typename DataGen2 = std::identity>
using launcher = test_std_ranges::test_range_algo<call_id, T, test_std_ranges::data_in_in,
/*DataGen1*/ std::identity, DataGen2>;
#if __cpp_lib_ranges_starts_ends_with >= 202106L
auto checker = TEST_PREPARE_CALLABLE(std::ranges::ends_with);
#else
struct {
template<std::ranges::input_range R1, std::ranges::input_range R2, typename Pred = std::ranges::equal_to,
typename Proj1 = std::identity, typename Proj2 = std::identity>
bool operator()(R1&& r1, R2&& r2, Pred pred = {}, Proj1 proj1 = {}, Proj2 proj2 = {})
{
std::ranges::reverse_view r2_reverse{r2};
auto last = r2_reverse.end();
return std::ranges::mismatch(std::ranges::reverse_view(r1), r2_reverse, pred, proj1, proj2).in2 == last;
}
} checker;
#endif
#endif

std::int32_t
main()
{
#if _ENABLE_STD_RANGES_TESTING
using namespace test_std_ranges;
namespace dpl_ranges = oneapi::dpl::ranges;

using data_gen_shift_med = decltype([](auto i){ return i + medium_size/2; });
using data_gen_shift_big = decltype([](auto i){ return i + big_size/2; });

launcher<0, int>{big_sz}(dpl_ranges::ends_with, checker, binary_pred_const);
launcher<1, int>{}(dpl_ranges::ends_with, checker, binary_pred, proj);
launcher<2, int, decltype(proj)>{}(dpl_ranges::ends_with, checker, binary_pred, proj);
launcher<3, P2>{}(dpl_ranges::ends_with, checker, binary_pred_const, &P2::x, &P2::proj);
launcher<4, P2>{}(dpl_ranges::ends_with, checker, binary_pred, &P2::proj, &P2::x);
launcher<5, int, data_gen_shift_med>{}(dpl_ranges::ends_with, checker);
launcher<6, int, data_gen_shift_big>{big_sz}(dpl_ranges::ends_with, checker);
#endif //_ENABLE_STD_RANGES_TESTING

return TestUtils::done(_ENABLE_STD_RANGES_TESTING);
}
50 changes: 50 additions & 0 deletions test/parallel_api/ranges/std_ranges_starts_with.pass.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Copyright (C) 2026 UXL Foundation Contributors
//
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "std_ranges_test.h"

#if _ENABLE_STD_RANGES_TESTING
template<int call_id, typename T, typename DataGen2 = std::identity>
using launcher = test_std_ranges::test_range_algo<call_id, T, test_std_ranges::data_in_in,
/*DataGen1*/ std::identity, DataGen2>;
#if __cpp_lib_ranges_starts_ends_with >= 202106L
auto checker = TEST_PREPARE_CALLABLE(std::ranges::starts_with);
#else
struct {
template<std::ranges::input_range R1, std::ranges::input_range R2, typename Pred = std::ranges::equal_to,
typename Proj1 = std::identity, typename Proj2 = std::identity>
bool operator()(R1&& r1, R2&& r2, Pred pred = {}, Proj1 proj1 = {}, Proj2 proj2 = {})
{
auto last = std::ranges::end(r2);
return std::ranges::mismatch(r1, r2, pred, proj1, proj2).in2 == last;
}
} checker;
#endif
#endif

std::int32_t
main()
{
#if _ENABLE_STD_RANGES_TESTING
using namespace test_std_ranges;
namespace dpl_ranges = oneapi::dpl::ranges;

auto almost_always_i = [](auto i){ return (i == medium_size/2 + 19)? 0 : i; };
using data_gen_needle = decltype(almost_always_i);

launcher<0, int>{big_sz}(dpl_ranges::starts_with, checker, binary_pred_const);
launcher<1, int>{}(dpl_ranges::starts_with, checker, binary_pred, proj);
launcher<2, int, decltype(proj)>{}(dpl_ranges::starts_with, checker, binary_pred, proj);
launcher<3, P2>{}(dpl_ranges::starts_with, checker, binary_pred_const, &P2::x, &P2::proj);
launcher<4, P2>{}(dpl_ranges::starts_with, checker, binary_pred, &P2::proj, &P2::x);
launcher<5, int, data_gen_needle>{}(dpl_ranges::starts_with, checker);
#endif //_ENABLE_STD_RANGES_TESTING

return TestUtils::done(_ENABLE_STD_RANGES_TESTING);
}
Loading