generator.go (1842B)
1 package yuid 2 3 import ( 4 "io" 5 "sync" 6 "time" 7 ) 8 9 const ( 10 Characters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_" // Exactly 64 characters 11 ) 12 13 type Generator struct { 14 sync.Mutex 15 Source io.Reader 16 Now func() int64 17 sequence uint8 18 timestamp int64 19 } 20 21 type GeneratorOption func(*Generator) 22 23 func NewGenerator(opts ...GeneratorOption) *Generator { 24 g := Generator{ 25 Source: InsecureSource{}, 26 Now: timeNow, 27 } 28 29 for _, opt := range opts { 30 opt(&g) 31 } 32 33 return &g 34 } 35 36 func WithSource(source io.Reader) GeneratorOption { 37 return func(g *Generator) { 38 g.Source = source 39 } 40 } 41 42 func WithNowFunc(now func() int64) GeneratorOption { 43 return func(g *Generator) { 44 g.Now = now 45 } 46 } 47 48 func (g *Generator) Next(id *ID) error { 49 var ts int64 50 var seq uint8 51 52 now := g.Now() 53 54 g.Lock() 55 56 if now > g.timestamp { 57 g.timestamp = now 58 g.sequence = 0 59 } else if g.sequence == 255 { 60 g.timestamp++ 61 g.sequence = 0 62 } else { 63 g.sequence++ 64 } 65 66 ts = g.timestamp 67 seq = g.sequence 68 69 _, err := io.ReadFull(g.Source, id[12:]) 70 71 g.Unlock() 72 73 if err != nil { 74 return err 75 } 76 77 putHeader(id, ts, seq) 78 setBody(id) 79 80 return nil 81 } 82 83 func putHeader(id *ID, ts int64, seq uint8) { 84 id[0] = Characters[(ts>>58)&63] 85 id[1] = Characters[(ts>>52)&63] 86 id[2] = Characters[(ts>>46)&63] 87 id[3] = Characters[(ts>>40)&63] 88 id[4] = Characters[(ts>>34)&63] 89 id[5] = Characters[(ts>>28)&63] 90 id[6] = Characters[(ts>>22)&63] 91 id[7] = Characters[(ts>>16)&63] 92 id[8] = Characters[(ts>>10)&63] 93 id[9] = Characters[(ts>>4)&63] 94 id[10] = Characters[uint8((ts&15)<<2)|(seq>>6)] 95 id[11] = Characters[seq&63] 96 } 97 98 func setBody(id *ID) { 99 for i := 12; i < IDSize; i++ { 100 id[i] = Characters[id[i]&63] 101 } 102 } 103 104 func (g *Generator) MustNext(id *ID) { 105 err := g.Next(id) 106 if err != nil { 107 panic(err) 108 } 109 } 110 111 func timeNow() int64 { 112 return time.Now().UnixMilli() 113 }