dispatch/vendor/github.com/kjk/betterguid/betterguid.go
2018-08-31 03:57:19 +02:00

70 lines
1.6 KiB
Go

package betterguid
import (
"math/rand"
"sync"
"time"
)
const (
// Modeled after base64 web-safe chars, but ordered by ASCII.
pushChars = "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"
)
var (
// Timestamp of last push, used to prevent local collisions if you push twice in one ms.
lastPushTimeMs int64
// We generate 72-bits of randomness which get turned into 12 characters and appended to the
// timestamp to prevent collisions with other clients. We store the last characters we
// generated because in the event of a collision, we'll use those same characters except
// "incremented" by one.
lastRandChars [12]int
mu sync.Mutex
rnd *rand.Rand
)
func init() {
// seed to get randomness
rnd = rand.New(rand.NewSource(time.Now().UnixNano()))
}
func genRandPart() {
for i := 0; i < len(lastRandChars); i++ {
lastRandChars[i] = rnd.Intn(64)
}
}
// New creates a new random, unique id
func New() string {
var id [8 + 12]byte
mu.Lock()
timeMs := time.Now().UTC().UnixNano() / 1e6
if timeMs == lastPushTimeMs {
// increment lastRandChars
for i := 0; i < 12; i++ {
lastRandChars[i]++
if lastRandChars[i] < 64 {
break
}
// increment the next byte
lastRandChars[i] = 0
}
} else {
genRandPart()
}
lastPushTimeMs = timeMs
// put random as the second part
for i := 0; i < 12; i++ {
id[19-i] = pushChars[lastRandChars[i]]
}
mu.Unlock()
// put current time at the beginning
for i := 7; i >= 0; i-- {
n := int(timeMs % 64)
id[i] = pushChars[n]
timeMs = timeMs / 64
}
return string(id[:])
}