---
id: e52d68870be3de9f644fcfc584bd8aec
author: Lemon Mint
title: 'gosuda.org/zstd: Drop-In съвместимост с DataDog/zstd, компресия със Zero CGO и изолация на сривовете'
description: CGO_ENABLED=0 Go библиотека Zstandard, която изпълнява горния (upstream) C libzstd 1.5.7 в работник със споделена памет, с API, съвместим с DataDog/zstd, повторно използваеми контексти и пакетна обработка на данни.
language: bg
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` е Go библиотека за компресия с `CGO_ENABLED=0`, която изпълнява официалния C енджин на Zstandard (`libzstd 1.5.7`) в изолиран работен процес чрез споделена памет [HQQ](https://gosuda.org/hqq). Тя предоставя API за директна замяна на `github.com/DataDog/zstd` (включително еднократни `Compress`/`Decompress`, поточни `io.Writer`/`io.Reader`, предварително обработени речници и повторно използваеми контексти с нулева алокация), като същевременно изпълнението на C кода остава напълно извън адресното пространство на Go рънтайма.

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

## Защо декомпресията трябва да бъде извън Go heap паметта

В продуктивни бекенд системи компресията и декомпресията се намират directly на границата с недоверени входни данни. Колектори на логове, брокери на съобщения, RPC шлюзове и системи за съхранение декомпресират гигабайти външно подадени фреймове всяка минута.

Много Go екипи избират `github.com/DataDog/zstd`, защото се свързва директно с upstream C `libzstd`: C имплементацията осигурява висока производителност на едно ядро, добри коефициенти на компресия с речници за малки записи и off-heap C арени (`ZSTD_CCtx` и `ZSTD_DCtx`), които не увеличават времето за сканиране от garbage collector-а на Go.

Свързването на `libzstd` с Go услуга чрез `cgo` обаче носи два оперативни проблема:

1. **Трудности при изграждане и внедряване**: Подобно на `go-sqlite3`, `DataDog/zstd` изисква `CGO_ENABLED=1` и целеви C компилаторен toolchain. Кроскомпилирането на услуги или изграждането на статични бинарни файлове за `linux/arm64` или `windows/arm64` изисква външни C sysroot среди.
2. **Споделено адресно пространство при недоверени входни данни**: Декомпресирането на повредени или злонамерени полезни товари в същото виртуално адресно пространство като вашия Go сървър означава, че всеки бъг в native C кода или в персонализираното обработване на речници може да прекрати целия Go процес.

`gosuda.org/zstd` запазва точния upstream C енджин `libzstd 1.5.7` и познатия API на `github.com/DataDog/zstd`, но премества изпълнението на C кода в проверен дъщерен процес (`zstd_server`), свързан с вашата Go програма чрез споделена памет, подравнена по cache line.

## Директна замяна на `github.com/DataDog/zstd`

Миграцията от `github.com/DataDog/zstd` изисква промяна само на импорт пътя към `"gosuda.org/zstd"`. Не е необходима инсталация на външен бинарен файл или код за настройка: при първото извикване `gosuda.org/zstd` материализира вграден `zstd_server` бинарен файл с SLSA атестация за вашата платформа, проверява SHA-256 дайджеста му чрез `gosuda.org/hqq/secureexec` и отваря пул от сесии в споделена памет.

```go
package main

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

	"gosuda.org/zstd"
)

func main() {
	// 1. Еднократни Compress / Decompress (повтаря 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. Поточни Writer и Reader с поддръжка на речници
	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)
}
```

### Поддържани платформи и вградени бинарни файлове

Всички шест целеви платформи се доставят с предварително компилирани работни бинарни файлове, фиксирани чрез SHA-256 и вградени чрез платформено-специфични build tags (`internal/embedded/embed_<goos>_<goarch>.go`):

| Целева ОС   | Целева архитектура (`GOARCH`) | Вграден бинарен файл            | Размер на файла | Build toolchain и произход                         |
| :---------- | :---------------------------- | :------------------------------ | --------------: | :------------------------------------------------ |
| **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 |

## Вътрешна структура на пътя за данни в споделена памет

За да остане пропускателната способност конкурентна спрямо in-process `cgo`, `gosuda.org/zstd` избягва сериализация през сокети и heap алокации в горещия път.

### 1. Кодиране в слот без алокация (`zstd_ctx.go` и `c/zstd_hooks.c`)

Когато използвате повторно използваем `zstd.Ctx` (или когато функциите на пакетно ниво `Compress` и `Decompress` заемат `Ctx` от вътрешния пул от сесии), данните текат директно през картографирания `StandardLink` слот:

```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. Генерираният `hqq-bindgen` клиент копира `src` directly в картографирания request слот и превключва sequence word-а на `SeqLock`.
2. C работникът чете `src` на място от request слота и подава указателя към полезния товар на response слота directly като целеви буфер на `ZSTD_compressCCtx` или `ZSTD_decompressDCtx`.
3. Go клиентът копира получения slice от response слота в подадения от извикващия slice `dst`. В стационарно състояние `ctx.CompressTo(dst, src, level)` и `ctx.DecompressInto(dst, src)` извършват `0 allocs/op`.

