174 lines
3.8 KiB
Go
174 lines
3.8 KiB
Go
// Copyright 2016 The Internal Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Package slice implements pools of pointers to slices.
|
|
package slice
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"github.com/cznic/mathutil"
|
|
)
|
|
|
|
var (
|
|
// Bytes is a ready to use *[]byte Pool.
|
|
Bytes *Pool
|
|
// Ints is a ready to use *[]int Pool.
|
|
Ints *Pool
|
|
)
|
|
|
|
func init() {
|
|
Bytes = newBytes()
|
|
Ints = NewPool(
|
|
func(size int) interface{} { // create
|
|
b := make([]int, size)
|
|
return &b
|
|
},
|
|
func(s interface{}) { // clear
|
|
b := *s.(*[]int)
|
|
b = b[:cap(b)]
|
|
for i := range b {
|
|
b[i] = 0
|
|
}
|
|
},
|
|
func(s interface{}, size int) { // setSize
|
|
p := s.(*[]int)
|
|
*p = (*p)[:size]
|
|
},
|
|
func(s interface{}) int { return cap(*s.(*[]int)) }, // cap
|
|
)
|
|
}
|
|
|
|
func newBytes() *Pool {
|
|
return NewPool(
|
|
func(size int) interface{} { // create
|
|
b := make([]byte, size)
|
|
return &b
|
|
},
|
|
func(s interface{}) { // clear
|
|
b := *s.(*[]byte)
|
|
b = b[:cap(b)]
|
|
for i := range b {
|
|
b[i] = 0
|
|
}
|
|
},
|
|
func(s interface{}, size int) { // setSize
|
|
p := s.(*[]byte)
|
|
*p = (*p)[:size]
|
|
},
|
|
func(s interface{}) int { return cap(*s.(*[]byte)) }, // cap
|
|
)
|
|
}
|
|
|
|
// Pool implements a pool of pointers to slices.
|
|
//
|
|
// Example usage pattern (assuming pool is, for example, a *[]byte Pool)
|
|
//
|
|
// p := pool.Get(size).(*[]byte)
|
|
// b := *p // Now you can use b in any way you need.
|
|
// ...
|
|
// // When b will not be used anymore
|
|
// pool.Put(p)
|
|
// ...
|
|
// // If b or p are not going out of scope soon, optionally
|
|
// b = nil
|
|
// p = nil
|
|
//
|
|
// Otherwise the pool cannot release the slice on garbage collection.
|
|
//
|
|
// Do not do
|
|
//
|
|
// p := pool.Get(size).(*[]byte)
|
|
// b := *p
|
|
// ...
|
|
// pool.Put(&b)
|
|
//
|
|
// or
|
|
//
|
|
// b := *pool.Get(size).(*[]byte)
|
|
// ...
|
|
// pool.Put(&b)
|
|
type Pool struct {
|
|
cap func(interface{}) int
|
|
clear func(interface{})
|
|
m [63]sync.Pool
|
|
null interface{}
|
|
setSize func(interface{}, int)
|
|
}
|
|
|
|
// NewPool returns a newly created Pool. Assuming the desired slice type is
|
|
// []T:
|
|
//
|
|
// The create function returns a *[]T of len == cap == size.
|
|
//
|
|
// The argument of clear is *[]T and the function sets all the slice elements
|
|
// to the respective zero value.
|
|
//
|
|
// The setSize function gets a *[]T and sets its len to size.
|
|
//
|
|
// The cap function gets a *[]T and returns its capacity.
|
|
func NewPool(
|
|
create func(size int) interface{},
|
|
clear func(interface{}),
|
|
setSize func(p interface{}, size int),
|
|
cap func(p interface{}) int,
|
|
) *Pool {
|
|
p := &Pool{clear: clear, setSize: setSize, cap: cap, null: create(0)}
|
|
for i := range p.m {
|
|
size := 1 << uint(i)
|
|
p.m[i] = sync.Pool{New: func() interface{} {
|
|
// 0: 1 - 1
|
|
// 1: 10 - 10
|
|
// 2: 11 - 100
|
|
// 3: 101 - 1000
|
|
// 4: 1001 - 10000
|
|
// 5: 10001 - 100000
|
|
return create(size)
|
|
}}
|
|
}
|
|
return p
|
|
}
|
|
|
|
// CGet returns a *[]T of len size. The pointed to slice is zeroed up to its
|
|
// cap. CGet panics for size < 0.
|
|
//
|
|
// CGet is safe for concurrent use by multiple goroutines.
|
|
func (p *Pool) CGet(size int) interface{} {
|
|
s := p.Get(size)
|
|
p.clear(s)
|
|
return s
|
|
}
|
|
|
|
// Get returns a *[]T of len size. The pointed to slice is not zeroed. Get
|
|
// panics for size < 0.
|
|
//
|
|
// Get is safe for concurrent use by multiple goroutines.
|
|
func (p *Pool) Get(size int) interface{} {
|
|
var index int
|
|
switch {
|
|
case size < 0:
|
|
panic("Pool.Get: negative size")
|
|
case size == 0:
|
|
return p.null
|
|
case size > 1:
|
|
index = mathutil.Log2Uint64(uint64(size-1)) + 1
|
|
}
|
|
s := p.m[index].Get()
|
|
p.setSize(s, size)
|
|
return s
|
|
}
|
|
|
|
// Put puts a *[]T into a pool for possible later reuse by CGet or Get. Put
|
|
// panics is its argument is not of type *[]T.
|
|
//
|
|
// Put is safe for concurrent use by multiple goroutines.
|
|
func (p *Pool) Put(b interface{}) {
|
|
size := p.cap(b)
|
|
if size == 0 {
|
|
return
|
|
}
|
|
|
|
p.m[mathutil.Log2Uint64(uint64(size))].Put(b)
|
|
}
|