Inhaltsverzeichnis

std.zstd — Zstandard-Kompression und -Dekompression

std.zstd implementiert Zstandard (RFC 8878) vollständig in Lyx — keine externen Abhängigkeiten. Der Dekompressionspfad unterstützt den vollständigen Zstd-Frame-Aufbau mit FSE-codierten Sequenzen und Huffman-codierten Literalen; der Kompressor arbeitet im Store-Modus (gültige RFC-8878-Frames, keine LZ77-Reduktion).

Standard Library · std.zlib · std.brotli · Archiv-Übersicht

Wann Zstd? Zstd ist das Kompressionsformat der modernen Server-Infrastruktur: Linux-Kernel-Module, Docker-Image-Layers, Kafka-Nachrichten, npm-Pakete und .tar.zst-Archive nutzen Zstd. std.zstd ist der richtige Einstiegspunkt wenn Zstd-komprimierte Daten dekomprimiert werden müssen — z.B. Streams aus Kafka, Backup-Dateien oder Systemarchive. Für das Erzeugen komprimierter Inhalte ist der Store-Kompressor ein gültiger Platzhalter; echte Kompression folgt in einer späteren Version.


Konstanten

Konstante Wert Bedeutung
ZSTD_OK 0 Kein Fehler
ZSTD_ERR -1 Allgemeiner Fehler (kein Zstd-Frame, korrupter Stream, nicht unterstütztes Feature)
ZSTD_OVERFLOW -2 Ausgabepuffer zu klein

Funktionen

Funktion Rückgabe Beschreibung
ZstdDecompress(src, srcLen, dst, dstMax) int64 Bytes oder ZSTD_ERR / ZSTD_OVERFLOW Dekomprimiert einen Zstd-Frame vollständig
ZstdCompress(src, srcLen, dst, dstMax) int64 Bytes oder ZSTD_ERR / ZSTD_OVERFLOW Erzeugt einen gültigen Zstd-Frame (Store-Modus)

Beide Funktionen arbeiten ohne interne Allokation des Ausgabepuffers — der Aufrufer stellt dst und dstMax bereit.


ZstdDecompress

Dekomprimiert einen vollständigen Zstd-Frame (RFC 8878). Unterstützt:

Wenn der Frame-Header eine bekannte unkomprimierte Größe enthält und diese dstMax übersteigt, gibt die Funktion sofort ZSTD_OVERFLOW zurück — ohne den gesamten Stream zu durchlaufen.

import std.zstd;
import std.alloc;
import std.io;

fn ZstdBeispiel(komprimiert: int64, kompLen: int64): void {
    // Zstd-Frame enthält die unkomprimierte Größe im Header (wenn gesetzt).
    // Ohne Vorabwissen großzügig schätzen.
    var dstMax: int64 := kompLen * 20;
    if dstMax < 65536 then { dstMax := 65536; }
    var dst: int64 := alloc(dstMax + 1);

    var n: int64 := ZstdDecompress(komprimiert, kompLen, dst, dstMax);
    if n == ZSTD_ERR then {
        PrintLn("Kein gültiger Zstd-Frame oder nicht unterstütztes Feature"c);
        free(dst, dstMax + 1);
        return;
    }
    if n == ZSTD_OVERFLOW then {
        PrintLn("Ausgabepuffer zu klein — größeren Puffer verwenden"c);
        free(dst, dstMax + 1);
        return;
    }

    poke8(dst + n, 0);
    Print("Dekomprimiert: "c); PrintLn(IntToStr(n)c);
    free(dst, dstMax + 1);
}

Bekannte Einschränkungen

Einschränkung Details
Treeless-Literalblöcke Huffman-Typ 3 (Wiederverwendung des Baums aus dem Vorblock) wird nicht unterstützt — gibt ZSTD_ERR zurück
Wörterbücher (Dictionary) Dictionary-ID wird gelesen und übersprungen; der Inhalt wird nicht angewandt
Skippable Frames Magic 0x184D2A5x wird nicht erkannt — gibt ZSTD_ERR zurück
Multi-Frame-Streams Nur ein Frame pro Aufruf; verkettete Frames werden nicht durchlaufen

In der Praxis erzeugen Standard-Werkzeuge (zstd, tar –zstd, Kafka-Codec) selten Treeless-Blöcke für einzelne unkomprimierte Streams. Multi-Frame-Ausgabe tritt bei sehr großen Dateien auf, die mit –long komprimiert wurden.


ZstdCompress

Erzeugt einen RFC-8878-konformen Zstd-Frame im Store-Modus: Alle Daten werden in Raw-Blöcken unkomprimiert eingebettet. Der Output ist ein vollständiger, gültiger Zstd-Frame — der Dekompressionspfad aller RFC-konformen Zstd-Implementierungen kann ihn lesen. Er ist nicht kleiner als die Eingabe.

