package packed
import (
"fmt"
"io"
"slices"
"lindenii.org/go/furgit/errs"
"lindenii.org/go/furgit/internal/compress/zlib"
"lindenii.org/go/furgit/internal/format/packfile"
"lindenii.org/go/furgit/internal/format/packfile/delta"
"lindenii.org/go/lgo/intconv"
"lindenii.org/go/lgo/sync"
)
//nolint:gochecknoglobals
var deltaHeaderPool = sync.NewPool(func() *[delta.MaxHeaderSizesLen]byte {
return new([delta.MaxHeaderSizesLen]byte)
})
// deltaNode is a delta entry on a resolution chain.
type deltaNode struct {
// payload is the entry's compressed delta payload view.
payload []byte
// size is the entry's declared inflated delta size.
size uint64
// baseOffset is the entry's base entry offset.
baseOffset int
}
// unpackEntry reconstructs the object stored at offset in p,
// following ref- and ofs-delta chains within the pack.
//
// A direct base-cache hit returns the shared cache buffer itself,
// so the result may alias cache storage and must not be mutated;
// delta-applied results are freshly allocated.
//
// Labels: Life-Parent, Mut-No.
func (packed *Packed) unpackEntry(p *pack, offset int) (packfile.EntryType, []byte, error) {
var zero packfile.EntryType
var (
chain []deltaNode
baseType packfile.EntryType
base []byte
fromCache bool
)
// Drill down to the innermost base,
// stopping early at any cached base.
cur := offset
for {
if cached, ok := packed.baseCache.Get(baseKey{pack: p, offset: cur}); ok {
baseType = cached.entryType
base = cached.content
fromCache = true
break
}
if len(chain) >= delta.MaxChainDepth {
return zero, nil, fmt.Errorf("%w: pack %q: delta chain too deep", ErrMalformedPackedStore, p.name)
}
header, payload, err := p.entryHeaderAt(cur, packed.objectFormat)
if err != nil {
return zero, nil, err
}
if header.Type.IsBase() {
base, err = inflate(payload, header.Size)
if err != nil {
return zero, nil, fmt.Errorf("%w: pack %q: %w", ErrMalformedPackedStore, p.name, err)
}
baseType = header.Type
break
}
baseOffset, err := packed.deltaBaseOffset(p, cur, header)
if err != nil {
return zero, nil, err
}
chain = append(chain, deltaNode{
payload: payload,
size: header.Size,
baseOffset: baseOffset,
})
cur = baseOffset
}
// A direct cache hit with no deltas to apply
// returns the shared cache buffer directly;
// callers are contractually Mut-No.
if len(chain) == 0 && fromCache {
return baseType, base, nil
}
// Apply deltas back up the chain, caching each consumed base.
for i, node := range slices.Backward(chain) {
deltaData, err := inflate(node.payload, node.size)
if err != nil {
return zero, nil, fmt.Errorf("%w: pack %q: %w", ErrMalformedPackedStore, p.name, err)
}
result, err := delta.Apply(base, deltaData)
if err != nil {
return zero, nil, fmt.Errorf("%w: pack %q: %w", ErrMalformedPackedStore, p.name, err)
}
consumedFromCache := fromCache && i == len(chain)-1
if !consumedFromCache {
packed.baseCache.Add(
baseKey{pack: p, offset: node.baseOffset},
cachedBase{entryType: baseType, content: base},
)
}
base = result
}
return baseType, base, nil
}
// deltaBaseOffset resolves a delta entry's base entry offset
// within the same pack.
func (packed *Packed) deltaBaseOffset(p *pack, offset int, header packfile.EntryHeader) (int, error) {
switch header.Type {
case packfile.EntryTypeOfsDelta:
dist, err := intconv.Uint64ToInt(header.OfsDistance)
if err != nil || dist == 0 || dist > offset {
return 0, fmt.Errorf("%w: pack %q: invalid ofs-delta distance", ErrMalformedPackedStore, p.name)
}
return offset - dist, nil
case packfile.EntryTypeRefDelta:
refBase := header.RefBase[:packed.objectFormat.Size()]
baseOffsetU, found, err := p.idx.Lookup(refBase)
if err != nil {
return 0, fmt.Errorf("%w: pack %q: %w", ErrMalformedPackedStore, p.name, err)
}
if !found {
baseID, idErr := packed.objectFormat.FromBytes(refBase)
if idErr != nil {
return 0, fmt.Errorf("%w: pack %q: %w", ErrMalformedPackedStore, p.name, idErr)
}
return 0, fmt.Errorf(
"%w: resolving ref-delta: %w",
ErrMalformedPackedStore, &errs.ObjectMissingError{OID: baseID},
)
}
baseOffset, err := intconv.Uint64ToInt(baseOffsetU)
if err != nil {
return 0, fmt.Errorf("%w: pack %q: ref-delta base offset overflows int: %w", ErrMalformedPackedStore, p.name, err)
}
return baseOffset, nil
case packfile.EntryTypeInvalid,
packfile.EntryTypeCommit,
packfile.EntryTypeTree,
packfile.EntryTypeBlob,
packfile.EntryTypeTag,
packfile.EntryTypeFuture:
}
panic("object/store/packed: deltaBaseOffset on non-delta entry")
}
// resolveType walks one delta chain
// to find the chained base object entry type,
// without inflating any content.
func (packed *Packed) resolveType(p *pack, offset int, entryHeader packfile.EntryHeader) (packfile.EntryType, error) {
var zero packfile.EntryType
depth := 0
for entryHeader.Type.IsDelta() {
if cached, ok := packed.baseCache.Peek(baseKey{pack: p, offset: offset}); ok {
return cached.entryType, nil
}
depth++
if depth > delta.MaxChainDepth {
return zero, fmt.Errorf("%w: pack %q: delta chain too deep", ErrMalformedPackedStore, p.name)
}
baseOffset, err := packed.deltaBaseOffset(p, offset, entryHeader)
if err != nil {
return zero, err
}
offset = baseOffset
entryHeader, _, err = p.entryHeaderAt(offset, packed.objectFormat)
if err != nil {
return zero, err
}
}
return entryHeader.Type, nil
}
// deltaResultSize reads the declared result size
// from one compressed delta payload prefix.
func deltaResultSize(payload []byte, deltaSize uint64) (int, error) {
zr, err := zlib.NewReaderBytes(payload)
if err != nil {
return 0, fmt.Errorf("reading delta header: %w", err)
}
defer func() { _ = zr.Close() }()
buf := deltaHeaderPool.Get()
defer deltaHeaderPool.Put(buf)
prefixLen := min(uint64(delta.MaxHeaderSizesLen), deltaSize)
prefix := buf[:prefixLen]
_, err = io.ReadFull(zr, prefix)
if err != nil {
return 0, fmt.Errorf("reading delta header: %w", err)
}
_, resultSize, _, err := delta.ParseHeaderSizes(prefix)
if err != nil {
return 0, fmt.Errorf("reading delta header: %w", err)
}
size, err := intconv.Uint64ToInt(resultSize)
if err != nil {
return 0, fmt.Errorf("reading delta header: result size overflows int: %w", err)
}
return size, nil
}