Optimize base58 decoding.

This change introduces an autogenerated base58 digit table to remove
the need to find the index of a character in the modified base58
alphabet each time.  Additionally, it removes some unnecessary big
integer allocations to cut down on the GC churn.

Before:
BenchmarkBase58Encode         20          64998995 ns/op           0.08 MB/s
BenchmarkBase58Decode         50          35965928 ns/op           0.19 MB/s

Now:
BenchmarkBase58Encode         20          64644351 ns/op           0.08 MB/s
BenchmarkBase58Decode        200           7914748 ns/op           0.86 MB/s
This commit is contained in:
Josh Rickmar 2015-01-24 13:49:08 -05:00
parent 9dcef5b30f
commit dca623d4ef
3 changed files with 138 additions and 13 deletions

49
base58/alphabet.go Normal file
View file

@ -0,0 +1,49 @@
// Copyright (c) 2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
// AUTOGENERATED by genalphabet.go; do not edit.
package base58
const (
// alphabet is the modified base58 alphabet used by Bitcoin.
alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
alphabetIdx0 = '1'
)
var b58 = [256]byte{
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 0, 1, 2, 3, 4, 5, 6,
7, 8, 255, 255, 255, 255, 255, 255,
255, 9, 10, 11, 12, 13, 14, 15,
16, 255, 17, 18, 19, 20, 21, 255,
22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 255, 255, 255, 255, 255,
255, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 255, 44, 45, 46,
47, 48, 49, 50, 51, 52, 53, 54,
55, 56, 57, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
}

View file

@ -1,4 +1,4 @@
// Copyright (c) 2013-2014 Conformal Systems LLC. // Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC // Use of this source code is governed by an ISC
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -6,11 +6,9 @@ package base58
import ( import (
"math/big" "math/big"
"strings"
) )
// alphabet is the modified base58 alphabet used by Bitcoin. //go:generate go run genalphabet.go
const alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
var bigRadix = big.NewInt(58) var bigRadix = big.NewInt(58)
var bigZero = big.NewInt(0) var bigZero = big.NewInt(0)
@ -20,16 +18,15 @@ func Decode(b string) []byte {
answer := big.NewInt(0) answer := big.NewInt(0)
j := big.NewInt(1) j := big.NewInt(1)
scratch := new(big.Int)
for i := len(b) - 1; i >= 0; i-- { for i := len(b) - 1; i >= 0; i-- {
tmp := strings.IndexAny(alphabet, string(b[i])) tmp := b58[b[i]]
if tmp == -1 { if tmp == 255 {
return []byte("") return []byte("")
} }
idx := big.NewInt(int64(tmp)) scratch.SetInt64(int64(tmp))
tmp1 := big.NewInt(0) scratch.Mul(j, scratch)
tmp1.Mul(j, idx) answer.Add(answer, scratch)
answer.Add(answer, tmp1)
j.Mul(j, bigRadix) j.Mul(j, bigRadix)
} }
@ -37,7 +34,7 @@ func Decode(b string) []byte {
var numZeros int var numZeros int
for numZeros = 0; numZeros < len(b); numZeros++ { for numZeros = 0; numZeros < len(b); numZeros++ {
if b[numZeros] != alphabet[0] { if b[numZeros] != alphabetIdx0 {
break break
} }
} }
@ -65,7 +62,7 @@ func Encode(b []byte) string {
if i != 0 { if i != 0 {
break break
} }
answer = append(answer, alphabet[0]) answer = append(answer, alphabetIdx0)
} }
// reverse // reverse

79
base58/genalphabet.go Normal file
View file

@ -0,0 +1,79 @@
// Copyright (c) 2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
//+build ignore
package main
import (
"bytes"
"io"
"log"
"os"
"strconv"
)
var (
start = []byte(`// Copyright (c) 2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
// AUTOGENERATED by genalphabet.go; do not edit.
package base58
const (
// alphabet is the modified base58 alphabet used by Bitcoin.
alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
alphabetIdx0 = '1'
)
var b58 = [256]byte{`)
end = []byte(`}`)
alphabet = []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")
tab = []byte("\t")
invalid = []byte("255")
comma = []byte(",")
space = []byte(" ")
nl = []byte("\n")
)
func write(w io.Writer, b []byte) {
_, err := w.Write(b)
if err != nil {
log.Fatal(err)
}
}
func main() {
fi, err := os.Create("alphabet.go")
if err != nil {
log.Fatal(err)
}
defer fi.Close()
write(fi, start)
write(fi, nl)
for i := byte(0); i < 32; i++ {
write(fi, tab)
for j := byte(0); j < 8; j++ {
idx := bytes.IndexByte(alphabet, i*8+j)
if idx == -1 {
write(fi, invalid)
} else {
write(fi, strconv.AppendInt(nil, int64(idx), 10))
}
write(fi, comma)
if j != 7 {
write(fi, space)
}
}
write(fi, nl)
}
write(fi, end)
write(fi, nl)
}