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  }