github.com/mavryk-network/mvgo@v1.19.9/base58/base58.go (about) 1 // Copyright (c) 2020-2021 Blockwatch Data Inc. 2 // Copyright (c) 2013-2015 The btcsuite developers 3 // Use of this source code is governed by an ISC 4 // license that can be found in the LICENSE file. 5 6 package base58 7 8 import ( 9 "math/big" 10 "sync" 11 ) 12 13 //go:generate go run genalphabet.go 14 15 var bigIntPool = &sync.Pool{ 16 New: func() interface{} { return big.NewInt(0) }, 17 } 18 19 var bigRadix = [...]*big.Int{ 20 big.NewInt(0), 21 big.NewInt(58), 22 big.NewInt(58 * 58), 23 big.NewInt(58 * 58 * 58), 24 big.NewInt(58 * 58 * 58 * 58), 25 big.NewInt(58 * 58 * 58 * 58 * 58), 26 big.NewInt(58 * 58 * 58 * 58 * 58 * 58), 27 big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58), 28 big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58), 29 big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58), 30 bigRadix10, 31 } 32 33 var bigRadix10 = big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58) // 58^10 34 35 // Decode decodes a modified base58 string to a byte slice. 36 func Decode(b string, buf []byte) []byte { 37 answerIf := bigIntPool.Get() 38 scratchIf := bigIntPool.Get() 39 answer := answerIf.(*big.Int).SetInt64(0) 40 scratch := scratchIf.(*big.Int).SetInt64(0) 41 42 // answer := big.NewInt(0) 43 // scratch := new(big.Int) 44 45 // Calculating with big.Int is slow for each iteration. 46 // x += b58[b[i]] * j 47 // j *= 58 48 // 49 // Instead we can try to do as much calculations on int64. 50 // We can represent a 10 digit base58 number using an int64. 51 // 52 // Hence we'll try to convert 10, base58 digits at a time. 53 // The rough idea is to calculate `t`, such that: 54 // 55 // t := b58[b[i+9]] * 58^9 ... + b58[b[i+1]] * 58^1 + b58[b[i]] * 58^0 56 // x *= 58^10 57 // x += t 58 // 59 // Of course, in addition, we'll need to handle boundary condition when `b` is not multiple of 58^10. 60 // In that case we'll use the bigRadix[n] lookup for the appropriate power. 61 for t := b; len(t) > 0; { 62 n := len(t) 63 if n > 10 { 64 n = 10 65 } 66 67 total := uint64(0) 68 for _, v := range t[:n] { 69 tmp := b58[v%256] 70 if tmp == 255 { 71 return []byte("") 72 } 73 total = total*58 + uint64(tmp) 74 } 75 76 answer.Mul(answer, bigRadix[n]) 77 scratch.SetUint64(total) 78 answer.Add(answer, scratch) 79 80 t = t[n:] 81 } 82 83 tmpval := answer.Bytes() 84 85 var numZeros int 86 for numZeros = 0; numZeros < len(b); numZeros++ { 87 if b[numZeros] != alphabetIdx0 { 88 break 89 } 90 } 91 flen := numZeros + len(tmpval) 92 if buf == nil || cap(buf) < flen { 93 buf = make([]byte, flen) 94 } 95 buf = buf[:flen] 96 copy(buf[numZeros:], tmpval) 97 bigIntPool.Put(answerIf) 98 bigIntPool.Put(scratchIf) 99 100 return buf 101 } 102 103 // Encode encodes a byte slice to a modified base58 string. 104 func Encode(b []byte) string { 105 xi := bigIntPool.Get() 106 x := xi.(*big.Int).SetBytes(b) 107 108 // maximum length of output is log58(2^(8*len(b))) == len(b) * 8 / log(58) 109 maxlen := int(float64(len(b))*1.365658237309761) + 1 110 bufi := bufPool.Get() 111 answer := bufi.([]byte) 112 if cap(answer) < maxlen { 113 answer = make([]byte, 0, maxlen) 114 } 115 modi := bigIntPool.Get() 116 mod := modi.(*big.Int).SetInt64(0) 117 for x.Sign() > 0 { 118 // Calculating with big.Int is slow for each iteration. 119 // x, mod = x / 58, x % 58 120 // 121 // Instead we can try to do as much calculations on int64. 122 // x, mod = x / 58^10, x % 58^10 123 // 124 // Which will give us mod, which is 10 digit base58 number. 125 // We'll loop that 10 times to convert to the answer. 126 127 x.DivMod(x, bigRadix10, mod) 128 if x.Sign() == 0 { 129 // When x = 0, we need to ensure we don't add any extra zeros. 130 m := mod.Int64() 131 for m > 0 { 132 answer = append(answer, alphabet[m%58]) 133 m /= 58 134 } 135 } else { 136 m := mod.Int64() 137 for i := 0; i < 10; i++ { 138 answer = append(answer, alphabet[m%58]) 139 m /= 58 140 } 141 } 142 } 143 bigIntPool.Put(xi) 144 bigIntPool.Put(modi) 145 146 // leading zero bytes 147 for _, i := range b { 148 if i != 0 { 149 break 150 } 151 answer = append(answer, alphabetIdx0) 152 } 153 154 // reverse 155 alen := len(answer) 156 for i := 0; i < alen/2; i++ { 157 answer[i], answer[alen-1-i] = answer[alen-1-i], answer[i] 158 } 159 160 res := string(answer) 161 bufPool.Put(bufi) 162 return res 163 }