/// <summary> /// Performs the generic zlib stream filter operation. /// </summary> /// <param name="input">Input chunk of bytes.</param> /// <param name="inputOffset">Current position within the chunk.</param> /// <param name="closing">Value indicating whether the stream will be closed.</param> /// <returns>Array of available bytes (even empty one). Null on non-critical error.</returns> protected byte[] FilterInner(byte[] input, ref int inputOffset, bool closing) { if (_state == ZlibState.Finished) { //if stream already ended, throw an error PhpException.Throw(PhpError.Warning, "using zlib stream that is already finished"); return(null); } if (_state == ZlibState.Failed) { //if stream already ended, throw an error PhpException.Throw(PhpError.Warning, "using zlib stream that failed"); return(null); } List <(byte[] Data, int Length)> subchunks = null; int status = zlibConst.Z_OK; // initialize if necessary if (_state == ZlibState.NotStarted) { _stream = new ZStream(); // init algorithm status = InitZlibOperation(_stream); // check for error if (status != zlibConst.Z_OK) { _state = ZlibState.Failed; PhpException.Throw(PhpError.Error, Zlib.zError(status)); return(null); } _state = ZlibState.Data; } if (_state == ZlibState.Data) { // input chunk _stream.next_in = input; _stream.next_in_index = inputOffset; _stream.avail_in = input.Length - inputOffset; long initial_total_out = _stream.total_out; long initial_total_in = _stream.total_in; int nextBufferSize = 8; int bufferSizeMax = 65536; // do while operation does some progress do { _stream.next_out = new byte[nextBufferSize]; _stream.next_out_index = 0; _stream.avail_out = _stream.next_out.Length; if (nextBufferSize < bufferSizeMax) { nextBufferSize *= 2; } long previous_total_out = _stream.total_out; status = PerformZlibOperation(_stream, GetFlushFlags(closing)); if (_stream.total_out - previous_total_out > 0) { // if the list was not initialize, do so if (subchunks == null) { subchunks = new List <(byte[], int)>(); } // add the subchunk to the list only when it contains some data subchunks.Add((_stream.next_out, (int)(_stream.total_out - previous_total_out))); } } // we continue only when progress was made and there is input available while ((status == zlibConst.Z_OK || status == zlibConst.Z_BUF_ERROR) && (_stream.avail_in > 0 || (_stream.avail_in == 0 && _stream.avail_out == 0))); // if the last op wasn't the end of stream (this happens only with Z_FINISH) or general success, return error if (status != zlibConst.Z_STREAM_END && status != zlibConst.Z_OK) { _state = ZlibState.Failed; PhpException.Throw(PhpError.Warning, Zlib.zError(status)); return(null); } // end the algorithm if requested if (closing || status == zlibConst.Z_STREAM_END) { _state = ZlibState.Finished; status = EndZlibOperation(_stream); if (status != zlibConst.Z_OK) { _state = ZlibState.Failed; PhpException.Throw(PhpError.Warning, Zlib.zError(status)); return(null); } } inputOffset = _stream.next_in_index; // if the chunk ended or everything is OK, connect the subchunks and return if (subchunks != null && subchunks.Count > 0) { byte[] result = new byte[_stream.total_out - initial_total_out]; long resultPos = 0; for (int i = 0; i < subchunks.Count; i++) { Buffer.BlockCopy( subchunks[i].Data, 0, result, (int)resultPos, (int)Math.Min(subchunks[i].Length, _stream.total_out - resultPos)); resultPos += subchunks[i].Length; } return(result); } else { return(new byte[0]); } } Debug.Fail(null); return(null); }