summaryrefslogtreecommitdiff
path: root/src/buildtool/execution_api/common/local_tree_map.hpp
blob: 915c7a7161a9adc36c8a14f0100f21de3c6b7383 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#ifndef INCLUDED_SRC_BUILDTOOL_EXECUTION_API_COMMON_LOCAL_TREE_MAP_HPP
#define INCLUDED_SRC_BUILDTOOL_EXECUTION_API_COMMON_LOCAL_TREE_MAP_HPP

#include <filesystem>
#include <shared_mutex>
#include <string>
#include <unordered_map>
#include <unordered_set>

#include "gsl-lite/gsl-lite.hpp"
#include "src/buildtool/common/artifact.hpp"
#include "src/buildtool/logging/logger.hpp"

/// \brief Maps `bazel_re::Digest` to `LocalTree`.
/// Digest may refer to `bazel_re::Directory` or Git tree object, depending on
/// mode being compatible or native, respectively.
class LocalTreeMap {
    /// \brief Thread-safe pool of unique object infos.
    class ObjectInfoPool {
      public:
        /// Get pointer to stored info, or a add new one and return its pointer.
        [[nodiscard]] auto GetOrAdd(Artifact::ObjectInfo const& info)
            -> Artifact::ObjectInfo const* {
            {  // get
                std::shared_lock lock{mutex_};
                auto it = infos_.find(info);
                if (it != infos_.end()) {
                    return &(*it);
                }
            }
            {  // or add
                std::unique_lock lock{mutex_};
                return &(*infos_.emplace(info).first);
            }
        }

      private:
        std::unordered_set<Artifact::ObjectInfo> infos_;
        mutable std::shared_mutex mutex_;
    };

  public:
    /// \brief Maps blob paths to object infos.
    /// Stores a flat list of blobs, without any trees. Subtrees are represented
    /// by joining the tree segments for the blob's path.
    class LocalTree {
        friend class LocalTreeMap;

      public:
        /// \brief Add a new path and info pair to the tree.
        /// Path must not be absolute, empty, or contain dot-segments.
        /// Object MUST NOT be a tree object.
        /// \param path The location to add the object info.
        /// \param info The object info to add.
        /// \returns true if successfully inserted or info existed before.
        [[nodiscard]] auto AddInfo(std::filesystem::path const& path,
                                   Artifact::ObjectInfo const& info) noexcept
            -> bool {
            gsl_Expects(not IsTreeObject(info.type));
            auto norm_path = path.lexically_normal();
            if (norm_path.is_absolute() or norm_path.empty() or
                *norm_path.begin() == "..") {
                Logger::Log(LogLevel::Error,
                            "cannot add malformed path to local tree: {}",
                            path.string());
                return false;
            }
            try {
                if (entries_.contains(norm_path.string())) {
                    return true;
                }
                if (auto const* info_ptr = infos_->GetOrAdd(info)) {
                    entries_.emplace(norm_path.string(), info_ptr);
                    return true;
                }
            } catch (std::exception const& ex) {
                Logger::Log(LogLevel::Error,
                            "adding object info to tree failed with:\n{}",
                            ex.what());
            }
            return false;
        }

        [[nodiscard]] auto size() const noexcept { return entries_.size(); }
        [[nodiscard]] auto begin() const noexcept { return entries_.begin(); }
        [[nodiscard]] auto end() const noexcept { return entries_.end(); }

      private:
        gsl::not_null<ObjectInfoPool*> infos_;
        std::unordered_map<std::string,
                           gsl::not_null<Artifact::ObjectInfo const*>>
            entries_{};

        explicit LocalTree(gsl::not_null<ObjectInfoPool*> infos) noexcept
            : infos_{std::move(infos)} {}
    };

    /// \brief Create a new `LocalTree` object.
    [[nodiscard]] auto CreateTree() noexcept -> LocalTree {
        return LocalTree{&infos_};
    }

    /// \brief Get pointer to existing `LocalTree` object.
    /// \param root_digest  The root digest of the tree to lookup.
    /// \returns nullptr if no tree was found for given root digest.
    [[nodiscard]] auto GetTree(bazel_re::Digest const& root_digest)
        const noexcept -> LocalTree const* {
        std::shared_lock lock{mutex_};
        auto it = trees_.find(root_digest);
        return (it != trees_.end()) ? &(it->second) : nullptr;
    }

    /// \brief Checks if entry for root digest exists.
    [[nodiscard]] auto HasTree(
        bazel_re::Digest const& root_digest) const noexcept -> bool {
        return GetTree(root_digest) != nullptr;
    }

    /// \brief Add new `LocalTree` for given root digest. Does not overwrite if
    /// a tree for the given root digest already exists.
    /// \param root_digest  The root digest to add the new tree for.
    /// \param tree         The new tree to add.
    /// \returns true if the tree was successfully added or existed before.
    [[nodiscard]] auto AddTree(bazel_re::Digest const& root_digest,
                               LocalTree&& tree) noexcept -> bool {
        if (not HasTree(root_digest)) {
            try {
                std::unique_lock lock{mutex_};
                trees_.emplace(root_digest, std::move(tree));
            } catch (std::exception const& ex) {
                Logger::Log(LogLevel::Error,
                            "adding local tree to tree map failed with:\n{}",
                            ex.what());
                return false;
            }
        }
        return true;
    }

  private:
    ObjectInfoPool infos_;  // pool to store each solid object info exactly once
    std::unordered_map<bazel_re::Digest, LocalTree> trees_;
    mutable std::shared_mutex mutex_;
};

#endif  // INCLUDED_SRC_BUILDTOOL_EXECUTION_API_COMMON_LOCAL_TREE_MAP_HPP