summaryrefslogtreecommitdiffstats
path: root/mfbt/EnumeratedRange.h
blob: ef0e6910ab5cd891b39173e5227385ffc606ffdf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* Iterator over contiguous enum values */

/*
 * Implements generator functions that create a range to iterate over the values
 * of a scoped or unscoped enum. Unlike IntegerRange, which can only function on
 * the underlying integral type, the elements of the generated sequence will
 * have the type of the enum in question.
 *
 * Note that the enum values should be contiguous in the iterated range;
 * unfortunately there exists no way for EnumeratedRange to enforce this
 * either dynamically or at compile time.
 */

#ifndef mozilla_EnumeratedRange_h
#define mozilla_EnumeratedRange_h

#include <limits>
#include <type_traits>

#include "mozilla/Assertions.h"
#include "mozilla/ReverseIterator.h"

namespace mozilla {

namespace detail {

template <typename EnumTypeT>
class EnumeratedIterator {
 public:
  typedef typename std::underlying_type<EnumTypeT>::type IntTypeT;

  template <typename EnumType>
  constexpr explicit EnumeratedIterator(EnumType aCurrent)
      : mCurrent(aCurrent) {}

  template <typename EnumType>
  explicit EnumeratedIterator(const EnumeratedIterator<EnumType>& aOther)
      : mCurrent(aOther.mCurrent) {}

  EnumTypeT operator*() const { return mCurrent; }

  /* Increment and decrement operators */

  EnumeratedIterator& operator++() {
    mCurrent = EnumTypeT(IntTypeT(mCurrent) + IntTypeT(1));
    return *this;
  }
  EnumeratedIterator& operator--() {
    mCurrent = EnumTypeT(IntTypeT(mCurrent) - IntTypeT(1));
    return *this;
  }
  EnumeratedIterator operator++(int) {
    auto ret = *this;
    mCurrent = EnumTypeT(IntTypeT(mCurrent) + IntTypeT(1));
    return ret;
  }
  EnumeratedIterator operator--(int) {
    auto ret = *this;
    mCurrent = EnumTypeT(IntTypeT(mCurrent) - IntTypeT(1));
    return ret;
  }

  /* Comparison operators */

  template <typename EnumType>
  friend bool operator==(const EnumeratedIterator<EnumType>& aIter1,
                         const EnumeratedIterator<EnumType>& aIter2);
  template <typename EnumType>
  friend bool operator!=(const EnumeratedIterator<EnumType>& aIter1,
                         const EnumeratedIterator<EnumType>& aIter2);
  template <typename EnumType>
  friend bool operator<(const EnumeratedIterator<EnumType>& aIter1,
                        const EnumeratedIterator<EnumType>& aIter2);
  template <typename EnumType>
  friend bool operator<=(const EnumeratedIterator<EnumType>& aIter1,
                         const EnumeratedIterator<EnumType>& aIter2);
  template <typename EnumType>
  friend bool operator>(const EnumeratedIterator<EnumType>& aIter1,
                        const EnumeratedIterator<EnumType>& aIter2);
  template <typename EnumType>
  friend bool operator>=(const EnumeratedIterator<EnumType>& aIter1,
                         const EnumeratedIterator<EnumType>& aIter2);

