/// <summary>
 /// Creates a <see cref="Stream"/> wrapper that decompresses brotli data.
 /// <para> For byte-by-byte reading (<see cref="ReadByte()"/>) internal buffer of specified size is
 /// allocated and used.</para>
 /// <para> Will block the thread until first kilobyte of data of source is available.</para>
 /// </summary>
 /// <param name="source">compressed data source</param>
 /// <param name="byteReadBufferSize">size of internal buffer used in case of byte-by-byte reading</param>
 /// <param name="customDictionary">custom dictionary data; <see langword="null"/> if not used</param>
 /// <exception cref="IOException">in case of corrupted data or source stream problems</exception>
 public BrotliInputStream(Stream source, int byteReadBufferSize, byte[] customDictionary)
 {
     if (byteReadBufferSize <= 0)
     {
         throw new ArgumentException("Bad buffer size:" + byteReadBufferSize);
     }
     else if (source == null)
     {
         throw new ArgumentException("source is null");
     }
     _buffer = new byte[byteReadBufferSize];
     _remainingBufferBytes = 0;
     _bufferOffset         = 0;
     try
     {
         BrotliState.SetInput(_state, source);
     }
     catch (BrotliRuntimeException ex)
     {
         throw new IOException("Brotli decoder initialization failed", ex);
     }
     if (customDictionary != null && customDictionary.Length != 0)
     {
         BrotliDecode.SetCustomDictionary(_state, customDictionary);
     }
 }
        /// <summary><inheritDoc/></summary>
        /// <exception cref="IOException"/>
        public override int Read(byte[] destBuffer, int destOffset, int destLen)
        {
            if (destOffset < 0)
            {
                throw new ArgumentException("Bad offset: " + destOffset);
            }
            else if (destLen < 0)
            {
                throw new ArgumentException("Bad length: " + destLen);
            }
            else if (destOffset + destLen > destBuffer.Length)
            {
                throw new ArgumentException("Buffer overflow: " + (destOffset + destLen) + " > " + destBuffer.Length);
            }
            else if (destLen == 0)
            {
                return(0);
            }
            int copyLen = Math.Max(_remainingBufferBytes - _bufferOffset, 0);

            if (copyLen != 0)
            {
                copyLen = Math.Min(copyLen, destLen);
                Array.Copy(_buffer, _bufferOffset, destBuffer, destOffset, copyLen);
                _bufferOffset += copyLen;
                destOffset    += copyLen;
                destLen       -= copyLen;
                if (destLen == 0)
                {
                    return(copyLen);
                }
            }
            try
            {
                _state.output       = destBuffer;
                _state.outputOffset = destOffset;
                _state.outputLength = destLen;
                _state.outputUsed   = 0;
                BrotliDecode.Decompress(_state);
                if (_state.outputUsed == 0)
                {
                    return(-1);
                }
                return(_state.outputUsed + copyLen);
            }
            catch (BrotliRuntimeException ex)
            {
                throw new IOException("Brotli stream decoding failed", ex);
            }
        }