---
id: e52d68870be3de9f644fcfc584bd8aec
author: Lemon Mint
title: 'gosuda.org/zstd: Drop-In-yhteensopiva DataDog/zstd-pakkaus ilman CGO-riippuvuutta ja kaatumisten eristäminen'
description: CGO_ENABLED=0 Go-Zstandard-kirjasto, joka suorittaa alkuperäisen C-kirjaston libzstd 1.5.7 jaetussa muistissa toimivassa työprosessissa, DataDog/zstd-yhteensopivalla rajapinnalla, uudelleenkäytettävillä kontekstiatuilla ja joukkokäsittelyllä (bulk batching).
language: fi
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` on `CGO_ENABLED=0` Go-pakkauskirjasto, joka suorittaa virallisen C-pohjaisen Zstandard-moottorin (`libzstd 1.5.7`) eristetyssä työprosessissa [HQQ](https://gosuda.org/hqq)-jaetun muistin kautta. Se tarjoaa pudotettavan korvaavan API:n `github.com/DataDog/zstd`-kirjastolle (mukaan lukien kertakäyttöiset `Compress`/`Decompress`, suoratoistavat `io.Writer`/`io.Reader`, esikäsitellyt sanakirjat sekä uudelleenkäytettävät nollaallokaatiokontekstit) ja pitää C-suorituksen kokonaan Go-ajonaikaisen osoiteavaruuden ulkopuolella.

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

## Miksi purku kuuluu Go-keon ulkopuolelle

Tuotantotaustajärjestelmissä pakkaus ja purku sijaitsevat suoraan luottamattoman syötteen rajapinnalla. Lokinkerääjät, viestivälittäjät, RPC-yhdyskäytävät ja tallennusmoottorit purkavat gigatavuja ulkoista dataa kehyksinä joka minuutti.

Monet Go-tiimit valitsevat `github.com/DataDog/zstd`-kirjaston, koska se sitoutuu suoraan alkuperäiseen C-kirjastoon `libzstd`: C-toteutus tarjoaa korkean yksiytimisen läpimenon, tiiviit sanakirjapakkaussuhteet pienille tietueille sekä Go-keon ulkopuoliset C-areenat (`ZSTD_CCtx` ja `ZSTD_DCtx`), jotka eivät kasvata Go-roskienkeruun tarkastusaikoja.

`libzstd`:n liittäminen Go-palveluun `cgo`:n kautta tuo kuitenkin kaksi operatiivista ongelmaa:

1. **Rakentamisen ja käyttöönoton haasteet**: Samoin kuin `go-sqlite3`, `DataDog/zstd` vaatii `CGO_ENABLED=1`-asetuksen ja kohdealustan C-kääntäjätoolchainin. Palveluiden ristikääntäminen tai staattisten binäärien rakentaminen kohteille `linux/arm64` tai `windows/arm64` edellyttää ulkoisia C-sysroot-ympäristöjä.
2. **Jaettu osoiteavaruus luottamattomilla syötteillä**: Vioittuneiden tai vihamielisten hyötykuormien purku samassa virtuaalisessa osoiteavaruudessa Go-palvelimesi kanssa tarkoittaa, että mikä tahansa virhe natiivissa C-koodissa tai mukautetussa sanakirjakäsittelyssä voi lopettaa koko Go-prosessin.

`gosuda.org/zstd` säilyttää tarkan alkuperäisen C-pohjaisen `libzstd 1.5.7`-moottorin ja tutun `github.com/DataDog/zstd`-API:n, mutta siirtää C-suorituksen vahvistettuun lapsiprosessiin (`zstd_server`), joka on yhteydessä Go-ohjelmaasi välimuistirivin kokoisen jaetun muistin kautta.

## Pudotettava korvaava toteutus `github.com/DataDog/zstd`-kirjastolle

Siirtyminen `github.com/DataDog/zstd`-kirjastosta edellyttää vain tuontipolun vaihtamista muotoon `"gosuda.org/zstd"`. Ulkoista binääriasennusta tai alustuskoodia ei tarvita: ensimmäisellä kutsulla `gosuda.org/zstd` purkaa upotetun SLSA-todistetun `zstd_server`-binäärin alustallesi, varmistaa sen SHA-256-tiivisteen `gosuda.org/hqq/secureexec`-paketin avulla ja avaa jaetun muistin istuntoaltaan.

```go
package main

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

	"gosuda.org/zstd"
)

