Fast implementation of Git in pure Go

internal/bufpool: Split files

runxiyu.tngl.sh 374ca215 5da80869

verified
+227 -214
+16
internal/bufpool/append.go
··· 1 + package bufpool 2 + 3 + // Append copies the provided bytes onto the end of the buffer, growing its 4 + // capacity if required. If src is empty, the method does nothing. 5 + // 6 + // The receiver retains ownership of the data; the caller may reuse src freely. 7 + func (buf *Buffer) Append(src []byte) { 8 + if len(src) == 0 { 9 + return 10 + } 11 + 12 + start := len(buf.buf) 13 + buf.ensureCapacity(start + len(src)) 14 + buf.buf = buf.buf[:start+len(src)] 15 + copy(buf.buf[start:], src) 16 + }
+31
internal/bufpool/borrow.go
··· 1 + package bufpool 2 + 3 + // Borrow retrieves a Buffer suitable for storing up to capHint bytes. 4 + // The returned Buffer may come from an internal sync.Pool. 5 + // 6 + // If capHint is smaller than DefaultBufferCap, it is automatically raised 7 + // to DefaultBufferCap. If no pooled buffer has sufficient capacity, a new 8 + // unpooled buffer is allocated. 9 + // 10 + // The caller must call Release() when finished using the returned Buffer. 11 + func Borrow(capHint int) Buffer { 12 + if capHint < DefaultBufferCap { 13 + capHint = DefaultBufferCap 14 + } 15 + 16 + classIdx, classCap, pooled := classFor(capHint) 17 + if !pooled { 18 + newBuf := make([]byte, 0, capHint) 19 + 20 + return Buffer{buf: newBuf, pool: unpooled} 21 + } 22 + //nolint:forcetypeassert 23 + buf := bufferPools[classIdx].Get().(*[]byte) 24 + if cap(*buf) < classCap { 25 + *buf = make([]byte, 0, classCap) 26 + } 27 + 28 + slice := (*buf)[:0] 29 + 30 + return Buffer{buf: slice, pool: poolIndex(classIdx)} //#nosec G115 31 + }
+24
internal/bufpool/buffer.go
··· 1 + package bufpool 2 + 3 + // Buffer is a growable byte container that optionally participates in a 4 + // memory pool. A Buffer may be obtained through Borrow() or constructed 5 + // directly from owned data via FromOwned(). 6 + // 7 + // A Buffer's underlying slice may grow as needed. When finished with a 8 + // pooled buffer, the caller should invoke Release() to return it to the pool. 9 + // 10 + // Buffers must not be copied after first use; doing so can cause double-returns 11 + // to the pool and data races. 12 + // 13 + // In general, pass Buffer around when used internally, and directly .Bytes() when 14 + // returning output across our API boundary. It is neither necessary nor efficient 15 + // to copy/append the .Bytes() to a newly-allocated slice; in cases where we do 16 + // want the raw byte slice out of our API boundary, it is perfectly acceptable to 17 + // simply not call Release(). 18 + // 19 + //go:nocopy 20 + type Buffer struct { 21 + _ struct{} // for nocopy 22 + buf []byte 23 + pool poolIndex 24 + }
-214
internal/bufpool/buffers.go
··· 1 - // Package bufpool provides a lightweight byte-buffer type with optional 2 - // pooling. 3 - package bufpool 4 - 5 - import "sync" 6 - 7 - const ( 8 - // DefaultBufferCap is the minimum capacity a borrowed buffer will have. 9 - // Borrow() will allocate or retrieve a buffer with at least this capacity. 10 - DefaultBufferCap = 32 * 1024 11 - 12 - // maxPooledBuffer defines the maximum capacity of a buffer that may be 13 - // returned to the pool. Buffers larger than this will not be pooled to 14 - // avoid unbounded memory usage. 15 - maxPooledBuffer = 8 << 20 16 - ) 17 - 18 - // Buffer is a growable byte container that optionally participates in a 19 - // memory pool. A Buffer may be obtained through Borrow() or constructed 20 - // directly from owned data via FromOwned(). 21 - // 22 - // A Buffer's underlying slice may grow as needed. When finished with a 23 - // pooled buffer, the caller should invoke Release() to return it to the pool. 24 - // 25 - // Buffers must not be copied after first use; doing so can cause double-returns 26 - // to the pool and data races. 27 - // 28 - // In general, pass Buffer around when used internally, and directly .Bytes() when 29 - // returning output across our API boundary. It is neither necessary nor efficient 30 - // to copy/append the .Bytes() to a newly-allocated slice; in cases where we do 31 - // want the raw byte slice out of our API boundary, it is perfectly acceptable to 32 - // simply not call Release(). 33 - // 34 - //go:nocopy 35 - type Buffer struct { 36 - _ struct{} // for nocopy 37 - buf []byte 38 - pool poolIndex 39 - } 40 - 41 - type poolIndex int8 42 - 43 - const ( 44 - unpooled poolIndex = -1 45 - ) 46 - 47 - var sizeClasses = [...]int{ 48 - DefaultBufferCap, 49 - 64 << 10, 50 - 128 << 10, 51 - 256 << 10, 52 - 512 << 10, 53 - 1 << 20, 54 - 2 << 20, 55 - 4 << 20, 56 - maxPooledBuffer, 57 - } 58 - 59 - var bufferPools = func() []sync.Pool { 60 - pools := make([]sync.Pool, len(sizeClasses)) 61 - for i, classCap := range sizeClasses { 62 - capCopy := classCap 63 - pools[i].New = func() any { 64 - buf := make([]byte, 0, capCopy) 65 - 66 - return &buf 67 - } 68 - } 69 - 70 - return pools 71 - }() 72 - 73 - // Borrow retrieves a Buffer suitable for storing up to capHint bytes. 74 - // The returned Buffer may come from an internal sync.Pool. 75 - // 76 - // If capHint is smaller than DefaultBufferCap, it is automatically raised 77 - // to DefaultBufferCap. If no pooled buffer has sufficient capacity, a new 78 - // unpooled buffer is allocated. 79 - // 80 - // The caller must call Release() when finished using the returned Buffer. 81 - func Borrow(capHint int) Buffer { 82 - if capHint < DefaultBufferCap { 83 - capHint = DefaultBufferCap 84 - } 85 - 86 - classIdx, classCap, pooled := classFor(capHint) 87 - if !pooled { 88 - newBuf := make([]byte, 0, capHint) 89 - 90 - return Buffer{buf: newBuf, pool: unpooled} 91 - } 92 - //nolint:forcetypeassert 93 - buf := bufferPools[classIdx].Get().(*[]byte) 94 - if cap(*buf) < classCap { 95 - *buf = make([]byte, 0, classCap) 96 - } 97 - 98 - slice := (*buf)[:0] 99 - 100 - return Buffer{buf: slice, pool: poolIndex(classIdx)} //#nosec G115 101 - } 102 - 103 - // FromOwned constructs a Buffer from a caller-owned byte slice. The resulting 104 - // Buffer does not participate in pooling and will never be returned to the 105 - // internal pool when released. 106 - func FromOwned(buf []byte) Buffer { 107 - return Buffer{buf: buf, pool: unpooled} 108 - } 109 - 110 - // Resize adjusts the length of the buffer to n bytes. If n exceeds the current 111 - // capacity, the underlying storage is grown. If n is negative, it is treated 112 - // as zero. 113 - // 114 - // The buffer's new contents beyond the previous length are undefined. 115 - func (buf *Buffer) Resize(n int) { 116 - if n < 0 { 117 - n = 0 118 - } 119 - 120 - buf.ensureCapacity(n) 121 - buf.buf = buf.buf[:n] 122 - } 123 - 124 - // Append copies the provided bytes onto the end of the buffer, growing its 125 - // capacity if required. If src is empty, the method does nothing. 126 - // 127 - // The receiver retains ownership of the data; the caller may reuse src freely. 128 - func (buf *Buffer) Append(src []byte) { 129 - if len(src) == 0 { 130 - return 131 - } 132 - 133 - start := len(buf.buf) 134 - buf.ensureCapacity(start + len(src)) 135 - buf.buf = buf.buf[:start+len(src)] 136 - copy(buf.buf[start:], src) 137 - } 138 - 139 - // Bytes returns the underlying byte slice that represents the current contents 140 - // of the buffer. Modifying the returned slice modifies the Buffer itself. 141 - func (buf *Buffer) Bytes() []byte { 142 - return buf.buf 143 - } 144 - 145 - // Release returns the buffer to the global pool if it originated from the 146 - // pool and its capacity is no larger than maxPooledBuffer. After release, the 147 - // Buffer becomes invalid and should not be used further. 148 - // 149 - // Releasing a non-pooled buffer has no effect beyond clearing its internal 150 - // storage. 151 - func (buf *Buffer) Release() { 152 - if buf.buf == nil { 153 - return 154 - } 155 - 156 - buf.returnToPool() 157 - buf.buf = nil 158 - buf.pool = unpooled 159 - } 160 - 161 - // ensureCapacity grows the underlying buffer to accommodate the requested 162 - // number of bytes. Growth doubles the capacity by default unless a larger 163 - // expansion is needed. If the previous storage was pooled and not oversized, 164 - // it is returned to the pool. 165 - func (buf *Buffer) ensureCapacity(needed int) { 166 - if cap(buf.buf) >= needed { 167 - return 168 - } 169 - 170 - classIdx, classCap, pooled := classFor(needed) 171 - 172 - var newBuf []byte 173 - 174 - if pooled { 175 - //nolint:forcetypeassert 176 - raw := bufferPools[classIdx].Get().(*[]byte) 177 - if cap(*raw) < classCap { 178 - *raw = make([]byte, 0, classCap) 179 - } 180 - 181 - newBuf = (*raw)[:len(buf.buf)] 182 - } else { 183 - newBuf = make([]byte, len(buf.buf), classCap) 184 - } 185 - 186 - copy(newBuf, buf.buf) 187 - buf.returnToPool() 188 - 189 - buf.buf = newBuf 190 - if pooled { 191 - buf.pool = poolIndex(classIdx) //#nosec G115 192 - } else { 193 - buf.pool = unpooled 194 - } 195 - } 196 - 197 - func classFor(size int) (idx, classCap int, ok bool) { 198 - for i, class := range sizeClasses { 199 - if size <= class { 200 - return i, class, true 201 - } 202 - } 203 - 204 - return -1, size, false 205 - } 206 - 207 - func (buf *Buffer) returnToPool() { 208 - if buf.pool == unpooled { 209 - return 210 - } 211 - 212 - tmp := buf.buf[:0] 213 - bufferPools[int(buf.pool)].Put(&tmp) 214 - }
+7
internal/bufpool/bytes.go
··· 1 + package bufpool 2 + 3 + // Bytes returns the underlying byte slice that represents the current contents 4 + // of the buffer. Modifying the returned slice modifies the Buffer itself. 5 + func (buf *Buffer) Bytes() []byte { 6 + return buf.buf 7 + }
+37
internal/bufpool/capacity.go
··· 1 + package bufpool 2 + 3 + // ensureCapacity grows the underlying buffer to accommodate the requested 4 + // number of bytes. Growth doubles the capacity by default unless a larger 5 + // expansion is needed. If the previous storage was pooled and not oversized, 6 + // it is returned to the pool. 7 + func (buf *Buffer) ensureCapacity(needed int) { 8 + if cap(buf.buf) >= needed { 9 + return 10 + } 11 + 12 + classIdx, classCap, pooled := classFor(needed) 13 + 14 + var newBuf []byte 15 + 16 + if pooled { 17 + //nolint:forcetypeassert 18 + raw := bufferPools[classIdx].Get().(*[]byte) 19 + if cap(*raw) < classCap { 20 + *raw = make([]byte, 0, classCap) 21 + } 22 + 23 + newBuf = (*raw)[:len(buf.buf)] 24 + } else { 25 + newBuf = make([]byte, len(buf.buf), classCap) 26 + } 27 + 28 + copy(newBuf, buf.buf) 29 + buf.returnToPool() 30 + 31 + buf.buf = newBuf 32 + if pooled { 33 + buf.pool = poolIndex(classIdx) //#nosec G115 34 + } else { 35 + buf.pool = unpooled 36 + } 37 + }
+23
internal/bufpool/class.go
··· 1 + package bufpool 2 + 3 + var sizeClasses = [...]int{ 4 + DefaultBufferCap, 5 + 64 << 10, 6 + 128 << 10, 7 + 256 << 10, 8 + 512 << 10, 9 + 1 << 20, 10 + 2 << 20, 11 + 4 << 20, 12 + maxPooledBuffer, 13 + } 14 + 15 + func classFor(size int) (idx, classCap int, ok bool) { 16 + for i, class := range sizeClasses { 17 + if size <= class { 18 + return i, class, true 19 + } 20 + } 21 + 22 + return -1, size, false 23 + }
+12
internal/bufpool/consts.go
··· 1 + package bufpool 2 + 3 + const ( 4 + // DefaultBufferCap is the minimum capacity a borrowed buffer will have. 5 + // Borrow() will allocate or retrieve a buffer with at least this capacity. 6 + DefaultBufferCap = 32 * 1024 7 + 8 + // maxPooledBuffer defines the maximum capacity of a buffer that may be 9 + // returned to the pool. Buffers larger than this will not be pooled to 10 + // avoid unbounded memory usage. 11 + maxPooledBuffer = 8 << 20 12 + )
+3
internal/bufpool/doc.go
··· 1 + // Package bufpool provides a lightweight byte-buffer type with optional 2 + // pooling. 3 + package bufpool
+8
internal/bufpool/from_owned.go
··· 1 + package bufpool 2 + 3 + // FromOwned constructs a Buffer from a caller-owned byte slice. The resulting 4 + // Buffer does not participate in pooling and will never be returned to the 5 + // internal pool when released. 6 + func FromOwned(buf []byte) Buffer { 7 + return Buffer{buf: buf, pool: unpooled} 8 + }
+7
internal/bufpool/index.go
··· 1 + package bufpool 2 + 3 + type poolIndex int8 4 + 5 + const ( 6 + unpooled poolIndex = -1 7 + )
+17
internal/bufpool/pool.go
··· 1 + package bufpool 2 + 3 + import "sync" 4 + 5 + var bufferPools = func() []sync.Pool { 6 + pools := make([]sync.Pool, len(sizeClasses)) 7 + for i, classCap := range sizeClasses { 8 + capCopy := classCap 9 + pools[i].New = func() any { 10 + buf := make([]byte, 0, capCopy) 11 + 12 + return &buf 13 + } 14 + } 15 + 16 + return pools 17 + }()
+17
internal/bufpool/release.go
··· 1 + package bufpool 2 + 3 + // Release returns the buffer to the global pool if it originated from the 4 + // pool and its capacity is no larger than maxPooledBuffer. After release, the 5 + // Buffer becomes invalid and should not be used further. 6 + // 7 + // Releasing a non-pooled buffer has no effect beyond clearing its internal 8 + // storage. 9 + func (buf *Buffer) Release() { 10 + if buf.buf == nil { 11 + return 12 + } 13 + 14 + buf.returnToPool() 15 + buf.buf = nil 16 + buf.pool = unpooled 17 + }
+15
internal/bufpool/resize.go
··· 1 + package bufpool 2 + 3 + // Resize adjusts the length of the buffer to n bytes. If n exceeds the current 4 + // capacity, the underlying storage is grown. If n is negative, it is treated 5 + // as zero. 6 + // 7 + // The buffer's new contents beyond the previous length are undefined. 8 + func (buf *Buffer) Resize(n int) { 9 + if n < 0 { 10 + n = 0 11 + } 12 + 13 + buf.ensureCapacity(n) 14 + buf.buf = buf.buf[:n] 15 + }
+10
internal/bufpool/return.go
··· 1 + package bufpool 2 + 3 + func (buf *Buffer) returnToPool() { 4 + if buf.pool == unpooled { 5 + return 6 + } 7 + 8 + tmp := buf.buf[:0] 9 + bufferPools[int(buf.pool)].Put(&tmp) 10 + }