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