Einzelnen Beitrag anzeigen

Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.343 Beiträge
 
Delphi 11 Alexandria
 
#18

AW: TGUID - einzigartige ID auf andere Computer Systeme ?

  Alt 27. Okt 2023, 13:17
Ich habe mir für den Zweck eine eigene Guid aufgebaut.
Wichtig ist mir, dass diese global persistent eindeutig sind und dass die Reihenfolge (jedenfalls auf einem Rechner) aufsteigend nachvollziehbar ist.

Sie besteht aus zwei Zeitstempeln und einem fortlaufenden Zähler.
Der erste Zeitstempel enthält den Moment des Projektstarts (ist also so eine Art Session-ID), der zweite den Moment der ID-Erzeugung.
Der Zähler läuft bei jeder ID-Erzeugung weiter zwischen 0 bis 65535. So ist die Reihenfolge gut nachvollziehbar und es gibt noch eine Differenzierung falls doch mal beide Zeitstempel auf zwei Geräten gleich sein sollten.

Ich bin nicht auf die Standard-Guid angewiesen und so ist das für mich sinnvoller und nachvollziehbarer.
Sie braucht mit 18 Byte zwei Byte mehr als die Standard-Guid.

Delphi-Quellcode:
unit myGuid;

interface

  uses

    System.Classes;

  const

    MaxWord = 65535;

  type

    PmyGuid = ^TmyGuid;

    TmyGuid = record //: 18 Byte / 144 Bit
      C : Word; //: 2 Byte / 16 Bit
      TS1: TDateTime; //: 8 Byte / 64 Bit
      Ts2: TDateTime; //: 8 Byte / 64 Bit
      class function Create(const aGuid: PmyGuid): TmyGuid; overload; static;
      class function Create(const aGuid: String): TmyGuid; overload; static;
      class function Create(const aGuid: TmyGuid): TmyGuid; overload; static;
      class function CreateEmpty: TmyGuid; static;
      class function CreateNew: TmyGuid; static;
      class function StringIsValidGuid(const aGuid: String): Boolean; static;
      class operator Equal(const Left, Right: TmyGuid): Boolean;
      class operator NotEqual(const Left, Right: TmyGuid): Boolean; inline;
      function GetHashCode: Integer;
      function IsEmpty: Boolean;
      function ToString: String;
      procedure DoEmpty;
      procedure DoNew;
      procedure FromString(const aGuid: String);
      procedure ReadFromStream(const aStream: TMemoryStream);
      procedure WriteToStream(const aStream: TMemoryStream);
    end;

