ref: 7eaa8614c897a97d241335982f4c04f1f27b0715
parent: 3a4b0c924197ba6d9570c8ac338c32b8093398da
author: Runxi Yu <me@runxiyu.org>
date: Sun Nov 23 03:00:00 EST 2025
bufpool: Return bytes.Buffer, rather than a pointer to it It's silly to allocate a bytes.Buffer struct, however small it is, every time Borrow is called, since the entire purpose is to reduce allocations.
--- a/internal/bufpool/buffers.go
+++ b/internal/bufpool/buffers.go
@@ -23,10 +23,11 @@
// pooled buffer, the caller should invoke Release() to return it to the pool.
//
// Buffers must not be copied after first use; doing so can cause double-returns
-// to the pool and data races. A zero-value Buffer is not valid for use. Use
-// the pointer type (*Buffer) returned by Borrow/FromOwned to avoid accidental
-// copies.
+// to the pool and data races.
+//
+//go:nocopy
type Buffer struct {+ _ struct{} // for nocopybuf []byte
pool poolIndex
}
@@ -69,7 +70,7 @@
// unpooled buffer is allocated.
//
// The caller must call Release() when finished using the returned Buffer.
-func Borrow(capHint int) *Buffer {+func Borrow(capHint int) Buffer { if capHint < DefaultBufferCap {capHint = DefaultBufferCap
}
@@ -76,7 +77,7 @@
classIdx, classCap, pooled := classFor(capHint)
if !pooled {newBuf := make([]byte, 0, capHint)
- return &Buffer{buf: newBuf, pool: unpooled}+ return Buffer{buf: newBuf, pool: unpooled}}
buf := bufferPools[classIdx].Get().(*[]byte)
if cap(*buf) < classCap {@@ -83,14 +84,14 @@
*buf = make([]byte, 0, classCap)
}
slice := (*buf)[:0]
- return &Buffer{buf: slice, pool: poolIndex(classIdx)}+ return Buffer{buf: slice, pool: poolIndex(classIdx)}}
// FromOwned constructs a Buffer from a caller-owned byte slice. The resulting
// Buffer does not participate in pooling and will never be returned to the
// internal pool when released.
-func FromOwned(buf []byte) *Buffer {- return &Buffer{buf: buf, pool: unpooled}+func FromOwned(buf []byte) Buffer {+ return Buffer{buf: buf, pool: unpooled}}
// Resize adjusts the length of the buffer to n bytes. If n exceeds the current
--- a/internal/flatex/decompress_bytes.go
+++ b/internal/flatex/decompress_bytes.go
@@ -21,16 +21,16 @@
},
}
-func Decompress(src []byte) (*bufpool.Buffer, int, error) {+func Decompress(src []byte) (bufpool.Buffer, int, error) {return DecompressSized(src, 0)
}
-func DecompressSized(src []byte, sizeHint int) (*bufpool.Buffer, int, error) {+func DecompressSized(src []byte, sizeHint int) (bufpool.Buffer, int, error) {d := bufferDecompressorPool.Get().(*bufferDecompressor)
defer bufferDecompressorPool.Put(d)
if err := d.inflater.reset(src); err != nil {- return nil, 0, err
+ return bufpool.Buffer{}, 0, err}
out := bufpool.Borrow(sizeHint)
@@ -47,7 +47,7 @@
return out, d.inflater.pos, nil
}
out.Release()
- return nil, 0, d.inflater.err
+ return bufpool.Buffer{}, 0, d.inflater.err}
d.inflater.step(&d.inflater)
if d.inflater.err != nil && len(d.inflater.toRead) == 0 {--- a/internal/zlibx/decompress.go
+++ b/internal/zlibx/decompress.go
@@ -9,45 +9,45 @@
"git.sr.ht/~runxiyu/furgit/internal/flatex"
)
-func Decompress(src []byte) (*bufpool.Buffer, error) {+func Decompress(src []byte) (bufpool.Buffer, error) {return DecompressSized(src, 0)
}
-func DecompressSized(src []byte, sizeHint int) (*bufpool.Buffer, error) {+func DecompressSized(src []byte, sizeHint int) (bufpool.Buffer, error) { if len(src) < 6 {- return nil, io.ErrUnexpectedEOF
+ return bufpool.Buffer{}, io.ErrUnexpectedEOF}
cmf := src[0]
flg := src[1]
if (cmf&0x0f != zlibDeflate) || (cmf>>4 > zlibMaxWindow) || (binary.BigEndian.Uint16(src[:2])%31 != 0) {- return nil, ErrHeader
+ return bufpool.Buffer{}, ErrHeader}
offset := 2
if flg&0x20 != 0 {- return nil, ErrHeader
+ return bufpool.Buffer{}, ErrHeader}
if len(src[offset:]) < 4 {- return nil, io.ErrUnexpectedEOF
+ return bufpool.Buffer{}, io.ErrUnexpectedEOF}
deflateData := src[offset:]
out, consumed, err := flatex.DecompressSized(deflateData, sizeHint)
if err != nil {- return nil, err
+ return bufpool.Buffer{}, err}
checksumPos := offset + consumed
if checksumPos+4 > len(src) {out.Release()
- return nil, io.ErrUnexpectedEOF
+ return bufpool.Buffer{}, io.ErrUnexpectedEOF}
expected := binary.BigEndian.Uint32(src[checksumPos : checksumPos+4])
if expected != adler32.Checksum(out.Bytes()) {out.Release()
- return nil, ErrChecksum
+ return bufpool.Buffer{}, ErrChecksum}
return out, nil
}
--- a/loose.go
+++ b/loose.go
@@ -24,37 +24,37 @@
return filepath.Join("objects", hex[:2], hex[2:]), nil}
-func (repo *Repository) looseRead(id Hash) (ObjectType, *bufpool.Buffer, error) {+func (repo *Repository) looseRead(id Hash) (ObjectType, bufpool.Buffer, error) {ty, body, err := repo.looseReadTyped(id)
if err != nil {- return ObjectTypeInvalid, nil, err
+ return ObjectTypeInvalid, bufpool.Buffer{}, err}
return ty, body, nil
}
-func (repo *Repository) looseReadTyped(id Hash) (ObjectType, *bufpool.Buffer, error) {+func (repo *Repository) looseReadTyped(id Hash) (ObjectType, bufpool.Buffer, error) {path, err := repo.loosePath(id)
if err != nil {- return ObjectTypeInvalid, nil, err
+ return ObjectTypeInvalid, bufpool.Buffer{}, err}
path = repo.repoPath(path)
f, err := os.Open(path)
if err != nil { if os.IsNotExist(err) {- return ObjectTypeInvalid, nil, ErrNotFound
+ return ObjectTypeInvalid, bufpool.Buffer{}, ErrNotFound}
- return ObjectTypeInvalid, nil, err
+ return ObjectTypeInvalid, bufpool.Buffer{}, err}
defer func() { _ = f.Close() }()compressed, err := io.ReadAll(f)
if err != nil {- return ObjectTypeInvalid, nil, err
+ return ObjectTypeInvalid, bufpool.Buffer{}, err}
raw, err := zlibx.Decompress(compressed)
if err != nil {- return ObjectTypeInvalid, nil, err
+ return ObjectTypeInvalid, bufpool.Buffer{}, err}
rawBytes := raw.Bytes()
@@ -61,7 +61,7 @@
nul := bytes.IndexByte(rawBytes, 0)
if nul < 0 {raw.Release()
- return ObjectTypeInvalid, nil, ErrInvalidObject
+ return ObjectTypeInvalid, bufpool.Buffer{}, ErrInvalidObject}
header := rawBytes[:nul]
@@ -70,17 +70,17 @@
ty, declaredSize, err := parseLooseHeader(header)
if err != nil {raw.Release()
- return ObjectTypeInvalid, nil, err
+ return ObjectTypeInvalid, bufpool.Buffer{}, err}
if declaredSize != int64(len(body)) {raw.Release()
- return ObjectTypeInvalid, nil, ErrInvalidObject
+ return ObjectTypeInvalid, bufpool.Buffer{}, ErrInvalidObject}
copy(rawBytes, body)
raw.Resize(len(body))
// if !repo.verifyRawObject(raw, id) {- // return ObjectTypeInvalid, nil, ErrInvalidObject
+ // return ObjectTypeInvalid, bufpool.Buffer{}, ErrInvalidObject// }
return ty, raw, nil
--- a/pack_pack.go
+++ b/pack_pack.go
@@ -22,10 +22,10 @@
Offset uint64
}
-func (repo *Repository) packRead(id Hash) (ObjectType, *bufpool.Buffer, error) {+func (repo *Repository) packRead(id Hash) (ObjectType, bufpool.Buffer, error) {loc, err := repo.packIndexFind(id)
if err != nil {- return ObjectTypeInvalid, nil, err
+ return ObjectTypeInvalid, bufpool.Buffer{}, err}
return repo.packReadAt(loc, id)
}
@@ -48,22 +48,22 @@
return packlocation{}, ErrNotFound}
-func (repo *Repository) packReadAt(loc packlocation, want Hash) (ObjectType, *bufpool.Buffer, error) {+func (repo *Repository) packReadAt(loc packlocation, want Hash) (ObjectType, bufpool.Buffer, error) {ty, body, err := repo.packBodyResolveAtLocation(loc)
if err != nil {- return ObjectTypeInvalid, nil, err
+ return ObjectTypeInvalid, bufpool.Buffer{}, err}
// if !repo.verifyTypedObject(ty, body.Bytes(), want) {// body.Release()
- // return ObjectTypeInvalid, nil, ErrInvalidObject
+ // return ObjectTypeInvalid, bufpool.Buffer{}, ErrInvalidObject// }
return ty, body, nil
}
-func (repo *Repository) packBodyResolveAtLocation(loc packlocation) (ObjectType, *bufpool.Buffer, error) {+func (repo *Repository) packBodyResolveAtLocation(loc packlocation) (ObjectType, bufpool.Buffer, error) {pf, err := repo.packFile(loc.PackPath)
if err != nil {- return ObjectTypeInvalid, nil, err
+ return ObjectTypeInvalid, bufpool.Buffer{}, err}
return repo.packBodyResolveWithin(pf, loc.Offset)
}
@@ -97,17 +97,17 @@
return ty, size, consumed, nil
}
-func packSectionInflate(pf *packFile, start uint64, sizeHint int) (*bufpool.Buffer, error) {+func packSectionInflate(pf *packFile, start uint64, sizeHint int) (bufpool.Buffer, error) { if start > uint64(len(pf.data)) {- return nil, ErrInvalidObject
+ return bufpool.Buffer{}, ErrInvalidObject}
body, err := zlibx.DecompressSized(pf.data[start:], sizeHint)
if err != nil {- return nil, err
+ return bufpool.Buffer{}, err}
if sizeHint > 0 && len(body.Bytes()) != sizeHint {body.Release()
- return nil, ErrInvalidObject
+ return bufpool.Buffer{}, ErrInvalidObject}
return body, nil
}
@@ -225,13 +225,13 @@
}
}
-func (repo *Repository) packBodyResolveWithin(pf *packFile, ofs uint64) (ObjectType, *bufpool.Buffer, error) {+func (repo *Repository) packBodyResolveWithin(pf *packFile, ofs uint64) (ObjectType, bufpool.Buffer, error) { if pf == nil {- return ObjectTypeInvalid, nil, ErrInvalidObject
+ return ObjectTypeInvalid, bufpool.Buffer{}, ErrInvalidObject}
type deltaFrame struct {- delta *bufpool.Buffer
+ delta bufpool.Buffer
}
var frames []deltaFrame
defer func() {@@ -241,16 +241,16 @@
}()
var (
- body *bufpool.Buffer
+ body bufpool.Buffer
bodyReady bool
resultTy ObjectType
)
- fail := func(err error) (ObjectType, *bufpool.Buffer, error) {+ fail := func(err error) (ObjectType, bufpool.Buffer, error) { if bodyReady {body.Release()
bodyReady = false
}
- return ObjectTypeInvalid, nil, err
+ return ObjectTypeInvalid, bufpool.Buffer{}, err}
resolved := false
@@ -348,20 +348,20 @@
return resultTy, body, nil
}
-func packDeltaApply(base, delta *bufpool.Buffer) (*bufpool.Buffer, error) {+func packDeltaApply(base, delta bufpool.Buffer) (bufpool.Buffer, error) {pos := 0
baseBytes := base.Bytes()
deltaBytes := delta.Bytes()
srcSize, err := packVarintRead(deltaBytes, &pos)
if err != nil {- return nil, err
+ return bufpool.Buffer{}, err}
dstSize, err := packVarintRead(deltaBytes, &pos)
if err != nil {- return nil, err
+ return bufpool.Buffer{}, err}
if srcSize != len(baseBytes) {- return nil, ErrInvalidObject
+ return bufpool.Buffer{}, ErrInvalidObject}
out := bufpool.Borrow(dstSize)
out.Resize(dstSize)
@@ -378,7 +378,7 @@
if op&0x01 != 0 { if pos >= len(deltaBytes) {out.Release()
- return nil, ErrInvalidObject
+ return bufpool.Buffer{}, ErrInvalidObject}
off |= int(deltaBytes[pos])
pos++
@@ -386,7 +386,7 @@
if op&0x02 != 0 { if pos >= len(deltaBytes) {out.Release()
- return nil, ErrInvalidObject
+ return bufpool.Buffer{}, ErrInvalidObject}
off |= int(deltaBytes[pos]) << 8
pos++
@@ -394,7 +394,7 @@
if op&0x04 != 0 { if pos >= len(deltaBytes) {out.Release()
- return nil, ErrInvalidObject
+ return bufpool.Buffer{}, ErrInvalidObject}
off |= int(deltaBytes[pos]) << 16
pos++
@@ -402,7 +402,7 @@
if op&0x08 != 0 { if pos >= len(deltaBytes) {out.Release()
- return nil, ErrInvalidObject
+ return bufpool.Buffer{}, ErrInvalidObject}
off |= int(deltaBytes[pos]) << 24
pos++
@@ -410,7 +410,7 @@
if op&0x10 != 0 { if pos >= len(deltaBytes) {out.Release()
- return nil, ErrInvalidObject
+ return bufpool.Buffer{}, ErrInvalidObject}
n |= int(deltaBytes[pos])
pos++
@@ -418,7 +418,7 @@
if op&0x20 != 0 { if pos >= len(deltaBytes) {out.Release()
- return nil, ErrInvalidObject
+ return bufpool.Buffer{}, ErrInvalidObject}
n |= int(deltaBytes[pos]) << 8
pos++
@@ -426,7 +426,7 @@
if op&0x40 != 0 { if pos >= len(deltaBytes) {out.Release()
- return nil, ErrInvalidObject
+ return bufpool.Buffer{}, ErrInvalidObject}
n |= int(deltaBytes[pos]) << 16
pos++
@@ -436,7 +436,7 @@
}
if off+n > len(baseBytes) || outPos+n > len(outBytes) {out.Release()
- return nil, ErrInvalidObject
+ return bufpool.Buffer{}, ErrInvalidObject}
copy(outBytes[outPos:], baseBytes[off:off+n])
outPos += n
@@ -444,7 +444,7 @@
n := int(op)
if pos+n > len(deltaBytes) || outPos+n > len(outBytes) {out.Release()
- return nil, ErrInvalidObject
+ return bufpool.Buffer{}, ErrInvalidObject}
copy(outBytes[outPos:], deltaBytes[pos:pos+n])
pos += n
@@ -451,13 +451,13 @@
outPos += n
default:
out.Release()
- return nil, ErrInvalidObject
+ return bufpool.Buffer{}, ErrInvalidObject}
}
if outPos != len(outBytes) {out.Release()
- return nil, ErrInvalidObject
+ return bufpool.Buffer{}, ErrInvalidObject}
return out, nil
}
--
⑨