@@ -3,74 +3,56 @@ package gonanoid
3
3
import (
4
4
"crypto/rand"
5
5
"errors"
6
- "fmt"
7
6
"math"
8
7
)
9
8
9
+ // defaultAlphabet is the alphabet used for ID characters by default.
10
10
var defaultAlphabet = []rune ("_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" )
11
+
11
12
const (
12
- defaultSize = 21
13
- defaultMaskSize = 5
13
+ defaultSize = 21
14
14
)
15
15
16
- // Generator function
17
- type Generator func ([]byte ) (int , error )
18
-
19
- // BytesGenerator is the default bytes generator
20
- var BytesGenerator Generator = rand .Read
21
-
22
- func initMasks (params ... int ) []uint {
23
- var size int
24
- if len (params ) == 0 {
25
- size = defaultMaskSize
26
- } else {
27
- size = params [0 ]
28
- }
29
- masks := make ([]uint , size )
30
- for i := 0 ; i < size ; i ++ {
31
- shift := 3 + i
32
- masks [i ] = (2 << uint (shift )) - 1
33
- }
34
- return masks
35
- }
36
-
37
- func getMask (alphabet []rune , masks []uint ) int {
38
- for i := 0 ; i < len (masks ); i ++ {
39
- curr := int (masks [i ])
40
- if curr >= len (alphabet )- 1 {
41
- return curr
16
+ // getMask generates bit mask used to obtain bits from the random bytes that are used to get index of random character
17
+ // from the alphabet. Example: if the alphabet has 6 = (110)_2 characters it is sufficient to use mask 7 = (111)_2
18
+ func getMask (alphabetSize int ) int {
19
+ for i := 1 ; i <= 8 ; i ++ {
20
+ mask := (2 << uint (i )) - 1
21
+ if mask >= alphabetSize - 1 {
22
+ return mask
42
23
}
43
24
}
44
25
return 0
45
26
}
46
27
47
28
// Generate is a low-level function to change alphabet and ID size.
48
- func Generate (rawAlphabet string , size int ) (string , error ) {
49
- alphabet := []rune (rawAlphabet )
29
+ func Generate (alphabet string , size int ) (string , error ) {
30
+ chars := []rune (alphabet )
50
31
51
32
if len (alphabet ) == 0 || len (alphabet ) > 255 {
52
- return "" , fmt . Errorf ("alphabet must not empty and contain no more than 255 chars. Current len is %d" , len ( alphabet ) )
33
+ return "" , errors . New ("alphabet must not be empty and contain no more than 255 chars" )
53
34
}
54
35
if size <= 0 {
55
- return "" , fmt . Errorf ("size must be positive integer" )
36
+ return "" , errors . New ("size must be positive integer" )
56
37
}
57
38
58
- masks := initMasks (size )
59
- mask := getMask (alphabet , masks )
39
+ mask := getMask (len (chars ))
40
+ // estimate how many random bytes we will need for the ID, we might actually need more but this is tradeoff
41
+ // between average case and worst case
60
42
ceilArg := 1.6 * float64 (mask * size ) / float64 (len (alphabet ))
61
43
step := int (math .Ceil (ceilArg ))
62
44
63
45
id := make ([]rune , size )
64
46
bytes := make ([]byte , step )
65
47
for j := 0 ; ; {
66
- _ , err := BytesGenerator (bytes )
48
+ _ , err := rand . Read (bytes )
67
49
if err != nil {
68
50
return "" , err
69
51
}
70
52
for i := 0 ; i < step ; i ++ {
71
53
currByte := bytes [i ] & byte (mask )
72
- if currByte < byte (len (alphabet )) {
73
- id [j ] = alphabet [currByte ]
54
+ if currByte < byte (len (chars )) {
55
+ id [j ] = chars [currByte ]
74
56
j ++
75
57
if j == size {
76
58
return string (id [:size ]), nil
@@ -80,22 +62,32 @@ func Generate(rawAlphabet string, size int) (string, error) {
80
62
}
81
63
}
82
64
83
- // Nanoid generates secure URL-friendly unique ID.
84
- func Nanoid (param ... int ) (string , error ) {
65
+ // MustGenerate is the same as Generate but panics on error.
66
+ func MustGenerate (alphabet string , size int ) string {
67
+ id , err := Generate (alphabet , size )
68
+ if err != nil {
69
+ panic (err )
70
+ }
71
+ return id
72
+ }
73
+
74
+ // New generates secure URL-friendly unique ID.
75
+ // Accepts optional parameter - length of the ID to be generated (21 by default).
76
+ func New (l ... int ) (string , error ) {
85
77
var size int
86
78
switch {
87
- case len (param ) == 0 :
79
+ case len (l ) == 0 :
88
80
size = defaultSize
89
- case len (param ) == 1 :
90
- size = param [0 ]
81
+ case len (l ) == 1 :
82
+ size = l [0 ]
91
83
if size < 0 {
92
84
return "" , errors .New ("negative id length" )
93
85
}
94
86
default :
95
87
return "" , errors .New ("unexpected parameter" )
96
88
}
97
89
bytes := make ([]byte , size )
98
- _ , err := BytesGenerator (bytes )
90
+ _ , err := rand . Read (bytes )
99
91
if err != nil {
100
92
return "" , err
101
93
}
@@ -106,24 +98,9 @@ func Nanoid(param ...int) (string, error) {
106
98
return string (id [:size ]), nil
107
99
}
108
100
109
- // ID provides more golang idiomatic interface for generating IDs.
110
- // Calling ID is shorter yet still clear `gonanoid.ID(20)` and it requires the lengths parameter by default.
111
- func ID (l int ) (string , error ) {
112
- return Nanoid (l )
113
- }
114
-
115
- // MustID is the same as ID but panics on error.
116
- func MustID (l int ) string {
117
- id , err := Nanoid (l )
118
- if err != nil {
119
- panic (err )
120
- }
121
- return id
122
- }
123
-
124
- // MustGenerate is the same as Generate but panics on error.
125
- func MustGenerate (rawAlphabet string , size int ) string {
126
- id , err := Generate (rawAlphabet , size )
101
+ // Must is the same as New but panics on error.
102
+ func Must (l ... int ) string {
103
+ id , err := New (l ... )
127
104
if err != nil {
128
105
panic (err )
129
106
}
0 commit comments