11package random
22
33import (
4- "math/rand"
4+ "bufio"
5+ "crypto/rand"
6+ "io"
57 "strings"
6- "time "
8+ "sync "
79)
810
911type (
1012 Random struct {
13+ readerPool sync.Pool
1114 }
1215)
1316
@@ -27,20 +30,55 @@ var (
2730)
2831
2932func New () * Random {
30- rand .Seed (time .Now ().UnixNano ())
31- return new (Random )
33+ // https://tip.golang.org/doc/go1.19#:~:text=Read%20no%20longer%20buffers%20random%20data%20obtained%20from%20the%20operating%20system%20between%20calls
34+ p := sync.Pool {New : func () interface {} {
35+ return bufio .NewReader (rand .Reader )
36+ }}
37+ return & Random {readerPool : p }
3238}
3339
3440func (r * Random ) String (length uint8 , charsets ... string ) string {
3541 charset := strings .Join (charsets , "" )
3642 if charset == "" {
3743 charset = Alphanumeric
3844 }
45+
46+ charsetLen := len (charset )
47+ if charsetLen > 255 {
48+ charsetLen = 255
49+ }
50+ maxByte := 255 - (256 % charsetLen )
51+
52+ reader := r .readerPool .Get ().(* bufio.Reader )
53+ defer r .readerPool .Put (reader )
54+
3955 b := make ([]byte , length )
40- for i := range b {
41- b [i ] = charset [rand .Int63 ()% int64 (len (charset ))]
56+ rs := make ([]byte , length + (length / 4 )) // perf: avoid read from rand.Reader many times
57+ var i uint8 = 0
58+
59+ // security note:
60+ // we can't just simply do b[i]=charset[rb%byte(charsetLen)],
61+ // for example, when charsetLen is 52, and rb is [0, 255], 256 = 52 * 4 + 48.
62+ // this will make the first 48 characters more possibly to be generated then others.
63+ // so we have to skip bytes when rb > maxByte
64+
65+ for {
66+ _ , err := io .ReadFull (reader , rs )
67+ if err != nil {
68+ panic ("unexpected error happened when reading from bufio.NewReader(crypto/rand.Reader)" )
69+ }
70+ for _ , rb := range rs {
71+ if rb > byte (maxByte ) {
72+ // Skip this number to avoid bias.
73+ continue
74+ }
75+ b [i ] = charset [rb % byte (charsetLen )]
76+ i ++
77+ if i == length {
78+ return string (b )
79+ }
80+ }
4281 }
43- return string (b )
4482}
4583
4684func String (length uint8 , charsets ... string ) string {
0 commit comments