diff --git a/src/FastLZMA2Net/CompressStream.cs b/src/FastLZMA2Net/CompressStream.cs index fdcd7c2..d7d9662 100644 --- a/src/FastLZMA2Net/CompressStream.cs +++ b/src/FastLZMA2Net/CompressStream.cs @@ -2,7 +2,10 @@ namespace FastLZMA2Net { - public class CompressStream : Stream, IDisposable + /// + /// Streaming Fast LZMA2 compress + /// + public class CompressStream : Stream { private readonly int bufferSize; private byte[] outputBufferArray; @@ -15,6 +18,10 @@ public class CompressStream : Stream, IDisposable public override bool CanRead => _innerStream != null && _innerStream.CanRead; public override bool CanWrite => false; public override bool CanSeek => false; + + /// + /// Can't determine decompressed data size + /// public override long Length => throw new NotSupportedException(); public override long Position @@ -22,41 +29,65 @@ public override long Position get => throw new NotSupportedException(); set => throw new NotSupportedException(); } - + /// + /// Compress Level [1..10] + /// public int CompressLevel { get => (int)GetParameter(FL2Parameter.CompressionLevel); set => SetParameter(FL2Parameter.CompressionLevel, (nuint)value); } + /// + /// Levels 1..10 Setting to 1 switches to an alternate cLevel table. + /// public int HighCompressLevel { get => (int)GetParameter(FL2Parameter.HighCompression); set => SetParameter(FL2Parameter.HighCompression, (nuint)value); } + /// + /// Dictionary size with FL2.DictSizeMin & FL2.DictSizeMax + /// public int DictionarySize { get => (int)GetParameter(FL2Parameter.DictionarySize); set => SetParameter(FL2Parameter.DictionarySize, (nuint)value); } - + /// + /// Match finder will resolve string matches up to this length. + /// If a longer match exists further back in the input, it will not be found. + /// Default = 42 + /// public int SearchDepth { get => (int)GetParameter(FL2Parameter.SearchDepth); set => SetParameter(FL2Parameter.SearchDepth, (nuint)value); } - + /// + /// Only useful for strategies >= opt. + /// Length of match considered "good enough" to stop search. + /// Larger values make compression stronger and slower. + /// Default = 48 + /// public int FastLength { get => (int)GetParameter(FL2Parameter.FastLength); set => SetParameter(FL2Parameter.FastLength, (nuint)value); } - public CompressStream(Stream innerStream, uint nbThreads = 0, int outBufferSize = 64 * 1024 * 1024) + /// + /// Initialize streaming compress context + /// + /// compressed data store + /// thread use, auto = 0 + /// Native interop buffer size, default = 64M + /// + public CompressStream(Stream dstStream, uint nbThreads = 0, int outBufferSize = 64 * 1024 * 1024) { bufferSize = outBufferSize; - _innerStream = innerStream; + _innerStream = dstStream; _context = NativeMethods.FL2_createCStreamMt(nbThreads, 1); var code = NativeMethods.FL2_initCStream(_context, 0); if (FL2Exception.IsError(code)) @@ -75,26 +106,54 @@ public CompressStream(Stream innerStream, uint nbThreads = 0, int outBufferSize }; } + /// + /// Append raw data to streaming, won't close compress stream + /// + /// Extra data + /// Start index in buffer + /// How many bytes to append public void Append(byte[] buffer, int offset, int count) { Append(buffer.AsSpan(offset, count)); } + /// + /// Append raw data to streaming, won't close compress stream + /// + /// Extra data public void Append(ReadOnlySpan buffer) { CompressCore(buffer, true); } + /// + /// Start compression and finish stream. + /// + /// Raw data + /// Start index in buffer + /// How many bytes to append public override void Write(byte[] buffer, int offset, int count) { Write(buffer.AsSpan(offset, count)); } + /// + /// Start compression and finish stream. + /// + /// Raw data public override void Write(ReadOnlySpan buffer) { CompressCore(buffer, false); } + /// + /// Start compression and finish stream asynchronized. + /// + /// Raw data + /// Start index in buffer + /// How many bytes to append + /// + /// public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { Memory bufferMemory = buffer.AsMemory(offset, count); @@ -102,6 +161,12 @@ public override async Task WriteAsync(byte[] buffer, int offset, int count, Canc return; } + /// + /// Start compression and finish stream asynchronized. + /// + /// Raw data + /// + /// public override async ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) { await new ValueTask(CompressCore(buffer.Span, false, cancellationToken)).ConfigureAwait(false); @@ -121,7 +186,7 @@ private unsafe int CompressCore(ReadOnlySpan buffer, bool Appending, Cance }; nuint code; - //push source data& receive part of compressed data + //push source data & receive part of compressed data do { outBuffer.pos = 0; @@ -150,12 +215,9 @@ private unsafe int CompressCore(ReadOnlySpan buffer, bool Appending, Cance code = NativeMethods.FL2_copyCStreamOutput(_context, ref outBuffer); if (FL2Exception.IsError(code)) { - if (FL2Exception.IsError(code)) + if (FL2Exception.GetErrorCode(code) != FL2ErrorCode.Buffer) { - if (FL2Exception.GetErrorCode(code) != FL2ErrorCode.Buffer) - { - throw new FL2Exception(code); - } + throw new FL2Exception(code); } } _innerStream.Write(outputBufferArray, 0, (int)outBuffer.pos); @@ -166,7 +228,7 @@ private unsafe int CompressCore(ReadOnlySpan buffer, bool Appending, Cance return 0; } - // receive all remaining compressed data + // receive all remaining compressed data for safety do { outBuffer.pos = 0; @@ -174,12 +236,9 @@ private unsafe int CompressCore(ReadOnlySpan buffer, bool Appending, Cance code = NativeMethods.FL2_flushStream(_context, ref outBuffer); if (FL2Exception.IsError(code)) { - if (FL2Exception.IsError(code)) + if (FL2Exception.GetErrorCode(code) != FL2ErrorCode.Buffer) { - if (FL2Exception.GetErrorCode(code) != FL2ErrorCode.Buffer) - { - throw new FL2Exception(code); - } + throw new FL2Exception(code); } } _innerStream.Write(outputBufferArray, 0, (int)outBuffer.pos); @@ -190,7 +249,7 @@ private unsafe int CompressCore(ReadOnlySpan buffer, bool Appending, Cance return 0; } - //Write compress checksum + //Write compress checksum if not appending mode if (!Appending) { code = NativeMethods.FL2_endStream(_context, ref outBuffer); @@ -216,24 +275,77 @@ private unsafe int CompressCore(ReadOnlySpan buffer, bool Appending, Cance return 0; } + /// + /// Not support + /// + /// + /// + /// + /// public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); + /// + /// Not support + /// + /// + /// public override void SetLength(long value) => throw new NotSupportedException(); + /// + /// Not support + /// + /// + /// + /// + /// + /// public override int Read(byte[] buffer, int offset, int count) => throw new NotSupportedException(); + /// + /// Not support + /// + /// + /// + /// public override int Read(Span buffer) => throw new NotSupportedException(); + /// + /// Not support + /// + /// + /// public override int ReadByte() => throw new NotSupportedException(); - - public override void Close() => Dispose(true); - + /// + /// Not support + /// + /// + /// + /// + /// + /// + /// public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken = default) - => throw new NotSupportedException(); - + => throw new NotSupportedException(); + + /// + /// Not support + /// + /// + /// + /// + /// public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) => throw new NotSupportedException(); + /// + /// Close streaming progress + /// + public override void Close() => Dispose(true); + + /// + /// Write Checksum in the end. finish compress progress. + /// + /// public override void Flush() { var code = NativeMethods.FL2_endStream(_context, ref outBuffer); @@ -253,6 +365,14 @@ public override void Flush() } } + + /// + /// Set detail compress parameter + /// + /// Parameter Enum + /// + /// Error Code + /// public nuint SetParameter(FL2Parameter param, nuint value) { nuint code = NativeMethods.FL2_CStream_setParameter(_context, param, value); @@ -263,6 +383,12 @@ public nuint SetParameter(FL2Parameter param, nuint value) return code; } + /// + /// Get detail compress parameter + /// + /// Parameter Enum + /// Parameter Value + /// public nuint GetParameter(FL2Parameter param) { var code = NativeMethods.FL2_CStream_getParameter(_context, param); @@ -277,7 +403,7 @@ protected override void Dispose(bool disposing) { if (!disposed) { - _innerStream.Flush(); + Flush(); if (disposing) { outputBufferHandle.Free(); diff --git a/src/FastLZMA2Net/Compressor.cs b/src/FastLZMA2Net/Compressor.cs index 5e0cc4c..b639fd7 100644 --- a/src/FastLZMA2Net/Compressor.cs +++ b/src/FastLZMA2Net/Compressor.cs @@ -1,65 +1,104 @@ using System.IO.MemoryMappedFiles; +using System.Text.RegularExpressions; namespace FastLZMA2Net { - //FL2CompressContext + /// + /// Fast LZMA2 Compress Context + /// public partial class Compressor : IDisposable { private readonly nint _CContext; private bool disposed = false; private bool disposedValue; + /// + /// Thread use of the context + /// public uint ThreadCount => NativeMethods.FL2_getCCtxThreadCount(_CContext); + + /// + /// Dictionary size property + /// public byte DictSizeProperty => NativeMethods.FL2_getCCtxDictProp(_CContext); + /// + /// Compress Level [1..10] + /// public int CompressLevel { get => (int)GetParameter(FL2Parameter.CompressionLevel); set => SetParameter(FL2Parameter.CompressionLevel, (nuint)value); } + /// + /// Levels 1..10 Setting to 1 switches to an alternate cLevel table. + /// public int HighCompressLevel { get => (int)GetParameter(FL2Parameter.HighCompression); set => SetParameter(FL2Parameter.HighCompression, (nuint)value); } + /// + /// Dictionary size with FL2.DictSizeMin & FL2.DictSizeMax + /// public int DictionarySize { get => (int)GetParameter(FL2Parameter.DictionarySize); set => SetParameter(FL2Parameter.DictionarySize, (nuint)value); } + /// + /// Match finder will resolve string matches up to this length. + /// If a longer match exists further back in the input, it will not be found. + /// Default = 42 + /// public int SearchDepth { get => (int)GetParameter(FL2Parameter.SearchDepth); set => SetParameter(FL2Parameter.SearchDepth, (nuint)value); } + /// + /// Only useful for strategies >= opt. + /// Length of match considered "good enough" to stop search. + /// Larger values make compression stronger and slower. + /// Default = 48 + /// public int FastLength { get => (int)GetParameter(FL2Parameter.FastLength); set => SetParameter(FL2Parameter.FastLength, (nuint)value); } + /// + /// Initialize new compress context + /// + /// How many thread use. auto = 0 + /// default = 6 public Compressor(uint nbThreads = 0, int compressLevel = 6) { - if (nbThreads == 1) - { - _CContext = NativeMethods.FL2_createCCtx(); - } - else - { - _CContext = NativeMethods.FL2_createCCtxMt(nbThreads); - } + _CContext = NativeMethods.FL2_createCCtxMt(nbThreads); CompressLevel = (int)compressLevel; } + /// + /// Compress data asynchronized + /// + /// Data byte array + /// Bytes Compressed public Task CompressAsync(byte[] src) { - return CompressAsync(src, 0); + return CompressAsync(src, CompressLevel); } + /// + /// Compress data asynchronized + /// + /// Data byte array + /// compress level + /// Bytes Compressed public Task CompressAsync(byte[] src, int compressLevel) { return Task.Run(() => Compress(src, compressLevel)); @@ -70,12 +109,16 @@ public byte[] Compress(byte[] src) return Compress(src, 0); } + /// + /// Compress data with compress level setting + /// + /// Data byte array + /// compress level + /// Bytes Compressed + /// public byte[] Compress(byte[] src, int compressLevel) { - if (src is null) - { - throw new ArgumentNullException(nameof(src)); - } + ArgumentNullException.ThrowIfNull(src); byte[] buffer = new byte[FL2.FindCompressBound(src)]; nuint code = NativeMethods.FL2_compressCCtx(_CContext, buffer, (nuint)buffer.Length, src, (nuint)src.Length, compressLevel); @@ -86,15 +129,18 @@ public byte[] Compress(byte[] src, int compressLevel) return buffer[..(int)code]; } + /// + /// Compress file using direct file access. No memory copy overhead. + /// + /// source file path + /// output file path + /// Bytes Compressed + /// public unsafe nuint Compress(string srcPath, string dstPath) { nuint code; FileInfo sourceFile = new FileInfo(srcPath); FileInfo destFile = new FileInfo(dstPath); - if (sourceFile.Length >= 0x7FFFFFFF) - { - throw new ArgumentOutOfRangeException(nameof(srcPath), "File is too large"); - } if (destFile.Exists) { destFile.Delete(); @@ -123,6 +169,13 @@ public unsafe nuint Compress(string srcPath, string dstPath) return code; } + /// + /// Set detail compress parameter + /// + /// Parameter Enum + /// + /// Error Code + /// public nuint SetParameter(FL2Parameter param, nuint value) { nuint code = NativeMethods.FL2_CCtx_setParameter(_CContext, param, value); @@ -133,6 +186,12 @@ public nuint SetParameter(FL2Parameter param, nuint value) return code; } + /// + /// Get detail compress parameter + /// + /// Parameter Enum + /// Parameter Value + /// public nuint GetParameter(FL2Parameter param) { var code = NativeMethods.FL2_CCtx_getParameter(_CContext, param); diff --git a/src/FastLZMA2Net/DecompressStream.cs b/src/FastLZMA2Net/DecompressStream.cs index da1f680..aef5bae 100644 --- a/src/FastLZMA2Net/DecompressStream.cs +++ b/src/FastLZMA2Net/DecompressStream.cs @@ -2,7 +2,10 @@ namespace FastLZMA2Net { - public class DecompressStream : Stream, IDisposable + /// + /// Streaming decompress context + /// + public class DecompressStream : Stream { private readonly int bufferSize = 16 * 1024 * 1024; private byte[] inputBufferArray; @@ -14,6 +17,10 @@ public class DecompressStream : Stream, IDisposable public override bool CanRead => _innerStream != null && _innerStream.CanRead; public override bool CanWrite => false; public override bool CanSeek => false; + + /// + /// Can't determine data size + /// public override long Length => throw new NotSupportedException(); public override long Position @@ -22,10 +29,17 @@ public override long Position set => throw new NotSupportedException(); } - public DecompressStream(Stream innerStream, uint nbThreads = 0, int inBufferSize = 16 * 1024 * 1024) + /// + /// Initialize streaming decompress context + /// + /// compressed data store + /// + /// Native interop buffer size, default = 64M + /// + public DecompressStream(Stream srcStream, uint nbThreads = 0, int inBufferSize = 64 * 1024 * 1024) { bufferSize = inBufferSize; - _innerStream = innerStream; + _innerStream = srcStream; if (nbThreads == 1) { @@ -53,13 +67,18 @@ public DecompressStream(Stream innerStream, uint nbThreads = 0, int inBufferSize }; } - public override void Flush() - { - _innerStream.Flush(); - } + /// + /// Copy decompressed data to destination stream + /// + /// public new void CopyTo(Stream destination) => CopyTo(destination, 256 * 1024 * 1024); + /// + /// Copy decompressed data to destination stream + /// + /// + /// Default = 256M public override void CopyTo(Stream destination, int bufferSize = 256 * 1024 * 1024) { byte[] outBufferArray = new byte[bufferSize]; @@ -72,24 +91,53 @@ public override void CopyTo(Stream destination, int bufferSize = 256 * 1024 * 10 } while (bytesRead != 0); } + /// + /// How many data has been decompressed + /// public ulong Progress => NativeMethods.FL2_getDStreamProgress(_context); + /// + /// Read decompressed data + /// + /// buffer array to receive data + /// Start index in buffer + /// How many bytes to append + /// How many bytes read. public override int Read(byte[] buffer, int offset, int count) { return Read(buffer.AsSpan(offset, count)); } + /// + /// Read decompressed data + /// + /// buffer array to receive data + /// How many bytes read. public override int Read(Span buffer) { return DecompressCore(buffer); } + /// + /// Read decompressed data asynchronized + /// + /// buffer array to receive data + /// Start index in buffer + /// How many bytes to append + /// + /// How many bytes read. public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { Memory bufferMemory = buffer.AsMemory(offset, count); return ReadAsync(buffer, cancellationToken).AsTask(); } + /// + /// Read decompressed data asynchronized + /// + /// buffer array to receive data + /// + /// How many bytes read. public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) { _innerStream.ReadAsync(new byte[10], 0, 0); @@ -144,27 +192,78 @@ private unsafe int DecompressCore(Span buffer, CancellationToken cancellat } } + /// + /// Not support + /// + /// + /// + /// + /// public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); + /// + /// Not support + /// + /// + /// public override void SetLength(long value) => throw new NotSupportedException(); + /// + /// Not support + /// + /// + /// + /// + /// public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); + /// + /// Not support + /// + /// + /// public override void Write(ReadOnlySpan buffer) => throw new NotSupportedException(); + /// + /// Not support + /// + /// + /// public override int ReadByte() => throw new NotSupportedException(); - - public override void Close() => Dispose(true); - + /// + /// Not support + /// + /// + /// + /// + /// + /// + /// public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => throw new NotSupportedException(); + /// + /// Not support + /// + /// + /// + /// + /// public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) => throw new NotSupportedException(); - + /// + /// Not support + /// + /// + /// public override void WriteByte(byte value) => throw new NotSupportedException(); + public override void Close() => Dispose(true); + public override void Flush() + { + _innerStream.Flush(); + } protected override void Dispose(bool disposing) { if (!disposed) diff --git a/src/FastLZMA2Net/Decompressor.cs b/src/FastLZMA2Net/Decompressor.cs index 00ce708..185327d 100644 --- a/src/FastLZMA2Net/Decompressor.cs +++ b/src/FastLZMA2Net/Decompressor.cs @@ -1,12 +1,21 @@ namespace FastLZMA2Net { + /// + /// Fast LZMA2 Decompress Context + /// public partial class Decompressor : IDisposable { private readonly nint _DContext; private bool disposedValue; - + /// + /// Thread use of the context + /// public uint ThreadCount => NativeMethods.FL2_getDCtxThreadCount(_DContext); + /// + /// Initialize new decompress context + /// + /// public Decompressor(uint nbThreads = 0) { if (nbThreads == 1) @@ -19,11 +28,21 @@ public Decompressor(uint nbThreads = 0) } } + /// + /// Initial new context with specific dict size property + /// + /// dictSizeProperty public void Init(byte prop) { NativeMethods.FL2_initDCtx(_DContext, prop); } + /// + /// Decompress + /// + /// Fast LZMA2 data + /// Raw data + /// public byte[] Decompress(byte[] data) { nuint decompressedSize = FL2.FindDecompressedSize(data); diff --git a/src/FastLZMA2Net/FastLZMA2Net.csproj b/src/FastLZMA2Net/FastLZMA2Net.csproj index 5f6aaa7..c28ae54 100644 --- a/src/FastLZMA2Net/FastLZMA2Net.csproj +++ b/src/FastLZMA2Net/FastLZMA2Net.csproj @@ -8,15 +8,15 @@ true True x64;x86;AnyCPU - 0.3.0 - alpha + 0.6.0 + beta $(VersionPrefix)-$(VersionSuffix) FastLZMA2Net False FastLZMA2Net KingsZNHONE Copyright © KingsZNHONE 2024-present - fast lzma2 compression + lzma2;compression README.md https://github.com/kingsznhone/FastLZMA2Net LICENSE @@ -25,6 +25,7 @@ snupkg latest-all logo.png + https://github.com/kingsznhone/FastLZMA2Net