aboutsummaryrefslogtreecommitdiff
path: root/pack_pack.go
diff options
context:
space:
mode:
authorGravatar Runxi Yu2025-11-14 00:00:00 +0000
committerGravatar Runxi Yu2025-11-14 00:00:00 +0000
commit9ef659a016d4ffeac931291984a4c71f9527a747 (patch)
tree957a76630fe248b638c0a9c84f7acef40a7ee9f5 /pack_pack.go
parentInitial commit (diff)
signature
Read types and sizes without inflating entire object
Diffstat (limited to 'pack_pack.go')
-rw-r--r--pack_pack.go83
1 files changed, 83 insertions, 0 deletions
diff --git a/pack_pack.go b/pack_pack.go
index 20974669..ee4d2b7a 100644
--- a/pack_pack.go
+++ b/pack_pack.go
@@ -73,6 +73,25 @@ func (repo *Repository) packBodyResolveAtLocation(loc PackLocation) (ObjType, bo
return repo.packBodyResolveWithin(pf, loc.Offset)
}
+func (repo *Repository) packTypeSizeAtLocation(loc PackLocation, seen map[packKey]struct{}) (ObjType, int64, error) {
+ pf, err := repo.packFile(loc.PackPath)
+ if err != nil {
+ return ObjInvalid, 0, err
+ }
+ return repo.packTypeSizeWithin(pf, loc.Offset, seen)
+}
+
+func (repo *Repository) packTypeSizeByID(id Hash, seen map[packKey]struct{}) (ObjType, int64, error) {
+ loc, err := repo.packIndexFind(id)
+ if err == nil {
+ return repo.packTypeSizeAtLocation(loc, seen)
+ }
+ if !errors.Is(err, ErrNotFound) {
+ return ObjInvalid, 0, err
+ }
+ return repo.looseTypeSize(id)
+}
+
func packHeaderRead(r io.Reader) (ObjType, int, error) {
var b [1]byte
_, err := io.ReadFull(r, b[:])
@@ -203,6 +222,70 @@ func (repo *Repository) packBodyResolveByID(id Hash) (ObjType, borrowedBody, err
return ty, borrowedFromOwned(body), nil
}
+type packKey struct {
+ path string
+ ofs uint64
+}
+
+func (repo *Repository) packTypeSizeWithin(pf *packFile, ofs uint64, seen map[packKey]struct{}) (ObjType, int64, error) {
+ if pf == nil {
+ return ObjInvalid, 0, ErrInvalidObject
+ }
+ if seen == nil {
+ seen = make(map[packKey]struct{})
+ }
+ key := packKey{path: pf.relPath, ofs: ofs}
+ if _, dup := seen[key]; dup {
+ return ObjInvalid, 0, ErrInvalidObject
+ }
+ seen[key] = struct{}{}
+ defer delete(seen, key)
+
+ r, err := pf.cursor(ofs)
+ if err != nil {
+ return ObjInvalid, 0, err
+ }
+ ty, size, err := packHeaderRead(r)
+ if err != nil {
+ return ObjInvalid, 0, err
+ }
+ declaredSize := int64(size)
+
+ switch ty {
+ case ObjCommit, ObjTree, ObjBlob, ObjTag:
+ return ty, declaredSize, nil
+ case ObjRefDelta:
+ var base Hash
+ _, err := io.ReadFull(r, base[:])
+ if err != nil {
+ return ObjInvalid, 0, err
+ }
+ baseTy, _, err := repo.packTypeSizeByID(base, seen)
+ if err != nil {
+ return ObjInvalid, 0, err
+ }
+ return baseTy, declaredSize, nil
+ case ObjOfsDelta:
+ dist, err := packDeltaReadOfsDistance(r)
+ if err != nil {
+ return ObjInvalid, 0, err
+ }
+ if ofs <= dist {
+ return ObjInvalid, 0, ErrInvalidObject
+ }
+ baseOfs := ofs - dist
+ baseTy, _, err := repo.packTypeSizeWithin(pf, baseOfs, seen)
+ if err != nil {
+ return ObjInvalid, 0, err
+ }
+ return baseTy, declaredSize, nil
+ case ObjInvalid, ObjFuture:
+ return ObjInvalid, 0, ErrInvalidObject
+ default:
+ return ObjInvalid, 0, ErrInvalidObject
+ }
+}
+
func (repo *Repository) packBodyResolveWithin(pf *packFile, ofs uint64) (ObjType, borrowedBody, error) {
r, err := pf.cursor(ofs)
if err != nil {