From 2cf9e89a22d0feea8c7f176c23c631303cf04300 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Tue, 26 Aug 2025 02:15:05 +0100 Subject: [PATCH] Move LzdStream to Aaru.Compression. --- Aaru.Archives/Zoo/Extern.cs | 21 -- Aaru.Compression/Aaru.Compression.csproj | 3 +- Aaru.Compression/LzdStream.cs | 333 ++++++++++++----------- 3 files changed, 172 insertions(+), 185 deletions(-) delete mode 100644 Aaru.Archives/Zoo/Extern.cs diff --git a/Aaru.Archives/Zoo/Extern.cs b/Aaru.Archives/Zoo/Extern.cs deleted file mode 100644 index cc0e561fe..000000000 --- a/Aaru.Archives/Zoo/Extern.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using Aaru.CommonTypes.Interfaces; - -namespace Aaru.Archives; - -public sealed partial class Zoo : IArchive -{ - [LibraryImport("libAaru.Compression.Native")] - private static partial IntPtr CreateLZDContext(); - - [LibraryImport("libAaru.Compression.Native")] - private static partial void DestroyLZDContext(IntPtr ctx); - - [LibraryImport("libAaru.Compression.Native")] - private static partial int LZD_FeedNative(IntPtr ctx, [In] byte[] inputBuffer, nuint inputSize); - - [LibraryImport("libAaru.Compression.Native")] - private static partial int LZD_DrainNative(IntPtr ctx, [Out] byte[] outputBuffer, nuint outputCapacity, - out nuint produced); -} \ No newline at end of file diff --git a/Aaru.Compression/Aaru.Compression.csproj b/Aaru.Compression/Aaru.Compression.csproj index cb1637d02..f37d49f85 100644 --- a/Aaru.Compression/Aaru.Compression.csproj +++ b/Aaru.Compression/Aaru.Compression.csproj @@ -42,7 +42,8 @@ - + + diff --git a/Aaru.Compression/LzdStream.cs b/Aaru.Compression/LzdStream.cs index 0ce72f247..ae6209514 100644 --- a/Aaru.Compression/LzdStream.cs +++ b/Aaru.Compression/LzdStream.cs @@ -1,11 +1,179 @@ using System; using System.Diagnostics; using System.IO; +using System.Runtime.InteropServices; -namespace Aaru.Archives; +namespace Aaru.Compression; -public sealed partial class Zoo +public partial class LzdStream : Stream { + private const int INPUT_CHUNK = 8192; + private const int OUTPUT_CHUNK = 8192; + + private readonly Stream _baseStream; + private readonly byte[] _inBuffer = new byte[INPUT_CHUNK]; + private readonly byte[] _outBuffer = new byte[OUTPUT_CHUNK]; + + private IntPtr _ctx; + private bool _disposed; + private bool _eof; // native says DONE + private bool _flushed; // whether we've sent the final empty feed + private int _outCount; + private int _outOffset; + + public LzdStream(Stream compressedStream) + { + _baseStream = compressedStream ?? throw new ArgumentNullException(nameof(compressedStream)); + _ctx = CreateLZDContext(); + + if(_ctx == IntPtr.Zero) throw new InvalidOperationException("Failed to create LZD context"); + } + + public override bool CanRead => true; + public override bool CanSeek => false; + public override bool CanWrite => false; + public override long Length => throw new NotSupportedException(); + + public override long Position + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + [LibraryImport("libAaru.Compression.Native")] + private static partial IntPtr CreateLZDContext(); + + [LibraryImport("libAaru.Compression.Native")] + private static partial void DestroyLZDContext(IntPtr ctx); + + [LibraryImport("libAaru.Compression.Native")] + private static partial int LZD_FeedNative(IntPtr ctx, [In] byte[] inputBuffer, nuint inputSize); + + [LibraryImport("libAaru.Compression.Native")] + private static partial int LZD_DrainNative(IntPtr ctx, [Out] byte[] outputBuffer, nuint outputCapacity, + out nuint produced); + + public override void Flush() {} + + public override int Read(byte[] buffer, int offset, int count) + { + if(_disposed) throw new ObjectDisposedException(nameof(LzdStream)); + if(buffer == null) throw new ArgumentNullException(nameof(buffer)); + if(offset < 0 || count < 0 || offset + count > buffer.Length) throw new ArgumentOutOfRangeException(); + + int totalRead = 0; + int totalOut = 0; + int iter = 0; + + while(totalRead < count) + { + Debug.WriteLine($"-- LOOP iter={iter++} total_out={totalOut} --"); + + // serve leftovers first + if(_outOffset < _outCount) + { + int toCopy = Math.Min(count - totalRead, _outCount - _outOffset); + Buffer.BlockCopy(_outBuffer, _outOffset, buffer, offset + totalRead, toCopy); + _outOffset += toCopy; + totalRead += toCopy; + totalOut += toCopy; + + continue; + } + + if(_eof) break; // nothing more to decode + + // drain from native + LZDStatus status = TryDrain(out int produced); + Debug.WriteLine($"[DRAIN] produced={produced} status={status} flushed={_flushed} eof={_eof}"); + + + if(produced > 0) + { + _outCount = produced; + _outOffset = 0; + + continue; // go copy them on next loop + } + + if(status == LZDStatus.NEED_INPUT) + { + int n = _baseStream.Read(_inBuffer, 0, _inBuffer.Length); + + if(n > 0) + { + _eof = false; // we're not done, fresh data incoming + Debug.WriteLine($"[FEED] size={n} flushed={_flushed} (real data)"); + + var f = (LZDStatus)LZD_FeedNative(_ctx, _inBuffer, (UIntPtr)n); + if(f == LZDStatus.ERROR) ThrowDecoderError(); + + continue; + } + + // end of base stream: flush native once + if(!_flushed) + { + Debug.WriteLine($"[FEED] size=0 flushed={_flushed} (final empty feed)"); + var f = (LZDStatus)LZD_FeedNative(_ctx, Array.Empty(), UIntPtr.Zero); + if(f == LZDStatus.ERROR) ThrowDecoderError(); + _flushed = true; + Debug.WriteLine(">>> SET _flushed=true"); + + continue; + } + + // no more to give + _eof = true; + Debug.WriteLine(">>> SET _eof=true (DONE from decoder)"); + + break; + } + + if(status == LZDStatus.DONE) + { + _eof = true; + Debug.WriteLine(">>> SET _eof=true (no more data and already flushed)"); + + break; + } + + // if OK but no bytes, loop again + } + + Debug.WriteLine($"TOTAL OUT={totalOut} bytes"); + + return totalRead; + } + + private LZDStatus TryDrain(out int produced) + { + var st = (LZDStatus)LZD_DrainNative(_ctx, _outBuffer, (UIntPtr)_outBuffer.Length, out UIntPtr p); + if(st == LZDStatus.ERROR) ThrowDecoderError(); + if(st == LZDStatus.DONE) _eof = true; + produced = (int)p; + + return st; + } + + private static void ThrowDecoderError() => throw new IOException("LZD decompression error"); + + protected override void Dispose(bool disposing) + { + if(!_disposed) + { + DestroyLZDContext(_ctx); + _ctx = IntPtr.Zero; + _disposed = true; + } + + base.Dispose(disposing); + } + + public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); + public override void SetLength(long value) => throw new NotSupportedException(); + public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); + #region Nested type: LZDStatus internal enum LZDStatus @@ -17,166 +185,5 @@ public sealed partial class Zoo ERROR = -1 } -#endregion - -#region Nested type: LzdStream - - public class LzdStream : Stream - { - private const int INPUT_CHUNK = 8192; - private const int OUTPUT_CHUNK = 8192; - - private readonly Stream _baseStream; - private readonly byte[] _inBuffer = new byte[INPUT_CHUNK]; - private readonly byte[] _outBuffer = new byte[OUTPUT_CHUNK]; - - private IntPtr _ctx; - private bool _disposed; - private bool _eof; // native says DONE - private bool _flushed; // whether we've sent the final empty feed - private int _outCount; - private int _outOffset; - - public LzdStream(Stream compressedStream) - { - _baseStream = compressedStream ?? throw new ArgumentNullException(nameof(compressedStream)); - _ctx = CreateLZDContext(); - - if(_ctx == IntPtr.Zero) throw new InvalidOperationException("Failed to create LZD context"); - } - - public override bool CanRead => true; - public override bool CanSeek => false; - public override bool CanWrite => false; - public override long Length => throw new NotSupportedException(); - - public override long Position - { - get => throw new NotSupportedException(); - set => throw new NotSupportedException(); - } - - public override void Flush() {} - - public override int Read(byte[] buffer, int offset, int count) - { - if(_disposed) throw new ObjectDisposedException(nameof(LzdStream)); - if(buffer == null) throw new ArgumentNullException(nameof(buffer)); - if(offset < 0 || count < 0 || offset + count > buffer.Length) throw new ArgumentOutOfRangeException(); - - int totalRead = 0; - int totalOut = 0; - int iter = 0; - - while(totalRead < count) - { - Debug.WriteLine($"-- LOOP iter={iter++} total_out={totalOut} --"); - - // serve leftovers first - if(_outOffset < _outCount) - { - int toCopy = Math.Min(count - totalRead, _outCount - _outOffset); - Buffer.BlockCopy(_outBuffer, _outOffset, buffer, offset + totalRead, toCopy); - _outOffset += toCopy; - totalRead += toCopy; - totalOut += toCopy; - - continue; - } - - if(_eof) break; // nothing more to decode - - // drain from native - LZDStatus status = TryDrain(out int produced); - Debug.WriteLine($"[DRAIN] produced={produced} status={status} flushed={_flushed} eof={_eof}"); - - - if(produced > 0) - { - _outCount = produced; - _outOffset = 0; - - continue; // go copy them on next loop - } - - if(status == LZDStatus.NEED_INPUT) - { - int n = _baseStream.Read(_inBuffer, 0, _inBuffer.Length); - - if(n > 0) - { - _eof = false; // we're not done, fresh data incoming - Debug.WriteLine($"[FEED] size={n} flushed={_flushed} (real data)"); - - var f = (LZDStatus)LZD_FeedNative(_ctx, _inBuffer, (UIntPtr)n); - if(f == LZDStatus.ERROR) ThrowDecoderError(); - - continue; - } - - // end of base stream: flush native once - if(!_flushed) - { - Debug.WriteLine($"[FEED] size=0 flushed={_flushed} (final empty feed)"); - var f = (LZDStatus)LZD_FeedNative(_ctx, Array.Empty(), UIntPtr.Zero); - if(f == LZDStatus.ERROR) ThrowDecoderError(); - _flushed = true; - Debug.WriteLine(">>> SET _flushed=true"); - - continue; - } - - // no more to give - _eof = true; - Debug.WriteLine(">>> SET _eof=true (DONE from decoder)"); - - break; - } - - if(status == LZDStatus.DONE) - { - _eof = true; - Debug.WriteLine(">>> SET _eof=true (no more data and already flushed)"); - - break; - } - - // if OK but no bytes, loop again - } - - Debug.WriteLine($"TOTAL OUT={totalOut} bytes"); - - return totalRead; - } - - private LZDStatus TryDrain(out int produced) - { - var st = (LZDStatus)LZD_DrainNative(_ctx, _outBuffer, (UIntPtr)_outBuffer.Length, out UIntPtr p); - if(st == LZDStatus.ERROR) ThrowDecoderError(); - if(st == LZDStatus.DONE) _eof = true; - produced = (int)p; - - return st; - } - - private static void ThrowDecoderError() => throw new IOException("LZD decompression error"); - - protected override void Dispose(bool disposing) - { - if(!_disposed) - { - DestroyLZDContext(_ctx); - _ctx = IntPtr.Zero; - _disposed = true; - } - - base.Dispose(disposing); - } - - public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); - public override void SetLength(long value) => throw new NotSupportedException(); - public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); - } - #endregion } \ No newline at end of file