Skip to content

Commit 36a8aad

Browse files
authored
likely pointless optimizations for SASLBuffer (#104)
* likely pointless optimizations for SASLBuffer * preallocate split response as well
1 parent 8895cdf commit 36a8aad

File tree

2 files changed

+27
-21
lines changed

2 files changed

+27
-21
lines changed

ircutils/sasl.go

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package ircutils
33
import (
44
"encoding/base64"
55
"errors"
6-
"strings"
76
)
87

98
var (
@@ -25,6 +24,7 @@ func EncodeSASLResponse(raw []byte) (result []string) {
2524
}
2625

2726
response := base64.StdEncoding.EncodeToString(raw)
27+
result = make([]string, 0, (len(response)/400)+1)
2828
lastLen := 0
2929
for len(response) > 0 {
3030
// TODO once we require go 1.21, this can be: lastLen = min(len(response), 400)
@@ -48,11 +48,11 @@ func EncodeSASLResponse(raw []byte) (result []string) {
4848
// Do not copy a SASLBuffer after first use.
4949
type SASLBuffer struct {
5050
maxLength int
51-
buffer strings.Builder
51+
buf []byte
5252
}
5353

5454
// NewSASLBuffer returns a new SASLBuffer. maxLength is the maximum amount of
55-
// base64'ed data to buffer (0 for no limit).
55+
// data to buffer (0 for no limit).
5656
func NewSASLBuffer(maxLength int) *SASLBuffer {
5757
result := new(SASLBuffer)
5858
result.Initialize(maxLength)
@@ -69,37 +69,43 @@ func (b *SASLBuffer) Initialize(maxLength int) {
6969
// response along with any decoding or protocol errors detected.
7070
func (b *SASLBuffer) Add(value string) (done bool, output []byte, err error) {
7171
if value == "+" {
72-
output, err = b.getAndReset()
73-
return true, output, err
72+
// total size is a multiple of 400 (possibly 0)
73+
output = b.buf
74+
b.Clear()
75+
return true, output, nil
7476
}
7577

7678
if len(value) > 400 {
77-
b.buffer.Reset()
79+
b.Clear()
7880
return true, nil, ErrSASLTooLong
7981
}
8082

81-
if b.maxLength != 0 && (b.buffer.Len()+len(value)) > b.maxLength {
82-
b.buffer.Reset()
83+
curLen := len(b.buf)
84+
chunkDecodedLen := base64.StdEncoding.DecodedLen(len(value))
85+
if b.maxLength != 0 && (curLen+chunkDecodedLen) > b.maxLength {
86+
b.Clear()
8387
return true, nil, ErrSASLLimitExceeded
8488
}
8589

86-
b.buffer.WriteString(value)
90+
// "append-make pattern" as in the bytes.Buffer implementation:
91+
b.buf = append(b.buf, make([]byte, chunkDecodedLen)...)
92+
n, err := base64.StdEncoding.Decode(b.buf[curLen:], []byte(value))
93+
b.buf = b.buf[0 : curLen+n]
94+
if err != nil {
95+
b.Clear()
96+
return true, nil, err
97+
}
8798
if len(value) < 400 {
88-
output, err = b.getAndReset()
89-
return true, output, err
99+
output = b.buf
100+
b.Clear()
101+
return true, output, nil
90102
} else {
91-
// 400 bytes, wait for continuation line or +
92103
return false, nil, nil
93104
}
94105
}
95106

96107
// Clear resets the buffer state.
97108
func (b *SASLBuffer) Clear() {
98-
b.buffer.Reset()
99-
}
100-
101-
func (b *SASLBuffer) getAndReset() (output []byte, err error) {
102-
output, err = base64.StdEncoding.DecodeString(b.buffer.String())
103-
b.buffer.Reset()
104-
return
109+
// we can't reuse this buffer in general since we may have returned it
110+
b.buf = nil
105111
}

ircutils/sasl_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func TestSplitResponse(t *testing.T) {
3131
}
3232

3333
func TestBuffer(t *testing.T) {
34-
b := NewSASLBuffer(1600)
34+
b := NewSASLBuffer(1200)
3535

3636
// less than 400 bytes
3737
done, output, err := b.Add("c2hpdmFyYW0Ac2hpdmFyYW0Ac2hpdmFyYW1wYXNzcGhyYXNl")
@@ -58,7 +58,7 @@ func TestBuffer(t *testing.T) {
5858
// a single +
5959
done, output, err = b.Add("+")
6060
assertEqual(done, true)
61-
assertEqual(len(output), 0)
61+
assertEqual(output, []byte(nil))
6262
assertEqual(err, nil)
6363

6464
// length limit

0 commit comments

Comments
 (0)