cloudflared-mirror/vendor/github.com/kshvakov/clickhouse/lib/writebuffer/buffer.go

114 lines
2.2 KiB
Go

package writebuffer
import (
"io"
"github.com/kshvakov/clickhouse/lib/leakypool"
)
const InitialSize = 256 * 1024
func New(initSize int) *WriteBuffer {
wb := &WriteBuffer{}
wb.addChunk(0, initSize)
return wb
}
type WriteBuffer struct {
chunks [][]byte
}
func (wb *WriteBuffer) Write(data []byte) (int, error) {
var (
chunkIdx = len(wb.chunks) - 1
dataSize = len(data)
)
for {
freeSize := cap(wb.chunks[chunkIdx]) - len(wb.chunks[chunkIdx])
if freeSize >= len(data) {
wb.chunks[chunkIdx] = append(wb.chunks[chunkIdx], data...)
return dataSize, nil
}
wb.chunks[chunkIdx] = append(wb.chunks[chunkIdx], data[:freeSize]...)
data = data[freeSize:]
wb.addChunk(0, wb.calcCap(len(data)))
chunkIdx++
}
}
func (wb *WriteBuffer) WriteTo(w io.Writer) (int64, error) {
var size int64
for _, chunk := range wb.chunks {
ln, err := w.Write(chunk)
if err != nil {
wb.Reset()
return 0, err
}
size += int64(ln)
}
wb.Reset()
return size, nil
}
func (wb *WriteBuffer) Bytes() []byte {
if len(wb.chunks) == 1 {
return wb.chunks[0]
}
bytes := make([]byte, 0, wb.len())
for _, chunk := range wb.chunks {
bytes = append(bytes, chunk...)
}
return bytes
}
func (wb *WriteBuffer) addChunk(size, capacity int) {
chunk := leakypool.GetBytes(size, capacity)
if cap(chunk) >= size {
chunk = chunk[:size]
}
wb.chunks = append(wb.chunks, chunk)
}
func (wb *WriteBuffer) len() int {
var v int
for _, chunk := range wb.chunks {
v += len(chunk)
}
return v
}
func (wb *WriteBuffer) calcCap(dataSize int) int {
dataSize = max(dataSize, 64)
if len(wb.chunks) == 0 {
return dataSize
}
// Always double the size of the last chunk
return max(dataSize, cap(wb.chunks[len(wb.chunks)-1])*2)
}
func (wb *WriteBuffer) Reset() {
if len(wb.chunks) == 0 {
return
}
// Recycle all chunks except the last one
chunkSizeThreshold := cap(wb.chunks[0])
for _, chunk := range wb.chunks[:len(wb.chunks)-1] {
// Drain chunks smaller than the initial size
if cap(chunk) >= chunkSizeThreshold {
leakypool.PutBytes(chunk[:0])
} else {
chunkSizeThreshold = cap(chunk)
}
}
// Keep the largest chunk
wb.chunks[0] = wb.chunks[len(wb.chunks)-1][:0]
wb.chunks = wb.chunks[:1]
}
func max(a, b int) int {
if b > a {
return b
}
return a
}