#ifndef GOOGLE_PROTOBUF_REPEATED_FIELD_PROXY_H__
#define GOOGLE_PROTOBUF_REPEATED_FIELD_PROXY_H__

#include <algorithm>
#include <cstddef>
#include <iterator>
#include <string>
#include <type_traits>
#include <utility>

#include "absl/log/absl_check.h"
#include "absl/strings/cord.h"
#include "absl/strings/string_view.h"
#include "google/protobuf/raw_ptr.h"
#include "google/protobuf/repeated_field.h"
#include "google/protobuf/repeated_field_proxy_iterator.h"
#include "google/protobuf/repeated_field_proxy_traits.h"
#include "google/protobuf/repeated_ptr_field.h"


// Must be included last.
#include "google/protobuf/port_def.inc"

namespace google {
namespace protobuf {

template <typename ElementType>
class RepeatedFieldProxy;

namespace internal {

template <typename ElementType>
class RepeatedFieldOrProxy;

template <typename ElementType, bool kOrProxy>
class MutableRepeatedFieldProxyImpl;
template <typename ElementType, bool kOrProxy>
class ConstRepeatedFieldProxyImpl;
template <typename ElementType, bool kOrProxy>
class RepeatedFieldProxyInternalPrivateAccessHelper;

namespace string_util {

template <typename StringType, typename T>
inline void CopyToString(StringType& element, T&& value) {
  // We want to explicitly enumerate all types we accept for constructing
  // strings, otherwise this would be a subtle place we'd be leaking the
  // `std::string` backing of `string_view` repeated fields. I.e. whatever new
  // backing we use for `string_view` fields would have to support all
  // `operator=` overloads that `std::string` has.
  //
  // With an explicit list, we no longer have this dependency.
  if constexpr (std::is_convertible_v<T, absl::string_view>) {
    element = absl::implicit_cast<absl::string_view>(value);
  } else if constexpr (std::is_convertible_v<T, const std::string&>) {
    element = absl::implicit_cast<const std::string&>(value);
  } else if constexpr (std::is_convertible_v<T, const char*>) {
    element = absl::implicit_cast<const char*>(value);
  } else {
    element = {value.data(), value.size()};
  }
}

template <typename T>
inline void SetElement(std::string& element, T&& value) {
  if constexpr (std::is_same_v<T&&, std::string&&>) {
    element = std::forward<T>(value);
  } else if constexpr (std::is_convertible_v<T, const absl::Cord&>) {
    const absl::Cord& cord = std::forward<T>(value);
    absl::CopyCordToString(cord, &element);
  } else {
    CopyToString(element, std::forward<T>(value));
  }
}

template <typename T>
inline void SetElement(absl::Cord& element, T&& value) {
  if constexpr (std::is_same_v<T&&, absl::Cord&&>) {
    element = std::forward<T>(value);
  } else if constexpr (std::is_convertible_v<T, const absl::Cord&>) {
    element = absl::implicit_cast<const absl::Cord&>(std::forward<T>(value));
  } else {
    CopyToString(element, std::forward<T>(value));
  }
}

}  // namespace string_util

// The base class for both mutable and const repeated field proxies. Implements
// all of the common methods and dependent types for both classes.
//
// Class structure:
//
// internal {- - - - - - - - - - - - - - - - - - - - - - -
//             RepeatedFieldProxyBase
//              |                   |
//   MutableRepeatedFieldProxyImpl  |
//              |                   |
//              |         ConstRepeatedFieldProxyImpl
// } - - - - - -|- - - - - - - - - - - - | - - - - - - - -
//   RepeatedFieldProxy<E>,              |
//   RepeatedFieldOrProxy<E>             |
//                          RepeatedFieldProxy<const E>,
//                          RepeatedFieldOrProxy<const E>
//
// RepeatedFieldProxyBase has the common implementation and interface between
// both mutable and const proxies, and `Mutable*Impl` and `Const*Impl` have the
// const/mutable-specific implementations + interfaces.
//
// Both `RepeatedFieldProxy` and `RepeatedFieldOrProxy` comprise the public
// API and are thin wrappers around `Mutable*Impl` and `Const*Impl`,
// implementing only the constructors and APIs particular to them.
template <typename ElementType, bool kOrProxy>
class RepeatedFieldProxyBase {
 protected:
  // If true, this is a view into a repeated field, meaning neither the elements
  // nor the container can be modified. If false, both the elements and the
  // container can be modified.
  static constexpr bool kIsConst = std::is_const_v<ElementType>;
  using Traits = RepeatedFieldTraits<std::remove_const_t<ElementType>>;
  using RepeatedFieldType = typename Traits::type;
  using ConstQualifiedRepeatedFieldType =
      std::conditional_t<kIsConst, const RepeatedFieldType, RepeatedFieldType>;

 public:
  using value_type = std::remove_const_t<ElementType>;
  using size_type = size_t;
  using difference_type = ptrdiff_t;
  using const_reference = typename Traits::const_reference;

  using const_iterator =
      internal::RepeatedFieldProxyIteratorImpl<const ElementType,
                                               /*kReverse=*/false, kOrProxy>;
  using iterator =
      internal::RepeatedFieldProxyIteratorImpl<ElementType,
                                               /*kReverse=*/false, kOrProxy>;
  using const_reverse_iterator =
      internal::RepeatedFieldProxyIteratorImpl<const ElementType,
                                               /*kReverse=*/true, kOrProxy>;
  using reverse_iterator =
      internal::RepeatedFieldProxyIteratorImpl<ElementType,
                                               /*kReverse=*/true, kOrProxy>;

