github.com/Asutorufa/yuhaiin@v0.3.6-0.20240502055049-7984da7023a0/pkg/utils/uuid/base58/base58.go (about)

     1  package base58
     2  
     3  import (
     4  	"crypto/sha256"
     5  	"errors"
     6  	"math/big"
     7  )
     8  
     9  const (
    10  	alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
    11  
    12  	alphabetIdx0 = '1'
    13  )
    14  
    15  var b58 = [256]byte{
    16  	255, 255, 255, 255, 255, 255, 255, 255,
    17  	255, 255, 255, 255, 255, 255, 255, 255,
    18  	255, 255, 255, 255, 255, 255, 255, 255,
    19  	255, 255, 255, 255, 255, 255, 255, 255,
    20  	255, 255, 255, 255, 255, 255, 255, 255,
    21  	255, 255, 255, 255, 255, 255, 255, 255,
    22  	255, 0, 1, 2, 3, 4, 5, 6,
    23  	7, 8, 255, 255, 255, 255, 255, 255,
    24  	255, 9, 10, 11, 12, 13, 14, 15,
    25  	16, 255, 17, 18, 19, 20, 21, 255,
    26  	22, 23, 24, 25, 26, 27, 28, 29,
    27  	30, 31, 32, 255, 255, 255, 255, 255,
    28  	255, 33, 34, 35, 36, 37, 38, 39,
    29  	40, 41, 42, 43, 255, 44, 45, 46,
    30  	47, 48, 49, 50, 51, 52, 53, 54,
    31  	55, 56, 57, 255, 255, 255, 255, 255,
    32  	255, 255, 255, 255, 255, 255, 255, 255,
    33  	255, 255, 255, 255, 255, 255, 255, 255,
    34  	255, 255, 255, 255, 255, 255, 255, 255,
    35  	255, 255, 255, 255, 255, 255, 255, 255,
    36  	255, 255, 255, 255, 255, 255, 255, 255,
    37  	255, 255, 255, 255, 255, 255, 255, 255,
    38  	255, 255, 255, 255, 255, 255, 255, 255,
    39  	255, 255, 255, 255, 255, 255, 255, 255,
    40  	255, 255, 255, 255, 255, 255, 255, 255,
    41  	255, 255, 255, 255, 255, 255, 255, 255,
    42  	255, 255, 255, 255, 255, 255, 255, 255,
    43  	255, 255, 255, 255, 255, 255, 255, 255,
    44  	255, 255, 255, 255, 255, 255, 255, 255,
    45  	255, 255, 255, 255, 255, 255, 255, 255,
    46  	255, 255, 255, 255, 255, 255, 255, 255,
    47  	255, 255, 255, 255, 255, 255, 255, 255,
    48  }
    49  
    50  var bigRadix = [...]*big.Int{
    51  	big.NewInt(0),
    52  	big.NewInt(58),
    53  	big.NewInt(58 * 58),
    54  	big.NewInt(58 * 58 * 58),
    55  	big.NewInt(58 * 58 * 58 * 58),
    56  	big.NewInt(58 * 58 * 58 * 58 * 58),
    57  	big.NewInt(58 * 58 * 58 * 58 * 58 * 58),
    58  	big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58),
    59  	big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58),
    60  	big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58),
    61  	bigRadix10,
    62  }
    63  
    64  var bigRadix10 = big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58) // 58^10
    65  
    66  // Decode decodes a modified base58 string to a byte slice.
    67  func Decode(b string) []byte {
    68  	answer := big.NewInt(0)
    69  	scratch := new(big.Int)
    70  
    71  	// Calculating with big.Int is slow for each iteration.
    72  	//    x += b58[b[i]] * j
    73  	//    j *= 58
    74  	//
    75  	// Instead we can try to do as many calculations on int64.
    76  	// We can represent a 10 digit base58 number using an int64.
    77  	//
    78  	// Hence, we'll try to convert 10, base58 digits at a time.
    79  	// The rough idea is to calculate `t`, such that:
    80  	//
    81  	//   t := b58[b[i+9]] * 58^9 ... + b58[b[i+1]] * 58^1 + b58[b[i]] * 58^0
    82  	//   x *= 58^10
    83  	//   x += t
    84  	//
    85  	// Of course, in addition, we'll need to handle boundary condition when `b` is not multiple of 58^10.
    86  	// In that case we'll use the bigRadix[n] lookup for the appropriate power.
    87  	for t := b; len(t) > 0; {
    88  		n := len(t)
    89  		if n > 10 {
    90  			n = 10
    91  		}
    92  
    93  		total := uint64(0)
    94  		for _, v := range t[:n] {
    95  			if v > 255 {
    96  				return []byte("")
    97  			}
    98  
    99  			tmp := b58[v]
   100  			if tmp == 255 {
   101  				return []byte("")
   102  			}
   103  			total = total*58 + uint64(tmp)
   104  		}
   105  
   106  		answer.Mul(answer, bigRadix[n])
   107  		scratch.SetUint64(total)
   108  		answer.Add(answer, scratch)
   109  
   110  		t = t[n:]
   111  	}
   112  
   113  	tmpval := answer.Bytes()
   114  
   115  	var numZeros int
   116  	for numZeros = 0; numZeros < len(b); numZeros++ {
   117  		if b[numZeros] != alphabetIdx0 {
   118  			break
   119  		}
   120  	}
   121  	flen := numZeros + len(tmpval)
   122  	val := make([]byte, flen)
   123  	copy(val[numZeros:], tmpval)
   124  
   125  	return val
   126  }
   127  
   128  // Encode encodes a byte slice to a modified base58 string.
   129  func Encode(b []byte) string {
   130  	x := new(big.Int)
   131  	x.SetBytes(b)
   132  
   133  	// maximum length of output is log58(2^(8*len(b))) == len(b) * 8 / log(58)
   134  	maxlen := int(float64(len(b))*1.365658237309761) + 1
   135  	answer := make([]byte, 0, maxlen)
   136  	mod := new(big.Int)
   137  	for x.Sign() > 0 {
   138  		// Calculating with big.Int is slow for each iteration.
   139  		//    x, mod = x / 58, x % 58
   140  		//
   141  		// Instead we can try to do as many calculations on int64.
   142  		//    x, mod = x / 58^10, x % 58^10
   143  		//
   144  		// Which will give us mod, which is 10 digit base58 number.
   145  		// We'll loop that 10 times to convert to the answer.
   146  
   147  		x.DivMod(x, bigRadix10, mod)
   148  		if x.Sign() == 0 {
   149  			// When x = 0, we need to ensure we don't add any extra zeros.
   150  			m := mod.Int64()
   151  			for m > 0 {
   152  				answer = append(answer, alphabet[m%58])
   153  				m /= 58
   154  			}
   155  		} else {
   156  			m := mod.Int64()
   157  			for i := 0; i < 10; i++ {
   158  				answer = append(answer, alphabet[m%58])
   159  				m /= 58
   160  			}
   161  		}
   162  	}
   163  
   164  	// leading zero bytes
   165  	for _, i := range b {
   166  		if i != 0 {
   167  			break
   168  		}
   169  		answer = append(answer, alphabetIdx0)
   170  	}
   171  
   172  	// reverse
   173  	alen := len(answer)
   174  	for i := 0; i < alen/2; i++ {
   175  		answer[i], answer[alen-1-i] = answer[alen-1-i], answer[i]
   176  	}
   177  
   178  	return string(answer)
   179  }
   180  
   181  // ErrChecksum indicates that the checksum of a check-encoded string does not verify against
   182  // the checksum.
   183  var ErrChecksum = errors.New("checksum error")
   184  
   185  // ErrInvalidFormat indicates that the check-encoded string has an invalid format.
   186  var ErrInvalidFormat = errors.New("invalid format: version and/or checksum bytes missing")
   187  
   188  // checksum: first four bytes of sha256^2
   189  func checksum(input []byte) (cksum [4]byte) {
   190  	h := sha256.Sum256(input)
   191  	h2 := sha256.Sum256(h[:])
   192  	copy(cksum[:], h2[:4])
   193  	return
   194  }
   195  
   196  // CheckEncode prepends a version byte and appends a four byte checksum.
   197  func CheckEncode(input []byte, version byte) string {
   198  	b := make([]byte, 0, 1+len(input)+4)
   199  	b = append(b, version)
   200  	b = append(b, input...)
   201  	cksum := checksum(b)
   202  	b = append(b, cksum[:]...)
   203  	return Encode(b)
   204  }
   205  
   206  // CheckDecode decodes a string that was encoded with CheckEncode and verifies the checksum.
   207  func CheckDecode(input string) (result []byte, version byte, err error) {
   208  	decoded := Decode(input)
   209  	if len(decoded) < 5 {
   210  		return nil, 0, ErrInvalidFormat
   211  	}
   212  	version = decoded[0]
   213  	var cksum [4]byte
   214  	copy(cksum[:], decoded[len(decoded)-4:])
   215  	if checksum(decoded[:len(decoded)-4]) != cksum {
   216  		return nil, 0, ErrChecksum
   217  	}
   218  	payload := decoded[1 : len(decoded)-4]
   219  	result = append(result, payload...)
   220  	return
   221  }