Add TargetPublicPair class.

This is conceptually similar to InheritedLibraries, except that this
separates the temporary UniqueVector<> used during construction, from
the final result, which is a simple vector of (target, is_public) pairs
instead, which is smaller to store and faster to parse during target
resolution.

This will be used in a future CL that moves and caches target resolution
computations to a dedicated class.

Bug: None

Change-Id: I0f7840d105e812a7d43c415ce5e71e43fec1268e
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/14880
Commit-Queue: David Turner <digit@google.com>
Reviewed-by: Brett Wilson <brettw@chromium.org>
diff --git a/build/gen.py b/build/gen.py
index fa23266..eae97f9 100755
--- a/build/gen.py
+++ b/build/gen.py
@@ -818,6 +818,7 @@
         'src/gn/string_utils_unittest.cc',
         'src/gn/substitution_pattern_unittest.cc',
         'src/gn/substitution_writer_unittest.cc',
+        'src/gn/target_public_pair_unittest.cc',
         'src/gn/target_unittest.cc',
         'src/gn/template_unittest.cc',
         'src/gn/test_with_scheduler.cc',
diff --git a/src/gn/target_public_pair.h b/src/gn/target_public_pair.h
new file mode 100644
index 0000000..f211a81
--- /dev/null
+++ b/src/gn/target_public_pair.h
@@ -0,0 +1,131 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_GN_TARGET_PUBLIC_PAIR_H_
+#define TOOLS_GN_TARGET_PUBLIC_PAIR_H_
+
+#include "gn/unique_vector.h"
+
+class Target;
+
+// C++ and Rust target resolution requires computing uniquified and
+// ordered lists of static/shared libraries that are collected through
+// the target's dependency tree.
+//
+// Maintaining the order is important to ensure the libraries are linked
+// in the correct order in the final link command line.
+//
+// Also each library must only appear once in the final list, even though
+// it may appear multiple times during the dependency tree walk, either as
+// a "private" or "public" dependency.
+//
+// The TargetPublicPair class below encodes a (target_ptr, is_public_flag)
+// pair, with convenience accessors and utility structs.
+//
+// The TargetPublicPairListBuilder is a builder-pattern class that generates
+// a unique vector of TargetPublicPair values (i.e. the final list described
+// above), and supporting the special logic required to build these lists
+// (see the comments for its Append() and AppendInherited() methods).
+//
+// A convenience encoding for a (target_ptr, is_public_flag) pair.
+class TargetPublicPair {
+ public:
+  TargetPublicPair() = default;
+  TargetPublicPair(const Target* target, bool is_public)
+      : target_(target), is_public_(is_public) {}
+  TargetPublicPair(std::pair<const Target*, bool> pair)
+      : target_(pair.first), is_public_(pair.second) {}
+
+  const Target* target() const { return target_; }
+  void set_target(const Target* target) { target_ = target; }
+
+  bool is_public() const { return is_public_; }
+  void set_is_public(bool is_public) { is_public_ = is_public; }
+
+  // Utility structs that can be used to instantiante containers
+  // that only use the target for lookups / comparisons. E.g.
+  //
+  //   std::unordered_set<TargetPublicPair,
+  //                      TargetPublicPair::TargetHash,
+  //                      TargetPublicPair::TargetEqualTo>
+  //
+  //   std::set<TargetPublicPair, TargetPublicPair::TargetLess>
+  //
+  struct TargetHash {
+    size_t operator()(TargetPublicPair p) const noexcept {
+      return std::hash<const Target*>()(p.target());
+    }
+  };
+
+  struct TargetEqualTo {
+    bool operator()(TargetPublicPair a, TargetPublicPair b) const noexcept {
+      return a.target() == b.target();
+    }
+  };
+
+  struct TargetLess {
+    bool operator()(TargetPublicPair a, TargetPublicPair b) const noexcept {
+      return a.target() < b.target();
+    }
+  };
+
+ private:
+  const Target* target_ = nullptr;
+  bool is_public_ = false;
+};
+
+// A helper type to build a uniquified ordered vector of TargetPublicPair
+// instances. Usage is:
+//
+//   1) Create builder instance.
+//
+//   2) Call Append() to add a direct dependency, or AppendInherited() to add
+//      transitive ones, as many times as necessary.
+//
+//   3) Call Build() to retrieve final list as a vector.
+//
+class TargetPublicPairListBuilder
+    : public UniqueVector<TargetPublicPair,
+                          TargetPublicPair::TargetHash,
+                          TargetPublicPair::TargetEqualTo> {
+ public:
+  // Add (target, is_public) to the list being constructed. If the target
+  // was not already in the list, record the |is_public| flag as is,
+  // otherwise, set the recorded flag to true only if |is_public| is true, or
+  // don't do anything otherwise.
+  void Append(const Target* target, bool is_public) {
+    auto ret = EmplaceBackWithIndex(target, is_public);
+    if (!ret.first && is_public) {
+      // UniqueVector<T>::operator[]() always returns a const reference
+      // because the returned values are lookup keys in its set-like data
+      // structure (thus modifying them would break its internal consistency).
+      // However, because TargetHash and TargetEqualTo are being used to
+      // instantiate this template, only the target() part of the value must
+      // remain constant, and it is possible to modify the is_public() part
+      // in-place safely.
+      auto* pair = const_cast<TargetPublicPair*>(&(*this)[ret.second]);
+      pair->set_is_public(true);
+    }
+  }
+
+  // Append all pairs from any container with begin() and end() iterators
+  // that dereference to values that convert to a TargetPublicPair value.
+  // If |is_public| is false, the input pair will be appended with the
+  // value of the public flag to false.
+  template <
+      typename C,
+      typename = std::void_t<
+          decltype(static_cast<TargetPublicPair>(*std::declval<C>().begin())),
+          decltype(static_cast<TargetPublicPair>(*std::declval<C>().end()))>>
+  void AppendInherited(const C& other, bool is_public) {
+    for (const auto& pair : other) {
+      Append(pair.target(), is_public && pair.is_public());
+    }
+  }
+
+  // Build and return the final list to the caller.
+  std::vector<TargetPublicPair> Build() { return release(); }
+};
+
+#endif  // TOOLS_GN_TARGET_PUBLIC_PAIR_H_
diff --git a/src/gn/target_public_pair_unittest.cc b/src/gn/target_public_pair_unittest.cc
new file mode 100644
index 0000000..8b08a0c
--- /dev/null
+++ b/src/gn/target_public_pair_unittest.cc
@@ -0,0 +1,54 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gn/target_public_pair.h"
+#include "util/test/test.h"
+
+TEST(TargetPublicPairTest, ConstructionAndMutation) {
+  // Fake target pointer values.
+  const auto* a_target = reinterpret_cast<const Target*>(1000);
+  const auto* b_target = reinterpret_cast<const Target*>(2000);
+
+  TargetPublicPair a_pair(a_target, true);
+  EXPECT_EQ(a_target, a_pair.target());
+  EXPECT_TRUE(a_pair.is_public());
+
+  TargetPublicPair b_pair(b_target, false);
+  EXPECT_EQ(b_target, b_pair.target());
+  EXPECT_FALSE(b_pair.is_public());
+
+  a_pair.set_target(b_target);
+  EXPECT_EQ(b_target, a_pair.target());
+  EXPECT_TRUE(a_pair.is_public());
+
+  a_pair.set_is_public(false);
+  EXPECT_EQ(b_target, a_pair.target());
+  EXPECT_FALSE(a_pair.is_public());
+
+  a_pair = TargetPublicPair(a_target, true);
+  EXPECT_EQ(a_target, a_pair.target());
+  EXPECT_TRUE(a_pair.is_public());
+
+  b_pair = std::move(a_pair);
+  EXPECT_EQ(a_target, b_pair.target());
+  EXPECT_TRUE(b_pair.is_public());
+}
+
+TEST(TargetPublicPairTest, Builder) {
+  const auto* a_target = reinterpret_cast<const Target*>(1000);
+  const auto* b_target = reinterpret_cast<const Target*>(2000);
+  TargetPublicPairListBuilder builder;
+
+  builder.Append(a_target, false);
+  builder.Append(b_target, false);
+  builder.Append(a_target, true);
+  builder.Append(b_target, false);
+
+  auto list = builder.Build();
+  EXPECT_EQ(2u, list.size());
+  EXPECT_EQ(a_target, list[0].target());
+  EXPECT_EQ(b_target, list[1].target());
+  EXPECT_TRUE(list[0].is_public());
+  EXPECT_FALSE(list[1].is_public());
+}