shithub: furgit

Download patch

ref: b480c5c4dfaddb9b137f50b1a7651e3bce2ecb98
parent: 36bb351d73932b34709dc35ebe37f480b3a0a6fd
author: Runxi Yu <me@runxiyu.org>
date: Sat Feb 21 15:21:55 EST 2026

objectstore/packed: Improve delta base caching

--- a/objectstore/packed/delta_apply.go
+++ b/objectstore/packed/delta_apply.go
@@ -4,27 +4,33 @@
 	"fmt"
 
 	deltaapply "codeberg.org/lindenii/furgit/format/delta/apply"
+	packfmt "codeberg.org/lindenii/furgit/format/pack"
 	"codeberg.org/lindenii/furgit/objecttype"
 )
 
 // deltaResolveContent resolves one object's content bytes from its pack location.
 func (store *Store) deltaResolveContent(start location) (objecttype.Type, []byte, error) {
-	plan, err := store.deltaPlanFor(start)
+	chain, err := store.deltaBuildChain(start)
 	if err != nil {
 		return objecttype.TypeInvalid, nil, err
 	}
+	return store.deltaResolveChain(chain)
+}
 
-	baseType, out, err := store.deltaResolveBase(plan)
+// deltaResolveChain resolves one object chain into content bytes.
+func (store *Store) deltaResolveChain(chain deltaChain) (objecttype.Type, []byte, error) {
+	ty, out, nextDelta, err := store.deltaResolveChainStart(chain)
 	if err != nil {
 		return objecttype.TypeInvalid, nil, err
 	}
-	for i := len(plan.frames) - 1; i >= 0; i-- {
-		frame := plan.frames[i]
-		pack, err := store.openPack(frame.packName)
+
+	for i := nextDelta; i >= 0; i-- {
+		node := chain.deltas[i]
+		pack, err := store.openPack(node.loc.packName)
 		if err != nil {
 			return objecttype.TypeInvalid, nil, err
 		}
-		delta, err := inflateAt(pack, frame.dataOffset, -1)
+		delta, err := inflateAt(pack, node.dataOffset, -1)
 		if err != nil {
 			return objecttype.TypeInvalid, nil, err
 		}
@@ -32,13 +38,75 @@
 		if err != nil {
 			return objecttype.TypeInvalid, nil, err
 		}
+		store.cacheMu.Lock()
+		store.deltaCache.add(
+			deltaBaseKey{packName: node.loc.packName, offset: node.loc.offset},
+			ty,
+			out,
+		)
+		store.cacheMu.Unlock()
 	}
-	if int64(len(out)) != plan.declaredSize {
+
+	if int64(len(out)) != chain.declaredSize {
 		return objecttype.TypeInvalid, nil, fmt.Errorf(
 			"objectstore/packed: resolved content size mismatch: got %d want %d",
 			len(out),
-			plan.declaredSize,
+			chain.declaredSize,
 		)
 	}
-	return baseType, out, nil
+	if ty != chain.baseType {
+		return objecttype.TypeInvalid, nil, fmt.Errorf(
+			"objectstore/packed: resolved content type mismatch: got %d want %d",
+			ty,
+			chain.baseType,
+		)
+	}
+	return ty, out, nil
+}
+
+// deltaResolveChainStart finds the nearest cached chain node or inflates the
+// innermost base object. It returns the starting bytes and the next delta index
+// to apply in reverse order.
+func (store *Store) deltaResolveChainStart(chain deltaChain) (objecttype.Type, []byte, int, error) {
+	for i, node := range chain.deltas {
+		store.cacheMu.RLock()
+		ty, out, ok := store.deltaCache.get(
+			deltaBaseKey{packName: node.loc.packName, offset: node.loc.offset},
+		)
+		store.cacheMu.RUnlock()
+		if ok {
+			return ty, out, i - 1, nil
+		}
+	}
+
+	store.cacheMu.RLock()
+	ty, out, ok := store.deltaCache.get(
+		deltaBaseKey{packName: chain.baseLoc.packName, offset: chain.baseLoc.offset},
+	)
+	store.cacheMu.RUnlock()
+	if ok {
+		return ty, out, len(chain.deltas) - 1, nil
+	}
+
+	pack, meta, err := store.entryMetaAt(chain.baseLoc)
+	if err != nil {
+		return objecttype.TypeInvalid, nil, 0, err
+	}
+	if !packfmt.IsBaseObjectType(meta.ty) {
+		return objecttype.TypeInvalid, nil, 0, fmt.Errorf("objectstore/packed: delta chain base is not a base object")
+	}
+	base, err := inflateAt(pack, meta.dataOffset, meta.size)
+	if err != nil {
+		return objecttype.TypeInvalid, nil, 0, err
+	}
+
+	store.cacheMu.Lock()
+	store.deltaCache.add(
+		deltaBaseKey{packName: chain.baseLoc.packName, offset: chain.baseLoc.offset},
+		meta.ty,
+		base,
+	)
+	store.cacheMu.Unlock()
+
+	return meta.ty, base, len(chain.deltas) - 1, nil
 }
--- a/objectstore/packed/delta_base.go
+++ /dev/null
@@ -1,40 +1,0 @@
-package packed
-
-import (
-	"fmt"
-
-	packfmt "codeberg.org/lindenii/furgit/format/pack"
-	"codeberg.org/lindenii/furgit/objecttype"
-)
-
-// deltaResolveBase materializes the base object body for one delta plan.
-func (store *Store) deltaResolveBase(plan deltaPlan) (objecttype.Type, []byte, error) {
-	cacheKey := deltaBaseKey{
-		packName: plan.baseLoc.packName,
-		offset:   plan.baseLoc.offset,
-	}
-
-	store.cacheMu.RLock()
-	if ty, content, ok := store.deltaCache.get(cacheKey); ok {
-		store.cacheMu.RUnlock()
-		return ty, content, nil
-	}
-	store.cacheMu.RUnlock()
-
-	pack, meta, err := store.entryMetaAt(plan.baseLoc)
-	if err != nil {
-		return objecttype.TypeInvalid, nil, err
-	}
-	if !packfmt.IsBaseObjectType(meta.ty) {
-		return objecttype.TypeInvalid, nil, fmt.Errorf("objectstore/packed: delta plan base is not a base object")
-	}
-	base, err := inflateAt(pack, meta.dataOffset, meta.size)
-	if err != nil {
-		return objecttype.TypeInvalid, nil, err
-	}
-
-	store.cacheMu.Lock()
-	store.deltaCache.add(cacheKey, meta.ty, base)
-	store.cacheMu.Unlock()
-	return meta.ty, base, nil
-}
--- a/objectstore/packed/delta_plan.go
+++ b/objectstore/packed/delta_plan.go
@@ -8,16 +8,16 @@
 	"codeberg.org/lindenii/furgit/objecttype"
 )
 
-// deltaFrame describes one delta payload to apply during reconstruction.
-type deltaFrame struct {
-	// packName identifies where the delta payload lives.
-	packName string
+// deltaNode describes one delta object in a reconstruction chain.
+type deltaNode struct {
+	// loc identifies the delta object's pack location.
+	loc location
 	// dataOffset points to the start of the delta zlib payload in pack.
 	dataOffset int
 }
 
-// deltaPlan describes how to reconstruct one requested object.
-type deltaPlan struct {
+// deltaChain describes how to reconstruct one requested object.
+type deltaChain struct {
 	// declaredSize is the target object's declared content size.
 	declaredSize int64
 	// baseLoc points to the innermost base object.
@@ -24,60 +24,60 @@
 	baseLoc location
 	// baseType is the canonical object type resolved from baseLoc.
 	baseType objecttype.Type
-	// frames contains deltas from target down toward base.
-	frames []deltaFrame
+	// deltas contains delta objects from target down toward base.
+	deltas []deltaNode
 }
 
-// deltaPlanFor walks one object's chain and builds a delta reconstruction plan.
-func (store *Store) deltaPlanFor(start location) (deltaPlan, error) {
+// deltaBuildChain walks one object's chain and builds a reconstruction chain.
+func (store *Store) deltaBuildChain(start location) (deltaChain, error) {
 	visited := make(map[location]struct{})
 	current := start
 
-	var plan deltaPlan
-	plan.declaredSize = -1
+	var chain deltaChain
+	chain.declaredSize = -1
 
 	for {
 		if _, ok := visited[current]; ok {
-			return deltaPlan{}, fmt.Errorf("objectstore/packed: delta cycle while resolving object")
+			return deltaChain{}, fmt.Errorf("objectstore/packed: delta cycle while resolving object")
 		}
 		visited[current] = struct{}{}
 
 		pack, meta, err := store.entryMetaAt(current)
 		if err != nil {
-			return deltaPlan{}, err
+			return deltaChain{}, err
 		}
-		if plan.declaredSize < 0 {
+		if chain.declaredSize < 0 {
 			if packfmt.IsBaseObjectType(meta.ty) {
-				plan.declaredSize = meta.size
+				chain.declaredSize = meta.size
 			} else {
 				declaredSize, err := deltaDeclaredSizeAt(pack, meta.dataOffset)
 				if err != nil {
-					return deltaPlan{}, err
+					return deltaChain{}, err
 				}
-				plan.declaredSize = declaredSize
+				chain.declaredSize = declaredSize
 			}
 		}
 
 		if packfmt.IsBaseObjectType(meta.ty) {
-			plan.baseLoc = current
-			plan.baseType = meta.ty
-			return plan, nil
+			chain.baseLoc = current
+			chain.baseType = meta.ty
+			return chain, nil
 		}
 
 		switch meta.ty {
 		case objecttype.TypeRefDelta:
-			plan.frames = append(plan.frames, deltaFrame{
-				packName:   current.packName,
+			chain.deltas = append(chain.deltas, deltaNode{
+				loc:        current,
 				dataOffset: meta.dataOffset,
 			})
 			next, err := store.lookup(meta.baseRefID)
 			if err != nil {
-				return deltaPlan{}, err
+				return deltaChain{}, err
 			}
 			current = next
 		case objecttype.TypeOfsDelta:
-			plan.frames = append(plan.frames, deltaFrame{
-				packName:   current.packName,
+			chain.deltas = append(chain.deltas, deltaNode{
+				loc:        current,
 				dataOffset: meta.dataOffset,
 			})
 			current = location{
@@ -85,11 +85,11 @@
 				offset:   meta.baseOfs,
 			}
 		case objecttype.TypeCommit, objecttype.TypeTree, objecttype.TypeBlob, objecttype.TypeTag:
-			return deltaPlan{}, fmt.Errorf("objectstore/packed: internal invariant violation for base type %d", meta.ty)
+			return deltaChain{}, fmt.Errorf("objectstore/packed: internal invariant violation for base type %d", meta.ty)
 		case objecttype.TypeInvalid, objecttype.TypeFuture:
-			return deltaPlan{}, fmt.Errorf("objectstore/packed: unsupported pack type %d", meta.ty)
+			return deltaChain{}, fmt.Errorf("objectstore/packed: unsupported pack type %d", meta.ty)
 		default:
-			return deltaPlan{}, fmt.Errorf("objectstore/packed: unsupported pack type %d", meta.ty)
+			return deltaChain{}, fmt.Errorf("objectstore/packed: unsupported pack type %d", meta.ty)
 		}
 	}
 }
--- a/objectstore/packed/read_header.go
+++ b/objectstore/packed/read_header.go
@@ -11,9 +11,9 @@
 	if err != nil {
 		return objecttype.TypeInvalid, 0, err
 	}
-	plan, err := store.deltaPlanFor(loc)
+	chain, err := store.deltaBuildChain(loc)
 	if err != nil {
 		return objecttype.TypeInvalid, 0, err
 	}
-	return plan.baseType, plan.declaredSize, nil
+	return chain.baseType, chain.declaredSize, nil
 }
--