try {
core::FileReader file_reader(*file);
core::CacheEntryReader reader(file_reader);
- manifest.read(reader);
+ std::vector<uint8_t> payload;
+ payload.resize(reader.header().payload_size());
+ reader.read(payload.data(), payload.size());
+ manifest.read(payload);
reader.finalize();
} catch (const core::Error& e) {
LOG("Error reading {}: {}", path, e.what());
header.set_entry_size_from_payload_size(manifest.serialized_size());
core::CacheEntryWriter writer(file_writer, header);
- manifest.write(writer);
+ std::vector<uint8_t> payload;
+ payload.reserve(header.payload_size());
+ manifest.serialize(payload);
+ writer.write(payload.data(), payload.size());
writer.finalize();
atomic_manifest_file.commit();
}
// Read `size` bytes. Throws `core::Error` on failure.
nonstd::span<const uint8_t> read_bytes(size_t size);
+ // Read and copy `buffer.size()` bytes into `buffer`. Throws `core::Error` on
+ // failure.
+ void read_and_copy_bytes(nonstd::span<uint8_t> buffer);
+
// Read a string of length `length`. Throws `core::Error` on failure.
std::string_view read_str(size_t length);
return bytes;
}
+inline void
+CacheEntryDataReader::read_and_copy_bytes(nonstd::span<uint8_t> buffer)
+{
+ const auto span = read_bytes(buffer.size());
+ memcpy(buffer.data(), span.data(), span.size());
+}
+
inline std::string_view
CacheEntryDataReader::read_str(const size_t length)
{
#include <Context.hpp>
#include <Hash.hpp>
#include <Logging.hpp>
-#include <core/Reader.hpp>
-#include <core/Writer.hpp>
+#include <core/CacheEntryDataReader.hpp>
+#include <core/CacheEntryDataWriter.hpp>
#include <core/exceptions.hpp>
#include <fmtmacros.hpp>
#include <hashutil.hpp>
const uint8_t Manifest::k_format_version = 0;
void
-Manifest::read(Reader& reader)
+Manifest::read(nonstd::span<const uint8_t> data)
{
clear();
+ core::CacheEntryDataReader reader(data);
+
const auto format_version = reader.read_int<uint8_t>();
if (format_version != k_format_version) {
- throw core::Error(FMT(
- "Unknown format version: {} != {}", format_version, k_format_version));
+ throw core::Error(FMT("Unknown manifest format version: {} != {}",
+ format_version,
+ k_format_version));
}
const auto file_count = reader.read_int<uint32_t>();
for (uint32_t i = 0; i < file_count; ++i) {
- m_files.push_back(reader.read_str(reader.read_int<uint16_t>()));
+ m_files.push_back(
+ std::string(reader.read_str(reader.read_int<uint16_t>())));
}
const auto file_info_count = reader.read_int<uint32_t>();
auto& entry = m_file_infos.back();
reader.read_int(entry.index);
- reader.read(entry.digest.bytes(), Digest::size());
+ reader.read_and_copy_bytes({entry.digest.bytes(), Digest::size()});
reader.read_int(entry.fsize);
reader.read_int(entry.mtime);
reader.read_int(entry.ctime);
for (uint32_t j = 0; j < file_info_index_count; ++j) {
entry.file_info_indexes.push_back(reader.read_int<uint32_t>());
}
- reader.read(entry.key.bytes(), Digest::size());
+ reader.read_and_copy_bytes({entry.key.bytes(), Digest::size()});
}
}
}
}
-size_t
+uint32_t
Manifest::serialized_size() const
{
uint64_t size = 0;
size += Digest::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 (size > max) {
+ throw core::Error(
+ FMT("Serialized manifest too large ({} > {})", size, max));
+ }
return size;
}
void
-Manifest::write(Writer& writer) const
+Manifest::serialize(std::vector<uint8_t>& output) const
{
+ core::CacheEntryDataWriter writer(output);
+
writer.write_int(k_format_version);
writer.write_int<uint32_t>(m_files.size());
for (const auto& file : m_files) {
writer.write_int<uint32_t>(m_file_infos.size());
for (const auto& file_info : m_file_infos) {
writer.write_int<uint32_t>(file_info.index);
- writer.write(file_info.digest.bytes(), Digest::size());
+ writer.write_bytes({file_info.digest.bytes(), Digest::size()});
writer.write_int(file_info.fsize);
writer.write_int(file_info.mtime);
writer.write_int(file_info.ctime);
for (auto index : result.file_info_indexes) {
writer.write_int(index);
}
- writer.write(result.key.bytes(), Digest::size());
+ writer.write_bytes({result.key.bytes(), Digest::size()});
}
}
}
void
-Manifest::dump(FILE* const stream) const
+Manifest::inspect(FILE* const stream) const
{
PRINT(stream, "Manifest format version: {}\n", k_format_version);
#include <Digest.hpp>
+#include <third_party/nonstd/span.hpp>
+
#include <cstdint>
#include <optional>
#include <string>
namespace core {
-class Reader;
-class Writer;
-
class Manifest
{
public:
Manifest() = default;
- void read(Reader& reader);
+ void read(nonstd::span<const uint8_t> data);
+
std::optional<Digest> look_up_result_digest(const Context& ctx) const;
bool add_result(const Digest& result_key,
const std::unordered_map<std::string, Digest>& included_files,
time_t time_of_compilation,
bool save_timestamp);
- size_t serialized_size() const;
- void write(Writer& writer) const;
- void dump(FILE* stream) const;
+ uint32_t serialized_size() const;
+ void serialize(std::vector<uint8_t>& output) const;
+
+ void inspect(FILE* stream) const;
private:
struct FileStats
std::vector<ResultEntry> m_results;
void clear();
+
uint32_t get_file_info_index(
const std::string& path,
const Digest& digest,
const std::unordered_map<FileInfo, uint32_t>& mf_file_infos,
time_t time_of_compilation,
bool save_timestamp);
+
bool
result_matches(const Context& ctx,
const ResultEntry& result,
const auto& header = cache_entry_reader.header();
header.inspect(stdout);
+ std::vector<uint8_t> data;
+ data.resize(cache_entry_reader.header().payload_size());
+ cache_entry_reader.read(data.data(), data.size());
+
switch (header.entry_type) {
case core::CacheEntryType::manifest: {
core::Manifest manifest;
- manifest.read(cache_entry_reader);
- manifest.dump(stdout);
+ manifest.read(data);
+ manifest.inspect(stdout);
break;
}
case core::CacheEntryType::result:
- 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);
ResultInspector result_inspector(stdout);
result_deserializer.visit(result_inspector);