diff options
| author | 2025-11-22 08:00:00 +0800 | |
|---|---|---|
| committer | 2025-11-22 08:00:00 +0800 | |
| commit | a504524ca695f04692f5a2cf49952ed039a6a348 (patch) | |
| tree | 4d0ab8463f1bd9be22aab2af7b7d38f88ef24c77 | |
| parent | bufpool: Improve perf by using buckets of different size classes (diff) | |
| signature | No signature | |
bufpool: Return *Buffer
| -rw-r--r-- | internal/bufpool/buffers.go | 22 | ||||
| -rw-r--r-- | internal/flatex/decompress_bytes.go | 10 | ||||
| -rw-r--r-- | internal/zlibx/decompress.go | 26 | ||||
| -rw-r--r-- | loose.go | 18 | ||||
| -rw-r--r-- | pack_pack.go | 54 |
5 files changed, 62 insertions, 68 deletions
diff --git a/internal/bufpool/buffers.go b/internal/bufpool/buffers.go index 24b29fb5..27c017bd 100644 --- a/internal/bufpool/buffers.go +++ b/internal/bufpool/buffers.go @@ -23,11 +23,10 @@ const ( // 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. -// -//go:nocopy +// 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. type Buffer struct { - _ noCopy buf []byte pool poolIndex } @@ -70,28 +69,28 @@ var bufferPools = func() []sync.Pool { // 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 } 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 { *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 @@ -187,8 +186,3 @@ func (buf *Buffer) returnToPool() { tmp := buf.buf[:0] bufferPools[int(buf.pool)].Put(&tmp) } - -type noCopy struct{} - -func (*noCopy) Lock() {} -func (*noCopy) Unlock() {} diff --git a/internal/flatex/decompress_bytes.go b/internal/flatex/decompress_bytes.go index 5e29f82d..ce6d0558 100644 --- a/internal/flatex/decompress_bytes.go +++ b/internal/flatex/decompress_bytes.go @@ -25,26 +25,26 @@ var bufferDecompressorPool = sync.Pool{ // Decompress inflates the provided DEFLATE stream and returns the full output // in a pooled bufpool.Buffer along with the number of consumed bytes from src. -func Decompress(src []byte) (bufpool.Buffer, int, error) { +func Decompress(src []byte) (*bufpool.Buffer, int, error) { return DecompressDictSized(src, nil, 0) } // DecompressDict inflates the provided DEFLATE stream using dict as the preset // dictionary and returns the full output in a pooled bufpool.Buffer. The second // returned value reports how many bytes of src were consumed. -func DecompressDict(src []byte, dict []byte) (bufpool.Buffer, int, error) { +func DecompressDict(src []byte, dict []byte) (*bufpool.Buffer, int, error) { return DecompressDictSized(src, dict, 0) } // DecompressDictSized is like DecompressDict but allows providing an expected // output size to pre-size the destination buffer and avoid repeated growth. // A non-positive sizeHint falls back to the default buffer capacity. -func DecompressDictSized(src []byte, dict []byte, sizeHint int) (bufpool.Buffer, int, error) { +func DecompressDictSized(src []byte, dict []byte, sizeHint int) (*bufpool.Buffer, int, error) { d := bufferDecompressorPool.Get().(*bufferDecompressor) defer bufferDecompressorPool.Put(d) if err := d.inflater.reset(src, dict); err != nil { - return bufpool.Buffer{}, 0, err + return nil, 0, err } out := bufpool.Borrow(sizeHint) @@ -61,7 +61,7 @@ func DecompressDictSized(src []byte, dict []byte, sizeHint int) (bufpool.Buffer, return out, d.inflater.pos, nil } out.Release() - return bufpool.Buffer{}, 0, d.inflater.err + return nil, 0, d.inflater.err } d.inflater.step(&d.inflater) if d.inflater.err != nil && len(d.inflater.toRead) == 0 { diff --git a/internal/zlibx/decompress.go b/internal/zlibx/decompress.go index c6eb65e5..68a92587 100644 --- a/internal/zlibx/decompress.go +++ b/internal/zlibx/decompress.go @@ -11,71 +11,71 @@ import ( // Decompress inflates the provided zlib wrapped stream and returns the // uncompressed data inside a pooled bufpool.Buffer. -func Decompress(src []byte) (bufpool.Buffer, error) { +func Decompress(src []byte) (*bufpool.Buffer, error) { return DecompressSized(src, 0) } // DecompressSized inflates the provided zlib stream, using sizeHint to // preallocate the output buffer when known (e.g. packfile entries). -func DecompressSized(src []byte, sizeHint int) (bufpool.Buffer, error) { +func DecompressSized(src []byte, sizeHint int) (*bufpool.Buffer, error) { return DecompressDictSized(src, nil, sizeHint) } // DecompressDict is like Decompress but accepts a preset dictionary. The // dictionary must match the checksum embedded in the stream if the dictionary // flag is present. -func DecompressDict(src []byte, dict []byte) (bufpool.Buffer, error) { +func DecompressDict(src []byte, dict []byte) (*bufpool.Buffer, error) { return DecompressDictSized(src, dict, 0) } // DecompressDictSized is like DecompressDict but allows providing an expected // uncompressed size to avoid buffer growth copies. -func DecompressDictSized(src []byte, dict []byte, sizeHint int) (bufpool.Buffer, error) { +func DecompressDictSized(src []byte, dict []byte, sizeHint int) (*bufpool.Buffer, error) { if len(src) < 6 { - return bufpool.Buffer{}, io.ErrUnexpectedEOF + return nil, io.ErrUnexpectedEOF } cmf := src[0] flg := src[1] if (cmf&0x0f != zlibDeflate) || (cmf>>4 > zlibMaxWindow) || (binary.BigEndian.Uint16(src[:2])%31 != 0) { - return bufpool.Buffer{}, ErrHeader + return nil, ErrHeader } offset := 2 haveDict := flg&0x20 != 0 if haveDict { if len(src) < offset+4 { - return bufpool.Buffer{}, io.ErrUnexpectedEOF + return nil, io.ErrUnexpectedEOF } if dict == nil { - return bufpool.Buffer{}, ErrDictionary + return nil, ErrDictionary } checksum := binary.BigEndian.Uint32(src[offset : offset+4]) if checksum != adler32.Checksum(dict) { - return bufpool.Buffer{}, ErrDictionary + return nil, ErrDictionary } offset += 4 } if len(src[offset:]) < 4 { - return bufpool.Buffer{}, io.ErrUnexpectedEOF + return nil, io.ErrUnexpectedEOF } deflateData := src[offset:] out, consumed, err := flatex.DecompressDictSized(deflateData, dict, sizeHint) if err != nil { - return bufpool.Buffer{}, err + return nil, err } checksumPos := offset + consumed if checksumPos+4 > len(src) { out.Release() - return bufpool.Buffer{}, io.ErrUnexpectedEOF + return nil, io.ErrUnexpectedEOF } expected := binary.BigEndian.Uint32(src[checksumPos : checksumPos+4]) if expected != adler32.Checksum(out.Bytes()) { out.Release() - return bufpool.Buffer{}, ErrChecksum + return nil, ErrChecksum } return out, nil } @@ -34,36 +34,36 @@ func (repo *Repository) looseRead(id Hash) (StoredObject, error) { return obj, err } -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, bufpool.Buffer{}, err + return ObjectTypeInvalid, nil, err } path = repo.repoPath(path) f, err := os.Open(path) if err != nil { if os.IsNotExist(err) { - return ObjectTypeInvalid, bufpool.Buffer{}, ErrNotFound + return ObjectTypeInvalid, nil, ErrNotFound } - return ObjectTypeInvalid, bufpool.Buffer{}, err + return ObjectTypeInvalid, nil, err } defer func() { _ = f.Close() }() compressed, err := io.ReadAll(f) if err != nil { - return ObjectTypeInvalid, bufpool.Buffer{}, err + return ObjectTypeInvalid, nil, err } raw, err := zlibx.Decompress(compressed) if err != nil { - return ObjectTypeInvalid, bufpool.Buffer{}, err + return ObjectTypeInvalid, nil, err } rawBytes := raw.Bytes() nul := bytes.IndexByte(rawBytes, 0) if nul < 0 { raw.Release() - return ObjectTypeInvalid, bufpool.Buffer{}, ErrInvalidObject + return ObjectTypeInvalid, nil, ErrInvalidObject } header := rawBytes[:nul] @@ -72,11 +72,11 @@ func (repo *Repository) looseReadTyped(id Hash) (ObjectType, bufpool.Buffer, err ty, declaredSize, err := parseLooseHeader(header) if err != nil { raw.Release() - return ObjectTypeInvalid, bufpool.Buffer{}, err + return ObjectTypeInvalid, nil, err } if declaredSize != int64(len(body)) { raw.Release() - return ObjectTypeInvalid, bufpool.Buffer{}, ErrInvalidObject + return ObjectTypeInvalid, nil, ErrInvalidObject } copy(rawBytes, body) diff --git a/pack_pack.go b/pack_pack.go index 6dcfb5d9..99f885a1 100644 --- a/pack_pack.go +++ b/pack_pack.go @@ -63,10 +63,10 @@ func (repo *Repository) packReadAt(loc packlocation, want Hash) (StoredObject, e return obj, err } -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, bufpool.Buffer{}, err + return ObjectTypeInvalid, nil, err } return repo.packBodyResolveWithin(pf, loc.Offset) } @@ -100,17 +100,17 @@ func packHeaderParse(data []byte) (ObjectType, int, int, error) { 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 bufpool.Buffer{}, ErrInvalidObject + return nil, ErrInvalidObject } body, err := zlibx.DecompressSized(pf.data[start:], sizeHint) if err != nil { - return bufpool.Buffer{}, err + return nil, err } if sizeHint > 0 && len(body.Bytes()) != sizeHint { body.Release() - return bufpool.Buffer{}, ErrInvalidObject + return nil, ErrInvalidObject } return body, nil } @@ -228,13 +228,13 @@ func (repo *Repository) packTypeSizeWithin(pf *packFile, ofs uint64, seen map[pa } } -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, bufpool.Buffer{}, ErrInvalidObject + return ObjectTypeInvalid, nil, ErrInvalidObject } type deltaFrame struct { - delta bufpool.Buffer + delta *bufpool.Buffer } var frames []deltaFrame defer func() { @@ -244,16 +244,16 @@ func (repo *Repository) packBodyResolveWithin(pf *packFile, ofs uint64) (ObjectT }() 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, bufpool.Buffer{}, err + return ObjectTypeInvalid, nil, err } resolved := false @@ -351,20 +351,20 @@ func (repo *Repository) packBodyResolveWithin(pf *packFile, ofs uint64) (ObjectT 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 bufpool.Buffer{}, err + return nil, err } dstSize, err := packVarintRead(deltaBytes, &pos) if err != nil { - return bufpool.Buffer{}, err + return nil, err } if srcSize != len(baseBytes) { - return bufpool.Buffer{}, ErrInvalidObject + return nil, ErrInvalidObject } out := bufpool.Borrow(dstSize) out.Resize(dstSize) @@ -381,7 +381,7 @@ func packDeltaApply(base, delta bufpool.Buffer) (bufpool.Buffer, error) { if op&0x01 != 0 { if pos >= len(deltaBytes) { out.Release() - return bufpool.Buffer{}, ErrInvalidObject + return nil, ErrInvalidObject } off |= int(deltaBytes[pos]) pos++ @@ -389,7 +389,7 @@ func packDeltaApply(base, delta bufpool.Buffer) (bufpool.Buffer, error) { if op&0x02 != 0 { if pos >= len(deltaBytes) { out.Release() - return bufpool.Buffer{}, ErrInvalidObject + return nil, ErrInvalidObject } off |= int(deltaBytes[pos]) << 8 pos++ @@ -397,7 +397,7 @@ func packDeltaApply(base, delta bufpool.Buffer) (bufpool.Buffer, error) { if op&0x04 != 0 { if pos >= len(deltaBytes) { out.Release() - return bufpool.Buffer{}, ErrInvalidObject + return nil, ErrInvalidObject } off |= int(deltaBytes[pos]) << 16 pos++ @@ -405,7 +405,7 @@ func packDeltaApply(base, delta bufpool.Buffer) (bufpool.Buffer, error) { if op&0x08 != 0 { if pos >= len(deltaBytes) { out.Release() - return bufpool.Buffer{}, ErrInvalidObject + return nil, ErrInvalidObject } off |= int(deltaBytes[pos]) << 24 pos++ @@ -413,7 +413,7 @@ func packDeltaApply(base, delta bufpool.Buffer) (bufpool.Buffer, error) { if op&0x10 != 0 { if pos >= len(deltaBytes) { out.Release() - return bufpool.Buffer{}, ErrInvalidObject + return nil, ErrInvalidObject } n |= int(deltaBytes[pos]) pos++ @@ -421,7 +421,7 @@ func packDeltaApply(base, delta bufpool.Buffer) (bufpool.Buffer, error) { if op&0x20 != 0 { if pos >= len(deltaBytes) { out.Release() - return bufpool.Buffer{}, ErrInvalidObject + return nil, ErrInvalidObject } n |= int(deltaBytes[pos]) << 8 pos++ @@ -429,7 +429,7 @@ func packDeltaApply(base, delta bufpool.Buffer) (bufpool.Buffer, error) { if op&0x40 != 0 { if pos >= len(deltaBytes) { out.Release() - return bufpool.Buffer{}, ErrInvalidObject + return nil, ErrInvalidObject } n |= int(deltaBytes[pos]) << 16 pos++ @@ -439,7 +439,7 @@ func packDeltaApply(base, delta bufpool.Buffer) (bufpool.Buffer, error) { } if off+n > len(baseBytes) || outPos+n > len(outBytes) { out.Release() - return bufpool.Buffer{}, ErrInvalidObject + return nil, ErrInvalidObject } copy(outBytes[outPos:], baseBytes[off:off+n]) outPos += n @@ -447,20 +447,20 @@ func packDeltaApply(base, delta bufpool.Buffer) (bufpool.Buffer, error) { n := int(op) if pos+n > len(deltaBytes) || outPos+n > len(outBytes) { out.Release() - return bufpool.Buffer{}, ErrInvalidObject + return nil, ErrInvalidObject } copy(outBytes[outPos:], deltaBytes[pos:pos+n]) pos += n outPos += n default: out.Release() - return bufpool.Buffer{}, ErrInvalidObject + return nil, ErrInvalidObject } } if outPos != len(outBytes) { out.Release() - return bufpool.Buffer{}, ErrInvalidObject + return nil, ErrInvalidObject } return out, nil } |
