Skip to content

Commit aac5b1a

Browse files
committed
[libcu++] Unwrap contiguous iterator adaptors back to their storage.
Fancy iterators over contiguous storage are not contiguous, so algorithms miss the fast copy. Unwrap peels iterator adaptors through their base down to a contiguous iterator, and rebind rebuilds a transform iterator over a copy of that storage, replacing the contiguous iterator with a pointer and keeping the functors. Only adaptors that can be rebuilt satisfy __iter_can_unwrap, which excludes transform output and input output iterators. A contiguous iterator unwraps to its address, even if it also has a base.
1 parent 4984d75 commit aac5b1a

14 files changed

Lines changed: 852 additions & 3 deletions

File tree

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of libcu++, the C++ Standard Library for your entire system,
4+
// under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.
8+
//
9+
//===----------------------------------------------------------------------===//
10+
11+
#ifndef _CUDA___ITERATOR_CONTIGUOUS_ITERATOR_ADAPTOR_H
12+
#define _CUDA___ITERATOR_CONTIGUOUS_ITERATOR_ADAPTOR_H
13+
14+
#include <cuda/std/detail/__config>
15+
16+
#if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC)
17+
# pragma GCC system_header
18+
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG)
19+
# pragma clang system_header
20+
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC)
21+
# pragma system_header
22+
#endif // no system header
23+
24+
#include <cuda/__fwd/iterator.h>
25+
#include <cuda/std/__concepts/concept_macros.h>
26+
#include <cuda/std/__iterator/concepts.h>
27+
#include <cuda/std/__iterator/iterator_traits.h>
28+
#include <cuda/std/__iterator/readable_traits.h>
29+
#include <cuda/std/__memory/pointer_traits.h>
30+
#include <cuda/std/__type_traits/is_convertible.h>
31+
#include <cuda/std/__type_traits/is_pointer.h>
32+
#include <cuda/std/__type_traits/remove_cvref.h>
33+
#include <cuda/std/__utility/declval.h>
34+
35+
#include <cuda/std/__cccl/prologue.h>
36+
37+
_CCCL_BEGIN_NAMESPACE_CUDA
38+
39+
//! @brief True when `_Iter` is an iterator adaptor whose `base()` returns the iterator it adapts, so not e.g. the
40+
//! counter of a counting iterator.
41+
template <class _Iter>
42+
_CCCL_CONCEPT __iter_has_member_base = _CCCL_REQUIRES_EXPR((_Iter), const _Iter& __iter)(
43+
requires(::cuda::std::__can_to_address<::cuda::std::remove_cvref_t<decltype(__iter.base())>>
44+
|| ::cuda::std::input_or_output_iterator<::cuda::std::remove_cvref_t<decltype(__iter.base())>>));
45+
46+
//! @brief True when `_Iter` is an iterator adaptor that can rebuild itself over the storage at a `_Tp*`.
47+
template <class _Iter, class _Tp>
48+
_CCCL_CONCEPT __iter_has_member_rebind =
49+
_CCCL_REQUIRES_EXPR((_Iter, _Tp), const _Iter& __iter, _Tp* __ptr)((__iter.__rebind(__ptr)));
50+
51+
//! @brief Peel iterator adaptors until a contiguous iterator or an iterator that adapts no other iterator remains.
52+
//! @param[in] __iter The iterator to unwrap.
53+
//! @return The address of the storage of a contiguous iterator. Otherwise the unwrapped base iterator of an iterator
54+
//! adaptor, or `__iter` when it is not an iterator adaptor.
55+
template <class _Iter>
56+
[[nodiscard]] _CCCL_API constexpr auto __unwrap_iterator_adaptor(const _Iter& __iter)
57+
{
58+
if constexpr (::cuda::std::__can_to_address<_Iter>)
59+
{ // Support standard contiguous iterators
60+
return ::cuda::std::to_address(__iter);
61+
}
62+
else if constexpr (__iter_has_member_base<_Iter>)
63+
{
64+
return ::cuda::__unwrap_iterator_adaptor(__iter.base());
65+
}
66+
else
67+
{
68+
return __iter;
69+
}
70+
}
71+
72+
//! @brief The iterator that remains after peeling iterator adaptors from `_Iter`.
73+
template <class _Iter>
74+
using __unwrapped_iterator_t = decltype(::cuda::__unwrap_iterator_adaptor(::cuda::std::declval<_Iter>()));
75+
76+
//! @brief The value type of the iterator that remains after peeling iterator adaptors from `_Iter`.
77+
template <class _Iter>
78+
using __unwrapped_iter_value_t = ::cuda::std::iter_value_t<__unwrapped_iterator_t<_Iter>>;
79+
80+
//! @brief True when `_Iter` can be rebuilt over the storage at a `_Tp*`, because it is a contiguous iterator or an
81+
//! iterator adaptor that can rebuild itself over it.
82+
template <class _Iter, class _Tp>
83+
_CCCL_CONCEPT __iter_can_rebind =
84+
(::cuda::std::__has_contiguous_traversal<_Iter> && ::cuda::std::is_convertible_v<_Tp*, __unwrapped_iterator_t<_Iter>>)
85+
|| __iter_has_member_rebind<_Iter, _Tp>;
86+
87+
//! @brief Rebuild `__iter` over the storage at `__ptr`.
88+
//! @param[in] __iter The iterator to rebuild.
89+
//! @param[in] __ptr The address of the storage that replaces the storage `__iter` reads from.
90+
//! @return A pointer to `__ptr` with the qualifiers of the address of a contiguous iterator. Otherwise a copy of the
91+
//! iterator adaptor over its rebuilt base iterator.
92+
_CCCL_TEMPLATE(class _Iter, class _Tp)
93+
_CCCL_REQUIRES(__iter_can_rebind<_Iter, _Tp>)
94+
[[nodiscard]] _CCCL_API constexpr auto __rebind_iterator_adaptor([[maybe_unused]] const _Iter& __iter, _Tp* __ptr)
95+
{
96+
if constexpr (__iter_has_member_rebind<_Iter, _Tp>)
97+
{
98+
return __iter.__rebind(__ptr);
99+
}
100+
else
101+
{
102+
return static_cast<__unwrapped_iterator_t<_Iter>>(__ptr);
103+
}
104+
}
105+
106+
//! @brief True when `_Iter` is an iterator adaptor over contiguous storage, which unwraps to a pointer and can be
107+
//! rebuilt over a copy of the storage behind it.
108+
template <class _Iter>
109+
_CCCL_CONCEPT __iter_can_unwrap = _CCCL_REQUIRES_EXPR((_Iter)) //
110+
(requires(::cuda::std::is_pointer_v<__unwrapped_iterator_t<_Iter>>),
111+
requires(__iter_has_member_rebind<_Iter, __unwrapped_iter_value_t<_Iter>>));
112+
113+
template <class _Iter>
114+
inline constexpr bool __is_contiguous_iterator_or_adaptor_impl =
115+
::cuda::std::is_pointer_v<_Iter> || ::cuda::std::__has_contiguous_traversal<_Iter>;
116+
117+
template <class _Fn, class _Iter>
118+
inline constexpr bool __is_contiguous_iterator_or_adaptor_impl<::cuda::transform_iterator<_Fn, _Iter>> =
119+
__is_contiguous_iterator_or_adaptor_impl<::cuda::std::remove_cvref_t<_Iter>>;
120+
121+
//! @brief True when `_Iter` is contiguous storage, or an adaptor whose stored iterator is.
122+
//! @tparam _Iter The iterator type to query. cv-qualifiers and references are ignored.
123+
template <class _Iter>
124+
inline constexpr bool __is_contiguous_iterator_or_adaptor_v =
125+
__is_contiguous_iterator_or_adaptor_impl<::cuda::std::remove_cvref_t<_Iter>>;
126+
127+
_CCCL_END_NAMESPACE_CUDA
128+
129+
#include <cuda/std/__cccl/epilogue.h>
130+
131+
#endif // _CUDA___ITERATOR_CONTIGUOUS_ITERATOR_ADAPTOR_H

