67 lines
1.2 KiB
Go
67 lines
1.2 KiB
Go
package idgen
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/base32"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
var encoding = base32.StdEncoding.WithPadding(base32.NoPadding)
|
|
|
|
// Creates a new, random token from 20 random bytes, encoded as base32.
|
|
func NewToken() string {
|
|
buf := make([]byte, 20)
|
|
rand.Read(buf) // Guaranteed not to return an error.
|
|
return encoding.EncodeToString(buf)
|
|
}
|
|
|
|
var (
|
|
lock sync.Mutex
|
|
ts int64 = time.Now().Unix()
|
|
seq int64 = 0
|
|
seqMax int64 = 1 << 20
|
|
)
|
|
|
|
// NextID can generate ~1M int64s per second.
|
|
//
|
|
// nodeID must be in [0, 63]. The counter is shared across all nodeIDs within a
|
|
// process, so IDs are not guaranteed to be sequential.
|
|
func NextID(nodeID int64) int64 {
|
|
if nodeID < 0 || nodeID >= 64 {
|
|
panic("nodeID out of range [0, 63]")
|
|
}
|
|
|
|
for {
|
|
if id := nextID(nodeID); id != 0 {
|
|
return id
|
|
}
|
|
time.Sleep(100 * time.Millisecond)
|
|
}
|
|
}
|
|
|
|
func nextID(nodeID int64) int64 {
|
|
lock.Lock()
|
|
defer lock.Unlock()
|
|
|
|
tt := time.Now().Unix()
|
|
if tt > ts {
|
|
ts = tt
|
|
seq = 1
|
|
} else {
|
|
seq++
|
|
if seq >= seqMax {
|
|
return 0
|
|
}
|
|
}
|
|
|
|
return (ts << 26) + (nodeID << 20) + seq
|
|
}
|
|
|
|
func SplitID(id int64) (unixTime, nodeID, counter int64) {
|
|
counter = id & (0x00000000000FFFFF)
|
|
nodeID = (id >> 20) & (0x000000000000003F)
|
|
unixTime = id >> 26
|
|
return
|
|
}
|