From f57d9f1de616d8d55f5366ae1bf6c576afd2dbd4 Mon Sep 17 00:00:00 2001 From: yitter Date: Mon, 17 Oct 2022 11:34:58 +0800 Subject: [PATCH] auto commit --- Delphi/README.md | 70 ++ Delphi/source/.gitignore | 5 + Delphi/source/BakClear.bat | 3 + Delphi/source/Contract/uIIdGenerator.pas | 16 + Delphi/source/Contract/uISnowWorker.pas | 13 + .../source/Contract/uIdGeneratorOptions.pas | 97 ++ .../source/Contract/uTOverCostActionArg.pas | 61 ++ Delphi/source/Core/uSnowWorkerM1.pas | 404 +++++++ Delphi/source/Core/uSnowWorkerM2.pas | 75 ++ Delphi/source/Core/uSnowWorkerM3.pas | 91 ++ Delphi/source/IdGenTest.dpr | 25 + Delphi/source/IdGenTest.dproj | 987 ++++++++++++++++++ Delphi/source/IdGenTest.res | Bin 0 -> 59520 bytes Delphi/source/uDefaultIdGenerator.pas | 137 +++ Delphi/source/uTest.dfm | 80 ++ Delphi/source/uTest.pas | 197 ++++ Delphi/source/uYitIdHelper.pas | 51 + README.md | 5 +- 18 files changed, 2315 insertions(+), 2 deletions(-) create mode 100644 Delphi/README.md create mode 100644 Delphi/source/.gitignore create mode 100644 Delphi/source/BakClear.bat create mode 100644 Delphi/source/Contract/uIIdGenerator.pas create mode 100644 Delphi/source/Contract/uISnowWorker.pas create mode 100644 Delphi/source/Contract/uIdGeneratorOptions.pas create mode 100644 Delphi/source/Contract/uTOverCostActionArg.pas create mode 100644 Delphi/source/Core/uSnowWorkerM1.pas create mode 100644 Delphi/source/Core/uSnowWorkerM2.pas create mode 100644 Delphi/source/Core/uSnowWorkerM3.pas create mode 100644 Delphi/source/IdGenTest.dpr create mode 100644 Delphi/source/IdGenTest.dproj create mode 100644 Delphi/source/IdGenTest.res create mode 100644 Delphi/source/uDefaultIdGenerator.pas create mode 100644 Delphi/source/uTest.dfm create mode 100644 Delphi/source/uTest.pas create mode 100644 Delphi/source/uYitIdHelper.pas diff --git a/Delphi/README.md b/Delphi/README.md new file mode 100644 index 0000000..20eecc4 --- /dev/null +++ b/Delphi/README.md @@ -0,0 +1,70 @@ +# ❄ idgenerator-Delphi + +## 编译环境、运行环境 + +Delphi XE10.3.3 + +## 调用示例(Delphi) + +第1步,**全局**初始化(创建只执行一次): + +```objectpascal +// 添加引用 +uses uIdGeneratorOptions, uIIdGenerator, uDefaultIdGenerator, uYitIdHelper; + +// 声明IdGeneratorOptions、YitIdHelper对象为全局变量 +var + IdGeneratorOption: TIdGeneratorOptions; + YitIdHelper: TYitIdHelper; + +// 创建并配置雪花算法参数: +begin + IdGeneratorOption := TIdGeneratorOptions.Create; + // 参数参考IdGeneratorOptions定义。 + with IdGeneratorOptiondo + begin + //以下全部为默认参数 + Method := 1; + // BaseTime := DateTime.Now.AddYears(-10); + WorkerId := 1; + + WorkerIdBitLength := 6; + SeqBitLength := 6; + + MaxSeqNumber := 0; + MinSeqNumber := 5; + + TopOverCostCount := 2000; + + DataCenterId := 0; + DataCenterIdBitLength := 0; + + TimestampType := 0; + end; + + YitIdHelper := TYitIdHelper.Create; + // 保存参数(务必调用,否则参数设置不生效): + YitIdHelper.SetIdGenerator(options); + + // 以上过程只需全局一次,且应在生成Id之前完成。 +end; +``` + +第2步,生成ID: + +```objectpascal +// 初始化后,在任何需要生成Id的地方,调用以下方法: +var Id: Int64 := YitIdHelper.NextId(); +``` + +第3步,释放内存(销毁只执行一次): + +```objectpascal +YitIdHelper.Free; +IdGeneratorOption.Free; +``` + +## 代码贡献者 +塵封追憶(Delphi-asdf, 82257695) + + diff --git a/Delphi/source/.gitignore b/Delphi/source/.gitignore new file mode 100644 index 0000000..63788ae --- /dev/null +++ b/Delphi/source/.gitignore @@ -0,0 +1,5 @@ +Win32/ +*.exe +*.dproj.local +*.identcache +__* diff --git a/Delphi/source/BakClear.bat b/Delphi/source/BakClear.bat new file mode 100644 index 0000000..2e652cb --- /dev/null +++ b/Delphi/source/BakClear.bat @@ -0,0 +1,3 @@ +DEL /S *.~* +DEL /S *.DCU +DEL /S *.DDP \ No newline at end of file diff --git a/Delphi/source/Contract/uIIdGenerator.pas b/Delphi/source/Contract/uIIdGenerator.pas new file mode 100644 index 0000000..aa76c69 --- /dev/null +++ b/Delphi/source/Contract/uIIdGenerator.pas @@ -0,0 +1,16 @@ +unit uIIdGenerator; + +interface + +type + IIdGenerator = interface + ['{C4E773E0-6E3E-410D-9F01-0826BA57BFF0}'] + /// + /// µInt64Id + /// + function NewLong(): Int64; + end; + +implementation + +end. diff --git a/Delphi/source/Contract/uISnowWorker.pas b/Delphi/source/Contract/uISnowWorker.pas new file mode 100644 index 0000000..66d4b5a --- /dev/null +++ b/Delphi/source/Contract/uISnowWorker.pas @@ -0,0 +1,13 @@ +unit uISnowWorker; + +interface + +type + ISnowWorker = interface + ['{AB5DCE35-5745-417F-9217-9094CA651A8C}'] + function NextId(): Int64; + end; + +implementation + +end. diff --git a/Delphi/source/Contract/uIdGeneratorOptions.pas b/Delphi/source/Contract/uIdGeneratorOptions.pas new file mode 100644 index 0000000..901c04f --- /dev/null +++ b/Delphi/source/Contract/uIdGeneratorOptions.pas @@ -0,0 +1,97 @@ +unit uIdGeneratorOptions; + +interface + +uses + System.DateUtils, System.SysUtils; + +type + TIdGeneratorOptions = class + private + FMethod: SmallInt; + // FBaseTime: TDateTime; + FBaseTime: Int64; + FWorkerId: Word; + FWorkerIdBitLength: Byte; + FSeqBitLength: Byte; + FMaxSeqNumber: Integer; + FMinSeqNumber: Word; + FTopOverCostCount: Integer; + FDataCenterId: Cardinal; + FDataCenterIdBitLength: Byte; + FTimestampType: Byte; + public + /// + /// ѩ㷽 + /// 1-Ư㷨|2-ͳ㷨Ĭ1 + /// + property Method: SmallInt read FMethod write FMethod default 1; + /// + /// ʱ䣨UTCʽ + /// ܳǰϵͳʱ + /// + // property BaseTime: TDateTime read FBaseTime write FBaseTime; + property BaseTime: Int64 read FBaseTime write FBaseTime; + /// + /// + /// ⲿ趨ֵ 2^WorkerIdBitLength-1 + /// + property WorkerId: Word read FWorkerId write FWorkerId default 0; + /// + /// λ + /// Ĭֵ6ȡֵΧ [1, 15]Ҫλ+λ22 + /// + property WorkerIdBitLength: Byte read FWorkerIdBitLength write FWorkerIdBitLength default 6; // 10; + /// + /// λ + /// Ĭֵ6ȡֵΧ [3, 21]Ҫλ+λ22 + /// + property SeqBitLength: Byte read FSeqBitLength write FSeqBitLength default 6; // 10; + /// + /// + /// ÷Χ [MinSeqNumber, 2^SeqBitLength-1]Ĭֵ0ʾȡֵ2^SeqBitLength-1] + /// + property MaxSeqNumber: Integer read FMaxSeqNumber write FMaxSeqNumber default 0; + /// + /// С + /// Ĭֵ5ȡֵΧ [5, MaxSeqNumber]ÿǰ5Ӧ0-4DZλ1-4ʱزӦԤλ0ֵֹԤλ + /// + property MinSeqNumber: Word read FMinSeqNumber write FMinSeqNumber default 5; + /// + /// Ưƴ + /// Ĭ2000ƼΧ500-10000йأ + /// + property TopOverCostCount: Integer read FTopOverCostCount write FTopOverCostCount default 2000; + /// + /// IDĬ0 + /// + property DataCenterId: Cardinal read FDataCenterId write FDataCenterId default 0; + /// + /// IDȣĬ0 + /// + property DataCenterIdBitLength: Byte read FDataCenterIdBitLength write FDataCenterIdBitLength default 0; + /// + /// ʱͣ0-룬1-룩Ĭ0 + /// + property TimestampType: Byte read FTimestampType write FTimestampType default 0; + + constructor Create(); overload; + constructor Create(WorkerId: Word); overload; + end; + +implementation + +{ TIdGeneratorOptions } + +constructor TIdGeneratorOptions.Create(WorkerId: Word); +begin + FBaseTime := 1582136402000; // EncodeDateTime(2020, 2, 20, 2, 20, 2, 20); + FWorkerId := WorkerId; +end; + +constructor TIdGeneratorOptions.Create(); +begin + FBaseTime := 1582136402000; // EncodeDateTime(2020, 2, 20, 2, 20, 2, 20); +end; + +end. diff --git a/Delphi/source/Contract/uTOverCostActionArg.pas b/Delphi/source/Contract/uTOverCostActionArg.pas new file mode 100644 index 0000000..d21ba2c --- /dev/null +++ b/Delphi/source/Contract/uTOverCostActionArg.pas @@ -0,0 +1,61 @@ +unit uTOverCostActionArg; + +interface + +type + TOverCostActionArg = class + private + FActionType: Integer; + FTimeTick: Int64; + FWorkerId: Word; + FOverCostCountInOneTerm: Integer; + FGenCountInOneTerm: Integer; + FTermIndex: Integer; + public + /// + /// ¼ + /// 1-ʼ2-8-Ư + /// + property ActionType: Integer read FActionType write FActionType default 0; + /// + /// ʱ + /// + property TimeTick: Int64 read FTimeTick write FTimeTick; + /// + /// + /// + property WorkerId: Word read FWorkerId write FWorkerId; + /// + /// ƯƼ + /// + property OverCostCountInOneTerm: Integer read FOverCostCountInOneTerm write FOverCostCountInOneTerm default 0; + /// + /// ƯڼID + /// + property GenCountInOneTerm: Integer read FGenCountInOneTerm write FGenCountInOneTerm default 0; + /// + /// Ư + /// + property TermIndex: Integer read FTermIndex write FTermIndex default 0; + + constructor Create(const WorkerId: Word; const TimeTick: Int64; const ActionType: Integer = 0; + const OverCostCountInOneTerm: Integer = 0; const GenCountWhenOverCost: Integer = 0; + const Index: Integer = 0); overload; + end; + +implementation + +{ TOverCostActionArg } + +constructor TOverCostActionArg.Create(const WorkerId: Word; const TimeTick: Int64; const ActionType: Integer; + const OverCostCountInOneTerm: Integer; const GenCountWhenOverCost: Integer; const Index: Integer); +begin + FWorkerId := WorkerId; + FTimeTick := TimeTick; + FActionType := ActionType; + FOverCostCountInOneTerm := OverCostCountInOneTerm; + FGenCountInOneTerm := GenCountWhenOverCost; + FTermIndex := Index; +end; + +end. diff --git a/Delphi/source/Core/uSnowWorkerM1.pas b/Delphi/source/Core/uSnowWorkerM1.pas new file mode 100644 index 0000000..d2b9ba0 --- /dev/null +++ b/Delphi/source/Core/uSnowWorkerM1.pas @@ -0,0 +1,404 @@ +unit uSnowWorkerM1; + +interface + +uses + uISnowWorker, uIdGeneratorOptions, System.SyncObjs, uTOverCostActionArg, System.DateUtils, System.SysUtils; + +/// +/// ѩƯ㷨 +/// +type + TSnowWorkerM1 = class(TInterfacedObject, ISnowWorker) + private + // private static long _StartTimeTick = 0; + // private static long _BaseTimeTick = 0; + protected + SyncLock: TCriticalSection; + protected + // FBaseTime: TDateTime; + FBaseTime: Int64; + FWorkerId: Word; + FWorkerIdBitLength: Byte; + FSeqBitLength: Byte; + FMaxSeqNumber: Integer; + FMinSeqNumber: Word; + FTopOverCostCount: Integer; + // + FTimestampShift: Byte; + FCurrentSeqNumber: Word; + FLastTimeTick: Int64; + FTurnBackTimeTick: Int64; + FTurnBackIndex: Byte; + FIsOverCost: Boolean; + FOverCostCountInOneTerm: Integer; + // + FGenCountInOneTerm: Integer; + FTermIndex: Integer; + protected + /// + /// ʱ + /// + // property BaseTime: TDateTime read FBaseTime write FBaseTime; + property BaseTime: Int64 read FBaseTime; + /// + /// + /// + property WorkerId: Word read FWorkerId default 0; + /// + /// λ + /// + property WorkerIdBitLength: Byte read FWorkerIdBitLength default 0; + /// + /// λ + /// + property SeqBitLength: Byte read FSeqBitLength default 0; + /// + /// + /// + property MaxSeqNumber: Integer read FMaxSeqNumber default 0; + /// + /// С + /// + property MinSeqNumber: Word read FMinSeqNumber default 0; + /// + /// Ưƴ + /// + property TopOverCostCount: Integer read FTopOverCostCount write FTopOverCostCount default 0; + + // + property TimestampShift: Byte read FTimestampShift write FTimestampShift default 0; + + // + property CurrentSeqNumber: Word read FCurrentSeqNumber write FCurrentSeqNumber; + property LastTimeTick: Int64 read FLastTimeTick write FLastTimeTick default 0; // -1L + property TurnBackTimeTick: Int64 read FTurnBackTimeTick write FTurnBackTimeTick default 0; // -1L; + property TurnBackIndex: Byte read FTurnBackIndex write FTurnBackIndex default 0; + property IsOverCost: Boolean read FIsOverCost write FIsOverCost default False; + property OverCostCountInOneTerm: Integer read FOverCostCountInOneTerm write FOverCostCountInOneTerm default 0; +{$IFDEF DEBUG} + property GenCountInOneTerm: Integer read FGenCountInOneTerm write FGenCountInOneTerm default 0; + property TermIndex: Integer read FTermIndex write FTermIndex default 0; +{$ENDIF} + protected +{$IFDEF DEBUG} + procedure DoGenIdAction(arg: TOverCostActionArg); + procedure BeginOverCostAction(UseTimeTick: Int64); + procedure EndOverCostAction(UseTimeTick: Int64); + procedure BeginTurnBackAction(UseTimeTick: Int64); + procedure EndTurnBackAction(UseTimeTick: Int64); +{$ENDIF} + // + function GetSecondTimeStamp(): Int64; + function GetMillisecondTimeStamp(): Int64; + // + function CalcId(UseTimeTick: Int64): Int64; virtual; + function CalcTurnBackId(UseTimeTick: Int64): Int64; virtual; + function NextOverCostId(): Int64; + function GetCurrentTimeTick(): Int64; virtual; + function GetNextTimeTick(): Int64; + public + // Action GenAction { get; set; } + function NextId(): Int64; + function NextNormalId(): Int64; + constructor Create(options: TIdGeneratorOptions); overload; + destructor Destroy(); override; + end; + +implementation + +{ TSnowWorkerM1 } + +function TSnowWorkerM1.GetSecondTimeStamp(): Int64; +var + ST: TDateTime; +begin + ST := EncodeDateTime(1970, 1, 1, 0, 0, 0, 0); + Result := MilliSecondsBetween(Now(), ST) - 28800; // 8*60*60; +end; + +function TSnowWorkerM1.GetMillisecondTimeStamp(): Int64; +var + ST: TDateTime; +begin + ST := EncodeDateTime(1970, 1, 1, 0, 0, 0, 0); + Result := MilliSecondsBetween(Now(), ST) - 28800000; // 8*60*60*1000; +end; + +constructor TSnowWorkerM1.Create(options: TIdGeneratorOptions); +begin + SyncLock := TCriticalSection.Create; + // 1.BaseTime + if (options.BaseTime <> 0) then + FBaseTime := options.BaseTime; + + // 2.WorkerIdBitLength + if (options.WorkerIdBitLength <> 0) then + begin + FWorkerIdBitLength := 6; + end + else + begin + FWorkerIdBitLength := options.WorkerIdBitLength; + end; + + // 3.WorkerId + FWorkerId := options.WorkerId; + + // 4.SeqBitLength + if (options.SeqBitLength = 0) then + begin + FSeqBitLength := 6; + end + else + begin + FSeqBitLength := options.SeqBitLength; + end; + + // 5.MaxSeqNumber + if (MaxSeqNumber = 0) then + begin + FMaxSeqNumber := (1 shl SeqBitLength) - 1; + end + else + begin + FMaxSeqNumber := options.MaxSeqNumber; + end; + + // 6.MinSeqNumber + FMinSeqNumber := options.MinSeqNumber; + + // 7.Others + FTopOverCostCount := options.TopOverCostCount; + // if (TopOverCostCount = 0) then + // begin + // FTopOverCostCount := 2000; + // end; + + FTimestampShift := Byte(WorkerIdBitLength + SeqBitLength); + FCurrentSeqNumber := options.MinSeqNumber; + + // FBaseTimeTick = BaseTime.Ticks; + // FStartTimeTick = (long)(DateTime.UtcNow.Subtract(BaseTime).TotalMilliseconds) - Environment.TickCount; +end; + +destructor TSnowWorkerM1.Destroy(); +begin + SyncLock.Free; + + inherited; +end; + +{$IFDEF DEBUG} + +procedure TSnowWorkerM1.DoGenIdAction(arg: TOverCostActionArg); +begin + // //return; + // Task.Run(() => + // { + // GenAction(arg); + // }); +end; + +procedure TSnowWorkerM1.BeginOverCostAction(UseTimeTick: Int64); +begin + +end; + +procedure TSnowWorkerM1.EndOverCostAction(UseTimeTick: Int64); +begin + +end; + +procedure TSnowWorkerM1.BeginTurnBackAction(UseTimeTick: Int64); +begin + +end; + +procedure TSnowWorkerM1.EndTurnBackAction(UseTimeTick: Int64); +begin + +end; +{$ENDIF} + +function TSnowWorkerM1.NextOverCostId(): Int64; +var + CurrentTimeTick: Int64; +begin + CurrentTimeTick := GetCurrentTimeTick(); + + if (CurrentTimeTick > FLastTimeTick) then + begin +{$IFDEF DEBUG} + EndOverCostAction(CurrentTimeTick); + FGenCountInOneTerm := 0; +{$ENDIF} + FLastTimeTick := CurrentTimeTick; + FCurrentSeqNumber := FMinSeqNumber; + FIsOverCost := False; + FOverCostCountInOneTerm := 0; + + Result := CalcId(FLastTimeTick); + Exit; + end; + + if (FOverCostCountInOneTerm >= FTopOverCostCount) then + begin +{$IFDEF DEBUG} + EndOverCostAction(CurrentTimeTick); + FGenCountInOneTerm := 0; +{$ENDIF} + // TODO: Ưֹȴʱʱʱزϳ˴ܵȴϳʱ䡣 + // ŻΪƯֹʱʱزӦ߼ʵͣݲ + FLastTimeTick := GetNextTimeTick(); + FCurrentSeqNumber := FMinSeqNumber; + FIsOverCost := False; + FOverCostCountInOneTerm := 0; + + Result := CalcId(FLastTimeTick); + Exit; + end; + + if (FCurrentSeqNumber > FMaxSeqNumber) then + begin +{$IFDEF DEBUG} + Inc(FGenCountInOneTerm); +{$ENDIF} + Inc(FLastTimeTick); + FCurrentSeqNumber := FMinSeqNumber; + FIsOverCost := True; + Inc(FOverCostCountInOneTerm); + + Result := CalcId(FLastTimeTick); + Exit; + end; + +{$IFDEF DEBUG} + Inc(FGenCountInOneTerm); +{$ENDIF} + Result := CalcId(FLastTimeTick); +end; + +function TSnowWorkerM1.NextNormalId: Int64; +var + CurrentTimeTick: Int64; +begin + CurrentTimeTick := GetCurrentTimeTick(); + + if (CurrentTimeTick < FLastTimeTick) then + begin + if (FTurnBackTimeTick < 1) then + begin + FTurnBackTimeTick := FLastTimeTick - 1; + + Inc(FTurnBackIndex); + // ÿǰ5λԤλ0ֵֹ1-4ʱز + // ֧4λز򣨱زصIDظ޴λزѭʹã + if (FTurnBackIndex > 4) then + begin + FTurnBackIndex := 1; + end; + +{$IFDEF DEBUG} + BeginTurnBackAction(FTurnBackTimeTick); +{$ENDIF} + end; + + // Sleep(1); + Result := CalcTurnBackId(FTurnBackTimeTick); + Exit; + end; + + // ʱ׷ƽʱ_TurnBackTimeTick + if (FTurnBackTimeTick > 0) then + begin +{$IFDEF DEBUG} + EndTurnBackAction(FTurnBackTimeTick); +{$ENDIF} + FTurnBackTimeTick := 0; + end; + + if (CurrentTimeTick > FLastTimeTick) then + begin + FLastTimeTick := CurrentTimeTick; + FCurrentSeqNumber := FMinSeqNumber; + + Result := CalcId(FLastTimeTick); + Exit; + end; + + if (FCurrentSeqNumber > FMaxSeqNumber) then + begin +{$IFDEF DEBUG} + BeginOverCostAction(CurrentTimeTick); + Inc(FTermIndex); + FGenCountInOneTerm := 1; +{$ENDIF} + FOverCostCountInOneTerm := 1; + Inc(FLastTimeTick); + FCurrentSeqNumber := FMinSeqNumber; + FIsOverCost := True; + + Result := CalcId(FLastTimeTick); + Exit; + end; + + Result := CalcId(FLastTimeTick); +end; + +function TSnowWorkerM1.CalcId(UseTimeTick: Int64): Int64; +begin + Result := ((UseTimeTick shl FTimestampShift) + (Int64(FWorkerId) shl FSeqBitLength) + Cardinal(FCurrentSeqNumber)); + + Inc(FCurrentSeqNumber); +end; + +function TSnowWorkerM1.CalcTurnBackId(UseTimeTick: Int64): Int64; +begin + Result := ((UseTimeTick shl FTimestampShift) + (Int64(FWorkerId) shl FSeqBitLength) + FTurnBackIndex); + + Dec(FTurnBackTimeTick); +end; + +function TSnowWorkerM1.GetCurrentTimeTick(): Int64; +var + Millis: Int64; +begin + // Millis := DateTimeToUnix(Now(), False); + Millis := GetMillisecondTimeStamp(); + Result := Millis - FBaseTime; +end; + +function TSnowWorkerM1.GetNextTimeTick(): Int64; +var + TempTimeTicker: Int64; +begin + TempTimeTicker := GetCurrentTimeTick(); + while (TempTimeTicker <= FLastTimeTick) do + begin + // Sleep(1); + TSpinWait.SpinUntil( + function(): Boolean + begin + Result := False; + end, 1); + TempTimeTicker := GetCurrentTimeTick(); + end; + + Result := TempTimeTicker; +end; + +function TSnowWorkerM1.NextId(): Int64; +begin + SyncLock.Enter; + try + if FIsOverCost then + Result := NextOverCostId() + else + Result := NextNormalId(); + finally + SyncLock.Leave; + end; +end; + +end. diff --git a/Delphi/source/Core/uSnowWorkerM2.pas b/Delphi/source/Core/uSnowWorkerM2.pas new file mode 100644 index 0000000..6e902eb --- /dev/null +++ b/Delphi/source/Core/uSnowWorkerM2.pas @@ -0,0 +1,75 @@ +unit uSnowWorkerM2; + +interface + +uses System.SysUtils, uSnowWorkerM1, uIdGeneratorOptions, System.SyncObjs; + +/// +/// ѩ㷨 +/// +type + TSnowWorkerM2 = class(TSnowWorkerM1) + public + function NextId(): Int64; + constructor Create(options: TIdGeneratorOptions); overload; + end; + +implementation + +{ TSnowWorkerM2 } + +function IncX(var x: Integer): Integer; inline; +begin + Result := x; + Inc(x); +end; + +constructor TSnowWorkerM2.Create(options: TIdGeneratorOptions); +begin + inherited Create(options); +end; + +function TSnowWorkerM2.NextId(): Int64; +var + CurrentTimeTick: Int64; +begin + SyncLock.Enter; + try + CurrentTimeTick := GetCurrentTimeTick(); + + if (FLastTimeTick = CurrentTimeTick) then + begin + // if (IncX(FCurrentSeqNumber) > FMaxSeqNumber) then + // begin + // FCurrentSeqNumber := FMinSeqNumber; + // CurrentTimeTick := GetNextTimeTick(); + // end; + if ((FCurrentSeqNumber) > FMaxSeqNumber) then + begin + FCurrentSeqNumber := FMinSeqNumber; + CurrentTimeTick := GetNextTimeTick(); + end + else + begin + Inc(FCurrentSeqNumber); + end; + end + else + begin + FCurrentSeqNumber := FMinSeqNumber; + end; + + if (CurrentTimeTick < FLastTimeTick) then + begin + raise Exception.Create(Format('Time error for %d milliseconds', [FLastTimeTick - CurrentTimeTick])); + end; + + FLastTimeTick := CurrentTimeTick; + Result := ((CurrentTimeTick shl FTimestampShift) + (Int64(FWorkerId) shl FSeqBitLength) + + Cardinal(FCurrentSeqNumber)); + finally + SyncLock.Leave; + end; +end; + +end. diff --git a/Delphi/source/Core/uSnowWorkerM3.pas b/Delphi/source/Core/uSnowWorkerM3.pas new file mode 100644 index 0000000..e90d59e --- /dev/null +++ b/Delphi/source/Core/uSnowWorkerM3.pas @@ -0,0 +1,91 @@ +unit uSnowWorkerM3; + +interface + +uses System.SysUtils, System.DateUtils, uSnowWorkerM1, uIdGeneratorOptions, System.SyncObjs; + +/// +/// ѩƯ㷨֧ID뼶ʱ +/// +type + TSnowWorkerM3 = class(TSnowWorkerM1) + protected + FDataCenterId: Cardinal; + FDataCenterIdBitLength: Byte; + FTimestampType: Byte; + protected + /// + /// IDĬ0 + /// + property DataCenterId: Cardinal read FDataCenterId default 0; + /// + /// IDȣĬ0 + /// + property DataCenterIdBitLength: Byte read FDataCenterIdBitLength default 0; + /// + /// ʱͣ0-룬1-룩Ĭ0 + /// + property TimestampType: Byte read FTimestampType default 0; + protected + function CalcId(UseTimeTick: Int64): Int64; override; + function CalcTurnBackId(UseTimeTick: Int64): Int64; override; + function GetCurrentTimeTick(): Int64; override; + public + constructor Create(options: TIdGeneratorOptions); overload; + end; + +implementation + +{ TSnowWorkerM3 } + +constructor TSnowWorkerM3.Create(options: TIdGeneratorOptions); +begin + // 뼶ʱ + FTimestampType := options.TimestampType; + + // DataCenter + FDataCenterId := options.DataCenterId; + FDataCenterIdBitLength := options.DataCenterIdBitLength; + + if (FTimestampType = 1) then + begin + FTopOverCostCount := 0; + end; + FTimestampShift := Byte(DataCenterIdBitLength + WorkerIdBitLength + SeqBitLength); + + inherited Create(options); +end; + +function TSnowWorkerM3.CalcId(UseTimeTick: Int64): Int64; +begin + Result := ((UseTimeTick shl FTimestampShift) + (Int64(DataCenterId) shl DataCenterIdBitLength) + + (Int64(WorkerId) shl SeqBitLength) + Int64(FCurrentSeqNumber)); + + Inc(FCurrentSeqNumber); +end; + +function TSnowWorkerM3.CalcTurnBackId(UseTimeTick: Int64): Int64; +begin + Result := ((UseTimeTick shl FTimestampShift) + (Int64(DataCenterId) shl DataCenterIdBitLength) + + (Int64(WorkerId) shl SeqBitLength) + FTurnBackIndex); + + Dec(FTurnBackTimeTick); +end; + +function TSnowWorkerM3.GetCurrentTimeTick: Int64; +var + Millis: Int64; +begin + if (TimestampType = 0) then + begin + Millis := GetMillisecondTimeStamp(); + Result := Millis - FBaseTime; + end + else + begin + Millis := GetSecondTimeStamp(); + Result := Millis - FBaseTime; + end; +end; + +end. diff --git a/Delphi/source/IdGenTest.dpr b/Delphi/source/IdGenTest.dpr new file mode 100644 index 0000000..8205adf --- /dev/null +++ b/Delphi/source/IdGenTest.dpr @@ -0,0 +1,25 @@ +program IdGenTest; + +uses + Vcl.Forms, + uTest in 'uTest.pas' {fTest} , + uISnowWorker in 'Contract\uISnowWorker.pas', + uIIdGenerator in 'Contract\uIIdGenerator.pas', + uTOverCostActionArg in 'Contract\uTOverCostActionArg.pas', + uIdGeneratorOptions in 'Contract\uIdGeneratorOptions.pas', + uSnowWorkerM1 in 'Core\uSnowWorkerM1.pas', + uSnowWorkerM2 in 'Core\uSnowWorkerM2.pas', + uSnowWorkerM3 in 'Core\uSnowWorkerM3.pas', + uDefaultIdGenerator in 'uDefaultIdGenerator.pas', + uYitIdHelper in 'uYitIdHelper.pas'; + +{$R *.res} + +begin + Application.Initialize; + Application.MainFormOnTaskbar := True; + ReportMemoryLeaksOnShutdown := (DebugHook <> 0); + Application.CreateForm(TfTest, fTest); + Application.Run; + +end. diff --git a/Delphi/source/IdGenTest.dproj b/Delphi/source/IdGenTest.dproj new file mode 100644 index 0000000..97237f5 --- /dev/null +++ b/Delphi/source/IdGenTest.dproj @@ -0,0 +1,987 @@ + + + {9237743A-F2B2-49BE-941B-C151799790AF} + 18.8 + VCL + IdGenTest.dpr + True + Release + Win32 + 1 + Application + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + .\$(Platform)\$(Config) + .\$(Platform)\$(Config) + false + false + false + false + false + System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) + $(BDS)\bin\delphi_PROJECTICON.ico + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + IdGenTest + + + DBXSqliteDriver;dxFlowChartRS26;dxPSdxMapControlLnkRS26;DBXDb2Driver;vclactnband;dxBarRS26;vclFireDAC;dxFireDACEMFRS26;tethering;KRCommon;dxSpreadSheetInplaceRichEditRS26;FireDACADSDriver;dxRichEditCoreRS26;dxPSdxSpreadSheetLnkRS26;FireDACMSSQLDriver;vcltouch;vcldb;svn;dxPSTeeChartRS26;dxGDIPlusRS26;dxPSdxFCLnkRS26;vclib;frxTee26;dxCloudServiceLibraryRS26;dxPSLnksRS26;FireDACDBXDriver;cxGridRS26;dxPsPrVwAdvRS26;dxPDFViewerRS26;vclx;dxPScxTLLnkRS26;RESTBackendComponents;VCLRESTComponents;fsTee26;vclie;bindengine;CloudService;dxmdsRS26;FireDACMySQLDriver;fsIBX26;frx26;dxdborRS26;DataSnapClient;dxFireDACServerModeRS26;bindcompdbx;fsFD26;DBXSybaseASEDriver;IndyIPServer;cxPivotGridRS26;IndySystem;frxDBX26;fsADO26;CnPack_D103R;cxTreeListdxBarPopupMenuRS26;dsnapcon;cxTreeListRS26;dxPScxPivotGridLnkRS26;cxSchedulerRibbonStyleEventEditorRS26;dxPSCoreRS26;inetwinsockets;FireDACMSAccDriver;FireDACInfxDriver;fmxFireDAC;vclimg;dxSpreadSheetRS26;dxBarExtItemsRS26;dxPSdxGaugeControlLnkRS26;emshosting;DBXOdbcDriver;FireDACTDataDriver;FMXTee;dxdbtrRS26;dxRichEditControlCoreRS26;soaprtl;DbxCommonDriver;dxFlowChartAdvancedCustomizeFormRS26;dxDockingRS26;KRWeb;xmlrtl;DataSnapNativeClient;soapmidas;fmxobj;cxLibraryRS26;rtl;emsserverresource;DbxClientDriver;DBXSybaseASADriver;dxPScxSchedulerLnkRS26;dxSpreadSheetConditionalFormattingDialogsRS26;appanalytics;dxRibbonCustomizationFormRS26;cxSchedulerGridRS26;KRFile;IndyIPClient;bindcompvcl;dxFlowChartLayoutsRS26;TeeUI;dxADOEMFRS26;VclSmp;FireDACODBCDriver;dxRibbonRS26;DataSnapIndy10ServerTransport;dxPScxCommonRS26;dxRichEditDocumentModelRS26;DataSnapProviderClient;FireDACMongoDBDriver;dxFlowChartDesignerRS26;dxPScxGridLnkRS26;dxSpreadSheetCoreRS26;DataSnapServerMidas;RESTComponents;DBXInterBaseDriver;sgcWebSocketsD10_3;dxPScxExtCommonRS26;emsclientfiredac;DataSnapFireDAC;svnui;frxFD26;DBXMSSQLDriver;dxRichEditControlRS26;DatasnapConnectorsFreePascal;dxGaugeControlRS26;dxorgcRS26;dxPScxVGridLnkRS26;bindcompfmx;DBXOracleDriver;inetdb;dxBarDBNavRS26;dxDBXServerModeRS26;SPCOMM;RaizeComponentsVcl;FmxTeeUI;emsedge;RaizeComponentsVclDb;FireDACIBDriver;fmx;fmxdae;dxServerModeRS26;dxWizardControlRS26;dxTabbedMDIRS26;fs26;dxEMFRS26;dbexpress;IndyCore;dxComnRS26;KRGraphics;frxIntIO26;dsnap;DataSnapCommon;emsclient;FireDACCommon;frxcs26;DataSnapConnectors;cxSchedulerTreeBrowserRS26;dxADOServerModeRS26;soapserver;cxPivotGridOLAPRS26;cxVerticalGridRS26;dxtrmdRS26;FireDACOracleDriver;DBXMySQLDriver;cxSchedulerRS26;cxSchedulerWebServiceStorageRS26;dxPSdxLCLnkRS26;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;dxMapControlRS26;frxIntIOIndy26;inet;dxSpellCheckerRS26;IndyIPCommon;dxSpreadSheetCoreConditionalFormattingDialogsRS26;vcl;dxPSdxDBOCLnkRS26;EhLib260;frxDB26;FireDACDb2Driver;dxSpreadSheetReportDesignerRS26;dxPScxPCProdRS26;dxNavBarRS26;fsDB26;dxCoreRS26;cxExportRS26;TeeDB;FireDAC;dxHttpIndyRequestRS26;dxPSPrVwRibbonRS26;frxe26;FireDACSqliteDriver;FireDACPgDriver;ibmonitor;FireDACASADriver;dxPSRichEditControlLnkRS26;cxPivotGridChartRS26;frxIBX26;dxPSDBTeeChartRS26;ibxpress;Tee;DataSnapServer;ibxbindings;dxPSdxDBTVLnkRS26;FireDACDSDriver;vclwinx;frxADO26;dxTileControlRS26;KRAutomation;dxSkinsCoreRS26;CustomIPTransport;vcldsnap;bindcomp;dxPSdxOCLnkRS26;DBXInformixDriver;dbxcds;adortl;dxSpreadSheetCoreDialogsRS26;dxBarExtDBItemsRS26;dsnapxml;dbrtl;IndyProtocols;inetdbxpress;dxPSdxPDFViewerLnkRS26;dxRichEditInplaceRS26;fmxase;$(DCC_UsePackage) + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + $(BDS)\bin\default_app.manifest + .\ + + + DBXSqliteDriver;dxFlowChartRS26;dxPSdxMapControlLnkRS26;DBXDb2Driver;vclactnband;dxBarRS26;vclFireDAC;dxFireDACEMFRS26;tethering;dxSpreadSheetInplaceRichEditRS26;FireDACADSDriver;dxRichEditCoreRS26;dxPSdxSpreadSheetLnkRS26;FireDACMSSQLDriver;vcltouch;vcldb;dxPSTeeChartRS26;dxGDIPlusRS26;dxPSdxFCLnkRS26;vclib;dxCloudServiceLibraryRS26;dxPSLnksRS26;FireDACDBXDriver;cxGridRS26;dxPsPrVwAdvRS26;dxPDFViewerRS26;vclx;dxPScxTLLnkRS26;RESTBackendComponents;VCLRESTComponents;vclie;bindengine;CloudService;dxmdsRS26;FireDACMySQLDriver;dxdborRS26;DataSnapClient;dxFireDACServerModeRS26;bindcompdbx;DBXSybaseASEDriver;IndyIPServer;cxPivotGridRS26;IndySystem;cxTreeListdxBarPopupMenuRS26;dsnapcon;cxTreeListRS26;dxPScxPivotGridLnkRS26;cxSchedulerRibbonStyleEventEditorRS26;dxPSCoreRS26;FireDACMSAccDriver;FireDACInfxDriver;fmxFireDAC;vclimg;dxSpreadSheetRS26;dxBarExtItemsRS26;dxPSdxGaugeControlLnkRS26;emshosting;DBXOdbcDriver;FireDACTDataDriver;FMXTee;dxdbtrRS26;dxRichEditControlCoreRS26;soaprtl;DbxCommonDriver;dxFlowChartAdvancedCustomizeFormRS26;dxDockingRS26;xmlrtl;DataSnapNativeClient;soapmidas;fmxobj;cxLibraryRS26;rtl;emsserverresource;DbxClientDriver;DBXSybaseASADriver;dxPScxSchedulerLnkRS26;dxSpreadSheetConditionalFormattingDialogsRS26;appanalytics;dxRibbonCustomizationFormRS26;cxSchedulerGridRS26;IndyIPClient;bindcompvcl;dxFlowChartLayoutsRS26;TeeUI;dxADOEMFRS26;VclSmp;FireDACODBCDriver;dxRibbonRS26;DataSnapIndy10ServerTransport;dxPScxCommonRS26;dxRichEditDocumentModelRS26;DataSnapProviderClient;FireDACMongoDBDriver;dxFlowChartDesignerRS26;dxPScxGridLnkRS26;dxSpreadSheetCoreRS26;DataSnapServerMidas;RESTComponents;DBXInterBaseDriver;sgcWebSocketsD10_3;dxPScxExtCommonRS26;emsclientfiredac;DataSnapFireDAC;DBXMSSQLDriver;dxRichEditControlRS26;DatasnapConnectorsFreePascal;dxGaugeControlRS26;dxorgcRS26;dxPScxVGridLnkRS26;bindcompfmx;DBXOracleDriver;inetdb;dxBarDBNavRS26;dxDBXServerModeRS26;RaizeComponentsVcl;FmxTeeUI;emsedge;RaizeComponentsVclDb;FireDACIBDriver;fmx;fmxdae;dxServerModeRS26;dxWizardControlRS26;dxTabbedMDIRS26;dxEMFRS26;dbexpress;IndyCore;dxComnRS26;dsnap;DataSnapCommon;emsclient;FireDACCommon;DataSnapConnectors;cxSchedulerTreeBrowserRS26;dxADOServerModeRS26;soapserver;cxPivotGridOLAPRS26;cxVerticalGridRS26;dxtrmdRS26;FireDACOracleDriver;DBXMySQLDriver;cxSchedulerRS26;cxSchedulerWebServiceStorageRS26;dxPSdxLCLnkRS26;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;dxMapControlRS26;inet;dxSpellCheckerRS26;IndyIPCommon;dxSpreadSheetCoreConditionalFormattingDialogsRS26;vcl;dxPSdxDBOCLnkRS26;EhLib260;FireDACDb2Driver;dxSpreadSheetReportDesignerRS26;dxPScxPCProdRS26;dxNavBarRS26;dxCoreRS26;cxExportRS26;TeeDB;FireDAC;dxHttpIndyRequestRS26;dxPSPrVwRibbonRS26;FireDACSqliteDriver;FireDACPgDriver;ibmonitor;FireDACASADriver;dxPSRichEditControlLnkRS26;cxPivotGridChartRS26;dxPSDBTeeChartRS26;ibxpress;Tee;DataSnapServer;ibxbindings;dxPSdxDBTVLnkRS26;FireDACDSDriver;vclwinx;dxTileControlRS26;dxSkinsCoreRS26;CustomIPTransport;vcldsnap;bindcomp;dxPSdxOCLnkRS26;DBXInformixDriver;dbxcds;adortl;dxSpreadSheetCoreDialogsRS26;dxBarExtDBItemsRS26;dsnapxml;dbrtl;IndyProtocols;inetdbxpress;dxPSdxPDFViewerLnkRS26;dxRichEditInplaceRS26;fmxase;$(DCC_UsePackage) + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + false + true + PerMonitorV2 + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + true + PerMonitorV2 + true + 1033 + + + + MainSource + + +
fTest
+
+ + + + + + + + + +
$R *.res
+
+ + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + Application + + + + IdGenTest.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + + + IdGenTest.exe + true + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + Contents\MacOS + 1 + .framework + + + Contents\MacOS + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + Contents\Resources\StartUp\ + 0 + + + Contents\Resources\StartUp\ + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + 1 + + + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + Contents + 1 + + + Contents + 1 + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + Contents\MacOS + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + True + False + + + 12 + + + + +
diff --git a/Delphi/source/IdGenTest.res b/Delphi/source/IdGenTest.res new file mode 100644 index 0000000000000000000000000000000000000000..f06d283a91f0ee97e07ba9a9c373947c68db69ab GIT binary patch literal 59520 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 literal 0 HcmV?d00001 diff --git a/Delphi/source/uDefaultIdGenerator.pas b/Delphi/source/uDefaultIdGenerator.pas new file mode 100644 index 0000000..80453b7 --- /dev/null +++ b/Delphi/source/uDefaultIdGenerator.pas @@ -0,0 +1,137 @@ +unit uDefaultIdGenerator; + +interface + +uses + uIIdGenerator, uISnowWorker, uIdGeneratorOptions, System.DateUtils, System.SysUtils; + +type + TDefaultIdGenerator = class(TInterfacedObject, IIdGenerator) + private + SnowWorker: ISnowWorker; + public + constructor Create(options: TIdGeneratorOptions); overload; + function NewLong(): Int64; + end; + +implementation + +uses + uSnowWorkerM1, uSnowWorkerM2, uSnowWorkerM3; + +{ TDefaultIdGenerator } + +function GetMillisecondTimeStamp(ET: TDateTime): Int64; +var + ST: TDateTime; +begin + ST := EncodeDateTime(1970, 1, 1, 0, 0, 0, 0); + Result := MilliSecondsBetween(ET, ST) - 28800000; // 8*60*60*1000; +end; + +constructor TDefaultIdGenerator.Create(options: TIdGeneratorOptions); +var + MaxLength, MaxWorkerIdNumber, MaxDataCenterIdNumber, MaxSeqNumber: Integer; +begin + if (options = nil) then + begin + raise Exception.Create('options error.'); + end; + + // 1.BaseTime + if (options.BaseTime < GetMillisecondTimeStamp(IncYear(Now(), -50))) or + (options.BaseTime > GetMillisecondTimeStamp(Now())) then + begin + raise Exception.Create('BaseTime error.'); + end; + + // 2.WorkerIdBitLength + if (options.TimestampType = 0) then + MaxLength := 22 + else + MaxLength := 31; // 뼶ʱʱŴ31λ + if (options.WorkerIdBitLength <= 0) then + begin + raise Exception.Create('WorkerIdBitLength error.(range:[1, 21])'); + end; + if (options.DataCenterIdBitLength + options.WorkerIdBitLength + options.SeqBitLength > MaxLength) then + begin + raise Exception.Create('errorDataCenterIdBitLength + WorkerIdBitLength + SeqBitLength <= ' + IntToStr(MaxLength)); + end; + + // 3.WorkerId + MaxWorkerIdNumber := (1 shl options.WorkerIdBitLength) - 1; + if (MaxWorkerIdNumber = 0) then + begin + MaxWorkerIdNumber := 63; + end; + if ((options.WorkerId < 0) or (options.WorkerId > MaxWorkerIdNumber)) then + begin + raise Exception.Create('WorkerId error. (range:[0, ' + IntToStr(MaxWorkerIdNumber) + ']'); + end; + + MaxDataCenterIdNumber := (1 shl options.DataCenterIdBitLength) - 1; + if (options.DataCenterId < 0) or (options.DataCenterId > MaxDataCenterIdNumber) then + begin + raise Exception.Create('DataCenterId error. (range:[0, ' + IntToStr(MaxDataCenterIdNumber) + ']'); + end; + + // 4.SeqBitLength + if ((options.SeqBitLength < 2) or (options.SeqBitLength > 21)) then + begin + raise Exception.Create('SeqBitLength error. (range:[2, 21])'); + end; + + // 5.MaxSeqNumber + MaxSeqNumber := (1 shl options.SeqBitLength) - 1; + if (MaxSeqNumber = 0) then + begin + MaxSeqNumber := 63; + end; + if ((options.MaxSeqNumber < 0) or (options.MaxSeqNumber > MaxSeqNumber)) then + begin + raise Exception.Create('MaxSeqNumber error. (range:[1, ' + IntToStr(MaxSeqNumber) + ']'); + end; + + // 6.MinSeqNumber + if ((options.MinSeqNumber < 5) or (options.MinSeqNumber > MaxSeqNumber)) then + begin + raise Exception.Create('MinSeqNumber error. (range:[5, ' + IntToStr(MaxSeqNumber) + ']'); + end; + + // 7.TopOverCostCount + if ((options.TopOverCostCount < 0) or (options.TopOverCostCount > 10000)) then + begin + raise Exception.Create('TopOverCostCount error. (range:[0, 10000]'); + end; + + case (options.Method) of + 2: + begin + SnowWorker := TSnowWorkerM2.Create(options); + end; + else + begin + if ((options.DataCenterIdBitLength = 0) and (options.TimestampType = 0)) then + begin + SnowWorker := TSnowWorkerM1.Create(options); + end + else + begin + SnowWorker := TSnowWorkerM3.Create(options); + end; + end; + end; + + if (options.Method <> 2) then + begin + Sleep(500); + end; +end; + +function TDefaultIdGenerator.NewLong(): Int64; +begin + Result := SnowWorker.NextId(); +end; + +end. diff --git a/Delphi/source/uTest.dfm b/Delphi/source/uTest.dfm new file mode 100644 index 0000000..e17d456 --- /dev/null +++ b/Delphi/source/uTest.dfm @@ -0,0 +1,80 @@ +object fTest: TfTest + Left = 0 + Top = 0 + Caption = #38634#33457#31639#27861#27979#35797 + ClientHeight = 509 + ClientWidth = 668 + Color = clBtnFace + Font.Charset = ANSI_CHARSET + Font.Color = clWindowText + Font.Height = -15 + Font.Name = 'Microsoft YaHei UI' + Font.Style = [] + OldCreateOrder = False + Position = poDesktopCenter + OnClose = FormClose + OnCreate = FormCreate + PixelsPerInch = 96 + TextHeight = 20 + object mmo1: TMemo + Left = 0 + Top = 100 + Width = 668 + Height = 409 + Align = alClient + ReadOnly = True + ScrollBars = ssVertical + TabOrder = 1 + end + object Panel1: TPanel + Left = 0 + Top = 0 + Width = 668 + Height = 100 + Align = alTop + Caption = 'Panel1' + ShowCaption = False + TabOrder = 0 + object Button1: TButton + Left = 32 + Top = 62 + Width = 90 + Height = 30 + Caption = #29983#25104'100'#20010 + TabOrder = 1 + OnClick = Button1Click + end + object btnTimeStamp: TButton + Left = 568 + Top = 62 + Width = 90 + Height = 30 + Caption = #27979#35797#26102#38388#25139 + TabOrder = 3 + OnClick = btnTimeStampClick + end + object Button2: TButton + Left = 168 + Top = 62 + Width = 90 + Height = 30 + Caption = '500W'#26102#38388 + TabOrder = 2 + OnClick = Button2Click + end + object rgSelect: TRadioGroup + Left = 1 + Top = 1 + Width = 666 + Height = 55 + Align = alTop + Caption = #38634#33457#31639#27861 + Columns = 3 + ItemIndex = 0 + Items.Strings = ( + #28418#31227#31639#27861 + #20256#32479#31639#27861) + TabOrder = 0 + end + end +end diff --git a/Delphi/source/uTest.pas b/Delphi/source/uTest.pas new file mode 100644 index 0000000..2dd5282 --- /dev/null +++ b/Delphi/source/uTest.pas @@ -0,0 +1,197 @@ +unit uTest; + +interface + +uses + Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, System.DateUtils, + Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, Vcl.StdCtrls, Vcl.ExtCtrls, uIdGeneratorOptions, uIIdGenerator, + uDefaultIdGenerator, uYitIdHelper; + +type + TfTest = class(TForm) + mmo1: TMemo; + Panel1: TPanel; + Button1: TButton; + btnTimeStamp: TButton; + Button2: TButton; + rgSelect: TRadioGroup; + procedure Button1Click(Sender: TObject); + procedure btnTimeStampClick(Sender: TObject); + procedure Button2Click(Sender: TObject); + procedure FormCreate(Sender: TObject); + procedure FormClose(Sender: TObject; var Action: TCloseAction); + private + { Private declarations } + IdGeneratorOption: TIdGeneratorOptions; + YitIdHelper: TYitIdHelper; + public + { Public declarations } + end; + +var + fTest: TfTest; + +implementation + +{$R *.dfm} + +function GetMillisecondTimeStamp(): Int64; +var + ST: TDateTime; +begin + ST := EncodeDateTime(1970, 1, 1, 0, 0, 0, 0); + Result := MilliSecondsBetween(Now(), ST) - 28800000; // 8*60*60*1000; +end; + +procedure TfTest.btnTimeStampClick(Sender: TObject); +const + T1 = 28800000; +var + SysTime: TSystemTime; + Time1, Time2: TDateTime; + T2, T3: Int64; +begin + // 뼶 + // mmo1.Lines.Add(IntToStr(DateTimeToUnix(EncodeDateTime(2020, 2, 20, 2, 20, 2, 20), False))); + // mmo1.Lines.Add(IntToStr(DateTimeToUnix(EncodeDateTime(2020, 2, 20, 2, 20, 2, 20), True))); + // mmo1.Lines.Add(FormatDateTime('yyyy-MM-dd HH:mm:ss.zzz', + // UnixToDateTime(DateTimeToUnix(EncodeDateTime(2020, 2, 20, 2, 20, 2, 20), False), False))); + mmo1.Lines.Add('ʱתΪUTCʱ䣺' + IntToStr(DateTimeToUnix(Now(), False))); + mmo1.Lines.Add('ֱȡUTCʱ䣺' + IntToStr(DateTimeToUnix(Now(), True))); + // mmo1.Lines.Add(FormatDateTime('yyyy-MM-dd HH:mm:ss.zzz', UnixToDateTime(DateTimeToUnix(Now(), False), False))); + // mmo1.Lines.Add(FormatDateTime('yyyy-MM-dd HH:mm:ss.zzz', UnixToDateTime(DateTimeToUnix(Now(), True), True))); + + // 뼶 + Time2 := EncodeDateTime(1970, 1, 1, 0, 0, 0, 0); + + // δʱ + GetLocalTime(SysTime); + Time1 := SystemTimeToDateTime(SysTime); + T2 := MilliSecondsBetween(Time1, Time2); + T3 := T2 - T1; + mmo1.Lines.Add('LocalTime' + IntToStr(T3)); + + // ʱ + GetSystemTime(SysTime); + Time1 := SystemTimeToDateTime(SysTime); + T2 := MilliSecondsBetween(Time1, Time2); + T3 := T2 - T1; + mmo1.Lines.Add('SystemTime' + IntToStr(T3)); + + // δʱ + Time1 := Now(); + T2 := MilliSecondsBetween(Time1, Time2); + T3 := T2 - T1; + mmo1.Lines.Add('Now' + IntToStr(T3)); +end; + +procedure TfTest.Button1Click(Sender: TObject); +var + options: TIdGeneratorOptions; + IdGen: IIdGenerator; + i, j: Integer; +begin + // ʹ÷ʽһֱʹ + options := TIdGeneratorOptions.Create; + try + with options do + begin + Method := rgSelect.ItemIndex + 1; + // BaseTime := DateTime.Now.AddYears(-10); + WorkerId := 2; + + WorkerIdBitLength := 6; + SeqBitLength := 6; + + MaxSeqNumber := 0; + MinSeqNumber := 5; + + TopOverCostCount := 2000; + + DataCenterId := 0; + DataCenterIdBitLength := 0; + + TimestampType := 0; + end; + + IdGen := TDefaultIdGenerator.Create(options); + + j := 100; + Screen.Cursor := crHourGlass; + mmo1.Lines.BeginUpdate(); + try + for i := 1 to j do + begin + mmo1.Lines.Add(FormatFloat('000', i) + '' + IntToStr(IdGen.NewLong())); + end; + mmo1.Perform(WM_VSCROLL, SB_BOTTOM, 0) + finally + mmo1.Lines.EndUpdate(); + Screen.Cursor := crDefault; + end; + + finally + options.Free; + end; +end; + +procedure TfTest.Button2Click(Sender: TObject); +const + Total = 5000000; +var + i: Integer; + Id: Int64; + ST, ET, Elapse: Cardinal; +begin + Screen.Cursor := crHourGlass; + try + ST := GetTickCount(); + for i := 1 to Total do + begin + Id := YitIdHelper.NextId(); + end; + ET := GetTickCount(); + finally + Screen.Cursor := crDefault; + end; + Elapse := ET - ST; + mmo1.Lines.Add(Format('500ʱ䣺%d룬ƽÿ%d', [Elapse, Trunc((Total / Elapse) * 1000)])); + +end; + +procedure TfTest.FormClose(Sender: TObject; var Action: TCloseAction); +begin + YitIdHelper.Free; + IdGeneratorOption.Free; +end; + +procedure TfTest.FormCreate(Sender: TObject); +begin + IdGeneratorOption := TIdGeneratorOptions.Create; + // Ĭϲ + with IdGeneratorOption do + begin + Method := rgSelect.ItemIndex + 1; + // BaseTime := DateTime.Now.AddYears(-10); + WorkerId := 2; + + WorkerIdBitLength := 6; + SeqBitLength := 6; + + MaxSeqNumber := 0; + MinSeqNumber := 5; + + TopOverCostCount := 2000; + + DataCenterId := 0; + DataCenterIdBitLength := 0; + + TimestampType := 0; + end; + + // ʹ÷ʽװʹ + YitIdHelper := TYitIdHelper.Create; + YitIdHelper.SetIdGenerator(IdGeneratorOption); +end; + +end. diff --git a/Delphi/source/uYitIdHelper.pas b/Delphi/source/uYitIdHelper.pas new file mode 100644 index 0000000..fe2f185 --- /dev/null +++ b/Delphi/source/uYitIdHelper.pas @@ -0,0 +1,51 @@ +unit uYitIdHelper; + +interface + +uses System.SysUtils, uIIdGenerator, uIdGeneratorOptions; + +type + TYitIdHelper = class + private + IdGenInstance: IIdGenerator; + public + function GetIdGenInstance(): IIdGenerator; + /// + /// òʼʱִһ + /// + /// + procedure SetIdGenerator(options: TIdGeneratorOptions); + /// + /// µId + /// ñǰȷ SetIdGenerator ʼ + /// + /// + function NextId(): Int64; + end; + +implementation + +uses + uDefaultIdGenerator; + +{ TYitIdHelper } + +function TYitIdHelper.GetIdGenInstance: IIdGenerator; +begin + Result := IdGenInstance; +end; + +procedure TYitIdHelper.SetIdGenerator(options: TIdGeneratorOptions); +begin + IdGenInstance := TDefaultIdGenerator.Create(options); +end; + +function TYitIdHelper.NextId(): Int64; +begin + if (IdGenInstance = nil) then + raise Exception.Create('Please initialize Yitter.IdGeneratorOptions first.'); + + Result := IdGenInstance.NewLong(); +end; + +end. diff --git a/README.md b/README.md index ef342dd..6426dab 100644 --- a/README.md +++ b/README.md @@ -250,6 +250,7 @@ extern GoInt32 Validate(GoInt32 workerId); | 🌲 Python | [查看示例][10] | | 🌲 C | [查看示例][5] | | 🌲 C (PHP扩展) | [查看示例][7] | +| 🌲 Pascal | [查看示例][6] | | 🌲 JavaScript | [查看示例][8] | | 🌲 TypeScript | [查看示例][9] | | 🌲 V | [查看示例][6] | @@ -278,7 +279,7 @@ QQ群:646049993 [3]: https://github.com/yitter/idgenerator/tree/master/Go [4]: https://github.com/yitter/idgenerator/tree/master/Rust [5]: https://github.com/yitter/idgenerator/tree/master/C -[6]: https://github.com/yitter/idgenerator/tree/master/zzz-OtherLanguages/V +[6]: https://github.com/yitter/idgenerator/tree/master/Pascal [7]: https://github.com/yitter/idgenerator/tree/master/PHP [8]: https://github.com/yitter/IdGenerator/tree/master/JavaScript [9]: https://github.com/yitter/IdGenerator/tree/master/TypeScript @@ -289,7 +290,7 @@ QQ群:646049993 [31]: https://github.com/yitter/idgenerator/tree/master/Go [41]: https://github.com/yitter/idgenerator/tree/master/Rust [51]: https://github.com/yitter/idgenerator/tree/master/C -[61]: https://github.com/yitter/idgenerator/tree/master/zzz-OtherLanguages/V +[61]: https://github.com/yitter/idgenerator/tree/master/Pascal [71]: https://github.com/yitter/idgenerator/tree/master/PHP [72]: https://github.com/yitter/idgenerator/tree/master/zzz-OtherLanguages/D