/
buffer_pool.go
135 lines (117 loc) · 2.59 KB
/
buffer_pool.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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// Copyright (c) 2014, Suryandaru Triandana <maurodelazeri@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package util
import (
"fmt"
"sync"
"sync/atomic"
)
// BufferPool is a 'buffer pool'.
type BufferPool struct {
pool [6]sync.Pool
baseline [5]int
get uint32
put uint32
less uint32
equal uint32
greater uint32
miss uint32
}
func (p *BufferPool) poolNum(n int) int {
for i, x := range p.baseline {
if n <= x {
return i
}
}
return len(p.baseline)
}
// Get returns buffer with length of n.
func (p *BufferPool) Get(n int) []byte {
if p == nil {
return make([]byte, n)
}
atomic.AddUint32(&p.get, 1)
poolNum := p.poolNum(n)
b := p.pool[poolNum].Get().(*[]byte)
if cap(*b) == 0 {
// If we grabbed nothing, increment the miss stats.
atomic.AddUint32(&p.miss, 1)
if poolNum == len(p.baseline) {
*b = make([]byte, n)
return *b
}
*b = make([]byte, p.baseline[poolNum])
*b = (*b)[:n]
return *b
} else {
// If there is enough capacity in the bytes grabbed, resize the length
// to n and return.
if n < cap(*b) {
atomic.AddUint32(&p.less, 1)
*b = (*b)[:n]
return *b
} else if n == cap(*b) {
atomic.AddUint32(&p.equal, 1)
*b = (*b)[:n]
return *b
} else if n > cap(*b) {
atomic.AddUint32(&p.greater, 1)
}
}
if poolNum == len(p.baseline) {
*b = make([]byte, n)
return *b
}
*b = make([]byte, p.baseline[poolNum])
*b = (*b)[:n]
return *b
}
// Put adds given buffer to the pool.
func (p *BufferPool) Put(b []byte) {
if p == nil {
return
}
poolNum := p.poolNum(cap(b))
atomic.AddUint32(&p.put, 1)
p.pool[poolNum].Put(&b)
}
func (p *BufferPool) String() string {
if p == nil {
return "<nil>"
}
return fmt.Sprintf("BufferPool{B·%d G·%d P·%d <·%d =·%d >·%d M·%d}",
p.baseline, p.get, p.put, p.less, p.equal, p.greater, p.miss)
}
// NewBufferPool creates a new initialized 'buffer pool'.
func NewBufferPool(baseline int) *BufferPool {
if baseline <= 0 {
panic("baseline can't be <= 0")
}
bufPool := &BufferPool{
baseline: [...]int{baseline / 4, baseline / 2, baseline, baseline * 2, baseline * 4},
pool: [6]sync.Pool{
{
New: func() interface{} { return new([]byte) },
},
{
New: func() interface{} { return new([]byte) },
},
{
New: func() interface{} { return new([]byte) },
},
{
New: func() interface{} { return new([]byte) },
},
{
New: func() interface{} { return new([]byte) },
},
{
New: func() interface{} { return new([]byte) },
},
},
}
return bufPool
}