====== 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)