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