====== std.brotli — Brotli-Kompression und -Dekompression ======
''std.brotli'' implementiert Brotli nach RFC 7932 vollständig in Lyx — keine externen Abhängigkeiten. Der Dekompressionspfad ist vollständig; der Kompressor arbeitet im Store-Modus (gültige RFC-7932-Streams, keine LZ77-Reduktion).
→ [[lyx_-_programmiersprache:units|Standard Library]] · [[lyx_-_programmiersprache:units:zlib|std.zlib]] · [[lyx_-_programmiersprache:units:archiv|Archiv-Übersicht]]
**Wann Brotli?** Brotli ist der Standard-Content-Encoding für HTTP/2 und HTTP/3 — alle modernen Browser unterstützen ''Content-Encoding: br''. Für Text und Web-Assets erzielt Brotli typisch 15–25 % bessere Kompressionsraten als gzip/DEFLATE. ''std.brotli'' ist primär für das Dekomprimieren von Brotli-Streams sinnvoll (z.B. HTTP-Responses von CDNs oder APIs). Für das Erzeugen komprimierter Inhalte eignet sich der Store-Kompressor als Platzhalter; echte LZ77-Kompression folgt in einer späteren Version.
----
===== Konstanten =====
^ Konstante ^ Wert ^ Bedeutung ^
| ''BROTLI_OK'' | 0 | Kein Fehler |
| ''BROTLI_ERR'' | -1 | Allgemeiner Fehler (korrupter Stream, ungültige Parameter) |
| ''BROTLI_OVERFLOW'' | -2 | Ausgabepuffer zu klein |
----
===== Funktionen =====
^ Funktion ^ Rückgabe ^ Beschreibung ^
| ''BrotliDecompress(src, srcLen, dst, dstMax)'' | ''int64'' Bytes oder ''BROTLI_ERR'' | Dekomprimiert einen Brotli-Stream vollständig |
| ''BrotliCompress(src, srcLen, dst, dstMax)'' | ''int64'' Bytes oder ''BROTLI_ERR'' / ''BROTLI_OVERFLOW'' | Erzeugt einen gültigen Brotli-Stream (Store-Modus) |
Beide Funktionen arbeiten ohne interne Allokation des Ausgabepuffers — der Aufrufer stellt ''dst'' und ''dstMax'' bereit.
----
===== BrotliDecompress =====
Dekomprimiert einen vollständigen Brotli-Stream (RFC 7932). Unterstützt alle Kompressionsmodi (stored, LZ77, Huffman-Codes, Kontext-Maps, Block-Types). Schiebefenster bis 16 MB (WBITS 10–24).
import std.brotli;
import std.alloc;
import std.io;
fn BrotliBeispiel(komprimiert: int64, kompLen: int64): void {
// Ausgabepuffer: Brotli-Streams haben kein eingebettetes Größenfeld.
// Wenn die unkomprimierte Größe unbekannt ist, großzügig schätzen.
var dstMax: int64 := kompLen * 10;
var dst: int64 := alloc(dstMax + 1);
var n: int64 := BrotliDecompress(komprimiert, kompLen, dst, dstMax);
if n == BROTLI_ERR then {
PrintLn("Dekomprimierung fehlgeschlagen"c);
free(dst, dstMax + 1);
return;
}
poke8(dst + n, 0);
Print("Dekomprimiert: "c); PrintLn(IntToStr(n)c);
free(dst, dstMax + 1);
}
**Bekannte Einschränkung:** Static-Dictionary-Transforms (RFC 7932 §8) werden nicht unterstützt. Diese Transforms sind in der Praxis sehr selten — Standard-HTTP-Responses und übliche Brotli-Werkzeuge erzeugen sie nicht.
----
===== BrotliCompress =====
Erzeugt einen RFC-7932-konformen Brotli-Stream im Store-Modus: Alle Daten werden unkomprimiert eingebettet. Der Output ist ein gültiger Brotli-Stream und kann von jedem RFC-7932-Dekompressionsprogramm gelesen werden — er ist aber nicht kleiner als die Eingabe.
**Puffergröße:** Worst-Case-Overhead berechnet sich als:
benötigt = srcLen + ceil(srcLen / 16 MB) × 5 + 2
Für Eingaben ≤ 16 MB genügt ''dstMax = srcLen + 7''.
import std.brotli;
import std.alloc;
fn BrotliStoreRoundtrip(): void {
var original: pchar := "Hallo Brotli!"c;
var srcLen: int64 := 13;
// Komprimieren (Store-Modus)
var cBuf: int64 := alloc(srcLen + 7);
var cLen: int64 := BrotliCompress(original as int64, srcLen, cBuf, srcLen + 7);
if cLen == BROTLI_ERR then { PrintLn("Komprimierung fehlgeschlagen"c); return; }
if cLen == BROTLI_OVERFLOW then { PrintLn("Puffer zu klein"c); return; }
Print("Komprimiert: "c); PrintLn(IntToStr(cLen)c);
// Dekomprimieren
var dBuf: int64 := alloc(srcLen + 1);
var dLen: int64 := BrotliDecompress(cBuf, cLen, dBuf, srcLen + 1);
poke8(dBuf + dLen, 0);
PrintLn(dBuf as pchar); // → "Hallo Brotli!"
free(cBuf, srcLen + 7);
free(dBuf, srcLen + 1);
}
----
===== Ausgabepuffer-Größe schätzen =====
Brotli-Streams enthalten keine eingebettete unkomprimierte Größe. Für die Dekompression gibt es drei Strategien:
**1. Größe bekannt (z.B. aus HTTP ''Content-Length'' + separatem Header):**
var dst: int64 := alloc(bekannteGroesse + 1);
BrotliDecompress(src, srcLen, dst, bekannteGroesse);
**2. Konservativer Faktor (unbekannte Größe):**
// Brotli-Kompressionsraten selten > 10:1 für reale Daten
var dstMax: int64 := srcLen * 10;
if dstMax < 65536 then { dstMax := 65536; }
var dst: int64 := alloc(dstMax + 1);
var n: int64 := BrotliDecompress(src, srcLen, dst, dstMax);
if n == BROTLI_ERR then {
// Puffer war möglicherweise zu klein — größer versuchen
}
**3. HTTP-Response (empfohlen):** Den ''Content-Length''-Header des dekomprimierten Inhalts aus dem HTTP-Response lesen, falls der Server ihn mitsendet.
----
===== Vergleich mit std.zlib =====
^ Merkmal ^ ''std.brotli'' ^ ''std.zlib'' ^
| Standard | RFC 7932 | RFC 1950 / 1951 |
| HTTP-Encoding | ''br'' | ''gzip'' / ''deflate'' |
| Kompressionsrate (Text) | besser (15–25 % vs. gzip) | gut |
| Kompressor | Store-Modus (kein LZ77) | vollständig (LZ77 + Huffman) |
| Dekompressionstempo | vergleichbar | vergleichbar |
| Browser-Support | alle modernen Browser | universell |
| Einsatz in std.zip | nein | ja (Methode 8) |
Für serverseitig erzeugte komprimierte HTTP-Responses (z.B. in einem Lyx-HTTP-Server) ist ''std.zlib'' mit dem GZIP-Modus aktuell die bessere Wahl, solange der ''std.brotli''-Kompressor im Store-Modus bleibt.
----
===== Speicherverwaltung =====
Beide Funktionen allokieren keinen Ausgabepuffer — der Aufrufer ist vollständig verantwortlich:
^ Puffer ^ Wer allokiert ^ Wer gibt frei ^
| ''src'' (Eingabe) | Aufrufer | Aufrufer |
| ''dst'' (Ausgabe) | Aufrufer (mindestens ''dstMax'' Bytes) | Aufrufer |
| Interne Arbeitspuffer | ''BrotliDecompress'' / ''BrotliCompress'' | automatisch beim Return |
Letzte Aktualisierung: 2026-06-13