---
id: e52d68870be3de9f644fcfc584bd8aec
author: Lemon Mint
title: 'gosuda.org/zstd: Substituição Direta do DataDog/zstd com Compressão Sem CGO e Isolamento de Falhas'
description: Biblioteca Go Zstandard com CGO_ENABLED=0 executando a libzstd em C upstream 1.5.7 em um worker de memória compartilhada, com API compatível com DataDog/zstd, contextos reutilizáveis e agrupamento em lote.
language: pt
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` é uma biblioteca de compressão Go com `CGO_ENABLED=0` que executa o mecanismo oficial Zstandard em C (`libzstd 1.5.7`) dentro de um processo trabalhador isolado, por meio de memória compartilhada [HQQ](https://gosuda.org/hqq). Ela oferece uma API de substituição direta para `github.com/DataDog/zstd` (incluindo `Compress`/`Decompress` em uma única chamada, `io.Writer`/`io.Reader` para streaming, dicionários pré-processados e contextos reutilizáveis com alocação zero), mantendo a execução do código C completamente fora do espaço de endereçamento do runtime Go.

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

## Por Que a Descompressão Deve Ocorrer Fora do Heap Go

Em backends de produção, a compressão e a descompressão situam-se diretamente na fronteira de entrada não confiável. Coletores de logs, corretores de mensagens, gateways RPC e mecanismos de armazenamento descomprimem gigabytes de quadros fornecidos externamente a cada minuto.

Muitas equipes Go escolhem `github.com/DataDog/zstd` porque ele se vincula diretamente à `libzstd` em C upstream: a implementação em C oferece alta vazão em um único núcleo, taxas de compressão de dicionário precisas em registros pequenos e arenas C fora do heap (`ZSTD_CCtx` e `ZSTD_DCtx`) que não aumentam o tempo de varredura do coletor de lixo do Go.

No entanto, vincular `libzstd` a um serviço Go por meio de `cgo` traz dois problemas operacionais:

1. **Atrito de Build e Implantação**: Assim como o `go-sqlite3`, o `DataDog/zstd` exige `CGO_ENABLED=1` e um conjunto de ferramentas de compilação C para a plataforma alvo. A compilação cruzada de serviços ou a geração de binários estáticos para `linux/arm64` ou `windows/arm64` exige sysroots C externos.
2. **Espaço de Endereçamento Compartilhado com Entradas Não Confiáveis**: Descomprimir cargas corrompidas ou adversariais no mesmo espaço de endereçamento virtual do seu servidor Go significa que qualquer defeito no código C nativo ou no tratamento de dicionários personalizados pode encerrar todo o processo Go.

O `gosuda.org/zstd` mantém o mecanismo exato `libzstd 1.5.7` em C upstream e a API familiar do `github.com/DataDog/zstd`, mas transfere a execução do código C para um processo filho verificado (`zstd_server`) conectado ao seu programa Go por memória compartilhada alinhada a linhas de cache.

## Substituição Direta de `github.com/DataDog/zstd`

A migração a partir de `github.com/DataDog/zstd` exige alterar apenas o caminho de importação para `"gosuda.org/zstd"`. Não é necessária instalação de binário externo nem código de configuração: na primeira chamada, o `gosuda.org/zstd` materializa um binário `zstd_server` embutido, com atestação SLSA, para a sua plataforma, verifica seu digest SHA-256 por meio de `gosuda.org/hqq/secureexec` e abre um pool de sessões em memória compartilhada.

```go
package main

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

	"gosuda.org/zstd"
)

func main() {
	// 1. Compress / Decompress em uma única chamada (espelha 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 e Reader de streaming com suporte a dicionário
	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)
}
```

### Plataformas Suportadas e Binários Embutidos

Todas as seis plataformas alvo distribuem binários de trabalhador pré-compilados e fixados por SHA-256, embutidos por meio de tags de build específicas de plataforma (`internal/embedded/embed_<goos>_<goarch>.go`):

| SO alvo     | Arquitetura alvo (`GOARCH`) | Binário embutido                | Tamanho do binário | Cadeia de build e proveniência                    |
| :---------- | :-------------------------- | :------------------------------ | -----------------: | :------------------------------------------------ |
| **Linux**   | `amd64`, `arm64`            | `zstd_server_linux_<arch>`      | ~770 - 880 KiB     | GCC `-O3`, Proveniência SLSA Build L3             |
| **macOS**   | `arm64`, `amd64`            | `zstd_server_darwin_<arch>`     | ~660 - 795 KiB     | AppleClang `-O3`, Proveniência SLSA Build L3      |
| **Windows** | `amd64`                     | `zstd_server_windows_amd64.exe` |          ~818 KiB  | MSVC `-O2`, Proveniência SLSA Build L3            |
| **Windows** | `arm64`                     | `zstd_server_windows_arm64.exe` |          ~551 KiB  | `zig cc -target aarch64-windows-gnu -O3`, SLSA L3 |

## Dentro do Caminho de Dados em Memória Compartilhada

Para manter uma vazão competitiva com o `cgo` em processo, o `gosuda.org/zstd` evita serialização por sockets e alocações de heap no caminho crítico.

### 1. Codificação de Slots com Alocação Zero (`zstd_ctx.go` e `c/zstd_hooks.c`)

Ao usar um `zstd.Ctx` reutilizável (ou quando as funções de nível de pacote `Compress` e `Decompress` emprestam um `Ctx` do pool interno de sessões), os dados fluem diretamente pelo slot `StandardLink` mapeado:

```text
Chamador Go (zstd.Ctx)                  Slot de Memória Compartilhada         Worker C11 (zstd_hooks.c)
----------------------                  -----------------------------         -------------------------
src []byte  ------------------------->  Payload do Slot de Requisição  ------>  ZSTD_compressCCtx(
                                                                                cctx, resp_slot, src, level)
