Path: blob/main/contrib/llvm-project/libcxx/include/__mdspan/extents.h
35262 views
// -*- C++ -*-1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7// Kokkos v. 4.08// Copyright (2022) National Technology & Engineering9// Solutions of Sandia, LLC (NTESS).10//11// Under the terms of Contract DE-NA0003525 with NTESS,12// the U.S. Government retains certain rights in this software.13//14//===---------------------------------------------------------------------===//1516#ifndef _LIBCPP___MDSPAN_EXTENTS_H17#define _LIBCPP___MDSPAN_EXTENTS_H1819#include <__assert>20#include <__config>21#include <__type_traits/common_type.h>22#include <__type_traits/is_convertible.h>23#include <__type_traits/is_nothrow_constructible.h>24#include <__type_traits/is_same.h>25#include <__type_traits/make_unsigned.h>26#include <__utility/integer_sequence.h>27#include <__utility/unreachable.h>28#include <array>29#include <cinttypes>30#include <concepts>31#include <cstddef>32#include <limits>33#include <span>3435#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)36# pragma GCC system_header37#endif3839_LIBCPP_PUSH_MACROS40#include <__undef_macros>4142_LIBCPP_BEGIN_NAMESPACE_STD4344#if _LIBCPP_STD_VER >= 234546namespace __mdspan_detail {4748// ------------------------------------------------------------------49// ------------ __static_array --------------------------------------50// ------------------------------------------------------------------51// array like class which provides an array of static values with get52template <class _Tp, _Tp... _Values>53struct __static_array {54static constexpr array<_Tp, sizeof...(_Values)> __array = {_Values...};5556public:57_LIBCPP_HIDE_FROM_ABI static constexpr size_t __size() { return sizeof...(_Values); }58_LIBCPP_HIDE_FROM_ABI static constexpr _Tp __get(size_t __index) noexcept { return __array[__index]; }5960template <size_t _Index>61_LIBCPP_HIDE_FROM_ABI static constexpr _Tp __get() {62return __get(_Index);63}64};6566// ------------------------------------------------------------------67// ------------ __possibly_empty_array -----------------------------68// ------------------------------------------------------------------6970// array like class which provides get function and operator [], and71// has a specialization for the size 0 case.72// This is needed to make the __maybe_static_array be truly empty, for73// all static values.7475template <class _Tp, size_t _Size>76struct __possibly_empty_array {77_Tp __vals_[_Size];78_LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator[](size_t __index) { return __vals_[__index]; }79_LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator[](size_t __index) const { return __vals_[__index]; }80};8182template <class _Tp>83struct __possibly_empty_array<_Tp, 0> {84_LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator[](size_t) { __libcpp_unreachable(); }85_LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator[](size_t) const { __libcpp_unreachable(); }86};8788// ------------------------------------------------------------------89// ------------ static_partial_sums ---------------------------------90// ------------------------------------------------------------------9192// Provides a compile time partial sum one can index into9394template <size_t... _Values>95struct __static_partial_sums {96_LIBCPP_HIDE_FROM_ABI static constexpr array<size_t, sizeof...(_Values)> __static_partial_sums_impl() {97array<size_t, sizeof...(_Values)> __values{_Values...};98array<size_t, sizeof...(_Values)> __partial_sums{{}};99size_t __running_sum = 0;100for (int __i = 0; __i != sizeof...(_Values); ++__i) {101__partial_sums[__i] = __running_sum;102__running_sum += __values[__i];103}104return __partial_sums;105}106static constexpr array<size_t, sizeof...(_Values)> __result{__static_partial_sums_impl()};107108_LIBCPP_HIDE_FROM_ABI static constexpr size_t __get(size_t __index) { return __result[__index]; }109};110111// ------------------------------------------------------------------112// ------------ __maybe_static_array --------------------------------113// ------------------------------------------------------------------114115// array like class which has a mix of static and runtime values but116// only stores the runtime values.117// The type of the static and the runtime values can be different.118// The position of a dynamic value is indicated through a tag value.119template <class _TDynamic, class _TStatic, _TStatic _DynTag, _TStatic... _Values>120struct __maybe_static_array {121static_assert(is_convertible<_TStatic, _TDynamic>::value,122"__maybe_static_array: _TStatic must be convertible to _TDynamic");123static_assert(is_convertible<_TDynamic, _TStatic>::value,124"__maybe_static_array: _TDynamic must be convertible to _TStatic");125126private:127// Static values member128static constexpr size_t __size_ = sizeof...(_Values);129static constexpr size_t __size_dynamic_ = ((_Values == _DynTag) + ... + 0);130using _StaticValues = __static_array<_TStatic, _Values...>;131using _DynamicValues = __possibly_empty_array<_TDynamic, __size_dynamic_>;132133// Dynamic values member134_LIBCPP_NO_UNIQUE_ADDRESS _DynamicValues __dyn_vals_;135136// static mapping of indices to the position in the dynamic values array137using _DynamicIdxMap = __static_partial_sums<static_cast<size_t>(_Values == _DynTag)...>;138139template <size_t... _Indices>140_LIBCPP_HIDE_FROM_ABI static constexpr _DynamicValues __zeros(index_sequence<_Indices...>) noexcept {141return _DynamicValues{((void)_Indices, 0)...};142}143144public:145_LIBCPP_HIDE_FROM_ABI constexpr __maybe_static_array() noexcept146: __dyn_vals_{__zeros(make_index_sequence<__size_dynamic_>())} {}147148// constructors from dynamic values only -- this covers the case for rank() == 0149template <class... _DynVals>150requires(sizeof...(_DynVals) == __size_dynamic_)151_LIBCPP_HIDE_FROM_ABI constexpr __maybe_static_array(_DynVals... __vals)152: __dyn_vals_{static_cast<_TDynamic>(__vals)...} {}153154template <class _Tp, size_t _Size >155requires(_Size == __size_dynamic_)156_LIBCPP_HIDE_FROM_ABI constexpr __maybe_static_array([[maybe_unused]] const span<_Tp, _Size>& __vals) {157if constexpr (_Size > 0) {158for (size_t __i = 0; __i < _Size; __i++)159__dyn_vals_[__i] = static_cast<_TDynamic>(__vals[__i]);160}161}162163// constructors from all values -- here rank will be greater than 0164template <class... _DynVals>165requires(sizeof...(_DynVals) != __size_dynamic_)166_LIBCPP_HIDE_FROM_ABI constexpr __maybe_static_array(_DynVals... __vals) {167static_assert(sizeof...(_DynVals) == __size_, "Invalid number of values.");168_TDynamic __values[__size_] = {static_cast<_TDynamic>(__vals)...};169for (size_t __i = 0; __i < __size_; __i++) {170_TStatic __static_val = _StaticValues::__get(__i);171if (__static_val == _DynTag) {172__dyn_vals_[_DynamicIdxMap::__get(__i)] = __values[__i];173} else174// Not catching this could lead to out of bounds errors later175// e.g. using my_mdspan_t = mdspan<int, extents<int, 10>>; my_mdspan_t = m(new int[5], 5);176// Right-hand-side construction looks ok with allocation and size matching,177// but since (potentially elsewhere defined) my_mdspan_t has static size m now thinks its range is 10 not 5178_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(179__values[__i] == static_cast<_TDynamic>(__static_val),180"extents construction: mismatch of provided arguments with static extents.");181}182}183184template <class _Tp, size_t _Size>185requires(_Size != __size_dynamic_)186_LIBCPP_HIDE_FROM_ABI constexpr __maybe_static_array(const span<_Tp, _Size>& __vals) {187static_assert(_Size == __size_ || __size_ == dynamic_extent);188for (size_t __i = 0; __i < __size_; __i++) {189_TStatic __static_val = _StaticValues::__get(__i);190if (__static_val == _DynTag) {191__dyn_vals_[_DynamicIdxMap::__get(__i)] = static_cast<_TDynamic>(__vals[__i]);192} else193// Not catching this could lead to out of bounds errors later194// e.g. using my_mdspan_t = mdspan<int, extents<int, 10>>; my_mdspan_t = m(new int[N], span<int,1>(&N));195// Right-hand-side construction looks ok with allocation and size matching,196// but since (potentially elsewhere defined) my_mdspan_t has static size m now thinks its range is 10 not N197_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(198static_cast<_TDynamic>(__vals[__i]) == static_cast<_TDynamic>(__static_val),199"extents construction: mismatch of provided arguments with static extents.");200}201}202203// access functions204_LIBCPP_HIDE_FROM_ABI static constexpr _TStatic __static_value(size_t __i) noexcept {205_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__i < __size_, "extents access: index must be less than rank");206return _StaticValues::__get(__i);207}208209_LIBCPP_HIDE_FROM_ABI constexpr _TDynamic __value(size_t __i) const {210_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__i < __size_, "extents access: index must be less than rank");211_TStatic __static_val = _StaticValues::__get(__i);212return __static_val == _DynTag ? __dyn_vals_[_DynamicIdxMap::__get(__i)] : static_cast<_TDynamic>(__static_val);213}214_LIBCPP_HIDE_FROM_ABI constexpr _TDynamic operator[](size_t __i) const {215_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__i < __size_, "extents access: index must be less than rank");216return __value(__i);217}218219// observers220_LIBCPP_HIDE_FROM_ABI static constexpr size_t __size() { return __size_; }221_LIBCPP_HIDE_FROM_ABI static constexpr size_t __size_dynamic() { return __size_dynamic_; }222};223224// Function to check whether a value is representable as another type225// value must be a positive integer otherwise returns false226// if _From is not an integral, we just check positivity227template <integral _To, class _From>228requires(integral<_From>)229_LIBCPP_HIDE_FROM_ABI constexpr bool __is_representable_as(_From __value) {230using _To_u = make_unsigned_t<_To>;231using _From_u = make_unsigned_t<_From>;232if constexpr (is_signed_v<_From>) {233if (__value < 0)234return false;235}236if constexpr (static_cast<_To_u>(numeric_limits<_To>::max()) >= static_cast<_From_u>(numeric_limits<_From>::max())) {237return true;238} else {239return static_cast<_To_u>(numeric_limits<_To>::max()) >= static_cast<_From_u>(__value);240}241}242243template <integral _To, class _From>244requires(!integral<_From>)245_LIBCPP_HIDE_FROM_ABI constexpr bool __is_representable_as(_From __value) {246if constexpr (is_signed_v<_To>) {247if (static_cast<_To>(__value) < 0)248return false;249}250return true;251}252253template <integral _To, class... _From>254_LIBCPP_HIDE_FROM_ABI constexpr bool __are_representable_as(_From... __values) {255return (__mdspan_detail::__is_representable_as<_To>(__values) && ... && true);256}257258template <integral _To, class _From, size_t _Size>259_LIBCPP_HIDE_FROM_ABI constexpr bool __are_representable_as(span<_From, _Size> __values) {260for (size_t __i = 0; __i < _Size; __i++)261if (!__mdspan_detail::__is_representable_as<_To>(__values[__i]))262return false;263return true;264}265266} // namespace __mdspan_detail267268// ------------------------------------------------------------------269// ------------ extents ---------------------------------------------270// ------------------------------------------------------------------271272// Class to describe the extents of a multi dimensional array.273// Used by mdspan, mdarray and layout mappings.274// See ISO C++ standard [mdspan.extents]275276template <class _IndexType, size_t... _Extents>277class extents {278public:279// typedefs for integral types used280using index_type = _IndexType;281using size_type = make_unsigned_t<index_type>;282using rank_type = size_t;283284static_assert(is_integral<index_type>::value && !is_same<index_type, bool>::value,285"extents::index_type must be a signed or unsigned integer type");286static_assert(((__mdspan_detail::__is_representable_as<index_type>(_Extents) || (_Extents == dynamic_extent)) && ...),287"extents ctor: arguments must be representable as index_type and nonnegative");288289private:290static constexpr rank_type __rank_ = sizeof...(_Extents);291static constexpr rank_type __rank_dynamic_ = ((_Extents == dynamic_extent) + ... + 0);292293// internal storage type using __maybe_static_array294using _Values = __mdspan_detail::__maybe_static_array<_IndexType, size_t, dynamic_extent, _Extents...>;295[[no_unique_address]] _Values __vals_;296297public:298// [mdspan.extents.obs], observers of multidimensional index space299_LIBCPP_HIDE_FROM_ABI static constexpr rank_type rank() noexcept { return __rank_; }300_LIBCPP_HIDE_FROM_ABI static constexpr rank_type rank_dynamic() noexcept { return __rank_dynamic_; }301302_LIBCPP_HIDE_FROM_ABI constexpr index_type extent(rank_type __r) const noexcept { return __vals_.__value(__r); }303_LIBCPP_HIDE_FROM_ABI static constexpr size_t static_extent(rank_type __r) noexcept {304return _Values::__static_value(__r);305}306307// [mdspan.extents.cons], constructors308_LIBCPP_HIDE_FROM_ABI constexpr extents() noexcept = default;309310// Construction from just dynamic or all values.311// Precondition check is deferred to __maybe_static_array constructor312template <class... _OtherIndexTypes>313requires((is_convertible_v<_OtherIndexTypes, index_type> && ...) &&314(is_nothrow_constructible_v<index_type, _OtherIndexTypes> && ...) &&315(sizeof...(_OtherIndexTypes) == __rank_ || sizeof...(_OtherIndexTypes) == __rank_dynamic_))316_LIBCPP_HIDE_FROM_ABI constexpr explicit extents(_OtherIndexTypes... __dynvals) noexcept317: __vals_(static_cast<index_type>(__dynvals)...) {318// Not catching this could lead to out of bounds errors later319// e.g. mdspan m(ptr, dextents<char, 1>(200u)); leads to an extent of -56 on m320_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__mdspan_detail::__are_representable_as<index_type>(__dynvals...),321"extents ctor: arguments must be representable as index_type and nonnegative");322}323324template <class _OtherIndexType, size_t _Size>325requires(is_convertible_v<const _OtherIndexType&, index_type> &&326is_nothrow_constructible_v<index_type, const _OtherIndexType&> &&327(_Size == __rank_ || _Size == __rank_dynamic_))328explicit(_Size != __rank_dynamic_)329_LIBCPP_HIDE_FROM_ABI constexpr extents(const array<_OtherIndexType, _Size>& __exts) noexcept330: __vals_(span(__exts)) {331// Not catching this could lead to out of bounds errors later332// e.g. mdspan m(ptr, dextents<char, 1>(array<unsigned,1>(200))); leads to an extent of -56 on m333_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__mdspan_detail::__are_representable_as<index_type>(span(__exts)),334"extents ctor: arguments must be representable as index_type and nonnegative");335}336337template <class _OtherIndexType, size_t _Size>338requires(is_convertible_v<const _OtherIndexType&, index_type> &&339is_nothrow_constructible_v<index_type, const _OtherIndexType&> &&340(_Size == __rank_ || _Size == __rank_dynamic_))341explicit(_Size != __rank_dynamic_)342_LIBCPP_HIDE_FROM_ABI constexpr extents(const span<_OtherIndexType, _Size>& __exts) noexcept343: __vals_(__exts) {344// Not catching this could lead to out of bounds errors later345// e.g. array a{200u}; mdspan<int, dextents<char,1>> m(ptr, extents(span<unsigned,1>(a))); leads to an extent of -56346// on m347_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__mdspan_detail::__are_representable_as<index_type>(__exts),348"extents ctor: arguments must be representable as index_type and nonnegative");349}350351private:352// Function to construct extents storage from other extents.353template <size_t _DynCount, size_t _Idx, class _OtherExtents, class... _DynamicValues>354requires(_Idx < __rank_)355_LIBCPP_HIDE_FROM_ABI constexpr _Values __construct_vals_from_extents(356integral_constant<size_t, _DynCount>,357integral_constant<size_t, _Idx>,358const _OtherExtents& __exts,359_DynamicValues... __dynamic_values) noexcept {360if constexpr (static_extent(_Idx) == dynamic_extent)361return __construct_vals_from_extents(362integral_constant<size_t, _DynCount + 1>(),363integral_constant<size_t, _Idx + 1>(),364__exts,365__dynamic_values...,366__exts.extent(_Idx));367else368return __construct_vals_from_extents(369integral_constant<size_t, _DynCount>(), integral_constant<size_t, _Idx + 1>(), __exts, __dynamic_values...);370}371372template <size_t _DynCount, size_t _Idx, class _OtherExtents, class... _DynamicValues>373requires((_Idx == __rank_) && (_DynCount == __rank_dynamic_))374_LIBCPP_HIDE_FROM_ABI constexpr _Values __construct_vals_from_extents(375integral_constant<size_t, _DynCount>,376integral_constant<size_t, _Idx>,377const _OtherExtents&,378_DynamicValues... __dynamic_values) noexcept {379return _Values{static_cast<index_type>(__dynamic_values)...};380}381382public:383// Converting constructor from other extents specializations384template <class _OtherIndexType, size_t... _OtherExtents>385requires((sizeof...(_OtherExtents) == sizeof...(_Extents)) &&386((_OtherExtents == dynamic_extent || _Extents == dynamic_extent || _OtherExtents == _Extents) && ...))387explicit((((_Extents != dynamic_extent) && (_OtherExtents == dynamic_extent)) || ...) ||388(static_cast<make_unsigned_t<index_type>>(numeric_limits<index_type>::max()) <389static_cast<make_unsigned_t<_OtherIndexType>>(numeric_limits<_OtherIndexType>::max())))390_LIBCPP_HIDE_FROM_ABI constexpr extents(const extents<_OtherIndexType, _OtherExtents...>& __other) noexcept391: __vals_(392__construct_vals_from_extents(integral_constant<size_t, 0>(), integral_constant<size_t, 0>(), __other)) {393if constexpr (rank() > 0) {394for (size_t __r = 0; __r < rank(); __r++) {395if constexpr (static_cast<make_unsigned_t<index_type>>(numeric_limits<index_type>::max()) <396static_cast<make_unsigned_t<_OtherIndexType>>(numeric_limits<_OtherIndexType>::max())) {397// Not catching this could lead to out of bounds errors later398// e.g. dextents<char,1>> e(dextents<unsigned,1>(200)) leads to an extent of -56 on e399_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(400__mdspan_detail::__is_representable_as<index_type>(__other.extent(__r)),401"extents ctor: arguments must be representable as index_type and nonnegative");402}403// Not catching this could lead to out of bounds errors later404// e.g. mdspan<int, extents<int, 10>> m = mdspan<int, dextents<int, 1>>(new int[5], 5);405// Right-hand-side construction was ok, but m now thinks its range is 10 not 5406_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(407(_Values::__static_value(__r) == dynamic_extent) ||408(static_cast<index_type>(__other.extent(__r)) == static_cast<index_type>(_Values::__static_value(__r))),409"extents construction: mismatch of provided arguments with static extents.");410}411}412}413414// Comparison operator415template <class _OtherIndexType, size_t... _OtherExtents>416_LIBCPP_HIDE_FROM_ABI friend constexpr bool417operator==(const extents& __lhs, const extents<_OtherIndexType, _OtherExtents...>& __rhs) noexcept {418if constexpr (rank() != sizeof...(_OtherExtents)) {419return false;420} else {421for (rank_type __r = 0; __r < __rank_; __r++) {422// avoid warning when comparing signed and unsigner integers and pick the wider of two types423using _CommonType = common_type_t<index_type, _OtherIndexType>;424if (static_cast<_CommonType>(__lhs.extent(__r)) != static_cast<_CommonType>(__rhs.extent(__r))) {425return false;426}427}428}429return true;430}431};432433// Recursive helper classes to implement dextents alias for extents434namespace __mdspan_detail {435436template <class _IndexType, size_t _Rank, class _Extents = extents<_IndexType>>437struct __make_dextents;438439template <class _IndexType, size_t _Rank, size_t... _ExtentsPack>440struct __make_dextents< _IndexType, _Rank, extents<_IndexType, _ExtentsPack...>> {441using type =442typename __make_dextents< _IndexType, _Rank - 1, extents<_IndexType, dynamic_extent, _ExtentsPack...>>::type;443};444445template <class _IndexType, size_t... _ExtentsPack>446struct __make_dextents< _IndexType, 0, extents<_IndexType, _ExtentsPack...>> {447using type = extents<_IndexType, _ExtentsPack...>;448};449450} // end namespace __mdspan_detail451452// [mdspan.extents.dextents], alias template453template <class _IndexType, size_t _Rank>454using dextents = typename __mdspan_detail::__make_dextents<_IndexType, _Rank>::type;455456# if _LIBCPP_STD_VER >= 26457// [mdspan.extents.dims], alias template `dims`458template <size_t _Rank, class _IndexType = size_t>459using dims = dextents<_IndexType, _Rank>;460# endif461462// Deduction guide for extents463# if _LIBCPP_STD_VER >= 26464template <class... _IndexTypes>465requires(is_convertible_v<_IndexTypes, size_t> && ...)466explicit extents(_IndexTypes...) -> extents<size_t, __maybe_static_ext<_IndexTypes>...>;467# else468template <class... _IndexTypes>469requires(is_convertible_v<_IndexTypes, size_t> && ...)470explicit extents(_IndexTypes...) -> extents<size_t, size_t(((void)sizeof(_IndexTypes), dynamic_extent))...>;471# endif472473namespace __mdspan_detail {474475// Helper type traits for identifying a class as extents.476template <class _Tp>477struct __is_extents : false_type {};478479template <class _IndexType, size_t... _ExtentsPack>480struct __is_extents<extents<_IndexType, _ExtentsPack...>> : true_type {};481482template <class _Tp>483inline constexpr bool __is_extents_v = __is_extents<_Tp>::value;484485// Function to check whether a set of indices are a multidimensional486// index into extents. This is a word of power in the C++ standard487// requiring that the indices are larger than 0 and smaller than488// the respective extents.489490template <integral _IndexType, class _From>491requires(integral<_From>)492_LIBCPP_HIDE_FROM_ABI constexpr bool __is_index_in_extent(_IndexType __extent, _From __value) {493if constexpr (is_signed_v<_From>) {494if (__value < 0)495return false;496}497using _Tp = common_type_t<_IndexType, _From>;498return static_cast<_Tp>(__value) < static_cast<_Tp>(__extent);499}500501template <integral _IndexType, class _From>502requires(!integral<_From>)503_LIBCPP_HIDE_FROM_ABI constexpr bool __is_index_in_extent(_IndexType __extent, _From __value) {504if constexpr (is_signed_v<_IndexType>) {505if (static_cast<_IndexType>(__value) < 0)506return false;507}508return static_cast<_IndexType>(__value) < __extent;509}510511template <size_t... _Idxs, class _Extents, class... _From>512_LIBCPP_HIDE_FROM_ABI constexpr bool513__is_multidimensional_index_in_impl(index_sequence<_Idxs...>, const _Extents& __ext, _From... __values) {514return (__mdspan_detail::__is_index_in_extent(__ext.extent(_Idxs), __values) && ...);515}516517template <class _Extents, class... _From>518_LIBCPP_HIDE_FROM_ABI constexpr bool __is_multidimensional_index_in(const _Extents& __ext, _From... __values) {519return __mdspan_detail::__is_multidimensional_index_in_impl(520make_index_sequence<_Extents::rank()>(), __ext, __values...);521}522523} // namespace __mdspan_detail524525#endif // _LIBCPP_STD_VER >= 23526527_LIBCPP_END_NAMESPACE_STD528529_LIBCPP_POP_MACROS530531#endif // _LIBCPP___MDSPAN_EXTENTS_H532533534