forked from valyala/gozstd
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmanaged_buffer.go
More file actions
145 lines (122 loc) · 3.57 KB
/
managed_buffer.go
File metadata and controls
145 lines (122 loc) · 3.57 KB
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
136
137
138
139
140
141
142
143
144
145
package gozstd
import (
"runtime"
"sync/atomic"
)
// ManagedBuffer is a buffer that automatically returns to the pool when garbage collected.
// This helps prevent buffer leaks when users forget to return buffers.
type ManagedBuffer struct {
data []byte
returned uint32 // atomic flag to prevent double-return
}
// NewManagedBuffer creates a new managed buffer with at least the specified capacity.
// The buffer will automatically return to the pool when garbage collected.
func NewManagedBuffer(minCapacity int) *ManagedBuffer {
mb := &ManagedBuffer{
data: GetBuffer(minCapacity),
returned: 0,
}
// Set finalizer for automatic cleanup
runtime.SetFinalizer(mb, (*ManagedBuffer).autoReturn)
return mb
}
// Bytes returns the underlying byte slice.
// The slice should not be used after Release() is called.
func (mb *ManagedBuffer) Bytes() []byte {
if atomic.LoadUint32(&mb.returned) != 0 {
return nil
}
return mb.data
}
// Len returns the length of the buffer.
func (mb *ManagedBuffer) Len() int {
if atomic.LoadUint32(&mb.returned) != 0 {
return 0
}
return len(mb.data)
}
// Cap returns the capacity of the buffer.
func (mb *ManagedBuffer) Cap() int {
if atomic.LoadUint32(&mb.returned) != 0 {
return 0
}
return cap(mb.data)
}
// Resize resizes the buffer to the specified length.
// If the new length is greater than capacity, a new buffer is allocated.
func (mb *ManagedBuffer) Resize(newLen int) {
if atomic.LoadUint32(&mb.returned) != 0 {
return
}
if newLen > cap(mb.data) {
// Need a larger buffer
newBuf := GetBuffer(newLen)
copy(newBuf, mb.data)
// Return old buffer to pool
PutBuffer(mb.data)
mb.data = newBuf[:newLen]
} else {
mb.data = mb.data[:newLen]
}
}
// Append appends data to the buffer, growing it if necessary.
func (mb *ManagedBuffer) Append(p []byte) {
if atomic.LoadUint32(&mb.returned) != 0 {
return
}
oldLen := len(mb.data)
newLen := oldLen + len(p)
if newLen > cap(mb.data) {
// Need a larger buffer
newBuf := GetBuffer(newLen)
copy(newBuf, mb.data)
// Return old buffer to pool
PutBuffer(mb.data)
mb.data = newBuf[:newLen]
} else {
mb.data = mb.data[:newLen]
}
copy(mb.data[oldLen:], p)
}
// Reset resets the buffer to zero length but keeps the capacity.
func (mb *ManagedBuffer) Reset() {
if atomic.LoadUint32(&mb.returned) != 0 {
return
}
mb.data = mb.data[:0]
}
// Release manually returns the buffer to the pool.
// The buffer should not be used after calling Release.
// This method is safe to call multiple times.
func (mb *ManagedBuffer) Release() {
if !atomic.CompareAndSwapUint32(&mb.returned, 0, 1) {
return // Already returned
}
// Clear finalizer since we're manually releasing
runtime.SetFinalizer(mb, nil)
// Return buffer to pool
PutBuffer(mb.data)
mb.data = nil
}
// autoReturn is called by the garbage collector to automatically return the buffer.
func (mb *ManagedBuffer) autoReturn() {
if atomic.LoadUint32(&mb.returned) == 0 {
mb.Release()
}
}
// GetManagedBuffer is a convenience function that returns a managed buffer.
func GetManagedBuffer(minCapacity int) *ManagedBuffer {
return NewManagedBuffer(minCapacity)
}
// GetManagedCompressBuffer returns a managed buffer suitable for compression.
func GetManagedCompressBuffer(inputSize int) *ManagedBuffer {
estimatedSize := inputSize + (inputSize >> 8) + 64
return NewManagedBuffer(estimatedSize)
}
// GetManagedDecompressBuffer returns a managed buffer suitable for decompression.
func GetManagedDecompressBuffer(hint int) *ManagedBuffer {
if hint <= 0 {
hint = 64 * 1024 // 64KB default
}
return NewManagedBuffer(hint)
}