# How to Get the Decompressed Size of a Zstd Frame

> Learn how to get the decompressed size of a Zstd frame using ZSTD_getFrameContentSize. Extract original uncompressed size from the header without full decompression.

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

---

**Call `ZSTD_getFrameContentSize()` to extract the original uncompressed size directly from the Zstandard frame header without performing full decompression.**

The Zstandard (zstd) compression library embeds the decompressed content size within the frame header whenever the size is known at compression time. According to the facebook/zstd source code, the `ZSTD_getFrameContentSize()` helper parses this metadata in constant time, enabling efficient buffer pre-allocation before calling `ZSTD_decompress()`.

## ZSTD_getFrameContentSize() Function Signature

The primary API for retrieving frame size information is declared in **[[`lib/zstd.h`](https://github.com/facebook/zstd/blob/main/lib/zstd.h)](https://github.com/facebook/zstd/blob/dev/lib/zstd.h)** (lines 179-190):

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

```

**Key parameters:**
- **`src`**: Pointer to the beginning of a ZSTD encoded frame.
- **`srcSize`**: Size of the buffer pointed to by `src`. Must be at least the header size (minimum 12 bytes for the maximum header).

The function performs strict header parsing only—it never touches the compressed payload—making it an **O(1)** operation regardless of frame size.

## Return Value Semantics

`ZSTD_getFrameContentSize()` returns an `unsigned long long` with three distinct outcome categories, as documented in the header:

| Return Value | Macro Definition | Meaning |
|-------------|------------------|---------|
| `>= 0` | N/A | Exact decompressed size in bytes (may be 0 for empty frames). |
| `0ULL - 1` | `ZSTD_CONTENTSIZE_UNKNOWN` | Header does not contain a size (typical for streaming compression). |
| `0ULL - 2` | `ZSTD_CONTENTSIZE_ERROR` | Invalid frame header, corrupt data, or `srcSize` too small. |

**Critical handling logic:**
- **`ZSTD_CONTENTSIZE_ERROR`**: Indicates malformed input or insufficient buffer size. Abort decompression.
- **`ZSTD_CONTENTSIZE_UNKNOWN`**: The frame was produced using streaming APIs (`ZSTD_compressStream()`) without storing the final size. Fall back to streaming decompression or supply a safe upper bound.
- **Valid size**: Allocate a destination buffer of exactly the returned size and proceed with `ZSTD_decompress()`.

## Deprecated API Differences

The older helper `ZSTD_getDecompressedSize()` (declared in **[[`lib/zstd.h`](https://github.com/facebook/zstd/blob/main/lib/zstd.h)](https://github.com/facebook/zstd/blob/dev/lib/zstd.h)** lines 202-214) is now deprecated. It merges empty frames, unknown sizes, and errors into a single `0` return value, making error handling ambiguous. New code should exclusively use `ZSTD_getFrameContentSize()` for explicit sentinel checking.

## Practical Implementation Example

The following example demonstrates the recommended workflow: probing the frame header, validating return codes, allocating exact memory, and decompressing.

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

int decompress_frame(const void *src, size_t srcSize)
{
    /* 1. Probe the frame header */
    unsigned long long decSize = ZSTD_getFrameContentSize(src, srcSize);
    
    if (decSize == ZSTD_CONTENTSIZE_ERROR) {
        fprintf(stderr, "Invalid ZSTD frame header or insufficient input.\n");
        return 1;
    }
    
    if (decSize == ZSTD_CONTENTSIZE_UNKNOWN) {
        fprintf(stderr, "Decompressed size not stored. Use ZSTD_decompressStream().\n");
        return 1;
    }
    
    /* 2. Pre-allocate exact output buffer */
    void *dst = malloc((size_t)decSize);
    if (!dst) {
        perror("malloc");
        return 1;
    }
    
    /* 3. Decompress in single pass */
    size_t ret = ZSTD_decompress(dst, (size_t)decSize, src, srcSize);
    if (ZSTD_isError(ret)) {
        fprintf(stderr, "Decompression failed: %s\n", ZSTD_getErrorName(ret));
        free(dst);
        return 1;
    }
    
    printf("Successfully decompressed %zu bytes (expected %llu).\n", ret, decSize);
    
    /* ... use dst ... */
    
    free(dst);
    return 0;
}

```

### Streaming Fallback for Unknown Sizes

When `ZSTD_CONTENTSIZE_UNKNOWN` is returned, implement streaming decompression using `ZSTD_decompressStream()`:

```c
void decompress_streaming(const void *src, size_t srcSize)
{
    ZSTD_DCtx *dctx = ZSTD_createDCtx();
    if (!dctx) {
        fprintf(stderr, "Failed to create decompression context.\n");
        return;
    }
    
    ZSTD_inBuffer input = { src, srcSize, 0 };
    size_t outBuffSize = ZSTD_DStreamOutSize(); /* 128KB default */
    void *outBuff = malloc(outBuffSize);
    if (!outBuff) {
        ZSTD_freeDCtx(dctx);
        return;
    }
    
    while (input.pos < input.size) {
        ZSTD_outBuffer output = { outBuff, outBuffSize, 0 };
        size_t ret = ZSTD_decompressStream(dctx, &output, &input);
        if (ZSTD_isError(ret)) {
            fprintf(stderr, "Stream error: %s\n", ZSTD_getErrorName(ret));
            break;
        }
        /* Process output.dst[0..output.pos] here */
    }
    
    free(outBuff);
    ZSTD_freeDCtx(dctx);
}

```

## Key Source Files in facebook/zstd

Understanding the implementation context requires examining these specific files:

- **[[`lib/zstd.h`](https://github.com/facebook/zstd/blob/main/lib/zstd.h)](https://github.com/facebook/zstd/blob/dev/lib/zstd.h)**: Declares `ZSTD_getFrameContentSize()` and documents the sentinel value semantics.
- **[[`lib/decompress/zstd_decompress.c`](https://github.com/facebook/zstd/blob/main/lib/decompress/zstd_decompress.c)](https://github.com/facebook/zstd/blob/dev/lib/decompress/zstd_decompress.c)**: Contains the low-level decompression implementation where header sizes are validated and used for buffer allocation.
- **[[`contrib/seekable_format/zstdseek_decompress.c`](https://github.com/facebook/zstd/blob/main/contrib/seekable_format/zstdseek_decompress.c)](https://github.com/facebook/zstd/blob/dev/contrib/seekable_format/zstdseek_decompress.c)**: Real-world usage demonstrating how seekable frame formats utilize the size extraction helper to navigate compressed archives.
- **[[`doc/educational_decoder/zstd_decompress.h`](https://github.com/facebook/zstd/blob/main/doc/educational_decoder/zstd_decompress.h)](https://github.com/facebook/zstd/blob/dev/doc/educational_decoder/zstd_decompress.h)**: Educational reference implementation showing header parsing logic.

## Summary

- **`ZSTD_getFrameContentSize()`** is the modern, non-deprecated API to get the decompressed size of a zstd frame from its header.
- The function returns `unsigned long long` values interpretable via three states: valid size, `ZSTD_CONTENTSIZE_UNKNOWN`, or `ZSTD_CONTENTSIZE_ERROR`.
- Always provide at least 12 bytes of the frame header to ensure the function can parse the maximum header size.
- The operation parses only header metadata (O(1)), never touching the compressed payload.
- For unknown sizes, fall back to the streaming API (`ZSTD_decompressStream()`) to avoid unbounded memory allocation.
- Avoid the deprecated `ZSTD_getDecompressedSize()` due to its ambiguous `0` return value.

## Frequently Asked Questions

### What is the minimum buffer size required to call ZSTD_getFrameContentSize?

You must provide at least **12 bytes** of the compressed frame. This represents the maximum possible header size for a ZSTD frame. Providing fewer bytes will result in `ZSTD_CONTENTSIZE_ERROR` because the function cannot parse the complete size field.

### How do I handle frames where the decompressed size is unknown?

When `ZSTD_getFrameContentSize()` returns `ZSTD_CONTENTSIZE_UNKNOWN`, the frame was compressed using streaming APIs without storing the final size in the header. You must use `ZSTD_decompressStream()` with a circular output buffer of reasonable size (e.g., `ZSTD_DStreamOutSize()`) to decompress incrementally rather than pre-allocating a single large buffer.

### What is the difference between ZSTD_getFrameContentSize and ZSTD_getDecompressedSize?

`ZSTD_getDecompressedSize()` is deprecated and ambiguous—it returns `0` for empty frames, unknown sizes, and errors alike. `ZSTD_getFrameContentSize()` distinguishes these cases using explicit sentinel values (`ZSTD_CONTENTSIZE_UNKNOWN` and `ZSTD_CONTENTSIZE_ERROR`), allowing proper error handling and distinct logic for streaming scenarios.

### Can ZSTD_getFrameContentSize return a size larger than available memory?

Yes. Because the function returns `unsigned long long` (64-bit), the declared decompressed size may exceed the addressable memory of a 32-bit process or available system RAM. Always check the returned value against system limits before calling `malloc()`, and prefer the streaming API if the size exceeds safe allocation thresholds.