How to Use the Basic Zstd Decompression API: A Complete Guide

The basic zstd decompression API provides a one-shot memory-to-memory interface centered on ZSTD_decompress(), requiring callers to pre-allocate destination buffers and verify results using ZSTD_isError() and ZSTD_getErrorName().

The facebook/zstd library offers a minimal, high-performance one-shot decompression interface for applications that process complete frames in memory. This basic zstd decompression API gives you full control over memory allocation while eliminating the overhead of persistent decompression contexts. Understanding the proper sequence of calls—from querying frame metadata to handling error codes—ensures robust integration into performance-critical C and C++ applications.

Core API Functions in lib/zstd.h

The public API surface for basic decompression resides in lib/zstd.h. Three functions form the complete workflow for decompressing a frame with known or unknown size.

Querying Frame Size with ZSTD_getFrameContentSize

Before allocating memory, determine the decompressed size by reading the frame header. The function declared at lines 779-796 in lib/zstd.h extracts the original content size:

unsigned long long ZSTD_getFrameContentSize(const void* src, size_t srcSize);

This returns:

  • The exact decompressed size (if stored in the frame)
  • ZSTD_CONTENTSIZE_UNKNOWN when the size was not stored (e.g., streaming compression without pledged size)
  • ZSTD_CONTENTSIZE_ERROR if the frame header is corrupted

Decompressing with ZSTD_decompress

The one-shot function declared at lines 164-174 performs the actual decompression:

size_t ZSTD_decompress(void* dst, size_t dstCapacity,
                       const void* src, size_t compressedSize);

This function creates a temporary decompression context internally, parses the frame header, and expands the compressed payload into dst. The dstCapacity must be greater than or equal to the original size; otherwise, it returns ZSTD_errorDstSizeTooSmall.

Error Detection with ZSTD_isError

All Zstandard functions return size_t values that may represent errors. Lines 59-62 in lib/zstd.h provide the error checking interface:

static unsigned int ZSTD_isError(size_t code);
const char* ZSTD_getErrorName(size_t code);

Use ZSTD_isError() to test return values and ZSTD_getErrorName() to obtain human-readable error strings.

Step-by-Step Implementation

Follow this deterministic workflow to decompress frames safely.

  1. Call ZSTD_getFrameContentSize() to read the frame header and determine the required buffer size.
  2. Allocate a destination buffer of at least the returned size (or use a safe upper bound if the size is unknown).
  3. Invoke ZSTD_decompress() with the source buffer, destination buffer, and capacity.
  4. Verify the return value with ZSTD_isError(); on success, the value equals the number of bytes written.

This design intentionally places memory management responsibility on the caller, enabling deterministic behavior on constrained systems and avoiding hidden allocations.

Practical Code Examples

Decompressing When Size Is Known

When the frame contains the original size metadata (the common case for one-shot compression):

#include <stdio.h>
#include <stdlib.h>
#include "zstd.h"

int main(void) {
    /* src points to a ZSTD-compressed frame and srcSize is its exact length */
    const void *src = /* … */;
    size_t srcSize = /* … */;

    /* 1. Query the original size stored in the frame header */
    unsigned long long originalSize = ZSTD_getFrameContentSize(src, srcSize);
    if (originalSize == ZSTD_CONTENTSIZE_ERROR) {
        fprintf(stderr, "Invalid ZSTD frame header\n");
        return 1;
    }
    if (originalSize == ZSTD_CONTENTSIZE_UNKNOWN) {
        fprintf(stderr, "Original size not stored – need streaming API\n");
        return 1;
    }

    /* 2. Allocate destination buffer */
    void *dst = malloc((size_t)originalSize);
    if (!dst) { perror("malloc"); return 1; }

    /* 3. Decompress */
    size_t ret = ZSTD_decompress(dst, (size_t)originalSize, src, srcSize);
    if (ZSTD_isError(ret)) {
        fprintf(stderr, "Decompression error: %s\n", ZSTD_getErrorName(ret));
        free(dst);
        return 1;
    }

    /* ret equals originalSize on success */
    printf("Decompressed %zu bytes\n", ret);

    free(dst);
    return 0;
}

