Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
root = true

[*]
charset = utf-8
end_of_line = crlf
indent_style = tab
insert_final_newline = true
trim_trailing_whitespace = true

[*.{csproj,config}]
ij_xml_space_inside_empty_tag = true
indent_style = space
indent_size = 2

[*.{slnx,yml}]
indent_style = space
indent_size = 2
79 changes: 79 additions & 0 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
name: Build and test

on:
push:
pull_request:
paths:
- '**.cs'
- '**.csproj'
workflow_call:

jobs:
build-and-test:
name: Build ${{ matrix.name }}
runs-on: ${{ matrix.os }}
timeout-minutes: 5

permissions:
contents: read
attestations: read

strategy:
fail-fast: true
matrix:
include:
- { os: ubuntu-24.04, name: linux-x64, ext: so, arch: 'x64' }
- { os: ubuntu-24.04-arm, name: linux-arm64, ext: so, arch: 'arm64' }
- { os: windows-2025, name: win-x64, ext: dll, arch: 'x64' }
- { os: windows-2025, name: win-x86, ext: dll, arch: 'x86' }
- { os: windows-11-arm, name: win-arm64, ext: dll, arch: 'arm64' }
- { os: macos-15, name: osx-arm64, ext: dylib, arch: 'arm64' }
- { os: macos-15-intel, name: osx-x64, ext: dylib, arch: 'x64' }

steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Setup .NET SDK
if: ${{ matrix.name != 'win-x86' }}
uses: actions/setup-dotnet@baa11fbfe1d6520db94683bd5c7a3818018e4309 # v5.1.0
with:
dotnet-version: '10.0.x'

- name: Install dotnet x86
if: ${{ matrix.name == 'win-x86' }}
run: |
iwr -Uri https://dot.net/v1/dotnet-install.ps1 -OutFile dotnet-install.ps1 -UseBasicParsing
./dotnet-install.ps1 -Channel 10.0 -Architecture x86 -InstallDir "C:/Program Files (x86)/dotnet"

- name: Show info
run: dotnet --info

- name: Install dependencies
run: |
dotnet restore ./ZstdNet/ZstdNet.csproj --runtime ${{ matrix.name }}
dotnet restore ./ZstdNet.Tests/ZstdNet.Tests.csproj --runtime ${{ matrix.name }}

- name: Download native libs
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: 22015842187 # https://github.com/skbkontur/ZstdNet/actions/runs/22015842187
merge-multiple: true
name: ${{ matrix.name }}
path: ./ZstdNet/

- name: Check attestation
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_FORCE_TTY: '100%'
run: |
gh attestation verify ./ZstdNet/runtimes/${{ matrix.name }}/native/libzstd.${{ matrix.ext }} --repo skbkontur/ZstdNet

- name: Build
run: |
dotnet build ./ZstdNet/ZstdNet.csproj --no-restore --runtime ${{ matrix.name }}
dotnet build ./ZstdNet.Tests/ZstdNet.Tests.csproj --no-restore --runtime ${{ matrix.name }}

- name: Test
run: dotnet test ./ZstdNet.Tests/ZstdNet.Tests.csproj --no-build --runtime ${{ matrix.name }}
74 changes: 74 additions & 0 deletions .github/workflows/build-native.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: Build Native

on:
workflow_dispatch:

jobs:
build:
name: Build ${{ matrix.name }}
runs-on: ${{ matrix.os }}

permissions:
id-token: write
contents: read
attestations: write

strategy:
fail-fast: true
matrix:
include:
- { os: ubuntu-24.04, name: linux-x64, ext: so, args: '' }
- { os: ubuntu-24.04, name: linux-arm64, ext: so, args: '-DCMAKE_SYSTEM_PROCESSOR=aarch64 -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc' }
- { os: windows-2025, name: win-x64, ext: dll, args: '-DZSTD_USE_STATIC_RUNTIME=ON' }
- { os: windows-2025, name: win-x86, ext: dll, args: '-DZSTD_USE_STATIC_RUNTIME=ON -A Win32' }
- { os: windows-2025, name: win-arm64, ext: dll, args: '-DZSTD_USE_STATIC_RUNTIME=ON -A ARM64' }
- { os: macos-15, name: osx-arm64, ext: dylib, args: '' }
- { os: macos-15-intel, name: osx-x64, ext: dylib, args: '' }

steps:
- name: Checkout zstd sources
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
repository: facebook/zstd
ref: f8745da6ff1ad1e7bab384bd1f9d742439278e99 # v1.5.7

- name: Install GCC ARM64
if: ${{ matrix.name == 'linux-arm64' }}
run: sudo apt-get update && sudo apt-get install gcc-aarch64-linux-gnu -y

- name: Configure CMake
run: >
cmake -S build/cmake -B output
${{ matrix.args }}
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_COMPILE_WARNING_AS_ERROR=ON
-DBUILD_SHARED_LIBS=ON
-DZSTD_BUILD_TESTS=OFF
-DZSTD_BUILD_SHARED=ON
-DZSTD_BUILD_STATIC=OFF
-DZSTD_BUILD_PROGRAMS=OFF
-DZSTD_LIB_DEPRECATED=OFF
-DZSTD_LEGACY_SUPPORT=0

