std.gzip

Der gzip-Container nach RFC 1952 — der Rahmen um DEFLATE, den std.zlib liefert. Erzeugte Dateien lassen sich mit gunzip auspacken, und von gzip erzeugte Dateien liest diese Unit.

Der Aufwand steckt nicht im Komprimieren, sondern in drei Kleinigkeiten des Formats:

  • Die optionalen Kopffelder. Nach dem Flag-Byte können FEXTRA (Länge + Daten), FNAME und FCOMMENT (nullterminiert) und FHCRC folgen. Wer sie nicht überspringt, setzt den DEFLATE-Strom an der falschen Stelle an — und merkt es erst, wenn der Trailer nicht passt.
  • Der Trailer wird geprüft, nicht nur gelesen. Eine mitgeführte, aber ungeprüfte Prüfsumme ist schlimmer als gar keine: Sie erweckt den Eindruck, der Inhalt sei abgesichert. Geprüft werden CRC-32 und ISIZE — und FHCRC obendrein, obwohl der RFC dort auch bloßes Überspringen erlaubt.
  • Mehrere aneinandergehängte Member sind zulässig (cat a.gz b.gz). Sie werden nacheinander ausgepackt und aneinandergehängt — so verhält sich gzip -d auch.

Autor: Andreas Röne
Copyright: 2024-2026 Andreas Röne

std.zlib · std.brotli · std.zstd · Standard Library


Funktionen

Signatur Beschreibung
GzipCompressBound(srcLen: int64): int64 Obergrenze der Ausgabegröße — so viel muss der Zielpuffer fassen
GzipCompress(src, srcLen, dst, dstMax): int64 Packt als ein gzip-Member; liefert die geschriebenen Bytes oder GZIP_OVERFLOW
GzipDecompress(src, srcLen, dst, dstMax): int64 Packt aus; liefert die geschriebenen Bytes, GZIP_ERR oder GZIP_OVERFLOW
Konstante Wert Bedeutung
GZIP_OK 0 Erfolg
GZIP_ERR -1 Kein gzip, verstümmelt oder Prüfsumme falsch
GZIP_OVERFLOW -2 Ausgabepuffer zu klein

import std.io;
import std.gzip;

fn main(): int64 {
    var src: pchar := "Hallo Welt Hallo Welt Hallo Welt"c;

    var bound: int64 := GzipCompressBound(32);
    var dst:   int64 := alloc(bound);
    var n:     int64 := GzipCompress(src as int64, 32, dst, bound);
    PrintLn(IntToStr(n));            // geschriebene Bytes

    var back: int64 := alloc(256);
    var m:    int64 := GzipDecompress(dst, n, back, 256);
    PrintLn(IntToStr(m));            // 32 — wieder die Ausgangslänge
    return 0;
}


Was der Container schreibt

GzipCompress schreibt den schlanken Kopf ohne optionale Felder: kein Dateiname, kein Kommentar, kein Zeitstempel. Nach RFC 1952 §2.3.1.2 genügt einem konformen Kompressor, ID1, ID2, CM, CRC32 und ISIZE richtig zu setzen und die übrigen Felder auf Vorgabewerten zu lassen.

MTIME = 0 heißt im RFC ausdrücklich „kein Zeitstempel vorhanden„. Das ist eine bewusste Entscheidung: Ein echter Zeitstempel wäre eine verdeckte Abhängigkeit von der Uhr und machte die Ausgabe unreproduzierbar — dieselbe Eingabe ergäbe bei jedem Lauf eine andere Datei.

Beim Auspacken werden alle optionalen Felder dagegen gelesen bzw. übersprungen. Ein gesetztes reserviertes Flag-Bit führt zu GZIP_ERR: Es könnte ein neues Feld ankündigen, das alles Folgende verschiebt — dann wäre stilles Weiterdekodieren Raten.

Anders als std.brotli und std.zstd, die nur den Store-Modus schreiben, komprimiert GzipCompress wirklich — über DeflateCompressLZ77 aus std.zlib. 4096 Byte mit vierstelligem Muster ergeben 65 Byte Ausgabe; sehr kurze, unregelmäßige Eingaben können durch Kopf und Trailer (18 Byte) dennoch wachsen. FDICT gibt es in gzip nicht und ist entsprechend nicht vorgesehen.

 
dstMax muss mindestens GzipCompressBound(srcLen) sein — nicht bloß groß genug für das Ergebnis. Die Prüfung steht vor dem Komprimieren und vergleicht gegen die Obergrenze:

GzipCompress(src, 4096, dst, 256)     // -> GZIP_OVERFLOW (-2), obwohl 65 Byte gereicht hätten


Den Zielpuffer also immer mit GzipCompressBound bemessen (srcLen + srcLen/8 + 128) und nach dem Aufruf auf die zurückgegebene Länge zuschneiden.

Letzte Aktualisierung: 2026-08-11