Resolves a chain of deltas, returning the final base object data. pack is
assumed to be looking at the start of the object data for the base object of
the chain, and will then apply the deltas in delta_offsets in reverse order
to obtain the final object.
fn resolveDeltaChain(
allocator: Allocator,
format: Oid.Format,
pack: *Io.File.Reader,
base_object: Object,
delta_offsets: []const u64,
cache: *ObjectCache,
) ![]const u8
fn resolveDeltaChain(
allocator: Allocator,
format: Oid.Format,
pack: *Io.File.Reader,
base_object: Object,
delta_offsets: []const u64,
cache: *ObjectCache,
) ![]const u8 {
var base_data = base_object.data;
var i: usize = delta_offsets.len;
while (i > 0) {
i -= 1;
const delta_offset = delta_offsets[i];
try pack.seekTo(delta_offset);
const delta_header = try EntryHeader.read(format, &pack.interface);
const delta_data = try readObjectRaw(allocator, &pack.interface, delta_header.uncompressedLength());
defer allocator.free(delta_data);
var delta_reader: Io.Reader = .fixed(delta_data);
_ = try delta_reader.takeLeb128(u64); // base object size
const expanded_size = try delta_reader.takeLeb128(u64);
const expanded_alloc_size = std.math.cast(usize, expanded_size) orelse return error.ObjectTooLarge;
const expanded_data = try allocator.alloc(u8, expanded_alloc_size);
errdefer allocator.free(expanded_data);
var expanded_delta_stream: Io.Writer = .fixed(expanded_data);
try expandDelta(base_data, &delta_reader, &expanded_delta_stream);
if (expanded_delta_stream.end != expanded_size) return error.InvalidObject;
try cache.put(allocator, delta_offset, .{ .type = base_object.type, .data = expanded_data });
base_data = expanded_data;
}
return base_data;
}