127 lines
3.5 KiB
Go
127 lines
3.5 KiB
Go
package uuid
|
|
|
|
import (
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
|
)
|
|
|
|
// Length and mask of all information in uuid
|
|
const (
|
|
internalUuidUnixTimeBits = 32
|
|
internalUuidUnixTimeMask = (1 << internalUuidUnixTimeBits) - 1
|
|
|
|
internalUuidTypeBits = 4
|
|
internalUuidTypeMask = (1 << internalUuidTypeBits) - 1
|
|
|
|
internalUuidServerIdBits = 8
|
|
internalUuidServerIdMask = (1 << internalUuidServerIdBits) - 1
|
|
|
|
internalUuidSeqIdBits = 19
|
|
internalUuidSeqIdMask = (1 << internalUuidSeqIdBits) - 1
|
|
|
|
seqNumberIdBits = 32
|
|
seqNumberIdMask = (1 << seqNumberIdBits) - 1
|
|
)
|
|
|
|
// InternalUuidInfo represents a struct which has all information in uuid
|
|
type InternalUuidInfo struct {
|
|
UnixTime uint32
|
|
UuidType uint8
|
|
UuidServerId uint8
|
|
SequentialId uint32
|
|
}
|
|
|
|
// InternalUuidGenerator represents internal bundled uuid generator
|
|
type InternalUuidGenerator struct {
|
|
uuidSeqNumbers [1 << internalUuidTypeBits]uint64
|
|
uuidServerId uint8
|
|
}
|
|
|
|
// NewInternalUuidGenerator returns a new internal uuid generator
|
|
func NewInternalUuidGenerator(config *settings.Config) (*InternalUuidGenerator, error) {
|
|
generator := &InternalUuidGenerator{
|
|
uuidServerId: config.UuidServerId,
|
|
}
|
|
|
|
return generator, nil
|
|
}
|
|
|
|
// GenerateUuid generates a new uuid
|
|
func (u *InternalUuidGenerator) GenerateUuid(idType UuidType) int64 {
|
|
uuids := u.GenerateUuids(idType, 1)
|
|
return uuids[0]
|
|
}
|
|
|
|
// GenerateUuids generates new uuids
|
|
func (u *InternalUuidGenerator) GenerateUuids(idType UuidType, count uint8) []int64 {
|
|
// 63bits = unixTime(32bits) + uuidType(4bits) + uuidServerId(8bits) + sequentialNumber(19bits)
|
|
|
|
uuids := make([]int64, count)
|
|
|
|
if count < 1 {
|
|
return uuids
|
|
}
|
|
|
|
var unixTime uint64
|
|
var newFirstSeqId uint64
|
|
var newLastSeqId uint64
|
|
uuidType := uint8(idType)
|
|
|
|
for {
|
|
unixTime = uint64(time.Now().Unix())
|
|
newLastSeqId = atomic.AddUint64(&u.uuidSeqNumbers[uuidType], uint64(count))
|
|
|
|
if newLastSeqId>>seqNumberIdBits == unixTime {
|
|
newFirstSeqId = newLastSeqId - uint64(count-1)
|
|
break
|
|
}
|
|
|
|
currentSeqId := newLastSeqId
|
|
newFirstSeqId = unixTime << seqNumberIdBits
|
|
newLastSeqId = newFirstSeqId + uint64(count-1)
|
|
|
|
if atomic.CompareAndSwapUint64(&u.uuidSeqNumbers[uuidType], currentSeqId, newLastSeqId) {
|
|
break
|
|
}
|
|
}
|
|
|
|
for i := 0; i < int(count); i++ {
|
|
seqId := (newFirstSeqId + uint64(i)) & seqNumberIdMask
|
|
uuids[i] = u.assembleUuid(unixTime, uuidType, seqId)
|
|
}
|
|
|
|
return uuids
|
|
}
|
|
|
|
// ParseUuidInfo returns a info struct which contains all information in uuid
|
|
func (u *InternalUuidGenerator) ParseUuidInfo(uuid int64) *InternalUuidInfo {
|
|
seqId := uint32(uuid & internalUuidSeqIdMask)
|
|
uuid = uuid >> internalUuidSeqIdBits
|
|
|
|
uuidServerId := uint8(uuid & internalUuidServerIdMask)
|
|
uuid = uuid >> internalUuidServerIdBits
|
|
|
|
uuidType := uint8(uuid & internalUuidTypeMask)
|
|
uuid = uuid >> internalUuidTypeBits
|
|
|
|
unixTime := uint32(uuid & internalUuidUnixTimeMask)
|
|
|
|
return &InternalUuidInfo{
|
|
UnixTime: unixTime,
|
|
UuidType: uuidType,
|
|
UuidServerId: uuidServerId,
|
|
SequentialId: seqId,
|
|
}
|
|
}
|
|
|
|
func (u *InternalUuidGenerator) assembleUuid(unixTime uint64, uuidType uint8, seqId uint64) int64 {
|
|
unixTimePart := (int64(unixTime) & internalUuidUnixTimeMask) << (internalUuidTypeBits + internalUuidServerIdBits + internalUuidSeqIdBits)
|
|
uuidTypePart := (int64(uuidType) & internalUuidTypeMask) << (internalUuidServerIdBits + internalUuidSeqIdBits)
|
|
uuidServerIdPart := (int64(u.uuidServerId) & internalUuidServerIdMask) << internalUuidSeqIdBits
|
|
seqIdPart := int64(seqId) & internalUuidSeqIdMask
|
|
|
|
return unixTimePart | uuidTypePart | uuidServerIdPart | seqIdPart
|
|
}
|