  // Allow explicit conversion to the backing repeated field type. This will
  // perform a deep copy of the repeated field backed by this proxy.
  //
  // Note that this exposes `RepeatedFieldType`, but will be kept around for
  // backwards compatibility with code that uses `RepeatedField`s or
  // `RepeatedPtrField`s directly. We may freely change `RepeatedFieldType` so
  // long as we maintain this explicit conversion to the legacy container types.
  explicit operator RepeatedFieldType() const {
    return RepeatedFieldType(field());
  }

  RepeatedFieldProxyBase(const RepeatedFieldProxyBase&) = default;

  // Assignment of repeated field proxies simply rebinds the proxy to a
  // different repeated field. It does not modify the underlying field.
  //
  // Copying should be done through `assign()`.
  RepeatedFieldProxyBase& operator=(const RepeatedFieldProxyBase&) = default;

  ~RepeatedFieldProxyBase() = default;

  // Returns true if the repeated field has no elements (size == 0).
  [[nodiscard]] bool empty() const { return field().empty(); }
  // Returns the number of elements in the repeated field.
  [[nodiscard]] size_type size() const {
    return static_cast<size_type>(field().size());
  }

  // Returns a const reference or view into the element at the given index,
  // performing bounds checking in accordance with `bounds_check_mode_*`.
  [[nodiscard]] const_reference get(size_type index) const {
    return field()[index];
  }

  [[nodiscard]] const_iterator cbegin() const { return begin(); }
  [[nodiscard]] const_iterator cend() const { return end(); }
  [[nodiscard]] iterator begin() const { return iterator(field().begin()); }
  [[nodiscard]] iterator end() const { return iterator(field().end()); }
  [[nodiscard]] reverse_iterator rbegin() const {
    return reverse_iterator(end());
  }
  [[nodiscard]] reverse_iterator rend() const {
    return reverse_iterator(begin());
  }

 protected:
  explicit RepeatedFieldProxyBase(ConstQualifiedRepeatedFieldType& field)
      : field_(&field) {}

  ConstQualifiedRepeatedFieldType& field() const { return *field_; }

