shithub: furgit

Download patch

ref: fd55beb6188350974afd93dfffcef973ebdad6e8
parent: b480c5c4dfaddb9b137f50b1a7651e3bce2ecb98
author: Runxi Yu <me@runxiyu.org>
date: Sat Feb 21 15:40:05 EST 2026

objectstore/packed: Don't use full delta reconstruction just to read headers

--- a/objectstore/packed/delta_apply.go
+++ b/objectstore/packed/delta_apply.go
@@ -14,11 +14,22 @@
 	if err != nil {
 		return objecttype.TypeInvalid, nil, err
 	}
-	return store.deltaResolveChain(chain)
+	pack, meta, err := store.entryMetaAt(start)
+	if err != nil {
+		return objecttype.TypeInvalid, nil, err
+	}
+	declaredSize := meta.size
+	if !packfmt.IsBaseObjectType(meta.ty) {
+		declaredSize, err = deltaDeclaredSizeAt(pack, meta.dataOffset)
+		if err != nil {
+			return objecttype.TypeInvalid, nil, err
+		}
+	}
+	return store.deltaResolveChain(chain, declaredSize)
 }
 
 // deltaResolveChain resolves one object chain into content bytes.
-func (store *Store) deltaResolveChain(chain deltaChain) (objecttype.Type, []byte, error) {
+func (store *Store) deltaResolveChain(chain deltaChain, declaredSize int64) (objecttype.Type, []byte, error) {
 	ty, out, nextDelta, err := store.deltaResolveChainStart(chain)
 	if err != nil {
 		return objecttype.TypeInvalid, nil, err
@@ -47,11 +58,11 @@
 		store.cacheMu.Unlock()
 	}
 
-	if int64(len(out)) != chain.declaredSize {
+	if int64(len(out)) != declaredSize {
 		return objecttype.TypeInvalid, nil, fmt.Errorf(
 			"objectstore/packed: resolved content size mismatch: got %d want %d",
 			len(out),
-			chain.declaredSize,
+			declaredSize,
 		)
 	}
 	if ty != chain.baseType {
--- a/objectstore/packed/delta_plan.go
+++ b/objectstore/packed/delta_plan.go
@@ -18,8 +18,6 @@
 
 // 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.
 	baseLoc location
 	// baseType is the canonical object type resolved from baseLoc.
@@ -34,7 +32,6 @@
 	current := start
 
 	var chain deltaChain
-	chain.declaredSize = -1
 
 	for {
 		if _, ok := visited[current]; ok {
@@ -42,20 +39,9 @@
 		}
 		visited[current] = struct{}{}
 
-		pack, meta, err := store.entryMetaAt(current)
+		_, meta, err := store.entryMetaAt(current)
 		if err != nil {
 			return deltaChain{}, err
-		}
-		if chain.declaredSize < 0 {
-			if packfmt.IsBaseObjectType(meta.ty) {
-				chain.declaredSize = meta.size
-			} else {
-				declaredSize, err := deltaDeclaredSizeAt(pack, meta.dataOffset)
-				if err != nil {
-					return deltaChain{}, err
-				}
-				chain.declaredSize = declaredSize
-			}
 		}
 
 		if packfmt.IsBaseObjectType(meta.ty) {
--- a/objectstore/packed/entry_parse.go
+++ b/objectstore/packed/entry_parse.go
@@ -56,6 +56,12 @@
 			return zero, fmt.Errorf("objectstore/packed: pack %q has invalid ofs-delta base", pack.name)
 		}
 		meta.baseOfs = offset - entry.OfsBaseDistance
+	case objecttype.TypeCommit, objecttype.TypeTree, objecttype.TypeBlob, objecttype.TypeTag:
+		// Base object types do not have delta base metadata.
+	case objecttype.TypeInvalid, objecttype.TypeFuture:
+		return zero, fmt.Errorf("objectstore/packed: pack %q has unsupported entry type %d", pack.name, meta.ty)
+	default:
+		return zero, fmt.Errorf("objectstore/packed: pack %q has unsupported entry type %d", pack.name, meta.ty)
 	}
 	return meta, nil
 }
--- a/objectstore/packed/read_header.go
+++ b/objectstore/packed/read_header.go
@@ -11,9 +11,5 @@
 	if err != nil {
 		return objecttype.TypeInvalid, 0, err
 	}
-	chain, err := store.deltaBuildChain(loc)
-	if err != nil {
-		return objecttype.TypeInvalid, 0, err
-	}
-	return chain.baseType, chain.declaredSize, nil
+	return store.resolveHeaderAt(loc)
 }
--- /dev/null
+++ b/objectstore/packed/read_header_resolve.go
@@ -1,0 +1,61 @@
+package packed
+
+import (
+	"fmt"
+
+	packfmt "codeberg.org/lindenii/furgit/format/pack"
+	"codeberg.org/lindenii/furgit/objecttype"
+)
+
+// resolveHeaderAt resolves one object's canonical type and declared content size.
+func (store *Store) resolveHeaderAt(start location) (objecttype.Type, int64, error) {
+	visited := make(map[location]struct{})
+	current := start
+	declaredSize := int64(-1)
+
+	for {
+		if _, ok := visited[current]; ok {
+			return objecttype.TypeInvalid, 0, fmt.Errorf("objectstore/packed: delta cycle while resolving object header")
+		}
+		visited[current] = struct{}{}
+
+		pack, meta, err := store.entryMetaAt(current)
+		if err != nil {
+			return objecttype.TypeInvalid, 0, err
+		}
+		if declaredSize < 0 {
+			if packfmt.IsBaseObjectType(meta.ty) {
+				declaredSize = meta.size
+			} else {
+				size, err := deltaDeclaredSizeAt(pack, meta.dataOffset)
+				if err != nil {
+					return objecttype.TypeInvalid, 0, err
+				}
+				declaredSize = size
+			}
+		}
+		if packfmt.IsBaseObjectType(meta.ty) {
+			return meta.ty, declaredSize, nil
+		}
+
+		switch meta.ty {
+		case objecttype.TypeRefDelta:
+			next, err := store.lookup(meta.baseRefID)
+			if err != nil {
+				return objecttype.TypeInvalid, 0, err
+			}
+			current = next
+		case objecttype.TypeOfsDelta:
+			current = location{
+				packName: current.packName,
+				offset:   meta.baseOfs,
+			}
+		case objecttype.TypeCommit, objecttype.TypeTree, objecttype.TypeBlob, objecttype.TypeTag:
+			return objecttype.TypeInvalid, 0, fmt.Errorf("objectstore/packed: internal invariant violation for base type %d", meta.ty)
+		case objecttype.TypeInvalid, objecttype.TypeFuture:
+			return objecttype.TypeInvalid, 0, fmt.Errorf("objectstore/packed: unsupported pack type %d", meta.ty)
+		default:
+			return objecttype.TypeInvalid, 0, fmt.Errorf("objectstore/packed: unsupported pack type %d", meta.ty)
+		}
+	}
+}
--