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 }