func main() {
	// 1. Kertakäyttöinen Compress / Decompress (peilaa github.com/DataDog/zstd-kirjaston)
	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. Suoratoistava Writer ja Reader sanakirjatuella
	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)
}
```

### Tuetut alustat ja upotetut binäärit

Kaikki kuusi kohdealustaa toimitetaan valmiiksi käännettyinä, SHA-256-kiinnitettyinä työbinäärinä, jotka upotetaan alustakohtaisilla rakennustunnisteilla (`internal/embedded/embed_<goos>_<goarch>.go`):

| Kohdekäyttöjärjestelmä | Kohdearkkitehtuuri (`GOARCH`) | Upotettu binääri                | Binäärin koko | Rakennustoolchain ja alkuperä                     |
| :--------------------- | :---------------------------- | :------------------------------ | ------------: | :------------------------------------------------ |
| **Linux**              | `amd64`, `arm64`              | `zstd_server_linux_<arch>`      | ~770 - 880 KiB | GCC `-O3`, SLSA Build L3 -alkuperä               |
| **macOS**              | `arm64`, `amd64`              | `zstd_server_darwin_<arch>`     | ~660 - 795 KiB | AppleClang `-O3`, SLSA Build L3 -alkuperä        |
| **Windows**            | `amd64`                       | `zstd_server_windows_amd64.exe` |       ~818 KiB | MSVC `-O2`, SLSA Build L3 -alkuperä              |
| **Windows**            | `arm64`                       | `zstd_server_windows_arm64.exe` |       ~551 KiB | `zig cc -target aarch64-windows-gnu -O3`, SLSA L3 |

## Jaetun muistin datapolun sisällä

Pitääkseen läpimenon kilpailukykyisenä sisäprosessin `cgo`:n kanssa `gosuda.org/zstd` välttää socket-sarjallistamisen ja keon allokaatiot kuumalla polulla.

### 1. Nollaallokaatioinen slot-koodaus (`zstd_ctx.go` ja `c/zstd_hooks.c`)

Kun käytät uudelleenkäytettävää `zstd.Ctx`-kontekstia (tai kun pakettitason `Compress`- ja `Decompress`-funktiot lainaavat `Ctx`-kontekstin sisäiseltä istuntoaltaalta), data kulkee suoraan kartoitetun `StandardLink`-slotin kautta:

```text
Go-kutsuja (zstd.Ctx)                   Jaetun muistin slot                   C11-työprosessi (zstd_hooks.c)
---------------------                   ------------------                    -------------------------
src []byte  ------------------------->  Pyyntöslotin hyötykuorma  ----------->  ZSTD_compressCCtx(
                                                                                cctx, resp_slot, src, level)
dst []byte  <-------------------------  Vastausslotin hyötykuorma <-----------  Kirjoittaa pakatun kehyksen suoraan
                                                                              jaettuun vastausslottiin
