short-uuid.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. // License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>
  2. package utils
  3. import (
  4. "crypto/rand"
  5. "math"
  6. "math/big"
  7. "github.com/ALTree/bigfloat"
  8. "github.com/google/uuid"
  9. )
  10. const (
  11. ESCAPE_CODE_SAFE_ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ "
  12. HUMAN_ALPHABET = "23456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
  13. )
  14. func num_to_string(number *big.Int, alphabet []rune, alphabet_len *big.Int, pad_to_length int) string {
  15. var zero, digit big.Int
  16. if number.Sign() < 0 {
  17. *number = zero
  18. }
  19. capacity := 64
  20. if pad_to_length > capacity {
  21. capacity = pad_to_length
  22. }
  23. ans := make([]rune, 0, capacity)
  24. for number.Cmp(&zero) == 1 {
  25. number.DivMod(number, alphabet_len, &digit)
  26. ans = append(ans, alphabet[digit.Uint64()])
  27. }
  28. al := len(ans)
  29. if pad_to_length > -1 && al < pad_to_length {
  30. ans = ans[:pad_to_length]
  31. for i := al; i < pad_to_length; i++ {
  32. ans[i] = alphabet[0]
  33. }
  34. }
  35. return string(ans)
  36. }
  37. func get_padding_length(alphabet_len int) int {
  38. bi := big.NewInt(2)
  39. bi = bi.Exp(bi, big.NewInt(128), nil)
  40. bb := new(big.Float).SetPrec(256)
  41. bb.SetInt(bi)
  42. log_al := bigfloat.Log(big.NewFloat(float64(alphabet_len)).SetPrec(256))
  43. log_b := bigfloat.Log(bb)
  44. res := new(big.Float).SetPrec(256)
  45. res = res.Quo(log_b, log_al)
  46. val, _ := res.Float64()
  47. return int(math.Ceil(val))
  48. }
  49. type ShortUUID struct {
  50. alphabet []rune
  51. alphabet_len big.Int
  52. pad_to_length int
  53. }
  54. func CreateShortUUID(alphabet string) *ShortUUID {
  55. if alphabet == "" {
  56. alphabet = HUMAN_ALPHABET
  57. }
  58. var ans = ShortUUID{
  59. alphabet: []rune(alphabet),
  60. }
  61. ans.pad_to_length = get_padding_length(len(ans.alphabet))
  62. ans.alphabet_len.SetUint64(uint64(len(ans.alphabet)))
  63. return &ans
  64. }
  65. func (self *ShortUUID) Random(num_bits int64) (string, error) {
  66. max := big.NewInt(0).Exp(big.NewInt(2), big.NewInt(num_bits), nil)
  67. bi, err := rand.Int(rand.Reader, max)
  68. if err != nil {
  69. return "", err
  70. }
  71. return num_to_string(bi, self.alphabet, &self.alphabet_len, self.pad_to_length), nil
  72. }
  73. func (self *ShortUUID) Uuid4() (string, error) {
  74. b, err := uuid.NewRandom()
  75. if err != nil {
  76. return "", err
  77. }
  78. bb, err := b.MarshalBinary()
  79. if err != nil {
  80. return "", err
  81. }
  82. var bi big.Int
  83. bi.SetBytes(bb)
  84. return num_to_string(&bi, self.alphabet, &self.alphabet_len, self.pad_to_length), nil
  85. }
  86. var HumanUUID *ShortUUID
  87. func HumanUUID4() (string, error) {
  88. if HumanUUID == nil {
  89. HumanUUID = CreateShortUUID(HUMAN_ALPHABET)
  90. }
  91. return HumanUUID.Uuid4()
  92. }
  93. func HumanRandomId(num_bits int64) (string, error) {
  94. if HumanUUID == nil {
  95. HumanUUID = CreateShortUUID(HUMAN_ALPHABET)
  96. }
  97. return HumanUUID.Random(num_bits)
  98. }