#pragma once#include<cstdint>#include<chrono>#include<stdexcept>#include<mutex>classsnowflake_nonlock{public:voidlock(){}voidunlock(){}};template<int64_tTwepoch,typenameLock=snowflake_nonlock>classsnowflake{usinglock_type=Lock;staticconstexprint64_tTWEPOCH=Twepoch;staticconstexprint64_tWORKER_ID_BITS=5L;staticconstexprint64_tDATACENTER_ID_BITS=5L;staticconstexprint64_tMAX_WORKER_ID=(1<<WORKER_ID_BITS)-1;staticconstexprint64_tMAX_DATACENTER_ID=(1<<DATACENTER_ID_BITS)-1;staticconstexprint64_tSEQUENCE_BITS=12L;staticconstexprint64_tWORKER_ID_SHIFT=SEQUENCE_BITS;staticconstexprint64_tDATACENTER_ID_SHIFT=SEQUENCE_BITS+WORKER_ID_BITS;staticconstexprint64_tTIMESTAMP_LEFT_SHIFT=SEQUENCE_BITS+WORKER_ID_BITS+DATACENTER_ID_BITS;staticconstexprint64_tSEQUENCE_MASK=(1<<SEQUENCE_BITS)-1;usingtime_point=std::chrono::time_point<std::chrono::steady_clock>;time_pointstart_time_point_=std::chrono::steady_clock::now();int64_tstart_millsecond_=std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();int64_tlast_timestamp_=-1;int64_tworkerid_=0;int64_tdatacenterid_=0;int64_tsequence_=0;lock_typelock_;public:snowflake()=default;snowflake(constsnowflake&)=delete;snowflake&operator=(constsnowflake&)=delete;voidinit(int64_tworkerid,int64_tdatacenterid){if(workerid>MAX_WORKER_ID||workerid<0){throwstd::runtime_error("worker Id can't be greater than 31 or less than 0");}if(datacenterid>MAX_DATACENTER_ID||datacenterid<0){throwstd::runtime_error("datacenter Id can't be greater than 31 or less than 0");}workerid_=workerid;datacenterid_=datacenterid;}int64_tnextid(){std::lock_guard<lock_type>lock(lock_);//std::chrono::steady_clock cannot decrease as physical time moves forwardautotimestamp=millsecond();if(last_timestamp_==timestamp){sequence_=(sequence_+1)&SEQUENCE_MASK;if(sequence_==0){timestamp=wait_next_millis(last_timestamp_);}}else{sequence_=0;}last_timestamp_=timestamp;return((timestamp-TWEPOCH)<<TIMESTAMP_LEFT_SHIFT)|(datacenterid_<<DATACENTER_ID_SHIFT)|(workerid_<<WORKER_ID_SHIFT)|sequence_;}private:int64_tmillsecond()constnoexcept{autodiff=std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now()-start_time_point_);returnstart_millsecond_+diff.count();}int64_twait_next_millis(int64_tlast)constnoexcept{autotimestamp=millsecond();while(timestamp<=last){timestamp=millsecond();}returntimestamp;}};