private const int BUFSIZE = 1024 * 32; // we do not need a big outbuf private void Init() { lzma_ret ret = XZUtils.CreateEncoder(_lzma_stream, _threads, _preset); if (ret != lzma_ret.LZMA_OK) { throw new Exception($"Can not create lzma stream: {ret}"); } _inbuf = Marshal.AllocHGlobal(BUFSIZE); _outbuf = Marshal.AllocHGlobal(BUFSIZE); // init lzma_stream _lzma_stream.next_in = _inbuf; _lzma_stream.next_out = _outbuf; _lzma_stream.avail_in = UIntPtr.Zero; _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 < BUFSIZE) 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> /// 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}"); } } }