Recursive AI

Getting started with OneArchive

All onearchive docs

Install the OneArchive and OneCompress previews for .NET 10, compress a buffer with Zstandard, then create, list, read and safely extract a .tar.zst archive, all in managed code with no native libraries.

Install

Both packages target net10.0, so you need the .NET 10 SDK. They are previews: this guide uses 0.7.0-preview.1 of both, and the API may change between previews.

Terminal
dotnet new console -o OneArchiveStart
cd OneArchiveStart
dotnet add package RecursiveAI.OneArchive --version 0.7.0-preview.1

RecursiveAI.OneArchive brings in RecursiveAI.OneCompress, the codecs. Use OneCompress on its own if you only need codecs and checksums.

Opt in to the preview APIs

Every codec and the archive layer carry [Experimental] until they meet their speed targets, so using one is a compiler error until you opt in. Each has its own diagnostic ID: Zstandard is ONECOMPRESS001, Deflate, zlib and gzip ONECOMPRESS003, the archive layer ONEARCHIVE001 (the full table is in the overview). Add the ones you use to the project file:

XML
<PropertyGroup>
  <NoWarn>$(NoWarn);ONECOMPRESS001;ONEARCHIVE001</NoWarn>
</PropertyGroup>

A first program

The program compresses a buffer, then archives an assets/ folder (three JSON level files) and notes.txt:

C#
using System.Text;
using RecursiveAI.OneArchive;
using RecursiveAI.OneCompress.Zstd;
 
// One codec call: compress and decompress a buffer.
byte[] data = Encoding.UTF8.GetBytes(string.Concat(Enumerable.Repeat("level=3 tiles=64x64 seed=42\n", 2000)));
byte[] packed = Zstd.Compress(data, new ZstdCompressionOptions { Level = 19 });
byte[] again = Zstd.Decompress(packed);
Console.WriteLine($"zstd: {data.Length:N0} -> {packed.Length:N0} bytes, round trip {again.AsSpan().SequenceEqual(data)}");
 
// Create an archive: format and compression come from the extension.
Archive.Create("backup.tar.zst").Add("assets/").Add("notes.txt", "docs/notes.txt").Save();
Console.WriteLine($"backup.tar.zst: {new FileInfo("backup.tar.zst").Length:N0} bytes");
 
// List it, and read one entry by name.
using (var archive = Archive.Open("backup.tar.zst"))
{
    foreach (var e in archive.Entries) Console.WriteLine($"  {e.Kind,-9} {e.Size,7:N0}  {e.Name}");
    using var s = archive.OpenEntry(archive.Find("docs/notes.txt")!);
    Console.WriteLine($"docs/notes.txt says: {new StreamReader(s).ReadToEnd().Trim()}");
}
 
// Extract it: safe by default, nothing is written outside "out".
Archive.Extract("backup.tar.zst", "out");
Console.WriteLine($"extracted {Directory.GetFiles("out", "*", SearchOption.AllDirectories).Length} files");
Text
zstd: 56,000 -> 48 bytes, round trip True
backup.tar.zst: 4,846 bytes
  Directory       0  assets
  Directory       0  assets/levels
  File       20,632  assets/levels/level1.json
  File       20,632  assets/levels/level2.json
  File       20,632  assets/levels/level3.json
  File           15  docs/notes.txt
docs/notes.txt says: Read me first.
extracted 4 files

What each part does

  • Zstd.Compress and Zstd.Decompress are one call each; Level runs from negative fast levels to 22. Every codec has the same shape: Lz4, Deflate, GZip, ZLib, BZip2, Xz, Brotli and the rest, plus stream types for data that doesn’t fit in memory.
  • Archive.Create(path) picks the format and compression from the extension: .tar, .tar.zst, .tar.gz, .tar.xz, .tar.lz4, .tar.lz, .zip, .cfb, or .gz, .zst, .lz4, .xz and .lz for one compressed file. Add(folder) adds a directory tree; Add(file, name) stores a file under another name.
  • Archive.Open finds the format from the bytes, not the name. Entries lists them, and OpenEntry reads one by random access when the source can seek.
  • Archive.Extract is safe by default: nothing is written outside the target, links can’t escape it, and limits on size, ratio, entry count and path length are on. See Extract untrusted archives.

What it reads and writes

Read and written: tar, zip (with WinZip AES), 7z (with AES-256), cpio, ar, ISO 9660 with Joliet and Rock Ridge, and Microsoft compound files. Read only: cab, and RAR 5 (listing, metadata and stored entries). The compressed bytes are identical on every platform, with and without hardware intrinsics, at any thread count and in WebAssembly.

Next