public int AddBytes(byte[] buffer, int offset, int length, IgnoreHelloRequestsSettings ignoreHelloRequests = IgnoreHelloRequestsSettings.IncludeHelloRequests)
        {
            var numAdded = 0;
            var end = offset + length;

            while (true)
            {
                if (_headerBufferLen == 0)
                {
                    while (offset + 4 <= end)
                    {
                        // We can read at least the header
                        int start = offset;
                        offset++;
                        var messageLen = Utils.ReadUInt24(buffer, ref offset);
                        offset += messageLen;
                        if (offset <= end)
                        {
                            // Whole message fits in buffer, this is the common case
                            var message = new byte[4 + messageLen];
                            Buffer.BlockCopy(buffer, start, message, 0, 4 + messageLen);
                            if ((!_hasHelloRequest && (ignoreHelloRequests == IgnoreHelloRequestsSettings.IncludeHelloRequests ||
                                (ignoreHelloRequests == IgnoreHelloRequestsSettings.IgnoreHelloRequestsUntilFinished && _hasFinished))) ||
                                !IsHelloRequest(message))
                            {
                                _messages.Add(message);
                                CheckType((HandshakeType)message[0]);
                                numAdded++;
                            }
                        }
                        else
                        {
                            // The header fits in the buffer, but not the entire message
                            _headerBuffer = new byte[4];
                            _headerBufferLen = 4;
                            Buffer.BlockCopy(buffer, start, _headerBuffer, 0, 4);
                            _buffer = new byte[messageLen];
                            _bufferLen = messageLen - (offset - end);
                            Buffer.BlockCopy(buffer, start + 4, _buffer, 0, _bufferLen);
                        }
                    }
                    if (offset < end)
                    {
                        // Else, the whole header does not fit in the buffer
                        _headerBuffer = new byte[4];
                        _headerBufferLen = end - offset;
                        Buffer.BlockCopy(buffer, offset, _headerBuffer, 0, _headerBufferLen);
                    }
                    return numAdded;
                }
                else
                {
                    // We have previously buffered up a part of a message that needs to be completed
                    
                    if (_headerBufferLen < 4)
                    {
                        var toCopy = Math.Min(end - offset, 4 - _headerBufferLen);
                        Buffer.BlockCopy(buffer, offset, _headerBuffer, _headerBufferLen, toCopy);
                        _headerBufferLen += toCopy;
                        offset += toCopy;

                        if (_headerBufferLen < 4)
                            return numAdded;
                    }

                    // Now header buffer is complete, so we can fetch message len and fill rest of message buffer as much as possible
                    var tmpOffset = 1;
                    var messageLen = Utils.ReadUInt24(_headerBuffer, ref tmpOffset);
                    var bytesToCopy = Math.Min(end - offset, messageLen - _bufferLen);
                    if (_buffer == null)
                        _buffer = new byte[messageLen];
                    Buffer.BlockCopy(buffer, offset, _buffer, _bufferLen, bytesToCopy);
                    offset += bytesToCopy;
                    _bufferLen += bytesToCopy;
                    if (_bufferLen != messageLen)
                    {
                        return numAdded;
                    }

                    // Now we have a complete message to insert to the queue
                    var message = new byte[4 + messageLen];
                    Buffer.BlockCopy(_headerBuffer, 0, message, 0, 4);
                    Buffer.BlockCopy(_buffer, 0, message, 4, messageLen);
                    if ((!_hasHelloRequest && (ignoreHelloRequests == IgnoreHelloRequestsSettings.IncludeHelloRequests ||
                        (ignoreHelloRequests == IgnoreHelloRequestsSettings.IgnoreHelloRequestsUntilFinished && _hasFinished))) ||
                        !IsHelloRequest(message))
                    {
                        _messages.Add(message);
                        CheckType((HandshakeType)message[0]);
                        numAdded++;
                    }
                    _headerBuffer = null;
                    _headerBufferLen = 0;
                    Utils.ClearArray(_buffer);
                    _buffer = null;
                    _bufferLen = 0;
                }
            }
        }
        public int AddBytes(byte[] buffer, int offset, int length, IgnoreHelloRequestsSettings ignoreHelloRequests = IgnoreHelloRequestsSettings.IncludeHelloRequests)
        {
            var numAdded = 0;
            var end      = offset + length;

            while (true)
            {
                if (_headerBufferLen == 0)
                {
                    while (offset + 4 <= end)
                    {
                        // We can read at least the header
                        int start = offset;
                        offset++;
                        var messageLen = Utils.ReadUInt24(buffer, ref offset);
                        offset += messageLen;
                        if (offset <= end)
                        {
                            // Whole message fits in buffer, this is the common case
                            var message = new byte[4 + messageLen];
                            Buffer.BlockCopy(buffer, start, message, 0, 4 + messageLen);
                            if ((!_hasHelloRequest && (ignoreHelloRequests == IgnoreHelloRequestsSettings.IncludeHelloRequests ||
                                                       (ignoreHelloRequests == IgnoreHelloRequestsSettings.IgnoreHelloRequestsUntilFinished && _hasFinished))) ||
                                !IsHelloRequest(message))
                            {
                                _messages.Add(message);
                                CheckType((HandshakeType)message[0]);
                                numAdded++;
                            }
                        }
                        else
                        {
                            // The header fits in the buffer, but not the entire message
                            _headerBuffer    = new byte[4];
                            _headerBufferLen = 4;
                            Buffer.BlockCopy(buffer, start, _headerBuffer, 0, 4);
                            _buffer    = new byte[messageLen];
                            _bufferLen = messageLen - (offset - end);
                            Buffer.BlockCopy(buffer, start + 4, _buffer, 0, _bufferLen);
                        }
                    }
                    if (offset < end)
                    {
                        // Else, the whole header does not fit in the buffer
                        _headerBuffer    = new byte[4];
                        _headerBufferLen = end - offset;
                        Buffer.BlockCopy(buffer, offset, _headerBuffer, 0, _headerBufferLen);
                    }
                    return(numAdded);
                }
                else
                {
                    // We have previously buffered up a part of a message that needs to be completed

                    if (_headerBufferLen < 4)
                    {
                        var toCopy = Math.Min(end - offset, 4 - _headerBufferLen);
                        Buffer.BlockCopy(buffer, offset, _headerBuffer, _headerBufferLen, toCopy);
                        _headerBufferLen += toCopy;
                        offset           += toCopy;

                        if (_headerBufferLen < 4)
                        {
                            return(numAdded);
                        }
                    }

                    // Now header buffer is complete, so we can fetch message len and fill rest of message buffer as much as possible
                    var tmpOffset   = 1;
                    var messageLen  = Utils.ReadUInt24(_headerBuffer, ref tmpOffset);
                    var bytesToCopy = Math.Min(end - offset, messageLen - _bufferLen);
                    if (_buffer == null)
                    {
                        _buffer = new byte[messageLen];
                    }
                    Buffer.BlockCopy(buffer, offset, _buffer, _bufferLen, bytesToCopy);
                    offset     += bytesToCopy;
                    _bufferLen += bytesToCopy;
                    if (_bufferLen != messageLen)
                    {
                        return(numAdded);
                    }

                    // Now we have a complete message to insert to the queue
                    var message = new byte[4 + messageLen];
                    Buffer.BlockCopy(_headerBuffer, 0, message, 0, 4);
                    Buffer.BlockCopy(_buffer, 0, message, 4, messageLen);
                    if ((!_hasHelloRequest && (ignoreHelloRequests == IgnoreHelloRequestsSettings.IncludeHelloRequests ||
                                               (ignoreHelloRequests == IgnoreHelloRequestsSettings.IgnoreHelloRequestsUntilFinished && _hasFinished))) ||
                        !IsHelloRequest(message))
                    {
                        _messages.Add(message);
                        CheckType((HandshakeType)message[0]);
                        numAdded++;
                    }
                    _headerBuffer    = null;
                    _headerBufferLen = 0;
                    Utils.ClearArray(_buffer);
                    _buffer    = null;
                    _bufferLen = 0;
                }
            }
        }