Предварително обработените речници (`CDict` и `DDict`) живеят в C работника зад генерационно проверявани 64-битови handles (`handle CDict`, `handle DDict` в `zstd.idl`). Създаването на речник веднъж чрез `ctx.NewCDict(rawDict, level)` парсва таблиците на речника в C работника и връща лек handle, който може да се използва повторно при хиляди извиквания, без повторно изпращане на байтовете на речника.

### 2. Амортизиране на IPC разходите чрез bulk framing (`zstd_bulk.go`)

При големи полезни товари (16 KiB и повече) ~200 ns цикъл през споделената памет е пренебрежим спрямо процесорното време, прекарано в `libzstd`. При работни натоварвания, които компресират хиляди малки съобщения от 100 байта до 1 KiB наведнъж, обаче заплащането на дори 200 ns за всеки елемент се натрупва.

`gosuda.org/zstd` предоставя `CompressBulk` и `DecompressBulk` върху `BulkCtx` (`zstd_bulk.go`), за да елиминира обмените по елемент:

```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` опакова целия пакет `[][]byte` в една заявка с префикс за дължина в споделената памет. C работникът обхожда всеки елемент на C ниво (`compress_bulk` в `c/zstd_hooks.c`), записва всички компресирани фреймове последователно в response слота и връща целия пакет при един обмен през споделената памет.

### 3. Оцеляване при прекратяване на работника без срив на извикващия

Тъй като `zstd_server` работи в отделен OS процес, фатален провал в C работника никога не сваля вашето Go приложение:

- Ако `zstd_server` бъде убит externally (например чрез `SIGKILL` или OOM събитие), транспортният слой на HQQ незабавно открива нарушения peer и връща типизиран `*rt.TransportError` от текущото извикване на `Compress` или `Decompress`.
- Функциите на пакетно ниво (`zstd.Compress`, `zstd.Decompress`, `zstd.NewWriter`, `zstd.NewReader`) автоматично откриват, че споделеният по подразбиране работник е изчезнал, стартират нова проверена инстанция на `zstd_server` при следващото извикване и възобновяват нормалната работа.

## Структура на хранилището

Кодовата база отделя facade-а `DataDog/zstd` за директна замяна от низкоуровневите примитиви за сесии и bulk операции на `hqq-bindgen`:

```text
gosuda.org/zstd
├── autolaunch.go / hashes.go  # API за Compress/Decompress за директна замяна, пул с автоматично възстановяване и SHA-256 хешове
├── zstd_stream.go             # Поточни Writer/Reader (NewWriterLevelDict, NewReaderDict)
├── zstd_ctx.go                # Ctx без алокация, CDict и DDict обвивки върху hqq-bindgen
├── zstd_bulk.go               # Framing за CompressBulk и DecompressBulk с един обмен
├── zstd.go                    # Генериран hqq-bindgen Go клиент за IDL услугата zstd
├── zstd.idl                   # HQQ IDL схема, дефинираща Context, CDict, DDict и RPC методи
├── internal/embedded/         # Платформено-тагнати //go:embed бинарни файлове zstd_server (6 OS/Arch цели)
└── c/                         # Имплементация на C11 сървъра (zstd_server.c, zstd_hooks.c, main.c)
```

## Начало на работа

Инсталирайте `gosuda.org/zstd` с `go get` и го използвайте с `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)
- **Architecture & 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)
