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.
→ Standard Library · std.string · 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.
Decodeist derzeit nicht abgesichert — für Daten aus fremder Quelle ungeeignet. Zwei Befunde, beide nachgemessen:
*max_output_lenwird nicht ausgewertet. Der Dekoder schreibt so viele Bytes, wie die Eingabe hergibt; ein zu kleiner Zielpuffer führt zum Überlauf in benachbarten Speicher (#1406).
* Ungültige Zeichen werden nicht erkannt. Die Umkehrtabelle markiert sie mit-1, gelesen wird vorzeichenlos — die Prüfungv < 0greift nie, undDecodeliefert Datenmüll statt-1(#1407).
Bis das behoben ist: Eingaben mitIsValidvorprüfen, die Zielpuffergröße ausDecodedLenberechnen und großzügig bemessen.
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 |
EncodedLenundDecodedLenrechnen ohne Nullbyte.EncodedLen(6)ergibt 8 — der Puffer muss 9 Byte fassen.DecodedLenliefert 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 alsDecodeUrlSafe. 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('***='): 2
Decode('-_-_'): 3
DecodeUrlSafe('-_-_'): 3
IsValid('Zm9vYmFy'): true
IsValid('Zm9v Ymfy'): false
IsValid('abc'): false
IsValid('-_-_'): false
HasValidPadding('a=b='): true
HasValidPaddingprüft nichts. Alle Verzweigungen der Funktion enden mittrue, das abschließendereturn falseist unerreichbar."a=b="und"===="gelten damit als gültig, obwohl=nur am Ende stehen darf (#1409).IsValidendet mit dieser Prüfung und erbt den Fehler — es prüft faktisch nur Länge und Zeichenvorrat.
Für URL-sicheres Base64 gibt es keine Prüffunktion:IsValidmeldet dortfalse, weil-und_nicht im Standardalphabet stehen.
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.
DecodeBasicAuthprü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ürDecode.
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
Decodeselbst terminieren. Die Kodierfunktionen hängen ein Nullbyte an, der Dekoder nicht — er liefert Binärdaten. Für die Textausgabepoke8(puffer + n, 0). Encodehört beim ersten Nullbyte auf. Für Binärdaten immerEncodeBytesmit ausdrücklicher Länge nehmen; dasselbe gilt sinngemäß fürEncodeUrlSafeundEncodeDataUrl, die beide keine Längenvariante haben.DecodedLenist eine obere Schranke, kein exaktes Ergebnis — der Rückgabewert vonDecodeist maßgeblich.- Kein Zeilenumbruch. MIME (RFC 2045) verlangt Umbrüche alle 76 Zeichen;
std.base64erzeugt eine durchgehende Zeile und überliest beim Dekodieren keine Umbrüche — ein\nin der Eingabe ist ein ungültiges Zeichen und wird derzeit als Wert 255 verrechnet. - Ein Speicherleck je Dekodierung:
DecodeundDecodeUrlSafelegen ihre 128-Byte-Umkehrtabelle bei jedem Aufruf neu an und geben sie nie frei — 1000 Aufrufe kosten 128 000 Byte (#1408). - Interne Festgrößen bei
EncodeBasicAuth(512 / 1024 Byte) undEncodeDataUrl(4096 Byte) begrenzen die Eingabelänge, ohne das zu melden.
Weiterführend: std.string · std.crypto · std.net.http · 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.
