diff --git a/base58/base58.go b/base58/base58.go index 19a72de..1448dda 100644 --- a/base58/base58.go +++ b/base58/base58.go @@ -11,6 +11,7 @@ import ( //go:generate go run genalphabet.go var bigRadix = big.NewInt(58) +var bigRadix10 = big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58) // 58^10 var bigZero = big.NewInt(0) // Decode decodes a modified base58 string to a byte slice. @@ -51,10 +52,32 @@ func Encode(b []byte) string { x.SetBytes(b) answer := make([]byte, 0, len(b)*136/100) + mod := new(big.Int) for x.Cmp(bigZero) > 0 { - mod := new(big.Int) - x.DivMod(x, bigRadix, mod) - answer = append(answer, alphabet[mod.Int64()]) + // 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.Cmp(bigZero) == 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 diff --git a/base58/base58bench_test.go b/base58/base58bench_test.go index 2ab8fca..04c4a00 100644 --- a/base58/base58bench_test.go +++ b/base58/base58bench_test.go @@ -11,25 +11,37 @@ import ( "github.com/btcsuite/btcutil/base58" ) -func BenchmarkBase58Encode(b *testing.B) { - b.StopTimer() - data := bytes.Repeat([]byte{0xff}, 5000) - b.SetBytes(int64(len(data))) - b.StartTimer() +var ( + raw5k = bytes.Repeat([]byte{0xff}, 5000) + raw100k = bytes.Repeat([]byte{0xff}, 100*1000) + encoded5k = base58.Encode(raw5k) + encoded100k = base58.Encode(raw100k) +) +func BenchmarkBase58Encode_5K(b *testing.B) { + b.SetBytes(int64(len(raw5k))) for i := 0; i < b.N; i++ { - base58.Encode(data) + base58.Encode(raw5k) } } -func BenchmarkBase58Decode(b *testing.B) { - b.StopTimer() - data := bytes.Repeat([]byte{0xff}, 5000) - encoded := base58.Encode(data) - b.SetBytes(int64(len(encoded))) - b.StartTimer() - +func BenchmarkBase58Encode_100K(b *testing.B) { + b.SetBytes(int64(len(raw100k))) for i := 0; i < b.N; i++ { - base58.Decode(encoded) + base58.Encode(raw100k) + } +} + +func BenchmarkBase58Decode_5K(b *testing.B) { + b.SetBytes(int64(len(encoded5k))) + for i := 0; i < b.N; i++ { + base58.Decode(encoded5k) + } +} + +func BenchmarkBase58Decode_100K(b *testing.B) { + b.SetBytes(int64(len(encoded100k))) + for i := 0; i < b.N; i++ { + base58.Decode(encoded100k) } }