blob: 49b21e71674607e28fcbfe33c2660d88e70e6c6a [file] [log] [blame]
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#ifndef PROTOBUF_HPB_REPEATED_FIELD_ITERATOR_H_
#define PROTOBUF_HPB_REPEATED_FIELD_ITERATOR_H_
#include <cstddef>
#include <cstring>
#include <iterator>
#include <type_traits>
#include "absl/strings/string_view.h"
#include "google/protobuf/hpb/backend/upb/interop.h"
#include "google/protobuf/hpb/hpb.h"
#include "upb/base/string_view.h"
#include "upb/mem/arena.h"
#include "upb/message/array.h"
#include "upb/message/message.h"
namespace hpb {
namespace internal {
// TODO: Implement std iterator for messages
template <typename T>
class RepeatedFieldScalarProxy;
template <typename T>
class RepeatedFieldStringProxy;
struct IteratorTestPeer;
template <typename T>
class Iterator;
template <typename PolicyT>
class ReferenceProxy;
template <typename PolicyT>
class InjectedRelationalsImpl {
using RP = ReferenceProxy<PolicyT>;
using V = typename PolicyT::value_type;
friend bool operator==(RP a, V b) { return static_cast<V>(a) == b; }
friend bool operator==(V a, RP b) { return a == static_cast<V>(b); }
friend bool operator==(RP a, RP b) {
return static_cast<V>(a) == static_cast<V>(b);
}
friend bool operator!=(RP a, V b) { return static_cast<V>(a) != b; }
friend bool operator!=(V a, RP b) { return a != static_cast<V>(b); }
friend bool operator!=(RP a, RP b) {
return static_cast<V>(a) != static_cast<V>(b);
}
friend bool operator<(RP a, V b) { return static_cast<V>(a) < b; }
friend bool operator<(V a, RP b) { return a < static_cast<V>(b); }
friend bool operator<(RP a, RP b) {
return static_cast<V>(a) < static_cast<V>(b);
}
friend bool operator<=(RP a, V b) { return static_cast<V>(a) <= b; }
friend bool operator<=(V a, RP b) { return a <= static_cast<V>(b); }
friend bool operator<=(RP a, RP b) {
return static_cast<V>(a) <= static_cast<V>(b);
}
friend bool operator>(RP a, V b) { return static_cast<V>(a) > b; }
friend bool operator>(V a, RP b) { return a > static_cast<V>(b); }
friend bool operator>(RP a, RP b) {
return static_cast<V>(a) > static_cast<V>(b);
}
friend bool operator>=(RP a, V b) { return static_cast<V>(a) >= b; }
friend bool operator>=(V a, RP b) { return a >= static_cast<V>(b); }
friend bool operator>=(RP a, RP b) {
return static_cast<V>(a) >= static_cast<V>(b);
}
};
class NoInjectedRelationalsImpl {};
// We need to inject relationals for the string references because the
// relationals for string_view are templates and won't allow for implicit
// conversions from ReferenceProxy to string_view before deduction.
template <typename PolicyT>
using InjectedRelationals = std::conditional_t<
std::is_same_v<std::remove_const_t<typename PolicyT::value_type>,
absl::string_view>,
InjectedRelationalsImpl<PolicyT>, NoInjectedRelationalsImpl>;
template <typename PolicyT>
class ReferenceProxy : InjectedRelationals<PolicyT> {
using value_type = typename PolicyT::value_type;
public:
ReferenceProxy(const ReferenceProxy&) = default;
ReferenceProxy& operator=(const ReferenceProxy& other) {
// Assign through the references
// TODO: Make this better for strings to avoid the copy.
it_.Set(other.it_.Get());
return *this;
}
friend void swap(ReferenceProxy a, ReferenceProxy b) { a.it_.swap(b.it_); }
operator value_type() const { return it_.Get(); }
void operator=(const value_type& value) const { it_.Set(value); }
void operator=(value_type&& value) const { it_.Set(std::move(value)); }
Iterator<PolicyT> operator&() const { return Iterator<PolicyT>(it_); }
private:
friend IteratorTestPeer;
friend ReferenceProxy<typename PolicyT::AddConst>;
friend Iterator<PolicyT>;
explicit ReferenceProxy(typename PolicyT::Payload elem) : it_(elem) {}
typename PolicyT::Payload it_;
};
template <template <typename> class PolicyTemplate, typename T>
class ReferenceProxy<PolicyTemplate<const T>>
: InjectedRelationals<PolicyTemplate<const T>> {
using PolicyT = PolicyTemplate<const T>;
using value_type = typename PolicyT::value_type;
public:
ReferenceProxy(ReferenceProxy<PolicyTemplate<T>> p) : it_(p.it_) {}
ReferenceProxy(const ReferenceProxy&) = default;
ReferenceProxy& operator=(const ReferenceProxy&) = delete;
operator value_type() const { return it_.Get(); }
Iterator<PolicyT> operator&() const { return Iterator<PolicyT>(it_); }
private:
friend IteratorTestPeer;
friend Iterator<PolicyT>;
explicit ReferenceProxy(typename PolicyT::Payload elem) : it_(elem) {}
typename PolicyT::Payload it_;
};
template <typename PolicyT>
class Iterator {
public:
using iterator_category = std::random_access_iterator_tag;
using value_type = std::remove_const_t<typename PolicyT::value_type>;
using difference_type = std::ptrdiff_t;
using pointer = Iterator;
using reference =
std::conditional_t<PolicyT::kUseReferenceProxy, ReferenceProxy<PolicyT>,
typename PolicyT::value_type>;
constexpr Iterator() noexcept : it_(nullptr) {}
Iterator(const Iterator& other) = default;
Iterator& operator=(const Iterator& other) = default;
template <
typename P = PolicyT,
typename = std::enable_if_t<std::is_const<typename P::value_type>::value>>
Iterator(const Iterator<typename P::RemoveConst>& other) : it_(other.it_) {}
constexpr reference operator*() const noexcept {
if constexpr (PolicyT::kUseReferenceProxy) {
return reference(it_);
} else {
return it_.Get();
}
}
// No operator-> needed because T is a scalar.
private:
// Hide the internal type.
using iterator = Iterator;
public:
// {inc,dec}rementable
constexpr iterator& operator++() noexcept {
it_.AddOffset(1);
return *this;
}
constexpr iterator operator++(int) noexcept {
auto copy = *this;
++*this;
return copy;
}
constexpr iterator& operator--() noexcept {
it_.AddOffset(-1);
return *this;
}
constexpr iterator operator--(int) noexcept {
auto copy = *this;
--*this;
return copy;
}
// equality_comparable
friend constexpr bool operator==(const iterator& x,
const iterator& y) noexcept {
return x.it_.Index() == y.it_.Index();
}
friend constexpr bool operator!=(const iterator& x,
const iterator& y) noexcept {
return !(x == y);
}
// less_than_comparable
friend constexpr bool operator<(const iterator& x,
const iterator& y) noexcept {
return x.it_.Index() < y.it_.Index();
}
friend constexpr bool operator<=(const iterator& x,
const iterator& y) noexcept {
return !(y < x);
}
friend constexpr bool operator>(const iterator& x,
const iterator& y) noexcept {
return y < x;
}
friend constexpr bool operator>=(const iterator& x,
const iterator& y) noexcept {
return !(x < y);
}
constexpr iterator& operator+=(difference_type d) noexcept {
it_.AddOffset(d);
return *this;
}
constexpr iterator operator+(difference_type d) const noexcept {
auto copy = *this;
copy += d;
return copy;
}
friend constexpr iterator operator+(const difference_type d,
iterator it) noexcept {
return it + d;
}
constexpr iterator& operator-=(difference_type d) noexcept {
it_.AddOffset(-d);
return *this;
}
constexpr iterator operator-(difference_type d) const noexcept {
auto copy = *this;
copy -= d;
return copy;
}
// indexable
constexpr reference operator[](difference_type d) const noexcept {
auto copy = *this;
copy += d;
return *copy;
}
// random access iterator
friend constexpr difference_type operator-(iterator x, iterator y) noexcept {
return x.it_.Index() - y.it_.Index();
}
private:
friend IteratorTestPeer;
friend ReferenceProxy<PolicyT>;
friend Iterator<typename PolicyT::AddConst>;
template <typename U>
friend class RepeatedFieldScalarProxy;
template <typename U>
friend class RepeatedFieldStringProxy;
template <typename U>
friend class RepeatedFieldProxy;
// Create from internal::RepeatedFieldScalarProxy.
explicit Iterator(typename PolicyT::Payload it) noexcept : it_(it) {}
// The internal iterator.
typename PolicyT::Payload it_;
};
template <typename T>
struct ScalarIteratorPolicy {
static constexpr bool kUseReferenceProxy = true;
using value_type = T;
using RemoveConst = ScalarIteratorPolicy<std::remove_const_t<T>>;
using AddConst = ScalarIteratorPolicy<const T>;
struct Payload {
T* value;
void AddOffset(ptrdiff_t offset) { value += offset; }
T Get() const { return *value; }
void Set(T new_value) const { *value = new_value; }
T* Index() const { return value; }
void swap(Payload& other) {
using std::swap;
swap(*value, *other.value);
}
operator typename ScalarIteratorPolicy<const T>::Payload() const {
return {value};
}
};
};
template <typename T>
struct StringIteratorPolicy {
static constexpr bool kUseReferenceProxy = true;
using value_type = T;
using RemoveConst = StringIteratorPolicy<std::remove_const_t<T>>;
using AddConst = StringIteratorPolicy<const T>;
struct Payload {
using Array =
std::conditional_t<std::is_const_v<T>, const upb_Array, upb_Array>;
Array* arr;
upb_Arena* arena;
size_t index;
void AddOffset(ptrdiff_t offset) { index += offset; }
absl::string_view Get() const {
upb_MessageValue message_value = upb_Array_Get(arr, index);
return absl::string_view(message_value.str_val.data,
message_value.str_val.size);
}
void Set(absl::string_view new_value) const {
char* data =
static_cast<char*>(upb_Arena_Malloc(arena, new_value.size()));
memcpy(data, new_value.data(), new_value.size());
upb_MessageValue message_value;
message_value.str_val =
upb_StringView_FromDataAndSize(data, new_value.size());
upb_Array_Set(arr, index, message_value);
}
size_t Index() const { return index; }
void swap(Payload& other) {
upb_MessageValue a = upb_Array_Get(this->arr, this->index);
upb_MessageValue b = upb_Array_Get(other.arr, other.index);
upb_Array_Set(this->arr, this->index, b);
upb_Array_Set(other.arr, other.index, a);
}
operator typename StringIteratorPolicy<const T>::Payload() const {
return {arr, arena, index};
}
};
};
template <typename T>
struct MessageIteratorPolicy {
static constexpr bool kUseReferenceProxy = false;
using value_type = std::conditional_t<std::is_const_v<T>, typename T::CProxy,
typename T::Proxy>;
using RemoveConst = MessageIteratorPolicy<std::remove_const_t<T>>;
using AddConst = MessageIteratorPolicy<const T>;
struct Payload {
using Array =
std::conditional_t<std::is_const_v<T>, const upb_Array, upb_Array>;
upb_Message** arr;
upb_Arena* arena;
void AddOffset(ptrdiff_t offset) { arr += offset; }
auto Get() const {
if constexpr (std::is_const_v<T>) {
return ::hpb::interop::upb::MakeCHandle<
typename std::remove_const_t<T>>(*arr, arena);
} else {
return hpb::interop::upb::MakeHandle<T>(*arr, arena);
}
}
auto Index() const { return arr; }
};
};
} // namespace internal
} // namespace hpb
#endif // PROTOBUF_HPB_REPEATED_FIELD_ITERATOR_H_