Commit e90e7a59 authored by Daniel Martí's avatar Daniel Martí

encoding/base32: simplify and speed up decoder

First, we can lift the enc.decodeMap nil check out of the loop.

Second, we can make it clear to the compiler that 'in := src[0]' doesn't
need a bounds check, by making len(src)==0 a single if check that always
stops the loop. This is by far the largest speed-up.

Third, we can use a dst slice index instead of reslicing dst, which
removes work from the loop body.

While at it, we can merge the two 'switch dlen' pieces of code, which
simplifies the code and doesn't affect performance.

name            old time/op    new time/op    delta
DecodeString-8    80.2µs ± 0%    67.5µs ± 0%  -15.81%  (p=0.002 n=6+6)

name            old speed      new speed      delta
DecodeString-8   163MB/s ± 0%   194MB/s ± 0%  +18.78%  (p=0.002 n=6+6)

Change-Id: Iefeaae94c03453f8760452b1da706a77b3522718
Reviewed-on: https://go-review.googlesource.com/c/154422
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarBrad Fitzpatrick <bradfitz@golang.org>
parent da50e10c
...@@ -284,7 +284,12 @@ func (e CorruptInputError) Error() string { ...@@ -284,7 +284,12 @@ func (e CorruptInputError) Error() string {
// additional data is an error. This method assumes that src has been // additional data is an error. This method assumes that src has been
// stripped of all supported whitespace ('\r' and '\n'). // stripped of all supported whitespace ('\r' and '\n').
func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) { func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
// Lift the nil check outside of the loop.
_ = enc.decodeMap
dsti := 0
olen := len(src) olen := len(src)
for len(src) > 0 && !end { for len(src) > 0 && !end {
// Decode quantum using the base32 alphabet // Decode quantum using the base32 alphabet
var dbuf [8]byte var dbuf [8]byte
...@@ -292,17 +297,15 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) { ...@@ -292,17 +297,15 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
for j := 0; j < 8; { for j := 0; j < 8; {
// We have reached the end and are missing padding if len(src) == 0 {
if len(src) == 0 && enc.padChar != NoPadding { if enc.padChar != NoPadding {
return n, false, CorruptInputError(olen - len(src) - j) // We have reached the end and are missing padding
} return n, false, CorruptInputError(olen - len(src) - j)
}
// We have reached the end and are not expecing any padding // We have reached the end and are not expecing any padding
if len(src) == 0 && enc.padChar == NoPadding {
dlen, end = j, true dlen, end = j, true
break break
} }
in := src[0] in := src[0]
src = src[1:] src = src[1:]
if in == byte(enc.padChar) && j >= 2 && len(src) < 8 { if in == byte(enc.padChar) && j >= 2 && len(src) < 8 {
...@@ -339,37 +342,26 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) { ...@@ -339,37 +342,26 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
// quantum // quantum
switch dlen { switch dlen {
case 8: case 8:
dst[4] = dbuf[6]<<5 | dbuf[7] dst[dsti+4] = dbuf[6]<<5 | dbuf[7]
n++
fallthrough fallthrough
case 7: case 7:
dst[3] = dbuf[4]<<7 | dbuf[5]<<2 | dbuf[6]>>3 dst[dsti+3] = dbuf[4]<<7 | dbuf[5]<<2 | dbuf[6]>>3
n++
fallthrough fallthrough
case 5: case 5:
dst[2] = dbuf[3]<<4 | dbuf[4]>>1 dst[dsti+2] = dbuf[3]<<4 | dbuf[4]>>1
n++
fallthrough fallthrough
case 4: case 4:
dst[1] = dbuf[1]<<6 | dbuf[2]<<1 | dbuf[3]>>4 dst[dsti+1] = dbuf[1]<<6 | dbuf[2]<<1 | dbuf[3]>>4
n++
fallthrough fallthrough
case 2: case 2:
dst[0] = dbuf[0]<<3 | dbuf[1]>>2 dst[dsti+0] = dbuf[0]<<3 | dbuf[1]>>2
} n++
if !end {
dst = dst[5:]
}
switch dlen {
case 2:
n += 1
case 4:
n += 2
case 5:
n += 3
case 7:
n += 4
case 8:
n += 5
} }
dsti += 5
} }
return n, end, nil return n, end, nil
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment