diff options
| author | 2025-11-14 00:00:00 +0000 | |
|---|---|---|
| committer | 2025-11-14 00:00:00 +0000 | |
| commit | 9ef659a016d4ffeac931291984a4c71f9527a747 (patch) | |
| tree | 957a76630fe248b638c0a9c84f7acef40a7ee9f5 /pack_pack.go | |
| parent | Initial commit (diff) | |
| signature | ||
Read types and sizes without inflating entire object
Diffstat (limited to 'pack_pack.go')
| -rw-r--r-- | pack_pack.go | 83 |
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 { |
