108 lines
2.2 KiB
Go
108 lines
2.2 KiB
Go
|
|
package id
|
||
|
|
|
||
|
|
import (
|
||
|
|
"apigo.cc/go/rand"
|
||
|
|
"sync"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"apigo.cc/go/encoding"
|
||
|
|
)
|
||
|
|
|
||
|
|
type IdMaker struct {
|
||
|
|
secCurrent uint64
|
||
|
|
secIndexNext uint64
|
||
|
|
secIndexLock sync.Mutex
|
||
|
|
Incr func(sec uint64) uint64
|
||
|
|
}
|
||
|
|
|
||
|
|
func NewIdMaker(incr func(sec uint64) uint64) *IdMaker {
|
||
|
|
return &IdMaker{Incr: incr, secIndexNext: 1}
|
||
|
|
}
|
||
|
|
|
||
|
|
var DefaultIdMaker = NewIdMaker(nil)
|
||
|
|
|
||
|
|
func MakeId(size int) string {
|
||
|
|
return DefaultIdMaker.Get(size)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (im *IdMaker) defaultIncr(sec uint64) uint64 {
|
||
|
|
im.secIndexLock.Lock()
|
||
|
|
defer im.secIndexLock.Unlock()
|
||
|
|
if im.secCurrent == sec {
|
||
|
|
im.secIndexNext++
|
||
|
|
} else {
|
||
|
|
if im.secCurrent == 0 {
|
||
|
|
im.secIndexNext = rand.FastInt(uint64(1000), uint64(1999))
|
||
|
|
} else {
|
||
|
|
im.secIndexNext = 1
|
||
|
|
}
|
||
|
|
im.secCurrent = sec
|
||
|
|
}
|
||
|
|
return im.secIndexNext
|
||
|
|
}
|
||
|
|
|
||
|
|
func (im *IdMaker) get(size int, ordered bool, hashToHead bool) string {
|
||
|
|
tm := time.Now()
|
||
|
|
|
||
|
|
nowSec := uint64(tm.Unix() - 946656000)
|
||
|
|
var n, sec uint64
|
||
|
|
secCapacity := uint64(901356495)
|
||
|
|
if nowSec < 11*secCapacity {
|
||
|
|
n = nowSec / secCapacity
|
||
|
|
sec = (nowSec % secCapacity) + 14776336
|
||
|
|
} else {
|
||
|
|
n = 11
|
||
|
|
sec = nowSec
|
||
|
|
}
|
||
|
|
|
||
|
|
var secIndex uint64
|
||
|
|
if im.Incr != nil {
|
||
|
|
secIndex = im.Incr(sec)
|
||
|
|
}
|
||
|
|
if secIndex == 0 {
|
||
|
|
secIndex = im.defaultIncr(sec)
|
||
|
|
}
|
||
|
|
|
||
|
|
intEncoder := encoding.DefaultIntEncoder
|
||
|
|
if ordered {
|
||
|
|
intEncoder = encoding.OrderedIntEncoder
|
||
|
|
}
|
||
|
|
secBytes := intEncoder.EncodeInt(sec)
|
||
|
|
secLen := len(secBytes)
|
||
|
|
inSecIndexBytes := intEncoder.EncodeInt(secIndex)
|
||
|
|
|
||
|
|
m := min(uint64(len(inSecIndexBytes)), 5)
|
||
|
|
|
||
|
|
secTagVal := n*5 + (m - 1)
|
||
|
|
var uid = make([]byte, 0, size)
|
||
|
|
uid = intEncoder.AppendInt(uid, secTagVal)
|
||
|
|
|
||
|
|
uid = append(uid, secBytes...)
|
||
|
|
uid = append(uid, inSecIndexBytes...)
|
||
|
|
uid = intEncoder.FillInt(uid, size)
|
||
|
|
if !ordered {
|
||
|
|
encoding.HashInt(encoding.ExchangeInt(uid), nil)
|
||
|
|
} else {
|
||
|
|
encoding.HashInt(encoding.ExchangeInt(uid[secLen+1:]), nil)
|
||
|
|
if hashToHead {
|
||
|
|
size = len(uid)
|
||
|
|
lastByte := uid[size-1]
|
||
|
|
copy(uid[1:], uid[:size-1])
|
||
|
|
uid[0] = lastByte
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return string(uid)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (im *IdMaker) Get(size int) string {
|
||
|
|
return im.get(size, false, false)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (im *IdMaker) GetForMysql(size int) string {
|
||
|
|
return im.get(size, true, true)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (im *IdMaker) GetForPostgreSQL(size int) string {
|
||
|
|
return im.get(size, true, false)
|
||
|
|
}
|