Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c0778114 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes I35923e79,Icc1263eb

* changes:
  FTL: Refine container semantics
  FTL: Prevent InitializerList overflow
parents 6928b561 206996f0
Loading
Loading
Loading
Loading
+49 −13
Original line number Diff line number Diff line
@@ -42,7 +42,7 @@ struct ArrayTraits {
  using const_reverse_iterator = std::reverse_iterator<const_iterator>;

  template <typename... Args>
  static pointer construct_at(const_iterator it, Args&&... args) {
  static constexpr pointer construct_at(const_iterator it, Args&&... args) {
    void* const ptr = const_cast<void*>(static_cast<const void*>(it));
    if constexpr (std::is_constructible_v<value_type, Args...>) {
      // TODO: Replace with std::construct_at in C++20.
@@ -52,6 +52,42 @@ struct ArrayTraits {
      return new (ptr) value_type{std::forward<Args>(args)...};
    }
  }

  // TODO: Make constexpr in C++20.
  template <typename... Args>
  static reference replace_at(const_iterator it, Args&&... args) {
    value_type element{std::forward<Args>(args)...};
    return replace_at(it, std::move(element));
  }

  // TODO: Make constexpr in C++20.
  static reference replace_at(const_iterator it, value_type&& value) {
    std::destroy_at(it);
    // This is only safe because exceptions are disabled.
    return *construct_at(it, std::move(value));
  }

  // TODO: Make constexpr in C++20.
  static void in_place_swap(reference a, reference b) {
    value_type c{std::move(a)};
    replace_at(&a, std::move(b));
    replace_at(&b, std::move(c));
  }

  // TODO: Make constexpr in C++20.
  static void in_place_swap_ranges(iterator first1, iterator last1, iterator first2) {
    while (first1 != last1) {
      in_place_swap(*first1++, *first2++);
    }
  }

  // TODO: Replace with std::uninitialized_copy in C++20.
  template <typename Iterator>
  static void uninitialized_copy(Iterator first, Iterator last, const_iterator out) {
    while (first != last) {
      construct_at(out++, *first++);
    }
  }
};

// CRTP mixin to define iterator functions in terms of non-const Self::begin and Self::end.
@@ -101,33 +137,33 @@ class ArrayIterators {
// TODO: Replace with operator<=> in C++20.
template <template <typename, std::size_t> class Array>
struct ArrayComparators {
  template <typename T, std::size_t N, std::size_t M>
  friend bool operator==(const Array<T, N>& lhs, const Array<T, M>& rhs) {
  template <typename T, typename U, std::size_t N, std::size_t M>
  friend bool operator==(const Array<T, N>& lhs, const Array<U, M>& rhs) {
    return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
  }

  template <typename T, std::size_t N, std::size_t M>
  friend bool operator<(const Array<T, N>& lhs, const Array<T, M>& rhs) {
  template <typename T, typename U, std::size_t N, std::size_t M>
  friend bool operator<(const Array<T, N>& lhs, const Array<U, M>& rhs) {
    return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
  }

  template <typename T, std::size_t N, std::size_t M>
  friend bool operator>(const Array<T, N>& lhs, const Array<T, M>& rhs) {
  template <typename T, typename U, std::size_t N, std::size_t M>
  friend bool operator>(const Array<T, N>& lhs, const Array<U, M>& rhs) {
    return rhs < lhs;
  }

  template <typename T, std::size_t N, std::size_t M>
  friend bool operator!=(const Array<T, N>& lhs, const Array<T, M>& rhs) {
  template <typename T, typename U, std::size_t N, std::size_t M>
  friend bool operator!=(const Array<T, N>& lhs, const Array<U, M>& rhs) {
    return !(lhs == rhs);
  }

  template <typename T, std::size_t N, std::size_t M>
  friend bool operator>=(const Array<T, N>& lhs, const Array<T, M>& rhs) {
  template <typename T, typename U, std::size_t N, std::size_t M>
  friend bool operator>=(const Array<T, N>& lhs, const Array<U, M>& rhs) {
    return !(lhs < rhs);
  }

  template <typename T, std::size_t N, std::size_t M>
  friend bool operator<=(const Array<T, N>& lhs, const Array<T, M>& rhs) {
  template <typename T, typename U, std::size_t N, std::size_t M>
  friend bool operator<=(const Array<T, N>& lhs, const Array<U, M>& rhs) {
    return !(lhs > rhs);
  }
};
+7 −0
Original line number Diff line number Diff line
@@ -65,6 +65,9 @@ template <typename K, typename V, std::size_t N, typename KeyEqual = std::equal_
class SmallMap final {
  using Map = SmallVector<std::pair<const K, V>, N>;

  template <typename, typename, std::size_t, typename>
  friend class SmallMap;

 public:
  using key_type = K;
  using mapped_type = V;
@@ -100,6 +103,10 @@ class SmallMap final {
    deduplicate();
  }

  // Copies or moves key-value pairs from a convertible map.
  template <typename Q, typename W, std::size_t M, typename E>
  SmallMap(SmallMap<Q, W, M, E> other) : map_(std::move(other.map_)) {}

  size_type max_size() const { return map_.max_size(); }
  size_type size() const { return map_.size(); }
  bool empty() const { return map_.empty(); }
+74 −10
Original line number Diff line number Diff line
@@ -37,6 +37,9 @@ struct is_small_vector;
// augmented by an unstable_erase operation that does not preserve order, and a replace operation
// that destructively emplaces.
//
// Unlike std::vector, T does not require copy/move assignment, so may be an object with const data
// members, or be const itself.
//
// SmallVector<T, 0> is a specialization that thinly wraps std::vector.
//
// Example usage:
@@ -105,10 +108,9 @@ class SmallVector final : details::ArrayTraits<T>, details::ArrayComparators<Sma
  SmallVector(Arg&& arg, Args&&... args)
      : vector_(std::in_place_type<Static>, std::forward<Arg>(arg), std::forward<Args>(args)...) {}

  // Copies at most N elements from a smaller convertible vector.
  template <typename U, std::size_t M, typename = std::enable_if_t<M <= N>>
  SmallVector(const SmallVector<U, M>& other)
      : SmallVector(kIteratorRange, other.begin(), other.end()) {}
  // Copies or moves elements from a smaller convertible vector.
  template <typename U, std::size_t M, typename = std::enable_if_t<(M > 0)>>
  SmallVector(SmallVector<U, M> other) : vector_(convert(std::move(other))) {}

  void swap(SmallVector& other) { vector_.swap(other.vector_); }

@@ -235,7 +237,30 @@ class SmallVector final : details::ArrayTraits<T>, details::ArrayComparators<Sma
    }
  }

  // Extracts the elements as std::vector.
  std::vector<T> promote() && {
    if (dynamic()) {
      return std::get<Dynamic>(std::move(vector_)).promote();
    } else {
      return {std::make_move_iterator(begin()), std::make_move_iterator(end())};
    }
  }

 private:
  template <typename, std::size_t>
  friend class SmallVector;

  template <typename U, std::size_t M>
  static std::variant<Static, Dynamic> convert(SmallVector<U, M>&& other) {
    using Other = SmallVector<U, M>;

    if (other.dynamic()) {
      return std::get<typename Other::Dynamic>(std::move(other.vector_));
    } else {
      return std::get<typename Other::Static>(std::move(other.vector_));
    }
  }

  template <auto InsertStatic, auto InsertDynamic, typename... Args>
  auto insert(Args&&... args) {
    if (Dynamic* const vector = std::get_if<Dynamic>(&vector_)) {
@@ -267,9 +292,10 @@ class SmallVector final : details::ArrayTraits<T>, details::ArrayComparators<Sma
// Partial specialization without static storage.
template <typename T>
class SmallVector<T, 0> final : details::ArrayTraits<T>,
                                details::ArrayComparators<SmallVector>,
                                details::ArrayIterators<SmallVector<T, 0>, T>,
                                std::vector<T> {
  using details::ArrayTraits<T>::construct_at;
  using details::ArrayTraits<T>::replace_at;

  using Iter = details::ArrayIterators<SmallVector, T>;
  using Impl = std::vector<T>;
@@ -291,8 +317,30 @@ class SmallVector<T, 0> final : details::ArrayTraits<T>,
  FTL_ARRAY_TRAIT(T, const_iterator);
  FTL_ARRAY_TRAIT(T, const_reverse_iterator);

  // See std::vector for underlying constructors.
  using Impl::Impl;

  // Copies and moves a vector, respectively.
  SmallVector(const SmallVector&) = default;
  SmallVector(SmallVector&&) = default;

  // Constructs elements in place. See StaticVector for underlying constructor.
  template <typename U, std::size_t... Sizes, typename... Types>
  SmallVector(InitializerList<U, std::index_sequence<Sizes...>, Types...>&& list)
      : SmallVector(SmallVector<T, sizeof...(Sizes)>(std::move(list))) {}

  // Copies or moves elements from a convertible vector.
  template <typename U, std::size_t M>
  SmallVector(SmallVector<U, M> other) : Impl(convert(std::move(other))) {}

  SmallVector& operator=(SmallVector other) {
    // Define copy/move assignment in terms of copy/move construction.
    swap(other);
    return *this;
  }

  void swap(SmallVector& other) { Impl::swap(other); }

  using Impl::empty;
  using Impl::max_size;
  using Impl::size;
@@ -324,10 +372,7 @@ class SmallVector<T, 0> final : details::ArrayTraits<T>,

  template <typename... Args>
  reference replace(const_iterator it, Args&&... args) {
    value_type element{std::forward<Args>(args)...};
    std::destroy_at(it);
    // This is only safe because exceptions are disabled.
    return *construct_at(it, std::move(element));
    return replace_at(it, std::forward<Args>(args)...);
  }

  template <typename... Args>
@@ -353,7 +398,26 @@ class SmallVector<T, 0> final : details::ArrayTraits<T>,
    pop_back();
  }

  void swap(SmallVector& other) { Impl::swap(other); }
  std::vector<T> promote() && { return std::move(*this); }

 private:
  template <typename U, std::size_t M>
  static Impl convert(SmallVector<U, M>&& other) {
    if constexpr (std::is_constructible_v<Impl, std::vector<U>&&>) {
      return std::move(other).promote();
    } else {
      SmallVector vector(other.size());

      // Consistently with StaticVector, T only requires copy/move construction from U, rather than
      // copy/move assignment.
      auto it = vector.begin();
      for (auto& element : other) {
        vector.replace(it++, std::move(element));
      }

      return vector;
    }
  }
};

template <typename>
+35 −11
Original line number Diff line number Diff line
@@ -39,6 +39,9 @@ constexpr struct IteratorRangeTag {
// adheres to standard containers, except the unstable_erase operation that does not preserve order,
// and the replace operation that destructively emplaces.
//
// Unlike std::vector, T does not require copy/move assignment, so may be an object with const data
// members, or be const itself.
//
// StaticVector<T, 1> is analogous to an iterable std::optional.
// StaticVector<T, 0> is an error.
//
@@ -78,7 +81,14 @@ class StaticVector final : details::ArrayTraits<T>,
                           details::ArrayComparators<StaticVector> {
  static_assert(N > 0);

  // For constructor that moves from a smaller convertible vector.
  template <typename, std::size_t>
  friend class StaticVector;

  using details::ArrayTraits<T>::construct_at;
  using details::ArrayTraits<T>::replace_at;
  using details::ArrayTraits<T>::in_place_swap_ranges;
  using details::ArrayTraits<T>::uninitialized_copy;

  using Iter = details::ArrayIterators<StaticVector, T>;
  friend Iter;
@@ -117,14 +127,18 @@ class StaticVector final : details::ArrayTraits<T>,
  StaticVector(StaticVector&& other) { swap<true>(other); }

  // Copies at most N elements from a smaller convertible vector.
  template <typename U, std::size_t M, typename = std::enable_if_t<M <= N>>
  template <typename U, std::size_t M>
  StaticVector(const StaticVector<U, M>& other)
      : StaticVector(kIteratorRange, other.begin(), other.end()) {}
      : StaticVector(kIteratorRange, other.begin(), other.end()) {
    static_assert(N >= M, "Insufficient capacity");
  }

  // Copies at most N elements from an array.
  // Copies at most N elements from a smaller convertible array.
  template <typename U, std::size_t M>
  explicit StaticVector(U (&array)[M])
      : StaticVector(kIteratorRange, std::begin(array), std::end(array)) {}
      : StaticVector(kIteratorRange, std::begin(array), std::end(array)) {
    static_assert(N >= M, "Insufficient capacity");
  }

  // Copies at most N elements from the range [first, last).
  //
@@ -139,7 +153,18 @@ class StaticVector final : details::ArrayTraits<T>,
  template <typename Iterator>
  StaticVector(IteratorRangeTag, Iterator first, Iterator last)
      : size_(std::min(max_size(), static_cast<size_type>(std::distance(first, last)))) {
    std::uninitialized_copy(first, first + size_, begin());
    uninitialized_copy(first, first + size_, begin());
  }

  // Moves at most N elements from a smaller convertible vector.
  template <typename U, std::size_t M>
  StaticVector(StaticVector<U, M>&& other) {
    static_assert(N >= M, "Insufficient capacity");

    // Same logic as swap<true>, though M need not be equal to N.
    std::uninitialized_move(other.begin(), other.end(), begin());
    std::destroy(other.begin(), other.end());
    std::swap(size_, other.size_);
  }

  // Constructs at most N elements. The template arguments T and N are inferred using the
@@ -178,7 +203,9 @@ class StaticVector final : details::ArrayTraits<T>,
  template <typename U, std::size_t Size, std::size_t... Sizes, typename... Types>
  StaticVector(InitializerList<U, std::index_sequence<Size, Sizes...>, Types...>&& list)
      : StaticVector(std::index_sequence<0, 0, Size>{}, std::make_index_sequence<Size>{},
                     std::index_sequence<Sizes...>{}, list.tuple) {}
                     std::index_sequence<Sizes...>{}, list.tuple) {
    static_assert(sizeof...(Sizes) < N, "Too many elements");
  }

  ~StaticVector() { std::destroy(begin(), end()); }

@@ -238,10 +265,7 @@ class StaticVector final : details::ArrayTraits<T>,
  //
  template <typename... Args>
  reference replace(const_iterator it, Args&&... args) {
    value_type element{std::forward<Args>(args)...};
    std::destroy_at(it);
    // This is only safe because exceptions are disabled.
    return *construct_at(it, std::move(element));
    return replace_at(it, std::forward<Args>(args)...);
  }

  // Appends an element, and returns an iterator to it. If the vector is full, the element is not
@@ -380,7 +404,7 @@ void StaticVector<T, N>::swap(StaticVector& other) {
    }

    // Swap elements [0, min).
    std::swap_ranges(begin(), begin() + min, other.begin());
    in_place_swap_ranges(begin(), begin() + min, other.begin());

    // No elements to move if sizes are equal.
    if (min == max) return;
+27 −0
Original line number Diff line number Diff line
@@ -96,6 +96,33 @@ TEST(SmallMap, Construct) {
  }
}

TEST(SmallMap, Assign) {
  {
    // Same types; smaller capacity.
    SmallMap map1 = ftl::init::map<char, std::string>('k', "kilo")('M', "mega")('G', "giga");
    const SmallMap map2 = ftl::init::map('T', "tera"s)('P', "peta"s);

    map1 = map2;
    EXPECT_EQ(map1, map2);
  }
  {
    // Convertible types; same capacity.
    SmallMap map1 = ftl::init::map<char, std::string>('M', "mega")('G', "giga");
    const SmallMap map2 = ftl::init::map('T', "tera")('P', "peta");

    map1 = map2;
    EXPECT_EQ(map1, map2);
  }
  {
    // Convertible types; zero capacity.
    SmallMap<char, std::string, 0> map1 = ftl::init::map('M', "mega")('G', "giga");
    const SmallMap<char, std::string, 0> map2 = ftl::init::map('T', "tera")('P', "peta");

    map1 = map2;
    EXPECT_EQ(map1, map2);
  }
}

TEST(SmallMap, UniqueKeys) {
  {
    // Duplicate mappings are discarded.
Loading