IntegerRange.h (5360B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* Iterator over ranges of integers */ 8 9 #ifndef mozilla_IntegerRange_h 10 #define mozilla_IntegerRange_h 11 12 #include "mozilla/Assertions.h" 13 #include "mozilla/ReverseIterator.h" 14 15 #include <iterator> 16 #include <type_traits> 17 18 namespace mozilla { 19 20 namespace detail { 21 22 template <typename IntTypeT> 23 class IntegerIterator { 24 public: 25 // It is disputable whether these type definitions are correct, since 26 // operator* doesn't return a reference at all. Also, the iterator_category 27 // can be at most std::input_iterator_tag (rather than 28 // std::bidrectional_iterator_tag, as it might seem), because it is a stashing 29 // iterator. See also, e.g., 30 // https://stackoverflow.com/questions/50909701/what-should-be-iterator-category-for-a-stashing-iterator 31 using value_type = const IntTypeT; 32 using pointer = const value_type*; 33 using reference = const value_type&; 34 using difference_type = std::make_signed_t<IntTypeT>; 35 using iterator_category = std::input_iterator_tag; 36 37 template <typename IntType> 38 explicit IntegerIterator(IntType aCurrent) : mCurrent(aCurrent) {} 39 40 template <typename IntType> 41 explicit IntegerIterator(const IntegerIterator<IntType>& aOther) 42 : mCurrent(aOther.mCurrent) {} 43 44 // This intentionally returns a value rather than a reference, to make 45 // mozilla::ReverseIterator work with it. Still, std::reverse_iterator cannot 46 // be used with IntegerIterator because it still is a "stashing iterator". See 47 // Bug 1175485. 48 IntTypeT operator*() const { return mCurrent; } 49 50 /* Increment and decrement operators */ 51 52 IntegerIterator& operator++() { 53 ++mCurrent; 54 return *this; 55 } 56 IntegerIterator& operator--() { 57 --mCurrent; 58 return *this; 59 } 60 IntegerIterator operator++(int) { 61 auto ret = *this; 62 ++mCurrent; 63 return ret; 64 } 65 IntegerIterator operator--(int) { 66 auto ret = *this; 67 --mCurrent; 68 return ret; 69 } 70 71 /* Comparison operators */ 72 73 friend bool operator==(const IntegerIterator<IntTypeT>& aIter1, 74 const IntegerIterator<IntTypeT>& aIter2) { 75 return aIter1.mCurrent == aIter2.mCurrent; 76 } 77 friend bool operator!=(const IntegerIterator<IntTypeT>& aIter1, 78 const IntegerIterator<IntTypeT>& aIter2) { 79 return aIter1.mCurrent != aIter2.mCurrent; 80 } 81 friend bool operator<(const IntegerIterator<IntTypeT>& aIter1, 82 const IntegerIterator<IntTypeT>& aIter2) { 83 return aIter1.mCurrent < aIter2.mCurrent; 84 } 85 friend bool operator<=(const IntegerIterator<IntTypeT>& aIter1, 86 const IntegerIterator<IntTypeT>& aIter2) { 87 return aIter1.mCurrent <= aIter2.mCurrent; 88 } 89 friend bool operator>(const IntegerIterator<IntTypeT>& aIter1, 90 const IntegerIterator<IntTypeT>& aIter2) { 91 return aIter1.mCurrent > aIter2.mCurrent; 92 } 93 friend bool operator>=(const IntegerIterator<IntTypeT>& aIter1, 94 const IntegerIterator<IntTypeT>& aIter2) { 95 return aIter1.mCurrent >= aIter2.mCurrent; 96 } 97 98 private: 99 IntTypeT mCurrent; 100 }; 101 102 template <typename IntTypeT> 103 class IntegerRange { 104 public: 105 typedef IntegerIterator<IntTypeT> iterator; 106 typedef IntegerIterator<IntTypeT> const_iterator; 107 typedef ReverseIterator<IntegerIterator<IntTypeT>> reverse_iterator; 108 typedef ReverseIterator<IntegerIterator<IntTypeT>> const_reverse_iterator; 109 110 template <typename IntType> 111 explicit IntegerRange(IntType aEnd) : mBegin(0), mEnd(aEnd) {} 112 113 template <typename IntType1, typename IntType2> 114 IntegerRange(IntType1 aBegin, IntType2 aEnd) : mBegin(aBegin), mEnd(aEnd) {} 115 116 iterator begin() const { return iterator(mBegin); } 117 const_iterator cbegin() const { return begin(); } 118 iterator end() const { return iterator(mEnd); } 119 const_iterator cend() const { return end(); } 120 reverse_iterator rbegin() const { return reverse_iterator(iterator(mEnd)); } 121 const_reverse_iterator crbegin() const { return rbegin(); } 122 reverse_iterator rend() const { return reverse_iterator(iterator(mBegin)); } 123 const_reverse_iterator crend() const { return rend(); } 124 125 private: 126 IntTypeT mBegin; 127 IntTypeT mEnd; 128 }; 129 130 } // namespace detail 131 132 template <typename IntType> 133 detail::IntegerRange<IntType> IntegerRange(IntType aEnd) { 134 static_assert(std::is_integral_v<IntType>, "value must be integral"); 135 if constexpr (std::is_signed_v<IntType>) { 136 MOZ_ASSERT(aEnd >= 0, "Should never have negative value here"); 137 } 138 return detail::IntegerRange<IntType>(aEnd); 139 } 140 141 template <typename IntType1, typename IntType2> 142 detail::IntegerRange<IntType2> IntegerRange(IntType1 aBegin, IntType2 aEnd) { 143 static_assert(std::is_integral_v<IntType1> && std::is_integral_v<IntType2>, 144 "values must both be integral"); 145 static_assert(std::is_signed_v<IntType1> == std::is_signed_v<IntType2>, 146 "signed/unsigned mismatch"); 147 MOZ_ASSERT(aEnd >= aBegin, "End value should be larger than begin value"); 148 return detail::IntegerRange<IntType2>(aBegin, aEnd); 149 } 150 151 } // namespace mozilla 152 153 #endif // mozilla_IntegerRange_h