 private:
  ConstQualifiedRepeatedFieldType* PROTOBUF_NONNULL field_;
};

// The following classes are used to specialize methods of `RepeatedFieldProxy`
// based on the element type. Most methods do not need specialization, since
// they look similar for all element types, maybe only differing in whether
// `const_reference` resolves to a `const T&` or some value type like
// `absl::string_view`.
//
// For methods that do have a different signature based on the element type, we
// make a `*With<MethodName>` class that defines only that method, specialized
// on the element type using whatever conditions make sense for the method. We
// then inherit from this type in `RepeatedFieldProxy`.

// Defines `set()` for primitive element types, which only take by value.
template <typename ElementType, bool kOrProxy, typename Enable = void>
class PROTOBUF_DECLSPEC_EMPTY_BASES RepeatedFieldProxyWithSet {
 public:
  // Sets the element at the given index to the given value.
  //
  // Performs bounds checking in accordance with `bounds_check_mode_*`.
  void set(size_t index, ElementType value) const {
    auto& field =
        RepeatedFieldProxyInternalPrivateAccessHelper<ElementType,
                                                      kOrProxy>::field(this);
    field[index] = value;
  }
};

// Defines `set()` for message element types, which take by const reference or
// rvalue.
template <typename ElementType, bool kOrProxy>
class PROTOBUF_DECLSPEC_EMPTY_BASES RepeatedFieldProxyWithSet<
    ElementType, kOrProxy,
    std::enable_if_t<RepeatedElementTypeIsMessage<ElementType>>> {
 public:
  // Sets the element at the given index to the given value by move-assignment.
  //
  // Performs bounds checking in accordance with `bounds_check_mode_*`.
  void set(size_t index, ElementType&& value) const {
    auto& field =
        RepeatedFieldProxyInternalPrivateAccessHelper<ElementType,
                                                      kOrProxy>::field(this);
    field[index] = std::move(value);
  }

  // Sets the element at the given index to the given value by copy-assignment.
  //
  // Performs bounds checking in accordance with `bounds_check_mode_*`.
  void set(size_t index, const ElementType& value) const {
    auto& field =
        RepeatedFieldProxyInternalPrivateAccessHelper<ElementType,
                                                      kOrProxy>::field(this);
    field[index] = value;
  }
};

// Defines `set()` for string element types, which dispatch to
// `string_util::SetElement` and accept many string-like types.
template <typename ElementType, bool kOrProxy>
class PROTOBUF_DECLSPEC_EMPTY_BASES RepeatedFieldProxyWithSet<
    ElementType, kOrProxy,
    std::enable_if_t<RepeatedElementTypeIsString<ElementType>>> {
 public:
  // Sets the element at the given index to the given value.
  //
  // Performs bounds checking in accordance with `bounds_check_mode_*`.
  template <typename T>
  void set(size_t index, T&& value) const {
    auto& field =
        RepeatedFieldProxyInternalPrivateAccessHelper<ElementType,
                                                      kOrProxy>::field(this);
    string_util::SetElement(field[index], std::forward<T>(value));
  }
};

// Defines `push_back()` for primitive element types, which only take by value.
template <typename ElementType, bool kOrProxy, typename Enable = void>
class PROTOBUF_DECLSPEC_EMPTY_BASES RepeatedFieldProxyWithPushBack {
 public:
  // Appends the given value to the end of the repeated field.
  void push_back(ElementType value) const {
    RepeatedFieldProxyInternalPrivateAccessHelper<ElementType, kOrProxy>::Add(
        this, value);
  }
};

// Defines `push_back()` for message element types, which take by const
// reference or rvalue.
template <typename ElementType, bool kOrProxy>
class PROTOBUF_DECLSPEC_EMPTY_BASES RepeatedFieldProxyWithPushBack<
    ElementType, kOrProxy,
    std::enable_if_t<RepeatedElementTypeIsMessage<ElementType>>> {
 public:
  // Appends the given value to the end of the repeated field by move
  // construction/assignment.
  void push_back(ElementType&& value) const {
    RepeatedFieldProxyInternalPrivateAccessHelper<ElementType, kOrProxy>::Add(
        this, std::move(value));
  }

  // Appends the given value to the end of the repeated field by copy
  // construction/assignment.
  void push_back(const ElementType& value) const {
    RepeatedFieldProxyInternalPrivateAccessHelper<ElementType, kOrProxy>::Add(
        this, value);
  }
};

// Defines `push_back()` for string element types, which dispatch to
// `string_util::SetElement` and accept many string-like types.
template <typename ElementType, bool kOrProxy>
class PROTOBUF_DECLSPEC_EMPTY_BASES RepeatedFieldProxyWithPushBack<
    ElementType, kOrProxy,
    std::enable_if_t<RepeatedElementTypeIsString<ElementType>>> {
 public:
  // Appends the given value to the end of the repeated field.
  template <typename T>
  void push_back(T&& value) const {
    string_util::SetElement(
        RepeatedFieldProxyInternalPrivateAccessHelper<ElementType,
                                                      kOrProxy>::Add(this),
        std::forward<T>(value));
  }
};

// Defines `emplace_back()` for all types except `absl::string_view`. Simply
// takes any arguments that can be passed to the constructor of `ElementType`
// and in-place constructs the element at the end of the repeated field.
template <typename ElementType, bool kOrProxy, typename Enable = void>
class PROTOBUF_DECLSPEC_EMPTY_BASES RepeatedFieldProxyWithEmplaceBack {
 public:
  // In-place constructs an element at the end of the repeated field, returning
  // a reference to the newly constructed element.
  template <typename... Args>
  auto& emplace_back(Args&&... args) const {
    return RepeatedFieldProxyInternalPrivateAccessHelper<
        ElementType, kOrProxy>::Emplace(this, std::forward<Args>(args)...);
  }
};

// Defines `emplace_back()` for `absl::string_view` element types. We explicitly
// list all constructors we want to support for repeated `string_views` to not
// leak the `std::string` backing of repeated `string_views`.
template <typename ElementType, bool kOrProxy>
class PROTOBUF_DECLSPEC_EMPTY_BASES RepeatedFieldProxyWithEmplaceBack<
    ElementType, kOrProxy,
    std::enable_if_t<std::is_same_v<ElementType, absl::string_view>>> {
 public:
  // In-place constructs an element at the end of the repeated field, returning
  // a string_view of the newly constructed element.
  absl::string_view emplace_back() const {
    return RepeatedFieldProxyInternalPrivateAccessHelper<
        ElementType, kOrProxy>::Emplace(this);
  }

  // In-place constructs an element at the end of the repeated field, returning
  // a string_view of the newly constructed element.
  absl::string_view emplace_back(absl::string_view value) const {
    return RepeatedFieldProxyInternalPrivateAccessHelper<
        ElementType, kOrProxy>::Emplace(this, value);
  }

  // In-place constructs an element at the end of the repeated field, returning
  // a string_view of the newly constructed element.
  absl::string_view emplace_back(std::string&& value) const {
    return RepeatedFieldProxyInternalPrivateAccessHelper<
        ElementType, kOrProxy>::Emplace(this, std::move(value));
  }

  // In-place constructs an element at the end of the repeated field, returning
  // a string_view of the newly constructed element.
  absl::string_view emplace_back(const std::string& value) const {
    return RepeatedFieldProxyInternalPrivateAccessHelper<
        ElementType, kOrProxy>::Emplace(this, value);
  }

  // In-place constructs an element at the end of the repeated field, returning
  // a string_view of the newly constructed element.
  absl::string_view emplace_back(const char* PROTOBUF_NONNULL value) const {
    return RepeatedFieldProxyInternalPrivateAccessHelper<
        ElementType, kOrProxy>::Emplace(this, value);
  }
};

// Defines `resize(new_size, value)` for all non-string repeated fields.
template <typename ElementType, bool kOrProxy, typename Enable = void>
class PROTOBUF_DECLSPEC_EMPTY_BASES RepeatedFieldProxyWithResize {
 public:
  // Resizes the repeated field to `new_size` elements. If `new_size` is smaller
  // than the current size, the field is truncated. Otherwise, the field is
  // extended with copies of `value`.
  void resize(size_t new_size, const ElementType& value) const {
    auto& field =
        RepeatedFieldProxyInternalPrivateAccessHelper<ElementType,
                                                      kOrProxy>::field(this);
    field.resize(new_size, value);
  }
};

// Defines `resize(new_size, value)` for non-Cord string repeated fields.
template <typename ElementType, bool kOrProxy>
class PROTOBUF_DECLSPEC_EMPTY_BASES RepeatedFieldProxyWithResize<
    ElementType, kOrProxy,
    std::enable_if_t<RepeatedElementTypeIsString<ElementType> &&
                     !std::is_same_v<ElementType, absl::Cord>>> {
 public:
  // Resizes the repeated field to `new_size` elements. If `new_size` is smaller
  // than the current size, the field is truncated. Otherwise, the field is
  // extended with copies of `value`.
  void resize(size_t new_size, absl::string_view value) const {
    auto& field =
        RepeatedFieldProxyInternalPrivateAccessHelper<ElementType,
                                                      kOrProxy>::field(this);
    field.resize(new_size, value);
  }
};

// Defines `resize(new_size, value)` for repeated Cords.
template <typename ElementType, bool kOrProxy>
class PROTOBUF_DECLSPEC_EMPTY_BASES RepeatedFieldProxyWithResize<
    ElementType, kOrProxy,
    std::enable_if_t<std::is_same_v<ElementType, absl::Cord>>> {
 public:
  // Resizes the repeated field to `new_size` elements. If `new_size` is smaller
  // than the current size, the field is truncated. Otherwise, the field is
  // extended with copies of `value`.
  void resize(size_t new_size, const absl::Cord& value) const {
    auto& field =
        RepeatedFieldProxyInternalPrivateAccessHelper<ElementType,
                                                      kOrProxy>::field(this);
    field.resize(new_size, value);
  }
};

template <typename ElementType, bool kOrProxy>
class PROTOBUF_DECLSPEC_EMPTY_BASES MutableRepeatedFieldProxyImpl
    : public internal::RepeatedFieldProxyBase<ElementType, kOrProxy>,
      public internal::RepeatedFieldProxyWithSet<ElementType, kOrProxy>,
      public internal::RepeatedFieldProxyWithPushBack<ElementType, kOrProxy>,
      public internal::RepeatedFieldProxyWithEmplaceBack<ElementType, kOrProxy>,
      public internal::RepeatedFieldProxyWithResize<ElementType, kOrProxy> {
  static_assert(!std::is_const_v<ElementType>);

 protected:
  using Base = internal::RepeatedFieldProxyBase<ElementType, kOrProxy>;
  using typename Base::RepeatedFieldType;

  using Base::field;

 public:
  using typename Base::const_iterator;
  using typename Base::iterator;
  using typename Base::size_type;

  using reference =
      typename internal::RepeatedFieldTraits<ElementType>::reference;

  MutableRepeatedFieldProxyImpl(const MutableRepeatedFieldProxyImpl& other) =
      default;
  // Mutable proxies are not assignable. This is intentional to avoid confusion
  // with the `assign` method, which reassigns the underlying repeated field.
  MutableRepeatedFieldProxyImpl& operator=(
      const MutableRepeatedFieldProxyImpl&) = delete;

  // Returns a type which references the element at the given index. Performs
  // bounds checking in accordance with `bounds_check_mode_*`.
  [[nodiscard]] reference operator[](size_type index) const {
    return field()[index];
  }

  // Removes the last element from the repeated field.
  void pop_back() const { field().RemoveLast(); }

  // Removes all elements from the repeated field. The field will be empty after
  // this call.
  void clear() const { field().Clear(); }

  // Removes the element at `position` from the repeated field. Returns an
  // iterator to the element immediately following the removed element.
  iterator erase(const_iterator position) const {
    // The internal iterator type may not match the proxy iterator type (for
    // example for `absl::string_view` proxies which are backed by
    // `std::string`). To avoid special casing, we will always cast to the
    // internal iterator type before passing down to erase, then cast back to
    // the proxy iterator type upon return. This conversion is redundant for
    // types which have matching exposed and internal element types.
    using const_internal_iterator = typename RepeatedFieldType::const_iterator;
    return iterator(field().erase(const_internal_iterator(position)));
  }

  // Removes the elements in the range `[first, last)` from the repeated field.
  // Returns an iterator to the element immediately following the last removed
  // element.
  iterator erase(const_iterator first, const_iterator last) const {
    using const_internal_iterator = typename RepeatedFieldType::const_iterator;
    return iterator(field().erase(const_internal_iterator(first),
                                  const_internal_iterator(last)));
  }

  // Copy-assigns the elements in the range `[begin, end)` to the repeated
  // field.
  //
  // If `begin` or `end` is an iterator into this repeated field, the behavior
  // is undefined.
  template <
      typename Iter,
      // A seemingly redundant verification that `Iter` is an iterator type.
      // Even though we use `std::iterator_traits` below, we duplicate the
      // condition here in case the implementation changes.
      typename = std::void_t<typename std::iterator_traits<Iter>::value_type>>
  auto assign(Iter begin, Iter end) const
      // Verify that the iterator value type is assignable to `ElementType`.
      // Pass through `push_back`, which is a catch-all for allowed conversions
      // to the element type.
      -> std::void_t<decltype(this->push_back(*begin))> {
    field().Clear();
    // Forward iterators in C++ are required to model `std::incrementable`,
    // which means they are suitable for multi-pass algorithms, and therefore
    // support `std::distance`.
    if constexpr (std::is_base_of_v<
                      std::forward_iterator_tag,
                      typename std::iterator_traits<Iter>::iterator_category>) {
      int distance = static_cast<int>(std::distance(begin, end));
      field().ReserveWithArena(arena(), distance);
    }
    for (; begin != end; ++begin) {
      this->push_back(*begin);
    }
  }

  // A hint to the container to expect to grow/shrink to `new_size` elements.
  // This may allow the container to make optimizations to avoid reallocations,
  // but may also be ignored.
  void reserve(size_type new_size) const {
    field().ReserveWithArena(arena(), new_size);
  }

  // Resizes the repeated field to `new_size` elements. If `new_size` is smaller
  // than the current size, the field is truncated. Otherwise, the field is
  // extended with default-valued elements.
  void resize(size_t new_size) const { field().resize(new_size); }

  // Because we have an overload of `resize` in this class, we need to
  // explicitly inherit the overload from the base class to avoid hiding it.
  using internal::RepeatedFieldProxyWithResize<ElementType, kOrProxy>::resize;

 protected:
  MutableRepeatedFieldProxyImpl(RepeatedFieldType& field,
                                Arena* PROTOBUF_NULLABLE arena)
      : Base(field), arena_(arena) {
    ABSL_DCHECK_EQ(arena, field.GetArena());
  }

  Arena* PROTOBUF_NULLABLE arena() const { return arena_; }

  // The following methods all forward to the backing repeated fields. This is
  // done here for access to private members of the legacy containers, which
  // only need to friend `MutableRepeatedFieldProxyImpl`.
  auto& Add() const { return *field().AddWithArena(arena()); }
  auto& Add(ElementType&& value) const {
    return *field().AddWithArena(arena(), std::move(value));
  }
  auto& Add(const ElementType& value) const {
    return *field().AddWithArena(arena(), value);
  }
  template <typename... Args>
  auto& Emplace(Args&&... args) const {
    return *field().EmplaceWithArena(arena(), std::forward<Args>(args)...);
  }

 private:
  friend RepeatedFieldProxyInternalPrivateAccessHelper<ElementType, kOrProxy>;

  Arena* PROTOBUF_NULLABLE const arena_;
};

template <typename ElementType, bool kOrProxy>
class PROTOBUF_DECLSPEC_EMPTY_BASES ConstRepeatedFieldProxyImpl
    : public internal::RepeatedFieldProxyBase<const ElementType, kOrProxy> {
  // A specialization of RepeatedFieldProxy for const proxies. This is needed
  // for mutating methods to not be exposed on const proxies.

 protected:
  using Base = internal::RepeatedFieldProxyBase<const ElementType, kOrProxy>;

  // Inherit constructors, but don't publicly expose them.
  //
  // Repeated field proxies have no public constructors aside from a copy
  // constructor. This is intentional, as layout of data that is proxied is an
  // implementation detail. By not exposing a way to construct a proxy, we can
  // freely change the layout of the underlying repeated field.
  using Base::Base;

  using Base::field;

 public:
  ConstRepeatedFieldProxyImpl()
      : Base(*internal::RawPtr<const typename Base::RepeatedFieldType>()) {}

  using typename Base::const_reference;
  using typename Base::size_type;

  ConstRepeatedFieldProxyImpl(const ConstRepeatedFieldProxyImpl& other) =
      default;
  ConstRepeatedFieldProxyImpl& operator=(const ConstRepeatedFieldProxyImpl&) =
      default;

  // Returns a type which references the element at the given index. Performs
  // bounds checking in accordance with `bounds_check_mode_*`.
  [[nodiscard]] const_reference operator[](size_type index) const {
    return field()[index];
  }

 private:
  // Note that we don't need an arena pointer here, since we don't mutate the
  // underlying repeated field.
};

}  // namespace internal

// A proxy for a repeated field of type `ElementType` in a Protobuf message.
// Proxies alias the repeated field and provide an interface to read or modify
// it, following STL naming conventions.
//
// Proxies themselves are value types, meaning they should be passed around by
// value similar to `absl::string_view` or `absl::Span`.
//
// Proxies cannot be constructed directly. They are returned from a message's
// repeated field accessors which have the `features.(pb.cpp).repeated_type =
// PROXY` annotation. This annotation is currently only available in edition
// `UNSTABLE`, but will eventually be available in an upcoming edition.
template <typename ElementType>
class PROTOBUF_DECLSPEC_EMPTY_BASES RepeatedFieldProxy final
    : public internal::MutableRepeatedFieldProxyImpl<ElementType,
                                                     /*kOrProxy=*/false> {
  static_assert(!std::is_const_v<ElementType>);

 private:
  using Base = internal::MutableRepeatedFieldProxyImpl<ElementType,
                                                       /*kOrProxy=*/false>;
  using Base::Base;
  using Base::field;

 public:
  // Copy-assigns `other` into this repeated field.
  //
  // This method exists because mutable proxies cannot be rebound.
  void assign(RepeatedFieldProxy<const ElementType> other) const {
    field().CopyFrom(other.field());
  }

  // Because we have an overload of `assign` in this class, we need to
  // explicitly inherit the overload from the base class to avoid hiding it.
  using Base::assign;

  // Move-assigns `other` into this repeated field. `other` is left in a valid
  // but unspecified state.
  void move_assign(RepeatedFieldProxy<ElementType> other) const {
    field() = std::move(other.field());
  }

  // Swaps the contents of this repeated field with `other`.
  //
  // Invalidates all iterators. Pointer stability is not guaranteed across the
  // swap for any element of either repeated field.
  //
  // If the underlying repeated fields are on different arenas, this may force
  // deep copies of the elements.
  void swap(RepeatedFieldProxy other) const { field().Swap(&other.field()); }

 private:
  friend RepeatedFieldProxy<const ElementType>;
  friend internal::RepeatedFieldOrProxy<ElementType>;
  friend internal::RepeatedFieldOrProxy<const ElementType>;
  friend internal::RepeatedFieldProxyInternalPrivateAccessHelper<
      ElementType, /*kOrProxy=*/false>;
};

template <typename ElementType>
class PROTOBUF_DECLSPEC_EMPTY_BASES RepeatedFieldProxy<const ElementType> final
    : public internal::ConstRepeatedFieldProxyImpl<ElementType,
                                                   /*kOrProxy=*/false> {
  // A specialization of RepeatedFieldProxy for const proxies. This is needed
  // for mutating methods to not be exposed on const proxies.

 private:
  using Base = internal::ConstRepeatedFieldProxyImpl<ElementType,
                                                     /*kOrProxy=*/false>;

  // Inherit constructors, but don't publicly expose them.
  //
  // Repeated field proxies have no public constructors aside from a copy
  // constructor. This is intentional, as layout of data that is proxied is an
  // implementation detail. By not exposing a way to construct a proxy, we can
  // freely change the layout of the underlying repeated field.
  using Base::Base;

 public:
  RepeatedFieldProxy() = default;

  RepeatedFieldProxy(const RepeatedFieldProxy& other) = default;
  RepeatedFieldProxy& operator=(const RepeatedFieldProxy& other) = default;

  // Allow implicit conversion from a mutable RepeatedFieldProxy to a const
  // RepeatedFieldProxy.
  //
  // NOLINTNEXTLINE(google-explicit-constructor)
  RepeatedFieldProxy(RepeatedFieldProxy<ElementType> other)
      : Base(other.field()) {}

 private:
  friend RepeatedFieldProxy<ElementType>;
  friend internal::RepeatedFieldOrProxy<const ElementType>;
  friend internal::RepeatedFieldProxyInternalPrivateAccessHelper<
      const ElementType, /*kOrProxy=*/false>;
};

// The size of proxies is not really important, since they should mostly be
// passed around by value and inlined away to oblivion. Regardless, size
// assertions guarantee that the compiler hasn't introduced invisible members
// that we didn't notice (e.g. `PROTOBUF_DECLSPEC_EMPTY_BASES`).
static_assert(sizeof(RepeatedFieldProxy<int>) == 2 * sizeof(void*));
static_assert(sizeof(RepeatedFieldProxy<const int>) == sizeof(void*));

namespace internal {

// A helper class for accessing private members of `RepeatedFieldProxy` in
// Protobuf internal code.
//
// DO NOT USE this class for any reason outside of protobuf internal code.
template <typename ElementType, bool kOrProxy = false>
class RepeatedFieldProxyInternalPrivateAccessHelper {
  using ProxyType =
      std::conditional_t<kOrProxy, RepeatedFieldOrProxy<ElementType>,
                         RepeatedFieldProxy<ElementType>>;

