// 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_