‎libcudacxx/include/cuda/__iterator/transform_iterator.h‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#if _LIBCUDACXX_HAS_SPACESHIP_OPERATOR()
2626
# include <cuda/std/__compare/three_way_comparable.h>
2727
#endif // _LIBCUDACXX_HAS_SPACESHIP_OPERATOR()
28+
#include <cuda/__iterator/contiguous_iterator_adaptor.h>
2829
#include <cuda/std/__concepts/constructible.h>
2930
#include <cuda/std/__concepts/derived_from.h>
3031
#include <cuda/std/__concepts/equality_comparable.h>
@@ -233,6 +234,18 @@ class transform_iterator : public __transform_iterator_category_base<_Fn, _Iter>
233234
return ::cuda::std::move(__iter());
234235
}
235236

237+
//! @brief Rebuilds this iterator over the storage at @p __ptr. The copy invokes a copy of the stored functor on the
238+
//! elements at @p __ptr.
239+
//! @param[in] __ptr The address of the storage that replaces the storage this iterator reads from.
240+
_CCCL_EXEC_CHECK_DISABLE
241+
_CCCL_TEMPLATE(class _Tp)
242+
_CCCL_REQUIRES(__iter_can_rebind<_Iter, _Tp>)
243+
[[nodiscard]] _CCCL_API constexpr auto __rebind(_Tp* __ptr) const
244+
{
245+
auto __base = ::cuda::__rebind_iterator_adaptor(__iter(), __ptr);
246+
return transform_iterator<_Fn, decltype(__base)>{::cuda::std::move(__base), __func()};
247+
}
248+
236249
//! @brief Dereferences the stored iterator and applies the stored functor to the result
237250
_CCCL_EXEC_CHECK_DISABLE
238251
#ifndef _CCCL_DOXYGEN_INVOKED // Doxygen has issues with the constraint

