llvm/libcxx/test/std/ranges/range.utility/range.utility.conv/container.h

//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef RANGES_RANGE_UTILITY_RANGE_UTILITY_CONV_CONTAINER_H
#define RANGES_RANGE_UTILITY_RANGE_UTILITY_CONV_CONTAINER_H

#include <algorithm>
#include <concepts>
#include <cstddef>

enum class CtrChoice { Invalid, DefaultCtrAndInsert, BeginEndPair, FromRangeT, DirectCtr };

enum class InserterChoice { Invalid, Insert, PushBack };

// Allows checking that `ranges::to` correctly follows the order of priority of different constructors -- e.g., if
// 3 constructors are available, the `from_range_t` constructor is chosen in favor of the constructor taking two
// iterators, etc.
template <class ElementType, CtrChoice Rank, InserterChoice Inserter = InserterChoice::Insert, bool CanReserve = false>
struct Container {
  CtrChoice ctr_choice           = CtrChoice::Invalid;
  InserterChoice inserter_choice = InserterChoice::Invalid;
  bool called_reserve            = false;

  int extra_arg1  = 0;
  char extra_arg2 = 0;

  using value_type              = ElementType;
  static constexpr int Capacity = 8;
  int size_                     = 0;
  ElementType buffer_[Capacity] = {};

  // Case 1 -- construct directly from the range.

  constexpr explicit Container(std::ranges::input_range auto&& in)
    requires(Rank >= CtrChoice::DirectCtr)
      : ctr_choice(CtrChoice::DirectCtr), size_(static_cast<int>(std::ranges::size(in))) {
    std::ranges::copy(in, begin());
  }

  // Check that `ranges::to` can also pass extra parameters.
  constexpr explicit Container(std::ranges::input_range auto&& in, int arg1, char arg2)
    requires(Rank >= CtrChoice::DirectCtr)
      : Container(in) {
    extra_arg1 = arg1;
    extra_arg2 = arg2;
  }

  // Case 2 -- use `from_range_t` constructor.

  constexpr Container(std::from_range_t, std::ranges::input_range auto&& in)
    requires(Rank >= CtrChoice::FromRangeT)
      : ctr_choice(CtrChoice::FromRangeT), size_(static_cast<int>(std::ranges::size(in))) {
    std::ranges::copy(in, begin());
  }

  constexpr Container(std::from_range_t, std::ranges::input_range auto&& in, int arg1, char arg2)
    requires(Rank >= CtrChoice::FromRangeT)
      : Container(std::from_range, in) {
    extra_arg1 = arg1;
    extra_arg2 = arg2;
  }

  // Case 3 -- use begin-end pair.

  template <class Iter>
  constexpr Container(Iter b, Iter e)
    requires(Rank >= CtrChoice::BeginEndPair)
      : ctr_choice(CtrChoice::BeginEndPair), size_(static_cast<int>(e - b)) {
    std::ranges::copy(b, e, begin());
  }

  template <class Iter>
  constexpr Container(Iter b, Iter e, int arg1, char arg2)
    requires(Rank >= CtrChoice::BeginEndPair)
      : Container(b, e) {
    extra_arg1 = arg1;
    extra_arg2 = arg2;
  }

  // Case 4 -- default-construct and insert, reserving the size if possible.

  constexpr Container()
    requires(Rank >= CtrChoice::DefaultCtrAndInsert)
      : ctr_choice(CtrChoice::DefaultCtrAndInsert) {}

  constexpr Container(int arg1, char arg2)
    requires(Rank >= CtrChoice::DefaultCtrAndInsert)
      : ctr_choice(CtrChoice::DefaultCtrAndInsert), extra_arg1(arg1), extra_arg2(arg2) {}

  constexpr ElementType* begin() { return buffer_; }
  constexpr ElementType* end() { return buffer_ + size_; }
  constexpr std::size_t size() const { return size_; }

  template <class T>
  constexpr void push_back(T val)
    requires(Inserter >= InserterChoice::PushBack)
  {
    inserter_choice = InserterChoice::PushBack;
    buffer_[size_]  = val;
    ++size_;
  }

  template <class T>
  constexpr ElementType* insert(ElementType* where, T val)
    requires(Inserter >= InserterChoice::Insert)
  {
    assert(size() + 1 <= Capacity);

    inserter_choice = InserterChoice::Insert;

    std::shift_right(where, end(), 1);
    *where = val;
    ++size_;

    return where;
  }

  constexpr void reserve(size_t)
    requires CanReserve
  {
    called_reserve = true;
  }

  constexpr std::size_t capacity() const
    requires CanReserve
  {
    return Capacity;
  }

  constexpr std::size_t max_size() const
    requires CanReserve
  {
    return Capacity;
  }

  friend constexpr bool operator==(const Container&, const Container&) = default;
};

#endif // RANGES_RANGE_UTILITY_RANGE_UTILITY_CONV_CONTAINER_H