feed-to-muc/vendor/github.com/chilts/sid/sid.go
2019-08-30 09:42:12 +02:00

186 lines
5.1 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 (
"fmt"
"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(n int64) string {
// now do the generation (backwards, so we just %64 then /64 along the way)
for i := 10; i >= 0; i-- {
index := n % 64
chars[i] = string(base64[index])
n = n / 64
}
return strings.Join(chars, "")
}
func toBase32(n int64) string {
b32 := strconv.FormatInt(n, 32)
for len(b32) < 13 {
b32 = "0" + b32
}
// log.Printf("b32=%s\n", b32)
return b32
}
func toHex(n int64) string {
hex := fmt.Sprintf("%x", n)
for len(hex) < 16 {
hex = "0" + hex
}
return hex
}
// 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)
}
// IdBase32 returns a 27 char string based on timestamp and a random number. The format is
// "XXXXXXXXXXXXX-YYYYYYYYYYYYY" 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 IdBase32() 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 toBase32(now) + "-" + toBase32(r)
}
// IdHex returns a char string based on timestamp and a random number. The format is
// "XXXXXXXXXXXXXXXX-YYYYYYYYYYYYYYYY" 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 IdHex() 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 toHex(now) + "-" + toHex(r)
}
// Id returns a 39 char string based on timestamp and a random number. The format is
// "XXXXXXXXXXXXXXXXXXX-YYYYYYYYYYYYYYYYYYY" 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
}