implementation

  uses

    System.SysUtils, System.DateUtils, System.Hash;

  var

    customTS1: TDateTime = 0;
    customTS2: TDateTime = 0;
    customC : Word = 0;

  {: *********************************** TmyGuid ********************************** :}

  {: ----------------------------------- default ---------------------------------- :}

  //: 2023-10-27
  class function TmyGuid.Create(const aGuid: String): TmyGuid;
  begin
    Result := TmyGuid.CreateEmpty;
    Result.FromString(aGuid);
  end;

  //: 2023-10-27
  class function TmyGuid.Create(const aGuid: TmyGuid): TmyGuid;
  begin
    Result := aGuid;
  end;

  //: 2023-10-27
  class function TmyGuid.Create(const aGuid: PmyGuid): TmyGuid;
  begin
    Result := aGuid^;
  end;

  //: 2023-10-27
  class function TmyGuid.CreateEmpty: TmyGuid;
  begin
    Result.TS1 := 0;
    Result.TS2 := 0;
    Result.C := 0;
  end;

  //: 2023-10-27
  class function TmyGuid.CreateNew: TmyGuid;
  begin
    customTS2 := Now;
    if (customC = MaxWord) then
      customC := 0
    else
      Inc(customC);
    Result.TS1 := customTS1;
    Result.TS2 := customTS2;
    Result.C := customC;
  end;

  //: 2023-10-27
  class function TmyGuid.StringIsValidGuid(const aGuid: String): Boolean;
  var
    I: Integer;
    C: Char;
  begin
    if (Length(aGuid) = 42) then
      begin
        Result := True;
        for I := 1 to Length(aGuid) do
          begin
            C := aGuid[I];
            case I of
              1:
                if C <> '#then
                  begin
                    Result := False;
                    Break;
                  end;
              19, 37:
                if C <> '-then
                  begin
                    Result := False;
                    Break;
                  end;
            else
              if not CharInSet(C, ['0'..'9']) then
                begin
                  Result := False;
                  Break;
                end;
            end;
          end;
      end
    else
      Result := False;
  end;

  //: 2023-10-27
  class operator TmyGuid.Equal(const Left, Right: TmyGuid): Boolean;
  begin
    Result := ((Left.TS1 = Right.TS1) and (Left.TS2 = Right.TS2) and (Left.C = Right.C));
  end;

  //: 2023-10-27
  class operator TmyGuid.NotEqual(const Left, Right: TmyGuid): Boolean;
  begin
    Result := ((Left.TS1 <> Right.TS1) or (Left.TS2 <> Right.TS2) or (Left.C <> Right.C));
  end;

  //: 2023-10-27
  function TmyGuid.GetHashCode: Integer;
  begin
    Result := 17;
    Result := Result * 397 + Integer(C);
    Result := Result * 397 + THashBobJenkins.GetHashValue(TS1, sizeOf(TDateTime), 5);
    Result := Result * 397 + THashBobJenkins.GetHashValue(TS2, sizeOf(TDateTime), 7);
  end;

  //: 2023-10-27
  function TmyGuid.IsEmpty: Boolean;
  begin
    Result := ((TS1 = 0) and (Ts2 = 0) and (C = 0));
  end;

  function TmyGuid.ToString: String;
  var
    S1, S2, S3: String;
  begin
    //: #yyyymmddhhnnsszzz-yyyymmddhhnnsszzz-xxxxx
    //: #12345678901234567890123456789012345678901 - > Length = 42
    if (TS1 = 0) then
      S1 := '00000000000000000'
    else
      S1 := FormatDateTime('yyyymmddhhnnsszzz', TS1);
    if (Ts2 = 0) then
      S2 := '00000000000000000'
    else
      S2 := FormatDateTime('yyyymmddhhnnsszzz', Ts2);
    if (C = 0) then
      S3 := '00000'
    else
      S3 := Format('%0.5d', [C]);
    Result := '#' + S1 + '-' + S2 + '-' + S3;
  end;

  //: 2023-10-27
  procedure TmyGuid.DoEmpty;
  begin
    TS1 := 0;
    TS2 := 0;
    C := 0;
  end;

  //: 2023-10-27
  procedure TmyGuid.DoNew;
  var
    lGuid: TmyGuid;
  begin
    lGuid := CreateNew;
    TS1 := lGuid.TS1;
    TS2 := lGuid.TS2;
    C := lGuid.C;
  end;

  //: 2023-10-27
  procedure TmyGuid.FromString(const aGuid: String);

    function EncodeTS(aGuid: String): TDateTime;
    var
      AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond: Word;
    begin
      AYear := StrToInt(Copy(aGuid, 1, 4));
      AMonth := StrToInt(Copy(aGuid, 5, 2));
      ADay := StrToInt(Copy(aGuid, 7, 2));
      AHour := StrToInt(Copy(aGuid, 9, 2));
      AMinute := StrToInt(Copy(aGuid, 11, 2));
      ASecond := StrToInt(Copy(aGuid, 13, 2));
      AMilliSecond := StrToInt(Copy(aGuid, 15, 3));
      Result := EncodeDateTime(AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond);
    end;

  begin
    if (StringIsValidGuid(aGuid)) then
      begin
        TS1 := EncodeTS(Copy(aGuid, 2, 17));
        Ts2 := EncodeTS(Copy(aGuid, 20, 17));
        C := StrToInt(Copy(aGuid, 38, 5));
      end
    else
      begin
        TS1 := 0;
        Ts2 := 0;
        C := 0;
      end;
  end;

  //: 2023-10-27
  procedure TmyGuid.ReadFromStream(const aStream: TMemoryStream);
  begin
    aStream.ReadData(C);
    aStream.ReadData(TS1);
    aStream.ReadData(Ts2);
  end;

  //: 2023-10-27
  procedure TmyGuid.WriteToStream(const aStream: TMemoryStream);
  begin
    aStream.WriteData(C);
    aStream.WriteData(TS1);
    aStream.WriteData(Ts2);
  end;

initialization

  customTS1 := Now;
  customTS2 := Now;
  customC := MaxWord;

end.
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)
  Mit Zitat antworten Zitat