# How to Use Decompression Contexts (DCtx) in Zstandard

> Learn how to use Zstandard decompression contexts DCtx to efficiently decode multiple ZSTD frames without reallocating memory, boosting throughput in frequent decoding tasks.

- Repository: [Meta/zstd](https://github.com/facebook/zstd)
- Tags: how-to-guide
- Published: 2026-09-09

---

**Zstandard’s `ZSTD_DCtx` API lets you allocate a reusable decompression context once and decode multiple ZSTD frames without repeated memory allocations, significantly improving throughput in high-frequency scenarios.**

The **Zstandard** compression library from `facebook/zstd` provides both simple one-shot functions and advanced persistent contexts for stateful decompression. Unlike the basic `ZSTD_decompress()` function which allocates internal state on every call, the **decompression context (`DCtx`)** architecture lets you retain the decoder's sliding window and entropy tables across multiple operations. This article explores the complete lifecycle of a `DCtx` based on the actual source implementation in [`lib/zstd.h`](https://github.com/facebook/zstd/blob/main/lib/zstd.h) and the internal decompression pipeline.

## Creating a Decompression Context

The first step in using **decompression contexts in zstd** is allocating the persistent state object. In [`lib/zstd.h`](https://github.com/facebook/zstd/blob/main/lib/zstd.h), the function `ZSTD_createDCtx()` declares the allocator:

```c
ZSTD_DCtx* ZSTD_createDCtx(void);

```

This function returns a pointer to a `ZSTD_DCtx` structure that holds the entire decoder state, including the sliding window buffer and entropy decoding tables. If allocation fails, it returns `NULL`.

Always pair creation with `ZSTD_freeDCtx()` (defined at line 305 in [`lib/zstd.h`](https://github.com/facebook/zstd/blob/main/lib/zstd.h)) to release memory:

```c
ZSTD_freeDCtx(dctx);

```

## Configuring Context Parameters

Before decompression begins, you can tune the **DCtx** behavior using `ZSTD_DCtx_setParameter()`. These parameters are *sticky*—they persist across decompression operations until explicitly changed or reset.

A common use case is limiting the maximum window size for memory-constrained environments:

```c
ZSTD_DCtx_setParameter(dctx, ZSTD_d_windowLogMax, 23);  /* 2^23 = 8 MiB */

```

This call, referencing the `ZSTD_d_windowLogMax` enum from [`lib/zstd.h`](https://github.com/facebook/zstd/blob/main/lib/zstd.h) line 86, prevents the decoder from allocating excessive memory when processing malicious or malformed input. Parameters must be set **before** the first `ZSTD_decompressDCtx()` call for a given frame.

## Decompressing Frames with ZSTD_decompressDCtx

The core decompression function signature lives at line 312 in [`lib/zstd.h`](https://github.com/facebook/zstd/blob/main/lib/zstd.h):

```c
size_t ZSTD_decompressDCtx(ZSTD_DCtx* dctx,
                           void* dst, size_t dstCapacity,
                     const void* src, size_t srcSize);

```

This operates exactly like `ZSTD_decompress()` but reuses the internal buffers already allocated in the **DCtx**:

```c
size_t result = ZSTD_decompressDCtx(dctx, dst, dstCapacity, src, srcSize);
if (ZSTD_isError(result)) {
    fprintf(stderr, "Error: %s\n", ZSTD_getErrorName(result));
}

```

**Thread safety note:** Each thread must own its own `DCtx`. Reusing the same context across multiple threads without synchronization causes race conditions in the internal state maintained in [`lib/decompress/zstd_decompress_internal.h`](https://github.com/facebook/zstd/blob/main/lib/decompress/zstd_decompress_internal.h).

## Resetting the Context Between Frames

When reusing a **DCtx** for multiple independent frames, you must clear leftover state to prevent cross-frame contamination. The `ZSTD_DCtx_reset()` function (line 94 in [`lib/zstd.h`](https://github.com/facebook/zstd/blob/main/lib/zstd.h)) provides several reset modes:

- **`ZSTD_reset_session_only`**: Clears streaming state but preserves parameters (useful when decoding many frames with identical settings)
- **`ZSTD_reset_session_and_parameters`**: Clears both state and sticky parameters (useful when switching between different compression profiles)

```c
/* After finishing one frame, prepare for the next */
ZSTD_DCtx_reset(dctx, ZSTD_reset_session_only);

```

According to the source in [`lib/decompress/zstd_decompress_block.h`](https://github.com/facebook/zstd/blob/main/lib/decompress/zstd_decompress_block.h), failing to reset causes the decoder to interpret the new frame's data using the previous frame's entropy tables, leading to corruption errors.

## Performance Benefits of DCtx Reuse

The **zstd decompression context** architecture provides three key advantages over one-shot decompression:

- **Memory efficiency:** Internal buffers (defined in [`lib/decompress/zstd_decompress_internal.h`](https://github.com/facebook/zstd/blob/main/lib/decompress/zstd_decompress_internal.h)) are allocated once during `ZSTD_createDCtx()` and reused, eliminating malloc/free overhead per frame.
- **Cache locality:** The decoder state remains hot in CPU cache across multiple calls, improving throughput in tight loops.
- **Dictionary support:** Advanced usage can attach a pre-trained dictionary via `ZSTD_DCtx_loadDictionary()` (declared in [`lib/decompress/zstd_ddict.h`](https://github.com/facebook/zstd/blob/main/lib/decompress/zstd_ddict.h)) and reuse it across many frames.

The test suite in [`tests/zstreamtest.c`](https://github.com/facebook/zstd/blob/main/tests/zstreamtest.c) demonstrates these patterns in production scenarios, showing significant latency reductions when processing thousands of small frames.

## Complete Example: Reusing a DCtx

This pattern from the Zstandard source demonstrates efficient **decompression context** usage for batch processing:

```c
#include <stdio.h>
#include "zstd.h"

int main(void) {
    /* Allocate once */
    ZSTD_DCtx *dctx = ZSTD_createDCtx();
    if (!dctx) { return 1; }

    /* Optional: limit memory usage */
    ZSTD_DCtx_setParameter(dctx, ZSTD_d_windowLogMax, 23);

    for (int i = 0; i < NUM_FRAMES; ++i) {
        void *src = get_compressed_data(i);
        size_t srcSize = get_compressed_size(i);
        void *dst = malloc(OUTPUT_SIZE);
        size_t dstCapacity = OUTPUT_SIZE;

        /* Decompress reusing the same context */
        size_t res = ZSTD_decompressDCtx(dctx, dst, dstCapacity, src, srcSize);
        
        if (ZSTD_isError(res)) {
            fprintf(stderr, "Error: %s\n", ZSTD_getErrorName(res));
            ZSTD_DCtx_reset(dctx, ZSTD_reset_session_only);
            free(dst);
            continue;
        }

        process_output(dst, res);
        free(dst);

        /* Prepare for next iteration */
        ZSTD_DCtx_reset(dctx, ZSTD_reset_session_only);
    }

    ZSTD_freeDCtx(dctx);
    return 0;
}

```

## Summary

- **Create** a context with `ZSTD_createDCtx()` from [`lib/zstd.h`](https://github.com/facebook/zstd/blob/main/lib/zstd.h) to allocate persistent decoder state.
- **Configure** parameters like `ZSTD_d_windowLogMax` using `ZSTD_DCtx_setParameter()` before decompression begins.
- **Decompress** frames using `ZSTD_decompressDCtx()` instead of the one-shot function to avoid repeated allocations.
- **Reset** the context between frames with `ZSTD_DCtx_reset()` to clear state while optionally preserving parameters.
- **Free** the context with `ZSTD_freeDCtx()` when processing completes to release internal buffers defined in [`lib/decompress/zstd_decompress_internal.h`](https://github.com/facebook/zstd/blob/main/lib/decompress/zstd_decompress_internal.h).

## Frequently Asked Questions

### What is the difference between `ZSTD_decompress()` and `ZSTD_decompressDCtx()`?

`ZSTD_decompress()` is a convenience wrapper that creates an internal temporary **DCtx**, performs the decompression, and immediately destroys it. `ZSTD_decompressDCtx()` requires you to provide and manage the context explicitly, eliminating allocation overhead when processing multiple frames. According to the `facebook/zstd` source, the one-shot function simply calls `ZSTD_createDCtx()` and `ZSTD_decompressDCtx()` internally.

### When should I reset a DCtx versus creating a new one?

Reset the **DCtx** when decoding sequential independent frames to reuse allocated memory. Use `ZSTD_reset_session_only` to preserve parameters like window limits, or `ZSTD_reset_session_and_parameters` to return to defaults. Only create a new context if the current one encounters an unrecoverable error or if you need different initial parameters that cannot be achieved via reset.

### How do I limit memory usage when using a decompression context?

Call `ZSTD_DCtx_setParameter(dctx, ZSTD_d_windowLogMax, value)` before the first decompression, where `value` is the base-2 logarithm of your desired maximum window size (e.g., 23 for 8 MiB). This parameter defined in [`lib/zstd.h`](https://github.com/facebook/zstd/blob/main/lib/zstd.h) restricts the sliding window buffer size allocated within the internal structures in [`lib/decompress/zstd_decompress_internal.h`](https://github.com/facebook/zstd/blob/main/lib/decompress/zstd_decompress_internal.h).

### Is `ZSTD_DCtx` thread-safe?

No. Each thread must maintain its own **decompression context** because the `DCtx` structure contains mutable streaming state, history buffers, and entropy tables. Sharing a `DCtx` across threads without synchronization causes data races. However, creating one `DCtx` per thread and reusing it for that thread's entire lifetime is the recommended high-performance pattern.