id/id.go

108 lines
2.2 KiB
Go
Raw Permalink Normal View History

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)
}