github.com/sohaha/zlsgo@v1.7.13-0.20240501141223-10dd1a906f76/zstring/snowflake.go (about) 1 package zstring 2 3 import ( 4 "errors" 5 "sync" 6 "time" 7 ) 8 9 // The algorithm is inspired by Twitter's famous snowflake 10 // its link is: https://github.com/twitter/snowflake/releases/tag/snowflake-2010 11 // timestamp(ms)42 | worker id(10) | sequence(12) 12 13 const ( 14 sEpoch = 1474802888000 15 // sWorkerIDBits Num of WorkerId Bits 16 sWorkerIDBits = 10 17 sWorkerIDShift = 12 18 sTimeStampShift = 22 19 // sequenceMask equal as getSequenceMask() 20 sequenceMask = 0xfff 21 sMaxWorker = 0x3ff 22 ) 23 24 // IDWorker Struct 25 type IDWorker struct { 26 workerID int64 27 lastTimeStamp int64 28 sequence int64 29 maxWorkerID int64 30 sync.RWMutex 31 } 32 33 // NewIDWorker Generate NewIDWorker with Given workerid 34 func NewIDWorker(workerid int64) (iw *IDWorker, err error) { 35 iw = new(IDWorker) 36 37 iw.maxWorkerID = getMaxWorkerID() 38 39 if workerid > iw.maxWorkerID || workerid < 0 { 40 return nil, errors.New("worker not fit") 41 } 42 iw.workerID = workerid 43 iw.lastTimeStamp = -1 44 iw.sequence = 0 45 return iw, nil 46 } 47 48 func getMaxWorkerID() int64 { 49 return -1 ^ -1<<sWorkerIDBits 50 } 51 52 func (iw *IDWorker) timeGen() int64 { 53 return time.Now().UnixNano() / 1000 / 1000 54 } 55 56 func (iw *IDWorker) timeReGen(last int64) int64 { 57 ts := iw.timeGen() 58 println(ts, last, last-ts) 59 for { 60 if ts < last { 61 ts = iw.timeGen() 62 } else { 63 break 64 } 65 } 66 return ts 67 } 68 69 // ID Generate next id 70 func (iw *IDWorker) ID() (ts int64, err error) { 71 iw.Lock() 72 defer iw.Unlock() 73 ts = iw.timeGen() 74 if ts == iw.lastTimeStamp { 75 iw.sequence = (iw.sequence + 1) & sequenceMask 76 if iw.sequence == 0 { 77 ts = iw.timeReGen(ts) 78 } 79 } else { 80 iw.sequence = 0 81 } 82 83 if ts < iw.lastTimeStamp { 84 err = errors.New("clock moved backwards, refuse gen id") 85 return 0, err 86 } 87 iw.lastTimeStamp = ts 88 ts = (ts-sEpoch)<<sTimeStampShift | iw.workerID<<sWorkerIDShift | iw.sequence 89 return ts, nil 90 } 91 92 // ParseID reverse uid to timestamp, workid, seq 93 func ParseID(id int64) (t time.Time, ts int64, workerId int64, seq int64) { 94 seq = id & sequenceMask 95 workerId = (id >> sWorkerIDShift) & sMaxWorker 96 ts = (id >> sTimeStampShift) + sEpoch 97 t = time.Unix(ts/1000, (ts%1000)*1000000) 98 return 99 }