using ZLibDotNet; namespace BlockCompressor; /// /// Provides a wrapper around ZLib compression functionality with simplified interface. /// internal class ZStreamWrapper { /// /// Gets the result code of the last compression operation. /// public int LastResult { get; private set; } /// /// Gets the total number of input bytes processed. /// public uint TotalIn { get; private set; } /// /// Gets the total number of output bytes generated. /// public uint TotalOut { get; private set; } /// /// Gets the Adler-32 checksum of the data. /// public uint Adler { get; private set; } // Buffers for input and output private byte[] inputBuffer; private byte[] outputBuffer; // ZLib instance private readonly ZLib zlib = new ZLib(); /// /// Compresses the specified input bytes using DEFLATE algorithm. /// /// The byte array to compress. /// The compression level to use, defaults to best compression. /// A new byte array containing the compressed data. /// Thrown when ZLib initialization fails. public byte[] Deflate(byte[] input, int compressionLevel = 6) { // Estimation of the maximum possible size for compressed data uint compressedSizeEstimate = (uint)input.Length + ((uint)input.Length >> 12) + ((uint)input.Length >> 14) + ((uint)input.Length >> 25) + 13; byte[] output = new byte[compressedSizeEstimate]; // We use a local method that works with ref ZStream DeflateInternal(input, output, compressionLevel); // Trim the array to the actual compressed size byte[] result = new byte[TotalOut]; Array.Copy(output, result, result.Length); return result; } /// /// Finalizes a deflate stream by flushing any pending output. /// /// A byte array containing any final compressed data, or an empty array if none. public byte[] DeflateFinish() { byte[] emptyInput = Array.Empty(); byte[] finalOutput = new byte[32]; // Enough for the end marker DeflateFinishInternal(emptyInput, finalOutput); if (TotalOut > 0) { byte[] result = new byte[TotalOut]; Array.Copy(finalOutput, result, result.Length); return result; } return Array.Empty(); } /// /// Performs the actual DEFLATE compression operation by interacting with ZStream. /// /// The input data to compress. /// The buffer to receive compressed output. /// The compression level to use. /// Thrown when ZLib initialization fails. private void DeflateInternal(byte[] input, byte[] output, int compressionLevel) { inputBuffer = input; outputBuffer = output; // Here we safely use ref struct ZStream zStream = new ZStream { Input = inputBuffer, Output = outputBuffer }; // Initialize if necessary if (LastResult == 0) { LastResult = zlib.DeflateInit( ref zStream, compressionLevel, ZLib.Z_DEFLATED, -15, // Negative for raw deflate (no header/trailer) 8, // MAX_MEM_LEVEL = 9 ZLib.Z_DEFAULT_STRATEGY); if (LastResult != ZLib.Z_OK) throw new InvalidOperationException($"Could not initialize deflate: {LastResult}"); } // Execute compression LastResult = zlib.Deflate(ref zStream, ZLib.Z_FULL_FLUSH); // Save important state TotalIn = zStream.TotalIn; TotalOut = zStream.TotalOut; Adler = zStream.Adler; } /// /// Finalizes the deflate stream and cleans up ZLib resources. /// /// The final input (typically empty). /// The buffer to receive any final compressed data. private void DeflateFinishInternal(byte[] input, byte[] output) { inputBuffer = input; outputBuffer = output; ZStream zStream = new ZStream { Input = inputBuffer, Output = outputBuffer }; // Finalize compression LastResult = zlib.Deflate(ref zStream, ZLib.Z_FINISH); // Save state TotalIn = zStream.TotalIn; TotalOut = zStream.TotalOut; Adler = zStream.Adler; // Close the stream zlib.DeflateEnd(ref zStream); } }