summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorJoel Rosdahl <joel@rosdahl.net>2022-09-11 13:48:05 +0200
committerJoel Rosdahl <joel@rosdahl.net>2022-09-21 17:06:29 +0200
commita03f14b7560f12eb28d6da1a60efc2bde4d2faf5 (patch)
treeffc62ac2a78ba668b571dc9e4b6b42937122ff21 /src/core
parente8def923d74dc68b4bbcb80177563f8ff23b5767 (diff)
downloadccache-a03f14b7560f12eb28d6da1a60efc2bde4d2faf5.tar.gz
chore: Simplify cache entry reading and writing
Cache entries are now fully read into memory before (de)compressing, checksumming and parsing, instead of streaming data like before. While this increases memory usage when working with large object files, it also simplifies the code a lot. Another motivation for this change is that cache entry data is not streamed from secondary storage anyway, and it makes sense to keep the architecture simple and similar for primary and secondary storage code paths. The cache entry format has modified so that the checksum covers the potentially compressed payload (plus the header), not the uncompressed payload (plus the header) like before. The checksum is now also stored in an uncompressed epilogue. Since the cache entry format has been changed, the input hash has been changed as well.
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/CacheEntry.cpp327
-rw-r--r--src/core/CacheEntry.hpp135
-rw-r--r--src/core/CacheEntryHeader.cpp108
-rw-r--r--src/core/CacheEntryHeader.hpp87
-rw-r--r--src/core/CacheEntryReader.cpp111
-rw-r--r--src/core/CacheEntryReader.hpp61
-rw-r--r--src/core/CacheEntryWriter.cpp61
-rw-r--r--src/core/CacheEntryWriter.hpp50
-rw-r--r--src/core/ChecksummingReader.hpp68
-rw-r--r--src/core/ChecksummingWriter.hpp74
-rw-r--r--src/core/FileWriter.hpp58
-rw-r--r--src/core/Manifest.cpp5
-rw-r--r--src/core/Manifest.hpp9
-rw-r--r--src/core/Reader.hpp82
-rw-r--r--src/core/Result.cpp37
-rw-r--r--src/core/Result.hpp24
-rw-r--r--src/core/Serializer.hpp (renamed from src/core/FileReader.hpp)37
-rw-r--r--src/core/Writer.hpp64
-rw-r--r--src/core/mainoptions.cpp77
-rw-r--r--src/core/types.cpp47
-rw-r--r--src/core/types.hpp17
22 files changed, 623 insertions, 920 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 83a6d4b0..d2431be3 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -1,8 +1,6 @@
set(
sources
- CacheEntryHeader.cpp
- CacheEntryReader.cpp
- CacheEntryWriter.cpp
+ CacheEntry.cpp
Manifest.cpp
Result.cpp
ResultExtractor.cpp
diff --git a/src/core/CacheEntry.cpp b/src/core/CacheEntry.cpp
new file mode 100644
index 00000000..fda2e111
--- /dev/null
+++ b/src/core/CacheEntry.cpp
@@ -0,0 +1,327 @@
+// Copyright (C) 2022 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#include "CacheEntry.hpp"
+
+#include <Logging.hpp>
+#include <ccache.hpp>
+#include <core/CacheEntryDataReader.hpp>
+#include <core/CacheEntryDataWriter.hpp>
+#include <core/Result.hpp>
+#include <core/exceptions.hpp>
+#include <core/types.hpp>
+#include <fmtmacros.hpp>
+#include <util/expected.hpp>
+#include <util/file.hpp>
+#include <util/zstd.hpp>
+
+#include <cstring>
+
+namespace {
+
+const size_t k_static_header_fields_size =
+ sizeof(core::CacheEntry::Header::magic)
+ + sizeof(core::CacheEntry::Header::entry_format_version)
+ + sizeof(core::CacheEntry::Header::entry_type)
+ + sizeof(core::CacheEntry::Header::compression_type)
+ + sizeof(core::CacheEntry::Header::compression_level)
+ + sizeof(core::CacheEntry::Header::self_contained)
+ + sizeof(core::CacheEntry::Header::creation_time)
+ + sizeof(core::CacheEntry::Header::entry_size)
+ // ccache_version length field:
+ + 1
+ // namespace_ length field:
+ + 1;
+
+const size_t k_epilogue_fields_size = sizeof(uint64_t) + sizeof(uint64_t);
+
+core::CacheEntryType
+cache_entry_type_from_int(const uint8_t entry_type)
+{
+ switch (entry_type) {
+ case 0:
+ return core::CacheEntryType::result;
+ break;
+ case 1:
+ return core::CacheEntryType::manifest;
+ break;
+ default:
+ throw core::Error(FMT("Unknown entry type: {}", entry_type));
+ }
+}
+
+} // namespace
+
+namespace core {
+
+CacheEntry::Header::Header(const Config& config,
+ core::CacheEntryType entry_type)
+ : magic(k_ccache_magic),
+ entry_format_version(k_cache_entry_format_version),
+ entry_type(entry_type),
+ compression_type(compression_type_from_config(config)),
+ compression_level(compression_level_from_config(config)),
+ self_contained(entry_type != CacheEntryType::result
+ || !core::Result::Serializer::use_raw_files(config)),
+ creation_time(time(nullptr)),
+ ccache_version(CCACHE_VERSION),
+ namespace_(config.namespace_()),
+ entry_size(0)
+{
+ if (compression_level == 0) {
+ compression_level = default_compression_level;
+ LOG("Using default compression level {}", compression_level);
+ }
+}
+
+CacheEntry::Header::Header(nonstd::span<const uint8_t> data)
+{
+ parse(data);
+}
+
+CacheEntry::Header::Header(const std::string& path)
+{
+ const auto data = util::read_file_part<util::Bytes>(path, 0, 1000);
+ if (!data) {
+ throw core::Error(data.error());
+ }
+ parse(*data);
+}
+
+std::string
+CacheEntry::Header::inspect() const
+{
+ std::string result;
+ result += result += FMT("Magic: {:04x}\n", magic);
+ result += FMT("Entry format version: {}\n", entry_format_version);
+ result += FMT("Entry type: {} ({})\n",
+ static_cast<uint8_t>(entry_type),
+ to_string(entry_type));
+ result += FMT("Compression type: {}\n", to_string(compression_type));
+ result += FMT("Compression level: {}\n", compression_level);
+ result += FMT("Self-contained: {}\n", self_contained ? "yes" : "no");
+ result += FMT("Creation time: {}\n", creation_time);
+ result += FMT("Ccache version: {}\n", ccache_version);
+ result += FMT("Namespace: {}\n", namespace_);
+ result += FMT("Entry size: {}\n", entry_size);
+ return result;
+}
+
+void
+CacheEntry::Header::parse(nonstd::span<const uint8_t> data)
+{
+ CacheEntryDataReader reader(data);
+ reader.read_int(magic);
+ if (magic != core::k_ccache_magic) {
+ throw core::Error(FMT("Bad magic value: 0x{:04x}", magic));
+ }
+
+ reader.read_int(entry_format_version);
+ if (entry_format_version != core::k_cache_entry_format_version) {
+ throw core::Error(
+ FMT("Unknown entry format version: {}", entry_format_version));
+ }
+
+ entry_type = cache_entry_type_from_int(reader.read_int<uint8_t>());
+ compression_type = compression_type_from_int(reader.read_int<uint8_t>());
+ reader.read_int(compression_level);
+ self_contained = bool(reader.read_int<uint8_t>());
+ reader.read_int(creation_time);
+ ccache_version = reader.read_str(reader.read_int<uint8_t>());
+ namespace_ = reader.read_str(reader.read_int<uint8_t>());
+ reader.read_int(entry_size);
+}
+
+size_t
+CacheEntry::Header::serialized_size() const
+{
+ return k_static_header_fields_size + ccache_version.length()
+ + namespace_.length();
+}
+
+void
+CacheEntry::Header::serialize(util::Bytes& output) const
+{
+ core::CacheEntryDataWriter writer(output);
+ writer.write_int(magic);
+ writer.write_int(entry_format_version);
+ writer.write_int(static_cast<uint8_t>(entry_type));
+ writer.write_int(static_cast<uint8_t>(compression_type));
+ writer.write_int(compression_level);
+ writer.write_int<uint8_t>(self_contained);
+ writer.write_int(creation_time);
+ writer.write_int<uint8_t>(ccache_version.length());
+ writer.write_str(ccache_version);
+ writer.write_int<uint8_t>(namespace_.length());
+ writer.write_str(namespace_);
+ writer.write_int(entry_size);
+}
+
+uint32_t
+CacheEntry::Header::uncompressed_payload_size() const
+{
+ return entry_size - serialized_size() - k_epilogue_fields_size;
+}
+
+CacheEntry::CacheEntry(nonstd::span<const uint8_t> data) : m_header(data)
+{
+ const size_t non_payload_size =
+ m_header.serialized_size() + k_epilogue_fields_size;
+ if (data.size() <= non_payload_size) {
+ throw core::Error("CacheEntry data underflow");
+ }
+ m_payload =
+ data.subspan(m_header.serialized_size(), data.size() - non_payload_size);
+ m_checksum = data.last(k_epilogue_fields_size);
+
+ switch (m_header.compression_type) {
+ case CompressionType::none:
+ break;
+
+ case CompressionType::zstd:
+ m_uncompressed_payload.reserve(m_header.uncompressed_payload_size());
+ util::throw_on_error<core::Error>(
+ util::zstd_decompress(
+ m_payload, m_uncompressed_payload, m_uncompressed_payload.capacity()),
+ "Cache entry payload decompression error: ");
+
+ break;
+ }
+}
+
+void
+CacheEntry::verify_checksum() const
+{
+ util::Bytes header_data;
+ m_header.serialize(header_data);
+
+ util::XXH3_128 checksum;
+ checksum.update(header_data);
+ checksum.update(m_payload);
+ const auto actual = checksum.digest();
+
+ if (actual != m_checksum) {
+ throw core::Error(
+ FMT("Incorrect checksum (actual {}, expected {})",
+ Util::format_base16(actual.data(), actual.size()),
+ Util::format_base16(m_checksum.data(), m_checksum.size())));
+ }
+}
+
+const CacheEntry::Header&
+CacheEntry::header() const
+{
+ return m_header;
+}
+
+nonstd::span<const uint8_t>
+CacheEntry::payload() const
+{
+ return m_header.compression_type == CompressionType::none
+ ? m_payload
+ : nonstd::span<const uint8_t>(m_uncompressed_payload);
+}
+
+util::Bytes
+CacheEntry::serialize(const CacheEntry::Header& header,
+ Serializer& payload_serializer)
+{
+ return do_serialize(
+ header,
+ payload_serializer.serialized_size(),
+ [&payload_serializer](util::Bytes& result, const CacheEntry::Header& hdr) {
+ switch (hdr.compression_type) {
+ case CompressionType::none:
+ payload_serializer.serialize(result);
+ break;
+
+ case CompressionType::zstd:
+ util::Bytes payload;
+ payload_serializer.serialize(payload);
+ util::throw_on_error<core::Error>(
+ util::zstd_compress(payload, result, hdr.compression_level),
+ "Cache entry payload compression error: ");
+ break;
+ }
+ });
+}
+
+util::Bytes
+CacheEntry::serialize(const CacheEntry::Header& header,
+ nonstd::span<const uint8_t> payload)
+{
+ return do_serialize(
+ header,
+ payload.size(),
+ [&payload](util::Bytes& result, const CacheEntry::Header& hdr) {
+ switch (hdr.compression_type) {
+ case CompressionType::none:
+ result.insert(result.end(), payload.begin(), payload.end());
+ break;
+
+ case CompressionType::zstd:
+ util::throw_on_error<core::Error>(
+ util::zstd_compress(payload, result, hdr.compression_level),
+ "Cache entry payload compression error: ");
+ break;
+ }
+ });
+}
+
+util::Bytes
+CacheEntry::do_serialize(
+ const CacheEntry::Header& header,
+ size_t serialized_payload_size,
+ std::function<void(util::Bytes& result, const Header& hdr)> serialize_payload)
+{
+ CacheEntry::Header hdr(header);
+ const size_t non_payload_size =
+ hdr.serialized_size() + k_epilogue_fields_size;
+ hdr.entry_size = non_payload_size + serialized_payload_size;
+
+ if (hdr.compression_type == CompressionType::zstd) {
+ const auto [level, explanation] =
+ util::zstd_supported_compression_level(hdr.compression_level);
+ if (!explanation.empty()) {
+ LOG("Using ZSTD compression level {} ({}) instead of {}",
+ level,
+ explanation,
+ hdr.compression_level);
+ }
+ hdr.compression_level = level;
+ }
+
+ const size_t max_serialized_size =
+ hdr.compression_type == CompressionType::zstd
+ ? (non_payload_size + util::zstd_compress_bound(serialized_payload_size))
+ : hdr.entry_size;
+ util::Bytes result;
+ result.reserve(max_serialized_size);
+
+ hdr.serialize(result);
+ serialize_payload(result, hdr);
+
+ util::XXH3_128 checksum;
+ checksum.update(result);
+ const auto digest = checksum.digest();
+ result.insert(result.end(), digest.begin(), digest.end());
+
+ return result;
+}
+
+} // namespace core
diff --git a/src/core/CacheEntry.hpp b/src/core/CacheEntry.hpp
new file mode 100644
index 00000000..c4adecfc
--- /dev/null
+++ b/src/core/CacheEntry.hpp
@@ -0,0 +1,135 @@
+// Copyright (C) 2021-2022 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#pragma once
+
+#include <core/Serializer.hpp>
+#include <core/types.hpp>
+#include <util/Bytes.hpp>
+#include <util/XXH3_128.hpp>
+
+#include <third_party/nonstd/span.hpp>
+
+#include <cstdint>
+#include <functional>
+#include <string>
+
+// Cache entry format
+// ==================
+//
+// Integers are big-endian.
+//
+// <entry> ::= <header> <payload> <epilogue>
+// <header> ::= <magic> <format_ver> <entry_type> <compr_type>
+// <compr_level> <creation_time> <ccache_ver> <namespace>
+// <entry_size>
+// <magic> ::= uint16_t (0xccac)
+// <format_ver> ::= uint8_t
+// <entry_type> ::= <result_entry> | <manifest_entry>
+// <result_entry> ::= 0 (uint8_t)
+// <manifest_entry> ::= 1 (uint8_t)
+// <self_contained> ::= 0/1 (uint8_t) ; whether suitable for secondary storage
+// <compr_type> ::= <compr_none> | <compr_zstd>
+// <compr_none> ::= 0 (uint8_t)
+// <compr_zstd> ::= 1 (uint8_t)
+// <compr_level> ::= int8_t
+// <creation_time> ::= uint64_t (Unix epoch time when entry was created)
+// <ccache_ver> ::= string length (uint8_t) + string data
+// <namespace> ::= string length (uint8_t) + string data
+// <entry_size> ::= uint64_t ; = size of entry in uncompressed form
+// <payload> ::= depends on entry_type; potentially compressed
+// <epilogue> ::= <checksum_high> <checksum_low>
+// <checksum_high> ::= uint64_t ; XXH3-128 (high bits) of <header>+<payload>
+// <checksum_low> ::= uint64_t ; XXH3-128 (low bits) of <header>+<payload>
+
+class Config;
+
+namespace core {
+
+const uint16_t k_ccache_magic = 0xccac;
+
+// Version 0:
+// - First version.
+// Version 1:
+// - Added self_contained field.
+// - The checksum is now for the (potentially) compressed payload instead of
+// the uncompressed payload, and the checksum is now always stored
+// uncompressed.
+const uint16_t k_cache_entry_format_version = 1;
+
+class CacheEntry
+{
+public:
+ constexpr static uint8_t default_compression_level = 1;
+
+ class Header
+ {
+ public:
+ Header(const Config& config, CacheEntryType entry_type);
+ explicit Header(nonstd::span<const uint8_t> data);
+ explicit Header(const std::string& path);
+
+ std::string inspect() const;
+
+ uint16_t magic;
+ uint8_t entry_format_version;
+ CacheEntryType entry_type;
+ CompressionType compression_type;
+ int8_t compression_level;
+ bool self_contained;
+ uint64_t creation_time;
+ std::string ccache_version;
+ std::string namespace_;
+ uint64_t entry_size;
+
+ size_t serialized_size() const;
+ void serialize(util::Bytes& output) const;
+ uint32_t uncompressed_payload_size() const;
+
+ private:
+ void parse(nonstd::span<const uint8_t> data);
+ };
+
+ explicit CacheEntry(nonstd::span<const uint8_t> data);
+
+ void verify_checksum() const;
+ const Header& header() const;
+
+ // Return uncompressed payload.
+ nonstd::span<const uint8_t> payload() const;
+
+ static util::Bytes serialize(const Header& header,
+ Serializer& payload_serializer);
+ static util::Bytes serialize(const Header& header,
+ nonstd::span<const uint8_t> payload);
+
+private:
+ Header m_header;
+ nonstd::span<const uint8_t> m_payload; // Potentially compressed
+ util::Bytes m_checksum;
+
+ mutable util::Bytes m_uncompressed_payload;
+
+ static util::Bytes
+ do_serialize(const Header& header,
+ size_t serialized_payload_size,
+ std::function<void(util::Bytes& result, const Header& header)>
+ serialize_payload);
+};
+
+} // namespace core
diff --git a/src/core/CacheEntryHeader.cpp b/src/core/CacheEntryHeader.cpp
deleted file mode 100644
index b1bbe9a2..00000000
--- a/src/core/CacheEntryHeader.cpp
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright (C) 2019-2022 Joel Rosdahl and other contributors
-//
-// See doc/AUTHORS.adoc for a complete list of contributors.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation; either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program; if not, write to the Free Software Foundation, Inc., 51
-// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-#include "CacheEntryHeader.hpp"
-
-#include <core/exceptions.hpp>
-#include <fmtmacros.hpp>
-
-const size_t k_static_header_fields_size =
- sizeof(core::CacheEntryHeader::magic)
- + sizeof(core::CacheEntryHeader::entry_format_version)
- + sizeof(core::CacheEntryHeader::entry_type)
- + sizeof(core::CacheEntryHeader::compression_type)
- + sizeof(core::CacheEntryHeader::compression_level)
- + sizeof(core::CacheEntryHeader::creation_time)
- + sizeof(core::CacheEntryHeader::entry_size)
- // ccache_version length field:
- + 1
- // namespace_ length field:
- + 1;
-
-const size_t k_static_epilogue_fields_size =
- sizeof(uint64_t) + sizeof(uint64_t);
-
-namespace core {
-
-CacheEntryHeader::CacheEntryHeader(const core::CacheEntryType entry_type_,
- const compression::Type compression_type_,
- const int8_t compression_level_,
- const uint64_t creation_time_,
- const std::string& ccache_version_,
- const std::string& namespace_arg,
- const uint64_t entry_size_)
- : magic(k_ccache_magic),
- entry_format_version(k_entry_format_version),
- entry_type(entry_type_),
- compression_type(compression_type_),
- compression_level(compression_level_),
- creation_time(creation_time_),
- ccache_version(ccache_version_),
- namespace_(namespace_arg),
- entry_size(entry_size_)
-{
-}
-
-uint32_t
-CacheEntryHeader::payload_size() const
-{
- const auto payload_size = entry_size - non_payload_size();
- // In order to support 32-bit ccache builds, restrict size to uint32_t for
- // now. This restriction can be lifted when we drop 32-bit support.
- const auto max = std::numeric_limits<uint32_t>::max();
- if (payload_size > max) {
- throw core::Error(
- FMT("Serialized result too large ({} > {})", payload_size, max));
- }
-
- return payload_size;
-}
-
-void
-CacheEntryHeader::set_entry_size_from_payload_size(const uint64_t payload_size)
-{
- entry_size = non_payload_size() + payload_size;
-}
-
-void
-CacheEntryHeader::inspect(FILE* const stream) const
-{
- PRINT(stream, "Magic: {:04x}\n", magic);
- PRINT(stream, "Entry format version: {}\n", entry_format_version);
- PRINT(stream,
- "Entry type: {} ({})\n",
- static_cast<uint8_t>(entry_type),
- to_string(entry_type));
- PRINT(stream,
- "Compression type: {}\n",
- compression::type_to_string(compression_type));
- PRINT(stream, "Compression level: {}\n", compression_level);
- PRINT(stream, "Creation time: {}\n", creation_time);
- PRINT(stream, "Ccache version: {}\n", ccache_version);
- PRINT(stream, "Namespace: {}\n", namespace_);
- PRINT(stream, "Entry size: {}\n", entry_size);
-}
-
-size_t
-CacheEntryHeader::non_payload_size() const
-{
- return k_static_header_fields_size + ccache_version.length()
- + namespace_.length() + k_static_epilogue_fields_size;
-}
-
-} // namespace core
diff --git a/src/core/CacheEntryHeader.hpp b/src/core/CacheEntryHeader.hpp
deleted file mode 100644
index a0f68918..00000000
--- a/src/core/CacheEntryHeader.hpp
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright (C) 2021-2022 Joel Rosdahl and other contributors
-//
-// See doc/AUTHORS.adoc for a complete list of contributors.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation; either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program; if not, write to the Free Software Foundation, Inc., 51
-// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-#pragma once
-
-#include <compression/types.hpp>
-#include <core/types.hpp>
-
-#include <cstdint>
-
-// Cache entry format
-// ==================
-//
-// Integers are big-endian.
-//
-// <entry> ::= <header> <payload> <epilogue>
-// <header> ::= <magic> <format_ver> <entry_type> <compr_type>
-// <compr_level> <creation_time> <ccache_ver> <namespace>
-// <entry_size>
-// <magic> ::= uint16_t (0xccac)
-// <format_ver> ::= uint8_t
-// <entry_type> ::= <result_entry> | <manifest_entry>
-// <result_entry> ::= 0 (uint8_t)
-// <manifest_entry> ::= 1 (uint8_t)
-// <compr_type> ::= <compr_none> | <compr_zstd>
-// <compr_none> ::= 0 (uint8_t)
-// <compr_zstd> ::= 1 (uint8_t)
-// <compr_level> ::= int8_t
-// <creation_time> ::= uint64_t (Unix epoch time when entry was created)
-// <ccache_ver> ::= string length (uint8_t) + string data
-// <namespace> ::= string length (uint8_t) + string data
-// <entry_size> ::= uint64_t ; = size of file if stored uncompressed
-// ; potentially compressed from here
-// <payload> ::= depends on entry_type
-// <epilogue> ::= <checksum_high> <checksum_low>
-// <checksum_high> ::= uint64_t ; XXH3-128 (high bits) of entry bytes
-// <checksum_low> ::= uint64_t ; XXH3-128 (low bits) of entry bytes
-
-namespace core {
-
-const uint16_t k_ccache_magic = 0xccac;
-const uint16_t k_entry_format_version = 0;
-
-struct CacheEntryHeader
-{
- CacheEntryHeader(core::CacheEntryType entry_type,
- compression::Type compression_type,
- int8_t compression_level,
- uint64_t creation_time,
- const std::string& ccache_version,
- const std::string& namespace_,
- uint64_t entry_size = 0);
-
- uint16_t magic;
- uint8_t entry_format_version;
- core::CacheEntryType entry_type;
- compression::Type compression_type;
- int8_t compression_level;
- uint64_t creation_time;
- std::string ccache_version;
- std::string namespace_;
- uint64_t entry_size;
-
- uint32_t payload_size() const;
- void set_entry_size_from_payload_size(uint64_t payload_size);
- void inspect(FILE* stream) const;
-
-private:
- size_t non_payload_size() const;
-};
-
-} // namespace core
diff --git a/src/core/CacheEntryReader.cpp b/src/core/CacheEntryReader.cpp
deleted file mode 100644
index ea166d6d..00000000
--- a/src/core/CacheEntryReader.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright (C) 2019-2022 Joel Rosdahl and other contributors
-//
-// See doc/AUTHORS.adoc for a complete list of contributors.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation; either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program; if not, write to the Free Software Foundation, Inc., 51
-// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-#include "CacheEntryReader.hpp"
-
-#include <core/exceptions.hpp>
-#include <fmtmacros.hpp>
-
-namespace {
-
-core::CacheEntryType
-cache_entry_type_from_int(const uint8_t entry_type)
-{
- switch (entry_type) {
- case 0:
- return core::CacheEntryType::result;
- break;
- case 1:
- return core::CacheEntryType::manifest;
- break;
- default:
- throw core::Error(FMT("Unknown entry type: {}", entry_type));
- }
-}
-
-} // namespace
-
-namespace core {
-
-CacheEntryReader::CacheEntryReader(core::Reader& reader)
- : m_checksumming_reader(reader)
-{
- const auto magic = m_checksumming_reader.read_int<uint16_t>();
- if (magic != core::k_ccache_magic) {
- throw core::Error(FMT("Bad magic value: 0x{:04x}", magic));
- }
-
- const auto entry_format_version = m_checksumming_reader.read_int<uint8_t>();
- if (entry_format_version != core::k_entry_format_version) {
- throw core::Error(
- FMT("Unknown entry format version: {}", entry_format_version));
- }
-
- const auto entry_type = m_checksumming_reader.read_int<uint8_t>();
- const auto compression_type = m_checksumming_reader.read_int<uint8_t>();
- const auto compression_level = m_checksumming_reader.read_int<int8_t>();
- const auto creation_time = m_checksumming_reader.read_int<uint64_t>();
- const auto ccache_version =
- m_checksumming_reader.read_str(m_checksumming_reader.read_int<uint8_t>());
- const auto tag =
- m_checksumming_reader.read_str(m_checksumming_reader.read_int<uint8_t>());
- const auto entry_size = m_checksumming_reader.read_int<uint64_t>();
-
- m_header = std::make_unique<CacheEntryHeader>(
- cache_entry_type_from_int(entry_type),
- compression::type_from_int(compression_type),
- compression_level,
- creation_time,
- ccache_version,
- tag,
- entry_size);
-
- m_decompressor = compression::Decompressor::create_from_type(
- m_header->compression_type, reader);
- m_checksumming_reader.set_reader(*m_decompressor);
-}
-
-size_t
-CacheEntryReader::read(void* const data, const size_t count)
-{
- return m_checksumming_reader.read(data, count);
-}
-
-void
-CacheEntryReader::finalize()
-{
- const util::XXH3_128::Digest actual = m_checksumming_reader.digest();
- util::XXH3_128::Digest expected;
- m_decompressor->read(expected.bytes(), expected.size());
-
- // actual == null_digest: Checksumming is not enabled now.
- // expected == null_digest: Checksumming was not enabled when the entry was
- // created.
- const util::XXH3_128::Digest null_digest;
-
- if (actual != expected && actual != null_digest && expected != null_digest) {
- throw core::Error(
- FMT("Incorrect checksum (actual {}, expected {})",
- Util::format_base16(actual.bytes(), actual.size()),
- Util::format_base16(expected.bytes(), expected.size())));
- }
-
- m_decompressor->finalize();
-}
-
-} // namespace core
diff --git a/src/core/CacheEntryReader.hpp b/src/core/CacheEntryReader.hpp
deleted file mode 100644
index a4e51819..00000000
--- a/src/core/CacheEntryReader.hpp
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (C) 2019-2021 Joel Rosdahl and other contributors
-//
-// See doc/AUTHORS.adoc for a complete list of contributors.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation; either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program; if not, write to the Free Software Foundation, Inc., 51
-// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-#pragma once
-
-#include <compression/Decompressor.hpp>
-#include <core/CacheEntryHeader.hpp>
-#include <core/ChecksummingReader.hpp>
-#include <core/Reader.hpp>
-#include <util/XXH3_128.hpp>
-
-namespace core {
-
-// This class knows how to read a cache entry with a format described in
-// CacheEntryHeader.
-class CacheEntryReader : public Reader
-{
-public:
- // Read cache entry data from `reader`.
- CacheEntryReader(Reader& reader);
-
- size_t read(void* data, size_t count) override;
- using Reader::read;
-
- // Close for reading.
- //
- // This method potentially verifies the end state after reading the cache
- // entry and throws `core::Error` if any integrity issues are found.
- void finalize();
-
- const CacheEntryHeader& header() const;
-
-private:
- ChecksummingReader m_checksumming_reader;
- std::unique_ptr<CacheEntryHeader> m_header;
- util::XXH3_128 m_checksum;
- std::unique_ptr<compression::Decompressor> m_decompressor;
-};
-
-inline const CacheEntryHeader&
-CacheEntryReader::header() const
-{
- return *m_header;
-}
-
-} // namespace core
diff --git a/src/core/CacheEntryWriter.cpp b/src/core/CacheEntryWriter.cpp
deleted file mode 100644
index b2de99d1..00000000
--- a/src/core/CacheEntryWriter.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (C) 2019-2021 Joel Rosdahl and other contributors
-//
-// See doc/AUTHORS.adoc for a complete list of contributors.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation; either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program; if not, write to the Free Software Foundation, Inc., 51
-// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-#include "CacheEntryWriter.hpp"
-
-#include <core/CacheEntryHeader.hpp>
-
-namespace core {
-
-CacheEntryWriter::CacheEntryWriter(core::Writer& writer,
- const CacheEntryHeader& header)
- : m_checksumming_writer(writer),
- m_compressor(compression::Compressor::create_from_type(
- header.compression_type, writer, header.compression_level))
-{
- m_checksumming_writer.write_int(header.magic);
- m_checksumming_writer.write_int(header.entry_format_version);
- m_checksumming_writer.write_int(static_cast<uint8_t>(header.entry_type));
- m_checksumming_writer.write_int(
- static_cast<uint8_t>(header.compression_type));
- m_checksumming_writer.write_int(m_compressor->actual_compression_level());
- m_checksumming_writer.write_int(header.creation_time);
- m_checksumming_writer.write_int<uint8_t>(header.ccache_version.length());
- m_checksumming_writer.write_str(header.ccache_version);
- m_checksumming_writer.write_int<uint8_t>(header.namespace_.length());
- m_checksumming_writer.write_str(header.namespace_);
- m_checksumming_writer.write_int(header.entry_size);
-
- m_checksumming_writer.set_writer(*m_compressor);
-}
-
-void
-CacheEntryWriter::write(const void* const data, const size_t count)
-{
- m_checksumming_writer.write(data, count);
-}
-
-void
-CacheEntryWriter::finalize()
-{
- const auto digest = m_checksumming_writer.digest();
- m_compressor->write(digest.bytes(), digest.size());
- m_compressor->finalize();
-}
-
-} // namespace core
diff --git a/src/core/CacheEntryWriter.hpp b/src/core/CacheEntryWriter.hpp
deleted file mode 100644
index 789eb0c8..00000000
--- a/src/core/CacheEntryWriter.hpp
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 2019-2021 Joel Rosdahl and other contributors
-//
-// See doc/AUTHORS.adoc for a complete list of contributors.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation; either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program; if not, write to the Free Software Foundation, Inc., 51
-// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-#pragma once
-
-#include <compression/Compressor.hpp>
-#include <core/ChecksummingWriter.hpp>
-#include <core/Writer.hpp>
-
-namespace core {
-
-struct CacheEntryHeader;
-
-// This class knows how to write a cache entry with a format described in
-// CacheEntryHeader.
-class CacheEntryWriter : public Writer
-{
-public:
- CacheEntryWriter(Writer& writer, const CacheEntryHeader& header);
-
- void write(const void* data, size_t count) override;
- using Writer::write;
-
- // Close for writing.
- //
- // This method potentially verifies the end state after writing the cache
- // entry and throws `core::Error` if any integrity issues are found.
- void finalize() override;
-
-private:
- ChecksummingWriter m_checksumming_writer;
- std::unique_ptr<compression::Compressor> m_compressor;
-};
-
-} // namespace core
diff --git a/src/core/ChecksummingReader.hpp b/src/core/ChecksummingReader.hpp
deleted file mode 100644
index 41c569fa..00000000
--- a/src/core/ChecksummingReader.hpp
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright (C) 2021 Joel Rosdahl and other contributors
-//
-// See doc/AUTHORS.adoc for a complete list of contributors.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation; either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program; if not, write to the Free Software Foundation, Inc., 51
-// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-#pragma once
-
-#include <core/Reader.hpp>
-#include <util/XXH3_128.hpp>
-
-namespace core {
-
-class ChecksummingReader : public Reader
-{
-public:
- ChecksummingReader(core::Reader& reader);
-
- using core::Reader::read;
- size_t read(void* data, size_t count) override;
-
- void set_reader(core::Reader& reader);
-
- util::XXH3_128::Digest digest() const;
-
-private:
- core::Reader* m_reader;
- util::XXH3_128 m_checksum;
-};
-
-inline ChecksummingReader::ChecksummingReader(core::Reader& reader)
- : m_reader(&reader)
-{
-}
-
-inline size_t
-ChecksummingReader::read(void* const data, const size_t count)
-{
- const auto bytes_read = m_reader->read(data, count);
- m_checksum.update(data, bytes_read);
- return bytes_read;
-}
-
-inline void
-ChecksummingReader::set_reader(core::Reader& reader)
-{
- m_reader = &reader;
-}
-
-inline util::XXH3_128::Digest
-ChecksummingReader::digest() const
-{
- return m_checksum.digest();
-}
-
-} // namespace core
diff --git a/src/core/ChecksummingWriter.hpp b/src/core/ChecksummingWriter.hpp
deleted file mode 100644
index 1d6b009f..00000000
--- a/src/core/ChecksummingWriter.hpp
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (C) 2021 Joel Rosdahl and other contributors
-//
-// See doc/AUTHORS.adoc for a complete list of contributors.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation; either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program; if not, write to the Free Software Foundation, Inc., 51
-// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-#pragma once
-
-#include <core/Writer.hpp>
-#include <util/XXH3_128.hpp>
-
-namespace core {
-
-class ChecksummingWriter : public Writer
-{
-public:
- ChecksummingWriter(core::Writer& writer);
-
- using core::Writer::write;
- void write(const void* data, size_t count) override;
- void finalize() override;
-
- void set_writer(core::Writer& writer);
-
- util::XXH3_128::Digest digest() const;
-
-private:
- core::Writer* m_writer;
- util::XXH3_128 m_checksum;
-};
-
-inline ChecksummingWriter::ChecksummingWriter(core::Writer& writer)
- : m_writer(&writer)
-{
-}
-
-inline void
-ChecksummingWriter::write(const void* const data, const size_t count)
-{
- m_writer->write(data, count);
- m_checksum.update(data, count);
-}
-
-inline void
-ChecksummingWriter::finalize()
-{
- m_writer->finalize();
-}
-
-inline void
-ChecksummingWriter::set_writer(core::Writer& writer)
-{
- m_writer = &writer;
-}
-
-inline util::XXH3_128::Digest
-ChecksummingWriter::digest() const
-{
- return m_checksum.digest();
-}
-
-} // namespace core
diff --git a/src/core/FileWriter.hpp b/src/core/FileWriter.hpp
deleted file mode 100644
index f4cdc0bd..00000000
--- a/src/core/FileWriter.hpp
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (C) 2021 Joel Rosdahl and other contributors
-//
-// See doc/AUTHORS.adoc for a complete list of contributors.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation; either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program; if not, write to the Free Software Foundation, Inc., 51
-// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-#pragma once
-
-#include <core/Writer.hpp>
-#include <core/exceptions.hpp>
-
-#include <cstdio>
-
-namespace core {
-
-class FileWriter : public Writer
-{
-public:
- FileWriter(FILE* stream);
-
- void write(const void* data, size_t size) override;
- void finalize() override;
-
-private:
- FILE* m_stream;
-};
-
-inline FileWriter::FileWriter(FILE* const stream) : m_stream(stream)
-{
-}
-
-inline void
-FileWriter::write(const void* const data, const size_t size)
-{
- if (size > 0 && fwrite(data, size, 1, m_stream) != 1) {
- throw core::Error("Failed to write to stream");
- }
-}
-
-inline void
-FileWriter::finalize()
-{
- fflush(m_stream);
-}
-
-} // namespace core
diff --git a/src/core/Manifest.cpp b/src/core/Manifest.cpp
index 9b46be19..f2a3e9cc 100644
--- a/src/core/Manifest.cpp
+++ b/src/core/Manifest.cpp
@@ -94,8 +94,7 @@ Manifest::read(nonstd::span<const uint8_t> data)
const auto file_count = reader.read_int<uint32_t>();
for (uint32_t i = 0; i < file_count; ++i) {
- m_files.push_back(
- std::string(reader.read_str(reader.read_int<uint16_t>())));
+ m_files.emplace_back(reader.read_str(reader.read_int<uint16_t>()));
}
const auto file_info_count = reader.read_int<uint32_t>();
@@ -231,7 +230,7 @@ Manifest::serialized_size() const
}
void
-Manifest::serialize(util::Bytes& output) const
+Manifest::serialize(util::Bytes& output)
{
core::CacheEntryDataWriter writer(output);
diff --git a/src/core/Manifest.hpp b/src/core/Manifest.hpp
index 267055a8..f277093f 100644
--- a/src/core/Manifest.hpp
+++ b/src/core/Manifest.hpp
@@ -19,7 +19,7 @@
#pragma once
#include <Digest.hpp>
-#include <util/Bytes.hpp>
+#include <core/Serializer.hpp>
#include <third_party/nonstd/span.hpp>
@@ -33,7 +33,7 @@ class Context;
namespace core {
-class Manifest
+class Manifest : public Serializer
{
public:
static const uint8_t k_format_version;
@@ -49,8 +49,9 @@ public:
time_t time_of_compilation,
bool save_timestamp);
- uint32_t serialized_size() const;
- void serialize(util::Bytes& output) const;
+ // core::Serializer
+ uint32_t serialized_size() const override;
+ void serialize(util::Bytes& output) override;
void inspect(FILE* stream) const;
diff --git a/src/core/Reader.hpp b/src/core/Reader.hpp
deleted file mode 100644
index 317005ef..00000000
--- a/src/core/Reader.hpp
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright (C) 2021-2022 Joel Rosdahl and other contributors
-//
-// See doc/AUTHORS.adoc for a complete list of contributors.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation; either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program; if not, write to the Free Software Foundation, Inc., 51
-// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-#pragma once
-
-#include <Util.hpp>
-#include <core/exceptions.hpp>
-
-#include <cstddef>
-#include <cstdint>
-#include <string>
-
-namespace core {
-
-class Reader
-{
-public:
- virtual ~Reader() = default;
-
- // Read `count` bytes into `data`, returning the actual number of bytes read
- // if not enough data is available. Throws `core::Error` on failure, e.g. if
- // no bytes could be read.
- virtual size_t read(void* data, size_t count) = 0;
-
- // Read an integer. Throws Error on failure.
- template<typename T> T read_int();
-
- // Read an integer into `value`. Throws Error on failure.
- template<typename T> void read_int(T& value);
-
- // Read a string of length `length`. Throws `core::Error` on failure.
- std::string read_str(size_t length);
-};
-
-template<typename T>
-inline T
-Reader::read_int()
-{
- uint8_t buffer[sizeof(T)];
- const auto bytes_read = read(buffer, sizeof(T));
- if (bytes_read != sizeof(T)) {
- throw core::Error("Read underflow");
- }
- T value;
- Util::big_endian_to_int(buffer, value);
- return value;
-}
-
-template<typename T>
-inline void
-Reader::read_int(T& value)
-{
- value = read_int<T>();
-}
-
-inline std::string
-Reader::read_str(const size_t length)
-{
- std::string value(length, 0);
- const auto bytes_read = read(&value[0], length);
- if (bytes_read != length) {
- throw core::Error("Read underflow");
- }
- return value;
-}
-
-} // namespace core
diff --git a/src/core/Result.cpp b/src/core/Result.cpp
index 9e454b7f..e2733778 100644
--- a/src/core/Result.cpp
+++ b/src/core/Result.cpp
@@ -70,8 +70,6 @@
namespace {
-const uint8_t k_result_format_version = 0;
-
// File data stored inside the result file.
const uint8_t k_embedded_file_marker = 0;
@@ -83,7 +81,7 @@ const uint8_t k_max_raw_file_entries = 10;
bool
should_store_raw_file(const Config& config, core::Result::FileType type)
{
- if (!config.file_clone() && !config.hard_link()) {
+ if (!core::Result::Serializer::use_raw_files(config)) {
return false;
}
@@ -108,11 +106,9 @@ should_store_raw_file(const Config& config, core::Result::FileType type)
} // namespace
-namespace core {
-
-namespace Result {
+namespace core::Result {
-const uint8_t k_version = 1;
+const uint8_t k_format_version = 0;
const char* const k_unknown_file_type = "<unknown type>";
@@ -182,10 +178,10 @@ Deserializer::visit(Deserializer::Visitor& visitor) const
{
CacheEntryDataReader reader(m_data);
const auto result_format_version = reader.read_int<uint8_t>();
- if (result_format_version != k_result_format_version) {
+ if (result_format_version != k_format_version) {
throw Error(FMT("Unknown result format version: {} != {}",
result_format_version,
- k_result_format_version));
+ k_format_version));
}
const auto n_files = reader.read_int<uint8_t>();
@@ -262,13 +258,12 @@ Serializer::serialized_size() const
return m_serialized_size;
}
-Serializer::SerializeResult
+void
Serializer::serialize(util::Bytes& output)
{
- SerializeResult serialize_result;
CacheEntryDataWriter writer(output);
- writer.write_int(k_result_format_version);
+ writer.write_int(k_format_version);
writer.write_int<uint8_t>(m_file_entries.size());
uint8_t file_number = 0;
@@ -296,8 +291,8 @@ Serializer::serialize(util::Bytes& output)
writer.write_int(file_size);
if (store_raw) {
- serialize_result.raw_files.emplace(file_number,
- std::get<std::string>(entry.data));
+ m_raw_files.push_back(
+ RawFile{file_number, std::get<std::string>(entry.data)});
} else if (is_file_entry) {
const auto& path = std::get<std::string>(entry.data);
const auto data = util::value_or_throw<Error>(
@@ -309,10 +304,18 @@ Serializer::serialize(util::Bytes& output)
++file_number;
}
+}
- return serialize_result;
+bool
+Serializer::use_raw_files(const Config& config)
+{
+ return config.file_clone() || config.hard_link();
}
-} // namespace Result
+const std::vector<Serializer::RawFile>&
+Serializer::get_raw_files() const
+{
+ return m_raw_files;
+}
-} // namespace core
+} // namespace core::Result
diff --git a/src/core/Result.hpp b/src/core/Result.hpp
index 140e9b23..805bcd0f 100644
--- a/src/core/Result.hpp
+++ b/src/core/Result.hpp
@@ -18,14 +18,13 @@
#pragma once
-#include <util/Bytes.hpp>
+#include <core/Serializer.hpp>
#include <util/types.hpp>
#include <third_party/nonstd/span.hpp>
#include <cstdint>
#include <string>
-#include <unordered_map>
#include <variant>
#include <vector>
@@ -38,7 +37,7 @@ class CacheEntryDataParser;
namespace Result {
-extern const uint8_t k_version;
+extern const uint8_t k_format_version;
extern const char* const k_unknown_file_type;
@@ -120,7 +119,7 @@ private:
};
// This class knows how to serialize a result cache entry.
-class Serializer
+class Serializer : public core::Serializer
{
public:
Serializer(const Config& config);
@@ -132,15 +131,20 @@ public:
// Register a file path whose content should be included in the result.
void add_file(FileType file_type, const std::string& path);
- uint32_t serialized_size() const;
+ // core::Serializer
+ uint32_t serialized_size() const override;
+ void serialize(util::Bytes& output) override;
- struct SerializeResult
+ static bool use_raw_files(const Config& config);
+
+ struct RawFile
{
- // Raw files to store in primary storage.
- std::unordered_map<uint8_t /*index*/, std::string /*path*/> raw_files;
+ uint8_t file_number;
+ std::string path;
};
- SerializeResult serialize(util::Bytes& output);
+ // Get raw files to store in primary storage.
+ const std::vector<RawFile>& get_raw_files() const;
private:
const Config& m_config;
@@ -152,6 +156,8 @@ private:
std::variant<nonstd::span<const uint8_t>, std::string> data;
};
std::vector<FileEntry> m_file_entries;
+
+ std::vector<RawFile> m_raw_files;
};
} // namespace Result
diff --git a/src/core/FileReader.hpp b/src/core/Serializer.hpp
index c7c7b8ce..bb5ddd03 100644
--- a/src/core/FileReader.hpp
+++ b/src/core/Serializer.hpp
@@ -1,4 +1,4 @@
-// Copyright (C) 2021 Joel Rosdahl and other contributors
+// Copyright (C) 2022 Joel Rosdahl and other contributors
//
// See doc/AUTHORS.adoc for a complete list of contributors.
//
@@ -18,39 +18,20 @@
#pragma once
-#include <core/Reader.hpp>
-#include <core/exceptions.hpp>
+#include <util/Bytes.hpp>
-#include <cstdio>
+#include <third_party/nonstd/span.hpp>
+
+#include <cstdint>
namespace core {
-class FileReader : public Reader
+class Serializer
{
public:
- FileReader(FILE* stream);
-
- size_t read(void* data, size_t size) override;
-
-private:
- FILE* m_stream;
+ virtual ~Serializer() = default;
+ virtual uint32_t serialized_size() const = 0;
+ virtual void serialize(util::Bytes& output) = 0;
};
-inline FileReader::FileReader(FILE* stream) : m_stream(stream)
-{
-}
-
-inline size_t
-FileReader::read(void* const data, const size_t size)
-{
- if (size == 0) {
- return 0;
- }
- const auto bytes_read = fread(data, 1, size, m_stream);
- if (bytes_read == 0) {
- throw core::Error("Failed to read from file stream");
- }
- return bytes_read;
-}
-
} // namespace core
diff --git a/src/core/Writer.hpp b/src/core/Writer.hpp
deleted file mode 100644
index 7474a5ee..00000000
--- a/src/core/Writer.hpp
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright (C) 2021 Joel Rosdahl and other contributors
-//
-// See doc/AUTHORS.adoc for a complete list of contributors.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation; either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program; if not, write to the Free Software Foundation, Inc., 51
-// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-#pragma once
-
-#include <Util.hpp>
-#include <assertions.hpp>
-
-#include <cstddef>
-#include <cstdint>
-#include <string>
-
-namespace core {
-
-class Writer
-{
-public:
- virtual ~Writer() = default;
-
- // Write `count` bytes from `data`. Throws `core::Error` on failure.
- virtual void write(const void* data, size_t count) = 0;
-
- // Write integer `value`. Throws `core::Error` on failure.
- template<typename T> void write_int(T value);
-
- // Write `value`. Throws `core::Error` on failure.
- void write_str(const std::string& value);
-
- // Finalize writing, e.g. flush written bytes and potentially check for error
- // states. Throws `core::Error` on failure.
- virtual void finalize() = 0;
-};
-
-template<typename T>
-inline void
-Writer::write_int(const T value)
-{
- uint8_t buffer[sizeof(T)];
- Util::int_to_big_endian(value, buffer);
- write(buffer, sizeof(T));
-}
-
-inline void
-Writer::write_str(const std::string& value)
-{
- write(value.data(), value.length());
-}
-
-} // namespace core
diff --git a/src/core/mainoptions.cpp b/src/core/mainoptions.cpp
index 28b7fc8c..717ca8e5 100644
--- a/src/core/mainoptions.cpp
+++ b/src/core/mainoptions.cpp
@@ -25,8 +25,7 @@
#include <InodeCache.hpp>
#include <ProgressBar.hpp>
#include <ccache.hpp>
-#include <core/CacheEntryReader.hpp>
-#include <core/FileReader.hpp>
+#include <core/CacheEntry.hpp>
#include <core/Manifest.hpp>
#include <core/Result.hpp>
#include <core/ResultExtractor.hpp>
@@ -157,39 +156,59 @@ configuration_printer(const std::string& key,
PRINT(stdout, "({}) {} = {}\n", origin, key, value);
}
+static nonstd::expected<std::vector<uint8_t>, std::string>
+read_from_path_or_stdin(const std::string& path)
+{
+ if (path == "-") {
+ std::vector<uint8_t> output;
+ const auto result =
+ util::read_fd(STDIN_FILENO, [&](const uint8_t* data, size_t size) {
+ output.insert(output.end(), data, data + size);
+ });
+ if (!result) {
+ return nonstd::make_unexpected(
+ FMT("Failed to read from stdin: {}", result.error()));
+ }
+ return output;
+ } else {
+ const auto result = util::read_file<std::vector<uint8_t>>(path);
+ if (!result) {
+ return nonstd::make_unexpected(
+ FMT("Failed to read from {}: {}", path, result.error()));
+ }
+ return *result;
+ }
+}
+
static int
inspect_path(const std::string& path)
{
- File file = path == "-" ? File(stdin) : File(path, "rb");
- if (!file) {
- PRINT(stderr, "Error: Failed to open \"{}\"", path);
+ const auto cache_entry_data = read_from_path_or_stdin(path);
+ if (!cache_entry_data) {
+ PRINT(stderr, "Error: {}\n", cache_entry_data.error());
return EXIT_FAILURE;
}
- core::FileReader file_reader(file.get());
- core::CacheEntryReader cache_entry_reader(file_reader);
- const auto& header = cache_entry_reader.header();
- header.inspect(stdout);
+ core::CacheEntry cache_entry(*cache_entry_data);
+ fputs(cache_entry.header().inspect().c_str(), stdout);
- std::vector<uint8_t> data;
- data.resize(cache_entry_reader.header().payload_size());
- cache_entry_reader.read(data.data(), data.size());
+ const auto payload = cache_entry.payload();
- switch (header.entry_type) {
+ switch (cache_entry.header().entry_type) {
case core::CacheEntryType::manifest: {
core::Manifest manifest;
- manifest.read(data);
+ manifest.read(payload);
manifest.inspect(stdout);
break;
}
case core::CacheEntryType::result:
- Result::Deserializer result_deserializer(data);
+ Result::Deserializer result_deserializer(payload);
ResultInspector result_inspector(stdout);
result_deserializer.visit(result_inspector);
break;
}
- cache_entry_reader.finalize();
+ cache_entry.verify_checksum();
return EXIT_SUCCESS;
}
@@ -423,12 +442,12 @@ process_main_options(int argc, const char* const* argv)
util::XXH3_128 checksum;
Fd fd(arg == "-" ? STDIN_FILENO : open(arg.c_str(), O_RDONLY));
if (fd) {
- util::read_fd(*fd, [&checksum](const void* data, size_t size) {
- checksum.update(data, size);
+ util::read_fd(*fd, [&checksum](const uint8_t* data, size_t size) {
+ checksum.update({data, size});
});
const auto digest = checksum.digest();
PRINT(
- stdout, "{}\n", Util::format_base16(digest.bytes(), digest.size()));
+ stdout, "{}\n", Util::format_base16(digest.data(), digest.size()));
} else {
PRINT(stderr, "Error: Failed to checksum {}\n", arg);
}
@@ -446,27 +465,25 @@ process_main_options(int argc, const char* const* argv)
}
case EXTRACT_RESULT: {
- File file = arg == "-" ? File(stdin) : File(arg, "rb");
- if (!file) {
- PRINT(stderr, "Error: Failed to open \"{}\"", arg);
+ const auto cache_entry_data = read_from_path_or_stdin(arg);
+ if (!cache_entry_data) {
+ PRINT(stderr, "Error: \"{}\"", cache_entry_data.error());
return EXIT_FAILURE;
}
std::optional<ResultExtractor::GetRawFilePathFunction> get_raw_file_path;
- if (arg == "-") {
+ if (arg != "-") {
get_raw_file_path = [&](uint8_t file_number) {
return storage::primary::PrimaryStorage::get_raw_file_path(
arg, file_number);
};
}
ResultExtractor result_extractor(".", get_raw_file_path);
- core::FileReader file_reader(file.get());
- core::CacheEntryReader cache_entry_reader(file_reader);
- std::vector<uint8_t> data;
- data.resize(cache_entry_reader.header().payload_size());
- cache_entry_reader.read(data.data(), data.size());
- Result::Deserializer result_deserializer(data);
+ core::CacheEntry cache_entry(*cache_entry_data);
+ const auto payload = cache_entry.payload();
+
+ Result::Deserializer result_deserializer(payload);
result_deserializer.visit(result_extractor);
- cache_entry_reader.finalize();
+ cache_entry.verify_checksum();
return EXIT_SUCCESS;
}
diff --git a/src/core/types.cpp b/src/core/types.cpp
index 6529a2f7..ecd05e52 100644
--- a/src/core/types.cpp
+++ b/src/core/types.cpp
@@ -1,4 +1,4 @@
-// Copyright (C) 2021 Joel Rosdahl and other contributors
+// Copyright (C) 2021-2022 Joel Rosdahl and other contributors
//
// See doc/AUTHORS.adoc for a complete list of contributors.
//
@@ -18,6 +18,11 @@
#include "types.hpp"
+#include <Config.hpp>
+#include <assertions.hpp>
+#include <core/exceptions.hpp>
+#include <fmtmacros.hpp>
+
namespace core {
std::string
@@ -35,4 +40,44 @@ to_string(const CacheEntryType type)
}
}
+int8_t
+compression_level_from_config(const Config& config)
+{
+ return config.compression() ? config.compression_level() : 0;
+}
+
+CompressionType
+compression_type_from_config(const Config& config)
+{
+ return config.compression() ? CompressionType::zstd : CompressionType::none;
+}
+
+CompressionType
+compression_type_from_int(const uint8_t type)
+{
+ switch (type) {
+ case static_cast<uint8_t>(CompressionType::none):
+ return CompressionType::none;
+
+ case static_cast<uint8_t>(CompressionType::zstd):
+ return CompressionType::zstd;
+ }
+
+ throw core::Error(FMT("Unknown type: {}", type));
+}
+
+std::string
+to_string(const CompressionType type)
+{
+ switch (type) {
+ case CompressionType::none:
+ return "none";
+
+ case CompressionType::zstd:
+ return "zstd";
+ }
+
+ ASSERT(false);
+}
+
} // namespace core
diff --git a/src/core/types.hpp b/src/core/types.hpp
index f993e256..592eb4fe 100644
--- a/src/core/types.hpp
+++ b/src/core/types.hpp
@@ -1,4 +1,4 @@
-// Copyright (C) 2021 Joel Rosdahl and other contributors
+// Copyright (C) 2021-2022 Joel Rosdahl and other contributors
//
// See doc/AUTHORS.adoc for a complete list of contributors.
//
@@ -21,10 +21,25 @@
#include <cstdint>
#include <string>
+class Config;
+
namespace core {
enum class CacheEntryType : uint8_t { result = 0, manifest = 1 };
std::string to_string(CacheEntryType type);
+enum class CompressionType : uint8_t {
+ none = 0,
+ zstd = 1,
+};
+
+int8_t compression_level_from_config(const Config& config);
+
+CompressionType compression_type_from_config(const Config& config);
+
+CompressionType compression_type_from_int(uint8_t type);
+
+std::string to_string(CompressionType type);
+
} // namespace core