====== std.base64 ======
Base64 nach **RFC 4648**: Binärdaten als druckbare ASCII-Zeichen. Je drei Eingabebytes werden zu vier Zeichen, das Ergebnis ist rund ein Drittel größer als die Eingabe. Die Unit kann das Standardalphabet und die URL-sichere Variante, dazu Padding-Helfer, HTTP-Basic-Auth und Data-URLs.
→ [[lyx_-_programmiersprache:units|Standard Library]] · [[lyx_-_programmiersprache:units:string|std.string]] · [[lyx_-_programmiersprache:units:alloc|std.alloc]]
Alle Beispiele dieser Seite sind mit ''lyxc 1.0.21A'' übersetzt und ausgeführt; die gezeigten Ausgaben sind echte Programmausgaben und mit Pythons ''base64'' gegengeprüft.
> **''Decode'' ist abgesichert** — beide früheren Befunde sind weg, nachgemessen mit ''lyxc 1.1.11B'':
>
> * ''max_output_len'' wird ausgewertet: ''%%Decode("QUJDREVGRw==", puf, 3)%%'' (7 Byte Nutzlast, Puffer 3) liefert **''-2''** und schreibt nicht über den Puffer hinaus ([[https://github.com/SEOLizer/LyX-Compiler/issues/1406|#1406]]).
> * Ungültige Zeichen werden erkannt: ''%%Decode("QUJ!", …)%%'' liefert **''-1''** statt Datenmüll ([[https://github.com/SEOLizer/LyX-Compiler/issues/1407|#1407]]).
>
> Die Rückgabewerte sind damit aussagekräftig: ''-1'' = ungültige Eingabe, ''-2'' = Zielpuffer zu klein, sonst die Zahl geschriebener Bytes. Eingaben mit ''IsValid'' vorzuprüfen und die Größe aus ''DecodedLen'' zu rechnen bleibt trotzdem der saubere Weg.
----
===== Alle Puffer gehören dem Aufrufer =====
Keine Funktion dieser Unit legt einen Ausgabepuffer an — jede schreibt in einen Puffer, den der Aufrufer stellt, und hängt ein Nullbyte an. Die Rückgabe ist die Zahl geschriebener Zeichen **ohne** dieses Nullbyte.
^ Für ^ Größe ^
| ''Encode'' / ''EncodeBytes'' | ''%%EncodedLen(len) + 1%%'' |
| ''EncodeUrlSafe'' | ''%%EncodedLen(len) + 1%%'' (das Ergebnis ist kürzer, weil kein Padding) |
| ''Decode'' / ''DecodeUrlSafe'' | ''%%DecodedLen(len) + 1%%'' |
| ''EncodeBasicAuth'' | ''%%6 + EncodedLen(user + pass + 1) + 1%%'' |
| ''EncodeDataUrl'' | ''%%13 + mime + EncodedLen(len) + 1%%'' |
> **''EncodedLen'' und ''DecodedLen'' rechnen ohne Nullbyte.** ''%%EncodedLen(6)%%'' ergibt 8 — der Puffer muss 9 Byte fassen. ''DecodedLen'' liefert die **obere Schranke** (''len / 4 × 3''); mit Padding sind es ein oder zwei Byte weniger.
Zwei Funktionen legen intern Puffer fester Größe an: ''EncodeBasicAuth'' rechnet mit 512 Byte für ''benutzer:passwort'' und 1024 Byte für das Ergebnis, ''EncodeDataUrl'' mit 4096 Byte für die kodierten Daten. Längere Eingaben laufen über diese internen Grenzen hinaus.
----
===== Kodieren =====
^ Signatur ^ Beschreibung ^
| ''Encode(input: pchar, output: pchar): int64'' | Kodiert bis zum Nullbyte. **Für Binärdaten ungeeignet** — ein Nullbyte im Eingang beendet die Kodierung. |
| ''EncodeBytes(input: pchar, input_len: int64, output: pchar): int64'' | Kodiert genau ''input_len'' Bytes; der Weg für Binärdaten. |
| ''EncodeUrlSafe(input: pchar, output: pchar): int64'' | Alphabet mit ''-'' und ''_'' statt ''+'' und ''/'', **ohne Padding**. Kodiert ebenfalls bis zum Nullbyte. |
| ''EncodedLen(input_len: int64): int64'' | ''%%(len + 2) / 3 × 4%%'' — die Länge mit Padding. |
import std.base64;
import std.alloc;
import std.string;
fn main(): int64 {
var buf: int64 := alloc(256);
var out: pchar := buf as pchar;
// RFC-4648-Testvektoren
PrintLn(StrConcat("'' -> ", IntToStr(Encode(""c, out))));
Encode("f"c, out); PrintLn(StrConcat("f -> ", out));
Encode("fo"c, out); PrintLn(StrConcat("fo -> ", out));
Encode("foo"c, out); PrintLn(StrConcat("foo -> ", out));
Encode("foob"c, out); PrintLn(StrConcat("foob -> ", out));
Encode("fooba"c, out); PrintLn(StrConcat("fooba -> ", out));
Encode("foobar"c, out); PrintLn(StrConcat("foobar -> ", out));
// Rueckweg
var back: int64 := alloc(256);
var n: int64 := Decode("Zm9vYmFy"c, back as pchar, 256);
poke8(back + n, 0);
PrintLn(StrConcat(StrConcat("Zm9vYmFy -> ", back as pchar), StrConcat(" (", StrConcat(IntToStr(n), " Byte)"))));
// URL-sichere Variante
EncodeUrlSafe("\xfb\xff\xbf"c, out);
PrintLn(StrConcat("URL-safe (fb ff bf) -> ", out));
Encode("\xfb\xff\xbf"c, out);
PrintLn(StrConcat("Standard (fb ff bf) -> ", out));
return 0;
}
'' -> 0
f -> Zg==
fo -> Zm8=
foo -> Zm9v
foob -> Zm9vYg==
fooba -> Zm9vYmE=
foobar -> Zm9vYmFy
Zm9vYmFy -> foobar (6 Byte)
URL-safe (fb ff bf) -> -_-_
Standard (fb ff bf) -> +/+/
Alle sieben Testvektoren aus RFC 4648 §10 stimmen, ebenso die Alphabet-Umschaltung an den Bytes ''fb ff bf''.
----
===== Dekodieren =====
^ Signatur ^ Beschreibung ^
| ''Decode(input: pchar, output: pchar, max_output_len: int64): int64'' | Dekodiert Standard-Base64. Rückgabe: Zahl der Bytes. **Das Ergebnis wird nicht nullterminiert** und ''max_output_len'' nicht geprüft. |
| ''DecodeUrlSafe(input: pchar, output: pchar, max_output_len: int64): int64'' | Dasselbe mit dem URL-sicheren Alphabet. |
| ''DecodedLen(input_len: int64): int64'' | ''%%len / 4 × 3%%'' — obere Schranke der Bytezahl. |
Der Dekoder arbeitet in Vierergruppen und hält an, sobald weniger als vier Zeichen übrig sind. Eine Eingabe, deren Länge kein Vielfaches von 4 ist, liefert also die Bytes der vollständigen Gruppen und ignoriert den Rest — ohne Fehler. Padding (''='') beendet die Verarbeitung sauber.
> **Der Standard-Dekoder verschluckt URL-sichere Eingaben.** ''%%Decode("-_-_")%%'' liefert drei Bytes statt einer Fehlermeldung — andere Bytes als ''DecodeUrlSafe''. Wer nicht weiß, welche Variante ankommt, prüft das Alphabet selbst: enthält die Eingabe ''-'' oder ''_'', ist es die URL-sichere Form; ''+'' oder ''/'' die Standardform.
----
===== Prüfen =====
^ Signatur ^ Beschreibung ^
| ''IsValidChar(c: int64): bool'' | Ein Zeichen des Standardalphabets oder ''=''. Kennt ''-'' und ''_'' **nicht**. |
| ''IsValid(input: pchar): bool'' | Länge ist Vielfaches von 4, alle Zeichen zulässig. Leerer Text gilt als gültig. |
| ''HasValidPadding(input: pchar): bool'' | **Liefert immer ''true''** — siehe Kasten. |
import std.base64;
import std.alloc;
import std.string;
fn J(b: bool): pchar { if (b) { return "true"; } return "false"; }
fn main(): int64 {
var out: int64 := alloc(256);
PrintLn(StrConcat("Decode('***='): ", IntToStr(Decode("***="c, out as pchar, 256))));
PrintLn(StrConcat("Decode('-_-_'): ", IntToStr(Decode("-_-_"c, out as pchar, 256))));
PrintLn(StrConcat("DecodeUrlSafe('-_-_'): ", IntToStr(DecodeUrlSafe("-_-_"c, out as pchar, 256))));
PrintLn(StrConcat("IsValid('Zm9vYmFy'): ", J(IsValid("Zm9vYmFy"c))));
PrintLn(StrConcat("IsValid('Zm9v Ymfy'): ", J(IsValid("Zm9v Ymfy"c))));
PrintLn(StrConcat("IsValid('abc'): ", J(IsValid("abc"c))));
PrintLn(StrConcat("IsValid('-_-_'): ", J(IsValid("-_-_"c))));
PrintLn(StrConcat("HasValidPadding('a=b='): ", J(HasValidPadding("a=b="c))));
return 0;
}
Decode('***='): -1
Decode('-_-_'): -1
DecodeUrlSafe('-_-_'): 3
IsValid('Zm9vYmFy'): true
IsValid('Zm9v Ymfy'): false
IsValid('abc'): false
IsValid('-_-_'): false
HasValidPadding('a=b='): false
> **''HasValidPadding'' prüft** ([[https://github.com/SEOLizer/LyX-Compiler/issues/1409|#1409]], nachgemessen mit ''lyxc 1.1.11B''): ''%%"a=b="%%'' und ''%%"===="%%'' liefern beide ''false'', weil ''='' nur am Ende stehen darf; ''%%"QUJD"%%'' liefert ''true''. ''IsValid'' endet mit dieser Prüfung und ist damit ebenfalls belastbar.
>
> Für URL-sicheres Base64 gibt es weiterhin **keine** Prüffunktion: ''IsValid'' meldet dort ''false'', weil ''-'' und ''_'' nicht im Standardalphabet stehen. Zum Dekodieren ist ''DecodeUrlSafe'' der richtige Weg — es nimmt ''-_-_'' an (Rückgabe 3), während ''Decode'' dieselbe Eingabe mit ''-1'' abweist.
----
===== Padding-Helfer =====
^ Signatur ^ Beschreibung ^
| ''StripPadding(input: pchar, output: pchar): int64'' | Entfernt **alle** ''=''-Zeichen, auch mitten im Text. |
| ''AddPadding(input: pchar, output: pchar): int64'' | Füllt auf ein Vielfaches von 4 auf. ''-1'', wenn die Länge modulo 4 gleich 1 ist — dieser Fall ist in Base64 nicht darstellbar. |
Typischer Einsatz: JSON Web Tokens und OAuth-Parameter tragen URL-sicheres Base64 ohne Padding. Vor dem Dekodieren mit einem Standard-Dekoder ergänzt ''AddPadding'' die fehlenden Zeichen.
StripPadding('Zm9vYg=='): 6 -> Zm9vYg
AddPadding('Zm9vYg'): 8 -> Zm9vYg==
AddPadding('Zm9vYgX'): 8
----
===== HTTP Basic Auth und Data-URLs =====
^ Signatur ^ Beschreibung ^
| ''EncodeBasicAuth(username: pchar, password: pchar, output: pchar): int64'' | Erzeugt den vollständigen Header-Wert einschließlich ''%%Basic %%''-Präfix. |
| ''DecodeBasicAuth(auth_header: pchar, username: pchar, password: pchar): int64'' | Zerlegt ihn wieder; ''1'' bei Erfolg, ''0'' bei fehlendem Präfix, ungültiger Kodierung oder fehlendem Doppelpunkt. |
| ''EncodeDataUrl(mime_type: pchar, data: pchar, output: pchar): int64'' | Baut ''data:<mime>;base64,<daten>''. Kodiert bis zum Nullbyte — für Binärdaten ungeeignet. |
import std.base64;
import std.alloc;
import std.string;
fn main(): int64 {
// HTTP Basic Auth
var hdr: int64 := alloc(512);
var n: int64 := EncodeBasicAuth("aladdin"c, "opensesame"c, hdr as pchar);
PrintLn(StrConcat(StrConcat("Header (", IntToStr(n)), StrConcat(" Zeichen): ", hdr as pchar)));
var user: int64 := alloc(128);
var pass: int64 := alloc(128);
var ok: int64 := DecodeBasicAuth(hdr as pchar, user as pchar, pass as pchar);
PrintLn(StrConcat(StrConcat("zurueck: ok=", IntToStr(ok)),
StrConcat(" Benutzer=", StrConcat(user as pchar, StrConcat(" Passwort=", pass as pchar)))));
// Ein Header ohne "Basic "-Praefix wird abgewiesen
PrintLn(StrConcat("DecodeBasicAuth('Bearer xyz'): ", IntToStr(DecodeBasicAuth("Bearer xyz"c, user as pchar, pass as pchar))));
// Data-URL
var url: int64 := alloc(512);
var m: int64 := EncodeDataUrl("text/plain"c, "Hallo Lyx"c, url as pchar);
PrintLn(StrConcat("Data-URL: ", url as pchar));
return 0;
}
Header (30 Zeichen): Basic YWxhZGRpbjpvcGVuc2VzYW1l
zurueck: ok=1 Benutzer=aladdin Passwort=opensesame
DecodeBasicAuth('Bearer xyz'): 0
Data-URL: data:text/plain;base64,SGFsbG8gTHl4
Beide Werte stimmen mit dem Beispiel aus RFC 7617 überein.
> **''DecodeBasicAuth'' prüft nur das Präfix, nicht die Länge der Zielpuffer.** Benutzername und Passwort werden ungeprüft in die übergebenen Puffer geschrieben; der interne Dekodierpuffer fasst 512 Byte. Für Header aus dem Netz gilt derselbe Vorbehalt wie für ''Decode''.
----
===== Konstanten =====
^ Konstante ^ Wert ^ Bedeutung ^
| ''ENCODE_STD'' | 0 | Standardalphabet (''+'', ''/'', mit Padding) |
| ''ENCODE_URL_SAFE'' | 1 | URL-sicher (''-'', ''_'', ohne Padding) |
Beide sind Kennzahlen für eigene Fallunterscheidungen — **keine** Funktion dieser Unit nimmt sie als Parameter entgegen. Die Variante wählt man über den Funktionsnamen (''Encode'' gegen ''EncodeUrlSafe'').
----
===== Fallstricke =====
* **Ergebnis von ''Decode'' selbst terminieren.** Die Kodierfunktionen hängen ein Nullbyte an, der Dekoder nicht — er liefert Binärdaten. Für die Textausgabe ''%%poke8(puffer + n, 0)%%''.
* **''Encode'' hört beim ersten Nullbyte auf.** Für Binärdaten immer ''EncodeBytes'' mit ausdrücklicher Länge nehmen; dasselbe gilt sinngemäß für ''EncodeUrlSafe'' und ''EncodeDataUrl'', die beide keine Längenvariante haben.
* **''DecodedLen'' ist eine obere Schranke**, kein exaktes Ergebnis — der Rückgabewert von ''Decode'' ist maßgeblich.
* **Kein Zeilenumbruch.** MIME (RFC 2045) verlangt Umbrüche alle 76 Zeichen; ''std.base64'' erzeugt eine durchgehende Zeile und überliest beim Dekodieren **keine** Umbrüche — ein ''\n'' in der Eingabe ist ein ungültiges Zeichen und wird derzeit als Wert 255 verrechnet.
* **Ein Speicherleck je Dekodierung:** ''Decode'' und ''DecodeUrlSafe'' legen ihre 128-Byte-Umkehrtabelle bei jedem Aufruf neu an und geben sie nie frei — 1000 Aufrufe kosten 128 000 Byte ([[https://github.com/SEOLizer/LyX-Compiler/issues/1408|#1408]]).
* **Interne Festgrößen** bei ''EncodeBasicAuth'' (512 / 1024 Byte) und ''EncodeDataUrl'' (4096 Byte) begrenzen die Eingabelänge, ohne das zu melden.
----
**Weiterführend:** [[lyx_-_programmiersprache:units:string|std.string]] · [[lyx_-_programmiersprache:units:crypto|std.crypto]] · [[lyx_-_programmiersprache:units:net:http|std.net.http]] · [[lyx_-_programmiersprache:units|Standard Library]]
**Quelle:** ''std/base64.lyx'' (498 Zeilen) · **Autor:** Andreas Röne · **Copyright:** 2024–2025 Andreas Röne
Letzte Aktualisierung: 2026-08-13 — Seite gegen ''std/base64.lyx'' neu geschrieben. Der Abschnitt „Externe Funktionen" mit ''libc_malloc'' ist entfallen: Die Bindung wurde mit #1310 entfernt, die Unit nutzt ''std.alloc''. Neu: drei lauffähige Beispiele mit echter Ausgabe (RFC-4648-Testvektoren gegen Python geprüft), Puffergrößen-Tabelle, Fallstricke — und die vier belegten Defekte #1406 (''max_output_len'' ignoriert), #1407 (ungültige Zeichen unerkannt), #1408 (Speicherleck je Aufruf), #1409 (''HasValidPadding'' immer ''true''). Geprüft mit ''lyxc 1.0.21A''.
Letzte Aktualisierung: 2026-08-27 — #1406, #1407 und #1409 gegen ''lyxc 1.1.11B'' nachgemessen und als behoben ausgewiesen; die Beispielausgabe im Prüf-Abschnitt entsprechend neu erhoben (''Decode'' liefert jetzt ''-1''/''-2'' statt Müllwerten).
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).