mirror of
https://salsa.debian.org/mdosch/feed-to-muc.git
synced 2024-11-23 06:28:39 +01:00
107 lines
3 KiB
Go
107 lines
3 KiB
Go
// Package sid provides the ability to generate Sortable Identifiers. These are also universally unique.
|
|
//
|
|
// id1 := sid.Id()
|
|
// id2 := sid.Id()
|
|
// fmt.Printf("id1 = %s\n", id1)
|
|
// fmt.Printf("id2 = %s\n", id2)
|
|
// // -> "id1 = 1IeSBAWW83j-2wgJ4PUtlAr"
|
|
// // -> "id2 = 1IeSBAWW9kK-0cDG64GQgGJ"
|
|
//
|
|
// This package is simple and only provides one function. The aim here is not pure speed, it is for an easy use-case
|
|
// without having to worry about goroutines and locking.
|
|
package sid
|
|
|
|
import (
|
|
"math/rand"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
func init() {
|
|
rand.Seed(time.Now().UTC().UnixNano())
|
|
}
|
|
|
|
// Remember the lastTime so that if (by chance) we get the same NanoSecond, we just incrememt the last random number.
|
|
var mu = &sync.Mutex{}
|
|
var lastTime int64
|
|
var lastRand int64
|
|
var chars = make([]string, 11, 11)
|
|
|
|
// 64 chars but ordered by ASCII
|
|
const base64 string = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~"
|
|
|
|
func toStr(now int64) string {
|
|
// now do the generation (backwards, so we just %64 then /64 along the way)
|
|
for i := 10; i >= 0; i-- {
|
|
index := now % 64
|
|
chars[i] = string(base64[index])
|
|
now = now / 64
|
|
}
|
|
|
|
return strings.Join(chars, "")
|
|
}
|
|
|
|
// IdBase64 returns a 23 char string based on timestamp and a random number. The format is "XXXXXXXXXXX-YYYYYYYYYYY"
|
|
// where X is the timestamp and Y is the random number. If (by any chance) this is called in the same nanosecond, the
|
|
// random number is incremented instead of a new one being generated. This makes sure that two consecutive Ids
|
|
// generated in the same goroutine also ensure those Ids are also sortable.
|
|
//
|
|
// It is safe to call from different goroutines since it has it's own locking.
|
|
func IdBase64() string {
|
|
// lock for lastTime, lastRand, and chars
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
|
|
now := time.Now().UTC().UnixNano()
|
|
var r int64
|
|
|
|
// if we have the same time, just inc lastRand, else create a new one
|
|
if now == lastTime {
|
|
lastRand++
|
|
} else {
|
|
lastRand = rand.Int63()
|
|
}
|
|
r = lastRand
|
|
|
|
// remember this for next time
|
|
lastTime = now
|
|
|
|
return toStr(now) + "-" + toStr(r)
|
|
}
|
|
|
|
// Id returns a 23 char string based on timestamp and a random number. The format is "XXXXXXXXXXX-YYYYYYYYYYY" where X
|
|
// is the timestamp and Y is the random number. If (by any chance) this is called in the same nanosecond, the random
|
|
// number is incremented instead of a new one being generated. This makes sure that two consecutive Ids generated in
|
|
// the same goroutine also ensure those Ids are also sortable.
|
|
//
|
|
// It is safe to call from different goroutines since it has it's own locking.
|
|
func Id() string {
|
|
// lock for lastTime, lastRand, and chars
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
|
|
now := time.Now().UTC().UnixNano()
|
|
var r int64
|
|
|
|
// if we have the same time, just inc lastRand, else create a new one
|
|
if now == lastTime {
|
|
lastRand++
|
|
} else {
|
|
lastRand = rand.Int63()
|
|
}
|
|
r = lastRand
|
|
|
|
// remember this for next time
|
|
lastTime = now
|
|
|
|
nowStr := strconv.FormatInt(now, 10)
|
|
rStr := strconv.FormatInt(r, 10)
|
|
|
|
for len(rStr) < 19 {
|
|
rStr = "0" + rStr
|
|
}
|
|
|
|
return nowStr + "-" + rStr
|
|
}
|