```

1. Generoitu `hqq-bindgen`-asiakas kopioi `src`-datan suoraan muistikartoitettuun pyyntöslottiin ja kääntää `SeqLock`-sekvenssisanan.
2. C-työprosessi lukee `src`-datan paikallaan pyyntöslotista ja välittää vastausslotin hyötykuorman osoittimen suoraan kohdepuskurina funktiolle `ZSTD_compressCCtx` tai `ZSTD_decompressDCtx`.
3. Go-asiakas kopioi tuloksena syntyneen siivun vastausslotista kutsujan antamaan `dst`-siivuun. Vakaassa tilassa `ctx.CompressTo(dst, src, level)` ja `ctx.DecompressInto(dst, src)` suorittavat `0 allocs/op`.

Esikäsitellyt sanakirjat (`CDict` ja `DDict`) sijaitsevat C-työprosessissa sukupolvitarkistettujen 64-bittisten kahvojen (`handle CDict`, `handle DDict` tiedostossa `zstd.idl`) takana. Sanakirjan luominen kerran kutsulla `ctx.NewCDict(rawDict, level)` jäsentää sanakirjataulut C-työprosessissa ja palauttaa kevyen kahvan, jota voidaan käyttää uudelleen tuhansissa kutsuissa ilman sanakirjatavujen uudelleenlähettämistä.

### 2. IPC-yleiskustannusten tasaaminen joukkokehystyksellä (`zstd_bulk.go`)

Suurille hyötykuormille (16 KiB ja suuremmat) noin 200 ns:n jaetun muistin kierto on merkityksetön verrattuna `libzstd`:n sisällä kuluvaan CPU-aikaan. Työkuormissa, jotka pakkaavat tuhansia pieniä 100 tavun – 1 KiB:n viestejä kerralla, jo 200 ns per kohde kasvaa kuitenkin merkittäväksi.

`gosuda.org/zstd` tarjoaa `CompressBulk`- ja `DecompressBulk`-funktiot `BulkCtx`-tyypille (`zstd_bulk.go`) kierrosten poistamiseksi kohdekohtaisesti:

```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` pakkaa koko `[][]byte`-erän yhdeksi pituusetuliitteiseksi pyyntökehykseksi jaettuun muistiin. C-työprosessi käy jokaisen kohteen läpi C-koodissa (`compress_bulk` tiedostossa `c/zstd_hooks.c`), kirjoittaa kaikki pakatut kehykset peräkkäin vastausslottiin ja palauttaa koko erän yhdellä jaetun muistin kierrolla.

### 3. Työprosessin päättymisen selviäminen ilman kutsujan kaatumista

Koska `zstd_server` toimii erillisessä käyttöjärjestelmäprosessissa, C-työprosessin fataali virhe ei koskaan kaada Go-sovellustasi:

- Jos `zstd_server` tapetaan ulkoisesti (esimerkiksi `SIGKILL`-signaalilla tai OOM-tapahtumalla), HQQ-siirtokerros havaitsee katkenneen vastapään välittömästi ja palauttaa tyypitetyn `*rt.TransportError`-virheen käynnissä olevasta `Compress`- tai `Decompress`-kutsusta.
- Pakettitason funktiot (`zstd.Compress`, `zstd.Decompress`, `zstd.NewWriter`, `zstd.NewReader`) havaitsevat automaattisesti, että jaettu oletustyöprosessi on poissa, käynnistävät seuraavalla kutsulla uuden vahvistetun `zstd_server`-instanssin ja palauttavat normaalin toiminnan.

## Repositorion rakenne

Koodikanta erottaa pudotettavan `DataDog/zstd`-julkisivun matalan tason `hqq-bindgen`-istunto- ja joukkoprimitiiveistä:

```text
gosuda.org/zstd
├── autolaunch.go / hashes.go  # Pudotettava Compress/Decompress-API, automaattisen palautumisen allas ja SHA-256-tiivisteet
├── zstd_stream.go             # Suoratoistava Writer/Reader (NewWriterLevelDict, NewReaderDict)
├── zstd_ctx.go                # Nollaallokaatioinen Ctx, CDict ja DDict hqq-bindgen-kääreinä
├── zstd_bulk.go               # Yhden kierroksen CompressBulk- ja DecompressBulk-kehystys
├── zstd.go                    # Generoitu hqq-bindgen Go -asiakas zstd IDL -palvelulle
├── zstd.idl                   # HQQ IDL -skeema, joka määrittelee Context-, CDict-, DDict- ja RPC-metodit
├── internal/embedded/         # Alustakohtaisesti merkityt //go:embed zstd_server -binäärit (6 OS/arkkitehtuuri-kohdetta)
└── c/                         # C11-palvelintoteutus (zstd_server.c, zstd_hooks.c, main.c)
```

## Aloittaminen

Asenna `gosuda.org/zstd` komennolla `go get` ja käytä sitä asetuksella `CGO_ENABLED=0`:

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

- **GitHub-repositorio**: [https://github.com/gosuda/hqq-zstd](https://github.com/gosuda/hqq-zstd)
- **Arkkitehtuuri ja vertailutestit**: [docs/ARCHITECTURE.md](https://github.com/gosuda/hqq-zstd/blob/main/docs/ARCHITECTURE.md)
- **HQQ-ydinajonaika**: [https://gosuda.org/hqq](https://gosuda.org/hqq)
