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  }