HeatshrinkDotNet 1.0.0
dotnet add package HeatshrinkDotNet --version 1.0.0
NuGet\Install-Package HeatshrinkDotNet -Version 1.0.0
<PackageReference Include="HeatshrinkDotNet" Version="1.0.0" />
<PackageVersion Include="HeatshrinkDotNet" Version="1.0.0" />
<PackageReference Include="HeatshrinkDotNet" />
paket add HeatshrinkDotNet --version 1.0.0
#r "nuget: HeatshrinkDotNet, 1.0.0"
#:package HeatshrinkDotNet@1.0.0
#addin nuget:?package=HeatshrinkDotNet&version=1.0.0
#tool nuget:?package=HeatshrinkDotNet&version=1.0.0
HeatshrinkDotNet
A C# port of heatshrink, the LZSS-based compression library designed for embedded and real-time systems with very small memory footprints. The port keeps the original's streaming sink/poll API and produces byte-identical output, so data is interchangeable with the C implementation (given matching window/lookahead parameters).
The library targets netstandard2.0 and net10.0. Sink and Poll accept
ReadOnlySpan<byte> / Span<byte> as well as arrays, so slices, stackalloc'd,
and pooled buffers can be used without copying (the netstandard2.0 target
uses the System.Memory package for span support).
Configuration
Both the encoder and decoder are configured with two parameters that must match between compression and decompression:
- windowBits (4–15): the sliding window is
2^windowBitsbytes. Larger windows find more repetition and compress better, at the cost of memory. - lookaheadBits (3 to
windowBits - 1): the maximum back-reference length is2^lookaheadBitsbytes.
The decoder additionally takes an input buffer size (any positive value; it only affects how much compressed data can be sunk at once).
Usage
The API is a streaming state machine: Sink feeds input in, Poll drains output,
and Finish signals the end of the stream. Buffers can be as small as one byte.
using HeatshrinkDotNet;
static byte[] Compress(byte[] input)
{
var encoder = new HeatshrinkEncoder(windowBits: 10, lookaheadBits: 5);
var output = new System.IO.MemoryStream();
var chunk = new byte[256];
var sunk = 0;
var finished = false;
while (!finished)
{
if (sunk < input.Length)
{
encoder.Sink(input, sunk, input.Length - sunk, out var count);
sunk += count;
}
EncoderPollResult pres;
do
{
pres = encoder.Poll(chunk, out var polled);
output.Write(chunk, 0, polled);
} while (pres == EncoderPollResult.More);
if (sunk == input.Length)
finished = encoder.Finish() == EncoderFinishResult.Done;
}
return output.ToArray();
}
static byte[] Decompress(byte[] compressed)
{
var decoder = new HeatshrinkDecoder(inputBufferSize: 256, windowBits: 10, lookaheadBits: 5);
var output = new System.IO.MemoryStream();
var chunk = new byte[256];
var sunk = 0;
var finished = false;
while (!finished)
{
if (sunk < compressed.Length)
{
decoder.Sink(compressed, sunk, compressed.Length - sunk, out var count);
sunk += count;
}
DecoderPollResult pres;
do
{
pres = decoder.Poll(chunk, out var polled);
output.Write(chunk, 0, polled);
} while (pres == DecoderPollResult.More);
if (sunk == compressed.Length)
finished = decoder.Finish() == DecoderFinishResult.Done;
}
return output.ToArray();
}
Reset() returns an encoder or decoder to its initial state so the instance can be
reused. Instances are not thread-safe; use one per stream.
Both constructors accept an optional Microsoft.Extensions.Logging.ILogger as the
last parameter. Diagnostic output (mirroring the C library's LOG statements) is
emitted at Trace level, with allocation messages at Debug; when no logger is
passed, or the level is disabled, logging costs a single guard check per site.
Tests
The test suite is a port of upstream heatshrink's, using xUnit v3 on .NET 10:
dotnet test
This includes two deterministic pseudo-random round-trip sweeps
(FuzzingSingleByteSizes / FuzzingMultiByteSizes) covering thousands of
size/seed/buffer combinations, and a CsCheck port of upstream's theft-based
property tests (round-trip integrity, the 9/8 worst-case size bound, and
encoder/decoder liveness under arbitrary input).
A separate project, HeatshrinkDotNet.InteropTest, compiles the vendored
upstream C sources into the reference heatshrink CLI and verifies the two
implementations produce byte-identical, mutually decodable streams — both
on fixed cases and via a CsCheck differential-fuzzing property with the C
implementation as the oracle. The C build needs a POSIX cc; on Windows
these tests skip themselves.
One known, tested divergence: at windowBits 15 the C encoder never finds
a match at all — both its search paths (indexed and brute-force) use
int16_t position arithmetic, and the input region starts at buffer
offset 32768, so every position reads as negative and the search loops
exit immediately. The C implementation therefore emits pure literal output
(a 9/8 expansion) at its own maximum window setting, while this port
compresses normally. The streams remain valid and decode interchangeably
in both directions; outputs are byte-identical for windowBits up to 14,
which works exactly because 32768 buffer positions max out int16_t at
32767. Known upstream as
atomicobject/heatshrink#86
(partial fix proposed in
#88, which does not
cover the brute-force path). The same 16-bit arithmetic also makes the C
encoder hang once 32K of input is buffered at windowBits 15
(#55); this port
does not share that, and the C decoder — which is unaffected — decodes
this port's windowBits 15 streams byte-exactly (covered by the interop
tests).
Fuzzing
HeatshrinkDotNet.Fuzz provides coverage-guided fuzzing (SharpFuzz +
libFuzzer) with two harnesses: decoder feeds arbitrary bytes to the
decoder, which must never throw, hang, or over-produce; roundtrip
checks that any payload compresses within the 9/8 bound and decompresses
back to itself. The first input bytes select the window/lookahead
configuration, so the fuzzer explores those too.
HeatshrinkDotNet.Fuzz/fuzz.sh decoder 300
The pipeline runs natively on Linux; on macOS the script runs it in a
Docker container (libFuzzer's coverage transport is ELF-only). The corpus
persists in HeatshrinkDotNet.Fuzz/corpus/, and crashing inputs can be
replayed with dotnet run -- replay <harness> <file>.
License
This port is licensed under the LGPL-3.0 (see LICENSE).
The original heatshrink C library is copyright (c) 2013-2015 Scott Vokes and licensed under the ISC license; its notice is preserved in LICENSE.heatshrink.
The original C# port was written by Shindo in 2019.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.11)
- System.Memory (>= 4.6.3)
-
net10.0
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.11)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on HeatshrinkDotNet:
| Package | Downloads |
|---|---|
|
libbgcode.NET
A .NET reader and writer for Prusa's binary G-code (bgcode) container format: file header, block enumeration, per-block compression (deflate, heatshrink), MeatPack G-code encoding and decoding, CRC-32 checksums, the JSON metadata PrusaSlicer 3 writes, and whole-file conversion to and from ASCII G-code. Implemented from the published format specification; the reader is hardened against untrusted input, the writer cannot produce a file the reference implementation refuses. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 266 | 8/31/2026 |