Christian Trott | fcaccf8 | 2023-05-16 19:38:11 | [diff] [blame] | 1 | // -*- C++ -*- |
| 2 | //===----------------------------------------------------------------------===// |
| 3 | // |
| 4 | // Part of the LLVM Project, 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 | // |
| 8 | // Kokkos v. 4.0 |
| 9 | // Copyright (2022) National Technology & Engineering |
| 10 | // Solutions of Sandia, LLC (NTESS). |
| 11 | // |
| 12 | // Under the terms of Contract DE-NA0003525 with NTESS, |
| 13 | // the U.S. Government retains certain rights in this software. |
| 14 | // |
| 15 | //===---------------------------------------------------------------------===// |
| 16 | |
| 17 | #ifndef _LIBCPP___MDSPAN_EXTENTS_H |
| 18 | #define _LIBCPP___MDSPAN_EXTENTS_H |
| 19 | |
| 20 | #include <__assert> |
| 21 | #include <__config> |
A. Jiang | 74e70ba | 2024-08-27 20:41:55 | [diff] [blame] | 22 | |
| 23 | #include <__concepts/arithmetic.h> |
Christian Trott | fcaccf8 | 2023-05-16 19:38:11 | [diff] [blame] | 24 | #include <__type_traits/common_type.h> |
| 25 | #include <__type_traits/is_convertible.h> |
| 26 | #include <__type_traits/is_nothrow_constructible.h> |
| 27 | #include <__type_traits/is_same.h> |
| 28 | #include <__type_traits/make_unsigned.h> |
| 29 | #include <__utility/integer_sequence.h> |
| 30 | #include <__utility/unreachable.h> |
| 31 | #include <array> |
| 32 | #include <cinttypes> |
| 33 | #include <concepts> |
| 34 | #include <cstddef> |
| 35 | #include <limits> |
| 36 | #include <span> |
| 37 | |
| 38 | #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) |
| 39 | # pragma GCC system_header |
| 40 | #endif |
| 41 | |
| 42 | _LIBCPP_PUSH_MACROS |
| 43 | #include <__undef_macros> |
| 44 | |
| 45 | _LIBCPP_BEGIN_NAMESPACE_STD |
| 46 | |
| 47 | #if _LIBCPP_STD_VER >= 23 |
| 48 | |
| 49 | namespace __mdspan_detail { |
| 50 | |
| 51 | // ------------------------------------------------------------------ |
| 52 | // ------------ __static_array -------------------------------------- |
| 53 | // ------------------------------------------------------------------ |
| 54 | // array like class which provides an array of static values with get |
| 55 | template <class _Tp, _Tp... _Values> |
| 56 | struct __static_array { |
| 57 | static constexpr array<_Tp, sizeof...(_Values)> __array = {_Values...}; |
| 58 | |
| 59 | public: |
| 60 | _LIBCPP_HIDE_FROM_ABI static constexpr size_t __size() { return sizeof...(_Values); } |
| 61 | _LIBCPP_HIDE_FROM_ABI static constexpr _Tp __get(size_t __index) noexcept { return __array[__index]; } |
| 62 | |
| 63 | template <size_t _Index> |
| 64 | _LIBCPP_HIDE_FROM_ABI static constexpr _Tp __get() { |
| 65 | return __get(_Index); |
| 66 | } |
| 67 | }; |
| 68 | |
| 69 | // ------------------------------------------------------------------ |
| 70 | // ------------ __possibly_empty_array ----------------------------- |
| 71 | // ------------------------------------------------------------------ |
| 72 | |
| 73 | // array like class which provides get function and operator [], and |
| 74 | // has a specialization for the size 0 case. |
| 75 | // This is needed to make the __maybe_static_array be truly empty, for |
| 76 | // all static values. |
| 77 | |
| 78 | template <class _Tp, size_t _Size> |
| 79 | struct __possibly_empty_array { |
| 80 | _Tp __vals_[_Size]; |
| 81 | _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator[](size_t __index) { return __vals_[__index]; } |
| 82 | _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator[](size_t __index) const { return __vals_[__index]; } |
| 83 | }; |
| 84 | |
| 85 | template <class _Tp> |
| 86 | struct __possibly_empty_array<_Tp, 0> { |
| 87 | _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator[](size_t) { __libcpp_unreachable(); } |
| 88 | _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator[](size_t) const { __libcpp_unreachable(); } |
| 89 | }; |
| 90 | |
| 91 | // ------------------------------------------------------------------ |
| 92 | // ------------ static_partial_sums --------------------------------- |
| 93 | // ------------------------------------------------------------------ |
| 94 | |
| 95 | // Provides a compile time partial sum one can index into |
| 96 | |
| 97 | template <size_t... _Values> |
| 98 | struct __static_partial_sums { |
| 99 | _LIBCPP_HIDE_FROM_ABI static constexpr array<size_t, sizeof...(_Values)> __static_partial_sums_impl() { |
| 100 | array<size_t, sizeof...(_Values)> __values{_Values...}; |
| 101 | array<size_t, sizeof...(_Values)> __partial_sums{{}}; |
| 102 | size_t __running_sum = 0; |
| 103 | for (int __i = 0; __i != sizeof...(_Values); ++__i) { |
| 104 | __partial_sums[__i] = __running_sum; |
| 105 | __running_sum += __values[__i]; |
| 106 | } |
| 107 | return __partial_sums; |
| 108 | } |
| 109 | static constexpr array<size_t, sizeof...(_Values)> __result{__static_partial_sums_impl()}; |
| 110 | |
| 111 | _LIBCPP_HIDE_FROM_ABI static constexpr size_t __get(size_t __index) { return __result[__index]; } |
| 112 | }; |
| 113 | |
| 114 | // ------------------------------------------------------------------ |
| 115 | // ------------ __maybe_static_array -------------------------------- |
| 116 | // ------------------------------------------------------------------ |
| 117 | |
| 118 | // array like class which has a mix of static and runtime values but |
| 119 | // only stores the runtime values. |
| 120 | // The type of the static and the runtime values can be different. |
| 121 | // The position of a dynamic value is indicated through a tag value. |
| 122 | template <class _TDynamic, class _TStatic, _TStatic _DynTag, _TStatic... _Values> |
| 123 | struct __maybe_static_array { |
| 124 | static_assert(is_convertible<_TStatic, _TDynamic>::value, |
| 125 | "__maybe_static_array: _TStatic must be convertible to _TDynamic"); |
| 126 | static_assert(is_convertible<_TDynamic, _TStatic>::value, |
| 127 | "__maybe_static_array: _TDynamic must be convertible to _TStatic"); |
| 128 | |
| 129 | private: |
| 130 | // Static values member |
| 131 | static constexpr size_t __size_ = sizeof...(_Values); |
| 132 | static constexpr size_t __size_dynamic_ = ((_Values == _DynTag) + ... + 0); |
| 133 | using _StaticValues = __static_array<_TStatic, _Values...>; |
| 134 | using _DynamicValues = __possibly_empty_array<_TDynamic, __size_dynamic_>; |
| 135 | |
| 136 | // Dynamic values member |
| 137 | _LIBCPP_NO_UNIQUE_ADDRESS _DynamicValues __dyn_vals_; |
| 138 | |
| 139 | // static mapping of indices to the position in the dynamic values array |
| 140 | using _DynamicIdxMap = __static_partial_sums<static_cast<size_t>(_Values == _DynTag)...>; |
| 141 | |
Nikolas Klauser | d231b50 | 2024-06-07 05:43:20 | [diff] [blame] | 142 | template <size_t... _Indices> |
| 143 | _LIBCPP_HIDE_FROM_ABI static constexpr _DynamicValues __zeros(index_sequence<_Indices...>) noexcept { |
| 144 | return _DynamicValues{((void)_Indices, 0)...}; |
Christian Trott | fcaccf8 | 2023-05-16 19:38:11 | [diff] [blame] | 145 | } |
| 146 | |
| 147 | public: |
| 148 | _LIBCPP_HIDE_FROM_ABI constexpr __maybe_static_array() noexcept |
| 149 | : __dyn_vals_{__zeros(make_index_sequence<__size_dynamic_>())} {} |
| 150 | |
| 151 | // constructors from dynamic values only -- this covers the case for rank() == 0 |
| 152 | template <class... _DynVals> |
| 153 | requires(sizeof...(_DynVals) == __size_dynamic_) |
| 154 | _LIBCPP_HIDE_FROM_ABI constexpr __maybe_static_array(_DynVals... __vals) |
| 155 | : __dyn_vals_{static_cast<_TDynamic>(__vals)...} {} |
| 156 | |
| 157 | template <class _Tp, size_t _Size > |
| 158 | requires(_Size == __size_dynamic_) |
| 159 | _LIBCPP_HIDE_FROM_ABI constexpr __maybe_static_array([[maybe_unused]] const span<_Tp, _Size>& __vals) { |
| 160 | if constexpr (_Size > 0) { |
| 161 | for (size_t __i = 0; __i < _Size; __i++) |
| 162 | __dyn_vals_[__i] = static_cast<_TDynamic>(__vals[__i]); |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | // constructors from all values -- here rank will be greater than 0 |
| 167 | template <class... _DynVals> |
| 168 | requires(sizeof...(_DynVals) != __size_dynamic_) |
| 169 | _LIBCPP_HIDE_FROM_ABI constexpr __maybe_static_array(_DynVals... __vals) { |
Nikolas Klauser | 6b4b29f | 2024-06-18 08:45:30 | [diff] [blame] | 170 | static_assert(sizeof...(_DynVals) == __size_, "Invalid number of values."); |
Christian Trott | fcaccf8 | 2023-05-16 19:38:11 | [diff] [blame] | 171 | _TDynamic __values[__size_] = {static_cast<_TDynamic>(__vals)...}; |
| 172 | for (size_t __i = 0; __i < __size_; __i++) { |
| 173 | _TStatic __static_val = _StaticValues::__get(__i); |
| 174 | if (__static_val == _DynTag) { |
| 175 | __dyn_vals_[_DynamicIdxMap::__get(__i)] = __values[__i]; |
Christian Trott | 488c3db | 2023-07-25 18:25:17 | [diff] [blame] | 176 | } else |
| 177 | // Not catching this could lead to out of bounds errors later |
| 178 | // e.g. using my_mdspan_t = mdspan<int, extents<int, 10>>; my_mdspan_t = m(new int[5], 5); |
| 179 | // Right-hand-side construction looks ok with allocation and size matching, |
| 180 | // but since (potentially elsewhere defined) my_mdspan_t has static size m now thinks its range is 10 not 5 |
| 181 | _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS( |
| 182 | __values[__i] == static_cast<_TDynamic>(__static_val), |
| 183 | "extents construction: mismatch of provided arguments with static extents."); |
Christian Trott | fcaccf8 | 2023-05-16 19:38:11 | [diff] [blame] | 184 | } |
| 185 | } |
| 186 | |
| 187 | template <class _Tp, size_t _Size> |
| 188 | requires(_Size != __size_dynamic_) |
| 189 | _LIBCPP_HIDE_FROM_ABI constexpr __maybe_static_array(const span<_Tp, _Size>& __vals) { |
Nikolas Klauser | 6b4b29f | 2024-06-18 08:45:30 | [diff] [blame] | 190 | static_assert(_Size == __size_ || __size_ == dynamic_extent); |
Christian Trott | fcaccf8 | 2023-05-16 19:38:11 | [diff] [blame] | 191 | for (size_t __i = 0; __i < __size_; __i++) { |
| 192 | _TStatic __static_val = _StaticValues::__get(__i); |
| 193 | if (__static_val == _DynTag) { |
| 194 | __dyn_vals_[_DynamicIdxMap::__get(__i)] = static_cast<_TDynamic>(__vals[__i]); |
Christian Trott | 488c3db | 2023-07-25 18:25:17 | [diff] [blame] | 195 | } else |
| 196 | // Not catching this could lead to out of bounds errors later |
| 197 | // e.g. using my_mdspan_t = mdspan<int, extents<int, 10>>; my_mdspan_t = m(new int[N], span<int,1>(&N)); |
| 198 | // Right-hand-side construction looks ok with allocation and size matching, |
| 199 | // but since (potentially elsewhere defined) my_mdspan_t has static size m now thinks its range is 10 not N |
| 200 | _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS( |
| 201 | static_cast<_TDynamic>(__vals[__i]) == static_cast<_TDynamic>(__static_val), |
| 202 | "extents construction: mismatch of provided arguments with static extents."); |
Christian Trott | fcaccf8 | 2023-05-16 19:38:11 | [diff] [blame] | 203 | } |
| 204 | } |
| 205 | |
| 206 | // access functions |
| 207 | _LIBCPP_HIDE_FROM_ABI static constexpr _TStatic __static_value(size_t __i) noexcept { |
varconst | 4122db1 | 2023-07-20 17:13:54 | [diff] [blame] | 208 | _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__i < __size_, "extents access: index must be less than rank"); |
Christian Trott | fcaccf8 | 2023-05-16 19:38:11 | [diff] [blame] | 209 | return _StaticValues::__get(__i); |
| 210 | } |
| 211 | |
| 212 | _LIBCPP_HIDE_FROM_ABI constexpr _TDynamic __value(size_t __i) const { |
varconst | 4122db1 | 2023-07-20 17:13:54 | [diff] [blame] | 213 | _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__i < __size_, "extents access: index must be less than rank"); |
Christian Trott | fcaccf8 | 2023-05-16 19:38:11 | [diff] [blame] | 214 | _TStatic __static_val = _StaticValues::__get(__i); |
| 215 | return __static_val == _DynTag ? __dyn_vals_[_DynamicIdxMap::__get(__i)] : static_cast<_TDynamic>(__static_val); |
| 216 | } |
| 217 | _LIBCPP_HIDE_FROM_ABI constexpr _TDynamic operator[](size_t __i) const { |
varconst | 4122db1 | 2023-07-20 17:13:54 | [diff] [blame] | 218 | _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__i < __size_, "extents access: index must be less than rank"); |
Christian Trott | fcaccf8 | 2023-05-16 19:38:11 | [diff] [blame] | 219 | return __value(__i); |
| 220 | } |
| 221 | |
| 222 | // observers |
| 223 | _LIBCPP_HIDE_FROM_ABI static constexpr size_t __size() { return __size_; } |
| 224 | _LIBCPP_HIDE_FROM_ABI static constexpr size_t __size_dynamic() { return __size_dynamic_; } |
| 225 | }; |
| 226 | |
| 227 | // Function to check whether a value is representable as another type |
| 228 | // value must be a positive integer otherwise returns false |
| 229 | // if _From is not an integral, we just check positivity |
| 230 | template <integral _To, class _From> |
Christian Trott | cfa096d | 2023-06-29 14:06:47 | [diff] [blame] | 231 | requires(integral<_From>) |
Christian Trott | fcaccf8 | 2023-05-16 19:38:11 | [diff] [blame] | 232 | _LIBCPP_HIDE_FROM_ABI constexpr bool __is_representable_as(_From __value) { |
| 233 | using _To_u = make_unsigned_t<_To>; |
| 234 | using _From_u = make_unsigned_t<_From>; |
| 235 | if constexpr (is_signed_v<_From>) { |
| 236 | if (__value < 0) |
| 237 | return false; |
| 238 | } |
| 239 | if constexpr (static_cast<_To_u>(numeric_limits<_To>::max()) >= static_cast<_From_u>(numeric_limits<_From>::max())) { |
| 240 | return true; |
| 241 | } else { |
| 242 | return static_cast<_To_u>(numeric_limits<_To>::max()) >= static_cast<_From_u>(__value); |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | template <integral _To, class _From> |
Christian Trott | cfa096d | 2023-06-29 14:06:47 | [diff] [blame] | 247 | requires(!integral<_From>) |
Christian Trott | fcaccf8 | 2023-05-16 19:38:11 | [diff] [blame] | 248 | _LIBCPP_HIDE_FROM_ABI constexpr bool __is_representable_as(_From __value) { |
| 249 | if constexpr (is_signed_v<_To>) { |
| 250 | if (static_cast<_To>(__value) < 0) |
| 251 | return false; |
| 252 | } |
| 253 | return true; |
| 254 | } |
| 255 | |
| 256 | template <integral _To, class... _From> |
| 257 | _LIBCPP_HIDE_FROM_ABI constexpr bool __are_representable_as(_From... __values) { |
| 258 | return (__mdspan_detail::__is_representable_as<_To>(__values) && ... && true); |
| 259 | } |
| 260 | |
| 261 | template <integral _To, class _From, size_t _Size> |
| 262 | _LIBCPP_HIDE_FROM_ABI constexpr bool __are_representable_as(span<_From, _Size> __values) { |
| 263 | for (size_t __i = 0; __i < _Size; __i++) |
| 264 | if (!__mdspan_detail::__is_representable_as<_To>(__values[__i])) |
| 265 | return false; |
| 266 | return true; |
| 267 | } |
| 268 | |
| 269 | } // namespace __mdspan_detail |
| 270 | |
| 271 | // ------------------------------------------------------------------ |
| 272 | // ------------ extents --------------------------------------------- |
| 273 | // ------------------------------------------------------------------ |
| 274 | |
| 275 | // Class to describe the extents of a multi dimensional array. |
| 276 | // Used by mdspan, mdarray and layout mappings. |
| 277 | // See ISO C++ standard [mdspan.extents] |
| 278 | |
| 279 | template <class _IndexType, size_t... _Extents> |
| 280 | class extents { |
| 281 | public: |
| 282 | // typedefs for integral types used |
| 283 | using index_type = _IndexType; |
| 284 | using size_type = make_unsigned_t<index_type>; |
| 285 | using rank_type = size_t; |
| 286 | |
A. Jiang | 74e70ba | 2024-08-27 20:41:55 | [diff] [blame] | 287 | static_assert(__libcpp_integer<index_type>, "extents::index_type must be a signed or unsigned integer type"); |
Christian Trott | fcaccf8 | 2023-05-16 19:38:11 | [diff] [blame] | 288 | static_assert(((__mdspan_detail::__is_representable_as<index_type>(_Extents) || (_Extents == dynamic_extent)) && ...), |
| 289 | "extents ctor: arguments must be representable as index_type and nonnegative"); |
| 290 | |
| 291 | private: |
| 292 | static constexpr rank_type __rank_ = sizeof...(_Extents); |
| 293 | static constexpr rank_type __rank_dynamic_ = ((_Extents == dynamic_extent) + ... + 0); |
| 294 | |
| 295 | // internal storage type using __maybe_static_array |
| 296 | using _Values = __mdspan_detail::__maybe_static_array<_IndexType, size_t, dynamic_extent, _Extents...>; |
| 297 | [[no_unique_address]] _Values __vals_; |
| 298 | |
| 299 | public: |
| 300 | // [mdspan.extents.obs], observers of multidimensional index space |
| 301 | _LIBCPP_HIDE_FROM_ABI static constexpr rank_type rank() noexcept { return __rank_; } |
| 302 | _LIBCPP_HIDE_FROM_ABI static constexpr rank_type rank_dynamic() noexcept { return __rank_dynamic_; } |
| 303 | |
| 304 | _LIBCPP_HIDE_FROM_ABI constexpr index_type extent(rank_type __r) const noexcept { return __vals_.__value(__r); } |
| 305 | _LIBCPP_HIDE_FROM_ABI static constexpr size_t static_extent(rank_type __r) noexcept { |
| 306 | return _Values::__static_value(__r); |
| 307 | } |
| 308 | |
| 309 | // [mdspan.extents.cons], constructors |
| 310 | _LIBCPP_HIDE_FROM_ABI constexpr extents() noexcept = default; |
| 311 | |
| 312 | // Construction from just dynamic or all values. |
| 313 | // Precondition check is deferred to __maybe_static_array constructor |
| 314 | template <class... _OtherIndexTypes> |
| 315 | requires((is_convertible_v<_OtherIndexTypes, index_type> && ...) && |
| 316 | (is_nothrow_constructible_v<index_type, _OtherIndexTypes> && ...) && |
| 317 | (sizeof...(_OtherIndexTypes) == __rank_ || sizeof...(_OtherIndexTypes) == __rank_dynamic_)) |
| 318 | _LIBCPP_HIDE_FROM_ABI constexpr explicit extents(_OtherIndexTypes... __dynvals) noexcept |
| 319 | : __vals_(static_cast<index_type>(__dynvals)...) { |
Christian Trott | 488c3db | 2023-07-25 18:25:17 | [diff] [blame] | 320 | // Not catching this could lead to out of bounds errors later |
| 321 | // e.g. mdspan m(ptr, dextents<char, 1>(200u)); leads to an extent of -56 on m |
| 322 | _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__mdspan_detail::__are_representable_as<index_type>(__dynvals...), |
| 323 | "extents ctor: arguments must be representable as index_type and nonnegative"); |
Christian Trott | fcaccf8 | 2023-05-16 19:38:11 | [diff] [blame] | 324 | } |
| 325 | |
| 326 | template <class _OtherIndexType, size_t _Size> |
Christian Trott | fc48765 | 2023-07-25 04:35:15 | [diff] [blame] | 327 | requires(is_convertible_v<const _OtherIndexType&, index_type> && |
| 328 | is_nothrow_constructible_v<index_type, const _OtherIndexType&> && |
Christian Trott | fcaccf8 | 2023-05-16 19:38:11 | [diff] [blame] | 329 | (_Size == __rank_ || _Size == __rank_dynamic_)) |
| 330 | explicit(_Size != __rank_dynamic_) |
| 331 | _LIBCPP_HIDE_FROM_ABI constexpr extents(const array<_OtherIndexType, _Size>& __exts) noexcept |
| 332 | : __vals_(span(__exts)) { |
Christian Trott | 488c3db | 2023-07-25 18:25:17 | [diff] [blame] | 333 | // Not catching this could lead to out of bounds errors later |
| 334 | // e.g. mdspan m(ptr, dextents<char, 1>(array<unsigned,1>(200))); leads to an extent of -56 on m |
| 335 | _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__mdspan_detail::__are_representable_as<index_type>(span(__exts)), |
| 336 | "extents ctor: arguments must be representable as index_type and nonnegative"); |
Christian Trott | fcaccf8 | 2023-05-16 19:38:11 | [diff] [blame] | 337 | } |
| 338 | |
| 339 | template <class _OtherIndexType, size_t _Size> |
Christian Trott | fc48765 | 2023-07-25 04:35:15 | [diff] [blame] | 340 | requires(is_convertible_v<const _OtherIndexType&, index_type> && |
| 341 | is_nothrow_constructible_v<index_type, const _OtherIndexType&> && |
Christian Trott | fcaccf8 | 2023-05-16 19:38:11 | [diff] [blame] | 342 | (_Size == __rank_ || _Size == __rank_dynamic_)) |
| 343 | explicit(_Size != __rank_dynamic_) |
| 344 | _LIBCPP_HIDE_FROM_ABI constexpr extents(const span<_OtherIndexType, _Size>& __exts) noexcept |
| 345 | : __vals_(__exts) { |
Christian Trott | 488c3db | 2023-07-25 18:25:17 | [diff] [blame] | 346 | // Not catching this could lead to out of bounds errors later |
| 347 | // e.g. array a{200u}; mdspan<int, dextents<char,1>> m(ptr, extents(span<unsigned,1>(a))); leads to an extent of -56 |
| 348 | // on m |
| 349 | _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__mdspan_detail::__are_representable_as<index_type>(__exts), |
| 350 | "extents ctor: arguments must be representable as index_type and nonnegative"); |
Christian Trott | fcaccf8 | 2023-05-16 19:38:11 | [diff] [blame] | 351 | } |
| 352 | |
| 353 | private: |
| 354 | // Function to construct extents storage from other extents. |
| 355 | template <size_t _DynCount, size_t _Idx, class _OtherExtents, class... _DynamicValues> |
| 356 | requires(_Idx < __rank_) |
| 357 | _LIBCPP_HIDE_FROM_ABI constexpr _Values __construct_vals_from_extents( |
| 358 | integral_constant<size_t, _DynCount>, |
| 359 | integral_constant<size_t, _Idx>, |
| 360 | const _OtherExtents& __exts, |
| 361 | _DynamicValues... __dynamic_values) noexcept { |
| 362 | if constexpr (static_extent(_Idx) == dynamic_extent) |
| 363 | return __construct_vals_from_extents( |
| 364 | integral_constant<size_t, _DynCount + 1>(), |
| 365 | integral_constant<size_t, _Idx + 1>(), |
| 366 | __exts, |
| 367 | __dynamic_values..., |
| 368 | __exts.extent(_Idx)); |
| 369 | else |
| 370 | return __construct_vals_from_extents( |
| 371 | integral_constant<size_t, _DynCount>(), integral_constant<size_t, _Idx + 1>(), __exts, __dynamic_values...); |
| 372 | } |
| 373 | |
| 374 | template <size_t _DynCount, size_t _Idx, class _OtherExtents, class... _DynamicValues> |
| 375 | requires((_Idx == __rank_) && (_DynCount == __rank_dynamic_)) |
| 376 | _LIBCPP_HIDE_FROM_ABI constexpr _Values __construct_vals_from_extents( |
| 377 | integral_constant<size_t, _DynCount>, |
| 378 | integral_constant<size_t, _Idx>, |
| 379 | const _OtherExtents&, |
| 380 | _DynamicValues... __dynamic_values) noexcept { |
| 381 | return _Values{static_cast<index_type>(__dynamic_values)...}; |
| 382 | } |
| 383 | |
| 384 | public: |
| 385 | // Converting constructor from other extents specializations |
| 386 | template <class _OtherIndexType, size_t... _OtherExtents> |
| 387 | requires((sizeof...(_OtherExtents) == sizeof...(_Extents)) && |
| 388 | ((_OtherExtents == dynamic_extent || _Extents == dynamic_extent || _OtherExtents == _Extents) && ...)) |
| 389 | explicit((((_Extents != dynamic_extent) && (_OtherExtents == dynamic_extent)) || ...) || |
| 390 | (static_cast<make_unsigned_t<index_type>>(numeric_limits<index_type>::max()) < |
| 391 | static_cast<make_unsigned_t<_OtherIndexType>>(numeric_limits<_OtherIndexType>::max()))) |
| 392 | _LIBCPP_HIDE_FROM_ABI constexpr extents(const extents<_OtherIndexType, _OtherExtents...>& __other) noexcept |
| 393 | : __vals_( |
| 394 | __construct_vals_from_extents(integral_constant<size_t, 0>(), integral_constant<size_t, 0>(), __other)) { |
| 395 | if constexpr (rank() > 0) { |
| 396 | for (size_t __r = 0; __r < rank(); __r++) { |
| 397 | if constexpr (static_cast<make_unsigned_t<index_type>>(numeric_limits<index_type>::max()) < |
| 398 | static_cast<make_unsigned_t<_OtherIndexType>>(numeric_limits<_OtherIndexType>::max())) { |
Christian Trott | 488c3db | 2023-07-25 18:25:17 | [diff] [blame] | 399 | // Not catching this could lead to out of bounds errors later |
| 400 | // e.g. dextents<char,1>> e(dextents<unsigned,1>(200)) leads to an extent of -56 on e |
| 401 | _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS( |
| 402 | __mdspan_detail::__is_representable_as<index_type>(__other.extent(__r)), |
| 403 | "extents ctor: arguments must be representable as index_type and nonnegative"); |
Christian Trott | fcaccf8 | 2023-05-16 19:38:11 | [diff] [blame] | 404 | } |
Christian Trott | 488c3db | 2023-07-25 18:25:17 | [diff] [blame] | 405 | // Not catching this could lead to out of bounds errors later |
| 406 | // e.g. mdspan<int, extents<int, 10>> m = mdspan<int, dextents<int, 1>>(new int[5], 5); |
| 407 | // Right-hand-side construction was ok, but m now thinks its range is 10 not 5 |
| 408 | _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS( |
Christian Trott | fcaccf8 | 2023-05-16 19:38:11 | [diff] [blame] | 409 | (_Values::__static_value(__r) == dynamic_extent) || |
| 410 | (static_cast<index_type>(__other.extent(__r)) == static_cast<index_type>(_Values::__static_value(__r))), |
| 411 | "extents construction: mismatch of provided arguments with static extents."); |
| 412 | } |
| 413 | } |
| 414 | } |
| 415 | |
| 416 | // Comparison operator |
| 417 | template <class _OtherIndexType, size_t... _OtherExtents> |
| 418 | _LIBCPP_HIDE_FROM_ABI friend constexpr bool |
| 419 | operator==(const extents& __lhs, const extents<_OtherIndexType, _OtherExtents...>& __rhs) noexcept { |
| 420 | if constexpr (rank() != sizeof...(_OtherExtents)) { |
| 421 | return false; |
| 422 | } else { |
| 423 | for (rank_type __r = 0; __r < __rank_; __r++) { |
| 424 | // avoid warning when comparing signed and unsigner integers and pick the wider of two types |
| 425 | using _CommonType = common_type_t<index_type, _OtherIndexType>; |
| 426 | if (static_cast<_CommonType>(__lhs.extent(__r)) != static_cast<_CommonType>(__rhs.extent(__r))) { |
| 427 | return false; |
| 428 | } |
| 429 | } |
| 430 | } |
| 431 | return true; |
| 432 | } |
| 433 | }; |
| 434 | |
| 435 | // Recursive helper classes to implement dextents alias for extents |
| 436 | namespace __mdspan_detail { |
| 437 | |
| 438 | template <class _IndexType, size_t _Rank, class _Extents = extents<_IndexType>> |
| 439 | struct __make_dextents; |
| 440 | |
| 441 | template <class _IndexType, size_t _Rank, size_t... _ExtentsPack> |
| 442 | struct __make_dextents< _IndexType, _Rank, extents<_IndexType, _ExtentsPack...>> { |
| 443 | using type = |
| 444 | typename __make_dextents< _IndexType, _Rank - 1, extents<_IndexType, dynamic_extent, _ExtentsPack...>>::type; |
| 445 | }; |
| 446 | |
| 447 | template <class _IndexType, size_t... _ExtentsPack> |
| 448 | struct __make_dextents< _IndexType, 0, extents<_IndexType, _ExtentsPack...>> { |
| 449 | using type = extents<_IndexType, _ExtentsPack...>; |
| 450 | }; |
| 451 | |
Louis Dionne | 953af0e | 2024-09-05 16:39:05 | [diff] [blame^] | 452 | } // namespace __mdspan_detail |
Christian Trott | fcaccf8 | 2023-05-16 19:38:11 | [diff] [blame] | 453 | |
| 454 | // [mdspan.extents.dextents], alias template |
| 455 | template <class _IndexType, size_t _Rank> |
| 456 | using dextents = typename __mdspan_detail::__make_dextents<_IndexType, _Rank>::type; |
| 457 | |
Xiaoyang Liu | 4e338dc | 2024-07-15 15:23:34 | [diff] [blame] | 458 | # if _LIBCPP_STD_VER >= 26 |
| 459 | // [mdspan.extents.dims], alias template `dims` |
| 460 | template <size_t _Rank, class _IndexType = size_t> |
| 461 | using dims = dextents<_IndexType, _Rank>; |
| 462 | # endif |
| 463 | |
Christian Trott | fcaccf8 | 2023-05-16 19:38:11 | [diff] [blame] | 464 | // Deduction guide for extents |
Xiaoyang Liu | 8c11d37 | 2024-06-25 15:20:14 | [diff] [blame] | 465 | # if _LIBCPP_STD_VER >= 26 |
Christian Trott | fcaccf8 | 2023-05-16 19:38:11 | [diff] [blame] | 466 | template <class... _IndexTypes> |
Xiaoyang Liu | 8c11d37 | 2024-06-25 15:20:14 | [diff] [blame] | 467 | requires(is_convertible_v<_IndexTypes, size_t> && ...) |
| 468 | explicit extents(_IndexTypes...) -> extents<size_t, __maybe_static_ext<_IndexTypes>...>; |
| 469 | # else |
| 470 | template <class... _IndexTypes> |
| 471 | requires(is_convertible_v<_IndexTypes, size_t> && ...) |
| 472 | explicit extents(_IndexTypes...) -> extents<size_t, size_t(((void)sizeof(_IndexTypes), dynamic_extent))...>; |
| 473 | # endif |
Christian Trott | fcaccf8 | 2023-05-16 19:38:11 | [diff] [blame] | 474 | |
Christian Trott | fcaccf8 | 2023-05-16 19:38:11 | [diff] [blame] | 475 | namespace __mdspan_detail { |
| 476 | |
Christian Trott | cfa096d | 2023-06-29 14:06:47 | [diff] [blame] | 477 | // Helper type traits for identifying a class as extents. |
Christian Trott | fcaccf8 | 2023-05-16 19:38:11 | [diff] [blame] | 478 | template <class _Tp> |
| 479 | struct __is_extents : false_type {}; |
| 480 | |
| 481 | template <class _IndexType, size_t... _ExtentsPack> |
| 482 | struct __is_extents<extents<_IndexType, _ExtentsPack...>> : true_type {}; |
| 483 | |
| 484 | template <class _Tp> |
| 485 | inline constexpr bool __is_extents_v = __is_extents<_Tp>::value; |
| 486 | |
Christian Trott | cfa096d | 2023-06-29 14:06:47 | [diff] [blame] | 487 | // Function to check whether a set of indices are a multidimensional |
| 488 | // index into extents. This is a word of power in the C++ standard |
| 489 | // requiring that the indices are larger than 0 and smaller than |
| 490 | // the respective extents. |
| 491 | |
| 492 | template <integral _IndexType, class _From> |
| 493 | requires(integral<_From>) |
| 494 | _LIBCPP_HIDE_FROM_ABI constexpr bool __is_index_in_extent(_IndexType __extent, _From __value) { |
| 495 | if constexpr (is_signed_v<_From>) { |
| 496 | if (__value < 0) |
| 497 | return false; |
| 498 | } |
| 499 | using _Tp = common_type_t<_IndexType, _From>; |
| 500 | return static_cast<_Tp>(__value) < static_cast<_Tp>(__extent); |
| 501 | } |
| 502 | |
| 503 | template <integral _IndexType, class _From> |
| 504 | requires(!integral<_From>) |
| 505 | _LIBCPP_HIDE_FROM_ABI constexpr bool __is_index_in_extent(_IndexType __extent, _From __value) { |
| 506 | if constexpr (is_signed_v<_IndexType>) { |
| 507 | if (static_cast<_IndexType>(__value) < 0) |
| 508 | return false; |
| 509 | } |
| 510 | return static_cast<_IndexType>(__value) < __extent; |
| 511 | } |
| 512 | |
| 513 | template <size_t... _Idxs, class _Extents, class... _From> |
| 514 | _LIBCPP_HIDE_FROM_ABI constexpr bool |
| 515 | __is_multidimensional_index_in_impl(index_sequence<_Idxs...>, const _Extents& __ext, _From... __values) { |
| 516 | return (__mdspan_detail::__is_index_in_extent(__ext.extent(_Idxs), __values) && ...); |
| 517 | } |
| 518 | |
| 519 | template <class _Extents, class... _From> |
| 520 | _LIBCPP_HIDE_FROM_ABI constexpr bool __is_multidimensional_index_in(const _Extents& __ext, _From... __values) { |
| 521 | return __mdspan_detail::__is_multidimensional_index_in_impl( |
| 522 | make_index_sequence<_Extents::rank()>(), __ext, __values...); |
| 523 | } |
| 524 | |
Christian Trott | fcaccf8 | 2023-05-16 19:38:11 | [diff] [blame] | 525 | } // namespace __mdspan_detail |
| 526 | |
| 527 | #endif // _LIBCPP_STD_VER >= 23 |
| 528 | |
| 529 | _LIBCPP_END_NAMESPACE_STD |
| 530 | |
| 531 | _LIBCPP_POP_MACROS |
| 532 | |
| 533 | #endif // _LIBCPP___MDSPAN_EXTENTS_H |