@@ -3,7 +3,6 @@ package ircutils
33import (
44 "encoding/base64"
55 "errors"
6- "strings"
76)
87
98var (
@@ -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.
4949type 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).
5656func 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.
7070func (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.
97108func (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}
0 commit comments