Home | History | Annotate | Download | only in gsl
      1 ///////////////////////////////////////////////////////////////////////////////
      2 //
      3 // Copyright (c) 2015 Microsoft Corporation. All rights reserved.
      4 //
      5 // This code is licensed under the MIT License (MIT).
      6 //
      7 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      8 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      9 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     10 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     11 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     12 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     13 // THE SOFTWARE.
     14 //
     15 ///////////////////////////////////////////////////////////////////////////////
     16 
     17 #ifndef GSL_MULTI_SPAN_H
     18 #define GSL_MULTI_SPAN_H
     19 
     20 #include <gsl/gsl_assert> // for Expects
     21 #include <gsl/gsl_byte>   // for byte
     22 #include <gsl/gsl_util>   // for narrow_cast
     23 
     24 #include <algorithm> // for transform, lexicographical_compare
     25 #include <array>     // for array
     26 #include <cassert>
     27 #include <cstddef>          // for ptrdiff_t, size_t, nullptr_t
     28 #include <cstdint>          // for PTRDIFF_MAX
     29 #include <functional>       // for divides, multiplies, minus, negate, plus
     30 #include <initializer_list> // for initializer_list
     31 #include <iterator>         // for iterator, random_access_iterator_tag
     32 #include <limits>           // for numeric_limits
     33 #include <new>
     34 #include <numeric>
     35 #include <stdexcept>
     36 #include <string>      // for basic_string
     37 #include <type_traits> // for enable_if_t, remove_cv_t, is_same, is_co...
     38 #include <utility>
     39 
     40 #if defined(_MSC_VER) && !defined(__clang__)
     41 
     42 // turn off some warnings that are noisy about our Expects statements
     43 #pragma warning(push)
     44 #pragma warning(disable : 4127) // conditional expression is constant
     45 #pragma warning(disable : 4702) // unreachable code
     46 
     47 // Turn MSVC /analyze rules that generate too much noise. TODO: fix in the tool.
     48 #pragma warning(disable : 26495) // uninitalized member when constructor calls constructor
     49 #pragma warning(disable : 26473) // in some instantiations we cast to the same type
     50 #pragma warning(disable : 26490) // TODO: bug in parser - attributes and templates
     51 #pragma warning(disable : 26465) // TODO: bug - suppression does not work on template functions
     52 
     53 #if _MSC_VER < 1910
     54 #pragma push_macro("constexpr")
     55 #define constexpr /*constexpr*/
     56 
     57 #endif                          // _MSC_VER < 1910
     58 #endif                          // _MSC_VER
     59 
     60 // GCC 7 does not like the signed unsigned missmatch (size_t ptrdiff_t)
     61 // While there is a conversion from signed to unsigned, it happens at
     62 // compiletime, so the compiler wouldn't have to warn indiscriminently, but
     63 // could check if the source value actually doesn't fit into the target type
     64 // and only warn in those cases.
     65 #if defined(__GNUC__) && __GNUC__ > 6
     66 #pragma GCC diagnostic push
     67 #pragma GCC diagnostic ignored "-Wsign-conversion"
     68 #endif
     69 
     70 namespace gsl
     71 {
     72 
     73 /*
     74 ** begin definitions of index and bounds
     75 */
     76 namespace details
     77 {
     78     template <typename SizeType>
     79     struct SizeTypeTraits
     80     {
     81         static const SizeType max_value = std::numeric_limits<SizeType>::max();
     82     };
     83 
     84     template <typename... Ts>
     85     class are_integral : public std::integral_constant<bool, true>
     86     {
     87     };
     88 
     89     template <typename T, typename... Ts>
     90     class are_integral<T, Ts...>
     91         : public std::integral_constant<bool,
     92                                         std::is_integral<T>::value && are_integral<Ts...>::value>
     93     {
     94     };
     95 } // namespace details
     96 
     97 template <std::size_t Rank>
     98 class multi_span_index final
     99 {
    100     static_assert(Rank > 0, "Rank must be greater than 0!");
    101 
    102     template <std::size_t OtherRank>
    103     friend class multi_span_index;
    104 
    105 public:
    106     static const std::size_t rank = Rank;
    107     using value_type = std::ptrdiff_t;
    108     using size_type = value_type;
    109     using reference = std::add_lvalue_reference_t<value_type>;
    110     using const_reference = std::add_lvalue_reference_t<std::add_const_t<value_type>>;
    111 
    112     constexpr multi_span_index() noexcept {}
    113 
    114     constexpr multi_span_index(const value_type (&values)[Rank]) noexcept
    115     {
    116         GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute
    117         GSL_SUPPRESS(bounds.3) // NO-FORMAT: attribute
    118         std::copy(values, values + Rank, elems);
    119     }
    120 
    121     template <typename... Ts, typename = std::enable_if_t<(sizeof...(Ts) == Rank) &&
    122                                                           details::are_integral<Ts...>::value>>
    123     constexpr multi_span_index(Ts... ds) noexcept : elems{narrow_cast<value_type>(ds)...}
    124     {}
    125 
    126     constexpr multi_span_index(const multi_span_index& other) noexcept = default;
    127 
    128     constexpr multi_span_index& operator=(const multi_span_index& rhs) noexcept = default;
    129 
    130     // Preconditions: component_idx < rank
    131     GSL_SUPPRESS(bounds.2) // NO-FORMAT: attribute
    132     GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute
    133     constexpr reference operator[](std::size_t component_idx)
    134     {
    135         Expects(component_idx < Rank); // Component index must be less than rank
    136         return elems[component_idx];
    137     }
    138 
    139     // Preconditions: component_idx < rank
    140     GSL_SUPPRESS(bounds.2) // NO-FORMAT: attribute
    141     GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute
    142     constexpr const_reference operator[](std::size_t component_idx) const 
    143     {
    144         Expects(component_idx < Rank); // Component index must be less than rank
    145         return elems[component_idx];
    146     }
    147 
    148     GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute
    149     GSL_SUPPRESS(bounds.3) // NO-FORMAT: attribute
    150     constexpr bool operator==(const multi_span_index& rhs) const
    151     {
    152         return std::equal(elems, elems + rank, rhs.elems);
    153     }
    154 
    155     constexpr bool operator!=(const multi_span_index& rhs) const
    156     {
    157         return !(*this == rhs);
    158     }
    159 
    160     constexpr multi_span_index operator+() const noexcept { return *this; }
    161 
    162     constexpr multi_span_index operator-() const
    163     {
    164         multi_span_index ret = *this;
    165         std::transform(ret, ret + rank, ret, std::negate<value_type>{});
    166         return ret;
    167     }
    168 
    169     constexpr multi_span_index operator+(const multi_span_index& rhs) const
    170     {
    171         multi_span_index ret = *this;
    172         ret += rhs;
    173         return ret;
    174     }
    175 
    176     constexpr multi_span_index operator-(const multi_span_index& rhs) const
    177     {
    178         multi_span_index ret = *this;
    179         ret -= rhs;
    180         return ret;
    181     }
    182 
    183     constexpr multi_span_index& operator+=(const multi_span_index& rhs)
    184     {
    185         GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute
    186         GSL_SUPPRESS(bounds.3) // NO-FORMAT: attribute
    187         std::transform(elems, elems + rank, rhs.elems, elems,
    188                        std::plus<value_type>{});
    189         return *this;
    190     }
    191 
    192     constexpr multi_span_index& operator-=(const multi_span_index& rhs)
    193     {
    194         GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute
    195         GSL_SUPPRESS(bounds.3) // NO-FORMAT: attribute
    196         std::transform(elems, elems + rank, rhs.elems, elems, std::minus<value_type>{});
    197         return *this;
    198     }
    199 
    200     constexpr multi_span_index operator*(value_type v) const
    201     {
    202         multi_span_index ret = *this;
    203         ret *= v;
    204         return ret;
    205     }
    206 
    207     constexpr multi_span_index operator/(value_type v) const
    208     {
    209         multi_span_index ret = *this;
    210         ret /= v;
    211         return ret;
    212     }
    213 
    214     friend constexpr multi_span_index operator*(value_type v, const multi_span_index& rhs)
    215     {
    216         return rhs * v;
    217     }
    218 
    219     constexpr multi_span_index& operator*=(value_type v)
    220     {
    221         GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute
    222         GSL_SUPPRESS(bounds.3) // NO-FORMAT: attribute
    223         std::transform(elems, elems + rank, elems,
    224                        [v](value_type x) { return std::multiplies<value_type>{}(x, v); });
    225         return *this;
    226     }
    227 
    228     constexpr multi_span_index& operator/=(value_type v)
    229     {
    230         GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute
    231         GSL_SUPPRESS(bounds.3) // NO-FORMAT: attribute
    232         std::transform(elems, elems + rank, elems,
    233                        [v](value_type x) { return std::divides<value_type>{}(x, v); });
    234         return *this;
    235     }
    236 
    237 private:
    238     value_type elems[Rank] = {};
    239 };
    240 
    241 #if !defined(_MSC_VER) || _MSC_VER >= 1910
    242 
    243 struct static_bounds_dynamic_range_t
    244 {
    245     template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
    246     constexpr operator T() const noexcept
    247     {
    248         return narrow_cast<T>(-1);
    249     }
    250 };
    251 
    252 constexpr bool operator==(static_bounds_dynamic_range_t, static_bounds_dynamic_range_t) noexcept
    253 {
    254     return true;
    255 }
    256 
    257 constexpr bool operator!=(static_bounds_dynamic_range_t, static_bounds_dynamic_range_t) noexcept
    258 {
    259     return false;
    260 }
    261 
    262 template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
    263 constexpr bool operator==(static_bounds_dynamic_range_t, T other) noexcept
    264 {
    265     return narrow_cast<T>(-1) == other;
    266 }
    267 
    268 template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
    269 constexpr bool operator==(T left, static_bounds_dynamic_range_t right) noexcept
    270 {
    271     return right == left;
    272 }
    273 
    274 template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
    275 constexpr bool operator!=(static_bounds_dynamic_range_t, T other) noexcept
    276 {
    277     return narrow_cast<T>(-1) != other;
    278 }
    279 
    280 template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
    281 constexpr bool operator!=(T left, static_bounds_dynamic_range_t right) noexcept
    282 {
    283     return right != left;
    284 }
    285 
    286 constexpr static_bounds_dynamic_range_t dynamic_range{};
    287 #else
    288 const std::ptrdiff_t dynamic_range = -1;
    289 #endif
    290 
    291 struct generalized_mapping_tag
    292 {
    293 };
    294 struct contiguous_mapping_tag : generalized_mapping_tag
    295 {
    296 };
    297 
    298 namespace details
    299 {
    300 
    301     template <std::ptrdiff_t Left, std::ptrdiff_t Right>
    302     struct LessThan
    303     {
    304         static const bool value = Left < Right;
    305     };
    306 
    307     template <std::ptrdiff_t... Ranges>
    308     struct BoundsRanges
    309     {
    310         using size_type = std::ptrdiff_t;
    311         static const size_type Depth = 0;
    312         static const size_type DynamicNum = 0;
    313         static const size_type CurrentRange = 1;
    314         static const size_type TotalSize = 1;
    315 
    316         // TODO : following signature is for work around VS bug
    317         template <typename OtherRange>
    318         constexpr BoundsRanges(const OtherRange&, bool /* firstLevel */)
    319         {}
    320 
    321         constexpr BoundsRanges(const std::ptrdiff_t* const) {}
    322         constexpr BoundsRanges() noexcept = default;
    323 
    324         template <typename T, std::size_t Dim>
    325         constexpr void serialize(T&) const
    326         {}
    327 
    328         template <typename T, std::size_t Dim>
    329         constexpr size_type linearize(const T&) const
    330         {
    331             return 0;
    332         }
    333 
    334         template <typename T, std::size_t Dim>
    335         constexpr size_type contains(const T&) const
    336         {
    337             return -1;
    338         }
    339 
    340         constexpr size_type elementNum(std::size_t) const noexcept { return 0; }
    341 
    342         constexpr size_type totalSize() const noexcept { return TotalSize; }
    343 
    344         constexpr bool operator==(const BoundsRanges&) const noexcept { return true; }
    345     };
    346 
    347     template <std::ptrdiff_t... RestRanges>
    348     struct BoundsRanges<dynamic_range, RestRanges...> : BoundsRanges<RestRanges...>
    349     {
    350         using Base = BoundsRanges<RestRanges...>;
    351         using size_type = std::ptrdiff_t;
    352         static const std::size_t Depth = Base::Depth + 1;
    353         static const std::size_t DynamicNum = Base::DynamicNum + 1;
    354         static const size_type CurrentRange = dynamic_range;
    355         static const size_type TotalSize = dynamic_range;
    356 
    357     private:
    358         size_type m_bound;
    359 
    360     public:
    361         GSL_SUPPRESS(f.23) // NO-FORMAT: attribute // this pointer type is cannot be assigned nullptr - issue in not_null
    362         GSL_SUPPRESS(bounds.1)  // NO-FORMAT: attribute
    363         constexpr BoundsRanges(const std::ptrdiff_t* const arr)
    364             : Base(arr + 1), m_bound(*arr * this->Base::totalSize())
    365         {
    366             Expects(0 <= *arr);
    367         }
    368 
    369         constexpr BoundsRanges() noexcept : m_bound(0) {}
    370 
    371         template <std::ptrdiff_t OtherRange, std::ptrdiff_t... RestOtherRanges>
    372         constexpr BoundsRanges(const BoundsRanges<OtherRange, RestOtherRanges...>& other,
    373                      bool /* firstLevel */ = true)
    374             : Base(static_cast<const BoundsRanges<RestOtherRanges...>&>(other), false)
    375             , m_bound(other.totalSize())
    376         {}
    377 
    378         template <typename T, std::size_t Dim = 0>
    379         constexpr void serialize(T& arr) const
    380         {
    381             arr[Dim] = elementNum();
    382             this->Base::template serialize<T, Dim + 1>(arr);
    383         }
    384 
    385         template <typename T, std::size_t Dim = 0>
    386         GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute
    387         constexpr size_type linearize(const T& arr) const
    388         {
    389             const size_type index = this->Base::totalSize() * arr[Dim];
    390             Expects(index < m_bound);
    391             return index + this->Base::template linearize<T, Dim + 1>(arr);
    392         }
    393 
    394         template <typename T, std::size_t Dim = 0>
    395         constexpr size_type contains(const T& arr) const
    396         {
    397             const ptrdiff_t last = this->Base::template contains<T, Dim + 1>(arr);
    398             if (last == -1) return -1;
    399             const ptrdiff_t cur = this->Base::totalSize() * arr[Dim];
    400             return cur < m_bound ? cur + last : -1;
    401         }
    402 
    403         GSL_SUPPRESS(c.128) // NO-FORMAT: attribute // no pointers to BoundsRanges should be ever used
    404         constexpr size_type totalSize() const noexcept
    405         {
    406             return m_bound;
    407         }
    408 
    409         GSL_SUPPRESS(c.128) // NO-FORMAT: attribute // no pointers to BoundsRanges should be ever used
    410         constexpr size_type elementNum() const noexcept
    411         {
    412             return totalSize() / this->Base::totalSize();
    413         }
    414 
    415         GSL_SUPPRESS(c.128) // NO-FORMAT: attribute // no pointers to BoundsRanges should be ever used
    416         constexpr size_type elementNum(std::size_t dim) const noexcept
    417         {
    418             if (dim > 0)
    419                 return this->Base::elementNum(dim - 1);
    420             else
    421                 return elementNum();
    422         }
    423 
    424         constexpr bool operator==(const BoundsRanges& rhs) const noexcept
    425         {
    426             return m_bound == rhs.m_bound &&
    427                    static_cast<const Base&>(*this) == static_cast<const Base&>(rhs);
    428         }
    429     };
    430 
    431     template <std::ptrdiff_t CurRange, std::ptrdiff_t... RestRanges>
    432     struct BoundsRanges<CurRange, RestRanges...> : BoundsRanges<RestRanges...>
    433     {
    434         using Base = BoundsRanges<RestRanges...>;
    435         using size_type = std::ptrdiff_t;
    436         static const std::size_t Depth = Base::Depth + 1;
    437         static const std::size_t DynamicNum = Base::DynamicNum;
    438         static const size_type CurrentRange = CurRange;
    439         static const size_type TotalSize =
    440             Base::TotalSize == dynamic_range ? dynamic_range : CurrentRange * Base::TotalSize;
    441 
    442         constexpr BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr) {}
    443         constexpr BoundsRanges() = default;
    444 
    445         template <std::ptrdiff_t OtherRange, std::ptrdiff_t... RestOtherRanges>
    446         constexpr BoundsRanges(const BoundsRanges<OtherRange, RestOtherRanges...>& other,
    447                      bool firstLevel = true)
    448             : Base(static_cast<const BoundsRanges<RestOtherRanges...>&>(other), false)
    449         {
    450             GSL_SUPPRESS(type.4) // NO-FORMAT: attribute // TODO: false positive
    451             (void) firstLevel;
    452         }
    453 
    454         template <typename T, std::size_t Dim = 0>
    455         constexpr void serialize(T& arr) const
    456         {
    457             arr[Dim] = elementNum();
    458             this->Base::template serialize<T, Dim + 1>(arr);
    459         }
    460 
    461         template <typename T, std::size_t Dim = 0>
    462         constexpr size_type linearize(const T& arr) const
    463         {
    464             GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute
    465             Expects(arr[Dim] >= 0 && arr[Dim] < CurrentRange); // Index is out of range
    466             GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute
    467             const ptrdiff_t d = arr[Dim];
    468             return this->Base::totalSize() * d +
    469                    this->Base::template linearize<T, Dim + 1>(arr);
    470         }
    471 
    472         template <typename T, std::size_t Dim = 0>
    473         constexpr size_type contains(const T& arr) const
    474         {
    475             if (arr[Dim] >= CurrentRange) return -1;
    476             const size_type last = this->Base::template contains<T, Dim + 1>(arr);
    477             if (last == -1) return -1;
    478             return this->Base::totalSize() * arr[Dim] + last;
    479         }
    480 
    481         GSL_SUPPRESS(c.128) // NO-FORMAT: attribute // no pointers to BoundsRanges should be ever used
    482         constexpr size_type totalSize() const noexcept
    483         {
    484             return CurrentRange * this->Base::totalSize();
    485         }
    486 
    487         GSL_SUPPRESS(c.128) // NO-FORMAT: attribute // no pointers to BoundsRanges should be ever used
    488         constexpr size_type elementNum() const noexcept
    489         {
    490             return CurrentRange;
    491         }
    492 
    493         GSL_SUPPRESS(c.128) // NO-FORMAT: attribute // no pointers to BoundsRanges should be ever used
    494         constexpr size_type elementNum(std::size_t dim) const noexcept
    495         {
    496             if (dim > 0)
    497                 return this->Base::elementNum(dim - 1);
    498             else
    499                 return elementNum();
    500         }
    501 
    502         constexpr bool operator==(const BoundsRanges& rhs) const noexcept
    503         {
    504             return static_cast<const Base&>(*this) == static_cast<const Base&>(rhs);
    505         }
    506     };
    507 
    508     template <typename SourceType, typename TargetType>
    509     struct BoundsRangeConvertible
    510         : public std::integral_constant<bool, (SourceType::TotalSize >= TargetType::TotalSize ||
    511                                                TargetType::TotalSize == dynamic_range ||
    512                                                SourceType::TotalSize == dynamic_range ||
    513                                                TargetType::TotalSize == 0)>
    514     {
    515     };
    516 
    517     template <typename TypeChain>
    518     struct TypeListIndexer
    519     {
    520         const TypeChain& obj_;
    521         TypeListIndexer(const TypeChain& obj) : obj_(obj) {}
    522 
    523         template <std::size_t N>
    524         const TypeChain& getObj(std::true_type)
    525         {
    526             return obj_;
    527         }
    528 
    529         template <std::size_t N, typename MyChain = TypeChain,
    530                   typename MyBase = typename MyChain::Base>
    531         auto getObj(std::false_type)
    532             -> decltype(TypeListIndexer<MyBase>(static_cast<const MyBase&>(obj_)).template get<N>())
    533         {
    534             return TypeListIndexer<MyBase>(static_cast<const MyBase&>(obj_)).template get<N>();
    535         }
    536 
    537         template <std::size_t N>
    538         auto get() -> decltype(getObj<N - 1>(std::integral_constant<bool, N == 0>()))
    539         {
    540             return getObj<N - 1>(std::integral_constant<bool, N == 0>());
    541         }
    542     };
    543 
    544     template <typename TypeChain>
    545     TypeListIndexer<TypeChain> createTypeListIndexer(const TypeChain& obj)
    546     {
    547         return TypeListIndexer<TypeChain>(obj);
    548     }
    549 
    550     template <std::size_t Rank, bool Enabled = (Rank > 1),
    551               typename Ret = std::enable_if_t<Enabled, multi_span_index<Rank - 1>>>
    552     constexpr Ret shift_left(const multi_span_index<Rank>& other) noexcept
    553     {
    554         Ret ret{};
    555         for (std::size_t i = 0; i < Rank - 1; ++i)
    556         {
    557             GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute
    558                 ret[i] = other[i + 1];
    559         }
    560         return ret;
    561     }
    562 } // namespace details
    563 
    564 template <typename IndexType>
    565 class bounds_iterator;
    566 
    567 template <std::ptrdiff_t... Ranges>
    568 class static_bounds
    569 {
    570 public:
    571     static_bounds(const details::BoundsRanges<Ranges...>&) {}
    572 };
    573 
    574 template <std::ptrdiff_t FirstRange, std::ptrdiff_t... RestRanges>
    575 class static_bounds<FirstRange, RestRanges...>
    576 {
    577     using MyRanges = details::BoundsRanges<FirstRange, RestRanges...>;
    578 
    579     MyRanges m_ranges;
    580     constexpr static_bounds(const MyRanges& range) noexcept : m_ranges(range) {}
    581 
    582     template <std::ptrdiff_t... OtherRanges>
    583     friend class static_bounds;
    584 
    585 public:
    586     static const std::size_t rank = MyRanges::Depth;
    587     static const std::size_t dynamic_rank = MyRanges::DynamicNum;
    588     static const std::ptrdiff_t static_size = MyRanges::TotalSize;
    589 
    590     using size_type = std::ptrdiff_t;
    591     using index_type = multi_span_index<rank>;
    592     using const_index_type = std::add_const_t<index_type>;
    593     using iterator = bounds_iterator<const_index_type>;
    594     using const_iterator = bounds_iterator<const_index_type>;
    595     using difference_type = std::ptrdiff_t;
    596     using sliced_type = static_bounds<RestRanges...>;
    597     using mapping_type = contiguous_mapping_tag;
    598 
    599     constexpr static_bounds() /*noexcept*/ = default;
    600 
    601     template <typename SourceType, typename TargetType, std::size_t Rank>
    602     struct BoundsRangeConvertible2;
    603 
    604     template <std::size_t Rank, typename SourceType, typename TargetType,
    605               typename Ret = BoundsRangeConvertible2<typename SourceType::Base,
    606                                                      typename TargetType::Base, Rank>>
    607     static auto helpBoundsRangeConvertible(SourceType, TargetType, std::true_type) -> Ret;
    608 
    609     template <std::size_t Rank, typename SourceType, typename TargetType>
    610     static auto helpBoundsRangeConvertible(SourceType, TargetType, ...) -> std::false_type;
    611 
    612     template <typename SourceType, typename TargetType, std::size_t Rank>
    613     struct BoundsRangeConvertible2
    614         : decltype(helpBoundsRangeConvertible<Rank - 1>(
    615               SourceType(), TargetType(),
    616               std::integral_constant<bool,
    617                                      SourceType::Depth == TargetType::Depth &&
    618                                          (SourceType::CurrentRange == TargetType::CurrentRange ||
    619                                           TargetType::CurrentRange == dynamic_range ||
    620                                           SourceType::CurrentRange == dynamic_range)>()))
    621     {
    622     };
    623 
    624     template <typename SourceType, typename TargetType>
    625     struct BoundsRangeConvertible2<SourceType, TargetType, 0> : std::true_type
    626     {
    627     };
    628 
    629     template <typename SourceType, typename TargetType, std::ptrdiff_t Rank = TargetType::Depth>
    630     struct BoundsRangeConvertible
    631         : decltype(helpBoundsRangeConvertible<Rank - 1>(
    632               SourceType(), TargetType(),
    633               std::integral_constant<bool,
    634                                      SourceType::Depth == TargetType::Depth &&
    635                                          (!details::LessThan<SourceType::CurrentRange,
    636                                                              TargetType::CurrentRange>::value ||
    637                                           TargetType::CurrentRange == dynamic_range ||
    638                                           SourceType::CurrentRange == dynamic_range)>()))
    639     {
    640     };
    641 
    642     template <typename SourceType, typename TargetType>
    643     struct BoundsRangeConvertible<SourceType, TargetType, 0> : std::true_type
    644     {
    645     };
    646 
    647     template <std::ptrdiff_t... Ranges,
    648               typename = std::enable_if_t<details::BoundsRangeConvertible<
    649                   details::BoundsRanges<Ranges...>,
    650                   details::BoundsRanges<FirstRange, RestRanges...>>::value>>
    651     constexpr static_bounds(const static_bounds<Ranges...>& other) : m_ranges(other.m_ranges)
    652     {
    653         Expects((MyRanges::DynamicNum == 0 && details::BoundsRanges<Ranges...>::DynamicNum == 0) ||
    654                 MyRanges::DynamicNum > 0 || other.m_ranges.totalSize() >= m_ranges.totalSize());
    655     }
    656 
    657     constexpr static_bounds(std::initializer_list<size_type> il) : m_ranges(il.begin())
    658     {
    659         // Size of the initializer list must match the rank of the array
    660         Expects((MyRanges::DynamicNum == 0 && il.size() == 1 && *il.begin() == static_size) ||
    661                 MyRanges::DynamicNum == il.size());
    662         // Size of the range must be less than the max element of the size type
    663         Expects(m_ranges.totalSize() <= PTRDIFF_MAX);
    664     }
    665 
    666     constexpr sliced_type slice() const noexcept
    667     {
    668         return sliced_type{static_cast<const details::BoundsRanges<RestRanges...>&>(m_ranges)};
    669     }
    670 
    671     constexpr size_type stride() const noexcept { return rank > 1 ? slice().size() : 1; }
    672 
    673     constexpr size_type size() const noexcept { return m_ranges.totalSize(); }
    674 
    675     constexpr size_type total_size() const noexcept { return m_ranges.totalSize(); }
    676 
    677     constexpr size_type linearize(const index_type& idx) const { return m_ranges.linearize(idx); }
    678 
    679     constexpr bool contains(const index_type& idx) const noexcept
    680     {
    681         return m_ranges.contains(idx) != -1;
    682     }
    683 
    684     constexpr size_type operator[](std::size_t idx) const noexcept
    685     {
    686         return m_ranges.elementNum(idx);
    687     }
    688 
    689     template <std::size_t Dim = 0>
    690     constexpr size_type extent() const noexcept
    691     {
    692         static_assert(Dim < rank,
    693                       "dimension should be less than rank (dimension count starts from 0)");
    694         return details::createTypeListIndexer(m_ranges).template get<Dim>().elementNum();
    695     }
    696 
    697     template <typename IntType>
    698     constexpr size_type extent(IntType dim) const
    699     {
    700         static_assert(std::is_integral<IntType>::value,
    701                       "Dimension parameter must be supplied as an integral type.");
    702         auto real_dim = narrow_cast<std::size_t>(dim);
    703         Expects(real_dim < rank);
    704 
    705         return m_ranges.elementNum(real_dim);
    706     }
    707 
    708     constexpr index_type index_bounds() const noexcept
    709     {
    710         size_type extents[rank] = {};
    711         m_ranges.serialize(extents);
    712         return {extents};
    713     }
    714 
    715     template <std::ptrdiff_t... Ranges>
    716     constexpr bool operator==(const static_bounds<Ranges...>& rhs) const noexcept
    717     {
    718         return this->size() == rhs.size();
    719     }
    720 
    721     template <std::ptrdiff_t... Ranges>
    722     constexpr bool operator!=(const static_bounds<Ranges...>& rhs) const noexcept
    723     {
    724         return !(*this == rhs);
    725     }
    726 
    727     constexpr const_iterator begin() const noexcept
    728     {
    729         return const_iterator(*this, index_type{});
    730     }
    731 
    732     constexpr const_iterator end() const noexcept
    733     {
    734         return const_iterator(*this, this->index_bounds());
    735     }
    736 };
    737 
    738 template <std::size_t Rank>
    739 class strided_bounds {
    740     template <std::size_t OtherRank>
    741     friend class strided_bounds;
    742 
    743 public:
    744     static const std::size_t rank = Rank;
    745     using value_type = std::ptrdiff_t;
    746     using reference = std::add_lvalue_reference_t<value_type>;
    747     using const_reference = std::add_const_t<reference>;
    748     using size_type = value_type;
    749     using difference_type = value_type;
    750     using index_type = multi_span_index<rank>;
    751     using const_index_type = std::add_const_t<index_type>;
    752     using iterator = bounds_iterator<const_index_type>;
    753     using const_iterator = bounds_iterator<const_index_type>;
    754     static const value_type dynamic_rank = rank;
    755     static const value_type static_size = dynamic_range;
    756     using sliced_type = std::conditional_t<rank != 0, strided_bounds<rank - 1>, void>;
    757     using mapping_type = generalized_mapping_tag;
    758 
    759     constexpr strided_bounds(const strided_bounds&) noexcept = default;
    760 
    761     constexpr strided_bounds& operator=(const strided_bounds&) noexcept = default;
    762 
    763     constexpr strided_bounds(const value_type (&values)[rank], index_type strides)
    764         : m_extents(values), m_strides(std::move(strides))
    765     {}
    766 
    767     constexpr strided_bounds(const index_type& extents, const index_type& strides) noexcept
    768         : m_extents(extents), m_strides(strides)
    769     {
    770     }
    771 
    772     constexpr index_type strides() const noexcept { return m_strides; }
    773 
    774     GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute
    775     constexpr size_type total_size() const noexcept
    776     {
    777         size_type ret = 0;
    778         for (std::size_t i = 0; i < rank; ++i) { ret += (m_extents[i] - 1) * m_strides[i]; }
    779         return ret + 1;
    780     }
    781 
    782     GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute
    783     constexpr size_type size() const noexcept
    784     {
    785         size_type ret = 1;
    786         for (std::size_t i = 0; i < rank; ++i) { ret *= m_extents[i]; }
    787         return ret;
    788     }
    789 
    790     constexpr bool contains(const index_type& idx) const noexcept
    791     {
    792         for (std::size_t i = 0; i < rank; ++i)
    793         {
    794             if (idx[i] < 0 || idx[i] >= m_extents[i]) return false;
    795         }
    796         return true;
    797     }
    798 
    799     GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute
    800     constexpr size_type linearize(const index_type& idx) const
    801     {
    802         size_type ret = 0;
    803         for (std::size_t i = 0; i < rank; i++)
    804         {
    805             Expects(idx[i] < m_extents[i]); // index is out of bounds of the array
    806             ret += idx[i] * m_strides[i];
    807         }
    808         return ret;
    809     }
    810 
    811     GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute
    812     constexpr size_type stride() const noexcept { return m_strides[0]; }
    813 
    814     template <bool Enabled = (rank > 1), typename Ret = std::enable_if_t<Enabled, sliced_type>>
    815     constexpr sliced_type slice() const
    816     {
    817         return {details::shift_left(m_extents), details::shift_left(m_strides)};
    818     }
    819 
    820     template <std::size_t Dim = 0>
    821 
    822     GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute
    823     constexpr size_type extent() const noexcept
    824     {
    825         static_assert(Dim < Rank,
    826                       "dimension should be less than rank (dimension count starts from 0)");
    827         return m_extents[Dim];
    828     }
    829 
    830     constexpr index_type index_bounds() const noexcept { return m_extents; }
    831 
    832     constexpr const_iterator begin() const noexcept { return const_iterator{*this, index_type{}}; }
    833 
    834     constexpr const_iterator end() const noexcept { return const_iterator{*this, index_bounds()}; }
    835 
    836 private:
    837     index_type m_extents;
    838     index_type m_strides;
    839 };
    840 
    841 template <typename T>
    842 struct is_bounds : std::integral_constant<bool, false>
    843 {
    844 };
    845 template <std::ptrdiff_t... Ranges>
    846 struct is_bounds<static_bounds<Ranges...>> : std::integral_constant<bool, true>
    847 {
    848 };
    849 template <std::size_t Rank>
    850 struct is_bounds<strided_bounds<Rank>> : std::integral_constant<bool, true>
    851 {
    852 };
    853 
    854 template <typename IndexType>
    855 class bounds_iterator
    856 {
    857 public:
    858     static const std::size_t rank = IndexType::rank;
    859     using iterator_category = std::random_access_iterator_tag;
    860     using value_type = IndexType;
    861     using difference_type = std::ptrdiff_t;
    862     using pointer = value_type*;
    863     using reference = value_type&;
    864     using index_type = value_type;
    865     using index_size_type = typename IndexType::value_type;
    866     template <typename Bounds>
    867     explicit bounds_iterator(const Bounds& bnd, value_type curr) noexcept
    868         : boundary_(bnd.index_bounds()), curr_(std::move(curr))
    869     {
    870         static_assert(is_bounds<Bounds>::value, "Bounds type must be provided");
    871     }
    872 
    873     constexpr reference operator*() const noexcept { return curr_; }
    874 
    875     constexpr pointer operator->() const noexcept { return &curr_; }
    876 
    877     GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute
    878     GSL_SUPPRESS(bounds.2) // NO-FORMAT: attribute
    879     constexpr bounds_iterator& operator++() noexcept
    880 
    881     {
    882         for (std::size_t i = rank; i-- > 0;)
    883         {
    884             if (curr_[i] < boundary_[i] - 1)
    885             {
    886                 curr_[i]++;
    887                 return *this;
    888             }
    889             curr_[i] = 0;
    890         }
    891         // If we're here we've wrapped over - set to past-the-end.
    892         curr_ = boundary_;
    893         return *this;
    894     }
    895 
    896     constexpr bounds_iterator operator++(int) noexcept
    897     {
    898         auto ret = *this;
    899         ++(*this);
    900         return ret;
    901     }
    902 
    903     GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute
    904     constexpr bounds_iterator& operator--()
    905     {
    906         if (!less(curr_, boundary_))
    907         {
    908             // if at the past-the-end, set to last element
    909             for (std::size_t i = 0; i < rank; ++i) { curr_[i] = boundary_[i] - 1; }
    910             return *this;
    911         }
    912         for (std::size_t i = rank; i-- > 0;)
    913         {
    914             if (curr_[i] >= 1)
    915             {
    916                 curr_[i]--;
    917                 return *this;
    918             }
    919             curr_[i] = boundary_[i] - 1;
    920         }
    921         // If we're here the preconditions were violated
    922         // "pre: there exists s such that r == ++s"
    923         Expects(false);
    924         return *this;
    925     }
    926 
    927     constexpr bounds_iterator operator--(int) noexcept
    928     {
    929         auto ret = *this;
    930         --(*this);
    931         return ret;
    932     }
    933 
    934     constexpr bounds_iterator operator+(difference_type n) const noexcept
    935     {
    936         bounds_iterator ret{*this};
    937         return ret += n;
    938     }
    939 
    940     GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute
    941     constexpr bounds_iterator& operator+=(difference_type n)
    942     {
    943         auto linear_idx = linearize(curr_) + n;
    944         std::remove_const_t<value_type> stride = 0;
    945         stride[rank - 1] = 1;
    946         for (std::size_t i = rank - 1; i-- > 0;) { stride[i] = stride[i + 1] * boundary_[i + 1]; }
    947         for (std::size_t i = 0; i < rank; ++i)
    948         {
    949             curr_[i] = linear_idx / stride[i];
    950             linear_idx = linear_idx % stride[i];
    951         }
    952         // index is out of bounds of the array
    953         Expects(!less(curr_, index_type{}) && !less(boundary_, curr_));
    954         return *this;
    955     }
    956 
    957     constexpr bounds_iterator operator-(difference_type n) const noexcept
    958     {
    959         bounds_iterator ret{*this};
    960         return ret -= n;
    961     }
    962 
    963     constexpr bounds_iterator& operator-=(difference_type n) noexcept { return *this += -n; }
    964 
    965     constexpr difference_type operator-(const bounds_iterator& rhs) const noexcept
    966     {
    967         return linearize(curr_) - linearize(rhs.curr_);
    968     }
    969 
    970     constexpr value_type operator[](difference_type n) const noexcept { return *(*this + n); }
    971 
    972     constexpr bool operator==(const bounds_iterator& rhs) const noexcept
    973     {
    974         return curr_ == rhs.curr_;
    975     }
    976 
    977 
    978     constexpr bool operator!=(const bounds_iterator& rhs) const noexcept { return !(*this == rhs); }
    979 
    980     constexpr bool operator<(const bounds_iterator& rhs) const noexcept
    981     {
    982         return less(curr_, rhs.curr_);
    983     }
    984 
    985     constexpr bool operator<=(const bounds_iterator& rhs) const noexcept { return !(rhs < *this); }
    986 
    987     constexpr bool operator>(const bounds_iterator& rhs) const noexcept { return rhs < *this; }
    988 
    989     constexpr bool operator>=(const bounds_iterator& rhs) const noexcept { return !(rhs > *this); }
    990 
    991     void swap(bounds_iterator& rhs) noexcept
    992     {
    993         std::swap(boundary_, rhs.boundary_);
    994         std::swap(curr_, rhs.curr_);
    995     }
    996 
    997 private:
    998 
    999     GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute
   1000     constexpr bool less(index_type& one, index_type& other) const noexcept
   1001     {
   1002         for (std::size_t i = 0; i < rank; ++i)
   1003         {
   1004             if (one[i] < other[i]) return true;
   1005         }
   1006         return false;
   1007     }
   1008 
   1009     GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute
   1010     constexpr index_size_type linearize(const value_type& idx) const noexcept
   1011     {
   1012         // TODO: Smarter impl.
   1013         // Check if past-the-end
   1014         index_size_type multiplier = 1;
   1015         index_size_type res = 0;
   1016         if (!less(idx, boundary_))
   1017         {
   1018             res = 1;
   1019             for (std::size_t i = rank; i-- > 0;)
   1020             {
   1021                 res += (idx[i] - 1) * multiplier;
   1022                 multiplier *= boundary_[i];
   1023             }
   1024         }
   1025         else
   1026         {
   1027             for (std::size_t i = rank; i-- > 0;)
   1028             {
   1029                 res += idx[i] * multiplier;
   1030                 multiplier *= boundary_[i];
   1031             }
   1032         }
   1033         return res;
   1034     }
   1035 
   1036     value_type boundary_;
   1037     std::remove_const_t<value_type> curr_;
   1038 };
   1039 
   1040 template <typename IndexType>
   1041 bounds_iterator<IndexType> operator+(typename bounds_iterator<IndexType>::difference_type n,
   1042                                      const bounds_iterator<IndexType>& rhs) noexcept
   1043 {
   1044     return rhs + n;
   1045 }
   1046 
   1047 namespace details
   1048 {
   1049     template <typename Bounds>
   1050     constexpr std::enable_if_t<
   1051         std::is_same<typename Bounds::mapping_type, generalized_mapping_tag>::value,
   1052         typename Bounds::index_type>
   1053     make_stride(const Bounds& bnd) noexcept
   1054     {
   1055         return bnd.strides();
   1056     }
   1057 
   1058     // Make a stride vector from bounds, assuming contiguous memory.
   1059     template <typename Bounds>
   1060     constexpr std::enable_if_t<
   1061         std::is_same<typename Bounds::mapping_type, contiguous_mapping_tag>::value,
   1062         typename Bounds::index_type>
   1063     make_stride(const Bounds& bnd) noexcept
   1064     {
   1065         auto extents = bnd.index_bounds();
   1066         typename Bounds::size_type stride[Bounds::rank] = {};
   1067 
   1068         stride[Bounds::rank - 1] = 1;
   1069         for (std::size_t i = 1; i < Bounds::rank; ++i)
   1070         {
   1071             GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute
   1072             GSL_SUPPRESS(bounds.2) // NO-FORMAT: attribute
   1073             stride[Bounds::rank - i - 1] = stride[Bounds::rank - i] * extents[Bounds::rank - i];
   1074         }
   1075         return {stride};
   1076     }
   1077 
   1078     template <typename BoundsSrc, typename BoundsDest>
   1079     void verifyBoundsReshape(const BoundsSrc& src, const BoundsDest& dest)
   1080     {
   1081         static_assert(is_bounds<BoundsSrc>::value && is_bounds<BoundsDest>::value,
   1082                       "The src type and dest type must be bounds");
   1083         static_assert(std::is_same<typename BoundsSrc::mapping_type, contiguous_mapping_tag>::value,
   1084                       "The source type must be a contiguous bounds");
   1085         static_assert(BoundsDest::static_size == dynamic_range ||
   1086                           BoundsSrc::static_size == dynamic_range ||
   1087                           BoundsDest::static_size == BoundsSrc::static_size,
   1088                       "The source bounds must have same size as dest bounds");
   1089         Expects(src.size() == dest.size());
   1090     }
   1091 
   1092 } // namespace details
   1093 
   1094 template <typename Span>
   1095 class contiguous_span_iterator;
   1096 template <typename Span>
   1097 class general_span_iterator;
   1098 
   1099 template <std::ptrdiff_t DimSize = dynamic_range>
   1100 struct dim_t
   1101 {
   1102     static const std::ptrdiff_t value = DimSize;
   1103 };
   1104 template <>
   1105 struct dim_t<dynamic_range>
   1106 {
   1107     static const std::ptrdiff_t value = dynamic_range;
   1108     const std::ptrdiff_t dvalue;
   1109     constexpr dim_t(std::ptrdiff_t size) noexcept : dvalue(size) {}
   1110 };
   1111 
   1112 template <std::ptrdiff_t N, class = std::enable_if_t<(N >= 0)>>
   1113 constexpr dim_t<N> dim() noexcept
   1114 {
   1115     return dim_t<N>();
   1116 }
   1117 
   1118 template <std::ptrdiff_t N = dynamic_range, class = std::enable_if_t<N == dynamic_range>>
   1119 constexpr dim_t<N> dim(std::ptrdiff_t n) noexcept
   1120 {
   1121     return dim_t<>(n);
   1122 }
   1123 
   1124 template <typename ValueType, std::ptrdiff_t FirstDimension = dynamic_range,
   1125           std::ptrdiff_t... RestDimensions>
   1126 class multi_span;
   1127 template <typename ValueType, std::size_t Rank>
   1128 class strided_span;
   1129 
   1130 namespace details
   1131 {
   1132     template <typename T, typename = std::true_type>
   1133     struct SpanTypeTraits
   1134     {
   1135         using value_type = T;
   1136         using size_type = std::size_t;
   1137     };
   1138 
   1139     template <typename Traits>
   1140     struct SpanTypeTraits<Traits, typename std::is_reference<typename Traits::span_traits&>::type>
   1141     {
   1142         using value_type = typename Traits::span_traits::value_type;
   1143         using size_type = typename Traits::span_traits::size_type;
   1144     };
   1145 
   1146     template <typename T, std::ptrdiff_t... Ranks>
   1147     struct SpanArrayTraits
   1148     {
   1149         using type = multi_span<T, Ranks...>;
   1150         using value_type = T;
   1151         using bounds_type = static_bounds<Ranks...>;
   1152         using pointer = T*;
   1153         using reference = T&;
   1154     };
   1155     template <typename T, std::ptrdiff_t N, std::ptrdiff_t... Ranks>
   1156     struct SpanArrayTraits<T[N], Ranks...> : SpanArrayTraits<T, Ranks..., N>
   1157     {
   1158     };
   1159 
   1160     template <typename BoundsType>
   1161     BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::true_type) // dynamic size
   1162     {
   1163         Expects(totalSize >= 0 && totalSize <= PTRDIFF_MAX);
   1164         return BoundsType{totalSize};
   1165     }
   1166     template <typename BoundsType>
   1167     BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::false_type) // static size
   1168     {
   1169         Expects(BoundsType::static_size <= totalSize);
   1170         return {};
   1171     }
   1172     template <typename BoundsType>
   1173     BoundsType newBoundsHelper(std::ptrdiff_t totalSize)
   1174     {
   1175         static_assert(BoundsType::dynamic_rank <= 1, "dynamic rank must less or equal to 1");
   1176         return newBoundsHelperImpl<BoundsType>(
   1177             totalSize, std::integral_constant<bool, BoundsType::dynamic_rank == 1>());
   1178     }
   1179 
   1180     struct Sep
   1181     {
   1182     };
   1183 
   1184     template <typename T, typename... Args>
   1185     T static_as_multi_span_helper(Sep, Args... args)
   1186     {
   1187         return T{narrow_cast<typename T::size_type>(args)...};
   1188     }
   1189     template <typename T, typename Arg, typename... Args>
   1190     std::enable_if_t<
   1191         !std::is_same<Arg, dim_t<dynamic_range>>::value && !std::is_same<Arg, Sep>::value, T>
   1192     static_as_multi_span_helper(Arg, Args... args)
   1193     {
   1194         return static_as_multi_span_helper<T>(args...);
   1195     }
   1196     template <typename T, typename... Args>
   1197     T static_as_multi_span_helper(dim_t<dynamic_range> val, Args... args)
   1198     {
   1199         return static_as_multi_span_helper<T>(args..., val.dvalue);
   1200     }
   1201 
   1202     template <typename... Dimensions>
   1203     struct static_as_multi_span_static_bounds_helper
   1204     {
   1205         using type = static_bounds<(Dimensions::value)...>;
   1206     };
   1207 
   1208     template <typename T>
   1209     struct is_multi_span_oracle : std::false_type
   1210     {
   1211     };
   1212 
   1213     template <typename ValueType, std::ptrdiff_t FirstDimension, std::ptrdiff_t... RestDimensions>
   1214     struct is_multi_span_oracle<multi_span<ValueType, FirstDimension, RestDimensions...>>
   1215         : std::true_type
   1216     {
   1217     };
   1218 
   1219     template <typename ValueType, std::ptrdiff_t Rank>
   1220     struct is_multi_span_oracle<strided_span<ValueType, Rank>> : std::true_type
   1221     {
   1222     };
   1223 
   1224     template <typename T>
   1225     struct is_multi_span : is_multi_span_oracle<std::remove_cv_t<T>>
   1226     {
   1227     };
   1228 } // namespace details
   1229 
   1230 template <typename ValueType, std::ptrdiff_t FirstDimension, std::ptrdiff_t... RestDimensions>
   1231 class multi_span
   1232 {
   1233     // TODO do we still need this?
   1234     template <typename ValueType2, std::ptrdiff_t FirstDimension2,
   1235               std::ptrdiff_t... RestDimensions2>
   1236     friend class multi_span;
   1237 
   1238 public:
   1239     using bounds_type = static_bounds<FirstDimension, RestDimensions...>;
   1240     static const std::size_t Rank = bounds_type::rank;
   1241     using size_type = typename bounds_type::size_type;
   1242     using index_type = typename bounds_type::index_type;
   1243     using value_type = ValueType;
   1244     using const_value_type = std::add_const_t<value_type>;
   1245     using pointer = std::add_pointer_t<value_type>;
   1246     using reference = std::add_lvalue_reference_t<value_type>;
   1247     using iterator = contiguous_span_iterator<multi_span>;
   1248     using const_span = multi_span<const_value_type, FirstDimension, RestDimensions...>;
   1249     using const_iterator = contiguous_span_iterator<const_span>;
   1250     using reverse_iterator = std::reverse_iterator<iterator>;
   1251     using const_reverse_iterator = std::reverse_iterator<const_iterator>;
   1252     using sliced_type =
   1253         std::conditional_t<Rank == 1, value_type, multi_span<value_type, RestDimensions...>>;
   1254 
   1255 private:
   1256     pointer data_;
   1257     bounds_type bounds_;
   1258 
   1259     friend iterator;
   1260     friend const_iterator;
   1261 
   1262 public:
   1263     // default constructor - same as constructing from nullptr_t
   1264     GSL_SUPPRESS(type.6) // NO-FORMAT: attribute // TODO: false positive
   1265     constexpr multi_span() noexcept
   1266         : multi_span(nullptr, bounds_type{})
   1267     {
   1268         static_assert(bounds_type::dynamic_rank != 0 ||
   1269                           (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0),
   1270                       "Default construction of multi_span<T> only possible "
   1271                       "for dynamic or fixed, zero-length spans.");
   1272     }
   1273 
   1274     // construct from nullptr - get an empty multi_span
   1275     GSL_SUPPRESS(type.6) // NO-FORMAT: attribute // TODO: false positive
   1276     constexpr multi_span(std::nullptr_t) noexcept
   1277         : multi_span(nullptr, bounds_type{})
   1278     {
   1279         static_assert(bounds_type::dynamic_rank != 0 ||
   1280                           (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0),
   1281                       "nullptr_t construction of multi_span<T> only possible "
   1282                       "for dynamic or fixed, zero-length spans.");
   1283     }
   1284 
   1285     // construct from nullptr with size of 0 (helps with template function calls)
   1286     template <class IntType, typename = std::enable_if_t<std::is_integral<IntType>::value>>
   1287 
   1288     // GSL_SUPPRESS(type.6) // NO-FORMAT: attribute // TODO: false positive // TODO: parser bug
   1289     constexpr multi_span(std::nullptr_t, IntType size) : multi_span(nullptr, bounds_type{})
   1290     {
   1291         static_assert(bounds_type::dynamic_rank != 0 ||
   1292                           (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0),
   1293                       "nullptr_t construction of multi_span<T> only possible "
   1294                       "for dynamic or fixed, zero-length spans.");
   1295         Expects(size == 0);
   1296     }
   1297 
   1298     // construct from a single element
   1299 
   1300     GSL_SUPPRESS(type.6) // NO-FORMAT: attribute // TODO: false positive
   1301     constexpr multi_span(reference data) noexcept
   1302         : multi_span(&data, bounds_type{1})
   1303     {
   1304         static_assert(bounds_type::dynamic_rank > 0 || bounds_type::static_size == 0 ||
   1305                           bounds_type::static_size == 1,
   1306                       "Construction from a single element only possible "
   1307                       "for dynamic or fixed spans of length 0 or 1.");
   1308     }
   1309 
   1310     // prevent constructing from temporaries for single-elements
   1311     constexpr multi_span(value_type&&) = delete;
   1312 
   1313     // construct from pointer + length
   1314     GSL_SUPPRESS(type.6) // NO-FORMAT: attribute // TODO: false positive
   1315     constexpr multi_span(pointer ptr, size_type size)
   1316         : multi_span(ptr, bounds_type{size})
   1317     {}
   1318 
   1319     // construct from pointer + length - multidimensional
   1320     constexpr multi_span(pointer data, bounds_type bounds)
   1321         : data_(data), bounds_(std::move(bounds))
   1322     {
   1323         Expects((bounds_.size() > 0 && data != nullptr) || bounds_.size() == 0);
   1324     }
   1325 
   1326     // construct from begin,end pointer pair
   1327     template <typename Ptr,
   1328               typename = std::enable_if_t<std::is_convertible<Ptr, pointer>::value &&
   1329                                           details::LessThan<bounds_type::dynamic_rank, 2>::value>>
   1330     constexpr multi_span(pointer begin, Ptr end)
   1331         : multi_span(begin,
   1332                      details::newBoundsHelper<bounds_type>(static_cast<pointer>(end) - begin))
   1333     {
   1334         Expects(begin != nullptr && end != nullptr && begin <= static_cast<pointer>(end));
   1335     }
   1336 
   1337     // construct from n-dimensions static array
   1338     template <typename T, std::size_t N, typename Helper = details::SpanArrayTraits<T, N>>
   1339     constexpr multi_span(T (&arr)[N])
   1340         : multi_span(reinterpret_cast<pointer>(arr), bounds_type{typename Helper::bounds_type{}})
   1341     {
   1342         static_assert(std::is_convertible<typename Helper::value_type(*)[], value_type(*)[]>::value,
   1343                       "Cannot convert from source type to target multi_span type.");
   1344         static_assert(std::is_convertible<typename Helper::bounds_type, bounds_type>::value,
   1345                       "Cannot construct a multi_span from an array with fewer elements.");
   1346     }
   1347 
   1348     // construct from n-dimensions dynamic array (e.g. new int[m][4])
   1349     // (precedence will be lower than the 1-dimension pointer)
   1350     template <typename T, typename Helper = details::SpanArrayTraits<T, dynamic_range>>
   1351     constexpr multi_span(T* const& data, size_type size)
   1352         : multi_span(reinterpret_cast<pointer>(data), typename Helper::bounds_type{size})
   1353     {
   1354         static_assert(std::is_convertible<typename Helper::value_type(*)[], value_type(*)[]>::value,
   1355                       "Cannot convert from source type to target multi_span type.");
   1356     }
   1357 
   1358     // construct from std::array
   1359     template <typename T, std::size_t N>
   1360     constexpr multi_span(std::array<T, N>& arr)
   1361         : multi_span(arr.data(), bounds_type{static_bounds<N>{}})
   1362     {
   1363         static_assert(
   1364             std::is_convertible<T(*)[], typename std::remove_const_t<value_type>(*)[]>::value,
   1365             "Cannot convert from source type to target multi_span type.");
   1366         static_assert(std::is_convertible<static_bounds<N>, bounds_type>::value,
   1367                       "You cannot construct a multi_span from a std::array of smaller size.");
   1368     }
   1369 
   1370     // construct from const std::array
   1371     template <typename T, std::size_t N>
   1372     constexpr multi_span(const std::array<T, N>& arr)
   1373         : multi_span(arr.data(), bounds_type{static_bounds<N>{}})
   1374     {
   1375         static_assert(
   1376             std::is_convertible<T(*)[], typename std::remove_const_t<value_type>(*)[]>::value,
   1377             "Cannot convert from source type to target multi_span type.");
   1378         static_assert(std::is_convertible<static_bounds<N>, bounds_type>::value,
   1379                       "You cannot construct a multi_span from a std::array of smaller size.");
   1380     }
   1381 
   1382     // prevent constructing from temporary std::array
   1383     template <typename T, std::size_t N>
   1384     constexpr multi_span(std::array<T, N>&& arr) = delete;
   1385 
   1386     // construct from containers
   1387     // future: could use contiguous_iterator_traits to identify only contiguous containers
   1388     // type-requirements: container must have .size(), operator[] which are value_type compatible
   1389     template <typename Cont, typename DataType = typename Cont::value_type,
   1390               typename = std::enable_if_t<
   1391                   !details::is_multi_span<Cont>::value &&
   1392                   std::is_convertible<DataType (*)[], value_type (*)[]>::value &&
   1393                   std::is_same<std::decay_t<decltype(std::declval<Cont>().size(),
   1394                                                      *std::declval<Cont>().data())>,
   1395                                DataType>::value>>
   1396     constexpr multi_span(Cont& cont)
   1397         : multi_span(static_cast<pointer>(cont.data()),
   1398                      details::newBoundsHelper<bounds_type>(narrow_cast<size_type>(cont.size())))
   1399     {}
   1400 
   1401     // prevent constructing from temporary containers
   1402     template <typename Cont, typename DataType = typename Cont::value_type,
   1403               typename = std::enable_if_t<
   1404                   !details::is_multi_span<Cont>::value &&
   1405                   std::is_convertible<DataType (*)[], value_type (*)[]>::value &&
   1406                   std::is_same<std::decay_t<decltype(std::declval<Cont>().size(),
   1407                                                      *std::declval<Cont>().data())>,
   1408                                DataType>::value>>
   1409     explicit constexpr multi_span(Cont&& cont) = delete;
   1410 
   1411     // construct from a convertible multi_span
   1412     template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
   1413               typename OtherBounds = static_bounds<OtherDimensions...>,
   1414               typename = std::enable_if_t<std::is_convertible<OtherValueType, ValueType>::value &&
   1415                                           std::is_convertible<OtherBounds, bounds_type>::value>>
   1416     constexpr multi_span(multi_span<OtherValueType, OtherDimensions...> other)
   1417         : data_(other.data_), bounds_(other.bounds_)
   1418     {}
   1419 
   1420     // trivial copy and move
   1421     constexpr multi_span(const multi_span&) = default;
   1422     constexpr multi_span(multi_span&&) = default;
   1423 
   1424     // trivial assignment
   1425     constexpr multi_span& operator=(const multi_span&) = default;
   1426     constexpr multi_span& operator=(multi_span&&) = default;
   1427 
   1428     // first() - extract the first Count elements into a new multi_span
   1429     template <std::ptrdiff_t Count>
   1430 
   1431     constexpr multi_span<ValueType, Count> first() const
   1432     {
   1433         static_assert(Count >= 0, "Count must be >= 0.");
   1434         static_assert(bounds_type::static_size == dynamic_range ||
   1435                           Count <= bounds_type::static_size,
   1436                       "Count is out of bounds.");
   1437 
   1438         Expects(bounds_type::static_size != dynamic_range || Count <= this->size());
   1439         return {this->data(), Count};
   1440     }
   1441 
   1442     // first() - extract the first count elements into a new multi_span
   1443     constexpr multi_span<ValueType, dynamic_range> first(size_type count) const
   1444     {
   1445         Expects(count >= 0 && count <= this->size());
   1446         return {this->data(), count};
   1447     }
   1448 
   1449     // last() - extract the last Count elements into a new multi_span
   1450     template <std::ptrdiff_t Count>
   1451     constexpr multi_span<ValueType, Count> last() const
   1452     {
   1453         static_assert(Count >= 0, "Count must be >= 0.");
   1454         static_assert(bounds_type::static_size == dynamic_range ||
   1455                           Count <= bounds_type::static_size,
   1456                       "Count is out of bounds.");
   1457 
   1458         Expects(bounds_type::static_size != dynamic_range || Count <= this->size());
   1459         return {this->data() + this->size() - Count, Count};
   1460     }
   1461 
   1462     // last() - extract the last count elements into a new multi_span
   1463     constexpr multi_span<ValueType, dynamic_range> last(size_type count) const
   1464     {
   1465         Expects(count >= 0 && count <= this->size());
   1466         return {this->data() + this->size() - count, count};
   1467     }
   1468 
   1469     // subspan() - create a subview of Count elements starting at Offset
   1470     template <std::ptrdiff_t Offset, std::ptrdiff_t Count>
   1471     constexpr multi_span<ValueType, Count> subspan() const
   1472     {
   1473         static_assert(Count >= 0, "Count must be >= 0.");
   1474         static_assert(Offset >= 0, "Offset must be >= 0.");
   1475         static_assert(bounds_type::static_size == dynamic_range ||
   1476                           ((Offset <= bounds_type::static_size) &&
   1477                            Count <= bounds_type::static_size - Offset),
   1478                       "You must describe a sub-range within bounds of the multi_span.");
   1479 
   1480         Expects(bounds_type::static_size != dynamic_range ||
   1481                 (Offset <= this->size() && Count <= this->size() - Offset));
   1482         return {this->data() + Offset, Count};
   1483     }
   1484 
   1485     // subspan() - create a subview of count elements starting at offset
   1486     // supplying dynamic_range for count will consume all available elements from offset
   1487     constexpr multi_span<ValueType, dynamic_range> subspan(size_type offset,
   1488                                                            size_type count = dynamic_range) const
   1489     {
   1490         Expects((offset >= 0 && offset <= this->size()) &&
   1491                 (count == dynamic_range || (count <= this->size() - offset)));
   1492         return {this->data() + offset, count == dynamic_range ? this->length() - offset : count};
   1493     }
   1494 
   1495     // section - creates a non-contiguous, strided multi_span from a contiguous one
   1496     constexpr strided_span<ValueType, Rank> section(index_type origin,
   1497                                                     index_type extents) const
   1498     {
   1499         const size_type size = this->bounds().total_size() - this->bounds().linearize(origin);
   1500         return {&this->operator[](origin), size,
   1501                 strided_bounds<Rank>{extents, details::make_stride(bounds())}};
   1502     }
   1503 
   1504     // length of the multi_span in elements
   1505     constexpr size_type size() const noexcept { return bounds_.size(); }
   1506 
   1507     // length of the multi_span in elements
   1508     constexpr size_type length() const noexcept { return this->size(); }
   1509 
   1510     // length of the multi_span in bytes
   1511     constexpr size_type size_bytes() const noexcept
   1512     {
   1513         return narrow_cast<size_type>(sizeof(value_type)) * this->size();
   1514     }
   1515 
   1516     // length of the multi_span in bytes
   1517     constexpr size_type length_bytes() const noexcept { return this->size_bytes(); }
   1518 
   1519     constexpr bool empty() const noexcept { return this->size() == 0; }
   1520 
   1521     static constexpr std::size_t rank() { return Rank; }
   1522 
   1523     template <std::size_t Dim = 0>
   1524     constexpr size_type extent() const noexcept
   1525     {
   1526         static_assert(Dim < Rank,
   1527                       "Dimension should be less than rank (dimension count starts from 0).");
   1528         return bounds_.template extent<Dim>();
   1529     }
   1530 
   1531     template <typename IntType>
   1532     constexpr size_type extent(IntType dim) const
   1533     {
   1534         return bounds_.extent(dim);
   1535     }
   1536 
   1537     constexpr bounds_type bounds() const noexcept { return bounds_; }
   1538 
   1539     constexpr pointer data() const noexcept { return data_; }
   1540 
   1541     template <typename FirstIndex>
   1542     constexpr reference operator()(FirstIndex idx)
   1543     {
   1544         return this->operator[](narrow_cast<std::ptrdiff_t>(idx));
   1545     }
   1546 
   1547     template <typename FirstIndex, typename... OtherIndices>
   1548     constexpr reference operator()(FirstIndex firstIndex, OtherIndices... indices)
   1549     {
   1550         const index_type idx = {narrow_cast<std::ptrdiff_t>(firstIndex),
   1551                                 narrow_cast<std::ptrdiff_t>(indices)...};
   1552         return this->operator[](idx);
   1553     }
   1554 
   1555     GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute
   1556     constexpr reference operator[](const index_type& idx) const
   1557     {
   1558         return data_[bounds_.linearize(idx)];
   1559     }
   1560 
   1561     template <bool Enabled = (Rank > 1), typename Ret = std::enable_if_t<Enabled, sliced_type>>
   1562 
   1563     GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute
   1564     constexpr Ret operator[](size_type idx) const
   1565     {
   1566         Expects(idx >= 0 && idx < bounds_.size()); // index is out of bounds of the array
   1567         const size_type ridx = idx * bounds_.stride();
   1568 
   1569         // index is out of bounds of the underlying data
   1570         Expects(ridx < bounds_.total_size());
   1571         return Ret{data_ + ridx, bounds_.slice()};
   1572     }
   1573 
   1574     constexpr iterator begin() const noexcept { return iterator{this, true}; }
   1575 
   1576     constexpr iterator end() const noexcept { return iterator{this, false}; }
   1577 
   1578     GSL_SUPPRESS(type.1) // NO-FORMAT: attribute
   1579     constexpr const_iterator cbegin() const noexcept
   1580     {
   1581         return const_iterator{reinterpret_cast<const const_span*>(this), true};
   1582     }
   1583 
   1584     constexpr const_iterator cend() const noexcept
   1585     {
   1586         return const_iterator{reinterpret_cast<const const_span*>(this), false};
   1587     }
   1588 
   1589     constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; }
   1590 
   1591     constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; }
   1592 
   1593     constexpr const_reverse_iterator crbegin() const noexcept
   1594     {
   1595         return const_reverse_iterator{cend()};
   1596     }
   1597 
   1598     constexpr const_reverse_iterator crend() const noexcept
   1599     {
   1600         return const_reverse_iterator{cbegin()};
   1601     }
   1602 
   1603     template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
   1604               typename = std::enable_if_t<std::is_same<std::remove_cv_t<value_type>,
   1605                                                        std::remove_cv_t<OtherValueType>>::value>>
   1606     constexpr bool operator==(const multi_span<OtherValueType, OtherDimensions...>& other) const
   1607     {
   1608         return bounds_.size() == other.bounds_.size() &&
   1609                (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin()));
   1610     }
   1611 
   1612     template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
   1613               typename = std::enable_if_t<std::is_same<std::remove_cv_t<value_type>,
   1614                                                        std::remove_cv_t<OtherValueType>>::value>>
   1615     constexpr bool operator!=(const multi_span<OtherValueType, OtherDimensions...>& other) const
   1616     {
   1617         return !(*this == other);
   1618     }
   1619 
   1620     template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
   1621               typename = std::enable_if_t<std::is_same<std::remove_cv_t<value_type>,
   1622                                                        std::remove_cv_t<OtherValueType>>::value>>
   1623     constexpr bool operator<(const multi_span<OtherValueType, OtherDimensions...>& other) const
   1624     {
   1625         return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end());
   1626     }
   1627 
   1628     template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
   1629               typename = std::enable_if_t<std::is_same<std::remove_cv_t<value_type>,
   1630                                                        std::remove_cv_t<OtherValueType>>::value>>
   1631     constexpr bool operator<=(const multi_span<OtherValueType, OtherDimensions...>& other) const
   1632     {
   1633         return !(other < *this);
   1634     }
   1635 
   1636     template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
   1637               typename = std::enable_if_t<std::is_same<std::remove_cv_t<value_type>,
   1638                                                        std::remove_cv_t<OtherValueType>>::value>>
   1639     constexpr bool operator>(const multi_span<OtherValueType, OtherDimensions...>& other) const
   1640         noexcept
   1641     {
   1642         return (other < *this);
   1643     }
   1644 
   1645     template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
   1646               typename = std::enable_if_t<std::is_same<std::remove_cv_t<value_type>,
   1647                                                        std::remove_cv_t<OtherValueType>>::value>>
   1648     constexpr bool operator>=(const multi_span<OtherValueType, OtherDimensions...>& other) const
   1649     {
   1650         return !(*this < other);
   1651     }
   1652 };
   1653 
   1654 //
   1655 // Free functions for manipulating spans
   1656 //
   1657 
   1658 // reshape a multi_span into a different dimensionality
   1659 // DimCount and Enabled here are workarounds for a bug in MSVC 2015
   1660 template <typename SpanType, typename... Dimensions2, std::size_t DimCount = sizeof...(Dimensions2),
   1661           bool Enabled = (DimCount > 0), typename = std::enable_if_t<Enabled>>
   1662 constexpr auto as_multi_span(SpanType s, Dimensions2... dims)
   1663     -> multi_span<typename SpanType::value_type, Dimensions2::value...>
   1664 {
   1665     static_assert(details::is_multi_span<SpanType>::value,
   1666                   "Variadic as_multi_span() is for reshaping existing spans.");
   1667     using BoundsType =
   1668         typename multi_span<typename SpanType::value_type, (Dimensions2::value)...>::bounds_type;
   1669     const auto tobounds = details::static_as_multi_span_helper<BoundsType>(dims..., details::Sep{});
   1670     details::verifyBoundsReshape(s.bounds(), tobounds);
   1671     return {s.data(), tobounds};
   1672 }
   1673 
   1674 // convert a multi_span<T> to a multi_span<const byte>
   1675 template <typename U, std::ptrdiff_t... Dimensions>
   1676 multi_span<const byte, dynamic_range> as_bytes(multi_span<U, Dimensions...> s) noexcept
   1677 {
   1678     static_assert(std::is_trivial<std::decay_t<U>>::value,
   1679                   "The value_type of multi_span must be a trivial type.");
   1680     return {reinterpret_cast<const byte*>(s.data()), s.size_bytes()};
   1681 }
   1682 
   1683 // convert a multi_span<T> to a multi_span<byte> (a writeable byte multi_span)
   1684 // this is not currently a portable function that can be relied upon to work
   1685 // on all implementations. It should be considered an experimental extension
   1686 // to the standard GSL interface.
   1687 template <typename U, std::ptrdiff_t... Dimensions>
   1688 multi_span<byte> as_writeable_bytes(multi_span<U, Dimensions...> s) noexcept
   1689 {
   1690     static_assert(std::is_trivial<std::decay_t<U>>::value,
   1691                   "The value_type of multi_span must be a trivial type.");
   1692     return {reinterpret_cast<byte*>(s.data()), s.size_bytes()};
   1693 }
   1694 
   1695 // convert a multi_span<const byte> to a multi_span<const T>
   1696 // this is not currently a portable function that can be relied upon to work
   1697 // on all implementations. It should be considered an experimental extension
   1698 // to the standard GSL interface.
   1699 template <typename U, std::ptrdiff_t... Dimensions>
   1700 constexpr auto as_multi_span(multi_span<const byte, Dimensions...> s) -> multi_span<
   1701     const U, static_cast<std::ptrdiff_t>(
   1702                  multi_span<const byte, Dimensions...>::bounds_type::static_size != dynamic_range
   1703                      ? (static_cast<std::size_t>(
   1704                             multi_span<const byte, Dimensions...>::bounds_type::static_size) /
   1705                         sizeof(U))
   1706                      : dynamic_range)>
   1707 {
   1708     using ConstByteSpan = multi_span<const byte, Dimensions...>;
   1709     static_assert(
   1710         std::is_trivial<std::decay_t<U>>::value &&
   1711             (ConstByteSpan::bounds_type::static_size == dynamic_range ||
   1712              ConstByteSpan::bounds_type::static_size % narrow_cast<std::ptrdiff_t>(sizeof(U)) == 0),
   1713         "Target type must be a trivial type and its size must match the byte array size");
   1714 
   1715     Expects((s.size_bytes() % narrow_cast<std::ptrdiff_t>(sizeof(U))) == 0 &&
   1716             (s.size_bytes() / narrow_cast<std::ptrdiff_t>(sizeof(U))) < PTRDIFF_MAX);
   1717     return {reinterpret_cast<const U*>(s.data()),
   1718             s.size_bytes() / narrow_cast<std::ptrdiff_t>(sizeof(U))};
   1719 }
   1720 
   1721 // convert a multi_span<byte> to a multi_span<T>
   1722 // this is not currently a portable function that can be relied upon to work
   1723 // on all implementations. It should be considered an experimental extension
   1724 // to the standard GSL interface.
   1725 template <typename U, std::ptrdiff_t... Dimensions>
   1726 constexpr auto as_multi_span(multi_span<byte, Dimensions...> s)
   1727     -> multi_span<U, narrow_cast<std::ptrdiff_t>(
   1728                          multi_span<byte, Dimensions...>::bounds_type::static_size != dynamic_range
   1729                              ? static_cast<std::size_t>(
   1730                                    multi_span<byte, Dimensions...>::bounds_type::static_size) /
   1731                                    sizeof(U)
   1732                              : dynamic_range)>
   1733 {
   1734     using ByteSpan = multi_span<byte, Dimensions...>;
   1735     static_assert(std::is_trivial<std::decay_t<U>>::value &&
   1736                       (ByteSpan::bounds_type::static_size == dynamic_range ||
   1737                        ByteSpan::bounds_type::static_size % sizeof(U) == 0),
   1738                   "Target type must be a trivial type and its size must match the byte array size");
   1739 
   1740     Expects((s.size_bytes() % sizeof(U)) == 0);
   1741     return {reinterpret_cast<U*>(s.data()),
   1742             s.size_bytes() / narrow_cast<std::ptrdiff_t>(sizeof(U))};
   1743 }
   1744 
   1745 template <typename T, std::ptrdiff_t... Dimensions>
   1746 constexpr auto as_multi_span(T* const& ptr, dim_t<Dimensions>... args)
   1747     -> multi_span<std::remove_all_extents_t<T>, Dimensions...>
   1748 {
   1749     return {reinterpret_cast<std::remove_all_extents_t<T>*>(ptr),
   1750             details::static_as_multi_span_helper<static_bounds<Dimensions...>>(args...,
   1751                                                                                details::Sep{})};
   1752 }
   1753 
   1754 template <typename T>
   1755 constexpr auto as_multi_span(T* arr, std::ptrdiff_t len) ->
   1756     typename details::SpanArrayTraits<T, dynamic_range>::type
   1757 {
   1758     return {reinterpret_cast<std::remove_all_extents_t<T>*>(arr), len};
   1759 }
   1760 
   1761 template <typename T, std::size_t N>
   1762 constexpr auto as_multi_span(T (&arr)[N]) -> typename details::SpanArrayTraits<T, N>::type
   1763 {
   1764     return {arr};
   1765 }
   1766 
   1767 template <typename T, std::size_t N>
   1768 constexpr multi_span<const T, N> as_multi_span(const std::array<T, N>& arr)
   1769 {
   1770     return {arr};
   1771 }
   1772 
   1773 template <typename T, std::size_t N>
   1774 constexpr multi_span<const T, N> as_multi_span(const std::array<T, N>&&) = delete;
   1775 
   1776 template <typename T, std::size_t N>
   1777 constexpr multi_span<T, N> as_multi_span(std::array<T, N>& arr)
   1778 {
   1779     return {arr};
   1780 }
   1781 
   1782 template <typename T>
   1783 constexpr multi_span<T, dynamic_range> as_multi_span(T* begin, T* end)
   1784 {
   1785     return {begin, end};
   1786 }
   1787 
   1788 template <typename Cont>
   1789 constexpr auto as_multi_span(Cont& arr) -> std::enable_if_t<
   1790     !details::is_multi_span<std::decay_t<Cont>>::value,
   1791     multi_span<std::remove_reference_t<decltype(arr.size(), *arr.data())>, dynamic_range>>
   1792 {
   1793     Expects(arr.size() < PTRDIFF_MAX);
   1794     return {arr.data(), narrow_cast<std::ptrdiff_t>(arr.size())};
   1795 }
   1796 
   1797 template <typename Cont>
   1798 constexpr auto as_multi_span(Cont&& arr) -> std::enable_if_t<
   1799     !details::is_multi_span<std::decay_t<Cont>>::value,
   1800     multi_span<std::remove_reference_t<decltype(arr.size(), *arr.data())>, dynamic_range>> = delete;
   1801 
   1802 // from basic_string which doesn't have nonconst .data() member like other contiguous containers
   1803 template <typename CharT, typename Traits, typename Allocator>
   1804 GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute
   1805 constexpr auto as_multi_span(std::basic_string<CharT, Traits, Allocator>& str)
   1806     -> multi_span<CharT, dynamic_range>
   1807 {
   1808     Expects(str.size() < PTRDIFF_MAX);
   1809     return {&str[0], narrow_cast<std::ptrdiff_t>(str.size())};
   1810 }
   1811 
   1812 // strided_span is an extension that is not strictly part of the GSL at this time.
   1813 // It is kept here while the multidimensional interface is still being defined.
   1814 template <typename ValueType, std::size_t Rank>
   1815 class strided_span
   1816 {
   1817 public:
   1818     using bounds_type = strided_bounds<Rank>;
   1819     using size_type = typename bounds_type::size_type;
   1820     using index_type = typename bounds_type::index_type;
   1821     using value_type = ValueType;
   1822     using const_value_type = std::add_const_t<value_type>;
   1823     using pointer = std::add_pointer_t<value_type>;
   1824     using reference = std::add_lvalue_reference_t<value_type>;
   1825     using iterator = general_span_iterator<strided_span>;
   1826     using const_strided_span = strided_span<const_value_type, Rank>;
   1827     using const_iterator = general_span_iterator<const_strided_span>;
   1828     using reverse_iterator = std::reverse_iterator<iterator>;
   1829     using const_reverse_iterator = std::reverse_iterator<const_iterator>;
   1830     using sliced_type =
   1831         std::conditional_t<Rank == 1, value_type, strided_span<value_type, Rank - 1>>;
   1832 
   1833 private:
   1834     pointer data_;
   1835     bounds_type bounds_;
   1836 
   1837     friend iterator;
   1838     friend const_iterator;
   1839     template <typename OtherValueType, std::size_t OtherRank>
   1840     friend class strided_span;
   1841 
   1842 public:
   1843     // from raw data
   1844     constexpr strided_span(pointer ptr, size_type size, bounds_type bounds)
   1845         : data_(ptr), bounds_(std::move(bounds))
   1846     {
   1847         Expects((bounds_.size() > 0 && ptr != nullptr) || bounds_.size() == 0);
   1848         // Bounds cross data boundaries
   1849         Expects(this->bounds().total_size() <= size);
   1850         GSL_SUPPRESS(type.4) // NO-FORMAT: attribute // TODO: false positive
   1851         (void) size;
   1852     }
   1853 
   1854     // from static array of size N
   1855     template <size_type N>
   1856     constexpr strided_span(value_type (&values)[N], bounds_type bounds)
   1857         : strided_span(values, N, std::move(bounds))
   1858     {}
   1859 
   1860     // from array view
   1861     template <typename OtherValueType, std::ptrdiff_t... Dimensions,
   1862               bool Enabled1 = (sizeof...(Dimensions) == Rank),
   1863               bool Enabled2 = std::is_convertible<OtherValueType*, ValueType*>::value,
   1864               typename = std::enable_if_t<Enabled1 && Enabled2>>
   1865     constexpr strided_span(multi_span<OtherValueType, Dimensions...> av, bounds_type bounds)
   1866         : strided_span(av.data(), av.bounds().total_size(), std::move(bounds))
   1867     {}
   1868 
   1869     // convertible
   1870     template <typename OtherValueType, typename = std::enable_if_t<std::is_convertible<
   1871                                            OtherValueType (*)[], value_type (*)[]>::value>>
   1872     constexpr strided_span(const strided_span<OtherValueType, Rank>& other)
   1873         : data_(other.data_), bounds_(other.bounds_)
   1874     {}
   1875 
   1876     // convert from bytes
   1877     template <typename OtherValueType>
   1878     constexpr strided_span<
   1879         typename std::enable_if<std::is_same<value_type, const byte>::value, OtherValueType>::type,
   1880         Rank>
   1881     as_strided_span() const
   1882     {
   1883         static_assert((sizeof(OtherValueType) >= sizeof(value_type)) &&
   1884                           (sizeof(OtherValueType) % sizeof(value_type) == 0),
   1885                       "OtherValueType should have a size to contain a multiple of ValueTypes");
   1886         auto d = narrow_cast<size_type>(sizeof(OtherValueType) / sizeof(value_type));
   1887 
   1888         const size_type size = this->bounds().total_size() / d;
   1889 
   1890         GSL_SUPPRESS(type.3) // NO-FORMAT: attribute
   1891         return {const_cast<OtherValueType*>(reinterpret_cast<const OtherValueType*>(this->data())),
   1892                 size,
   1893                 bounds_type{resize_extent(this->bounds().index_bounds(), d),
   1894                             resize_stride(this->bounds().strides(), d)}};
   1895     }
   1896 
   1897     constexpr strided_span section(index_type origin, index_type extents) const
   1898     {
   1899         const size_type size = this->bounds().total_size() - this->bounds().linearize(origin);
   1900         return {&this->operator[](origin), size,
   1901                 bounds_type{extents, details::make_stride(bounds())}};
   1902     }
   1903 
   1904     GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute
   1905     constexpr reference operator[](const index_type& idx) const
   1906     {
   1907         return data_[bounds_.linearize(idx)];
   1908     }
   1909 
   1910     template <bool Enabled = (Rank > 1), typename Ret = std::enable_if_t<Enabled, sliced_type>>
   1911     GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute
   1912     constexpr Ret operator[](size_type idx) const
   1913     {
   1914         Expects(idx < bounds_.size()); // index is out of bounds of the array
   1915         const size_type ridx = idx * bounds_.stride();
   1916 
   1917         // index is out of bounds of the underlying data
   1918         Expects(ridx < bounds_.total_size());
   1919         return {data_ + ridx, bounds_.slice().total_size(), bounds_.slice()};
   1920     }
   1921 
   1922     constexpr bounds_type bounds() const noexcept { return bounds_; }
   1923 
   1924     template <std::size_t Dim = 0>
   1925     constexpr size_type extent() const noexcept
   1926     {
   1927         static_assert(Dim < Rank,
   1928                       "dimension should be less than Rank (dimension count starts from 0)");
   1929         return bounds_.template extent<Dim>();
   1930     }
   1931 
   1932     constexpr size_type size() const noexcept { return bounds_.size(); }
   1933 
   1934     constexpr pointer data() const noexcept { return data_; }
   1935 
   1936     constexpr bool empty() const noexcept { return this->size() == 0; }
   1937 
   1938     constexpr explicit operator bool() const noexcept { return data_ != nullptr; }
   1939 
   1940     constexpr iterator begin() const { return iterator{this, true}; }
   1941 
   1942     constexpr iterator end() const { return iterator{this, false}; }
   1943 
   1944     constexpr const_iterator cbegin() const
   1945     {
   1946         return const_iterator{reinterpret_cast<const const_strided_span*>(this), true};
   1947     }
   1948 
   1949     constexpr const_iterator cend() const
   1950     {
   1951         return const_iterator{reinterpret_cast<const const_strided_span*>(this), false};
   1952     }
   1953 
   1954     constexpr reverse_iterator rbegin() const { return reverse_iterator{end()}; }
   1955 
   1956     constexpr reverse_iterator rend() const { return reverse_iterator{begin()}; }
   1957 
   1958     constexpr const_reverse_iterator crbegin() const { return const_reverse_iterator{cend()}; }
   1959 
   1960     constexpr const_reverse_iterator crend() const { return const_reverse_iterator{cbegin()}; }
   1961 
   1962     template <typename OtherValueType, std::ptrdiff_t OtherRank,
   1963               typename = std::enable_if_t<std::is_same<std::remove_cv_t<value_type>,
   1964                                                        std::remove_cv_t<OtherValueType>>::value>>
   1965     constexpr bool operator==(const strided_span<OtherValueType, OtherRank>& other) const
   1966     {
   1967         return bounds_.size() == other.bounds_.size() &&
   1968                (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin()));
   1969     }
   1970 
   1971     template <typename OtherValueType, std::ptrdiff_t OtherRank,
   1972               typename = std::enable_if_t<std::is_same<std::remove_cv_t<value_type>,
   1973                                                        std::remove_cv_t<OtherValueType>>::value>>
   1974     constexpr bool operator!=(const strided_span<OtherValueType, OtherRank>& other) const
   1975     {
   1976         return !(*this == other);
   1977     }
   1978 
   1979     template <typename OtherValueType, std::ptrdiff_t OtherRank,
   1980               typename = std::enable_if_t<std::is_same<std::remove_cv_t<value_type>,
   1981                                                        std::remove_cv_t<OtherValueType>>::value>>
   1982     constexpr bool operator<(const strided_span<OtherValueType, OtherRank>& other) const
   1983     {
   1984         return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end());
   1985     }
   1986 
   1987     template <typename OtherValueType, std::ptrdiff_t OtherRank,
   1988               typename = std::enable_if_t<std::is_same<std::remove_cv_t<value_type>,
   1989                                                        std::remove_cv_t<OtherValueType>>::value>>
   1990     constexpr bool operator<=(const strided_span<OtherValueType, OtherRank>& other) const
   1991     {
   1992         return !(other < *this);
   1993     }
   1994 
   1995     template <typename OtherValueType, std::ptrdiff_t OtherRank,
   1996               typename = std::enable_if_t<std::is_same<std::remove_cv_t<value_type>,
   1997                                                        std::remove_cv_t<OtherValueType>>::value>>
   1998     constexpr bool operator>(const strided_span<OtherValueType, OtherRank>& other) const
   1999     {
   2000         return (other < *this);
   2001     }
   2002 
   2003     template <typename OtherValueType, std::ptrdiff_t OtherRank,
   2004               typename = std::enable_if_t<std::is_same<std::remove_cv_t<value_type>,
   2005                                                        std::remove_cv_t<OtherValueType>>::value>>
   2006     constexpr bool operator>=(const strided_span<OtherValueType, OtherRank>& other) const
   2007     {
   2008         return !(*this < other);
   2009     }
   2010 
   2011 private:
   2012     static index_type resize_extent(const index_type& extent, std::ptrdiff_t d)
   2013     {
   2014         // The last dimension of the array needs to contain a multiple of new type elements
   2015         GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute
   2016         Expects(extent[Rank - 1] >= d && (extent[Rank - 1] % d == 0));
   2017 
   2018         index_type ret = extent;
   2019         ret[Rank - 1] /= d;
   2020 
   2021         return ret;
   2022     }
   2023 
   2024     template <bool Enabled = (Rank == 1), typename = std::enable_if_t<Enabled>>
   2025     static index_type resize_stride(const index_type& strides, std::ptrdiff_t, void* = nullptr)
   2026     {
   2027         // Only strided arrays with regular strides can be resized
   2028         GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute
   2029         Expects(strides[Rank - 1] == 1);
   2030 
   2031         return strides;
   2032     }
   2033 
   2034     template <bool Enabled = (Rank > 1), typename = std::enable_if_t<Enabled>>
   2035     GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute
   2036     static index_type resize_stride(const index_type& strides, std::ptrdiff_t d)
   2037     {
   2038         // Only strided arrays with regular strides can be resized
   2039         Expects(strides[Rank - 1] == 1);
   2040         // The strides must have contiguous chunks of
   2041         // memory that can contain a multiple of new type elements
   2042         Expects(strides[Rank - 2] >= d && (strides[Rank - 2] % d == 0));
   2043 
   2044         for (std::size_t i = Rank - 1; i > 0; --i)
   2045         {
   2046             // Only strided arrays with regular strides can be resized
   2047             Expects((strides[i - 1] >= strides[i]) && (strides[i - 1] % strides[i] == 0));
   2048         }
   2049 
   2050         index_type ret = strides / d;
   2051         ret[Rank - 1] = 1;
   2052 
   2053         return ret;
   2054     }
   2055 };
   2056 
   2057 template <class Span>
   2058 class contiguous_span_iterator
   2059 {
   2060 public:
   2061     using iterator_category = std::random_access_iterator_tag;
   2062     using value_type = typename Span::value_type;
   2063     using difference_type = std::ptrdiff_t;
   2064     using pointer = value_type*;
   2065     using reference = value_type&;
   2066 
   2067 private:
   2068     template <typename ValueType, std::ptrdiff_t FirstDimension, std::ptrdiff_t... RestDimensions>
   2069     friend class multi_span;
   2070 
   2071     pointer data_;
   2072     const Span* m_validator;
   2073 
   2074     GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute
   2075     void validateThis() const {
   2076         // iterator is out of range of the array
   2077         Expects(data_ >= m_validator->data_ && data_ < m_validator->data_ + m_validator->size());
   2078     }
   2079 
   2080     GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute
   2081     contiguous_span_iterator(const Span* container, bool isbegin)
   2082         : data_(isbegin ? container->data_ : container->data_ + container->size())
   2083         , m_validator(container)
   2084     {}
   2085 
   2086 public:
   2087     reference operator*() const
   2088     {
   2089         validateThis();
   2090         return *data_;
   2091     }
   2092     pointer operator->() const
   2093     {
   2094         validateThis();
   2095         return data_;
   2096     }
   2097 
   2098     GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute
   2099     contiguous_span_iterator& operator++() noexcept
   2100     {
   2101         ++data_;
   2102         return *this;
   2103     }
   2104     contiguous_span_iterator operator++(int) noexcept
   2105     {
   2106         auto ret = *this;
   2107         ++(*this);
   2108         return ret;
   2109     }
   2110 
   2111     GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute
   2112     contiguous_span_iterator& operator--() noexcept
   2113     {
   2114         --data_;
   2115         return *this;
   2116     }
   2117     contiguous_span_iterator operator--(int) noexcept
   2118     {
   2119         auto ret = *this;
   2120         --(*this);
   2121         return ret;
   2122     }
   2123     contiguous_span_iterator operator+(difference_type n) const noexcept
   2124     {
   2125         contiguous_span_iterator ret{*this};
   2126         return ret += n;
   2127     }
   2128     contiguous_span_iterator& operator+=(difference_type n) noexcept
   2129     {
   2130         data_ += n;
   2131         return *this;
   2132     }
   2133     contiguous_span_iterator operator-(difference_type n) const noexcept
   2134     {
   2135         contiguous_span_iterator ret{*this};
   2136         return ret -= n;
   2137     }
   2138 
   2139     contiguous_span_iterator& operator-=(difference_type n) { return *this += -n; }
   2140     difference_type operator-(const contiguous_span_iterator& rhs) const
   2141     {
   2142         Expects(m_validator == rhs.m_validator);
   2143         return data_ - rhs.data_;
   2144     }
   2145     reference operator[](difference_type n) const { return *(*this + n); }
   2146     bool operator==(const contiguous_span_iterator& rhs) const
   2147     {
   2148         Expects(m_validator == rhs.m_validator);
   2149         return data_ == rhs.data_;
   2150     }
   2151 
   2152     bool operator!=(const contiguous_span_iterator& rhs) const { return !(*this == rhs); }
   2153 
   2154     bool operator<(const contiguous_span_iterator& rhs) const
   2155     {
   2156         Expects(m_validator == rhs.m_validator);
   2157         return data_ < rhs.data_;
   2158     }
   2159 
   2160     bool operator<=(const contiguous_span_iterator& rhs) const { return !(rhs < *this); }
   2161     bool operator>(const contiguous_span_iterator& rhs) const { return rhs < *this; }
   2162     bool operator>=(const contiguous_span_iterator& rhs) const { return !(rhs > *this); }
   2163 
   2164     void swap(contiguous_span_iterator& rhs) noexcept
   2165     {
   2166         std::swap(data_, rhs.data_);
   2167         std::swap(m_validator, rhs.m_validator);
   2168     }
   2169 };
   2170 
   2171 template <typename Span>
   2172 contiguous_span_iterator<Span> operator+(typename contiguous_span_iterator<Span>::difference_type n,
   2173                                          const contiguous_span_iterator<Span>& rhs) noexcept
   2174 {
   2175     return rhs + n;
   2176 }
   2177 
   2178 template <typename Span>
   2179 class general_span_iterator {
   2180 public:
   2181     using iterator_category = std::random_access_iterator_tag;
   2182     using value_type = typename Span::value_type;
   2183     using difference_type = std::ptrdiff_t;
   2184     using pointer = value_type*;
   2185     using reference = value_type&;
   2186 
   2187 private:
   2188     template <typename ValueType, std::size_t Rank>
   2189     friend class strided_span;
   2190 
   2191     const Span* m_container;
   2192     typename Span::bounds_type::iterator m_itr;
   2193     general_span_iterator(const Span* container, bool isbegin)
   2194         : m_container(container)
   2195         , m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end())
   2196     {}
   2197 
   2198 public:
   2199     reference operator*() noexcept { return (*m_container)[*m_itr]; }
   2200     pointer operator->() noexcept { return &(*m_container)[*m_itr]; }
   2201     general_span_iterator& operator++() noexcept
   2202     {
   2203         ++m_itr;
   2204         return *this;
   2205     }
   2206     general_span_iterator operator++(int) noexcept
   2207     {
   2208         auto ret = *this;
   2209         ++(*this);
   2210         return ret;
   2211     }
   2212     general_span_iterator& operator--() noexcept
   2213     {
   2214         --m_itr;
   2215         return *this;
   2216     }
   2217     general_span_iterator operator--(int) noexcept
   2218     {
   2219         auto ret = *this;
   2220         --(*this);
   2221         return ret;
   2222     }
   2223     general_span_iterator operator+(difference_type n) const noexcept
   2224     {
   2225         general_span_iterator ret{*this};
   2226         return ret += n;
   2227     }
   2228     general_span_iterator& operator+=(difference_type n) noexcept
   2229     {
   2230         m_itr += n;
   2231         return *this;
   2232     }
   2233     general_span_iterator operator-(difference_type n) const noexcept
   2234     {
   2235         general_span_iterator ret{*this};
   2236         return ret -= n;
   2237     }
   2238     general_span_iterator& operator-=(difference_type n) noexcept { return *this += -n; }
   2239     difference_type operator-(const general_span_iterator& rhs) const
   2240     {
   2241         Expects(m_container == rhs.m_container);
   2242         return m_itr - rhs.m_itr;
   2243     }
   2244 
   2245     GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute
   2246     value_type operator[](difference_type n) const { return (*m_container)[m_itr[n]]; }
   2247 
   2248     bool operator==(const general_span_iterator& rhs) const
   2249     {
   2250         Expects(m_container == rhs.m_container);
   2251         return m_itr == rhs.m_itr;
   2252     }
   2253     bool operator!=(const general_span_iterator& rhs) const { return !(*this == rhs); }
   2254     bool operator<(const general_span_iterator& rhs) const
   2255     {
   2256         Expects(m_container == rhs.m_container);
   2257         return m_itr < rhs.m_itr;
   2258     }
   2259     bool operator<=(const general_span_iterator& rhs) const { return !(rhs < *this); }
   2260     bool operator>(const general_span_iterator& rhs) const { return rhs < *this; }
   2261     bool operator>=(const general_span_iterator& rhs) const { return !(rhs > *this); }
   2262     void swap(general_span_iterator& rhs) noexcept
   2263     {
   2264         std::swap(m_itr, rhs.m_itr);
   2265         std::swap(m_container, rhs.m_container);
   2266     }
   2267 };
   2268 
   2269 template <typename Span>
   2270 general_span_iterator<Span> operator+(typename general_span_iterator<Span>::difference_type n,
   2271                                       const general_span_iterator<Span>& rhs) noexcept
   2272 {
   2273     return rhs + n;
   2274 }
   2275 
   2276 } // namespace gsl
   2277 
   2278 #if defined(_MSC_VER) && !defined(__clang__)
   2279 #if _MSC_VER < 1910
   2280 
   2281 #undef constexpr
   2282 #pragma pop_macro("constexpr")
   2283 #endif // _MSC_VER < 1910
   2284 
   2285 #pragma warning(pop)
   2286 
   2287 #endif // _MSC_VER
   2288 
   2289 #if defined(__GNUC__) && __GNUC__ > 6
   2290 #pragma GCC diagnostic pop
   2291 #endif // __GNUC__ > 6
   2292 
   2293 #endif // GSL_MULTI_SPAN_H
   2294