---
id: e52d68870be3de9f644fcfc584bd8aec
author: Lemon Mint
title: 'gosuda.org/zstd: Drop-In-Ersatz für DataDog/zstd mit Zero CGO und Crash-Isolation'
description: CGO_ENABLED=0 Go-Zstandard-Bibliothek, die die upstream C-libzstd 1.5.7 in einem Worker mit gemeinsamem Speicher ausführt, mit einer DataDog/zstd-kompatiblen API, wiederverwendbaren Kontexten und Bulk-Batching.
language: de
date: 2026-10-18T14:07:11.281986997Z
path: /zstd
go_package: gosuda.org/zstd
go_repourl: https://github.com/gosuda/hqq-zstd.git
category: lowlevel
---

`gosuda.org/zstd` ist eine `CGO_ENABLED=0`-Go-Kompressionsbibliothek, die die offizielle C-Zstandard-Engine (`libzstd 1.5.7`) innerhalb eines isolierten Worker-Prozesses über gemeinsamen Speicher von [HQQ](https://gosuda.org/hqq) ausführt. Sie bietet eine Drop-in-Replacement-API für `github.com/DataDog/zstd` (einschließlich einmaliger `Compress`/`Decompress`-Aufrufe, streamender `io.Writer`/`io.Reader`, vorab aufbereiteter Wörterbücher und wiederverwendbarer Kontexte ohne Allokationen), während die C-Ausführung vollständig außerhalb des Adressraums der Go-Laufzeit bleibt.

```bash
go get gosuda.org/zstd@v0.3.10507
```

## Warum die Dekompression außerhalb Ihres Go-Heaps stattfinden sollte

In produktiven Backend-Systemen liegen Kompression und Dekompression unmittelbar an der Grenze zu nicht vertrauenswürdigen Eingaben. Log-Collectors, Message-Broker, RPC-Gateways und Speicher-Engines dekomprimieren jede Minute Gigabyte extern bereitgestellter Frames.

Viele Go-Teams wählen `github.com/DataDog/zstd`, weil die Bibliothek direkt an das upstream-C-`libzstd` gebunden ist: Die C-Implementierung liefert einen hohen Durchsatz pro Kern, wirksame Wörterbuch-Kompressionsraten bei kleinen Datensätzen sowie außerhalb des Heaps liegende C-Arenen (`ZSTD_CCtx` und `ZSTD_DCtx`), die die Scan-Zeiten der Go-Garbage-Collection nicht erhöhen.

Die Einbindung von `libzstd` in einen Go-Dienst über `cgo` bringt jedoch zwei betriebliche Schwachstellen mit sich:

1. **Erschwernisse bei Build und Deployment**: Ebenso wie `go-sqlite3` erfordert `DataDog/zstd` `CGO_ENABLED=1` sowie eine Ziel-C-Compiler-Toolchain. Das Cross-Compiling von Diensten oder das Erstellen statischer Binärdateien für `linux/arm64` oder `windows/arm64` erfordert externe C-Sysroots.
2. **Gemeinsamer Adressraum bei nicht vertrauenswürdigen Eingaben**: Die Dekompression beschädigter oder feindseliger Nutzdaten innerhalb desselben virtuellen Adressraums wie Ihr Go-Server bedeutet, dass jeder Fehler im nativen C-Code oder in der eigenen Wörterbuchverarbeitung den gesamten Go-Prozess beenden kann.

`gosuda.org/zstd` behält die exakte upstream-C-Engine `libzstd 1.5.7` und die vertraute API von `github.com/DataDog/zstd` bei, verlagert die C-Ausführung jedoch in einen verifizierten Kindprozess (`zstd_server`), der über cache-line-ausgerichteten gemeinsamen Speicher mit Ihrem Go-Programm verbunden ist.

## Drop-in-Replacement für `github.com/DataDog/zstd`

Für die Migration von `github.com/DataDog/zstd` muss lediglich der Importpfad auf `"gosuda.org/zstd"` geändert werden. Es ist weder eine externe Binärinstallation noch Setup-Code erforderlich: Beim ersten Aufruf erzeugt `gosuda.org/zstd` eine eingebettete, per SLSA attestierte `zstd_server`-Binärdatei für Ihre Plattform, verifiziert deren SHA-256-Digest über `gosuda.org/hqq/secureexec` und öffnet einen Pool gemeinsam genutzter Speicher-Sitzungen.

```go
package main

import (
	"bytes"
	"fmt"
	"io"
	"log"

	"gosuda.org/zstd"
)

func main() {
	// 1. One-shot Compress / Decompress (mirrors github.com/DataDog/zstd)
	src := []byte("shared-memory zstd payload without cgo")
	compressed, err := zstd.CompressLevel(nil, src, zstd.BestSpeed)
	if err != nil {
		log.Fatal(err)
	}
	plain, err := zstd.Decompress(nil, compressed)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("one-shot: %d bytes -> %d bytes (%s)\n", len(src), len(compressed), plain)

	// 2. Streaming Writer and Reader with Dictionary Support
	var buf bytes.Buffer
	w := zstd.NewWriterLevel(&buf, zstd.DefaultCompression)
	if _, err := w.Write(src); err != nil {
		log.Fatal(err)
	}
	if err := w.Close(); err != nil {
		log.Fatal(err)
	}

	r := zstd.NewReader(&buf)
	defer r.Close()
	streamed, err := io.ReadAll(r)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("streamed: %s\n", streamed)
}
```

### Unterstützte Plattformen und eingebettete Binärdateien

Alle sechs Zielplattformen werden mit vorgefertigten, per SHA-256 fixierten Worker-Binärdateien ausgeliefert, die über plattformspezifische Build-Tags (`internal/embedded/embed_<goos>_<goarch>.go`) eingebettet werden:

| Zielbetriebssystem | Zielarchitektur (`GOARCH`) | Eingebettete Binärdatei         | Binärgröße     | Build-Toolchain und Herkunftsnachweis             |
| :----------------- | :------------------------- | :------------------------------ | -------------: | :------------------------------------------------ |
| **Linux**          | `amd64`, `arm64`           | `zstd_server_linux_<arch>`      | ~770 - 880 KiB | GCC `-O3`, SLSA Build L3 Provenance               |
| **macOS**          | `arm64`, `amd64`           | `zstd_server_darwin_<arch>`     | ~660 - 795 KiB | AppleClang `-O3`, SLSA Build L3 Provenance        |
| **Windows**        | `amd64`                    | `zstd_server_windows_amd64.exe` |       ~818 KiB | MSVC `-O2`, SLSA Build L3 Provenance              |
| **Windows**        | `arm64`                    | `zstd_server_windows_arm64.exe` |       ~551 KiB | `zig cc -target aarch64-windows-gnu -O3`, SLSA L3 |

## Im gemeinsamen Speicher-Datenpfad

Um den Durchsatz im Vergleich zu prozessinternem `cgo` konkurrenzfähig zu halten, vermeidet `gosuda.org/zstd` Socket-Serialisierung und Heap-Allokationen auf dem Hot Path.

### 1. Allokationsfreie Slot-Kodierung (`zstd_ctx.go` und `c/zstd_hooks.c`)

Wenn Sie einen wiederverwendbaren `zstd.Ctx` verwenden (oder wenn die Paketfunktionen `Compress` und `Decompress` einen `Ctx` aus dem internen Sitzungspool entnehmen), fließen die Daten direkt durch den abgebildeten `StandardLink`-Slot:

```text
Go Caller (zstd.Ctx)                    Shared-Memory Slot                    C11 Worker (zstd_hooks.c)
--------------------                    ------------------                    -------------------------
src []byte  ------------------------->  Request Slot Payload  ------------->  ZSTD_compressCCtx(
                                                                                cctx, resp_slot, src, level)
dst []byte  <-------------------------  Response Slot Payload <-------------  Writes compressed frame directly
                                                                              into shared response slot
```

1. Der generierte `hqq-bindgen`-Client kopiert `src` direkt in den speicherabgebildeten Request-Slot und setzt das `SeqLock`-Sequenzwort um.
2. Der C-Worker liest `src` direkt aus dem Request-Slot und übergibt den Payload-Zeiger des Response-Slots unmittelbar als Zielpuffer an `ZSTD_compressCCtx` bzw. `ZSTD_decompressDCtx`.
3. Der Go-Client kopiert das resultierende Slice aus dem Response-Slot in das vom Aufrufer bereitgestellte `dst`-Slice. Im stationären Betrieb führen `ctx.CompressTo(dst, src, level)` und `ctx.DecompressInto(dst, src)` `0 allocs/op` aus.

Vorab aufbereitete Wörterbücher (`CDict` und `DDict`) befinden sich auf dem C-Worker hinter generationsgeprüften 64-Bit-Handles (`handle CDict`, `handle DDict` in `zstd.idl`). Das einmalige Erstellen eines Wörterbuchs über `ctx.NewCDict(rawDict, level)` parst die Wörterbuchtabellen innerhalb des C-Workers und liefert ein leichtgewichtiges Handle zurück, das über Tausende von Aufrufen hinweg wiederverwendet werden kann, ohne die Wörterbuchbytes erneut zu übertragen.

### 2. Amortisierung des IPC-Overheads durch Bulk-Framing (`zstd_bulk.go`)

Bei großen Nutzdaten (16 KiB und darüber) ist der Round Trip über gemeinsamen Speicher von etwa 200 ns im Vergleich zur CPU-Zeit innerhalb von `libzstd` vernachlässigbar. Bei Arbeitslasten, die Tausende kleiner Nachrichten von 100 Byte bis 1 KiB auf einmal komprimieren, summieren sich jedoch selbst 200 ns pro Element.

`gosuda.org/zstd` stellt `CompressBulk` und `DecompressBulk` auf `BulkCtx` (`zstd_bulk.go`) bereit, um Round Trips pro Element zu eliminieren:

```go
bctx, err := client.NewBulkCtx(1 << 20)
if err != nil {
	log.Fatal(err)
}
defer bctx.Close()

items := [][]byte{
	[]byte(`{"event":"login","user":101}`),
	[]byte(`{"event":"purchase","user":101,"amount":4200}`),
	[]byte(`{"event":"logout","user":101}`),
}
compressedItems, err := bctx.CompressBulk(nil, items, zstd.DefaultCompression)
```

`CompressBulk` packt den gesamten `[][]byte`-Stapel in einen einzigen längenpräfixierten Request-Frame im gemeinsamen Speicher. Der C-Worker durchläuft jedes Element in C (`compress_bulk` in `c/zstd_hooks.c`), schreibt alle komprimierten Frames zusammenhängend in den Response-Slot und liefert den gesamten Stapel in einem einzigen Round Trip über gemeinsamen Speicher zurück.

### 3. Überleben des Worker-Abbruchs ohne Absturz des Aufrufers

Da `zstd_server` in einem separaten Betriebssystemprozess läuft, bringt ein fataler Fehler im C-Worker Ihre Go-Anwendung nie zum Absturz:

- Wird `zstd_server` extern beendet (etwa durch `SIGKILL` oder ein OOM-Ereignis), erkennt die HQQ-Transportschicht den unterbrochenen Peer sofort und gibt einen typisierten `*rt.TransportError` aus dem laufenden `Compress`- oder `Decompress`-Aufruf zurück.
- Paketweite Funktionen (`zstd.Compress`, `zstd.Decompress`, `zstd.NewWriter`, `zstd.NewReader`) erkennen automatisch, dass der gemeinsam genutzte Standard-Worker nicht mehr vorhanden ist, starten beim nächsten Aufruf eine neue verifizierte `zstd_server`-Instanz und nehmen den regulären Betrieb wieder auf.

## Repository-Struktur

Die Codebasis trennt die Drop-in-Fassade `DataDog/zstd` von den low-level Session- und Bulk-Primitiven von `hqq-bindgen`:

```text
gosuda.org/zstd
├── autolaunch.go / hashes.go  # Drop-in Compress/Decompress API, auto-recovery pool, and SHA-256 hashes
├── zstd_stream.go             # Streaming Writer/Reader (NewWriterLevelDict, NewReaderDict)
├── zstd_ctx.go                # Zero-allocation Ctx, CDict, and DDict wrappers over hqq-bindgen
├── zstd_bulk.go               # Single-round-trip CompressBulk and DecompressBulk framing
├── zstd.go                    # Generated hqq-bindgen Go client for the zstd IDL service
├── zstd.idl                   # HQQ IDL schema defining Context, CDict, DDict, and RPC methods
├── internal/embedded/         # Platform-tagged //go:embed zstd_server binaries (6 OS/Arch targets)
└── c/                         # C11 server implementation (zstd_server.c, zstd_hooks.c, main.c)
```

## Erste Schritte

Installieren Sie `gosuda.org/zstd` mit `go get` und verwenden Sie es mit `CGO_ENABLED=0`:

```bash
go get gosuda.org/zstd@v0.3.10507
```

- **GitHub-Repository**: [https://github.com/gosuda/hqq-zstd](https://github.com/gosuda/hqq-zstd)
- **Architektur und Benchmarks**: [docs/ARCHITECTURE.md](https://github.com/gosuda/hqq-zstd/blob/main/docs/ARCHITECTURE.md)
- **HQQ Core Runtime**: [https://gosuda.org/hqq](https://gosuda.org/hqq)