‎libcudacxx/include/cuda/iterator‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
// IWYU pragma: begin_exports
2525
#include <cuda/__iterator/constant_iterator.h>
26+
#include <cuda/__iterator/contiguous_iterator_adaptor.h>
2627
#include <cuda/__iterator/counting_iterator.h>
2728
#include <cuda/__iterator/discard_iterator.h>
2829
#include <cuda/__iterator/permutation_iterator.h>

‎libcudacxx/include/cuda/std/__iterator/iterator_traits.h‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,11 @@ inline constexpr bool __has_random_access_traversal =
535535
__has_iterator_category_convertible_to<_Iter, random_access_iterator_tag>
536536
|| __has_iterator_concept_convertible_to<_Iter, random_access_iterator_tag>;
537537

538+
// in C++17 the standard does not yet know about the contiguous iterator concept, so we need to check for it explicitly
539+
template <class _Iter>
540+
_CCCL_CONCEPT __is_cpp17_contiguous_iterator = _CCCL_REQUIRES_EXPR((_Iter)) //
541+
(typename(_ITER_CONCEPT<_Iter>), requires(is_convertible_v<_ITER_CONCEPT<_Iter>, contiguous_iterator_tag>));
542+
538543
// __has_contiguous_traversal determines if an iterator is known by
539544
// libc++ to be contiguous, either because it advertises itself as such
540545
// (in C++20) or because it is a pointer type or a known trivial wrapper
@@ -545,7 +550,7 @@ inline constexpr bool __has_random_access_traversal =
545550
template <class _Iter>
546551
inline constexpr bool __has_contiguous_traversal =
547552
__has_iterator_category_convertible_to<_Iter, contiguous_iterator_tag>
548-
|| __has_iterator_concept_convertible_to<_Iter, contiguous_iterator_tag>;
553+
|| __has_iterator_concept_convertible_to<_Iter, contiguous_iterator_tag> || __is_cpp17_contiguous_iterator<_Iter>;
549554

550555
// Any native pointer which is an iterator is also a contiguous iterator.
551556
template <class _Tp>

0 commit comments

Comments
 (0)