From b8121db2a38f928dbafc7a00901b6b26cf09962b Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 18 Nov 2016 02:12:08 -0500 Subject: [PATCH 1/3] Optimize DecodeString and Decode methods. In the DecodeString case it avoids unnecessary intermediate strings. In the Decode case it just avoids bytes.Map, which is likely slow due to having to make a function call for each character. --- base32.go | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/base32.go b/base32.go index 99db4a4..ebb794a 100644 --- a/base32.go +++ b/base32.go @@ -6,10 +6,8 @@ package base32 import ( - "bytes" "io" "strconv" - "strings" ) /* @@ -67,13 +65,6 @@ var HexEncoding = NewEncoding(encodeHex) var RawStdEncoding = NewEncoding(encodeStd).WithPadding(NoPadding) var RawHexEncoding = NewEncoding(encodeHex).WithPadding(NoPadding) -var removeNewlinesMapper = func(r rune) rune { - if r == '\r' || r == '\n' { - return -1 - } - return r -} - /* * Encoder */ @@ -344,17 +335,29 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) { // written. If src contains invalid base32 data, it will return the // number of bytes successfully written and CorruptInputError. // New line characters (\r and \n) are ignored. -func (enc *Encoding) Decode(dst, src []byte) (n int, err error) { - src = bytes.Map(removeNewlinesMapper, src) - n, _, err = enc.decode(dst, src) +func (enc *Encoding) Decode(dst, s []byte) (n int, err error) { + stripped := make([]byte, 0, len(s)) + for i := 0; i < len(s); i++ { + c := s[i] + if c != '\r' && c != '\n' { + stripped = append(stripped, c) + } + } + n, _, err = enc.decode(dst, stripped) return } // DecodeString returns the bytes represented by the base32 string s. func (enc *Encoding) DecodeString(s string) ([]byte, error) { - s = strings.Map(removeNewlinesMapper, s) + stripped := make([]byte, 0, len(s)) + for i := 0; i < len(s); i++ { + c := s[i] + if c != '\r' && c != '\n' { + stripped = append(stripped, c) + } + } dbuf := make([]byte, enc.DecodedLen(len(s))) - n, _, err := enc.decode(dbuf, []byte(s)) + n, _, err := enc.decode(dbuf, stripped) return dbuf[:n], err } From 06d804ae065adf02add8480a96b9c0e9e5d821bb Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 18 Nov 2016 20:42:37 -0500 Subject: [PATCH 2/3] Use "range" instead of indexes in Decode(). --- base32.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/base32.go b/base32.go index ebb794a..ffccfe6 100644 --- a/base32.go +++ b/base32.go @@ -337,8 +337,7 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) { // New line characters (\r and \n) are ignored. func (enc *Encoding) Decode(dst, s []byte) (n int, err error) { stripped := make([]byte, 0, len(s)) - for i := 0; i < len(s); i++ { - c := s[i] + for _, c := range s { if c != '\r' && c != '\n' { stripped = append(stripped, c) } From f4e78d0b9e1b60fa4a229b3e356f070ca218ae7c Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Sat, 19 Nov 2016 18:14:49 -0500 Subject: [PATCH 3/3] Only allocate a single string/[]byte in DecodeString. Do this by calling the internal decode using the same byte array for the source and dest. --- base32.go | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/base32.go b/base32.go index ffccfe6..1ff1422 100644 --- a/base32.go +++ b/base32.go @@ -336,6 +336,7 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) { // number of bytes successfully written and CorruptInputError. // New line characters (\r and \n) are ignored. func (enc *Encoding) Decode(dst, s []byte) (n int, err error) { + // FIXME: if dst is the same as s use decodeInPlace stripped := make([]byte, 0, len(s)) for _, c := range s { if c != '\r' && c != '\n' { @@ -346,18 +347,27 @@ func (enc *Encoding) Decode(dst, s []byte) (n int, err error) { return } +func (enc *Encoding) decodeInPlace(strb []byte) (n int, err error) { + off := 0 + for _, b := range strb { + if b == '\n' || b == '\r' { + continue + } + strb[off] = b + off++ + } + n, _, err = enc.decode(strb, strb[:off]) + return +} + // DecodeString returns the bytes represented by the base32 string s. func (enc *Encoding) DecodeString(s string) ([]byte, error) { - stripped := make([]byte, 0, len(s)) - for i := 0; i < len(s); i++ { - c := s[i] - if c != '\r' && c != '\n' { - stripped = append(stripped, c) - } + strb := []byte(s) + n, err := enc.decodeInPlace(strb) + if err != nil { + return nil, err } - dbuf := make([]byte, enc.DecodedLen(len(s))) - n, _, err := enc.decode(dbuf, stripped) - return dbuf[:n], err + return strb[:n], nil } type decoder struct {