xref: /third_party/gn/src/gn/unique_vector.h (revision 6d528ed9)
16d528ed9Sopenharmony_ci// Copyright 2014 The Chromium Authors. All rights reserved.
26d528ed9Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be
36d528ed9Sopenharmony_ci// found in the LICENSE file.
46d528ed9Sopenharmony_ci
56d528ed9Sopenharmony_ci#ifndef TOOLS_GN_UNIQUE_VECTOR_H_
66d528ed9Sopenharmony_ci#define TOOLS_GN_UNIQUE_VECTOR_H_
76d528ed9Sopenharmony_ci
86d528ed9Sopenharmony_ci#include <stddef.h>
96d528ed9Sopenharmony_ci#include <stdint.h>
106d528ed9Sopenharmony_ci
116d528ed9Sopenharmony_ci#include <algorithm>
126d528ed9Sopenharmony_ci#include <functional>
136d528ed9Sopenharmony_ci#include <vector>
146d528ed9Sopenharmony_ci
156d528ed9Sopenharmony_ci#include "hash_table_base.h"
166d528ed9Sopenharmony_ci
176d528ed9Sopenharmony_ci// A HashTableBase node type used by all UniqueVector<> instantiations.
186d528ed9Sopenharmony_ci// The node stores the item's hash value and its index plus 1, in order
196d528ed9Sopenharmony_ci// to follow the HashTableBase requirements (i.e. zero-initializable null
206d528ed9Sopenharmony_ci// value).
216d528ed9Sopenharmony_cistruct UniqueVectorNode {
226d528ed9Sopenharmony_ci  uint32_t hash32;
236d528ed9Sopenharmony_ci  uint32_t index_plus1;
246d528ed9Sopenharmony_ci
256d528ed9Sopenharmony_ci  size_t hash_value() const { return hash32; }
266d528ed9Sopenharmony_ci
276d528ed9Sopenharmony_ci  bool is_valid() const { return !is_null(); }
286d528ed9Sopenharmony_ci
296d528ed9Sopenharmony_ci  bool is_null() const { return index_plus1 == 0; }
306d528ed9Sopenharmony_ci
316d528ed9Sopenharmony_ci  // Do not support deletion, making lookup faster.
326d528ed9Sopenharmony_ci  static constexpr bool is_tombstone() { return false; }
336d528ed9Sopenharmony_ci
346d528ed9Sopenharmony_ci  // Return vector index.
356d528ed9Sopenharmony_ci  size_t index() const { return index_plus1 - 1u; }
366d528ed9Sopenharmony_ci
376d528ed9Sopenharmony_ci  static uint32_t ToHash32(size_t hash) { return static_cast<uint32_t>(hash); }
386d528ed9Sopenharmony_ci
396d528ed9Sopenharmony_ci  // Create new instance from hash value and vector index.
406d528ed9Sopenharmony_ci  static UniqueVectorNode Make(size_t hash, size_t index) {
416d528ed9Sopenharmony_ci    return {ToHash32(hash), static_cast<uint32_t>(index + 1u)};
426d528ed9Sopenharmony_ci  }
436d528ed9Sopenharmony_ci};
446d528ed9Sopenharmony_ci
456d528ed9Sopenharmony_ciusing UniqueVectorHashTableBase = HashTableBase<UniqueVectorNode>;
466d528ed9Sopenharmony_ci
476d528ed9Sopenharmony_ci// A common HashSet implementation used by all UniqueVector instantiations.
486d528ed9Sopenharmony_ciclass UniqueVectorHashSet : public UniqueVectorHashTableBase {
496d528ed9Sopenharmony_ci public:
506d528ed9Sopenharmony_ci  using BaseType = UniqueVectorHashTableBase;
516d528ed9Sopenharmony_ci  using Node = BaseType::Node;
526d528ed9Sopenharmony_ci
536d528ed9Sopenharmony_ci  // Specialized Lookup() template function.
546d528ed9Sopenharmony_ci  // |hash| is the hash value for |item|.
556d528ed9Sopenharmony_ci  // |item| is the item search key being looked up.
566d528ed9Sopenharmony_ci  // |vector| is containing vector for existing items.
576d528ed9Sopenharmony_ci  //
586d528ed9Sopenharmony_ci  // Returns a non-null mutable Node pointer.
596d528ed9Sopenharmony_ci  template <typename T, typename EqualTo = std::equal_to<T>>
606d528ed9Sopenharmony_ci  Node* Lookup(size_t hash, const T& item, const std::vector<T>& vector) const {
616d528ed9Sopenharmony_ci    uint32_t hash32 = Node::ToHash32(hash);
626d528ed9Sopenharmony_ci    return BaseType::NodeLookup(hash32, [&](const Node* node) {
636d528ed9Sopenharmony_ci      return hash32 == node->hash32 && EqualTo()(vector[node->index()], item);
646d528ed9Sopenharmony_ci    });
656d528ed9Sopenharmony_ci  }
666d528ed9Sopenharmony_ci
676d528ed9Sopenharmony_ci  // Specialized Insert() function that converts |index| into the proper
686d528ed9Sopenharmony_ci  // UniqueVectorKey type.
696d528ed9Sopenharmony_ci  void Insert(Node* node, size_t hash, size_t index) {
706d528ed9Sopenharmony_ci    *node = Node::Make(hash, index);
716d528ed9Sopenharmony_ci    BaseType::UpdateAfterInsert();
726d528ed9Sopenharmony_ci  }
736d528ed9Sopenharmony_ci
746d528ed9Sopenharmony_ci  void Clear() { NodeClear(); }
756d528ed9Sopenharmony_ci};
766d528ed9Sopenharmony_ci
776d528ed9Sopenharmony_ci// An ordered set optimized for GN's usage. Such sets are used to store lists
786d528ed9Sopenharmony_ci// of configs and libraries, and are appended to but not randomly inserted
796d528ed9Sopenharmony_ci// into.
806d528ed9Sopenharmony_citemplate <typename T,
816d528ed9Sopenharmony_ci          typename Hash = std::hash<T>,
826d528ed9Sopenharmony_ci          typename EqualTo = std::equal_to<T>>
836d528ed9Sopenharmony_ciclass UniqueVector {
846d528ed9Sopenharmony_ci public:
856d528ed9Sopenharmony_ci  using Vector = std::vector<T>;
866d528ed9Sopenharmony_ci  using iterator = typename Vector::iterator;
876d528ed9Sopenharmony_ci  using const_iterator = typename Vector::const_iterator;
886d528ed9Sopenharmony_ci
896d528ed9Sopenharmony_ci  const Vector& vector() const { return vector_; }
906d528ed9Sopenharmony_ci  size_t size() const { return vector_.size(); }
916d528ed9Sopenharmony_ci  bool empty() const { return vector_.empty(); }
926d528ed9Sopenharmony_ci  void clear() {
936d528ed9Sopenharmony_ci    vector_.clear();
946d528ed9Sopenharmony_ci    set_.Clear();
956d528ed9Sopenharmony_ci  }
966d528ed9Sopenharmony_ci  void reserve(size_t s) { vector_.reserve(s); }
976d528ed9Sopenharmony_ci
986d528ed9Sopenharmony_ci  const T& operator[](size_t index) const { return vector_[index]; }
996d528ed9Sopenharmony_ci
1006d528ed9Sopenharmony_ci  const_iterator begin() const { return vector_.begin(); }
1016d528ed9Sopenharmony_ci  const_iterator end() const { return vector_.end(); }
1026d528ed9Sopenharmony_ci
1036d528ed9Sopenharmony_ci  // Extract the vector out of the instance, clearing it at the same time.
1046d528ed9Sopenharmony_ci  Vector release() {
1056d528ed9Sopenharmony_ci    Vector result = std::move(vector_);
1066d528ed9Sopenharmony_ci    clear();
1076d528ed9Sopenharmony_ci    return result;
1086d528ed9Sopenharmony_ci  }
1096d528ed9Sopenharmony_ci
1106d528ed9Sopenharmony_ci  // Returns true if the item was appended, false if it already existed (and
1116d528ed9Sopenharmony_ci  // thus the vector was not modified).
1126d528ed9Sopenharmony_ci  bool push_back(const T& t) {
1136d528ed9Sopenharmony_ci    size_t hash;
1146d528ed9Sopenharmony_ci    auto* node = Lookup(t, &hash);
1156d528ed9Sopenharmony_ci    if (node->is_valid()) {
1166d528ed9Sopenharmony_ci      return false;  // Already have this one.
1176d528ed9Sopenharmony_ci    }
1186d528ed9Sopenharmony_ci    vector_.push_back(t);
1196d528ed9Sopenharmony_ci    set_.Insert(node, hash, vector_.size() - 1);
1206d528ed9Sopenharmony_ci    return true;
1216d528ed9Sopenharmony_ci  }
1226d528ed9Sopenharmony_ci
1236d528ed9Sopenharmony_ci  // Same as above, but moves the item into the vector if possible.
1246d528ed9Sopenharmony_ci  bool push_back(T&& t) {
1256d528ed9Sopenharmony_ci    size_t hash = Hash()(t);
1266d528ed9Sopenharmony_ci    auto* node = Lookup(t, &hash);
1276d528ed9Sopenharmony_ci    if (node->is_valid()) {
1286d528ed9Sopenharmony_ci      return false;  // Already have this one.
1296d528ed9Sopenharmony_ci    }
1306d528ed9Sopenharmony_ci    vector_.push_back(std::move(t));
1316d528ed9Sopenharmony_ci    set_.Insert(node, hash, vector_.size() - 1);
1326d528ed9Sopenharmony_ci    return true;
1336d528ed9Sopenharmony_ci  }
1346d528ed9Sopenharmony_ci
1356d528ed9Sopenharmony_ci  // Construct an item in-place if possible. Return true if it was
1366d528ed9Sopenharmony_ci  // appended, false otherwise.
1376d528ed9Sopenharmony_ci  template <typename... ARGS>
1386d528ed9Sopenharmony_ci  bool emplace_back(ARGS... args) {
1396d528ed9Sopenharmony_ci    return push_back(T{std::forward<ARGS>(args)...});
1406d528ed9Sopenharmony_ci  }
1416d528ed9Sopenharmony_ci
1426d528ed9Sopenharmony_ci  // Try to add an item to the vector. Return (true, index) in
1436d528ed9Sopenharmony_ci  // case of insertion, or (false, index) otherwise. In both cases
1446d528ed9Sopenharmony_ci  // |index| will be the item's index in the set and will not be
1456d528ed9Sopenharmony_ci  // kIndexNone. This can be used to implement a map using a
1466d528ed9Sopenharmony_ci  // UniqueVector<> for keys, and a parallel array for values.
1476d528ed9Sopenharmony_ci  std::pair<bool, size_t> PushBackWithIndex(const T& t) {
1486d528ed9Sopenharmony_ci    size_t hash;
1496d528ed9Sopenharmony_ci    auto* node = Lookup(t, &hash);
1506d528ed9Sopenharmony_ci    if (node->is_valid()) {
1516d528ed9Sopenharmony_ci      return {false, node->index()};
1526d528ed9Sopenharmony_ci    }
1536d528ed9Sopenharmony_ci    size_t result = vector_.size();
1546d528ed9Sopenharmony_ci    vector_.push_back(t);
1556d528ed9Sopenharmony_ci    set_.Insert(node, hash, result);
1566d528ed9Sopenharmony_ci    return {true, result};
1576d528ed9Sopenharmony_ci  }
1586d528ed9Sopenharmony_ci
1596d528ed9Sopenharmony_ci  // Same as above, but moves the item into the set on success.
1606d528ed9Sopenharmony_ci  std::pair<bool, size_t> PushBackWithIndex(T&& t) {
1616d528ed9Sopenharmony_ci    size_t hash;
1626d528ed9Sopenharmony_ci    auto* node = Lookup(t, &hash);
1636d528ed9Sopenharmony_ci    if (node->is_valid()) {
1646d528ed9Sopenharmony_ci      return {false, node->index()};
1656d528ed9Sopenharmony_ci    }
1666d528ed9Sopenharmony_ci    size_t result = vector_.size();
1676d528ed9Sopenharmony_ci    vector_.push_back(std::move(t));
1686d528ed9Sopenharmony_ci    set_.Insert(node, hash, result);
1696d528ed9Sopenharmony_ci    return {true, result};
1706d528ed9Sopenharmony_ci  }
1716d528ed9Sopenharmony_ci
1726d528ed9Sopenharmony_ci  // Construct an item in-place if possible. If a corresponding
1736d528ed9Sopenharmony_ci  // item is already in the vector, return (false, index), otherwise
1746d528ed9Sopenharmony_ci  // perform the insertion and return (true, index). In both cases
1756d528ed9Sopenharmony_ci  // |index| will be the item's index in the vector and will not be
1766d528ed9Sopenharmony_ci  // kIndexNone.
1776d528ed9Sopenharmony_ci  template <typename... ARGS>
1786d528ed9Sopenharmony_ci  std::pair<bool, size_t> EmplaceBackWithIndex(ARGS... args) {
1796d528ed9Sopenharmony_ci    return PushBackWithIndex(T{std::forward<ARGS>(args)...});
1806d528ed9Sopenharmony_ci  }
1816d528ed9Sopenharmony_ci
1826d528ed9Sopenharmony_ci  // Appends a range of items from an iterator.
1836d528ed9Sopenharmony_ci  template <typename iter>
1846d528ed9Sopenharmony_ci  void Append(const iter& begin, const iter& end) {
1856d528ed9Sopenharmony_ci    for (iter i = begin; i != end; ++i)
1866d528ed9Sopenharmony_ci      push_back(*i);
1876d528ed9Sopenharmony_ci  }
1886d528ed9Sopenharmony_ci
1896d528ed9Sopenharmony_ci  // Append from any iterable container with begin() and end()
1906d528ed9Sopenharmony_ci  // methods, whose iterators derefence to values convertible to T.
1916d528ed9Sopenharmony_ci  template <typename C,
1926d528ed9Sopenharmony_ci            typename = std::void_t<
1936d528ed9Sopenharmony_ci                decltype(static_cast<const T>(*std::declval<C>().begin())),
1946d528ed9Sopenharmony_ci                decltype(static_cast<const T>(*std::declval<C>().end()))>>
1956d528ed9Sopenharmony_ci  void Append(const C& other) {
1966d528ed9Sopenharmony_ci    Append(other.begin(), other.end());
1976d528ed9Sopenharmony_ci  }
1986d528ed9Sopenharmony_ci
1996d528ed9Sopenharmony_ci  // Append from any moveable iterable container with begin() and
2006d528ed9Sopenharmony_ci  // end() methods. This variant moves items from the container
2016d528ed9Sopenharmony_ci  // into the UniqueVector instance.
2026d528ed9Sopenharmony_ci  template <typename C,
2036d528ed9Sopenharmony_ci            typename = std::void_t<
2046d528ed9Sopenharmony_ci                decltype(static_cast<T>(*std::declval<C>().begin())),
2056d528ed9Sopenharmony_ci                decltype(static_cast<T>(*std::declval<C>().end()))>>
2066d528ed9Sopenharmony_ci  void Append(C&& other) {
2076d528ed9Sopenharmony_ci    for (auto it = other.begin(); it != other.end(); ++it)
2086d528ed9Sopenharmony_ci      push_back(std::move(*it));
2096d528ed9Sopenharmony_ci  }
2106d528ed9Sopenharmony_ci
2116d528ed9Sopenharmony_ci  // Returns true if the item is already in the vector.
2126d528ed9Sopenharmony_ci  bool Contains(const T& t) const {
2136d528ed9Sopenharmony_ci    size_t hash;
2146d528ed9Sopenharmony_ci    return Lookup(t, &hash)->is_valid();
2156d528ed9Sopenharmony_ci  }
2166d528ed9Sopenharmony_ci
2176d528ed9Sopenharmony_ci  // Returns the index of the item matching the given value in the list, or
2186d528ed9Sopenharmony_ci  // kIndexNone if it's not found.
2196d528ed9Sopenharmony_ci  size_t IndexOf(const T& t) const {
2206d528ed9Sopenharmony_ci    size_t hash;
2216d528ed9Sopenharmony_ci    return Lookup(t, &hash)->index();
2226d528ed9Sopenharmony_ci  }
2236d528ed9Sopenharmony_ci
2246d528ed9Sopenharmony_ci  static constexpr size_t kIndexNone = 0xffffffffu;
2256d528ed9Sopenharmony_ci
2266d528ed9Sopenharmony_ci private:
2276d528ed9Sopenharmony_ci  // Lookup hash set node for item |t|, also sets |*hash| to the hash value.
2286d528ed9Sopenharmony_ci  UniqueVectorNode* Lookup(const T& t, size_t* hash) const {
2296d528ed9Sopenharmony_ci    *hash = Hash()(t);
2306d528ed9Sopenharmony_ci    return set_.Lookup<T, EqualTo>(*hash, t, vector_);
2316d528ed9Sopenharmony_ci  }
2326d528ed9Sopenharmony_ci
2336d528ed9Sopenharmony_ci  Vector vector_;
2346d528ed9Sopenharmony_ci  UniqueVectorHashSet set_;
2356d528ed9Sopenharmony_ci};
2366d528ed9Sopenharmony_ci
2376d528ed9Sopenharmony_ci#endif  // TOOLS_GN_UNIQUE_VECTOR_H_
238