aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Runxi Yu2025-11-22 08:00:00 +0800
committerGravatar Runxi Yu2025-11-22 08:00:00 +0800
commit1e24deb7de0de8fc9720c0307811147cc1020083 (patch)
tree4a6dcd46f3af72630572db1c816060d523c3c857
parentdiffbytes: Fix linter error on redundant if statement (diff)
signatureNo signature
pack: Don't use bytes.Reader cursor, use mmaped slice directly
-rw-r--r--pack_pack.go140
1 files changed, 66 insertions, 74 deletions
diff --git a/pack_pack.go b/pack_pack.go
index 3913d580..1cd6b35b 100644
--- a/pack_pack.go
+++ b/pack_pack.go
@@ -1,12 +1,9 @@
package furgit
import (
- "bytes"
"encoding/binary"
"errors"
- "fmt"
"io"
- "math"
"os"
"sync"
"syscall"
@@ -82,35 +79,29 @@ func (repo *Repository) packTypeSizeAtLocation(loc packlocation, seen map[packKe
return repo.packTypeSizeWithin(pf, loc.Offset, seen)
}
-func packHeaderRead(r io.Reader) (ObjectType, int, error) {
- var b [1]byte
- _, err := io.ReadFull(r, b[:])
- if err != nil {
- return ObjectTypeInvalid, 0, err
+func packHeaderParse(data []byte) (ObjectType, int, int, error) {
+ if len(data) == 0 {
+ return ObjectTypeInvalid, 0, 0, io.ErrUnexpectedEOF
}
- ty := ObjectType((b[0] >> 4) & 0x07)
- size := int(b[0] & 0x0f)
+ b := data[0]
+ ty := ObjectType((b >> 4) & 0x07)
+ size := int(b & 0x0f)
shift := 4
- for (b[0] & 0x80) != 0 {
- _, err = io.ReadFull(r, b[:])
- if err != nil {
- return ObjectTypeInvalid, 0, err
+ consumed := 1
+ for (b & 0x80) != 0 {
+ if consumed >= len(data) {
+ return ObjectTypeInvalid, 0, 0, io.ErrUnexpectedEOF
}
- size |= int(b[0]&0x7f) << shift
+ b = data[consumed]
+ size |= int(b&0x7f) << shift
shift += 7
- if (b[0] & 0x80) == 0 {
- break
- }
+ consumed++
}
- return ty, size, nil
+ return ty, size, consumed, nil
}
-func packSectionInflate(pf *packFile, objectOfs uint64, r *bytes.Reader, sizeHint int) (bufpool.Buffer, error) {
- total := r.Size()
- remaining := int64(r.Len())
- consumed := total - remaining
- start := objectOfs + uint64(consumed)
- if int64(consumed) < 0 || start > uint64(len(pf.data)) {
+func packSectionInflate(pf *packFile, start uint64, sizeHint int) (bufpool.Buffer, error) {
+ if start > uint64(len(pf.data)) {
return bufpool.Buffer{}, ErrInvalidObject
}
body, err := zlibx.Decompress(pf.data[start:])
@@ -124,21 +115,22 @@ func packSectionInflate(pf *packFile, objectOfs uint64, r *bytes.Reader, sizeHin
return body, nil
}
-func packDeltaReadOfsDistance(r io.Reader) (uint64, error) {
- var b [1]byte
- _, err := io.ReadFull(r, b[:])
- if err != nil {
- return 0, err
+func packDeltaReadOfsDistance(data []byte) (uint64, int, error) {
+ if len(data) == 0 {
+ return 0, 0, io.ErrUnexpectedEOF
}
- dist := uint64(b[0] & 0x7f)
- for (b[0] & 0x80) != 0 {
- _, err = io.ReadFull(r, b[:])
- if err != nil {
- return 0, err
+ b := data[0]
+ dist := uint64(b & 0x7f)
+ consumed := 1
+ for (b & 0x80) != 0 {
+ if consumed >= len(data) {
+ return 0, 0, io.ErrUnexpectedEOF
}
- dist = ((dist + 1) << 7) + uint64(b[0]&0x7f)
+ b = data[consumed]
+ consumed++
+ dist = ((dist + 1) << 7) + uint64(b&0x7f)
}
- return dist, nil
+ return dist, consumed, nil
}
type packKey struct {
@@ -171,11 +163,10 @@ func (repo *Repository) packTypeSizeWithin(pf *packFile, ofs uint64, seen map[pa
seen[key] = struct{}{}
visited = append(visited, key)
- r, err := pf.cursor(ofs)
- if err != nil {
- return ObjectTypeInvalid, 0, err
+ if ofs >= uint64(len(pf.data)) {
+ return ObjectTypeInvalid, 0, ErrInvalidObject
}
- ty, size, err := packHeaderRead(r)
+ ty, size, consumed, err := packHeaderParse(pf.data[ofs:])
if err != nil {
return ObjectTypeInvalid, 0, err
}
@@ -184,15 +175,20 @@ func (repo *Repository) packTypeSizeWithin(pf *packFile, ofs uint64, seen map[pa
firstHeader = false
}
+ if uint64(consumed) > uint64(len(pf.data))-ofs {
+ return ObjectTypeInvalid, 0, io.ErrUnexpectedEOF
+ }
+ dataStart := ofs + uint64(consumed)
switch ty {
case ObjectTypeCommit, ObjectTypeTree, ObjectTypeBlob, ObjectTypeTag:
return ty, declaredSize, nil
case ObjectTypeRefDelta:
- var base Hash
- _, err := io.ReadFull(r, base.data[:repo.hashSize])
- if err != nil {
- return ObjectTypeInvalid, 0, err
+ hashEnd := dataStart + uint64(repo.hashSize)
+ if hashEnd > uint64(len(pf.data)) {
+ return ObjectTypeInvalid, 0, io.ErrUnexpectedEOF
}
+ var base Hash
+ copy(base.data[:], pf.data[dataStart:hashEnd])
base.size = repo.hashSize
loc, err := repo.packIndexFind(base)
if err == nil {
@@ -212,13 +208,17 @@ func (repo *Repository) packTypeSizeWithin(pf *packFile, ofs uint64, seen map[pa
}
return baseTy, declaredSize, nil
case ObjectTypeOfsDelta:
- dist, err := packDeltaReadOfsDistance(r)
+ dist, distConsumed, err := packDeltaReadOfsDistance(pf.data[dataStart:])
if err != nil {
return ObjectTypeInvalid, 0, err
}
if ofs <= dist {
return ObjectTypeInvalid, 0, ErrInvalidObject
}
+ dataStart += uint64(distConsumed)
+ if dataStart > uint64(len(pf.data)) {
+ return ObjectTypeInvalid, 0, ErrInvalidObject
+ }
ofs -= dist
case ObjectTypeInvalid, ObjectTypeFuture:
return ObjectTypeInvalid, 0, ErrInvalidObject
@@ -258,18 +258,21 @@ func (repo *Repository) packBodyResolveWithin(pf *packFile, ofs uint64) (ObjectT
resolved := false
for !resolved {
- r, err := pf.cursor(ofs)
- if err != nil {
- return fail(err)
+ if ofs >= uint64(len(pf.data)) {
+ return fail(ErrInvalidObject)
}
- ty, size, err := packHeaderRead(r)
+ ty, size, consumed, err := packHeaderParse(pf.data[ofs:])
if err != nil {
return fail(err)
}
+ if uint64(consumed) > uint64(len(pf.data))-ofs {
+ return fail(io.ErrUnexpectedEOF)
+ }
+ dataStart := ofs + uint64(consumed)
switch ty {
case ObjectTypeCommit, ObjectTypeTree, ObjectTypeBlob, ObjectTypeTag:
- body, err = packSectionInflate(pf, ofs, r, size)
+ body, err = packSectionInflate(pf, dataStart, size)
if err != nil {
return fail(err)
}
@@ -277,13 +280,14 @@ func (repo *Repository) packBodyResolveWithin(pf *packFile, ofs uint64) (ObjectT
resultTy = ty
resolved = true
case ObjectTypeRefDelta:
- var base Hash
- _, err := io.ReadFull(r, base.data[:repo.hashSize])
- if err != nil {
- return fail(err)
+ hashEnd := dataStart + uint64(repo.hashSize)
+ if hashEnd > uint64(len(pf.data)) {
+ return fail(io.ErrUnexpectedEOF)
}
+ var base Hash
+ copy(base.data[:], pf.data[dataStart:hashEnd])
base.size = repo.hashSize
- delta, err := packSectionInflate(pf, ofs, r, 0)
+ delta, err := packSectionInflate(pf, hashEnd, 0)
if err != nil {
return fail(err)
}
@@ -308,14 +312,18 @@ func (repo *Repository) packBodyResolveWithin(pf *packFile, ofs uint64) (ObjectT
bodyReady = true
resolved = true
case ObjectTypeOfsDelta:
- dist, err := packDeltaReadOfsDistance(r)
+ dist, distConsumed, err := packDeltaReadOfsDistance(pf.data[dataStart:])
if err != nil {
return fail(err)
}
if ofs <= dist {
return fail(ErrInvalidObject)
}
- delta, err := packSectionInflate(pf, ofs, r, 0)
+ deltaStart := dataStart + uint64(distConsumed)
+ if deltaStart > uint64(len(pf.data)) {
+ return fail(ErrInvalidObject)
+ }
+ delta, err := packSectionInflate(pf, deltaStart, 0)
if err != nil {
return fail(err)
}
@@ -562,22 +570,6 @@ func (pf *packFile) Close() error {
return closeErr
}
-func (pf *packFile) cursor(ofs uint64) (*bytes.Reader, error) {
- if pf == nil {
- return nil, ErrInvalidObject
- }
- if pf.size < 0 {
- return nil, ErrInvalidObject
- }
- if ofs > uint64(pf.size) {
- return nil, fmt.Errorf("furgit: pack: offset %d beyond %s", ofs, pf.relPath)
- }
- if ofs > uint64(math.MaxInt64) {
- return nil, fmt.Errorf("furgit: pack: offset %d too large", ofs)
- }
- return bytes.NewReader(pf.data[ofs:]), nil
-}
-
func (repo *Repository) packFile(rel string) (*packFile, error) {
repo.packFilesMu.RLock()
pf, ok := repo.packFiles[rel]