Switch from string to []byte slices

The way I'm using the base58 required me to do a lot conversions between
[]byte slices and strings. Not anymore.

Also switching to []byte slices allowed some nice performance
improvements.

Before, with strings:

    BenchmarkEncode-4        3000000               511 ns/op
    BenchmarkDecode-4        5000000               300 ns/op

After, with []byte slices:

    BenchmarkEncode-4        5000000               256 ns/op
    BenchmarkDecode-4       20000000               107 ns/op
This commit is contained in:
2016-07-03 11:14:38 +01:00
parent 785190bf71
commit f8ea86748a
2 changed files with 28 additions and 16 deletions

View File

@@ -1,38 +1,41 @@
package base58 package base58
import ( import (
"bytes"
"errors" "errors"
"strings"
) )
// Alphabet is the default alphabet. // Alphabet is the default alphabet.
const Alphabet = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ" var Alphabet = []byte(
const base = len(Alphabet) "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ",
)
var base = len(Alphabet)
var errInvalidBase58 = errors.New("invalid base58") var errInvalidBase58 = errors.New("invalid base58")
// Encode converts a base10 integer to a base58 string using the default // Encode converts a base10 integer to a base58 string using the default
// alphabet. // alphabet.
func Encode(num int) string { func Encode(num int) []byte {
str := "" str := []byte{}
for num >= base { for num >= base {
mod := num % base mod := num % base
str = string(Alphabet[mod]) + str str = prepend(str, Alphabet[mod])
num = (num - mod) / base num = (num - mod) / base
} }
return string(Alphabet[num]) + str return prepend(str, Alphabet[num])
} }
// Decode converts a base58 string to a base10 integer using the default // Decode converts a base58 string to a base10 integer using the default
// alphabet. // alphabet.
func Decode(str string) (int, error) { func Decode(str []byte) (int, error) {
num := 0 num := 0
multi := 1 multi := 1
for i := len(str); i > 0; i-- { for i := len(str); i > 0; i-- {
char := string(str[i-1]) char := str[i-1]
index := strings.Index(Alphabet, char) index := bytes.IndexByte(Alphabet, char)
if index == -1 { if index == -1 {
return -1, errInvalidBase58 return -1, errInvalidBase58
} }
@@ -42,3 +45,10 @@ func Decode(str string) (int, error) {
return num, nil return num, nil
} }
func prepend(slice []byte, elem byte) []byte {
slice = append(slice, byte(0))
copy(slice[1:], slice)
slice[0] = elem
return slice
}

View File

@@ -186,26 +186,28 @@ type Base58Suite struct {
// Tests // Tests
func (s *Base58Suite) TestAlphabet() { func (s *Base58Suite) TestAlphabet() {
expected := "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ" s.Equal(
s.Equal(expected, Alphabet) []byte("123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"),
Alphabet,
)
} }
func (s *Base58Suite) TestEncode() { func (s *Base58Suite) TestEncode() {
for str, num := range examples { for str, num := range examples {
result := Encode(num) result := Encode(num)
s.Equal(str, result) s.Equal([]byte(str), result)
} }
} }
func (s *Base58Suite) TestDecode() { func (s *Base58Suite) TestDecode() {
for str, num := range examples { for str, num := range examples {
result, _ := Decode(str) result, _ := Decode([]byte(str))
s.Equal(num, result) s.Equal(num, result)
} }
} }
func (s *Base58Suite) TestDecodeError() { func (s *Base58Suite) TestDecodeError() {
result, err := Decode("invalid@base58.string") result, err := Decode([]byte("invalid@base58.string"))
s.Equal(-1, result) s.Equal(-1, result)
s.Equal("invalid base58", err.Error()) s.Equal("invalid base58", err.Error())
@@ -227,6 +229,6 @@ func BenchmarkEncode(b *testing.B) {
func BenchmarkDecode(b *testing.B) { func BenchmarkDecode(b *testing.B) {
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
_, _ = Decode("6hKMCS") _, _ = Decode([]byte("6hKMCS"))
} }
} }