-
Notifications
You must be signed in to change notification settings - Fork 0
/
data_generateMethods.go
157 lines (124 loc) · 3.19 KB
/
data_generateMethods.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package passwordGenerator
import (
"crypto/rand"
"strings"
"sync"
)
func (d *Data) generateRune() (r rune, err error) {
iBig, err := rand.Int(rand.Reader, d.availableRunesLenBig)
if err != nil {
return
}
i := iBig.Int64()
r = d.availableRunes[i]
return
}
// Generate creates a single password made up of random runes.
// The password will conform to the options contained in the Data struct.
// An error is also returned from Generate, which will be non-nil if
// the random-number generator is not working, meaning that a password
// cannot be succesfully generated, or if the options in the Data struct
// determine that there are no available runes to be randomly selected
// when generating passwords.
func (d *Data) Generate() (password string, err error) {
if len(d.availableRunes) == 0 {
err = ErrNoAvailableRunes
return
}
return d.generate()
}
func (d *Data) generate() (password string, err error) {
var builder strings.Builder
var r rune
for i := d.options.Len - 1; i >= 0; i-- {
r, err = d.generateRune()
if err != nil {
return
}
builder.WriteRune(r)
}
password = builder.String()
return
}
const (
dataGenerateManyBatchSize = 128
)
func (d *Data) generateManyBatchWithoutConcurrency(
passwords []string,
start int,
end int,
) (err error) {
for i := start; i < end; i++ {
var p string
if p, err = d.generate(); err != nil {
return
}
passwords[i] = p
}
return
}
func (d *Data) generateManyBatchWithConcurrency(
passwords []string,
start int,
end int,
wg *sync.WaitGroup,
errChan chan error,
) {
defer wg.Done()
for i := start; i < end; i++ {
select {
case err := <-errChan:
errChan <- err
return
default:
}
p, err := d.generate()
if err != nil {
select {
case errChan <- err:
default:
}
return
}
passwords[i] = p
}
}
// GenerateMany creates many passwords made up of random runes.
// The function's single argument will determine exactly how many
// passwords are generated.
// Any passwords will conform to the options contained in the Data struct.
// An error is also returned from GenerateMany, which will be non-nil if
// the random-number generator is not working, meaning that one of the
// passwords cannot be succesfully generated, or if the options in the Data struct
// determine that there are no available runes to be randomly selected
// when generating passwords.
func (d *Data) GenerateMany(n int) (passwords []string, err error) {
if len(d.availableRunes) == 0 {
err = ErrNoAvailableRunes
return
}
passwords = make([]string, n)
if n <= dataGenerateManyBatchSize {
err = d.generateManyBatchWithoutConcurrency(passwords, 0, n)
} else {
var wg sync.WaitGroup
wholeBatches := n / dataGenerateManyBatchSize
wg.Add(wholeBatches)
errChan := make(chan error, 1)
for i := 0; i < wholeBatches; i++ {
start := i * dataGenerateManyBatchSize
end := start + dataGenerateManyBatchSize
go d.generateManyBatchWithConcurrency(passwords, start, end, &wg, errChan)
}
if start := wholeBatches * dataGenerateManyBatchSize; start < n {
wg.Add(1)
go d.generateManyBatchWithConcurrency(passwords, start, n, &wg, errChan)
}
wg.Wait()
select {
case err = <-errChan:
default:
}
}
return
}