#ifndef PROTOZERO_ITERATORS_HPP #define PROTOZERO_ITERATORS_HPP /***************************************************************************** protozero - Minimalistic protocol buffer decoder and encoder in C++. This file is from https://github.com/mapbox/protozero where you can find more documentation. *****************************************************************************/ /** * @file iterators.hpp * * @brief Contains the iterators for access to packed repeated fields. */ #include #include #if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN # include #endif #include #include #include #include namespace protozero { /** * A range of iterators based on std::pair. Created from beginning and * end iterators. Used as a return type from some pbf_reader methods * that is easy to use with range-based for loops. */ template > class iterator_range : #ifdef PROTOZERO_STRICT_API protected #else public #endif P { public: /// The type of the iterators in this range. using iterator = T; /// The value type of the underlying iterator. using value_type = typename std::iterator_traits::value_type; /** * Default constructor. Create empty iterator_range. */ constexpr iterator_range() : P(iterator{}, iterator{}) { } /** * Create iterator range from two iterators. * * @param first_iterator Iterator to beginning of range. * @param last_iterator Iterator to end of range. */ constexpr iterator_range(iterator&& first_iterator, iterator&& last_iterator) : P(std::forward(first_iterator), std::forward(last_iterator)) { } /// Return iterator to beginning of range. constexpr iterator begin() const noexcept { return this->first; } /// Return iterator to end of range. constexpr iterator end() const noexcept { return this->second; } /// Return iterator to beginning of range. constexpr iterator cbegin() const noexcept { return this->first; } /// Return iterator to end of range. constexpr iterator cend() const noexcept { return this->second; } /** * Return true if this range is empty. * * Complexity: Constant. */ constexpr bool empty() const noexcept { return begin() == end(); } /** * Get the size of the range, ie the number of elements it contains. * * Complexity: Constant or linear depending on the underlaying iterator. */ std::size_t size() const noexcept { return static_cast(std::distance(begin(), end())); } /** * Get element at the beginning of the range. * * @pre Range must not be empty. */ value_type front() const { protozero_assert(!empty()); return *(this->first); } /** * Advance beginning of range by one. * * @pre Range must not be empty. */ void drop_front() { protozero_assert(!empty()); ++this->first; } /** * Swap the contents of this range with the other. * * @param other Other range to swap data with. */ void swap(iterator_range& other) noexcept { using std::swap; swap(this->first, other.first); swap(this->second, other.second); } }; // struct iterator_range /** * Swap two iterator_ranges. * * @param lhs First range. * @param rhs Second range. */ template inline void swap(iterator_range& lhs, iterator_range& rhs) noexcept { lhs.swap(rhs); } /** * A forward iterator used for accessing packed repeated fields of fixed * length (fixed32, sfixed32, float, double). */ template class const_fixed_iterator { /// Pointer to current iterator position const char* m_data = nullptr; public: using iterator_category = std::random_access_iterator_tag; using value_type = T; using difference_type = std::ptrdiff_t; using pointer = value_type*; using reference = value_type&; const_fixed_iterator() noexcept = default; explicit const_fixed_iterator(const char* data) noexcept : m_data(data) { } const_fixed_iterator(const const_fixed_iterator&) noexcept = default; const_fixed_iterator(const_fixed_iterator&&) noexcept = default; const_fixed_iterator& operator=(const const_fixed_iterator&) noexcept = default; const_fixed_iterator& operator=(const_fixed_iterator&&) noexcept = default; ~const_fixed_iterator() noexcept = default; value_type operator*() const { value_type result; std::memcpy(&result, m_data, sizeof(value_type)); #if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN detail::byteswap_inplace(&result); #endif return result; } const_fixed_iterator& operator++() noexcept { m_data += sizeof(value_type); return *this; } const_fixed_iterator operator++(int) noexcept { const const_fixed_iterator tmp{*this}; ++(*this); return tmp; } bool operator==(const_fixed_iterator rhs) const noexcept { return m_data == rhs.m_data; } bool operator!=(const_fixed_iterator rhs) const noexcept { return !(*this == rhs); } const_fixed_iterator& operator--() noexcept { m_data -= sizeof(value_type); return *this; } const_fixed_iterator operator--(int) noexcept { const const_fixed_iterator tmp{*this}; --(*this); return tmp; } friend bool operator<(const_fixed_iterator lhs, const_fixed_iterator rhs) noexcept { return lhs.m_data < rhs.m_data; } friend bool operator>(const_fixed_iterator lhs, const_fixed_iterator rhs) noexcept { return rhs < lhs; } friend bool operator<=(const_fixed_iterator lhs, const_fixed_iterator rhs) noexcept { return !(lhs > rhs); } friend bool operator>=(const_fixed_iterator lhs, const_fixed_iterator rhs) noexcept { return !(lhs < rhs); } const_fixed_iterator& operator+=(difference_type val) noexcept { m_data += (sizeof(value_type) * val); return *this; } friend const_fixed_iterator operator+(const_fixed_iterator lhs, difference_type rhs) noexcept { const_fixed_iterator tmp{lhs}; tmp.m_data += (sizeof(value_type) * rhs); return tmp; } friend const_fixed_iterator operator+(difference_type lhs, const_fixed_iterator rhs) noexcept { const_fixed_iterator tmp{rhs}; tmp.m_data += (sizeof(value_type) * lhs); return tmp; } const_fixed_iterator& operator-=(difference_type val) noexcept { m_data -= (sizeof(value_type) * val); return *this; } friend const_fixed_iterator operator-(const_fixed_iterator lhs, difference_type rhs) noexcept { const_fixed_iterator tmp{lhs}; tmp.m_data -= (sizeof(value_type) * rhs); return tmp; } friend difference_type operator-(const_fixed_iterator lhs, const_fixed_iterator rhs) noexcept { return static_cast(lhs.m_data - rhs.m_data) / static_cast(sizeof(T)); } value_type operator[](difference_type n) const noexcept { return *(*this + n); } }; // class const_fixed_iterator /** * A forward iterator used for accessing packed repeated varint fields * (int32, uint32, int64, uint64, bool, enum). */ template class const_varint_iterator { protected: /// Pointer to current iterator position const char* m_data = nullptr; /// Pointer to end iterator position const char* m_end = nullptr; public: using iterator_category = std::forward_iterator_tag; using value_type = T; using difference_type = std::ptrdiff_t; using pointer = value_type*; using reference = value_type&; static difference_type distance(const_varint_iterator begin, const_varint_iterator end) noexcept { // We know that each varint contains exactly one byte with the most // significant bit not set. We can use this to quickly figure out // how many varints there are without actually decoding the varints. return std::count_if(begin.m_data, end.m_data, [](char c) noexcept { return (static_cast(c) & 0x80u) == 0; }); } const_varint_iterator() noexcept = default; const_varint_iterator(const char* data, const char* end) noexcept : m_data(data), m_end(end) { } const_varint_iterator(const const_varint_iterator&) noexcept = default; const_varint_iterator(const_varint_iterator&&) noexcept = default; const_varint_iterator& operator=(const const_varint_iterator&) noexcept = default; const_varint_iterator& operator=(const_varint_iterator&&) noexcept = default; ~const_varint_iterator() noexcept = default; value_type operator*() const { const char* d = m_data; // will be thrown away return static_cast(decode_varint(&d, m_end)); } const_varint_iterator& operator++() { skip_varint(&m_data, m_end); return *this; } const_varint_iterator operator++(int) { const const_varint_iterator tmp{*this}; ++(*this); return tmp; } bool operator==(const const_varint_iterator& rhs) const noexcept { return m_data == rhs.m_data && m_end == rhs.m_end; } bool operator!=(const const_varint_iterator& rhs) const noexcept { return !(*this == rhs); } }; // class const_varint_iterator /** * A forward iterator used for accessing packed repeated svarint fields * (sint32, sint64). */ template class const_svarint_iterator : public const_varint_iterator { public: using iterator_category = std::forward_iterator_tag; using value_type = T; using difference_type = std::ptrdiff_t; using pointer = value_type*; using reference = value_type&; const_svarint_iterator() noexcept : const_varint_iterator() { } const_svarint_iterator(const char* data, const char* end) noexcept : const_varint_iterator(data, end) { } const_svarint_iterator(const const_svarint_iterator&) = default; const_svarint_iterator(const_svarint_iterator&&) noexcept = default; const_svarint_iterator& operator=(const const_svarint_iterator&) = default; const_svarint_iterator& operator=(const_svarint_iterator&&) noexcept = default; ~const_svarint_iterator() = default; value_type operator*() const { const char* d = this->m_data; // will be thrown away return static_cast(decode_zigzag64(decode_varint(&d, this->m_end))); } const_svarint_iterator& operator++() { skip_varint(&this->m_data, this->m_end); return *this; } const_svarint_iterator operator++(int) { const const_svarint_iterator tmp{*this}; ++(*this); return tmp; } }; // class const_svarint_iterator } // end namespace protozero namespace std { // Specialize std::distance for all the protozero iterators. Because // functions can't be partially specialized, we have to do this for // every value_type we are using. template <> inline typename protozero::const_varint_iterator::difference_type distance>(protozero::const_varint_iterator first, // NOLINT(readability-inconsistent-declaration-parameter-name) protozero::const_varint_iterator last) { return protozero::const_varint_iterator::distance(first, last); } template <> inline typename protozero::const_varint_iterator::difference_type distance>(protozero::const_varint_iterator first, // NOLINT(readability-inconsistent-declaration-parameter-name) protozero::const_varint_iterator last) { return protozero::const_varint_iterator::distance(first, last); } template <> inline typename protozero::const_varint_iterator::difference_type distance>(protozero::const_varint_iterator first, // NOLINT(readability-inconsistent-declaration-parameter-name) protozero::const_varint_iterator last) { return protozero::const_varint_iterator::distance(first, last); } template <> inline typename protozero::const_varint_iterator::difference_type distance>(protozero::const_varint_iterator first, // NOLINT(readability-inconsistent-declaration-parameter-name) protozero::const_varint_iterator last) { return protozero::const_varint_iterator::distance(first, last); } template <> inline typename protozero::const_svarint_iterator::difference_type distance>(protozero::const_svarint_iterator first, // NOLINT(readability-inconsistent-declaration-parameter-name) protozero::const_svarint_iterator last) { return protozero::const_svarint_iterator::distance(first, last); } template <> inline typename protozero::const_svarint_iterator::difference_type distance>(protozero::const_svarint_iterator first, // NOLINT(readability-inconsistent-declaration-parameter-name) protozero::const_svarint_iterator last) { return protozero::const_svarint_iterator::distance(first, last); } } // end namespace std #endif // PROTOZERO_ITERATORS_HPP