 private:
  EnumTypeT mCurrent;
};

template <typename EnumType>
bool operator==(const EnumeratedIterator<EnumType>& aIter1,
                const EnumeratedIterator<EnumType>& aIter2) {
  return aIter1.mCurrent == aIter2.mCurrent;
}

template <typename EnumType>
bool operator!=(const EnumeratedIterator<EnumType>& aIter1,
                const EnumeratedIterator<EnumType>& aIter2) {
  return aIter1.mCurrent != aIter2.mCurrent;
}

template <typename EnumType>
bool operator<(const EnumeratedIterator<EnumType>& aIter1,
               const EnumeratedIterator<EnumType>& aIter2) {
  return aIter1.mCurrent < aIter2.mCurrent;
}

template <typename EnumType>
bool operator<=(const EnumeratedIterator<EnumType>& aIter1,
                const EnumeratedIterator<EnumType>& aIter2) {
  return aIter1.mCurrent <= aIter2.mCurrent;
}

template <typename EnumType>
bool operator>(const EnumeratedIterator<EnumType>& aIter1,
               const EnumeratedIterator<EnumType>& aIter2) {
  return aIter1.mCurrent > aIter2.mCurrent;
}

template <typename EnumType>
bool operator>=(const EnumeratedIterator<EnumType>& aIter1,
                const EnumeratedIterator<EnumType>& aIter2) {
  return aIter1.mCurrent >= aIter2.mCurrent;
}

template <typename EnumTypeT>
class EnumeratedRange {
 public:
  typedef EnumeratedIterator<EnumTypeT> iterator;
  typedef EnumeratedIterator<EnumTypeT> const_iterator;
  typedef ReverseIterator<iterator> reverse_iterator;
  typedef ReverseIterator<const_iterator> const_reverse_iterator;

  template <typename EnumType>
  constexpr EnumeratedRange(EnumType aBegin, EnumType aEnd)
      : mBegin(aBegin), mEnd(aEnd) {}

  iterator begin() const { return iterator(mBegin); }
  const_iterator cbegin() const { return begin(); }
  iterator end() const { return iterator(mEnd); }
  const_iterator cend() const { return end(); }
  reverse_iterator rbegin() const { return reverse_iterator(mEnd); }
  const_reverse_iterator crbegin() const { return rbegin(); }
  reverse_iterator rend() const { return reverse_iterator(mBegin); }
  const_reverse_iterator crend() const { return rend(); }

 private:
  EnumTypeT mBegin;
  EnumTypeT mEnd;
};

}  // namespace detail

#ifdef __GNUC__
// Enums can have an unsigned underlying type, which makes some of the
// comparisons below always true or always false. Temporarily disable
// -Wtype-limits to avoid breaking -Werror builds.
#  pragma GCC diagnostic push
#  pragma GCC diagnostic ignored "-Wtype-limits"
#endif

// Create a range to iterate from aBegin to aEnd, exclusive.
template <typename EnumType>
constexpr detail::EnumeratedRange<EnumType> MakeEnumeratedRange(EnumType aBegin,
                                                                EnumType aEnd) {
  MOZ_ASSERT(aBegin <= aEnd, "Cannot generate invalid, unbounded range!");
  return detail::EnumeratedRange<EnumType>(aBegin, aEnd);
}

// Create a range to iterate from EnumType(0) to aEnd, exclusive. EnumType(0)
// should exist, but note that there is no way for us to ensure that it does!
template <typename EnumType>
constexpr detail::EnumeratedRange<EnumType> MakeEnumeratedRange(EnumType aEnd) {
  return MakeEnumeratedRange(EnumType(0), aEnd);
}

// Create a range to iterate from aBegin to aEnd, inclusive.
//
// NOTE: This internally constructs a value that is one past `aEnd`, so the
// enumeration needs to either have a fixed underlying type, or `aEnd + 1` must
// be inside the range of the enumeration, in order to not be undefined
// behavior.
//
// See bug 1614512.
template <typename EnumType>
constexpr detail::EnumeratedRange<EnumType> MakeInclusiveEnumeratedRange(
    EnumType aBegin, EnumType aEnd) {
  using EnumUnderlyingType = typename std::underlying_type_t<EnumType>;
  const auto end = static_cast<EnumUnderlyingType>(aEnd);

  MOZ_ASSERT(end != std::numeric_limits<EnumUnderlyingType>::max(),
             "aEnd shouldn't overflow!");
  return MakeEnumeratedRange(aBegin, static_cast<EnumType>(end + 1));
}

template <typename EnumType>
constexpr auto MakeInclusiveEnumeratedRange(EnumType aEnd) {
  return MakeInclusiveEnumeratedRange(EnumType{0}, aEnd);
}

#ifdef __GNUC__
#  pragma GCC diagnostic pop
#endif

}  // namespace mozilla

#endif  // mozilla_EnumeratedRange_h