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