Closed AmitKumarDas closed 2 years ago
// https://blog.questionable.services/article/using-buffer-pools-with-go/
//
// - https://github.com/oxtoacart/bpool
package bpool
type SizedBufferPool struct {
c chan *bytes.Buffer
a int
}
// SizedBufferPool creates a new BufferPool
// bounded to the given size
//
// size defines the number of buffers to be
// retained in the pool
//
// alloc sets the initial capacity of a new
// buffer to minimize calls to make()
func NewSizedBufferPool(size int, alloc int) (bp *SizedBufferPool) {
return &SizedBufferPool{
c: make(chan *bytes.Buffer, size), // buffered
a: alloc,
}
}
// Get gets a Buffer from the pool or creates
// a new one if none are available in the pool
//
// Buffers have a pre-allocated capacity
func (bp *SizedBufferPool) Get() (b *bytes.Buffer) {
select { // non blocking
case b = <-bp.c:
// reuse existing buffer
default:
// or create new buffer
// buffer is []byte
b = bytes.NewBuffer(make([]byte, 0, bp.a))
}
return
}
// Put returns the given Buffer to the pool
func (bp *SizedBufferPool) Put(b *bytes.Buffer) {
b.Reset() // no more content
// Release buffers over our maximum capacity
// and re-create a pre-sized buffer to replace it
if cap(b.Bytes()) > bp.a {
// discard the buffer & create a new
b = bytes.NewBuffer(make([]byte, 0, bp.a))
}
select { // non blocking
case bp.c <- b:
default: // discard the buffer if the pool is full
}
}
// buffer.Cap() method in Go 1.5 is different
// from calling cap(b.Bytes()). The latter
// returns the capacity of the **unread** portion
// of the buffer’s underlying slice, which may
// not be the total capacity if you’ve read
// from it during its lifetime. This doesn’t
// affect our implementation as we call b.Reset()
// (which resets the **read offset**) before we
// check the capacity, which means we get the
// “correct” (full) capacity of the underlying
// slice.