//===----------------------------------------------------------------------===// // // Part of libcu++, the C++ Standard Library for your entire system, // under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. // //===----------------------------------------------------------------------===// #ifndef _CUDA_STD_SPAN #define _CUDA_STD_SPAN #include #if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC) # pragma GCC system_header #elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG) # pragma clang system_header #elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC) # pragma system_header #endif // no system header #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include _CCCL_BEGIN_NAMESPACE_CUDA_STD template _CCCL_CONCEPT __span_array_convertible = is_convertible_v<_From (*)[], _To (*)[]>; template _CCCL_CONCEPT __span_compatible_range = _CCCL_REQUIRES_EXPR((_Range, _ElementType))( requires(::cuda::std::ranges::contiguous_range<_Range>), requires(::cuda::std::ranges::sized_range<_Range>), requires((::cuda::std::ranges::borrowed_range<_Range> || is_const_v<_ElementType>) ), requires((!is_array_v>) ), requires((!__is_cuda_std_span_v> && !__is_cuda_std_array_v>) ), requires( is_convertible_v> (*)[], _ElementType (*)[]>)); template _CCCL_CONCEPT __span_compatible_iterator = _CCCL_REQUIRES_EXPR((_It, _Tp)) // (requires(contiguous_iterator<_It>), requires(__span_array_convertible>, _Tp>)); template _CCCL_CONCEPT __span_compatible_sentinel_for = _CCCL_REQUIRES_EXPR((_Sentinel, _It)) // (requires(sized_sentinel_for<_Sentinel, _It>), requires(!is_convertible_v<_Sentinel, size_t>)); template > inline constexpr size_t __maybe_static_ext = dynamic_extent; template inline constexpr size_t __maybe_static_ext<_Tp, true> = {_Tp::value}; template class _CCCL_TYPE_VISIBILITY_DEFAULT span { public: // constants and types using element_type = _Tp; using value_type = remove_cv_t<_Tp>; using size_type = size_t; using difference_type = ptrdiff_t; using pointer = _Tp*; using const_pointer = const _Tp*; using reference = _Tp&; using const_reference = const _Tp&; using iterator = __wrap_iter; using reverse_iterator = ::cuda::std::reverse_iterator; static constexpr size_type extent = _Extent; // [span.cons], span constructors, copy, assignment, and destructor _CCCL_TEMPLATE(size_t _Sz = _Extent) _CCCL_REQUIRES((_Sz == 0)) _CCCL_API constexpr span() noexcept : __data_{nullptr} {} _CCCL_HIDE_FROM_ABI span(const span&) noexcept = default; _CCCL_HIDE_FROM_ABI span& operator=(const span&) noexcept = default; _CCCL_EXEC_CHECK_DISABLE _CCCL_TEMPLATE(class _It) _CCCL_REQUIRES(__span_compatible_iterator<_It, element_type>) _CCCL_API constexpr explicit span(_It __first, [[maybe_unused]] size_type __count) : __data_{::cuda::std::to_address(__first)} { _CCCL_ASSERT(_Extent == __count, "size mismatch in span's constructor (iterator, len)"); } _CCCL_EXEC_CHECK_DISABLE _CCCL_TEMPLATE(class _It, class _End) _CCCL_REQUIRES(__span_compatible_iterator<_It, element_type> _CCCL_AND __span_compatible_sentinel_for<_End, _It>) _CCCL_API constexpr explicit span(_It __first, [[maybe_unused]] _End __last) : __data_{::cuda::std::to_address(__first)} { _CCCL_ASSERT((__last - __first >= 0), "invalid range in span's constructor (iterator, sentinel)"); _CCCL_ASSERT(__last - __first == _Extent, "invalid range in span's constructor (iterator, sentinel): last - first != extent"); } _CCCL_TEMPLATE(size_t _Sz = _Extent) _CCCL_REQUIRES((_Sz != 0)) _CCCL_API constexpr span(type_identity_t (&__arr)[_Sz]) noexcept : __data_{__arr} {} _CCCL_TEMPLATE(class _OtherElementType) _CCCL_REQUIRES(__span_array_convertible<_OtherElementType, element_type>) _CCCL_API constexpr span(array<_OtherElementType, _Extent>& __arr) noexcept : __data_{__arr.data()} {} _CCCL_TEMPLATE(class _OtherElementType) _CCCL_REQUIRES(__span_array_convertible) _CCCL_API constexpr span(const array<_OtherElementType, _Extent>& __arr) noexcept : __data_{__arr.data()} {} #if _CCCL_HAS_HOST_STD_LIB() _CCCL_EXEC_CHECK_DISABLE _CCCL_TEMPLATE(class _OtherElementType) _CCCL_REQUIRES(__span_array_convertible<_OtherElementType, element_type>) _CCCL_HOST_API constexpr span(::std::array<_OtherElementType, _Extent>& __arr) noexcept : __data_{__arr.data()} {} _CCCL_EXEC_CHECK_DISABLE _CCCL_TEMPLATE(class _OtherElementType) _CCCL_REQUIRES(__span_array_convertible) _CCCL_HOST_API constexpr span(const ::std::array<_OtherElementType, _Extent>& __arr) noexcept : __data_{__arr.data()} {} #endif // _CCCL_HAS_HOST_STD_LIB() // NOLINTBEGIN(bugprone-forwarding-reference-overload) _CCCL_EXEC_CHECK_DISABLE _CCCL_TEMPLATE(class _Range) _CCCL_REQUIRES(__span_compatible_range<_Range, element_type>) _CCCL_API constexpr explicit span(_Range&& __r) : __data_{::cuda::std::ranges::__data_cpo{}(__r)} { _CCCL_ASSERT(::cuda::std::ranges::__size_cpo{}(__r) == _Extent, "size mismatch in span's constructor (range)"); } // NOLINTEND(bugprone-forwarding-reference-overload) // Deviation from P4144R1 Hana Dusikova's proposed fix: uses initializer_list<_InitListValueType> // instead of initializer_list. The const is redundant because // initializer_list::begin() already returns const T*, so initializer_list::begin() // returns const const T* which collapses to const T* — identical behavior. Omitting the const // also avoids complications with template argument deduction in the CCCL SFINAE fallback path. _CCCL_TEMPLATE(class _InitListValueType, class _Ep = element_type) _CCCL_REQUIRES(is_same_v<_InitListValueType, value_type> _CCCL_AND is_const_v<_Ep>) _CCCL_API constexpr span(initializer_list<_InitListValueType> __il) : __data_{__il.begin()} { _CCCL_ASSERT(_Extent == __il.size(), "size mismatch in span's constructor (initializer_list)"); } _CCCL_TEMPLATE(class _OtherElementType, size_t _Extent2 = _Extent) _CCCL_REQUIRES((_Extent2 != dynamic_extent) _CCCL_AND __span_array_convertible<_OtherElementType, element_type>) _CCCL_API constexpr span(const span<_OtherElementType, _Extent2>& __other) noexcept : __data_{__other.data()} {} _CCCL_TEMPLATE(class _OtherElementType) _CCCL_REQUIRES(__span_array_convertible<_OtherElementType, element_type>) _CCCL_API constexpr explicit span(const span<_OtherElementType, dynamic_extent>& __other) noexcept : __data_{__other.data()} { _CCCL_ASSERT(_Extent == __other.size(), "size mismatch in span's constructor (other span)"); } // ~span() noexcept = default; template _CCCL_API constexpr span first() const noexcept { // ternary avoids "pointless comparison of unsigned integer with zero" warning static_assert(_Count == 0 ? true : _Count <= _Extent, "span::first(): Count out of range"); return span{data(), _Count}; } template _CCCL_API constexpr span last() const noexcept { // ternary avoids "pointless comparison of unsigned integer with zero" warning static_assert(_Count == 0 ? true : _Count <= _Extent, "span::last(): Count out of range"); return span{data() + size() - _Count, _Count}; } _CCCL_API constexpr span first(size_type __count) const noexcept { _CCCL_ASSERT(__count <= size(), "span::first(count): count out of range"); return {data(), __count}; } _CCCL_API constexpr span last(size_type __count) const noexcept { _CCCL_ASSERT(__count <= size(), "span::last(count): count out of range"); return {data() + size() - __count, __count}; } template using __subspan_t = span; template _CCCL_API constexpr __subspan_t<_Offset, _Count> subspan() const noexcept { // ternary avoids "pointless comparison of unsigned integer with zero" warning if constexpr (_Offset != 0) { static_assert(_Offset <= _Extent, "span::subspan(): Offset out of range"); } if constexpr (_Count != 0) { static_assert(_Count == dynamic_extent || _Count <= _Extent - _Offset, "span::subspan(): Offset + Count out of range"); } return __subspan_t<_Offset, _Count>{data() + _Offset, _Count == dynamic_extent ? size() - _Offset : _Count}; } _CCCL_API constexpr span subspan(size_type __offset, size_type __count = dynamic_extent) const noexcept { _CCCL_ASSERT(__offset <= size(), "span::subspan(offset, count): offset out of range"); _CCCL_ASSERT(__count <= size() || __count == dynamic_extent, "span::subspan(offset, count): count out of range"); if (__count == dynamic_extent) { return {data() + __offset, size() - __offset}; } _CCCL_ASSERT(__count <= size() - __offset, "span::subspan(offset, count): offset + count out of range"); return {data() + __offset, __count}; } _CCCL_API constexpr size_type size() const noexcept { return _Extent; } _CCCL_API constexpr size_type size_bytes() const noexcept { return _Extent * sizeof(element_type); } [[nodiscard]] _CCCL_API constexpr bool empty() const noexcept { return _Extent == 0; } _CCCL_API constexpr reference operator[](size_type __idx) const noexcept { _CCCL_ASSERT(__idx < size(), "span::operator[](index): index out of range"); return __data_[__idx]; } _CCCL_API constexpr reference at(size_type __idx) const { if (__idx >= size()) { _CCCL_THROW(::std::out_of_range, "span::at"); } return __data_[__idx]; } _CCCL_API constexpr reference front() const noexcept { _CCCL_ASSERT(!empty(), "span::front() on empty span"); return __data_[0]; } _CCCL_API constexpr reference back() const noexcept { _CCCL_ASSERT(!empty(), "span::back() on empty span"); return __data_[size() - 1]; } _CCCL_API constexpr pointer data() const noexcept { return __data_; } // [span.iter], span iterator support _CCCL_API constexpr iterator begin() const noexcept { return iterator(data()); } _CCCL_API constexpr iterator end() const noexcept { return iterator(data() + size()); } _CCCL_API constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator(end()); } _CCCL_API constexpr reverse_iterator rend() const noexcept { return reverse_iterator(begin()); } _CCCL_API inline span __as_bytes() const noexcept { return span{reinterpret_cast(data()), size_bytes()}; } _CCCL_API inline span __as_writable_bytes() const noexcept { return span{reinterpret_cast(data()), size_bytes()}; } private: pointer __data_; }; template class _CCCL_TYPE_VISIBILITY_DEFAULT span<_Tp, dynamic_extent> { public: // constants and types using element_type = _Tp; using value_type = remove_cv_t<_Tp>; using size_type = size_t; using difference_type = ptrdiff_t; using pointer = _Tp*; using const_pointer = const _Tp*; using reference = _Tp&; using const_reference = const _Tp&; using iterator = __wrap_iter; using reverse_iterator = ::cuda::std::reverse_iterator; static constexpr size_type extent = dynamic_extent; // [span.cons], span constructors, copy, assignment, and destructor _CCCL_API constexpr span() noexcept : __data_{nullptr} , __size_{0} {} _CCCL_HIDE_FROM_ABI span(const span&) noexcept = default; _CCCL_HIDE_FROM_ABI span& operator=(const span&) noexcept = default; _CCCL_EXEC_CHECK_DISABLE _CCCL_TEMPLATE(class _It) _CCCL_REQUIRES(__span_compatible_iterator<_It, element_type>) _CCCL_API constexpr span(_It __first, size_type __count) : __data_{::cuda::std::to_address(__first)} , __size_{__count} {} _CCCL_EXEC_CHECK_DISABLE _CCCL_TEMPLATE(class _It, class _End) _CCCL_REQUIRES(__span_compatible_iterator<_It, element_type> _CCCL_AND __span_compatible_sentinel_for<_End, _It>) _CCCL_API constexpr span(_It __first, _End __last) : __data_(::cuda::std::to_address(__first)) , __size_(__last - __first) { _CCCL_ASSERT(__last - __first >= 0, "invalid range in span's constructor (iterator, sentinel)"); } template _CCCL_API constexpr span(type_identity_t (&__arr)[_Sz]) noexcept : __data_{__arr} , __size_{_Sz} {} _CCCL_TEMPLATE(class _OtherElementType, size_t _Sz) _CCCL_REQUIRES(__span_array_convertible<_OtherElementType, element_type>) _CCCL_API constexpr span(array<_OtherElementType, _Sz>& __arr) noexcept : __data_{__arr.data()} , __size_{_Sz} {} _CCCL_TEMPLATE(class _OtherElementType, size_t _Sz) _CCCL_REQUIRES(__span_array_convertible) _CCCL_API constexpr span(const array<_OtherElementType, _Sz>& __arr) noexcept : __data_{__arr.data()} , __size_{_Sz} {} #if _CCCL_HAS_HOST_STD_LIB() _CCCL_TEMPLATE(class _OtherElementType, size_t _Sz) _CCCL_REQUIRES(__span_array_convertible<_OtherElementType, element_type>) _CCCL_HOST_API constexpr span(::std::array<_OtherElementType, _Sz>& __arr) noexcept : __data_{__arr.data()} , __size_{_Sz} {} _CCCL_TEMPLATE(class _OtherElementType, size_t _Sz) _CCCL_REQUIRES(__span_array_convertible) _CCCL_HOST_API constexpr span(const ::std::array<_OtherElementType, _Sz>& __arr) noexcept : __data_{__arr.data()} , __size_{_Sz} {} #endif // _CCCL_HAS_HOST_STD_LIB() // NOLINTBEGIN(bugprone-forwarding-reference-overload) _CCCL_EXEC_CHECK_DISABLE _CCCL_TEMPLATE(class _Range) _CCCL_REQUIRES(__span_compatible_range<_Range, element_type>) _CCCL_API constexpr span(_Range&& __r) : __data_(::cuda::std::ranges::__data_cpo{}(__r)) , __size_{::cuda::std::ranges::__size_cpo{}(__r)} {} // NOLINTEND(bugprone-forwarding-reference-overload) // Deviation from P4144R1 Hana Dusikova's proposed fix: uses initializer_list<_InitListValueType> // instead of initializer_list. The const is redundant because // initializer_list::begin() already returns const T*, so initializer_list::begin() // returns const const T* which collapses to const T* — identical behavior. Omitting the const // also avoids complications with template argument deduction in the CCCL SFINAE fallback path. _CCCL_TEMPLATE(class _InitListValueType, class _Ep = element_type) _CCCL_REQUIRES(is_same_v<_InitListValueType, value_type> _CCCL_AND is_const_v<_Ep>) _CCCL_API constexpr span(initializer_list<_InitListValueType> __il) : __data_{__il.begin()} , __size_{__il.size()} {} _CCCL_TEMPLATE(class _OtherElementType, size_t _OtherExtent) _CCCL_REQUIRES(__span_array_convertible<_OtherElementType, element_type>) _CCCL_API constexpr span(const span<_OtherElementType, _OtherExtent>& __other) noexcept : __data_{__other.data()} , __size_{__other.size()} {} // ~span() noexcept = default; template _CCCL_API constexpr span first() const noexcept { // ternary avoids "pointless comparison of unsigned integer with zero" warning _CCCL_ASSERT(_Count == 0 ? true : _Count <= size(), "span::first(): Count out of range"); return span{data(), _Count}; } template _CCCL_API constexpr span last() const noexcept { // ternary avoids "pointless comparison of unsigned integer with zero" warning _CCCL_ASSERT(_Count == 0 ? true : _Count <= size(), "span::last(): Count out of range"); return span{data() + size() - _Count, _Count}; } _CCCL_API constexpr span first(size_type __count) const noexcept { _CCCL_ASSERT(__count <= size(), "span::first(count): count out of range"); return {data(), __count}; } _CCCL_API constexpr span last(size_type __count) const noexcept { _CCCL_ASSERT(__count <= size(), "span::last(count): count out of range"); return {data() + size() - __count, __count}; } template using __subspan_t = span; template _CCCL_API constexpr __subspan_t<_Offset, _Count> subspan() const noexcept { // ternary avoids "pointless comparison of unsigned integer with zero" warning _CCCL_ASSERT(_Offset == 0 ? true : _Offset <= size(), "span::subspan(): Offset out of range"); _CCCL_ASSERT(_Count == dynamic_extent || (_Count == 0 ? true : _Count <= size() - _Offset), "span::subspan(): Offset + Count out of range"); return __subspan_t<_Offset, _Count>{data() + _Offset, _Count == dynamic_extent ? size() - _Offset : _Count}; } constexpr span _CCCL_API inline subspan( size_type __offset, size_type __count = dynamic_extent) const noexcept { _CCCL_ASSERT(__offset <= size(), "span::subspan(offset, count): offset out of range"); _CCCL_ASSERT(__count <= size() || __count == dynamic_extent, "span::subspan(offset, count): count out of range"); if (__count == dynamic_extent) { return {data() + __offset, size() - __offset}; } _CCCL_ASSERT(__count <= size() - __offset, "span::subspan(offset, count): offset + count out of range"); return {data() + __offset, __count}; } _CCCL_API constexpr size_type size() const noexcept { return __size_; } _CCCL_API constexpr size_type size_bytes() const noexcept { return __size_ * sizeof(element_type); } [[nodiscard]] _CCCL_API constexpr bool empty() const noexcept { return __size_ == 0; } _CCCL_API constexpr reference operator[](size_type __idx) const noexcept { _CCCL_ASSERT(__idx < size(), "span::operator[](index): index out of range"); return __data_[__idx]; } _CCCL_API constexpr reference at(size_type __idx) const { if (__idx >= size()) { _CCCL_THROW(::std::out_of_range, "span::at"); } return __data_[__idx]; } _CCCL_API constexpr reference front() const noexcept { _CCCL_ASSERT(!empty(), "span::front() on empty span"); return __data_[0]; } _CCCL_API constexpr reference back() const noexcept { _CCCL_ASSERT(!empty(), "span::back() on empty span"); return __data_[size() - 1]; } _CCCL_API constexpr pointer data() const noexcept { return __data_; } // [span.iter], span iterator support _CCCL_API constexpr iterator begin() const noexcept { return iterator(data()); } _CCCL_API constexpr iterator end() const noexcept { return iterator(data() + size()); } _CCCL_API constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator(end()); } _CCCL_API constexpr reverse_iterator rend() const noexcept { return reverse_iterator(begin()); } _CCCL_API inline span __as_bytes() const noexcept { return span{reinterpret_cast(data()), size_bytes()}; } _CCCL_API inline span __as_writable_bytes() const noexcept { return span{reinterpret_cast(data()), size_bytes()}; } private: pointer __data_; size_type __size_; }; // as_bytes & as_writable_bytes template _CCCL_API inline auto as_bytes(span<_Tp, _Extent> __s) noexcept { return __s.__as_bytes(); } _CCCL_TEMPLATE(class _Tp, size_t _Extent) _CCCL_REQUIRES((!is_const<_Tp>::value)) _CCCL_API inline auto as_writable_bytes(span<_Tp, _Extent> __s) noexcept { return __s.__as_writable_bytes(); } // Deduction guides template _CCCL_DEDUCTION_GUIDE_ATTRIBUTES span(_Tp (&)[_Sz]) -> span<_Tp, _Sz>; template _CCCL_DEDUCTION_GUIDE_ATTRIBUTES span(array<_Tp, _Sz>&) -> span<_Tp, _Sz>; template _CCCL_DEDUCTION_GUIDE_ATTRIBUTES span(const array<_Tp, _Sz>&) -> span; #if _CCCL_HAS_HOST_STD_LIB() template _CCCL_DEDUCTION_GUIDE_ATTRIBUTES span(::std::array<_Tp, _Sz>&) -> span<_Tp, _Sz>; template _CCCL_DEDUCTION_GUIDE_ATTRIBUTES span(const ::std::array<_Tp, _Sz>&) -> span; #endif // _CCCL_HAS_HOST_STD_LIB() _CCCL_TEMPLATE(class _It, class _EndOrSize) _CCCL_REQUIRES(contiguous_iterator<_It>) _CCCL_DEDUCTION_GUIDE_ATTRIBUTES span(_It, _EndOrSize) -> span>, __maybe_static_ext<_EndOrSize>>; _CCCL_TEMPLATE(class _Range) _CCCL_REQUIRES(::cuda::std::ranges::contiguous_range<_Range>) _CCCL_DEDUCTION_GUIDE_ATTRIBUTES span(_Range&&) -> span>>; _CCCL_END_NAMESPACE_CUDA_STD _CCCL_BEGIN_NAMESPACE_CUDA_STD_RANGES template inline constexpr bool enable_borrowed_range> = true; template inline constexpr bool enable_view> = true; _CCCL_END_NAMESPACE_CUDA_STD_RANGES #include #endif // _CUDA_STD_SPAN