diff options
Diffstat (limited to 'src')
12 files changed, 229 insertions, 260 deletions
diff --git a/src/buildtool/execution_api/common/TARGETS b/src/buildtool/execution_api/common/TARGETS index 214e87be..825f612f 100644 --- a/src/buildtool/execution_api/common/TARGETS +++ b/src/buildtool/execution_api/common/TARGETS @@ -7,6 +7,7 @@ , "execution_action.hpp" , "execution_response.hpp" , "tree_reader.hpp" + , "stream_dumper.hpp" ] , "srcs": ["execution_api.cpp"] , "deps": diff --git a/src/buildtool/execution_api/common/stream_dumper.hpp b/src/buildtool/execution_api/common/stream_dumper.hpp new file mode 100644 index 00000000..31f9b292 --- /dev/null +++ b/src/buildtool/execution_api/common/stream_dumper.hpp @@ -0,0 +1,104 @@ +// Copyright 2024 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef INCLUDED_SRC_BUILDTOOL_EXECUTION_API_COMMON_STREAM_DUMPER_HPP +#define INCLUDED_SRC_BUILDTOOL_EXECUTION_API_COMMON_STREAM_DUMPER_HPP + +#include <cstdio> +#include <optional> +#include <string> +#include <utility> + +#include "gsl/gsl" +#include "src/buildtool/common/artifact.hpp" +#include "src/buildtool/compatibility/compatibility.hpp" +#include "src/buildtool/execution_api/bazel_msg/bazel_msg_factory.hpp" +#include "src/buildtool/file_system/object_type.hpp" + +template <typename TImpl> +class StreamDumper final { + public: + template <typename... Args> + explicit StreamDumper(Args&&... args) noexcept + : impl_(std::forward<Args>(args)...) {} + + /// \brief Dump artifact to file stream. + /// Tree artifacts are pretty-printed (i.e., contents are listed) unless + /// raw_tree is set, then the raw tree will be written to the file stream. + /// \param info The object info of the artifact to dump. + /// \param stream The file stream to dump to. + /// \param raw_tree Dump tree as raw blob. + /// \returns true on success. + [[nodiscard]] auto DumpToStream(Artifact::ObjectInfo const& info, + gsl::not_null<FILE*> const& stream, + bool raw_tree) const noexcept -> bool { + const bool is_tree = IsTreeObject(info.type); + if (is_tree and raw_tree) { + return DumpRawTree(info, stream); + } + return is_tree ? DumpTree(info, stream) : DumpBlob(info, stream); + } + + private: + TImpl impl_; + + [[nodiscard]] auto DumpRawTree( + Artifact::ObjectInfo const& info, + gsl::not_null<FILE*> const& stream) const noexcept -> bool { + auto writer = [this, &stream](std::string const& data) -> bool { + return DumpString(data, stream); + }; + return impl_.DumpRawTree(info, writer); + } + + [[nodiscard]] auto DumpTree( + Artifact::ObjectInfo const& info, + gsl::not_null<FILE*> const& stream) const noexcept -> bool { + if (Compatibility::IsCompatible()) { + auto directory = impl_.ReadDirectory(info.digest); + auto data = directory + ? BazelMsgFactory::DirectoryToString(*directory) + : std::nullopt; + if (data) { + return DumpString(*data, stream); + } + } + else { + auto entries = impl_.ReadGitTree(info.digest); + auto data = entries ? BazelMsgFactory::GitTreeToString(*entries) + : std::nullopt; + if (data) { + return DumpString(*data, stream); + } + } + return false; + } + + [[nodiscard]] auto DumpBlob( + Artifact::ObjectInfo const& info, + gsl::not_null<FILE*> const& stream) const noexcept -> bool { + auto writer = [this, &stream](std::string const& data) -> bool { + return DumpString(data, stream); + }; + return impl_.DumpBlob(info, writer); + } + + [[nodiscard]] auto DumpString( + std::string const& data, + gsl::not_null<FILE*> const& stream) const noexcept -> bool { + return std::fwrite(data.data(), 1, data.size(), stream) == data.size(); + } +}; + +#endif // INCLUDED_SRC_BUILDTOOL_EXECUTION_API_COMMON_STREAM_DUMPER_HPP diff --git a/src/buildtool/execution_api/local/local_api.hpp b/src/buildtool/execution_api/local/local_api.hpp index c81b75c1..b9be5aed 100644 --- a/src/buildtool/execution_api/local/local_api.hpp +++ b/src/buildtool/execution_api/local/local_api.hpp @@ -37,6 +37,7 @@ #include "src/buildtool/execution_api/bazel_msg/blob_tree.hpp" #include "src/buildtool/execution_api/common/common_api.hpp" #include "src/buildtool/execution_api/common/execution_api.hpp" +#include "src/buildtool/execution_api/common/stream_dumper.hpp" #include "src/buildtool/execution_api/common/tree_reader.hpp" #include "src/buildtool/execution_api/execution_service/cas_utils.hpp" #include "src/buildtool/execution_api/git/git_api.hpp" @@ -141,13 +142,13 @@ class LocalApi final : public IExecutionApi { std::vector<Artifact::ObjectInfo> const& artifacts_info, std::vector<int> const& fds, bool raw_tree) noexcept -> bool final { + auto dumper = StreamDumper<LocalCasReader>{storage_->CAS()}; return CommonRetrieveToFds( artifacts_info, fds, - [&cas = storage_->CAS(), &raw_tree]( - Artifact::ObjectInfo const& info, - gsl::not_null<FILE*> const& out) { - return cas.DumpToStream(info, out, raw_tree); + [&dumper, &raw_tree](Artifact::ObjectInfo const& info, + gsl::not_null<FILE*> const& out) { + return dumper.DumpToStream(info, out, raw_tree); }, [&repo_config = repo_config_, &raw_tree]( Artifact::ObjectInfo const& info, int fd) { diff --git a/src/buildtool/execution_api/local/local_cas_reader.cpp b/src/buildtool/execution_api/local/local_cas_reader.cpp index 411bc5d7..a8933adc 100644 --- a/src/buildtool/execution_api/local/local_cas_reader.cpp +++ b/src/buildtool/execution_api/local/local_cas_reader.cpp @@ -14,6 +14,10 @@ #include "src/buildtool/execution_api/local/local_cas_reader.hpp" +#include <cstdio> +#include <memory> + +#include "gsl/gsl" #include "src/buildtool/crypto/hash_function.hpp" #include "src/buildtool/execution_api/bazel_msg/bazel_msg_factory.hpp" #include "src/buildtool/file_system/file_system_manager.hpp" @@ -64,3 +68,44 @@ auto LocalCasReader::ReadGitTree(ArtifactDigest const& digest) const noexcept Logger::Log(LogLevel::Debug, "Tree {} not found in CAS", digest.hash()); return std::nullopt; } + +auto LocalCasReader::DumpRawTree(Artifact::ObjectInfo const& info, + DumpCallback const& dumper) const noexcept + -> bool { + auto path = cas_.TreePath(info.digest); + return path ? DumpRaw(*path, dumper) : false; +} + +auto LocalCasReader::DumpBlob(Artifact::ObjectInfo const& info, + DumpCallback const& dumper) const noexcept + -> bool { + auto path = cas_.BlobPath(info.digest, IsExecutableObject(info.type)); + return path ? DumpRaw(*path, dumper) : false; +} + +auto LocalCasReader::DumpRaw(std::filesystem::path const& path, + DumpCallback const& dumper) noexcept -> bool { + auto closer = [](gsl::owner<FILE*> file) -> void { + if (file != nullptr) { + std::fclose(file); + } + }; + auto in = std::shared_ptr<FILE>{std::fopen(path.c_str(), "rb"), closer}; + if (not in) { + return false; + } + + constexpr std::size_t kChunkSize{512}; + std::string buffer(kChunkSize, '\0'); + while (auto size = std::fread(buffer.data(), 1, buffer.size(), in.get())) { + try { + buffer.resize(size); + if (not std::invoke(dumper, buffer)) { + return false; + } + } catch (...) { + return false; + } + } + return true; +} diff --git a/src/buildtool/execution_api/local/local_cas_reader.hpp b/src/buildtool/execution_api/local/local_cas_reader.hpp index 69baf3a9..9618dbc4 100644 --- a/src/buildtool/execution_api/local/local_cas_reader.hpp +++ b/src/buildtool/execution_api/local/local_cas_reader.hpp @@ -15,8 +15,12 @@ #ifndef INCLUDED_SRC_BUILDTOOL_EXECUTION_API_LOCAL_LOCAL_CAS_READER_HPP #define INCLUDED_SRC_BUILDTOOL_EXECUTION_API_LOCAL_LOCAL_CAS_READER_HPP +#include <filesystem> +#include <functional> #include <optional> +#include <string> +#include "src/buildtool/common/artifact.hpp" #include "src/buildtool/common/artifact_digest.hpp" #include "src/buildtool/common/bazel_types.hpp" #include "src/buildtool/file_system/git_repo.hpp" @@ -24,6 +28,8 @@ class LocalCasReader final { public: + using DumpCallback = std::function<bool(std::string const&)>; + explicit LocalCasReader(LocalCAS<true> const& cas) noexcept : cas_(cas) {} [[nodiscard]] auto ReadDirectory(ArtifactDigest const& digest) @@ -32,8 +38,20 @@ class LocalCasReader final { [[nodiscard]] auto ReadGitTree(ArtifactDigest const& digest) const noexcept -> std::optional<GitRepo::tree_entries_t>; + [[nodiscard]] auto DumpRawTree(Artifact::ObjectInfo const& info, + DumpCallback const& dumper) const noexcept + -> bool; + + [[nodiscard]] auto DumpBlob(Artifact::ObjectInfo const& info, + DumpCallback const& dumper) const noexcept + -> bool; + private: LocalCAS<true> const& cas_; + + [[nodiscard]] static auto DumpRaw(std::filesystem::path const& path, + DumpCallback const& dumper) noexcept + -> bool; }; -#endif // INCLUDED_SRC_BUILDTOOL_EXECUTION_API_LOCAL_LOCAL_CAS_READER_HPP
\ No newline at end of file +#endif // INCLUDED_SRC_BUILDTOOL_EXECUTION_API_LOCAL_LOCAL_CAS_READER_HPP diff --git a/src/buildtool/execution_api/remote/bazel/bazel_api.cpp b/src/buildtool/execution_api/remote/bazel/bazel_api.cpp index 47d024f9..0f1fdd04 100644 --- a/src/buildtool/execution_api/remote/bazel/bazel_api.cpp +++ b/src/buildtool/execution_api/remote/bazel/bazel_api.cpp @@ -31,6 +31,7 @@ #include "src/buildtool/execution_api/bazel_msg/bazel_common.hpp" #include "src/buildtool/execution_api/bazel_msg/bazel_msg_factory.hpp" #include "src/buildtool/execution_api/common/common_api.hpp" +#include "src/buildtool/execution_api/common/stream_dumper.hpp" #include "src/buildtool/execution_api/common/tree_reader.hpp" #include "src/buildtool/execution_api/remote/bazel/bazel_ac_client.hpp" #include "src/buildtool/execution_api/remote/bazel/bazel_action.hpp" @@ -284,12 +285,13 @@ auto BazelApi::CreateAction( std::vector<Artifact::ObjectInfo> const& artifacts_info, std::vector<int> const& fds, bool raw_tree) noexcept -> bool { + auto dumper = StreamDumper<BazelNetworkReader>{*network_}; return CommonRetrieveToFds( artifacts_info, fds, - [&network = network_, &raw_tree](Artifact::ObjectInfo const& info, - gsl::not_null<FILE*> const& out) { - return network->DumpToStream(info, out, raw_tree); + [&dumper, &raw_tree](Artifact::ObjectInfo const& info, + gsl::not_null<FILE*> const& out) { + return dumper.DumpToStream(info, out, raw_tree); }, std::nullopt // remote can't fallback to Git ); diff --git a/src/buildtool/execution_api/remote/bazel/bazel_network.cpp b/src/buildtool/execution_api/remote/bazel/bazel_network.cpp index 485e65dc..4cc3a274 100644 --- a/src/buildtool/execution_api/remote/bazel/bazel_network.cpp +++ b/src/buildtool/execution_api/remote/bazel/bazel_network.cpp @@ -23,118 +23,6 @@ #include "src/buildtool/logging/log_level.hpp" #include "src/buildtool/logging/logger.hpp" -namespace { - -[[nodiscard]] auto ReadDirectory( - gsl::not_null<BazelNetwork const*> const& network, - bazel_re::Digest const& digest) noexcept - -> std::optional<bazel_re::Directory> { - auto blobs = network->ReadBlobs({digest}).Next(); - if (blobs.size() == 1) { - return BazelMsgFactory::MessageFromString<bazel_re::Directory>( - blobs.at(0).data); - } - Logger::Log(LogLevel::Debug, - "Directory {} not found in CAS", - NativeSupport::Unprefix(digest.hash())); - return std::nullopt; -} - -[[nodiscard]] auto ReadGitTree( - gsl::not_null<BazelNetwork const*> const& network, - bazel_re::Digest const& digest) noexcept - -> std::optional<GitRepo::tree_entries_t> { - auto blobs = network->ReadBlobs({digest}).Next(); - if (blobs.size() == 1) { - auto const& content = blobs.at(0).data; - auto check_symlinks = - [&network](std::vector<bazel_re::Digest> const& ids) { - auto size = ids.size(); - auto reader = network->ReadBlobs(ids); - auto blobs = reader.Next(); - std::size_t count{}; - while (not blobs.empty()) { - if (count + blobs.size() > size) { - Logger::Log(LogLevel::Debug, - "received more blobs than requested."); - return false; - } - for (auto const& blob : blobs) { - if (not PathIsNonUpwards(blob.data)) { - return false; - } - } - count += blobs.size(); - blobs = reader.Next(); - } - return true; - }; - return GitRepo::ReadTreeData( - content, - HashFunction::ComputeTreeHash(content).Bytes(), - check_symlinks, - /*is_hex_id=*/false); - } - Logger::Log(LogLevel::Debug, - "Tree {} not found in CAS", - NativeSupport::Unprefix(digest.hash())); - return std::nullopt; -} - -[[nodiscard]] auto TreeToStream( - gsl::not_null<BazelNetwork const*> const& network, - bazel_re::Digest const& tree_digest, - gsl::not_null<FILE*> const& stream, - bool raw_tree) noexcept -> bool { - if (raw_tree) { - auto blobs = network->ReadBlobs({tree_digest}).Next(); - if (blobs.size() != 1) { - Logger::Log(LogLevel::Debug, - "Object {} not found in CAS", - NativeSupport::Unprefix(tree_digest.hash())); - return false; - } - auto const& str = blobs.at(0).data; - std::fwrite(str.data(), 1, str.size(), stream); - return true; - } - if (Compatibility::IsCompatible()) { - if (auto dir = ReadDirectory(network, tree_digest)) { - if (auto data = BazelMsgFactory::DirectoryToString(*dir)) { - auto const& str = *data; - std::fwrite(str.data(), 1, str.size(), stream); - return true; - } - } - } - else { - if (auto entries = ReadGitTree(network, tree_digest)) { - if (auto data = BazelMsgFactory::GitTreeToString(*entries)) { - auto const& str = *data; - std::fwrite(str.data(), 1, str.size(), stream); - return true; - } - } - } - return false; -} - -[[nodiscard]] auto BlobToStream( - gsl::not_null<BazelNetwork const*> const& network, - bazel_re::Digest const& blob_digest, - gsl::not_null<FILE*> const& stream) noexcept -> bool { - auto reader = network->IncrementalReadSingleBlob(blob_digest); - auto data = reader.Next(); - while (data and not data->empty()) { - auto const& str = *data; - std::fwrite(str.data(), 1, str.size(), stream); - data = reader.Next(); - } - return data.has_value(); -} - -} // namespace - BazelNetwork::BazelNetwork(std::string instance_name, std::string const& host, Port port, @@ -314,11 +202,3 @@ auto BazelNetwork::QueryFullTree(bazel_re::Digest const& digest) const noexcept } return cas_->GetTree(instance_name_, digest, kMaxBatchTransferSize); } - -auto BazelNetwork::DumpToStream(Artifact::ObjectInfo const& info, - gsl::not_null<FILE*> const& stream, - bool raw_tree) const noexcept -> bool { - return IsTreeObject(info.type) - ? TreeToStream(this, info.digest, stream, raw_tree) - : BlobToStream(this, info.digest, stream); -} diff --git a/src/buildtool/execution_api/remote/bazel/bazel_network.hpp b/src/buildtool/execution_api/remote/bazel/bazel_network.hpp index 6ee44921..82a20c4d 100644 --- a/src/buildtool/execution_api/remote/bazel/bazel_network.hpp +++ b/src/buildtool/execution_api/remote/bazel/bazel_network.hpp @@ -110,10 +110,6 @@ class BazelNetwork { std::vector<std::string> const& output_files) const noexcept -> std::optional<bazel_re::ActionResult>; - [[nodiscard]] auto DumpToStream(Artifact::ObjectInfo const& info, - gsl::not_null<FILE*> const& stream, - bool raw_tree) const noexcept -> bool; - /// \brief Query full tree from remote CAS. Note that this is currently not // supported by Buildbarn revision c3c06bbe2a. [[nodiscard]] auto QueryFullTree(bazel_re::Digest const& digest) diff --git a/src/buildtool/execution_api/remote/bazel/bazel_network_reader.cpp b/src/buildtool/execution_api/remote/bazel/bazel_network_reader.cpp index ee0fbc78..a0e96237 100644 --- a/src/buildtool/execution_api/remote/bazel/bazel_network_reader.cpp +++ b/src/buildtool/execution_api/remote/bazel/bazel_network_reader.cpp @@ -92,6 +92,41 @@ auto BazelNetworkReader::ReadGitTree(ArtifactDigest const& digest) return std::nullopt; } +auto BazelNetworkReader::DumpRawTree(Artifact::ObjectInfo const& info, + DumpCallback const& dumper) const noexcept + -> bool { + auto blobs = network_.ReadBlobs({info.digest}).Next(); + if (blobs.size() != 1) { + Logger::Log( + LogLevel::Debug, "Object {} not found in CAS", info.digest.hash()); + return false; + } + + try { + return std::invoke(dumper, blobs.at(0).data); + } catch (...) { + return false; + } +} + +auto BazelNetworkReader::DumpBlob(Artifact::ObjectInfo const& info, + DumpCallback const& dumper) const noexcept + -> bool { + auto reader = network_.IncrementalReadSingleBlob(info.digest); + auto data = reader.Next(); + while (data and not data->empty()) { + try { + if (not std::invoke(dumper, *data)) { + return false; + } + } catch (...) { + return false; + } + data = reader.Next(); + } + return data.has_value(); +} + auto BazelNetworkReader::MakeAuxiliaryMap( std::vector<bazel_re::Directory>&& full_tree) noexcept -> std::optional<DirectoryMap> { diff --git a/src/buildtool/execution_api/remote/bazel/bazel_network_reader.hpp b/src/buildtool/execution_api/remote/bazel/bazel_network_reader.hpp index 8d46c9f8..fb5f5184 100644 --- a/src/buildtool/execution_api/remote/bazel/bazel_network_reader.hpp +++ b/src/buildtool/execution_api/remote/bazel/bazel_network_reader.hpp @@ -15,9 +15,13 @@ #ifndef INCLUDED_SRC_BUILDTOOL_EXECUTION_API_REMOTE_BAZEL_BAZEL_TREE_READER_HPP #define INCLUDED_SRC_BUILDTOOL_EXECUTION_API_REMOTE_BAZEL_BAZEL_TREE_READER_HPP +#include <filesystem> +#include <functional> #include <optional> +#include <string> #include <unordered_map> +#include "src/buildtool/common/artifact.hpp" #include "src/buildtool/common/artifact_digest.hpp" #include "src/buildtool/common/bazel_types.hpp" #include "src/buildtool/execution_api/remote/bazel/bazel_network.hpp" @@ -25,6 +29,8 @@ class BazelNetworkReader final { public: + using DumpCallback = std::function<bool(std::string const&)>; + explicit BazelNetworkReader( BazelNetwork const& network, std::optional<ArtifactDigest> request_remote_tree = @@ -36,6 +42,14 @@ class BazelNetworkReader final { [[nodiscard]] auto ReadGitTree(ArtifactDigest const& digest) const noexcept -> std::optional<GitRepo::tree_entries_t>; + [[nodiscard]] auto DumpRawTree(Artifact::ObjectInfo const& info, + DumpCallback const& dumper) const noexcept + -> bool; + + [[nodiscard]] auto DumpBlob(Artifact::ObjectInfo const& info, + DumpCallback const& dumper) const noexcept + -> bool; + private: using DirectoryMap = std::unordered_map<bazel_re::Digest, bazel_re::Directory>; @@ -48,4 +62,4 @@ class BazelNetworkReader final { -> std::optional<DirectoryMap>; }; -#endif // INCLUDED_SRC_BUILDTOOL_EXECUTION_API_REMOTE_BAZEL_BAZEL_TREE_READER_HPP
\ No newline at end of file +#endif // INCLUDED_SRC_BUILDTOOL_EXECUTION_API_REMOTE_BAZEL_BAZEL_TREE_READER_HPP diff --git a/src/buildtool/storage/local_cas.hpp b/src/buildtool/storage/local_cas.hpp index 9288e423..9309ef5d 100644 --- a/src/buildtool/storage/local_cas.hpp +++ b/src/buildtool/storage/local_cas.hpp @@ -199,17 +199,6 @@ class LocalCAS { std::string const& tree_data) const noexcept -> std::optional<LargeObjectError>; - /// \brief Dump artifact to file stream. - /// Tree artifacts are pretty-printed (i.e., contents are listed) unless - /// raw_tree is set, then the raw tree will be written to the file stream. - /// \param info The object info of the artifact to dump. - /// \param stream The file stream to dump to. - /// \param raw_tree Dump tree as raw blob. - /// \returns true on success. - [[nodiscard]] auto DumpToStream(Artifact::ObjectInfo const& info, - gsl::not_null<FILE*> const& stream, - bool raw_tree) const noexcept -> bool; - /// \brief Uplink blob from this generation to latest LocalCAS generation. /// Performs a synchronization if requested and if blob is only available /// with inverse x-bit. This function is only available for instances that diff --git a/src/buildtool/storage/local_cas.tpp b/src/buildtool/storage/local_cas.tpp index 664606c9..4850cbd7 100644 --- a/src/buildtool/storage/local_cas.tpp +++ b/src/buildtool/storage/local_cas.tpp @@ -25,112 +25,6 @@ namespace detail { -template <class T_CAS> -[[nodiscard]] auto ReadDirectory(T_CAS const& cas, - bazel_re::Digest const& digest) noexcept - -> std::optional<bazel_re::Directory> { - if (auto const path = cas.TreePath(digest)) { - if (auto const content = FileSystemManager::ReadFile(*path)) { - return BazelMsgFactory::MessageFromString<bazel_re::Directory>( - *content); - } - } - Logger::Log(LogLevel::Error, - "Directory {} not found in CAS", - NativeSupport::Unprefix(digest.hash())); - return std::nullopt; -} - -template <class T_CAS> -[[nodiscard]] auto ReadGitTree(T_CAS const& cas, - bazel_re::Digest const& digest) noexcept - -> std::optional<GitRepo::tree_entries_t> { - if (auto const path = cas.TreePath(digest)) { - if (auto const content = FileSystemManager::ReadFile(*path)) { - auto check_symlinks = - [&cas](std::vector<bazel_re::Digest> const& ids) { - for (auto const& id : ids) { - auto link_path = - cas.BlobPath(id, /*is_executable=*/false); - if (not link_path) { - return false; - } - // in the local CAS we store as files - auto content = FileSystemManager::ReadFile(*link_path); - if (not content or not PathIsNonUpwards(*content)) { - return false; - } - } - return true; - }; - return GitRepo::ReadTreeData( - *content, - HashFunction::ComputeTreeHash(*content).Bytes(), - check_symlinks, - /*is_hex_id=*/false); - } - } - Logger::Log(LogLevel::Debug, - "Tree {} not found in CAS", - NativeSupport::Unprefix(digest.hash())); - return std::nullopt; -} - -[[nodiscard]] inline auto DumpToStream( - gsl::not_null<FILE*> const& stream, - std::optional<std::string> const& data) noexcept -> bool { - if (data) { - std::fwrite(data->data(), 1, data->size(), stream); - return true; - } - return false; -} - -template <class T_CAS> -[[nodiscard]] auto TreeToStream(T_CAS const& cas, - bazel_re::Digest const& tree_digest, - gsl::not_null<FILE*> const& stream) noexcept - -> bool { - if (Compatibility::IsCompatible()) { - if (auto dir = ReadDirectory(cas, tree_digest)) { - return DumpToStream(stream, - BazelMsgFactory::DirectoryToString(*dir)); - } - } - else { - if (auto entries = ReadGitTree(cas, tree_digest)) { - return DumpToStream(stream, - BazelMsgFactory::GitTreeToString(*entries)); - } - } - return false; -} - -template <class T_CAS> -[[nodiscard]] auto BlobToStream(T_CAS const& cas, - Artifact::ObjectInfo const& blob_info, - gsl::not_null<FILE*> const& stream) noexcept - -> bool { - constexpr std::size_t kChunkSize{512}; - auto path = - cas.BlobPath(blob_info.digest, IsExecutableObject(blob_info.type)); - if (not path and not Compatibility::IsCompatible()) { - // in native mode, lookup object in tree cas to dump tree as blob - path = cas.TreePath(blob_info.digest); - } - if (path) { - std::string data(kChunkSize, '\0'); - if (gsl::owner<FILE*> in = std::fopen(path->c_str(), "rb")) { - while (auto size = std::fread(data.data(), 1, kChunkSize, in)) { - std::fwrite(data.data(), 1, size, stream); - } - std::fclose(in); - return true; - } - } - return false; -} - [[nodiscard]] static inline auto CheckDigestConsistency( bazel_re::Digest const& lhs, bazel_re::Digest const& rhs) noexcept -> bool { @@ -147,16 +41,6 @@ template <class T_CAS> } // namespace detail template <bool kDoGlobalUplink> -auto LocalCAS<kDoGlobalUplink>::DumpToStream(Artifact::ObjectInfo const& info, - gsl::not_null<FILE*> const& stream, - bool raw_tree) const noexcept - -> bool { - return IsTreeObject(info.type) and not raw_tree - ? detail::TreeToStream(*this, info.digest, stream) - : detail::BlobToStream(*this, info, stream); -} - -template <bool kDoGlobalUplink> template <bool kIsLocalGeneration> requires(kIsLocalGeneration) auto LocalCAS<kDoGlobalUplink>::LocalUplinkBlob( LocalGenerationCAS const& latest, |