How to Use the Zstandard Simple Compression API in C

The Zstandard simple compression API enables one-shot compression and decompression through ZSTD_compress() and ZSTD_decompress(), which automatically manage internal contexts and require only source data, destination buffers, and a compression level.

The facebook/zstd library provides a straightforward interface for memory-to-memory compression tasks where data fits comfortably in RAM. These functions, declared in lib/zstd.h as part of the public ZSTDLIB_API, handle context creation and cleanup automatically, making them ideal for quick integration without explicit state management.

Core Functions of the Simple API

The simple compression API centers on two primary functions:

  • ZSTD_compress() – Compresses a complete source buffer into a destination buffer in a single call.
  • ZSTD_decompress() – Restores a compressed buffer back to its original uncompressed form.

Both functions return the number of bytes written (as size_t) on success. On failure, they return an error code that you must check using ZSTD_isError() and describe with ZSTD_getErrorName(). According to the source code in lib/zstd.c, these functions wrap the lower-level streaming engine by creating internal contexts, executing the operation, and freeing resources before returning.

Buffer Sizing and Error Handling

Before compression, you must allocate a destination buffer large enough to hold the worst-case compressed output. The helper ZSTD_compressBound(srcSize) calculates this maximum size based on the input length. Always use this value to prevent buffer overflows during ZSTD_compress() operations.

For decompression, you typically need to know the original data size. The function ZSTD_getFrameContentSize() inspects the compressed frame header and returns the uncompressed size without performing full decompression. If the original size is stored in the frame header, you can allocate an exact-sized buffer; otherwise, you must provide a buffer large enough for your use case.

Compressing Data with ZSTD_compress

The following example demonstrates basic compression using the simple API. It allocates a buffer using ZSTD_compressBound(), calls ZSTD_compress() with compression level 3, and validates the result.

/* compress_example.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "zstd.h"

int main(void) {
    const char *input = "Hello Zstandard! This text will be compressed.";
    size_t srcSize = strlen(input);
    int level = 3;                           // standard compression level

    /* Allocate a destination buffer large enough for worst-case size */
    size_t maxCompressedSize = ZSTD_compressBound(srcSize);
    void *compressed = malloc(maxCompressedSize);
    if (!compressed) { fprintf(stderr, "malloc failed\n"); return 1; }

    size_t cSize = ZSTD_compress(compressed, maxCompressedSize,
                                 input, srcSize, level);
    if (ZSTD_isError(cSize)) {
        fprintf(stderr, "compression error: %s\n",
                ZSTD_getErrorName(cSize));
        free(compressed);
        return 1;
    }

    printf("Compressed %zu bytes into %zu bytes (level %d)\n",
           srcSize, cSize, level);
    free(compressed);
    return 0;
}

Compile the example by linking against the zstd library:

gcc compress_example.c -lzstd -o compress_example

Decompressing Data with ZSTD_decompress

For decompression, first query the frame content size to allocate the correct buffer, then call ZSTD_decompress(). The following example assumes compressed and cSize variables exist from a previous compression operation.

/* decompress_example.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "zstd.h"

int main(void) {
    /* Assume `compressed` and `cSize` were produced by the previous example */
    extern void *compressed;   /* replace with actual pointer */
    extern size_t cSize;       /* replace with actual size   */

    /* First, retrieve the original size (optional but useful) */
    unsigned long long origSize = ZSTD_getFrameContentSize(compressed, cSize);
    if (origSize == ZSTD_CONTENTSIZE_UNKNOWN) {
        fprintf(stderr, "Original size unknown – use a large buffer\n");
        origSize = 1024;  /* fallback */
    }

    void *decompressed = malloc((size_t)origSize);
    if (!decompressed) { fprintf(stderr, "malloc failed\n"); return 1; }

    size_t dSize = ZSTD_decompress(decompressed, (size_t)origSize,
                                   compressed, cSize);
    if (ZSTD_isError(dSize)) {
        fprintf(stderr, "decompression error: %s\n",
                ZSTD_getErrorName(dSize));
        free(decompressed);
        return 1;
    }

    printf("Decompressed %zu bytes: \"%.*s\"\n", dSize,
           (int)dSize, (char *)decompressed);
    free(decompressed);
    return 0;
}

Configuring Compression Levels

The simple API accepts an integer compression level ranging from -ZSTD_minCLevel() up to ZSTD_maxCLevel(). The default level is 3, which balances speed and compression ratio effectively. Negative levels prioritize compression speed over ratio, suitable for real-time applications, while higher values increase compression density at the cost of CPU time and latency.

Implementation Details and Thread Safety

The implementation in lib/zstd.c creates a compression or decompression context internally, executes the operation using the streaming engine, and destroys the context before returning. This self-contained design eliminates the need for explicit ZSTD_CCtx or ZSTD_DCtx management in client code.

The simple API functions are thread-safe as implemented in the source code. You may call ZSTD_compress() or ZSTD_decompress() concurrently from multiple threads, provided each invocation uses distinct source and destination buffers. No locks are required because the functions do not share mutable state between calls.

Summary

  • Single-call compression: Use ZSTD_compress() with a destination buffer sized via ZSTD_compressBound().
  • Single-call decompression: Use ZSTD_decompress() after optionally querying the original size with ZSTD_getFrameContentSize().
  • Error checking: Always validate return values using ZSTD_isError() and describe failures with ZSTD_getErrorName().
  • No context required: The simple API manages internal contexts automatically as implemented in lib/zstd.c.
  • Thread-safe: Concurrent calls are safe when using separate input and output buffers per thread.

Frequently Asked Questions

What is the difference between the simple API and the streaming API in zstd?

The simple API performs one-shot compression and decompression using ZSTD_compress() and ZSTD_decompress(), automatically managing internal contexts declared in lib/zstd.h. The streaming API requires explicit context objects (ZSTD_CCtx or ZSTD_DCtx) and processes data in chunks through ZSTD_compressStream() or ZSTD_decompressStream(), making it suitable for large files or network streams where memory is constrained.

How do I determine the correct size for the decompression buffer?

Call ZSTD_getFrameContentSize() on the compressed frame to extract the original uncompressed size from the header. If this returns ZSTD_CONTENTSIZE_UNKNOWN, you must either allocate a buffer large enough for your application's maximum expected size or switch to the streaming API to process data incrementally without requiring the full size upfront.

What compression level should I use for general-purpose data?

Use level 3, which is the default and provides an optimal balance between compression speed and ratio according to the lib/zstd.h definitions. Use negative levels (down to -ZSTD_minCLevel()) for speed-critical applications like real-time logging, or levels above 3 (up to ZSTD_maxCLevel()) for archival storage where compression density matters more than throughput.

Are the simple compression functions thread-safe?

Yes. The functions are thread-safe as long as each thread operates on distinct source and destination buffers. The implementation in lib/zstd.c creates and destroys internal contexts per call, so no shared mutable state exists between invocations, allowing safe concurrent usage without synchronization primitives.

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 →