dst []byte  <-------------------------  Payload do Slot de Resposta  <------  Grava o quadro comprimido diretamente
                                                                              no slot de resposta compartilhado
```

1. O cliente `hqq-bindgen` gerado copia `src` diretamente para o slot de requisição mapeado em memória e alterna a palavra de sequência do `SeqLock`.
2. O worker C lê `src` no próprio slot de requisição e passa o ponteiro de payload do slot de resposta diretamente como buffer de destino para `ZSTD_compressCCtx` ou `ZSTD_decompressDCtx`.
3. O cliente Go copia a fatia resultante do slot de resposta para a fatia `dst` fornecida pelo chamador. Em regime permanente, `ctx.CompressTo(dst, src, level)` e `ctx.DecompressInto(dst, src)` executam `0 allocs/op`.

Dicionários pré-processados (`CDict` e `DDict`) residem no worker C atrás de handles de 64 bits com verificação de geração (`handle CDict`, `handle DDict` em `zstd.idl`). Criar um dicionário uma única vez com `ctx.NewCDict(rawDict, level)` processa as tabelas do dicionário dentro do worker C e retorna um handle leve que pode ser reutilizado em milhares de chamadas sem reenviar os bytes do dicionário.

### 2. Amortização do Overhead de IPC com Enquadramento em Lote (`zstd_bulk.go`)

Para cargas grandes (16 KiB ou mais), o percurso de ida e volta de ~200 ns na memória compartilhada é desprezível em comparação com o tempo de CPU gasto dentro da `libzstd`. Para cargas que comprimem milhares de mensagens pequenas, de 100 bytes a 1 KiB, de uma só vez, porém, pagar até 200 ns por item se acumula.

O `gosuda.org/zstd` oferece `CompressBulk` e `DecompressBulk` em `BulkCtx` (`zstd_bulk.go`) para eliminar os percursos de ida e volta por item:

```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)
```

O `CompressBulk` empacota todo o lote `[][]byte` em um único quadro de requisição com prefixo de comprimento, na memória compartilhada. O worker C percorre cada item em C (`compress_bulk` em `c/zstd_hooks.c`), grava todos os quadros comprimidos de forma contígua no slot de resposta e devolve o lote inteiro em um único percurso de ida e volta pela memória compartilhada.

### 3. Sobrevivência à Terminação do Worker Sem Derrubar o Chamador

Como o `zstd_server` executa em um processo do sistema operacional separado, uma falha fatal dentro do worker C nunca derruba sua aplicação Go:

- Se o `zstd_server` for encerrado externamente (por exemplo, por `SIGKILL` ou por um evento de falta de memória, OOM), a camada de transporte HQQ detecta imediatamente o par interrompido e retorna um `*rt.TransportError` tipado da chamada `Compress` ou `Decompress` em andamento.
- As funções de nível de pacote (`zstd.Compress`, `zstd.Decompress`, `zstd.NewWriter`, `zstd.NewReader`) detectam automaticamente que o worker padrão compartilhado não existe mais, iniciam uma nova instância verificada de `zstd_server` na chamada seguinte e retomam a operação normal.

## Estrutura do Repositório

A base de código separa a fachada de substituição direta `DataDog/zstd` das primitivas de sessão e de lote de baixo nível do `hqq-bindgen`:

```text
gosuda.org/zstd
├── autolaunch.go / hashes.go  # API Compress/Decompress de substituição direta, pool de recuperação automática e hashes SHA-256
├── zstd_stream.go             # Writer/Reader de streaming (NewWriterLevelDict, NewReaderDict)
├── zstd_ctx.go                # Ctx, CDict e DDict de alocação zero sobre o hqq-bindgen
├── zstd_bulk.go               # Enquadramento de CompressBulk e DecompressBulk em um único percurso de ida e volta
├── zstd.go                    # Cliente Go hqq-bindgen gerado para o serviço IDL zstd
├── zstd.idl                   # Esquema IDL HQQ que define Context, CDict, DDict e métodos RPC
├── internal/embedded/         # Binários zstd_server com //go:embed e tags de plataforma (6 alvos OS/arquitetura)
└── c/                         # Implementação do servidor C11 (zstd_server.c, zstd_hooks.c, main.c)
```

## Primeiros Passos

Instale o `gosuda.org/zstd` com `go get` e use-o com `CGO_ENABLED=0`:

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

- **Repositório no GitHub**: [https://github.com/gosuda/hqq-zstd](https://github.com/gosuda/hqq-zstd)
- **Arquitetura e 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)