- name: Build using CMake
run: cmake --build output --config Release

- name: Rename shared lib file (Windows)
if: ${{ startsWith(runner.os, 'win') }}
run: mv output/lib/Release/zstd.dll output/lib/libzstd.dll

- name: Move shared lib to runtimes
run: |
mkdir -p artifacts/runtimes/${{ matrix.name }}/native/
cp output/lib/libzstd.${{ matrix.ext }} artifacts/runtimes/${{ matrix.name }}/native/

- name: Provenance attestation
uses: actions/attest-build-provenance@96278af6caaf10aea03fd8d33a09a777ca52d62f # v3.2.0
with:
subject-path: artifacts/runtimes/*/native/libzstd.*

- name: Upload artifacts
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: ${{ matrix.name }}
path: artifacts/
63 changes: 63 additions & 0 deletions .github/workflows/pack-and-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: Pack and publish

on:
workflow_dispatch:

jobs:
pack-and-publish:
runs-on: ubuntu-24.04
environment:
name: production
timeout-minutes: 5

permissions:
id-token: write
contents: read
attestations: write

steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Setup .NET SDK
uses: actions/setup-dotnet@baa11fbfe1d6520db94683bd5c7a3818018e4309 # v5.1.0
with:
dotnet-version: '10.0.103'

- name: Specify .NET version
run: dotnet new globaljson --sdk-version 10.0.103 --roll-forward disable

- name: Show .NET info
run: dotnet --info

- name: Download native libs
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: 22015842187 # https://github.com/skbkontur/ZstdNet/actions/runs/22015842187
merge-multiple: true
path: ./ZstdNet/

- name: Check native libs attestation
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_FORCE_TTY: '100%'
run: |
find ./ZstdNet/runtimes -path "*/native/libzstd.*" -type f -print -exec gh attestation verify {} --repo skbkontur/ZstdNet ';'

- name: Pack
run: dotnet pack --configuration Release --output ./

- name: Attest NuGet package provenance
uses: actions/attest-build-provenance@96278af6caaf10aea03fd8d33a09a777ca52d62f # v3.2.0
with:
subject-path: './ZstdNet.*.nupkg'

- name: Upload artifacts
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: package
path: './ZstdNet.*.nupkg'

- name: Publish Nuget package
run: dotnet nuget push ZstdNet.*.nupkg --api-key "${{ secrets.NUGET_API_KEY }}" --source https://api.nuget.org/v3/index.json --skip-duplicate
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ BSD License

For ZstdNet software

Copyright (c) 2016-present, SKB Kontur. All rights reserved.
Copyright (c) 2016-2026, SKB Kontur. All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Expand Down
43 changes: 24 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
ZstdNet
=======

