|
29 | 29 |
|
30 | 30 | package buffer |
31 | 31 |
|
32 | | -const ( |
33 | | - MaxMessageSize = (1 << 16) - 1 // largest possible UDP datagram |
34 | | -) |
35 | | - |
36 | | -// Source produces new Buffers. |
37 | | -type Source interface { |
38 | | - |
39 | | - // Get returns a Buffer of at least the requested size. |
40 | | - // Implementations may chose to return error if the request can not be fulfilled. |
41 | | - Get(size int) (*Buffer, error) |
42 | | -} |
| 32 | +import "fmt" |
43 | 33 |
|
44 | 34 | // Recycler holds state necessary for a correct Buffer return to its originating Source |
45 | 35 | type Recycler interface { |
46 | 36 | Recycle(*Buffer) |
47 | 37 | } |
48 | 38 |
|
49 | 39 | // Buffer is a reusable slice of bytes of fixed length. |
50 | | -// The returned Data slice must not be retained past Release. |
| 40 | +// Buffer or its Data must not be retained past Release. |
51 | 41 | type Buffer struct { |
| 42 | + // Data len tracks valid payload + offset (offset passed out of band). |
| 43 | + // It starts equal to the requested size for new buffers, and can be adjusted. |
| 44 | + // Data is read-only, exposed for convenience but should never be assigned to. |
| 45 | + // Buffer methods maintain the invariant that Data[offset:len(Data)] is valid payload, |
| 46 | + // SetLen and Shift let callers adjust the offset and length of the valid payload as needed. |
52 | 47 | data []byte |
53 | 48 | recycler Recycler |
54 | 49 | } |
55 | 50 |
|
56 | | -// New creates a standalone Buffer. |
| 51 | +// New creates Buffer referencing the provided Recycler. |
57 | 52 | func New(b []byte, recycler Recycler) *Buffer { |
58 | 53 | return &Buffer{data: b, recycler: recycler} |
59 | 54 | } |
60 | 55 |
|
61 | | -// Make creates a standalone Buffer with a new byte slice of the requested size. |
| 56 | +// Make creates Buffer with a new byte slice of the requested size. |
62 | 57 | func Make(size int) *Buffer { |
63 | | - return &Buffer{data: make([]byte, size), recycler: nil} |
| 58 | + buf, _ := DefaultSource.Get(size) // fragment pool never errors |
| 59 | + return buf |
| 60 | +} |
| 61 | + |
| 62 | +// ShiftOffset moves the valid data in the Buffer from oldOffset to newOffset, and adjusts the length accordingly. |
| 63 | +func (b *Buffer) ShiftOffset(oldOffset, newOffset int) int { |
| 64 | + packetLen := len(b.data) - oldOffset |
| 65 | + if newOffset+packetLen > cap(b.data) || newOffset < 0 { |
| 66 | + return -1 |
| 67 | + } |
| 68 | + n := copy(b.data[newOffset:newOffset+packetLen], b.data[oldOffset:]) |
| 69 | + if n != packetLen { |
| 70 | + panic(fmt.Sprintf("short copy: %d != %d", n, packetLen)) |
| 71 | + } |
| 72 | + b.data = b.data[:newOffset+n] |
| 73 | + return n |
64 | 74 | } |
65 | 75 |
|
66 | | -// Data returns the full underlying byte slice of the Buffer. |
67 | | -func (b *Buffer) Data() []byte { |
| 76 | +func (b *Buffer) Bytes() []byte { |
68 | 77 | return b.data |
69 | 78 | } |
70 | 79 |
|
71 | | -// Release returns the Buffer to its originating Source for reuse. |
72 | | -// The Buffer must not be used after calling Release. |
73 | | -func (b *Buffer) Release() { |
74 | | - if b.recycler != nil { |
75 | | - clear(b.data) |
76 | | - b.recycler.Recycle(b) |
77 | | - } |
| 80 | +// SetLen sets the length of the valid data in the Buffer. |
| 81 | +// Intended to be used for truncating the valid data post read, |
| 82 | +// or extending post encryption. Does not check the capacity. |
| 83 | +func (b *Buffer) SetLen(l int) { |
| 84 | + b.data = b.data[:l] |
78 | 85 | } |
79 | 86 |
|
80 | | -// ReleaseAll calls Release on each non-nil Buffer in the slice, and sets the slice elements to nil. |
81 | | -func ReleaseAll(bs []*Buffer) { |
82 | | - for i := range bs { |
83 | | - if bs[i] != nil { |
84 | | - bs[i].Release() |
85 | | - bs[i] = nil |
| 87 | +// Ensure returns a Buffer of the requested len, with the valid data from the provided Buffer. |
| 88 | +// The returned Buffer may be the same as the provided Buffer if it has sufficient capacity, or a new Buffer otherwise. |
| 89 | +// Safe to call on a nil Buffer. |
| 90 | +func Ensure(b *Buffer, size int, src Source) (*Buffer, error) { |
| 91 | + if src == nil { |
| 92 | + src = DefaultSource |
| 93 | + } |
| 94 | + if b == nil { |
| 95 | + return src.Get(size) |
| 96 | + } |
| 97 | + if size > cap(b.data) { |
| 98 | + bb, err := src.Get(size) |
| 99 | + if err != nil { |
| 100 | + return nil, err |
| 101 | + } |
| 102 | + n := copy(bb.data, b.data) |
| 103 | + if n != len(b.data) { |
| 104 | + panic(fmt.Sprintf("short copy: %d != %d", n, len(b.data))) |
86 | 105 | } |
| 106 | + Release(b) |
| 107 | + return bb, nil |
87 | 108 | } |
| 109 | + b.data = b.data[:size] |
| 110 | + return b, nil |
88 | 111 | } |
89 | 112 |
|
90 | | -// Arena is a Buffer with an internal watermark for sequential allocations. |
91 | | -// FIXME Arena needs a graceful fallback on overflow. |
92 | | -type Arena struct { |
93 | | - *Buffer |
94 | | - watermark int |
95 | | -} |
96 | | - |
97 | | -// Get returns a slice of the Arena's Buffer of the requested size, and advances the watermark. |
98 | | -func (a *Arena) Get(size int) []byte { |
99 | | - if a.watermark+size > len(a.Buffer.Data()) { |
100 | | - panic("arena overflow") // or return a heap-allocated fallback |
| 113 | +// Release returns Buffer to its Source for reuse. |
| 114 | +// Safe to call on a nil Buffer. |
| 115 | +func Release(b *Buffer) { |
| 116 | + if b == nil { |
| 117 | + return |
| 118 | + } |
| 119 | + b.data = b.data[:cap(b.data)] |
| 120 | + clear(b.data) |
| 121 | + if b.recycler != nil { |
| 122 | + b.recycler.Recycle(b) |
101 | 123 | } |
102 | | - b := a.Buffer.Data()[a.watermark : a.watermark+size] |
103 | | - a.watermark += size |
104 | | - return b |
105 | 124 | } |
106 | 125 |
|
107 | | -// Flush resets the Arena's watermark to zero, and clears the valid data in the Buffer. |
108 | | -func (a *Arena) Flush() { |
109 | | - clear(a.Buffer.Data()[:a.watermark]) |
110 | | - a.watermark = 0 |
| 126 | +// ReleaseAll calls Release on each non-nil Buffer in the slice, and sets the slice elements to nil. |
| 127 | +func ReleaseAll(bs []*Buffer) { |
| 128 | + for i := range bs { |
| 129 | + Release(bs[i]) |
| 130 | + bs[i] = nil |
| 131 | + } |
111 | 132 | } |
0 commit comments