Skip to content

Commit d7467f5

Browse files
committed
store read size with the buffer; helpers
1 parent f34bee9 commit d7467f5

34 files changed

Lines changed: 604 additions & 308 deletions

buffer/buffer.go

Lines changed: 69 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -29,83 +29,104 @@
2929

3030
package buffer
3131

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"
4333

4434
// Recycler holds state necessary for a correct Buffer return to its originating Source
4535
type Recycler interface {
4636
Recycle(*Buffer)
4737
}
4838

4939
// 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.
5141
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.
5247
data []byte
5348
recycler Recycler
5449
}
5550

56-
// New creates a standalone Buffer.
51+
// New creates Buffer referencing the provided Recycler.
5752
func New(b []byte, recycler Recycler) *Buffer {
5853
return &Buffer{data: b, recycler: recycler}
5954
}
6055

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.
6257
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
6474
}
6575

66-
// Data returns the full underlying byte slice of the Buffer.
67-
func (b *Buffer) Data() []byte {
76+
func (b *Buffer) Bytes() []byte {
6877
return b.data
6978
}
7079

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]
7885
}
7986

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)))
86105
}
106+
Release(b)
107+
return bb, nil
87108
}
109+
b.data = b.data[:size]
110+
return b, nil
88111
}
89112

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)
101123
}
102-
b := a.Buffer.Data()[a.watermark : a.watermark+size]
103-
a.watermark += size
104-
return b
105124
}
106125

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+
}
111132
}

buffer/constants.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/* SPDX-License-Identifier: MIT
2+
*
3+
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
4+
*/
5+
package buffer
6+
7+
const (
8+
MaxMessageSize = MaxSegmentSize
9+
)

buffer/constants_android.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//go:build android
2+
3+
/* SPDX-License-Identifier: MIT
4+
*
5+
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
6+
*/
7+
8+
package buffer
9+
10+
const (
11+
MaxSegmentSize = 2200
12+
MaxBytesPerSource = 4096 * MaxSegmentSize
13+
)

buffer/constants_default.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//go:build !android && !ios && !windows
2+
3+
/* SPDX-License-Identifier: MIT
4+
*
5+
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
6+
*/
7+
8+
package buffer
9+
10+
const (
11+
MaxSegmentSize = (1 << 16) - 1 // largest possible UDP datagram
12+
MaxBytesPerSource = 0 // Disable and allow for infinite memory growth
13+
)

buffer/constants_ios.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//go:build ios
2+
3+
/* SPDX-License-Identifier: MIT
4+
*
5+
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
6+
*/
7+
8+
package buffer
9+
10+
// Fit within memory limits for iOS's Network Extension API, which has stricter requirements.
11+
// These are vars instead of consts, because heavier network extensions might want to reduce
12+
// them further.
13+
var (
14+
MaxBytesPerSource = 1024 * MaxSegmentSize
15+
)
16+
17+
const MaxSegmentSize = 1700

buffer/constants_windows.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//go:build windows
2+
3+
/* SPDX-License-Identifier: MIT
4+
*
5+
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
6+
*/
7+
8+
package buffer
9+
10+
const (
11+
MaxSegmentSize = 2048 - 32 // largest possible UDP datagram
12+
MaxBytesPerSource = 0 // Disable and allow for infinite memory growth
13+
)

buffer/pool.go

Lines changed: 0 additions & 59 deletions
This file was deleted.

0 commit comments

Comments
 (0)