-
Notifications
You must be signed in to change notification settings - Fork 0
/
generator.go
94 lines (79 loc) · 2.12 KB
/
generator.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
package uulid
import (
"crypto/rand"
"encoding/binary"
"math/bits"
"sync"
"time"
)
// Generator implements an UUID generator based on the ULID spec.
// The generated UULID is monotonically increased for calls within the same millisecond.
type Generator struct {
mu sync.Mutex
seed uint64
ms uint64
hi uint16
lo uint64
}
// NewGenerator is like NewGeneratorWithSeed()
// but uses a secure random seed from crypto/rand.
func NewGenerator() (r *Generator, err error) {
b := make([]byte, 8)
if _, err := rand.Read(b); err != nil {
return nil, err
}
return NewGeneratorWithSeed(binary.BigEndian.Uint64(b)), nil
}
// NewGeneratorWithSeed creates a new UULID generator.
// It will used the given as the seed for the internal monotonic RNG.
//
// Ensure that a good random seed is used or use NewGenerator()
// which provides a secure seed from crypto/rand.
func NewGeneratorWithSeed(seed uint64) (r *Generator) {
return &Generator{seed: seed}
}
// New creates a UULID with the current system time.
func (r *Generator) New() (id UULID, err error) {
r.mu.Lock()
defer r.mu.Unlock()
ms := Timestamp(time.Now())
if err = id.SetTimestamp(ms); err != nil {
return id, err
}
if err = r.read(id[6:], ms); err != nil {
return id, err
}
return id, nil
}
// read generates a pseudo random entropy that is
// incremented monotonically within the same millisecond interval
func (r *Generator) read(p []byte, ms uint64) (err error) {
// within the same millisecond interval of the previous call
// increment lower entropy bytes and return
if r.ms == ms {
lo := r.lo
hi := r.hi
if r.lo++; r.lo < lo {
if r.hi++; r.hi < hi {
return ErrMonotonicOverflow
}
}
binary.BigEndian.PutUint16(p[:2], r.hi)
binary.BigEndian.PutUint64(p[2:], r.lo)
return nil
}
r.advance(ms)
binary.BigEndian.PutUint16(p[:2], r.hi)
binary.BigEndian.PutUint64(p[2:], r.lo)
return nil
}
func (r *Generator) advance(ms uint64) {
r.ms = ms
r.hi = uint16(r.uint64r())
r.lo = r.uint64r()
}
func (r *Generator) uint64r() (v uint64) {
r.seed += 0xa0761d6478bd642f
hi, lo := bits.Mul64(r.seed^0xe7037ed1a0b428db, r.seed)
return hi ^ lo
}