parselink-old/include/parselink/msgpack/token/views.h
Kurt Sassenrath 1c7047e314 Implement token map_view, additional formatters.
msgpack::map_view can be used to iterate, pair-wise, over a range of
msgpack::token. It will immediately return if the first token is not a
map, and will skip over nested map/arrays.

Note for the future: It will be handy to be able to get the subspan
corresponding to the nested map/array. Will think about how to solve
that later.

Begin incorporating map_view into the server.

Add formatters for std::byte, dynamic theme for bool, and spans thereof.
Maybe switch to range?
2023-10-12 14:52:06 -07:00

139 lines
4.5 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 <fmt/format.h>
#include <ranges>
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 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<V, V>;
using reference = std::pair<base_reference, base_reference>;
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_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_;
}
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