parselink-old/include/parselink/msgpack/token/views.h

138 lines
4.2 KiB
C++

//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: msgpack
//
// Token view utilities.
//
// MessagePack maps and arrays are nested, and the token reader only parses
// out the type. This file provides utilities for iterating over these
// "container" formats without incurring additional overhead on the parser when
// it is not needed.
//
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
#ifndef msgpack_token_views_f19c250e782ed51c
#define msgpack_token_views_f19c250e782ed51c
#include "type.h"
#include <bits/iterator_concepts.h>
#include <ranges>
#include <fmt/format.h>
namespace msgpack {
template <std::ranges::view V>
requires std::ranges::input_range<V>
&& std::same_as<token, std::ranges::range_value_t<V>>
struct map_view : public std::ranges::view_interface<map_view<V>> {
public:
class sentinel;
class iterator {
friend class sentinel;
using base_iterator = std::ranges::iterator_t<V>;
using base_sentinel = std::ranges::sentinel_t<V>;
using base_value_type = std::ranges::range_value_t<V>;
using base_reference = std::ranges::range_reference_t<V>;
base_iterator next(base_iterator current, std::size_t n = 1) {
while (n && current != std::ranges::end(*base_)) {
if (auto m = current->template get<map_desc>(); m) {
n += m->count * 2;
} else if (auto m = current->template get<array_desc>(); m) {
n += m->count;
}
++current;
--n;
}
return current;
}
public:
using value_type = std::pair<base_value_type, base_value_type>;
using reference = std::pair<base_reference, base_reference>;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
iterator() = default;
iterator(V const& base)
: base_{&base}
, k_{std::ranges::begin(base)} {
// Ensure that k_ points to a map_desc. If not, then we
// effectively treat this as the end.
if (k_->type() == msgpack::format::type::map) {
remaining_ = k_->template get<msgpack::map_desc>()->count + 1;
// Advance to the first entry in the map.
++k_;
v_ = next(k_);
}
}
[[nodiscard]] reference operator*() const { return {*k_, *v_}; }
iterator& operator++() {
k_ = next(v_);
v_ = next(k_);
--remaining_;
return *this;
}
[[nodiscard]] iterator operator++(int) {
auto tmp = *this;
++(*this);
return tmp;
}
[[nodiscard]] bool operator==(iterator const& rhs) const {
return k_ == rhs.remaining_ && base_ == rhs.base_;
}
private:
V const* base_{};
base_iterator k_{};
base_iterator v_{};
std::size_t remaining_{};
};
class sentinel {
public:
[[nodiscard]] bool operator==(sentinel const&) const { return true; }
[[nodiscard]] bool operator==(iterator const& rhs) const {
return rhs.remaining_ == 0
|| rhs.k_ == std::ranges::end(*rhs.base_);
}
};
constexpr map_view() noexcept = default;
constexpr map_view(V base)
: base_{std::move(base)} {}
[[nodiscard]] constexpr iterator begin() const { return {base_}; }
[[nodiscard]] constexpr sentinel end() const { return {}; }
private:
V base_;
};
template <class Range>
map_view(Range&&) -> map_view<std::views::all_t<Range>>;
} // namespace msgpack
#endif // msgpack_token_views_f19c250e782ed51c