Frame-Aufbau:

Puffergröße (Worst Case):

benötigt = srcLen + 13 + ceil(srcLen / 131072) × 3

Für Eingaben ≤ 128 KB genügt dstMax = srcLen + 16. Leere Eingabe erzeugt 16 Bytes (Frame-Header + ein leerer letzter Block).

ZstdCompress prüft den Puffer vorab und gibt ZSTD_OVERFLOW zurück bevor Daten geschrieben werden.

import std.zstd;
import std.alloc;

fn ZstdStoreRoundtrip(): void {
    var original: pchar := "Hallo Zstd!"c;
    var srcLen: int64 := 11;

    // Komprimieren (Store-Modus)
    var cBuf: int64 := alloc(srcLen + 16);
    var cLen: int64 := ZstdCompress(original as int64, srcLen, cBuf, srcLen + 16);
    if cLen == ZSTD_ERR then { PrintLn("Fehler"c); return; }

    Print("Frame-Größe: "c); PrintLn(IntToStr(cLen)c);

    // Dekomprimieren
    var dBuf: int64 := alloc(srcLen + 1);
    var dLen: int64 := ZstdDecompress(cBuf, cLen, dBuf, srcLen + 1);
    poke8(dBuf + dLen, 0);
    PrintLn(dBuf as pchar);   // → "Hallo Zstd!"

    free(cBuf, srcLen + 16);
    free(dBuf, srcLen + 1);
}


Ausgabepuffer-Größe schätzen

Zstd-Frames enthalten im Header die unkomprimierte Größe (wenn der Kompressor sie gesetzt hat). Für gängige Werkzeuge trifft das in der Regel zu.

1. Content Size aus Frame-Header lesen (empfohlen, wenn möglich):

Die ersten 13 Bytes eines Zstd-Frames lassen sich manuell lesen:

Bytes 0–3: Magic (28 B5 2F FD)
Byte 4:    Frame Header Descriptor (FHD)
  bit[7:6] = FCS (Frame Content Size Feld-Größe): 0=1B, 1=2B, 2=4B, 3=8B
  bit[5]   = SSF (Single Segment Flag)
  bit[1:0] = DID (Dictionary ID size): 0=0B, 1=1B, 2=2B, 3=4B
Ab Byte 5:  optionaler Window Descriptor (wenn SSF=0), DID-Feld, Content Size

Für die meisten Standard-Streams (zstd-Werkzeug, kein Dictionary) ist FHD 0xE0 → SSF=1, FCS=3 → 8-Byte Content Size direkt ab Byte 5 im LE64-Format.

2. Konservativer Faktor (unbekannte Größe):

var dstMax: int64 := srcLen * 20;
if dstMax < 65536 then { dstMax := 65536; }
var dst: int64 := alloc(dstMax + 1);
var n: int64 := ZstdDecompress(src, srcLen, dst, dstMax);
if n == ZSTD_OVERFLOW then {
    // Puffer war zu klein — größer versuchen
}


Vergleich mit anderen Kompressionseinheiten

Merkmal std.zstd std.brotli std.zlib
Standard RFC 8878 RFC 7932 RFC 1950 / 1951
HTTP-Encoding zstd (aufkommend) br (HTTP/2, HTTP/3) gzip / deflate (universell)
Typischer Einsatz Server-Infrastruktur, Backups, Kafka, Docker Web-Assets, HTTP-Responses ZIP-Inhalte, .tar.gz, HTTP
Kompressor Store-Modus (kein LZ77) Store-Modus (kein LZ77) vollständig (LZ77 + Huffman)
Dekompressionstempo sehr schnell (typisch schnellster) vergleichbar gut
Dekompressionspfad vollständig (RFC 8878, keine Wörterbücher) vollständig (RFC 7932, kein §8-Static-Dict) vollständig
Einsatz in std.zip nein nein ja (Methode 8)
Frame enthält unkomprimierte Größe ja (meist) nein nein

Für HTTP-Responses (serverseitig) ist std.zlib mit GZIP aktuell die beste Wahl. std.zstd ist bevorzugt für Infrastruktur-nahe Systeme (Kafka-Dekodierung, Backup-Restore, .zst-Dateien).


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 ZstdDecompress / ZstdCompress automatisch beim Return

ZstdDecompress allokiert intern FSE-Tabellen, Huffman-Bäume und Lese-Kontexte — diese werden vollständig freigegeben bevor die Funktion zurückkehrt.

Letzte Aktualisierung: 2026-06-13