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 contiguous enum values */
8 
9 /*
10  * Implements generator functions that create a range to iterate over the values
11  * of a scoped or unscoped enum. Unlike IntegerRange, which can only function on
12  * the underlying integral type, the elements of the generated sequence will
13  * have the type of the enum in question.
14  *
15  * Note that the enum values should be contiguous in the iterated range;
16  * unfortunately there exists no way for EnumeratedRange to enforce this
17  * either dynamically or at compile time.
18  */
19 
20 #ifndef mozilla_EnumeratedRange_h
21 #define mozilla_EnumeratedRange_h
22 
23 #include <limits>
24 #include <type_traits>
25 
26 #include "mozilla/Assertions.h"
27 #include "mozilla/ReverseIterator.h"
28 
29 namespace mozilla {
30 
31 namespace detail {
32 
33 template <typename EnumTypeT>
34 class EnumeratedIterator {
35  public:
36   typedef typename std::underlying_type<EnumTypeT>::type IntTypeT;
37 
38   template <typename EnumType>
EnumeratedIterator(EnumType aCurrent)39   constexpr explicit EnumeratedIterator(EnumType aCurrent)
40       : mCurrent(aCurrent) {}
41 
42   template <typename EnumType>
EnumeratedIterator(const EnumeratedIterator<EnumType> & aOther)43   explicit EnumeratedIterator(const EnumeratedIterator<EnumType>& aOther)
44       : mCurrent(aOther.mCurrent) {}
45 
46   EnumTypeT operator*() const { return mCurrent; }
47 
48   /* Increment and decrement operators */
49 
50   EnumeratedIterator& operator++() {
51     mCurrent = EnumTypeT(IntTypeT(mCurrent) + IntTypeT(1));
52     return *this;
53   }
54   EnumeratedIterator& operator--() {
55     mCurrent = EnumTypeT(IntTypeT(mCurrent) - IntTypeT(1));
56     return *this;
57   }
58   EnumeratedIterator operator++(int) {
59     auto ret = *this;
60     mCurrent = EnumTypeT(IntTypeT(mCurrent) + IntTypeT(1));
61     return ret;
62   }
63   EnumeratedIterator operator--(int) {
64     auto ret = *this;
65     mCurrent = EnumTypeT(IntTypeT(mCurrent) - IntTypeT(1));
66     return ret;
67   }
68 
69   /* Comparison operators */
70 
71   template <typename EnumType>
72   friend bool operator==(const EnumeratedIterator<EnumType>& aIter1,
73                          const EnumeratedIterator<EnumType>& aIter2);
74   template <typename EnumType>
75   friend bool operator!=(const EnumeratedIterator<EnumType>& aIter1,
76                          const EnumeratedIterator<EnumType>& aIter2);
77   template <typename EnumType>
78   friend bool operator<(const EnumeratedIterator<EnumType>& aIter1,
79                         const EnumeratedIterator<EnumType>& aIter2);
80   template <typename EnumType>
81   friend bool operator<=(const EnumeratedIterator<EnumType>& aIter1,
82                          const EnumeratedIterator<EnumType>& aIter2);
83   template <typename EnumType>
84   friend bool operator>(const EnumeratedIterator<EnumType>& aIter1,
85                         const EnumeratedIterator<EnumType>& aIter2);
86   template <typename EnumType>
87   friend bool operator>=(const EnumeratedIterator<EnumType>& aIter1,
88                          const EnumeratedIterator<EnumType>& aIter2);
89 
90  private:
91   EnumTypeT mCurrent;
92 };
93 
94 template <typename EnumType>
95 bool operator==(const EnumeratedIterator<EnumType>& aIter1,
96                 const EnumeratedIterator<EnumType>& aIter2) {
97   return aIter1.mCurrent == aIter2.mCurrent;
98 }
99 
100 template <typename EnumType>
101 bool operator!=(const EnumeratedIterator<EnumType>& aIter1,
102                 const EnumeratedIterator<EnumType>& aIter2) {
103   return aIter1.mCurrent != aIter2.mCurrent;
104 }
105 
106 template <typename EnumType>
107 bool operator<(const EnumeratedIterator<EnumType>& aIter1,
108                const EnumeratedIterator<EnumType>& aIter2) {
109   return aIter1.mCurrent < aIter2.mCurrent;
110 }
111 
112 template <typename EnumType>
113 bool operator<=(const EnumeratedIterator<EnumType>& aIter1,
114                 const EnumeratedIterator<EnumType>& aIter2) {
115   return aIter1.mCurrent <= aIter2.mCurrent;
116 }
117 
118 template <typename EnumType>
119 bool operator>(const EnumeratedIterator<EnumType>& aIter1,
120                const EnumeratedIterator<EnumType>& aIter2) {
121   return aIter1.mCurrent > aIter2.mCurrent;
122 }
123 
124 template <typename EnumType>
125 bool operator>=(const EnumeratedIterator<EnumType>& aIter1,
126                 const EnumeratedIterator<EnumType>& aIter2) {
127   return aIter1.mCurrent >= aIter2.mCurrent;
128 }
129 
130 template <typename EnumTypeT>
131 class EnumeratedRange {
132  public:
133   typedef EnumeratedIterator<EnumTypeT> iterator;
134   typedef EnumeratedIterator<EnumTypeT> const_iterator;
135   typedef ReverseIterator<iterator> reverse_iterator;
136   typedef ReverseIterator<const_iterator> const_reverse_iterator;
137 
138   template <typename EnumType>
EnumeratedRange(EnumType aBegin,EnumType aEnd)139   constexpr EnumeratedRange(EnumType aBegin, EnumType aEnd)
140       : mBegin(aBegin), mEnd(aEnd) {}
141 
begin()142   iterator begin() const { return iterator(mBegin); }
cbegin()143   const_iterator cbegin() const { return begin(); }
end()144   iterator end() const { return iterator(mEnd); }
cend()145   const_iterator cend() const { return end(); }
rbegin()146   reverse_iterator rbegin() const { return reverse_iterator(mEnd); }
crbegin()147   const_reverse_iterator crbegin() const { return rbegin(); }
rend()148   reverse_iterator rend() const { return reverse_iterator(mBegin); }
crend()149   const_reverse_iterator crend() const { return rend(); }
150 
151  private:
152   EnumTypeT mBegin;
153   EnumTypeT mEnd;
154 };
155 
156 }  // namespace detail
157 
158 #ifdef __GNUC__
159 // Enums can have an unsigned underlying type, which makes some of the
160 // comparisons below always true or always false. Temporarily disable
161 // -Wtype-limits to avoid breaking -Werror builds.
162 #  pragma GCC diagnostic push
163 #  pragma GCC diagnostic ignored "-Wtype-limits"
164 #endif
165 
166 // Create a range to iterate from aBegin to aEnd, exclusive.
167 template <typename EnumType>
MakeEnumeratedRange(EnumType aBegin,EnumType aEnd)168 constexpr detail::EnumeratedRange<EnumType> MakeEnumeratedRange(EnumType aBegin,
169                                                                 EnumType aEnd) {
170   MOZ_ASSERT(aBegin <= aEnd, "Cannot generate invalid, unbounded range!");
171   return detail::EnumeratedRange<EnumType>(aBegin, aEnd);
172 }
173 
174 // Create a range to iterate from EnumType(0) to aEnd, exclusive. EnumType(0)
175 // should exist, but note that there is no way for us to ensure that it does!
176 template <typename EnumType>
MakeEnumeratedRange(EnumType aEnd)177 constexpr detail::EnumeratedRange<EnumType> MakeEnumeratedRange(EnumType aEnd) {
178   return MakeEnumeratedRange(EnumType(0), aEnd);
179 }
180 
181 // Create a range to iterate from aBegin to aEnd, inclusive.
182 //
183 // NOTE: This internally constructs a value that is one past `aEnd`, so the
184 // enumeration needs to either have a fixed underlying type, or `aEnd + 1` must
185 // be inside the range of the enumeration, in order to not be undefined
186 // behavior.
187 //
188 // See bug 1614512.
189 template <typename EnumType>
MakeInclusiveEnumeratedRange(EnumType aBegin,EnumType aEnd)190 constexpr detail::EnumeratedRange<EnumType> MakeInclusiveEnumeratedRange(
191     EnumType aBegin, EnumType aEnd) {
192   using EnumUnderlyingType = typename std::underlying_type_t<EnumType>;
193   const auto end = static_cast<EnumUnderlyingType>(aEnd);
194 
195   MOZ_ASSERT(end != std::numeric_limits<EnumUnderlyingType>::max(),
196              "aEnd shouldn't overflow!");
197   return MakeEnumeratedRange(aBegin, static_cast<EnumType>(end + 1));
198 }
199 
200 #ifdef __GNUC__
201 #  pragma GCC diagnostic pop
202 #endif
203 
204 }  // namespace mozilla
205 
206 #endif  // mozilla_EnumeratedRange_h
207