Ejemplo n.º 1
0
        /// <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);
        }