a53e38424c
* calculate maximum output length more precisely to avoid allocation in the append * use big.Int.Sign instead of needing bigZero name old time/op new time/op delta Base58Encode_5K-8 5.86ms ± 3% 5.79ms ± 2% -1.27% (p=0.035 n=9+10) Base58Encode_100K-8 2.23s ± 1% 2.23s ± 0% ~ (p=0.074 n=9+8) Base58Decode_5K-8 281µs ± 1% 282µs ± 1% ~ (p=0.720 n=9+10) Base58Decode_100K-8 89.4ms ± 7% 88.3ms ± 7% ~ (p=0.123 n=10+10) name old speed new speed delta Base58Encode_5K-8 854kB/s ± 3% 864kB/s ± 2% ~ (p=0.134 n=9+10) Base58Encode_100K-8 40.0kB/s ± 0% 40.0kB/s ± 0% ~ (all equal) Base58Decode_5K-8 24.3MB/s ± 1% 24.2MB/s ± 1% ~ (p=0.644 n=9+10) Base58Decode_100K-8 1.53MB/s ± 7% 1.55MB/s ± 7% ~ (p=0.218 n=10+10) name old alloc/op new alloc/op delta Base58Encode_5K-8 28.7kB ± 0% 19.2kB ± 0% -33.03% (p=0.000 n=10+10) Base58Encode_100K-8 557kB ± 0% 385kB ± 0% -30.88% (p=0.000 n=10+10) Base58Decode_5K-8 349kB ± 0% 349kB ± 0% ~ (all equal) Base58Decode_100K-8 133MB ± 0% 133MB ± 0% ~ (p=0.183 n=10+10) name old allocs/op new allocs/op delta Base58Encode_5K-8 5.00 ± 0% 4.00 ± 0% -20.00% (p=0.000 n=10+10) Base58Encode_100K-8 5.00 ± 0% 4.00 ± 0% -20.00% (p=0.000 n=10+10) Base58Decode_5K-8 129 ± 0% 129 ± 0% ~ (all equal) Base58Decode_100K-8 2.51k ± 0% 2.51k ± 0% ~ (p=0.321 n=10+10) When Go 1.16 is released, performance will improve significantly due to improvements to math/big.Int's division implementation.
138 lines
3.2 KiB
Go
138 lines
3.2 KiB
Go
// Copyright (c) 2013-2015 The btcsuite developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package base58
|
|
|
|
import (
|
|
"math/big"
|
|
)
|
|
|
|
//go:generate go run genalphabet.go
|
|
|
|
var bigRadix = [...]*big.Int{
|
|
big.NewInt(0),
|
|
big.NewInt(58),
|
|
big.NewInt(58 * 58),
|
|
big.NewInt(58 * 58 * 58),
|
|
big.NewInt(58 * 58 * 58 * 58),
|
|
big.NewInt(58 * 58 * 58 * 58 * 58),
|
|
big.NewInt(58 * 58 * 58 * 58 * 58 * 58),
|
|
big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58),
|
|
big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58),
|
|
big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58),
|
|
bigRadix10,
|
|
}
|
|
|
|
var bigRadix10 = big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58) // 58^10
|
|
|
|
// Decode decodes a modified base58 string to a byte slice.
|
|
func Decode(b string) []byte {
|
|
answer := big.NewInt(0)
|
|
scratch := new(big.Int)
|
|
|
|
// Calculating with big.Int is slow for each iteration.
|
|
// x += b58[b[i]] * j
|
|
// j *= 58
|
|
//
|
|
// Instead we can try to do as much calculations on int64.
|
|
// We can represent a 10 digit base58 number using an int64.
|
|
//
|
|
// Hence we'll try to convert 10, base58 digits at a time.
|
|
// The rough idea is to calculate `t`, such that:
|
|
//
|
|
// t := b58[b[i+9]] * 58^9 ... + b58[b[i+1]] * 58^1 + b58[b[i]] * 58^0
|
|
// x *= 58^10
|
|
// x += t
|
|
//
|
|
// Of course, in addition, we'll need to handle boundary condition when `b` is not multiple of 58^10.
|
|
// In that case we'll use the bigRadix[n] lookup for the appropriate power.
|
|
for t := b; len(t) > 0; {
|
|
n := len(t)
|
|
if n > 10 {
|
|
n = 10
|
|
}
|
|
|
|
total := uint64(0)
|
|
for _, v := range t[:n] {
|
|
tmp := b58[v]
|
|
if tmp == 255 {
|
|
return []byte("")
|
|
}
|
|
total = total*58 + uint64(tmp)
|
|
}
|
|
|
|
answer.Mul(answer, bigRadix[n])
|
|
scratch.SetUint64(total)
|
|
answer.Add(answer, scratch)
|
|
|
|
t = t[n:]
|
|
}
|
|
|
|
tmpval := answer.Bytes()
|
|
|
|
var numZeros int
|
|
for numZeros = 0; numZeros < len(b); numZeros++ {
|
|
if b[numZeros] != alphabetIdx0 {
|
|
break
|
|
}
|
|
}
|
|
flen := numZeros + len(tmpval)
|
|
val := make([]byte, flen)
|
|
copy(val[numZeros:], tmpval)
|
|
|
|
return val
|
|
}
|
|
|
|
// Encode encodes a byte slice to a modified base58 string.
|
|
func Encode(b []byte) string {
|
|
x := new(big.Int)
|
|
x.SetBytes(b)
|
|
|
|
// maximum length of output is log58(2^(8*len(b))) == len(b) * 8 / log(58)
|
|
maxlen := int(float64(len(b))*1.365658237309761) + 1
|
|
answer := make([]byte, 0, maxlen)
|
|
mod := new(big.Int)
|
|
for x.Sign() > 0 {
|
|
// Calculating with big.Int is slow for each iteration.
|
|
// x, mod = x / 58, x % 58
|
|
//
|
|
// Instead we can try to do as much calculations on int64.
|
|
// x, mod = x / 58^10, x % 58^10
|
|
//
|
|
// Which will give us mod, which is 10 digit base58 number.
|
|
// We'll loop that 10 times to convert to the answer.
|
|
|
|
x.DivMod(x, bigRadix10, mod)
|
|
if x.Sign() == 0 {
|
|
// When x = 0, we need to ensure we don't add any extra zeros.
|
|
m := mod.Int64()
|
|
for m > 0 {
|
|
answer = append(answer, alphabet[m%58])
|
|
m /= 58
|
|
}
|
|
} else {
|
|
m := mod.Int64()
|
|
for i := 0; i < 10; i++ {
|
|
answer = append(answer, alphabet[m%58])
|
|
m /= 58
|
|
}
|
|
}
|
|
}
|
|
|
|
// leading zero bytes
|
|
for _, i := range b {
|
|
if i != 0 {
|
|
break
|
|
}
|
|
answer = append(answer, alphabetIdx0)
|
|
}
|
|
|
|
// reverse
|
|
alen := len(answer)
|
|
for i := 0; i < alen/2; i++ {
|
|
answer[i], answer[alen-1-i] = answer[alen-1-i], answer[i]
|
|
}
|
|
|
|
return string(answer)
|
|
}
|