====== std.uuid — UUIDs ====== → [[lyx_-_programmiersprache:units|Zurück zur Unit-Übersicht]] Erzeugen, Einlesen und Vergleichen von UUIDs. Eine UUID besteht aus **16 Bytes**; die Unit arbeitet durchgängig auf rohen Puffern, die der Aufrufer bereitstellt. Zusätzlich Umwandlung in die übliche Textform ''xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'' und zurück. **Autor:** Andreas Röne\\ **Copyright:** 2024–2025 Andreas Röne\\ **Quelle:** ''std/uuid.lyx'' **Seit lyxc 1.0.20F arbeitet die Unit korrekt** (#1473, #1474, #1475). Nachgemessen über zwei Programmläufe: UUID 1 = A80673CB-6110-4E4C-97C3-53D3381EC76E Laenge 36 UUID 2 = 1B6600FA-037C-42C4-9DD3-E7FB475A1756 verschieden? 1 Variant = 1 Version = 4 zweiter Lauf: UUID 1 = 96A9C854-4CF1-4266-8B30-1E4282917663 Behoben sind damit alle drei früheren Mängel: die Folge ist je Programmlauf verschieden (vorher fest einkompilierte Startsaat), ''ToString'' liefert **36** Zeichen statt 35, und ''GetVariant'' meldet 1 für RFC 4122. ===== Import ===== import std.uuid; ---- ===== Aufbau einer UUID ===== 16 Bytes, in der Textform zu fünf Gruppen zusammengefasst: 550e8400-e29b-41d4-a716-446655440000 └──────┘ └──┘ └──┘ └──┘ └──────────┘ Byte 4-5 6-7 8-9 Byte 10-15 0-3 │ │ │ └── obere Bits: Variante (10 = RFC 4122) └── obere 4 Bits: Version (4 = zufällig, 7 = zeitbasiert) ^ Version ^ Herkunft der Bits ^ Eigenschaft ^ | 4 | 122 Zufallsbits | keine Ordnung, keine Rückschlüsse auf Zeit oder Rechner | | 7 | 48 Bit Unix-Zeit in Millisekunden, Rest zufällig | zeitlich sortierbar — günstig als Datenbankschlüssel | Die restlichen 6 Bits sind in beiden Fällen durch Version und Variante belegt. ---- ===== Funktionen ===== ==== Einlesen und Prüfen ==== ^ Signatur ^ Beschreibung ^ Status ^ | ''FromString(str: pchar, output: pchar): bool'' | Liest die Textform in 16 Bytes ein; Bindestriche werden übersprungen | ok | | ''IsValid(str: pchar): bool'' | Prüft Länge (36 oder 32) und Hex-Zeichen | ok | | ''GetVersion(uuid: pchar): int64'' | Version aus den oberen 4 Bit von Byte 6 | ok | | ''GetVariant(uuid: pchar): int64'' | Variante aus Byte 8 | **meldet nie RFC** (#1475) | | ''IsNil(uuid: pchar): bool'' | Ob alle 16 Bytes 0 sind | ok | | ''Compare(a: pchar, b: pchar): int64'' | Byteweiser Vergleich: ''-1'', ''0'', ''1'' | ok | | ''SetNil(uuid: pchar): bool'' | Setzt alle 16 Bytes auf 0 | ok | ''FromString'' nimmt die Form mit und ohne Bindestriche und akzeptiert Groß- wie Kleinschreibung. Beide Funktionen prüfen nur Form und Zeichenvorrat, nicht ob Version und Variante sinnvoll gesetzt sind. ==== Erzeugen und Ausgeben ==== ^ Signatur ^ Beschreibung ^ Status ^ | ''GenerateV4(output: pchar): bool'' | 16 Byte UUID v4 | **wiederholbar** (#1473) | | ''GenerateV7(output: pchar): bool'' | 16 Byte UUID v7 | **ohne Zeitstempel** (#1473, #1475) | | ''ToString(uuid: pchar, output: pchar): bool'' | 16 Bytes in die Textform | **Format fehlerhaft** (#1474) | | ''GenerateV4String(output: pchar): bool'' | v4 direkt als Text | erbt beide Fehler | | ''GenerateV7String(output: pchar): bool'' | v7 direkt als Text | erbt beide Fehler | ==== Konstante ==== ^ Name ^ Wert ^ Bedeutung ^ | ''UUID_SIZE'' | 16 | Bytes einer UUID | Für die Textform sind **37 Bytes** vorzusehen: 36 Zeichen plus abschließendes Nullbyte. Die vier ''UUID_VARIANT_*''-Konstanten sind im Quelltext ohne ''pub'' deklariert und damit von außen nicht ansprechbar (#1475). ---- ===== Beispiel: UUID einlesen und prüfen ===== import std.uuid; import std.alloc; fn main(): int64 { // UUID aus dem Standardformat einlesen var bytes: int64 := alloc(UUID_SIZE); var text: pchar := "550e8400-e29b-41d4-a716-446655440000"; PrintLn("IsValid = ", IntToStr(IsValid(text) as int64)); PrintLn("FromString = ", IntToStr(FromString(text, bytes as pchar) as int64)); PrintLn("Version = ", IntToStr(GetVersion(bytes as pchar))); PrintLn("IsNil = ", IntToStr(IsNil(bytes as pchar) as int64)); // Die 16 Bytes einzeln Print("Bytes = "); var i: int64 := 0; while (i < UUID_SIZE) { Print(IntToStr(StrCharAt(bytes as pchar, i)), " "); i := i + 1; } PrintLn(""); // Nil-UUID und Vergleich var nil: int64 := alloc(UUID_SIZE); SetNil(nil as pchar); PrintLn("IsNil(nil) = ", IntToStr(IsNil(nil as pchar) as int64)); PrintLn("Compare(nil, uuid) = ", IntToStr(Compare(nil as pchar, bytes as pchar))); PrintLn("Compare(uuid, uuid) = ", IntToStr(Compare(bytes as pchar, bytes as pchar))); free(bytes, UUID_SIZE); free(nil, UUID_SIZE); return 0; } Ausgabe: IsValid = 1 FromString = 1 Version = 4 IsNil = 0 Bytes = 85 14 132 0 226 155 65 212 167 22 68 102 85 68 0 0 IsNil(nil) = 1 Compare(nil, uuid) = -1 Compare(uuid, uuid) = 0 ''Compare'' vergleicht die Bytes in ihrer Reihenfolge. Bei UUID v7 entspricht das der zeitlichen Ordnung — sobald der Zeitstempel tatsächlich gesetzt wird (#1475). ---- ===== Was nicht funktioniert ===== Geprüft mit ''lyxc 1.0.21A''. ==== Jeder Programmlauf erzeugt dieselben UUIDs (#1473) ==== var s: int64 := alloc(40); var i: int64 := 0; while (i < 3) { GenerateV4String(s as pchar); PrintLn(s as pchar); i := i + 1; } Zwei aufeinanderfolgende Aufrufe desselben Programms: --- Lauf 1: --- Lauf 2: DC0465AA-1FAD-4D5A-9AE5AC1B1E5F1370 DC0465AA-1FAD-4D5A-9AE5AC1B1E5F1370 796CFD10-FF19-4F60-9D04ACB41D022B46 796CFD10-FF19-4F60-9D04ACB41D022B46 78733AF2-DF5F-4EB7-8859D1EE3910CB48 78733AF2-DF5F-4EB7-8859D1EE3910CB48 Ursache ist ein eigener Zufallsgenerator mit fest einkompilierter Startsaat (''SEED_INIT := 12345''), der keine Systemquelle liest. Innerhalb eines Laufs sind die Werte verschieden — deshalb fällt es im Test mit einem einzelnen Prozess nicht auf. Zwei gleichzeitig laufende Dienste vergeben jedoch **dieselben** IDs. Bis zum Fix die Zufallsbytes selbst besorgen. ''std.crypto.rand'' liest ''getrandom(2)'': import std.crypto.rand; import std.uuid; fn EigeneV4(out: pchar): bool { if (RandBytesExact(out as int64, 16) != 16) { return false; } StrSetChar(out, 6, (StrCharAt(out, 6) & 15) | 64); // Version 4 StrSetChar(out, 8, (StrCharAt(out, 8) & 63) | 128); // Variante RFC 4122 return true; } ==== Die Textform (behoben, #1474) ==== Eingabe : 550e8400-e29b-41d4-a716-446655440000 Ausgabe : 550E8400-E29B-41D4-A716446655440000 Laenge : 35 FromString(Ausgabe) = 0 Der vierte Bindestrich fehlt — ''ToString'' schreibt 8-4-4-**16** statt 8-4-4-**4-12**. Zwei Folgen: der Rundlauf ist gebrochen (''FromString'' verlangt mindestens 36 Zeichen und lehnt die eigene Ausgabe ab), und fremde Systeme weisen den Wert als ungültige UUID zurück. Nebenbei gibt ''ToString'' **Großbuchstaben** aus; RFC 4122 verlangt für die Ausgabe Kleinschreibung. Beim Einlesen sind beide Formen erlaubt, das machen ''FromString'' und ''IsValid'' korrekt. ==== GetVariant meldet nie RFC 4122 (#1475) ==== Byte 8 = 167 (0xA7, obere Bits 101) GetVariant = 0 (NCS — erwartet: RFC 4122) Die Prüfreihenfolge fängt die RFC-Werte im ersten Zweig ab; der ''UUID_VARIANT_RFC''-Zweig ist unerreichbarer Code. Auch selbst erzeugte UUIDs melden NCS, obwohl ''GenerateV4'' die Variantenbits korrekt setzt. ''GetVersion'' arbeitet dagegen richtig. ==== UUID v7 ist nicht sortierbar (#1475) ==== Statt der Unix-Zeit stehen in den oberen 48 Bit die Werte des Pseudozufallsgenerators: v7[0] = 28722F82-AE00-742D-... v7[1] = 2017EF53-5201-74E7-... <- kleiner als der vorherige v7[2] = 78ED86DA-0100-7686-... Wer v7 wegen der Index-Lokalität in Datenbanken wählt, bekommt hier das Gegenteil. Der Zeitanteil ließe sich aus ''NowMs()'' in [[lyx_-_programmiersprache:units:time|std.time]] bilden. ==== Speicherleck in den String-Varianten (#1475) ==== ''GenerateV4String'' und ''GenerateV7String'' legen intern 16 Byte an und geben sie nicht frei — pro Aufruf. In einem Dienst, der je Anfrage eine ID erzeugt, summiert sich das. ---- ===== Verwandte Units ===== * [[lyx_-_programmiersprache:units:crypto:rand|std.crypto.rand]] — Zufallsbytes aus ''getrandom(2)'' * [[lyx_-_programmiersprache:units:time|std.time]] — ''NowMs()'' für zeitbasierte Kennungen * [[lyx_-_programmiersprache:units:base64|std.base64]] — kompaktere Textform für Kennungen Codebeispiele geprüft: gegen **lyxc 1.2.5C** übersetzt (Prüflauf 2026-09-08 über die gesamte Doku: 574 Vollprogramme, 0 echte Fehler; zusätzlich 5159 Aufrufe gegen die ''pub fn''-Signaturen in ''aurum/std'' gehalten, 0 Abweichungen). ---- Letzte Aktualisierung: 2026-08-30 (Dateistand)