forked from zalando/skipper
-
Notifications
You must be signed in to change notification settings - Fork 0
/
standard.go
66 lines (56 loc) · 2 KB
/
standard.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
package flowid
import (
"fmt"
"math/rand"
"regexp"
)
const (
flowIdAlphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-+"
alphabetBitMask = 63
MaxLength = 64
MinLength = 8
defaultLen = 16
)
var (
ErrInvalidLen = fmt.Errorf("Invalid length. Must be between %d and %d", MinLength, MaxLength)
standardFlowIDRegex = regexp.MustCompile(`^[0-9a-zA-Z+-]+$`)
)
type standardGenerator struct {
length int
}
// NewStandardGenerator creates a new FlowID generator that generates flow IDs with length l.
// The alphabet is limited to 64 elements and requires a random 6 bit value to index any of them.
// The cost to rnd.IntXX is not very relevant but the bit shifting operations are faster.
// For this reason a single call to rnd.Int63 is used and its bits are mapped up to 10 chunks of 6 bits each.
// The byte data type carries 2 additional bits for the next chunk which are cleared with the alphabet bit mask.
// It is safe for concurrent use.
func NewStandardGenerator(l int) (Generator, error) {
if l < MinLength || l > MaxLength {
return nil, ErrInvalidLen
}
return &standardGenerator{length: l}, nil
}
// Generate returns a new Flow ID from the built-in generator with the configured length
func (g *standardGenerator) Generate() (string, error) {
u := make([]byte, g.length)
for i := 0; i < g.length; i += 10 {
b := rand.Int63()
for e := 0; e < 10 && i+e < g.length; e++ {
c := byte(b>>uint(6*e)) & alphabetBitMask // 6 bits only
u[i+e] = flowIdAlphabet[c]
}
}
return string(u), nil
}
// MustGenerate is a convenience function equivalent to Generate that panics on failure instead of returning an error.
func (g *standardGenerator) MustGenerate() string {
id, err := g.Generate()
if err != nil {
panic(err)
}
return id
}
// IsValid checks if the given flowId follows the format of this generator
func (g *standardGenerator) IsValid(flowId string) bool {
return len(flowId) >= MinLength && len(flowId) <= MaxLength && standardFlowIDRegex.MatchString(flowId)
}