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]atomic.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) if len(uuids) < 1 { return 0 } return uuids[0] } // GenerateUuids generates new uuids func (u *InternalUuidGenerator) GenerateUuids(idType UuidType, count uint16) []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 = u.uuidSeqNumbers[uuidType].Add(uint64(count)) newSeqUnixTime := newLastSeqId >> seqNumberIdBits if unixTime == newSeqUnixTime { newFirstSeqId = newLastSeqId - uint64(count-1) break } else if unixTime < newSeqUnixTime { continue } currentSeqId := newLastSeqId newFirstSeqId = unixTime << seqNumberIdBits newLastSeqId = newFirstSeqId + uint64(count-1) if u.uuidSeqNumbers[uuidType].CompareAndSwap(currentSeqId, newLastSeqId) { break } } for i := 0; i < int(count); i++ { seqId := (newFirstSeqId + uint64(i)) & seqNumberIdMask // the internal uuid generator can only generate 524,287 ids per second for specified type if seqId > internalUuidSeqIdMask { return nil } uuids[i] = u.assembleUuid(unixTime, uuidType, seqId) } return uuids } func (u *InternalUuidGenerator) parseInternalUuidInfo(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 }