git-zero-core 0.1.0

Allocation-free no_std Git objects, repositories, packs, zlib and delta primitives
Documentation

git-zero-core

Allocation-free Git objects, repositories, packs, zlib, and delta primitives. Safe no_std Rust without alloc; the caller owns storage, scratch, and I/O.

[dependencies]
git-zero-core = "0.1.0"

read enables decoding and repository validation; write enables snapshot construction and encoding. Both are enabled by default. Select only what the application needs, for example default-features = false, features = ["read"]. The API reference describes the public types. Use git-zero when client or server reexports are useful.

With write, pack::write_indexed_with_scratch streams a stored-block pack and a Git v2 index using one caller-owned pack::IndexEntry per object and bounded read scratch. It supports SHA-1/SHA-256, selected object orders, wire CRCs and 64-bit offsets. Publish the pair only after success: errors invalidate both outputs. Existing verified entry metadata can be streamed separately with pack::write_index.

With read, pack::Decoder accepts fragmented SHA-1/SHA-256 packs without retaining a complete pack, object body, or object-count-sized index. The host supplies a 32 KiB inflate window and arbitrary input/output slices. Events expose entry headers, body fragments, entry CRCs, plain-object IDs, and the verified pack checksum. Offset/reference deltas emit instruction bytes for separate base resolution. Wire size, object count, inflation size and expansion ratio have independent limits. Stage all output privately until the trailer and subsequent object/base/connectivity validation pass; this decoder alone is not a push validator. No Git executable, filesystem, allocator or runtime API is used.

pack::DeltaDecoder parses inflated delta instructions across arbitrary chunks. It checks base/result sizes, source/target ranges, instruction count, chain depth and exact EOF. Copy events expose ranges for bounded asynchronous base reads; insert events borrow input fragments. It owns neither the base nor the output. Execute every event before resuming and use ObjectDigest to hash output chunks with the inherited kind and declared size. A completed delta authenticates its syntax; the host still verifies storage I/O, object identity and connectivity.

pack::IndexHeader retains only a version-2 index's 1032-byte header/fanout. Its independent IndexLookup states request exact ranges for binary search, CRCs and direct/64-bit offsets, without loading the complete index. Hosts can await reads and cache larger immutable storage pages. Authenticate index bytes, ordering and their pack relationship before using this lookup API; parsing a header alone does not establish integrity or trustworthy absence.

pack::EntryDecoder resumes at a trusted pack offset using the same bounded entry parser as pack intake. It stops after one entry, preserving following input bytes, and reports its CRC and plain-object ID. Compare those with the authenticated index; resolve delta instructions separately. It verifies neither the enclosing pack checksum nor graph connectivity.

With write, pack::PackComposer assembles fetch packs from authenticated zlib streams without inflation/recompression. It writes fresh entry headers, CRCs and a pack checksum with a bounded wire budget and fixed state. Reference deltas allow a host to reorder entries or move OFS deltas between packs after resolving their base IDs. The host supplies a unique authorized selection and its delta base closure, and awaits storage between writes. Errors invalidate partial output.

object::LinkDecoder streams commit parents, root trees, tag targets and tree entries into a host-owned graph index. It retains two caller-sized tree names, fixed header scratch and 64-bit positions; opaque messages and blobs need no body storage. Historical recognized tree modes are preserved and flagged when noncanonical. The host authenticates objects, checks target existence/kinds and all duplicate tree names, and enforces reachability and ref-update policy. Scanner completion alone is not proof of a valid repository graph.

pack::RangeResolver reconstructs complete objects or partial canonical ranges through explicit metadata/read requests. It follows nested deltas using caller RangeFrame slots, with independent output, representation-work, instruction, read-size and depth limits. Storage failures can be retried before supplying a reply. For new staged objects, request ObjectRange::All, hash every output fragment and complete pack/graph validation before publication. Existing bases must already have authenticated metadata and canonical identities.

pack::EntryRangeDecoder supplies representation ranges from authenticated compressed entries using one 32 KiB window and caller read scratch. It skips the inflated prefix, preserves pending output under backpressure, and stops at the requested boundary. Partial completion does not validate the unread entry tail. Hosts can await bounded source reads and cache inflated pages/ranges to avoid repeated prefix inflation; neither API retains complete objects or an index.

read enables zlib inflation, pack/delta decoding and repository validation. write enables snapshots, pack/zlib encoding and pack caching. Both are enabled by default. Object IDs, borrowed objects, repositories, readers, packet lines and HTTP request/response values are always available.

With read, checkout provides borrowed file/symlink contents, byte-name tree lookup and bounded tag/commit-to-tree traversal over a caller-owned ObjectSource. It supports both object formats and retains only IDs and offsets between source reads, so caches may evict the previous object on every lookup. Hosts supply hash-verified objects and implement permissions, filesystem limits and I/O.

Part of git-zero.

With write, pack::Encoder and pack::LooseEncoder expose resumable pulls into caller-owned output slices. They retain stored-DEFLATE framing, Adler-32 and pack hash state without object-sized scratch. Packs accept a borrowed selection or all objects. Hold repository metadata, bodies and selected indices stable until EOF; a host can recreate the repository from immutable inputs between pulls. Each call performs at most one body read. Preserve unsent output before advancing again. Encoding matches the synchronous stored-block writers in both object formats.

cache::selected caches raw stored-block packs by their exact object set. It supports independent refs and partial selections in SHA-1/SHA-256. Complete authorization, visible-ref/reachability checks, graph selection and traversal budgets before every call. Pass strictly ascending selected indices with Selection::Indices (or Selection::All for all objects). ACK-only rounds and shallow-list probes skip the cache; their control packets are generated separately.

use git_zero_core::{
    cache::selected::{self, Buffers, FixedCache, Selection},
    io::Write,
    ObjectId, Repository, Result,
};

fn serve(
    repository: &impl Repository,
    indices: &[usize],
    cache: &mut FixedCache<'_>,
    output: &mut impl Write,
) -> Result<()> {
    let mut ids = [ObjectId::Sha1([0; 20]); 64]; // One slot per selected object.
    let mut staging = [0; 8192];
    selected::write(repository, Selection::Indices(indices), cache, Buffers {
        ids: &mut ids,
        pack: &mut staging,
        read: &mut [0; 1024],
        byte_limit: 8192,
    }, output)?;
    Ok(())
}

Construct FixedCache::new(&mut cache_bytes) once from a host-owned byte buffer and retain it across calls to serve, supplying a fresh response sink each time. An unchanged authorized selection can reuse the verified pack; cache.statistics().hits records successful cache hits.

Hits require ID scratch and a byte limit, with no pack staging or body reads when the provider supplies precomputed IDs. Both hits and misses validate the pack trailer, stored zlib framing/Adler32, canonical object hashes and exact set before output; misses also validate before insertion. The key's 48-byte encoding includes schema, object format, codec and encoder revision. Equivalent sets can use different pack orders: publish pack/index URLs using the actual pack checksum, rather than the selected-set key digest. Backend errors and corruption propagate. Failed insertion preserves the old entry; an output error may retain a new complete entry for retry. Hosts discard partial output and own atomic publication, persistence and coordination between concurrent requests.