-
Notifications
You must be signed in to change notification settings - Fork 5.9k
/
bytespool.go
90 lines (79 loc) · 2.31 KB
/
bytespool.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
// Copyright 2017 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
package bytespool
import (
"sync"
)
// BytesPool maintains large bytes pools, used for reducing memory allocation.
// It has a slice of pools which handle different size of bytes.
// Can be safely used concurrently.
type BytesPool struct {
buckets []sync.Pool
}
const (
kilo = 1024
mega = kilo * kilo
baseSize = kilo
numBuckets = 18
maxSize = 128 * mega
)
// DefaultPool is a default BytesBool instance.
var DefaultPool = NewBytesPool()
// NewBytesPool creates a new bytes pool.
func NewBytesPool() *BytesPool {
bp := new(BytesPool)
bp.buckets = make([]sync.Pool, numBuckets)
for i := uint(0); i < numBuckets; i++ {
bp.buckets[i].New = makeNewFunc(i)
}
return bp
}
func makeNewFunc(shift uint) func() interface{} {
return func() interface{} {
return make([]byte, baseSize<<shift)
}
}
// Alloc allocates a bytes which has the size of power of two.
// The caller should keep the origin bytes and use the returned data.
// When finished using, the origin bytes should be freed to the pool.
// The allocated data may not have zero value.
func (bp *BytesPool) Alloc(size int) (origin, data []byte) {
if size > maxSize {
return nil, make([]byte, size)
}
i := bucketIdx(size)
origin = bp.buckets[i].Get().([]byte)
data = origin[:size]
return
}
// Free frees the data which should be the original bytes return by Alloc.
// It returns the bucket index of the data. returns -1 means the data is not returned to the pool.
func (bp *BytesPool) Free(origin []byte) int {
originLen := len(origin)
if originLen > maxSize || originLen < baseSize || !isPowerOfTwo(originLen) {
return -1
}
i := bucketIdx(originLen)
bp.buckets[i].Put(origin)
return i
}
func isPowerOfTwo(x int) bool {
return x&(x-1) == 0
}
func bucketIdx(size int) (i int) {
for size > baseSize {
size = (size + 1) >> 1
i++
}
return
}