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