Skip to main content

emit_pack

Function emit_pack 

Source
pub fn emit_pack<'p>(
    entries: &'p [EmitEntry],
    hash: GitHashKind,
    out: &mut dyn Write,
    stored_of: &dyn Fn(usize) -> Result<&'p [u8]>,
) -> Result<EmitReport>
Expand description

Assemble ordered entries into a packfile.

§What this does and does not do

It copies. Every entry’s compressed payload goes out byte for byte as payload_of hands it over; nothing here inflates, deflates or computes a delta. The only thing rewritten is the entry header, and only because OFS_DELTA carries a distance that is relative to a position in the input pack. An entry whose payload the caller rebuilt says so in EmitEntry::recompressed and is counted apart, so the receipt names which of the two happened rather than assuming.

entries must already be in topological_order and must be closed — every OFS_DELTA base present. A base that is absent is an error here rather than a silently dropped back-reference, because the alternative is a pack whose closure does not hold and a client that discovers it.

The caller supplies stored_of, which borrows entry i’s whole stored bytes — header included. Splitting it that way keeps this function free of any opinion about where bytes live: they may be a slice of a mapping of the archive, a slice of an owned buffer the caller rebuilt, or anything else that outlives the call.

§Why the WHOLE entry and not the payload

It used to be payload_of, handing over stored[header_len..], and the header the caller had just stripped was then re-parsed here anyway — for the stated size, and for a REF_DELTA’s base oid. Three readings of one varint across two files, and the caller’s header_len had to agree with this function’s type_and_size or a ref-delta went out with its base named twice (which it once did: git index-pack --strict answered inflate returned 1). One reading, in one place, is LAW 5’s fix by construction.

§It streams, and it borrows — P-018 and P-025

Two properties, both deliberate, both previously absent:

  • stored_of returns &[u8], not Vec<u8>. It used to return an owned buffer, which is P-025 in its exact form: data.to_owned(), one heap allocation per object served, on a path whose entire claim is that it copies stored bytes without touching them. It is indexed by entry rather than handed an &EmitEntry so that the bytes may live outside the entry — which since 2026-08-14 they do: an EntryBytes::Extent is an address, and the bytes it names are a slice of the mapped archive.
  • The pack goes to out as it is built, and the trailer is hashed incrementally. It used to accumulate the whole pack in a Vec<u8> and hash it at the end, so serving a 2 GiB clone meant holding 2 GiB. The one fact the old shape got for free — the output offset an OFS_DELTA distance is measured against — is counted here instead, which is cheaper than the buffer that was carrying it.

What is emphatically not here is gix’s pack pipeline: no counting pass with a serial reduce, no sort_by over counts, no BTreeMap reorder, no hashing of every byte on a consuming thread. Those are P-018’s serial tail, and this function is the reason none of it is needed — the bytes are already deflated and already delta-encoded, so emitting is a copy and an addition.