---
title: "LZ4 compression and decompression in C#"
url: https://recursiveai.org/onearchive/cookbook/lz4-compress-decompress/
description: "Compress and decompress an LZ4 frame in C#, save it to a file, and restore the original bytes. Includes compression options and truncated-input handling."
version: "1.2.0"
---

# LZ4 compression and decompression in C#

**OneArchive** is a free, open-source archive library for .NET from Recursive AI. MIT or Apache-2.0, no native dependencies.

[NuGet](https://www.nuget.org/packages/RecursiveAI.OneCompress) · [GitHub](https://github.com/recursive-ai-oss/onearchive-csharp) · [Docs](https://recursiveai.org/onearchive/docs/) · [Download example (.zip)](https://recursiveai.org/onearchive/cookbook/lz4-compress-decompress/lz4-compress-decompress.zip)

LZ4 is a fast, lossless compression format. The codec ships in the `RecursiveAI.OneCompress` package (part of OneArchive). A frame is a complete file wrapper: compressed bytes with a header and end marker.

```shell
dotnet add package RecursiveAI.OneCompress
```

Requires [.NET 10](https://dotnet.microsoft.com/download/dotnet/10.0) (net10.0); .NET 9 and earlier aren’t supported.

```csharp
using RecursiveAI.OneCompress.Lz4;

byte[] frame = Lz4.Compress("hello LZ4"u8);
File.WriteAllBytes("message.lz4", frame);
byte[] restored = Lz4.Decompress(
    File.ReadAllBytes("message.lz4"));
Console.WriteLine(
    System.Text.Encoding.UTF8.GetString(restored));
```

Writes `message.lz4` and prints:

```text
hello LZ4
```

Unzip [the example](https://recursiveai.org/onearchive/cookbook/lz4-compress-decompress/lz4-compress-decompress.zip), then `dotnet run`.

## The full program

No input files are needed. The program builds 100 copies of `hello LZ4` followed by a newline, compresses those UTF-8 bytes, reads the saved frame, and writes the decoded bytes to `restored.txt`.

```csharp
using System.Text;
using RecursiveAI.OneCompress.Lz4;

byte[] source = Encoding.UTF8.GetBytes(
    string.Concat(Enumerable.Repeat("hello LZ4\n", 100)));
byte[] frame = Lz4.Compress(source);
File.WriteAllBytes("message.lz4", frame);
byte[] restored = Lz4.Decompress(
    File.ReadAllBytes("message.lz4"));
File.WriteAllBytes("restored.txt", restored);
Console.WriteLine($"Input: {source.Length} bytes");
Console.WriteLine($"Frame: {frame.Length} bytes");
Console.WriteLine($"Restored: {restored.Length} bytes");
bool equal = source.AsSpan().SequenceEqual(restored);
Console.WriteLine($"Equal: {equal}");
```

```text
Input: 1000 bytes
Frame: 50 bytes
Restored: 1000 bytes
Equal: True
```

A truncated frame throws `CompressionFormatException`; because the program writes `restored.txt` only after decompression returns, that failure does not write a partial restored file.

## Variations

**Choose high compression and block checksums.** This complete program uses level 9. Content checksums are already enabled by default; the extra option adds a checksum for each block.

```csharp
using RecursiveAI.OneCompress.Lz4;

byte[] source = System.Text.Encoding.UTF8.GetBytes(
    string.Concat(Enumerable.Repeat("hello LZ4\n", 100)));
var options = new Lz4CompressionOptions
{
    Level = 9,
    BlockChecksum = true
};
byte[] frame = Lz4.Compress(source, options);
byte[] restored = Lz4.Decompress(frame);
Console.WriteLine(source.AsSpan().SequenceEqual(restored));
```

```text
True
```

**Catch truncated input.** This deliberately removes the final byte of a frame.

```csharp
using RecursiveAI.OneCompress;
using RecursiveAI.OneCompress.Lz4;

byte[] frame = Lz4.Compress("hello LZ4"u8);
try
{
    Lz4.Decompress(frame.AsSpan(0, frame.Length - 1));
}
catch (CompressionFormatException error)
{
    Console.WriteLine(error.GetType().Name);
}
```

```text
CompressionFormatException
```

## Related recipes

- [Create a ZIP archive](https://recursiveai.org/onearchive/cookbook/create-zip/)
- [Read and write gzip files](https://recursiveai.org/onearchive/cookbook/gzip-interop/)
- [Use Zstandard dictionaries](https://recursiveai.org/onearchive/cookbook/zstd-dictionaries/)

## The short answer

For LZ4 compression in C#, install `RecursiveAI.OneCompress`, import `RecursiveAI.OneCompress.Lz4`, and call `Lz4.Compress` with your bytes. It returns a complete frame. Save those bytes directly as an `.lz4` file. Read the file and pass its bytes to `Lz4.Decompress` to recover the original content.

The compression codecs are part of OneArchive but ship in the separate OneCompress package. This task does not require the archive package or an archive entry API. The example uses the released .NET 10 package and operates on byte arrays, not filenames inside a container.

## What is LZ4 compression?

LZ4 is lossless compression: decoding restores the input bytes rather than an approximation. Its [format documentation](https://github.com/lz4/lz4#documentation) distinguishes the raw block format from the frame format used to organize blocks into files and streams. That distinction determines which API you should call.

In OneCompress, `Lz4.Compress` and `Lz4.Decompress` are the frame pair. `Lz4Block.Compress` and `Lz4Block.Decompress` handle raw blocks instead. A raw block needs its compressed length and an output bound supplied outside the block. A frame carries a descriptor, block boundaries and an end marker. It can also carry content size and checksums. See the [decoder documentation](https://recursiveai.org/onearchive/docs/lz4-decoder/) for the accepted frame variants.

Do not send a raw block to the frame decoder or strip off a frame header and assume the remainder is one raw block. A frame may contain several blocks, block-size fields, checksums and an end marker. Match the API to the format your producer actually writes, not just a filename extension.

## How much does LZ4 compress?

There is no fixed ratio. The complete program’s repeated text becomes a 50-byte frame from 1,000 input bytes; that is one small, deliberately repetitive example, not an estimate for photos, encrypted bytes or your application’s records. Compare the frame length with the original byte length on representative inputs. Include the framing overhead when deciding whether to store the compressed representation.

The [encoder documentation](https://recursiveai.org/onearchive/docs/lz4-encoder/) defines level 1 as the default and levels 2 through 12 as high compression. The level controls how the encoder searches for matches; it does not change the need for a matching frame decoder. The level-9 variation shows where that option goes without assuming it will shrink every input. A small or already-compressed payload can cost more bytes to frame than it saves.

## Is LZ4 compression good?

It fits a byte round-trip when both ends agree on the LZ4 format. For a portable `.lz4` payload, choose frames rather than an application-specific raw-block wrapper. For several named files, an archive is a separate requirement: this example stores one byte sequence, not a directory, filenames or timestamps.

The one-call APIs allocate byte arrays for their complete results. OneCompress also exposes `Lz4Stream` for stream-based use and `Lz4Encoder` and `Lz4Decoder` for incremental buffers. Choose those when retaining both complete arrays is not appropriate. The [decoder’s options](https://recursiveai.org/onearchive/docs/lz4-decoder/) default to a 1 GiB maximum total output and checksum verification enabled; a limit is not a promise that an allocation of that size is appropriate for your application.

For malformed input, catch the format exception as shown above. Keep the write after successful decoding if the destination must not receive partial output. The example’s final equality check compares the restored bytes with the original input, independently of how the text happens to display in a console.
