Add ResolvedTargetDeps class

Which provides simpler and faster ways to parse target dependencies.

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

Change-Id: Ic411989d136346cee72ca877e3c96021113dc901
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/14881
Reviewed-by: Brett Wilson <brettw@chromium.org>
Commit-Queue: David Turner <digit@google.com>
diff --git a/build/gen.py b/build/gen.py
index eae97f9..2d109ed 100755
--- a/build/gen.py
+++ b/build/gen.py
@@ -807,6 +807,7 @@
         'src/gn/path_output_unittest.cc',
         'src/gn/pattern_unittest.cc',
         'src/gn/pointer_set_unittest.cc',
+        'src/gn/resolved_target_deps_unittest.cc',
         'src/gn/runtime_deps_unittest.cc',
         'src/gn/scope_per_file_provider_unittest.cc',
         'src/gn/scope_unittest.cc',
diff --git a/src/gn/resolved_target_deps.h b/src/gn/resolved_target_deps.h
new file mode 100644
index 0000000..57e961f
--- /dev/null
+++ b/src/gn/resolved_target_deps.h
@@ -0,0 +1,90 @@
+// 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_RESOLVED_TARGET_DEPS_H_
+#define TOOLS_GN_RESOLVED_TARGET_DEPS_H_
+
+#include "base/containers/span.h"
+#include "gn/label_ptr.h"
+
+#include <memory>
+
+// A class used to record the dependencies of a given Target in
+// a way that is much more efficient to iterate over than having three
+// separate LabelTargetVector instances. Technically equivalent to
+// DepsIterator, but profiling shows that this class is much faster
+// to use during graph-traversal heavy operations.
+//
+// Usage is:
+//   1) Create instance, passing const references to the LabelTargetVector
+//      instances for the private, public and data deps for the target.
+//
+//   2) Use private_deps(), public_deps(), data_deps(), linked_deps()
+//      and all_deps() to retrieve spans that cover various subsets of
+//      interests. These can be used directly in for-range loops as in:
+//
+//       for (const Target* target : resolved.linked_deps()) {
+//         ..
+//       }
+//
+class ResolvedTargetDeps {
+ public:
+  ResolvedTargetDeps() = default;
+
+  ResolvedTargetDeps(const LabelTargetVector& public_deps,
+                     const LabelTargetVector& private_deps,
+                     const LabelTargetVector& data_deps)
+      : public_count_(static_cast<uint32_t>(public_deps.size())),
+        private_count_(static_cast<uint32_t>(private_deps.size())),
+        data_count_(static_cast<uint32_t>(data_deps.size())),
+        deps_(Allocate(public_deps, private_deps, data_deps)) {}
+
+  size_t size() const { return private_count_ + public_count_ + data_count_; }
+
+  base::span<const Target*> public_deps() const {
+    return {deps_.get(), public_count_};
+  }
+
+  base::span<const Target*> private_deps() const {
+    return {deps_.get() + public_count_, private_count_};
+  }
+
+  base::span<const Target*> data_deps() const {
+    return {deps_.get() + private_count_ + public_count_, data_count_};
+  }
+
+  base::span<const Target*> linked_deps() const {
+    return {deps_.get(), private_count_ + public_count_};
+  }
+
+  base::span<const Target*> all_deps() const {
+    return {deps_.get(), private_count_ + public_count_ + data_count_};
+  }
+
+  static std::unique_ptr<const Target*[]> Allocate(
+      const LabelTargetVector& public_deps,
+      const LabelTargetVector& private_deps,
+      const LabelTargetVector& data_deps) {
+    size_t total_size =
+        private_deps.size() + public_deps.size() + data_deps.size();
+    auto result = std::make_unique<const Target*[]>(total_size);
+    const Target** ptr = result.get();
+    for (const auto& pair : public_deps)
+      *ptr++ = pair.ptr;
+    for (const auto& pair : private_deps)
+      *ptr++ = pair.ptr;
+    for (const auto& pair : data_deps)
+      *ptr++ = pair.ptr;
+    return result;
+  }
+
+ private:
+  uint32_t public_count_ = 0;
+  uint32_t private_count_ = 0;
+  uint32_t data_count_ = 0;
+  // Store the pointers in the following order: public, private, data.
+  std::unique_ptr<const Target*[]> deps_;
+};
+
+#endif  // TOOLS_GN_RESOLVED_TARGET_DEPS_H_
diff --git a/src/gn/resolved_target_deps_unittest.cc b/src/gn/resolved_target_deps_unittest.cc
new file mode 100644
index 0000000..74d623d
--- /dev/null
+++ b/src/gn/resolved_target_deps_unittest.cc
@@ -0,0 +1,63 @@
+// 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/resolved_target_deps.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+TEST(ResolvedTargetDeps, DefaultConstruction) {
+  ResolvedTargetDeps deps;
+  EXPECT_EQ(0u, deps.size());
+  EXPECT_TRUE(deps.public_deps().empty());
+  EXPECT_TRUE(deps.private_deps().empty());
+  EXPECT_TRUE(deps.data_deps().empty());
+  EXPECT_TRUE(deps.linked_deps().empty());
+  EXPECT_TRUE(deps.all_deps().empty());
+}
+
+TEST(ResolvedTargetDeps, Construction) {
+  TestWithScope setup;
+  TestTarget a(setup, "//foo:a", Target::STATIC_LIBRARY);
+  TestTarget b(setup, "//foo:b", Target::SOURCE_SET);
+  TestTarget c(setup, "//foo:c", Target::SOURCE_SET);
+  TestTarget d(setup, "//foo:d", Target::SOURCE_SET);
+  TestTarget e(setup, "//foo:e", Target::EXECUTABLE);
+
+  LabelTargetVector public_vec;
+  LabelTargetVector private_vec;
+  LabelTargetVector data_vec;
+
+  public_vec.emplace_back(&a);
+  public_vec.emplace_back(&b);
+  private_vec.emplace_back(&c);
+  private_vec.emplace_back(&d);
+  data_vec.emplace_back(&e);
+
+  ResolvedTargetDeps deps(public_vec, private_vec, data_vec);
+  EXPECT_EQ(5u, deps.size());
+
+  EXPECT_EQ(2u, deps.public_deps().size());
+  EXPECT_EQ(&a, deps.public_deps()[0]);
+  EXPECT_EQ(&b, deps.public_deps()[1]);
+
+  EXPECT_EQ(2u, deps.private_deps().size());
+  EXPECT_EQ(&c, deps.private_deps()[0]);
+  EXPECT_EQ(&d, deps.private_deps()[1]);
+
+  EXPECT_EQ(1u, deps.data_deps().size());
+  EXPECT_EQ(&e, deps.data_deps()[0]);
+
+  EXPECT_EQ(4u, deps.linked_deps().size());
+  EXPECT_EQ(&a, deps.linked_deps()[0]);
+  EXPECT_EQ(&b, deps.linked_deps()[1]);
+  EXPECT_EQ(&c, deps.linked_deps()[2]);
+  EXPECT_EQ(&d, deps.linked_deps()[3]);
+
+  EXPECT_EQ(5u, deps.all_deps().size());
+  EXPECT_EQ(&a, deps.all_deps()[0]);
+  EXPECT_EQ(&b, deps.all_deps()[1]);
+  EXPECT_EQ(&c, deps.all_deps()[2]);
+  EXPECT_EQ(&d, deps.all_deps()[3]);
+  EXPECT_EQ(&e, deps.all_deps()[4]);
+}