1 #ifndef PROTOZERO_ITERATORS_HPP
2 #define PROTOZERO_ITERATORS_HPP
3
4 /*****************************************************************************
5
6 protozero - Minimalistic protocol buffer decoder and encoder in C++.
7
8 This file is from https://github.com/mapbox/protozero where you can find more
9 documentation.
10
11 *****************************************************************************/
12
13 /**
14 * @file iterators.hpp
15 *
16 * @brief Contains the iterators for access to packed repeated fields.
17 */
18
19 #include <cstring>
20 #include <iterator>
21 #include <utility>
22
23 #include <protozero/config.hpp>
24 #include <protozero/varint.hpp>
25
26 #if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
27 # include <protozero/byteswap.hpp>
28 #endif
29
30 namespace protozero {
31
32 /**
33 * A range of iterators based on std::pair. Created from beginning and
34 * end iterators. Used as a return type from some pbf_reader methods
35 * that is easy to use with range-based for loops.
36 */
37 template <typename T, typename P = std::pair<T, T>>
38 class iterator_range :
39 #ifdef PROTOZERO_STRICT_API
40 protected
41 #else
42 public
43 #endif
44 P {
45
46 public:
47
48 /// The type of the iterators in this range.
49 using iterator = T;
50
51 /// The value type of the underlying iterator.
52 using value_type = typename std::iterator_traits<T>::value_type;
53
54 /**
55 * Default constructor. Create empty iterator_range.
56 */
iterator_range()57 constexpr iterator_range() :
58 P(iterator{}, iterator{}) {
59 }
60
61 /**
62 * Create iterator range from two iterators.
63 *
64 * @param first_iterator Iterator to beginning or range.
65 * @param last_iterator Iterator to end or range.
66 */
iterator_range(iterator && first_iterator,iterator && last_iterator)67 constexpr iterator_range(iterator&& first_iterator, iterator&& last_iterator) :
68 P(std::forward<iterator>(first_iterator),
69 std::forward<iterator>(last_iterator)) {
70 }
71
72 /// Return iterator to beginning of range.
begin() const73 constexpr iterator begin() const noexcept {
74 return this->first;
75 }
76
77 /// Return iterator to end of range.
end() const78 constexpr iterator end() const noexcept {
79 return this->second;
80 }
81
82 /// Return iterator to beginning of range.
cbegin() const83 constexpr iterator cbegin() const noexcept {
84 return this->first;
85 }
86
87 /// Return iterator to end of range.
cend() const88 constexpr iterator cend() const noexcept {
89 return this->second;
90 }
91
92 /// Return true if this range is empty.
empty() const93 constexpr std::size_t empty() const noexcept {
94 return begin() == end();
95 }
96
97 /**
98 * Get element at the beginning of the range.
99 *
100 * @pre Range must not be empty.
101 */
front() const102 value_type front() const {
103 protozero_assert(!empty());
104 return *(this->first);
105 }
106
107 /**
108 * Advance beginning of range by one.
109 *
110 * @pre Range must not be empty.
111 */
drop_front()112 void drop_front() {
113 protozero_assert(!empty());
114 ++this->first;
115 }
116
117 /**
118 * Swap the contents of this range with the other.
119 *
120 * @param other Other range to swap data with.
121 */
swap(iterator_range & other)122 void swap(iterator_range& other) noexcept {
123 using std::swap;
124 swap(this->first, other.first);
125 swap(this->second, other.second);
126 }
127
128 }; // struct iterator_range
129
130 /**
131 * Swap two iterator_ranges.
132 *
133 * @param lhs First range.
134 * @param rhs Second range.
135 */
136 template <typename T>
swap(iterator_range<T> & lhs,iterator_range<T> & rhs)137 inline void swap(iterator_range<T>& lhs, iterator_range<T>& rhs) noexcept {
138 lhs.swap(rhs);
139 }
140
141 /**
142 * A forward iterator used for accessing packed repeated fields of fixed
143 * length (fixed32, sfixed32, float, double).
144 */
145 template <typename T>
146 class const_fixed_iterator {
147
148 /// Pointer to current iterator position
149 const char* m_data;
150
151 /// Pointer to end iterator position
152 const char* m_end;
153
154 public:
155
156 using iterator_category = std::forward_iterator_tag;
157 using value_type = T;
158 using difference_type = std::ptrdiff_t;
159 using pointer = value_type*;
160 using reference = value_type&;
161
const_fixed_iterator()162 const_fixed_iterator() noexcept :
163 m_data(nullptr),
164 m_end(nullptr) {
165 }
166
const_fixed_iterator(const char * data,const char * end)167 const_fixed_iterator(const char* data, const char* end) noexcept :
168 m_data(data),
169 m_end(end) {
170 }
171
172 const_fixed_iterator(const const_fixed_iterator&) noexcept = default;
173 const_fixed_iterator(const_fixed_iterator&&) noexcept = default;
174
175 const_fixed_iterator& operator=(const const_fixed_iterator&) noexcept = default;
176 const_fixed_iterator& operator=(const_fixed_iterator&&) noexcept = default;
177
178 ~const_fixed_iterator() noexcept = default;
179
operator *() const180 value_type operator*() const {
181 value_type result;
182 std::memcpy(&result, m_data, sizeof(value_type));
183 #if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
184 detail::byteswap_inplace(&result);
185 #endif
186 return result;
187 }
188
operator ++()189 const_fixed_iterator& operator++() {
190 m_data += sizeof(value_type);
191 return *this;
192 }
193
operator ++(int)194 const_fixed_iterator operator++(int) {
195 const const_fixed_iterator tmp(*this);
196 ++(*this);
197 return tmp;
198 }
199
operator ==(const const_fixed_iterator & rhs) const200 bool operator==(const const_fixed_iterator& rhs) const noexcept {
201 return m_data == rhs.m_data && m_end == rhs.m_end;
202 }
203
operator !=(const const_fixed_iterator & rhs) const204 bool operator!=(const const_fixed_iterator& rhs) const noexcept {
205 return !(*this == rhs);
206 }
207
208 }; // class const_fixed_iterator
209
210 /**
211 * A forward iterator used for accessing packed repeated varint fields
212 * (int32, uint32, int64, uint64, bool, enum).
213 */
214 template <typename T>
215 class const_varint_iterator {
216
217 protected:
218
219 /// Pointer to current iterator position
220 const char* m_data;
221
222 /// Pointer to end iterator position
223 const char* m_end;
224
225 public:
226
227 using iterator_category = std::forward_iterator_tag;
228 using value_type = T;
229 using difference_type = std::ptrdiff_t;
230 using pointer = value_type*;
231 using reference = value_type&;
232
const_varint_iterator()233 const_varint_iterator() noexcept :
234 m_data(nullptr),
235 m_end(nullptr) {
236 }
237
const_varint_iterator(const char * data,const char * end)238 const_varint_iterator(const char* data, const char* end) noexcept :
239 m_data(data),
240 m_end(end) {
241 }
242
243 const_varint_iterator(const const_varint_iterator&) noexcept = default;
244 const_varint_iterator(const_varint_iterator&&) noexcept = default;
245
246 const_varint_iterator& operator=(const const_varint_iterator&) noexcept = default;
247 const_varint_iterator& operator=(const_varint_iterator&&) noexcept = default;
248
249 ~const_varint_iterator() noexcept = default;
250
operator *() const251 value_type operator*() const {
252 const char* d = m_data; // will be thrown away
253 return static_cast<value_type>(decode_varint(&d, m_end));
254 }
255
operator ++()256 const_varint_iterator& operator++() {
257 skip_varint(&m_data, m_end);
258 return *this;
259 }
260
operator ++(int)261 const_varint_iterator operator++(int) {
262 const const_varint_iterator tmp(*this);
263 ++(*this);
264 return tmp;
265 }
266
operator ==(const const_varint_iterator & rhs) const267 bool operator==(const const_varint_iterator& rhs) const noexcept {
268 return m_data == rhs.m_data && m_end == rhs.m_end;
269 }
270
operator !=(const const_varint_iterator & rhs) const271 bool operator!=(const const_varint_iterator& rhs) const noexcept {
272 return !(*this == rhs);
273 }
274
275 }; // class const_varint_iterator
276
277 /**
278 * A forward iterator used for accessing packed repeated svarint fields
279 * (sint32, sint64).
280 */
281 template <typename T>
282 class const_svarint_iterator : public const_varint_iterator<T> {
283
284 public:
285
286 using iterator_category = std::forward_iterator_tag;
287 using value_type = T;
288 using difference_type = std::ptrdiff_t;
289 using pointer = value_type*;
290 using reference = value_type&;
291
const_svarint_iterator()292 const_svarint_iterator() noexcept :
293 const_varint_iterator<T>() {
294 }
295
const_svarint_iterator(const char * data,const char * end)296 const_svarint_iterator(const char* data, const char* end) noexcept :
297 const_varint_iterator<T>(data, end) {
298 }
299
300 const_svarint_iterator(const const_svarint_iterator&) = default;
301 const_svarint_iterator(const_svarint_iterator&&) = default;
302
303 const_svarint_iterator& operator=(const const_svarint_iterator&) = default;
304 const_svarint_iterator& operator=(const_svarint_iterator&&) = default;
305
306 ~const_svarint_iterator() = default;
307
operator *() const308 value_type operator*() const {
309 const char* d = this->m_data; // will be thrown away
310 return static_cast<value_type>(decode_zigzag64(decode_varint(&d, this->m_end)));
311 }
312
operator ++()313 const_svarint_iterator& operator++() {
314 skip_varint(&this->m_data, this->m_end);
315 return *this;
316 }
317
operator ++(int)318 const_svarint_iterator operator++(int) {
319 const const_svarint_iterator tmp(*this);
320 ++(*this);
321 return tmp;
322 }
323
324 }; // class const_svarint_iterator
325
326 } // end namespace protozero
327
328 #endif // PROTOZERO_ITERATORS_HPP
329