gonanoid.go 1.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. package gonanoid
  2. import (
  3. "crypto/rand"
  4. "math"
  5. )
  6. const (
  7. defaultAlphabet = "_~0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" // len=64
  8. defaultSize = 22
  9. defaultMaskSize = 5
  10. )
  11. func initMasks(params ...int) []uint {
  12. var size int
  13. if len(params) == 0 {
  14. size = defaultMaskSize
  15. } else {
  16. size = params[0]
  17. }
  18. masks := make([]uint, size)
  19. for i := 0; i < size; i++ {
  20. shift := 3 + i
  21. masks[i] = (2 << uint(shift)) - 1
  22. }
  23. return masks
  24. }
  25. func getMask(alphabet string, masks []uint) int {
  26. for i := 0; i < len(masks); i++ {
  27. curr := int(masks[i])
  28. if curr >= len(alphabet)-1 {
  29. return curr
  30. }
  31. }
  32. return 0
  33. }
  34. // Generate is a low-level function to change alphabet and ID size.
  35. func Generate(alphabet string, size int) (string, error) {
  36. masks := initMasks(size)
  37. mask := getMask(alphabet, masks)
  38. ceilArg := 1.6 * float64(mask*size) / float64(len(alphabet))
  39. step := int(math.Ceil(ceilArg))
  40. id := make([]byte, size)
  41. bytes := make([]byte, step)
  42. for j := 0; ; {
  43. _, err := rand.Read(bytes)
  44. if err != nil {
  45. return "", err
  46. }
  47. for i := 0; i < step; i++ {
  48. currByte := bytes[i] & byte(mask)
  49. if currByte < byte(len(alphabet)) {
  50. id[j] = alphabet[currByte]
  51. j++
  52. if j == size {
  53. return string(id[:size]), nil
  54. }
  55. }
  56. }
  57. }
  58. }
  59. // Nanoid generates secure URL-friendly unique ID.
  60. func Nanoid(param ...int) (string, error) {
  61. var size int
  62. if len(param) == 0 {
  63. size = defaultSize
  64. } else {
  65. size = param[0]
  66. }
  67. bytes := make([]byte, size)
  68. _, err := rand.Read(bytes)
  69. if err != nil {
  70. return "", err
  71. }
  72. id := make([]byte, size)
  73. for i := 0; i < size; i++ {
  74. id[i] = defaultAlphabet[bytes[i]&63]
  75. }
  76. return string(id[:size]), nil
  77. }