Handling Unknown Frame Sizes

When ZSTD_getFrameContentSize() returns ZSTD_CONTENTSIZE_UNKNOWN, allocate a safe upper bound or switch to streaming:

#define MAX_OUT_SIZE (16 * 1024 * 1024)

void *dst = malloc(MAX_OUT_SIZE);
size_t ret = ZSTD_decompress(dst, MAX_OUT_SIZE, src, srcSize);
if (ZSTD_isError(ret)) {
    /* Checks for ZSTD_errorDstSizeTooSmall or ZSTD_errorCorruptFrame */
    fprintf(stderr, "Error: %s\n", ZSTD_getErrorName(ret));
} else {
    printf("Successfully decompressed %zu bytes\n", ret);
}

This pattern prevents buffer overruns at the cost of potentially over-allocating memory.

Minimal Error Handling Wrapper

For applications requiring strict failure handling, wrap the decompression call:

size_t zstd_decompress_or_die(void *dst, size_t dstCap,
                               const void *src, size_t srcSize) {
    size_t r = ZSTD_decompress(dst, dstCap, src, srcSize);
    if (ZSTD_isError(r)) {
        fprintf(stderr, "ZSTD_decompress failed: %s\n",
                ZSTD_getErrorName(r));
        abort();
    }
    return r;
}

Key Source Files

Understanding the implementation locations helps with debugging and advanced customization:

Summary

  • The basic zstd decompression API centers on ZSTD_decompress() in lib/zstd.h, providing stateless, one-shot decompression without persistent context allocation.
  • Always query the frame size first using ZSTD_getFrameContentSize() to allocate appropriately sized buffers; handle ZSTD_CONTENTSIZE_UNKNOWN by allocating a safe upper bound or switching to the streaming API.
  • Verify all return values using ZSTD_isError() and obtain diagnostic messages via ZSTD_getErrorName().
  • The API explicitly does not allocate memory, giving you full control over buffer management and enabling deterministic performance characteristics.

Frequently Asked Questions

What is the difference between ZSTD_decompress and the streaming API?

ZSTD_decompress() is a one-shot function that decompresses entire frames in a single call, internally creating a temporary decompression context and then discarding it. The streaming API uses a persistent ZSTD_DCtx object to process data incrementally, which is necessary for frames larger than available memory or for continuous data streams. Use the basic API for discrete, memory-resident frames; use streaming for large or unbounded data.

How do I handle frames where the original size is unknown?

When ZSTD_getFrameContentSize() returns ZSTD_CONTENTSIZE_UNKNOWN, the compressor did not store the decompressed size in the frame header. You must either allocate a buffer using a known safe upper bound (accepting that ZSTD_decompress() may return ZSTD_errorDstSizeTooSmall if insufficient) or switch to the streaming decompression API. This commonly occurs when compressing with the streaming API without providing a pledged output size.

What error codes can ZSTD_decompress return?

According to lib/zstd_errors.h, common errors include ZSTD_errorDstSizeTooSmall (destination buffer smaller than original size), ZSTD_errorCorruptFrame (invalid input data), and memory allocation failures. All errors are detectable via ZSTD_isError() and convertible to descriptive strings via ZSTD_getErrorName(). On success, the function returns the number of bytes written, which always equals the original decompressed size.

Is the basic zstd decompression API thread-safe?

Yes, ZSTD_decompress() is thread-safe because it creates a temporary internal context for each call and does not modify global state. However, you must ensure that the source and destination buffers are not accessed by other threads during the operation. For scenarios requiring persistent dictionaries or custom memory allocators, use the explicit context API with ZSTD_DCtx objects instead of the basic one-shot interface.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →