protected override void Dispose(bool disposing) { if (_stream == null) { return; } try { // compress all remaining data while (true) { // do compress, LZMA_FINISH action should return LZMA_OK or LZMA_STREAM_END on success var ret = Native.lzma_code(_lzma_stream, lzma_action.LZMA_FINISH); if (ret != lzma_ret.LZMA_STREAM_END && ret != lzma_ret.LZMA_OK) { throw new Exception($"lzma_code returns {ret}"); } // write output buffer to underlying stream if (_lzma_stream.avail_out == UIntPtr.Zero || ret == lzma_ret.LZMA_STREAM_END) { int size = (int)(BUFSIZE - (uint)_lzma_stream.avail_out); var data = ArrayPool <byte> .Shared.Rent(size); Marshal.Copy(_outbuf, data, 0, size); _stream.Write(data, 0, size); ArrayPool <byte> .Shared.Return(data); // Reset next_out and avail_out. _lzma_stream.next_out = _outbuf; _lzma_stream.avail_out = (UIntPtr)BUFSIZE; } if (ret == lzma_ret.LZMA_STREAM_END) { break; } } } finally { Native.lzma_end(_lzma_stream); Marshal.FreeHGlobal(_inbuf); Marshal.FreeHGlobal(_outbuf); _stream.Close(); _stream = null; } }
/// <summary> /// learn from 01_compress_easy.c /// </summary> /// <param name="buffer"></param> /// <param name="offset"></param> /// <param name="count"></param> public override void Write(byte[] buffer, int offset, int count) { if (buffer == null) { throw new ArgumentNullException(nameof(buffer)); } if (offset < 0) { throw new ArgumentOutOfRangeException(nameof(offset)); } if (count < 0) { throw new ArgumentOutOfRangeException(nameof(count)); } if (count + offset > buffer.Length) { throw new ArgumentOutOfRangeException(nameof(count), "offset+count > buffer.length"); } if (count == 0) { return; } int bytesProcessed = 0; while (true) { // Fill the input buffer if it is empty. if (_lzma_stream.avail_in == UIntPtr.Zero) { int bytesToProcess = Math.Min(count - bytesProcessed, BUFSIZE); if (bytesToProcess == 0) { break; // no more data to compress } _lzma_stream.next_in = _inbuf; _lzma_stream.avail_in = (UIntPtr)bytesToProcess; Marshal.Copy(buffer, offset + bytesProcessed, _inbuf, bytesToProcess); bytesProcessed += bytesToProcess; } // do compress, RUN action should return LZMA_OK on success var ret = Native.lzma_code(_lzma_stream, lzma_action.LZMA_RUN); if (ret != lzma_ret.LZMA_OK) { throw new Exception($"lzma_code returns {ret}"); } // check output buffer if (_lzma_stream.avail_out == UIntPtr.Zero) { byte[] data = new byte[BUFSIZE]; Marshal.Copy(_outbuf, data, 0, data.Length); _stream.Write(data, 0, data.Length); // Reset next_out and avail_out. _lzma_stream.next_out = _outbuf; _lzma_stream.avail_out = (UIntPtr)BUFSIZE; } } }
/// <summary> /// learn from 02_decompress.c /// </summary> /// <param name="buffer"></param> /// <param name="offset"></param> /// <param name="count"></param> /// <returns></returns> public override int Read(byte[] buffer, int offset, int count) { if (buffer == null) { throw new ArgumentNullException(nameof(buffer)); } if (offset < 0) { throw new ArgumentOutOfRangeException(nameof(offset)); } if (count < 0) { throw new ArgumentOutOfRangeException(nameof(count)); } if (count + offset > buffer.Length) { throw new ArgumentOutOfRangeException(nameof(count), "offset+count > buffer.length"); } if (count == 0) { return(0); } int cTotalRead = 0; while (true) { // read from underlying stream if (_lzma_stream.avail_in == UIntPtr.Zero && action == lzma_action.LZMA_RUN) { // read more data from underlying stream var data = new byte[BUFSIZE]; var bytesRead = _stream.Read(data, 0, BUFSIZE); if (bytesRead == 0) { action = lzma_action.LZMA_FINISH; // source stream has no more data } _lzma_stream.next_in = _inbuf; _lzma_stream.avail_in = (UIntPtr)bytesRead; Marshal.Copy(data, 0, _inbuf, bytesRead); } // try to read from existing outbuf int cReadable = BUFSIZE - (int)(uint)_lzma_stream.avail_out - read_pos; if (cReadable > 0) { var cCopy = Math.Min(cReadable, count - cTotalRead); var p = Native.Is64Bit ? new IntPtr(_outbuf.ToInt64() + read_pos) : new IntPtr(_outbuf.ToInt32() + read_pos); Marshal.Copy(p, buffer, offset + cTotalRead, cCopy); cTotalRead += cCopy; read_pos += cCopy; Trace.Assert(cTotalRead <= count); if (cTotalRead == count) { return(cTotalRead); } } // need to read more data from outbuf // if previous decode returns LZMA_STREAM_END, there will be no more data if (ret == lzma_ret.LZMA_STREAM_END) { return(cTotalRead); } // otherwise, reset outbuf to recv more decompressed data from liblzma, or decompress is finished Trace.Assert(read_pos + (uint)_lzma_stream.avail_out <= BUFSIZE); if (_lzma_stream.avail_out == UIntPtr.Zero && read_pos + (uint)_lzma_stream.avail_out == BUFSIZE) { _lzma_stream.next_out = _outbuf; _lzma_stream.avail_out = (UIntPtr)BUFSIZE; read_pos = 0; } // do decompress ret = Native.lzma_code(_lzma_stream, action); if (ret != lzma_ret.LZMA_OK && ret != lzma_ret.LZMA_STREAM_END) { throw new Exception($"lzma_code returns {ret}"); } } }
/// <summary> /// liblzma has provided lzma_stream_buffer_encode and lzma_stream_buffer_decode, but here I re-invent the wheel again... /// </summary> /// <param name="_lzma_stream"></param> /// <param name="data"></param> /// <param name="offset"></param> /// <param name="count"></param> /// <returns></returns> private static byte[] CodeBuffer(lzma_stream _lzma_stream, byte[] data, int offset, int count) { const int BUFSIZE = 4096; var outStream = new MemoryStream(BUFSIZE); var inbuf = Marshal.AllocHGlobal(BUFSIZE); var outbuf = Marshal.AllocHGlobal(BUFSIZE); try { var action = lzma_action.LZMA_RUN; _lzma_stream.next_in = inbuf; _lzma_stream.avail_in = UIntPtr.Zero; _lzma_stream.next_out = outbuf; _lzma_stream.avail_out = (UIntPtr)BUFSIZE; int read_pos = offset; while (true) { if (_lzma_stream.avail_in == UIntPtr.Zero) { if (read_pos < offset + count) { int bytesToProcess = Math.Min(BUFSIZE, offset + count - read_pos); _lzma_stream.next_in = inbuf; _lzma_stream.avail_in = (UIntPtr)bytesToProcess; Marshal.Copy(data, read_pos, inbuf, bytesToProcess); read_pos += bytesToProcess; Trace.Assert(read_pos <= offset + count); } if (read_pos == offset + count) { action = lzma_action.LZMA_FINISH; } } var ret = Native.lzma_code(_lzma_stream, action); if (_lzma_stream.avail_out == UIntPtr.Zero || ret == lzma_ret.LZMA_STREAM_END) { int write_size = BUFSIZE - (int)(uint)_lzma_stream.avail_out; var tmp = new byte[write_size]; Marshal.Copy(outbuf, tmp, 0, write_size); outStream.Write(tmp, 0, write_size); _lzma_stream.next_out = outbuf; _lzma_stream.avail_out = (UIntPtr)BUFSIZE; } if (ret != lzma_ret.LZMA_OK) { if (ret == lzma_ret.LZMA_STREAM_END) { break; } throw new Exception($"lzma_code returns {ret}"); } } } finally { Marshal.FreeHGlobal(inbuf); Marshal.FreeHGlobal(outbuf); } return(outStream.ToArray()); }