  // Casts up to a `MutableRepeatedFieldProxyImpl<ElementType>` from a subclass
  // of `MutableRepeatedFieldProxyImpl<ElementType>`. This is used to implement
  // the CRTP pattern for `*With<MethodName>` classes.
  template <typename C>
  static MutableRepeatedFieldProxyImpl<ElementType, kOrProxy> ToProxyType(
      const C* PROTOBUF_NONNULL proxy) {
    return *static_cast<
        const MutableRepeatedFieldProxyImpl<ElementType, kOrProxy>*>(proxy);
  }

 public:
  template <typename... Args>
  static RepeatedFieldProxy<ElementType> Construct(Args&&... args) {
    return RepeatedFieldProxy<ElementType>(std::forward<Args>(args)...);
  }

  static auto& field(const ProxyType& proxy) { return proxy.field(); }

  // Takes any subclass of `RepeatedFieldProxy<ElementType>`, upcasts to
  // `RepeatedFieldProxy<ElementType>`, then calls `field()`. This is used to
  // implement the CRTP pattern for `*With<MethodName>` classes.
  template <typename C>
  static auto& field(const C* PROTOBUF_NONNULL proxy) {
    return ToProxyType(proxy).field();
  }

  template <typename C, typename... Args>
  static auto& Add(const C* PROTOBUF_NONNULL proxy, Args&&... args) {
    return ToProxyType(proxy).Add(std::forward<Args>(args)...);
  }
  template <typename C, typename... Args>
  static auto& Emplace(const C* PROTOBUF_NONNULL proxy, Args&&... args) {
    return ToProxyType(proxy).Emplace(std::forward<Args>(args)...);
  }
};

// A mutable `RepeatedFieldOrProxy` for a repeated field of type `ElementType`
// in a Protobuf message. Proxies alias the repeated field and provide an
// interface to read or modify it, following STL naming conventions.
//
// Unlike `RepeatedFieldProxy`, `RepeatedFieldOrProxy` can be constructed from
// the legacy repeated field containers (`google::protobuf::RepeatedField` and
// `google::protobuf::RepeatedPtrField`). This container can be used in code which has not
// yet fully migrated to proxies. It is particularly useful for function
// parameters that have many callers, allowing the callers to be migrated to
// proxies incrementally.
//
// Proxies themselves are value types, meaning they should be passed around by
// value similar to `absl::string_view` or `absl::Span`.
template <typename ElementType>
class RepeatedFieldOrProxy final
    : public internal::MutableRepeatedFieldProxyImpl<ElementType,
                                                     /*kOrProxy=*/true> {
  // `const ElementType` is specialized below.
  static_assert(!std::is_const_v<ElementType>);

 private:
  using Base = internal::MutableRepeatedFieldProxyImpl<ElementType,
                                                       /*kOrProxy=*/true>;
  using RepeatedFieldType = typename Base::RepeatedFieldType;

  // Inherit constructors.
  using Base::Base;

  using Base::field;

 public:
  // Allow implicit conversion from a RepeatedField to a RepeatedFieldOrProxy.
  //
  // NOLINTNEXTLINE(google-explicit-constructor)
  RepeatedFieldOrProxy(RepeatedFieldType& field)
      : Base(field, field.GetArena()) {}

  // Allow implicit conversion from a RepeatedField* to a RepeatedFieldOrProxy,
  // but inline this to call the RepeatedFieldOrProxy(RepeatedFieldType& field)
  // constructor.
  //
  // This will be used to ease migration along for functions that currently take
  // a RepeatedField* parameter. If we allow implicit conversion from
  // RepeatedField* to RepeatedFieldOrProxy, then we can change the type of the
  // parameter from RepeatedField<T>* to RepeatedFieldOrProxy<T> without
  // updating any callers. Then, the C++ inliner will later come along and
  // dereference the repeated field pointer argument to the method at callsites.
  PROTOBUF_REFACTOR_INLINE()
  // NOLINTNEXTLINE
  RepeatedFieldOrProxy(RepeatedFieldType* PROTOBUF_NONNULL field)
      : RepeatedFieldOrProxy(*field) {}

  // Allow implicit conversion from a RepeatedFieldProxy to a
  // RepeatedFieldOrProxy.
  //
  // NOLINTNEXTLINE(google-explicit-constructor)
  RepeatedFieldOrProxy(RepeatedFieldProxy<ElementType> proxy)
      : Base(proxy.field(), proxy.arena()) {}

  // Allow explicit conversion to the legacy repeated field container.
  //
  // Note: this performs a deep copy of the underlying repeated field.
  explicit operator std::remove_const_t<RepeatedFieldType>() const {
    return RepeatedFieldType(static_cast<const Base&>(*this));
  }

  // Note: we do not directly expose the following methods from the base class
  // because they have overloads that take a `RepeatedFieldProxy`. We re-define
  // them below with overloads that take `RepeatedFieldOrProxy`.

  // Copy-assigns `other` into this repeated field.
  //
  // This method exists because mutable proxies cannot be rebound.
  void assign(RepeatedFieldOrProxy<const ElementType> other) const {
    Base::field().CopyFrom(other.field());
  }

  // Because we have an overload of `assign` in this class, we need to
  // explicitly inherit the overload from the base class to avoid hiding it.
  using Base::assign;

  // Move-assigns `other` into this repeated field. `other` is left in a valid
  // but unspecified state.
  //
  // This method only differs from `assign` in that it is a hint that you don't
  // need the contents of `other` anymore. It is not guaranteed that the
  // contents will be efficiently transferred, nor that pointer stability will
  // be preserved for elements of `other`.
  void move_assign(RepeatedFieldOrProxy other) const {
    field() = std::move(other.field());
  }

  // Swaps the contents of this repeated field with `other`.
  //
  // Invalidates all iterators. Pointer stability is not guaranteed across the
  // swap for any element of either repeated field.
  //
  // If the underlying repeated fields are on different arenas, this may force
  // deep copies of the elements.
  void swap(RepeatedFieldOrProxy other) const {
    Base::field().Swap(&other.field());
  }

 private:
  friend RepeatedFieldProxy<ElementType>;
  friend RepeatedFieldOrProxy<const ElementType>;
  friend internal::RepeatedFieldProxyInternalPrivateAccessHelper<
      ElementType, /*kOrProxy=*/true>;
};

// A const proxy for a repeated field of type `ElementType` in a Protobuf
// message. Proxies alias the repeated field and provide an interface to read or
// modify it, following STL naming conventions.
//
// Unlike `RepeatedFieldProxy`, `RepeatedFieldOrProxy` can be constructed from
// the legacy repeated field containers (`google::protobuf::RepeatedField` and
// `google::protobuf::RepeatedPtrField`). This container can be used in code which has not
// yet fully migrated to proxies. It is particularly useful for function
// parameters that have many callers, allowing the callers to be migrated to
// proxies incrementally.
//
// Proxies themselves are value types, meaning they should be passed around by
// value similar to `absl::string_view` or `absl::Span`.
template <typename ElementType>
class RepeatedFieldOrProxy<const ElementType> final
    : public internal::ConstRepeatedFieldProxyImpl<ElementType,
                                                   /*kOrProxy=*/true> {
  using Base = internal::ConstRepeatedFieldProxyImpl<ElementType,
                                                     /*kOrProxy=*/true>;
  using RepeatedFieldType = typename Base::RepeatedFieldType;

  // Inherit constructors.
  using Base::Base;

  using Base::field;

 public:
  RepeatedFieldOrProxy() = default;

  RepeatedFieldOrProxy(const RepeatedFieldOrProxy& other) = default;
  RepeatedFieldOrProxy& operator=(const RepeatedFieldOrProxy& other) = default;

  // Allow implicit conversion from a mutable RepeatedFieldOrProxy to a const
  // RepeatedFieldOrProxy.
  //
  // NOLINTNEXTLINE(google-explicit-constructor)
  RepeatedFieldOrProxy(RepeatedFieldOrProxy<ElementType> other)
      : Base(other.field()) {}

  // Allow implicit conversion from a RepeatedField to a RepeatedFieldOrProxy.
  //
  // NOLINTNEXTLINE(google-explicit-constructor)
  RepeatedFieldOrProxy(const RepeatedFieldType& field) : Base(field) {}

  // Allow implicit conversion from a const RepeatedField* to a
  // RepeatedFieldOrProxy, but inline this to call the
  // RepeatedFieldOrProxy(RepeatedFieldType& field) constructor.
  //
  // This will be used to ease migration along for functions that currently take
  // a const RepeatedField* parameter. If we allow implicit conversion from
  // const RepeatedField* to RepeatedFieldOrProxy, then we can change the type
  // of the parameter from const RepeatedField<T>* to
  // RepeatedFieldOrProxy<const T> without updating any callers. Then, the C++
  // inliner will later come along and dereference the repeated field pointer
  // argument to the method at callsites.
  PROTOBUF_REFACTOR_INLINE()
  // NOLINTNEXTLINE
  RepeatedFieldOrProxy(RepeatedFieldType* PROTOBUF_NONNULL field)
      : RepeatedFieldOrProxy(*field) {}

  // Allow implicit conversion from a RepeatedFieldProxy to a
  // RepeatedFieldOrProxy.
  //
  // NOLINTNEXTLINE(google-explicit-constructor)
  RepeatedFieldOrProxy(RepeatedFieldProxy<ElementType> proxy)
      : Base(proxy.field()) {}

  // NOLINTNEXTLINE(google-explicit-constructor)
  RepeatedFieldOrProxy(RepeatedFieldProxy<const ElementType> proxy)
      : Base(proxy.field()) {}

  // Allow explicit conversion to the legacy repeated field container.
  //
  // Note: this performs a deep copy of the underlying repeated field.
  explicit operator RepeatedFieldType() const {
    return RepeatedFieldType(static_cast<const Base&>(*this));
  }

 private:
  friend RepeatedFieldOrProxy<ElementType>;
};

static_assert(sizeof(RepeatedFieldOrProxy<int>) ==
                  sizeof(RepeatedFieldProxy<int>),
              "Mutable `RepeatedFieldOrProxy` is not the expected size");
static_assert(sizeof(RepeatedFieldOrProxy<const int>) ==
                  sizeof(RepeatedFieldProxy<const int>),
              "Const `RepeatedFieldOrProxy` is not the expected size");

}  // namespace internal

// Like C++20's std::erase_if, for RepeatedFieldProxy
template <int&... DeductionBarrier, typename T, typename Pred>
size_t erase_if(RepeatedFieldProxy<T> cont, Pred pred) {
  return google::protobuf::erase_if(
      internal::RepeatedFieldProxyInternalPrivateAccessHelper<
          T, /*kOrProxy=*/false>::field(cont),
      pred);
}

// Like C++20's std::erase, for RepeatedFieldProxy
template <int&... DeductionBarrier, typename T, typename U>
size_t erase(RepeatedFieldProxy<T> cont, const U& value) {
  return google::protobuf::erase(internal::RepeatedFieldProxyInternalPrivateAccessHelper<
                           T, /*kOrProxy=*/false>::field(cont),
                       value);
}

// Like C++20's std::erase_if, for RepeatedFieldOrProxy.
template <int&... DeductionBarrier, typename T, typename Pred>
size_t erase_if(internal::RepeatedFieldOrProxy<T> cont, Pred pred) {
  return google::protobuf::erase_if(
      internal::RepeatedFieldProxyInternalPrivateAccessHelper<
          T, /*kOrProxy=*/true>::field(cont),
      pred);
}

// Like C++20's std::erase, for RepeatedFieldOrProxy.
template <int&... DeductionBarrier, typename T, typename U>
size_t erase(internal::RepeatedFieldOrProxy<T> cont, const U& value) {
  return google::protobuf::erase(internal::RepeatedFieldProxyInternalPrivateAccessHelper<
                           T, /*kOrProxy=*/true>::field(cont),
                       value);
}

// Like C++20's std::sort, for RepeatedFieldProxy.
template <int&... DeductionBarrier, typename T, typename Compare>
void c_sort(RepeatedFieldProxy<T> cont, Compare cmp) {
  google::protobuf::sort(cont.begin(), cont.end(), cmp);
}
// Like C++20's std::sort, for RepeatedFieldProxy, with default comparison.
template <int&... DeductionBarrier, typename T>
void c_sort(RepeatedFieldProxy<T> cont) {
  google::protobuf::sort(cont.begin(), cont.end());
}
// Like C++20's std::stable_sort, for RepeatedFieldProxy.
template <int&... DeductionBarrier, typename T, typename Compare>
void c_stable_sort(RepeatedFieldProxy<T> cont, Compare cmp) {
  google::protobuf::stable_sort(cont.begin(), cont.end(), cmp);
}
// Like C++20's std::stable_sort, for RepeatedFieldProxy, with default
// comparison.
template <int&... DeductionBarrier, typename T>
void c_stable_sort(RepeatedFieldProxy<T> cont) {
  google::protobuf::stable_sort(cont.begin(), cont.end());
}

// Like C++20's std::sort, for RepeatedFieldOrProxy.
template <int&..., typename T, typename Compare>
void c_sort(internal::RepeatedFieldOrProxy<T> cont, Compare cmp) {
  google::protobuf::sort(cont.begin(), cont.end(), cmp);
}
// Like C++20's std::sort, for RepeatedFieldOrProxy, with default comparison.
template <int&..., typename T>
void c_sort(internal::RepeatedFieldOrProxy<T> cont) {
  google::protobuf::sort(cont.begin(), cont.end());
}
// Like C++20's std::stable_sort, for RepeatedFieldOrProxy.
template <int&..., typename T, typename Compare>
void c_stable_sort(internal::RepeatedFieldOrProxy<T> cont, Compare cmp) {
  google::protobuf::stable_sort(cont.begin(), cont.end(), cmp);
}
// Like C++20's std::stable_sort, for RepeatedFieldOrProxy, with default
// comparison.
template <int&..., typename T>
void c_stable_sort(internal::RepeatedFieldOrProxy<T> cont) {
  google::protobuf::stable_sort(cont.begin(), cont.end());
}

}  // namespace protobuf
}  // namespace google

#include "google/protobuf/port_undef.inc"

#endif  // GOOGLE_PROTOBUF_REPEATED_FIELD_PROXY_H__
