You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
142 lines
3.3 KiB
142 lines
3.3 KiB
// 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] { |
|
if v > 255 { |
|
return []byte("") |
|
} |
|
|
|
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, Ciphers[m%58]) |
|
m /= 58 |
|
} |
|
} else { |
|
m := mod.Int64() |
|
for i := 0; i < 10; i++ { |
|
answer = append(answer, Ciphers[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) |
|
}
|
|
|