forked from ebhomengo/niki
1
0
Fork 0
niki/vendor/github.com/mailru/easyjson/buffer/pool.go

457 lines
5.7 KiB
Go
Raw Normal View History

2024-05-14 13:07:09 +00:00
// Package buffer implements a buffer for serialization, consisting of a chain of []byte-s to
2024-05-14 13:07:09 +00:00
// reduce copying and to allow reuse of individual chunks.
2024-05-14 13:07:09 +00:00
package buffer
import (
"io"
"net"
"sync"
)
// PoolConfig contains configuration for the allocation and reuse strategy.
2024-05-14 13:07:09 +00:00
type PoolConfig struct {
StartSize int // Minimum chunk size that is allocated.
2024-05-14 13:07:09 +00:00
PooledSize int // Minimum chunk size that is reused, reusing chunks too small will result in overhead.
MaxSize int // Maximum chunk size that will be allocated.
2024-05-14 13:07:09 +00:00
}
var config = PoolConfig{
StartSize: 128,
2024-05-14 13:07:09 +00:00
PooledSize: 512,
MaxSize: 32768,
2024-05-14 13:07:09 +00:00
}
// Reuse pool: chunk size -> pool.
2024-05-14 13:07:09 +00:00
var buffers = map[int]*sync.Pool{}
func initBuffers() {
2024-05-14 13:07:09 +00:00
for l := config.PooledSize; l <= config.MaxSize; l *= 2 {
2024-05-14 13:07:09 +00:00
buffers[l] = new(sync.Pool)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
func init() {
2024-05-14 13:07:09 +00:00
initBuffers()
2024-05-14 13:07:09 +00:00
}
// Init sets up a non-default pooling and allocation strategy. Should be run before serialization is done.
2024-05-14 13:07:09 +00:00
func Init(cfg PoolConfig) {
2024-05-14 13:07:09 +00:00
config = cfg
2024-05-14 13:07:09 +00:00
initBuffers()
2024-05-14 13:07:09 +00:00
}
// putBuf puts a chunk to reuse pool if it can be reused.
2024-05-14 13:07:09 +00:00
func putBuf(buf []byte) {
2024-05-14 13:07:09 +00:00
size := cap(buf)
2024-05-14 13:07:09 +00:00
if size < config.PooledSize {
2024-05-14 13:07:09 +00:00
return
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
if c := buffers[size]; c != nil {
2024-05-14 13:07:09 +00:00
c.Put(buf[:0])
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
// getBuf gets a chunk from reuse pool or creates a new one if reuse failed.
2024-05-14 13:07:09 +00:00
func getBuf(size int) []byte {
2024-05-14 13:07:09 +00:00
if size >= config.PooledSize {
2024-05-14 13:07:09 +00:00
if c := buffers[size]; c != nil {
2024-05-14 13:07:09 +00:00
v := c.Get()
2024-05-14 13:07:09 +00:00
if v != nil {
2024-05-14 13:07:09 +00:00
return v.([]byte)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return make([]byte, 0, size)
2024-05-14 13:07:09 +00:00
}
// Buffer is a buffer optimized for serialization without extra copying.
2024-05-14 13:07:09 +00:00
type Buffer struct {
// Buf is the current chunk that can be used for serialization.
2024-05-14 13:07:09 +00:00
Buf []byte
toPool []byte
bufs [][]byte
2024-05-14 13:07:09 +00:00
}
// EnsureSpace makes sure that the current chunk contains at least s free bytes,
2024-05-14 13:07:09 +00:00
// possibly creating a new chunk.
2024-05-14 13:07:09 +00:00
func (b *Buffer) EnsureSpace(s int) {
2024-05-14 13:07:09 +00:00
if cap(b.Buf)-len(b.Buf) < s {
2024-05-14 13:07:09 +00:00
b.ensureSpaceSlow(s)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
func (b *Buffer) ensureSpaceSlow(s int) {
2024-05-14 13:07:09 +00:00
l := len(b.Buf)
2024-05-14 13:07:09 +00:00
if l > 0 {
2024-05-14 13:07:09 +00:00
if cap(b.toPool) != cap(b.Buf) {
2024-05-14 13:07:09 +00:00
// Chunk was reallocated, toPool can be pooled.
2024-05-14 13:07:09 +00:00
putBuf(b.toPool)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
if cap(b.bufs) == 0 {
2024-05-14 13:07:09 +00:00
b.bufs = make([][]byte, 0, 8)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
b.bufs = append(b.bufs, b.Buf)
2024-05-14 13:07:09 +00:00
l = cap(b.toPool) * 2
2024-05-14 13:07:09 +00:00
} else {
2024-05-14 13:07:09 +00:00
l = config.StartSize
2024-05-14 13:07:09 +00:00
}
if l > config.MaxSize {
2024-05-14 13:07:09 +00:00
l = config.MaxSize
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
b.Buf = getBuf(l)
2024-05-14 13:07:09 +00:00
b.toPool = b.Buf
2024-05-14 13:07:09 +00:00
}
// AppendByte appends a single byte to buffer.
2024-05-14 13:07:09 +00:00
func (b *Buffer) AppendByte(data byte) {
2024-05-14 13:07:09 +00:00
b.EnsureSpace(1)
2024-05-14 13:07:09 +00:00
b.Buf = append(b.Buf, data)
2024-05-14 13:07:09 +00:00
}
// AppendBytes appends a byte slice to buffer.
2024-05-14 13:07:09 +00:00
func (b *Buffer) AppendBytes(data []byte) {
2024-05-14 13:07:09 +00:00
if len(data) <= cap(b.Buf)-len(b.Buf) {
2024-05-14 13:07:09 +00:00
b.Buf = append(b.Buf, data...) // fast path
2024-05-14 13:07:09 +00:00
} else {
2024-05-14 13:07:09 +00:00
b.appendBytesSlow(data)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
func (b *Buffer) appendBytesSlow(data []byte) {
2024-05-14 13:07:09 +00:00
for len(data) > 0 {
2024-05-14 13:07:09 +00:00
b.EnsureSpace(1)
sz := cap(b.Buf) - len(b.Buf)
2024-05-14 13:07:09 +00:00
if sz > len(data) {
2024-05-14 13:07:09 +00:00
sz = len(data)
2024-05-14 13:07:09 +00:00
}
b.Buf = append(b.Buf, data[:sz]...)
2024-05-14 13:07:09 +00:00
data = data[sz:]
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
// AppendString appends a string to buffer.
2024-05-14 13:07:09 +00:00
func (b *Buffer) AppendString(data string) {
2024-05-14 13:07:09 +00:00
if len(data) <= cap(b.Buf)-len(b.Buf) {
2024-05-14 13:07:09 +00:00
b.Buf = append(b.Buf, data...) // fast path
2024-05-14 13:07:09 +00:00
} else {
2024-05-14 13:07:09 +00:00
b.appendStringSlow(data)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
func (b *Buffer) appendStringSlow(data string) {
2024-05-14 13:07:09 +00:00
for len(data) > 0 {
2024-05-14 13:07:09 +00:00
b.EnsureSpace(1)
sz := cap(b.Buf) - len(b.Buf)
2024-05-14 13:07:09 +00:00
if sz > len(data) {
2024-05-14 13:07:09 +00:00
sz = len(data)
2024-05-14 13:07:09 +00:00
}
b.Buf = append(b.Buf, data[:sz]...)
2024-05-14 13:07:09 +00:00
data = data[sz:]
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
// Size computes the size of a buffer by adding sizes of every chunk.
2024-05-14 13:07:09 +00:00
func (b *Buffer) Size() int {
2024-05-14 13:07:09 +00:00
size := len(b.Buf)
2024-05-14 13:07:09 +00:00
for _, buf := range b.bufs {
2024-05-14 13:07:09 +00:00
size += len(buf)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return size
2024-05-14 13:07:09 +00:00
}
// DumpTo outputs the contents of a buffer to a writer and resets the buffer.
2024-05-14 13:07:09 +00:00
func (b *Buffer) DumpTo(w io.Writer) (written int, err error) {
2024-05-14 13:07:09 +00:00
bufs := net.Buffers(b.bufs)
2024-05-14 13:07:09 +00:00
if len(b.Buf) > 0 {
2024-05-14 13:07:09 +00:00
bufs = append(bufs, b.Buf)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
n, err := bufs.WriteTo(w)
for _, buf := range b.bufs {
2024-05-14 13:07:09 +00:00
putBuf(buf)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
putBuf(b.toPool)
b.bufs = nil
2024-05-14 13:07:09 +00:00
b.Buf = nil
2024-05-14 13:07:09 +00:00
b.toPool = nil
return int(n), err
2024-05-14 13:07:09 +00:00
}
// BuildBytes creates a single byte slice with all the contents of the buffer. Data is
2024-05-14 13:07:09 +00:00
// copied if it does not fit in a single chunk. You can optionally provide one byte
2024-05-14 13:07:09 +00:00
// slice as argument that it will try to reuse.
2024-05-14 13:07:09 +00:00
func (b *Buffer) BuildBytes(reuse ...[]byte) []byte {
2024-05-14 13:07:09 +00:00
if len(b.bufs) == 0 {
2024-05-14 13:07:09 +00:00
ret := b.Buf
2024-05-14 13:07:09 +00:00
b.toPool = nil
2024-05-14 13:07:09 +00:00
b.Buf = nil
2024-05-14 13:07:09 +00:00
return ret
2024-05-14 13:07:09 +00:00
}
var ret []byte
2024-05-14 13:07:09 +00:00
size := b.Size()
// If we got a buffer as argument and it is big enough, reuse it.
2024-05-14 13:07:09 +00:00
if len(reuse) == 1 && cap(reuse[0]) >= size {
2024-05-14 13:07:09 +00:00
ret = reuse[0][:0]
2024-05-14 13:07:09 +00:00
} else {
2024-05-14 13:07:09 +00:00
ret = make([]byte, 0, size)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
for _, buf := range b.bufs {
2024-05-14 13:07:09 +00:00
ret = append(ret, buf...)
2024-05-14 13:07:09 +00:00
putBuf(buf)
2024-05-14 13:07:09 +00:00
}
ret = append(ret, b.Buf...)
2024-05-14 13:07:09 +00:00
putBuf(b.toPool)
b.bufs = nil
2024-05-14 13:07:09 +00:00
b.toPool = nil
2024-05-14 13:07:09 +00:00
b.Buf = nil
return ret
2024-05-14 13:07:09 +00:00
}
type readCloser struct {
offset int
bufs [][]byte
2024-05-14 13:07:09 +00:00
}
func (r *readCloser) Read(p []byte) (n int, err error) {
2024-05-14 13:07:09 +00:00
for _, buf := range r.bufs {
2024-05-14 13:07:09 +00:00
// Copy as much as we can.
2024-05-14 13:07:09 +00:00
x := copy(p[n:], buf[r.offset:])
2024-05-14 13:07:09 +00:00
n += x // Increment how much we filled.
// Did we empty the whole buffer?
2024-05-14 13:07:09 +00:00
if r.offset+x == len(buf) {
2024-05-14 13:07:09 +00:00
// On to the next buffer.
2024-05-14 13:07:09 +00:00
r.offset = 0
2024-05-14 13:07:09 +00:00
r.bufs = r.bufs[1:]
// We can release this buffer.
2024-05-14 13:07:09 +00:00
putBuf(buf)
2024-05-14 13:07:09 +00:00
} else {
2024-05-14 13:07:09 +00:00
r.offset += x
2024-05-14 13:07:09 +00:00
}
if n == len(p) {
2024-05-14 13:07:09 +00:00
break
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
// No buffers left or nothing read?
2024-05-14 13:07:09 +00:00
if len(r.bufs) == 0 {
2024-05-14 13:07:09 +00:00
err = io.EOF
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return
2024-05-14 13:07:09 +00:00
}
func (r *readCloser) Close() error {
2024-05-14 13:07:09 +00:00
// Release all remaining buffers.
2024-05-14 13:07:09 +00:00
for _, buf := range r.bufs {
2024-05-14 13:07:09 +00:00
putBuf(buf)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
// In case Close gets called multiple times.
2024-05-14 13:07:09 +00:00
r.bufs = nil
return nil
2024-05-14 13:07:09 +00:00
}
// ReadCloser creates an io.ReadCloser with all the contents of the buffer.
2024-05-14 13:07:09 +00:00
func (b *Buffer) ReadCloser() io.ReadCloser {
2024-05-14 13:07:09 +00:00
ret := &readCloser{0, append(b.bufs, b.Buf)}
b.bufs = nil
2024-05-14 13:07:09 +00:00
b.toPool = nil
2024-05-14 13:07:09 +00:00
b.Buf = nil
return ret
2024-05-14 13:07:09 +00:00
}