[![Build and test](https://github.com/skbkontur/ZstdNet/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/skbkontur/ZstdNet/actions/workflows/build-and-test.yml)
[![NuGet](https://img.shields.io/nuget/v/ZstdNet.svg)](https://www.nuget.org/packages/ZstdNet/)
[![NuGet Downloads](https://img.shields.io/nuget/dt/ZstdNet.svg)](https://www.nuget.org/packages/ZstdNet)

**ZstdNet** is a wrapper of **Zstd** native library for .NET languages. It has the following features:
**ZstdNet** is a wrapper of **Zstd** native library for .NET languages targeting `netstadard2.{0|1}`.
ZstdNet NuGet package includes pre-built native shared libraries for [various platforms](.github/workflows/build-native.yml), including `win`, `linux`, `osx`.

* Compression and decompression of byte arrays
The package relies on the [dotnet runtime identifier resolution mechanism](https://learn.microsoft.com/en-us/dotnet/core/rid-catalog).
For .NET Framework, the package provides a [targets](ZstdNet/build/ZstdNet.targets) fallback, which requires an explicit selection of the platform — `x86`, `x64` or `ARM64`).

If you need to resolve native dependency at runtime, you can use the `NativeLibrary.SetDllImportResolver` (see an [example](ZstdNet.Tests/NativeResolver.cs)).
And for .NET Framework — `SetDllDirectory` from `kernel32.dll`.

Provenance attestation is enabled for all artifacts in this repository including native libs, see page [Attestations](https://github.com/skbkontur/ZstdNet/attestations).

### Features

* Compression and decompression of byte arrays and `Span<byte>`
* Streaming compression and decompression
* Generation of Dictionaries from a collection of samples
* Advanced parameters support
* Generation of dictionaries

Take a look on a library reference or unit tests to explore its behavior in different situations.

Zstd
----

**Zstd**, short for Zstandard, is a fast lossless compression algorithm, which
provides both good compression ratio _and_ speed for your standard compression
needs. "Standard" translates into everyday situations which neither look for
highest possible ratio (which LZMA and ZPAQ cover) nor extreme speeds (which
LZ4 covers). Zstandard is licensed under [BSD 3-Clause License](ZstdNet/build/LICENSE).
**Zstandard**, or **zstd** as short version, is a fast lossless compression algorithm,
targeting real-time compression scenarios at zlib-level and better compression ratios.

**Zstd** is initially developed by Yann Collet and the source is available at:
https://github.com/facebook/zstd
Expand All @@ -33,19 +44,13 @@ http://fastcompression.blogspot.ru/2016/02/compressing-small-data.html
Reference
---------

### Requirements

*ZstdNet* requires *libzstd* >= v1.4.0. Both 32-bit and 64-bit versions are supported.
The corresponding DLLs are included in this repository cross-compiled using
`(i686|x86_64)-w64-mingw32-gcc -DZSTD_MULTITHREAD -DZSTD_LEGACY_SUPPORT=0 -pthread -s`.
Note that `ZSTD_LEGACY_SUPPORT=0` means "do not support legacy formats" to minimize the binary size.

### Exceptions

The wrapper throws `ZstdException` in case of malformed data or an error inside *libzstd*.
If the given destination buffer is too small, `ZstdException` with `ZSTD_error_dstSize_tooSmall`
error code is thrown away.
Check [zstd_errors.h](https://github.com/facebook/zstd/blob/v1.4.5/lib/common/zstd_errors.h#L52) for more info.

Check [zstd_errors.h](https://github.com/facebook/zstd/blob/v1.5.7/lib/zstd_errors.h#L60) for more info.

### Compressor class

Expand Down Expand Up @@ -148,7 +153,7 @@ performance and memory overhead.
- `IReadOnlyDictionary<ZSTD_cParameter, int> advancedParams` &mdash; advanced API provides a way
to set specific parameters during compression. For example, it allows you to compress with multiple threads,
enable long distance matching mode and more.
Check [zstd.h](https://github.com/facebook/zstd/blob/v1.4.5/lib/zstd.h#L265) for additional info.
Check [zstd.h](https://github.com/facebook/zstd/blob/v1.5.7/lib/zstd.h#L349) for additional info.

Specified options will be exposed in read-only fields.

Expand Down Expand Up @@ -266,7 +271,7 @@ performance and memory overhead.
Default is `null` (no dictionary).
- `IReadOnlyDictionary<ZSTD_dParameter, int> advancedParams` &mdash; advanced decompression API
that allows you to set parameters like maximum memory usage.
Check [zstd.h](https://github.com/facebook/zstd/blob/v1.4.5/lib/zstd.h#L513) for additional info.
Check [zstd.h](https://github.com/facebook/zstd/blob/v1.5.7/lib/zstd.h#L640) for additional info.

Specified options will be exposed in read-only fields.

Expand Down Expand Up @@ -295,6 +300,6 @@ performance and memory overhead.
Wrapper Authors
---------------

Copyright (c) 2016-present [SKB Kontur](https://kontur.ru/eng/about)
Copyright (c) 2016-2026 [SKB Kontur](https://kontur.ru/eng/about)

*ZstdNet* is distributed under [BSD 3-Clause License](LICENSE).
21 changes: 13 additions & 8 deletions ZstdNet.Benchmarks/CompressionBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;

Expand All @@ -22,6 +23,16 @@ public class CompressionOverheadBenchmarks
private readonly Compressor CompressorAdvanced = new Compressor(new CompressionOptions(null, new Dictionary<ZSTD_cParameter, int>()));
private readonly Decompressor Decompressor = new Decompressor();

// Native dependencies are not added to the deps.json file via ProjectReference
// https://github.com/dotnet/sdk/issues/10575
static CompressionOverheadBenchmarks()
{
var ext = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "dll" : RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "dylib" : "so";
NativeLibrary.SetDllImportResolver(typeof(DictBuilder).Assembly, (name, a, b) => name == "libzstd"
? NativeLibrary.Load(Path.Combine(AppContext.BaseDirectory, "runtimes", RuntimeInformation.RuntimeIdentifier, "native", $"libzstd.{ext}"))
: IntPtr.Zero);
}

[GlobalSetup]
public void GlobalSetup()
{
Expand Down Expand Up @@ -55,10 +66,7 @@ public void CompressStream(int zstdBufferSize, int copyBufferSize)
[Arguments(7, 13)]
public async Task CompressStreamAsync(int zstdBufferSize, int copyBufferSize)
{
#if !NET48
await
#endif
using var compressionStream = new CompressionStream(Stream.Null, CompressionOptions.Default, zstdBufferSize);
await using var compressionStream = new CompressionStream(Stream.Null, CompressionOptions.Default, zstdBufferSize);
await new MemoryStream(Data).CopyToAsync(compressionStream, copyBufferSize);
}

Expand All @@ -74,10 +82,7 @@ public void DecompressStream(int zstdBufferSize, int copyBufferSize)
[Arguments(7, 13)]
public async Task DecompressStreamAsync(int zstdBufferSize, int copyBufferSize)
{
#if !NET48
await
#endif
using var decompressionStream = new DecompressionStream(new MemoryStream(CompressedStreamData), zstdBufferSize);
await using var decompressionStream = new DecompressionStream(new MemoryStream(CompressedStreamData), zstdBufferSize);
await decompressionStream.CopyToAsync(Stream.Null, copyBufferSize);
}
}
Expand Down
Loading