gonanoid.go 1.8 KB

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