using ZstdSharp; namespace StellaOps.Concelier.Federation.Compression; /// /// ZStandard compression utilities for federation bundles. /// public static class ZstdCompression { /// Default compression level (balanced speed/ratio). public const int DefaultLevel = 3; /// Minimum compression level. public const int MinLevel = 1; /// Maximum compression level. public const int MaxLevel = 19; /// /// Create a compression stream wrapping the output. /// /// Target stream for compressed data. /// Compression level (1-19, default 3). /// Stream that compresses data written to it. public static CompressionStream CreateCompressionStream(Stream output, int level = DefaultLevel) { ValidateLevel(level); return new CompressionStream(output, level); } /// /// Create a decompression stream wrapping the input. /// /// Source stream of compressed data. /// Stream that decompresses data read from it. public static DecompressionStream CreateDecompressionStream(Stream input) { return new DecompressionStream(input); } /// /// Compress data in memory. /// /// Uncompressed data. /// Compression level (1-19, default 3). /// Compressed data. public static byte[] Compress(ReadOnlySpan data, int level = DefaultLevel) { ValidateLevel(level); using var compressor = new Compressor(level); return compressor.Wrap(data).ToArray(); } /// /// Decompress data in memory. /// /// Compressed data. /// Decompressed data. public static byte[] Decompress(ReadOnlySpan compressed) { using var decompressor = new Decompressor(); return decompressor.Unwrap(compressed).ToArray(); } /// /// Compress a file to another file. /// public static async Task CompressFileAsync( string inputPath, string outputPath, int level = DefaultLevel, CancellationToken ct = default) { ValidateLevel(level); await using var input = File.OpenRead(inputPath); await using var output = File.Create(outputPath); await using var compressor = CreateCompressionStream(output, level); await input.CopyToAsync(compressor, ct); } /// /// Decompress a file to another file. /// public static async Task DecompressFileAsync( string inputPath, string outputPath, CancellationToken ct = default) { await using var input = File.OpenRead(inputPath); await using var decompressor = CreateDecompressionStream(input); await using var output = File.Create(outputPath); await decompressor.CopyToAsync(output, ct); } /// /// Decompress from one stream to another. /// public static async Task DecompressAsync( Stream input, Stream output, CancellationToken ct = default) { await using var decompressor = CreateDecompressionStream(input); await decompressor.CopyToAsync(output, ct); } /// /// Compress from one stream to another. /// public static async Task CompressAsync( Stream input, Stream output, int level = DefaultLevel, CancellationToken ct = default) { ValidateLevel(level); await using var compressor = CreateCompressionStream(output, level); await input.CopyToAsync(compressor, ct); } private static void ValidateLevel(int level) { if (level < MinLevel || level > MaxLevel) { throw new ArgumentOutOfRangeException( nameof(level), level, $"Compression level must be between {MinLevel} and {MaxLevel}"); } } }