bool CSteamNetworkConnectionBase::SNP_ReceiveReliableSegment( int64 nPktNum, int64 nSegBegin, const uint8 *pSegmentData, int cbSegmentSize, SteamNetworkingMicroseconds usecNow )
{
	int nLogLevelPacketDecode = m_connectionConfig.m_LogLevel_PacketDecode.Get();

	// Calculate segment end stream position
	int64 nSegEnd = nSegBegin + cbSegmentSize;

	// Spew
	SpewVerboseGroup( nLogLevelPacketDecode, "[%s]   decode pkt %lld reliable range [%lld,%lld)\n",
		GetDescription(),
		(long long)nPktNum,
		(long long)nSegBegin, (long long)nSegEnd );

	// No segment data?  Seems fishy, but if it happens, just skip it.
	Assert( cbSegmentSize >= 0 );
	if ( cbSegmentSize <= 0 )
	{
		// Spew but rate limit in case of malicious sender
		SpewWarningRateLimited( usecNow, "[%s] decode pkt %lld empty reliable segment?\n",
			GetDescription(),
			(long long)nPktNum );
		return true;
	}

	// Ignore data segments when we are not going to process them (e.g. linger)
	if ( GetState() != k_ESteamNetworkingConnectionState_Connected )
	{
		SpewVerboseGroup( nLogLevelPacketDecode, "[%s]   discarding pkt %lld [%lld,%lld) as connection is in state %d\n",
			GetDescription(),
			(long long)nPktNum,
			(long long)nSegBegin, (long long)nSegEnd,
			(int)GetState() );
		return true;
	}

	// Check if the entire thing is stuff we have already received, then
	// we can discard it
	if ( nSegEnd <= m_receiverState.m_nReliableStreamPos )
		return true;

	// !SPEED! Should we have a fast path here for small messages
	// where we have nothing buffered, and avoid all the copying into the 
	// stream buffer and decode directly.

	// What do we expect to receive next?
	const int64 nExpectNextStreamPos = m_receiverState.m_nReliableStreamPos + len( m_receiverState.m_bufReliableStream );

	// Check if we need to grow the reliable buffer to hold the data
	if ( nSegEnd > nExpectNextStreamPos )
	{
		int64 cbNewSize = nSegEnd - m_receiverState.m_nReliableStreamPos;
		Assert( cbNewSize > len( m_receiverState.m_bufReliableStream ) );

		// Check if we have too much data buffered, just stop processing
		// this packet, and forget we ever received it.  We need to protect
		// against a malicious sender trying to create big gaps.  If they
		// are legit, they will notice that we go back and fill in the gaps
		// and we will get caught up.
		if ( cbNewSize > k_cbMaxBufferedReceiveReliableData )
		{
			// Stop processing the packet, and don't ack it.
			// This indicates the connection is in pretty bad shape,
			// so spew about it.  But rate limit in case of malicious sender
			SpewWarningRateLimited( usecNow, "[%s] decode pkt %lld abort.  %lld bytes reliable data buffered [%lld-%lld), new size would be %lld to %lld\n",
				GetDescription(),
				(long long)nPktNum,
				(long long)m_receiverState.m_bufReliableStream.size(),
				(long long)m_receiverState.m_nReliableStreamPos,
				(long long)( m_receiverState.m_nReliableStreamPos + m_receiverState.m_bufReliableStream.size() ),
				(long long)cbNewSize, (long long)nSegEnd
			);
			return false;  // DO NOT ACK THIS PACKET
		}

		// Check if this is going to make a new gap
		if ( nSegBegin > nExpectNextStreamPos )
		{
			if ( !m_receiverState.m_mapReliableStreamGaps.empty() )
			{

				// We should never have a gap at the very end of the buffer.
				// (Why would we extend the buffer, unless we needed to to
				// store some data?)
				Assert( m_receiverState.m_mapReliableStreamGaps.rbegin()->second < nExpectNextStreamPos );

				// We need to add a new gap.  See if we're already too fragmented.
				if ( len( m_receiverState.m_mapReliableStreamGaps ) >= k_nMaxReliableStreamGaps_Extend )
				{
					// Stop processing the packet, and don't ack it
					// This indicates the connection is in pretty bad shape,
					// so spew about it.  But rate limit in case of malicious sender
					SpewWarningRateLimited( usecNow, "[%s] decode pkt %lld abort.  Reliable stream already has %d fragments, first is [%lld,%lld), last is [%lld,%lld), new segment is [%lld,%lld)\n",
						GetDescription(),
						(long long)nPktNum,
						len( m_receiverState.m_mapReliableStreamGaps ),
						(long long)m_receiverState.m_mapReliableStreamGaps.begin()->first, (long long)m_receiverState.m_mapReliableStreamGaps.begin()->second,
						(long long)m_receiverState.m_mapReliableStreamGaps.rbegin()->first, (long long)m_receiverState.m_mapReliableStreamGaps.rbegin()->second,
						(long long)nSegBegin, (long long)nSegEnd
					);
					return false;  // DO NOT ACK THIS PACKET
				}
			}

			// Add a gap
			m_receiverState.m_mapReliableStreamGaps[ nExpectNextStreamPos ] = nSegBegin;
		}
		m_receiverState.m_bufReliableStream.resize( size_t( cbNewSize ) );
	}

	// If segment overlapped the existing buffer, we might need to discard the front
	// bit or discard a gap that was filled
	if ( nSegBegin < nExpectNextStreamPos )
	{

		// Check if the front bit has already been processed, then skip it
		if ( nSegBegin < m_receiverState.m_nReliableStreamPos )
		{
			int nSkip = m_receiverState.m_nReliableStreamPos - nSegBegin;
			cbSegmentSize -= nSkip;
			pSegmentData += nSkip;
			nSegBegin += nSkip;
		}
		Assert( nSegBegin < nSegEnd );

		// Check if this filled in one or more gaps (or made a hole in the middle!)
		if ( !m_receiverState.m_mapReliableStreamGaps.empty() )
		{
			auto gapFilled = m_receiverState.m_mapReliableStreamGaps.upper_bound( nSegBegin );
			if ( gapFilled != m_receiverState.m_mapReliableStreamGaps.begin() )
			{
				--gapFilled;
				Assert( gapFilled->first < gapFilled->second ); // Make sure we don't have degenerate/invalid gaps in our table
				Assert( gapFilled->first <= nSegBegin ); // Make sure we located the gap we think we located
				if ( gapFilled->second > nSegBegin ) // gap is not entirely before this segment
				{
					do {

						// Common case where we fill exactly at the start
						if ( nSegBegin == gapFilled->first )
						{
							if ( nSegEnd < gapFilled->second )
							{
								// We filled the first bit of the gap.  Chop off the front bit that we filled.
								// We cast away const here because we know that we aren't violating the ordering constraints
								const_cast<int64&>( gapFilled->first ) = nSegEnd;
								break;
							}

							// Filled the whole gap.
							// Erase, and move forward in case this also fills more gaps
							// !SPEED! Since exactly filing the gap should be common, we might
							// check specifically for that case and early out here.
							gapFilled = m_receiverState.m_mapReliableStreamGaps.erase( gapFilled );
						}
						else if ( nSegEnd >= gapFilled->second )
						{
							// Chop off the end of the gap
							Assert( nSegBegin < gapFilled->second );
							gapFilled->second = nSegBegin;

							// And maybe subsequent gaps!
							++gapFilled;
						}
						else
						{
							// We are fragmenting.
							Assert( nSegBegin > gapFilled->first );
							Assert( nSegEnd < gapFilled->second );

							// Protect against malicious sender.  A good sender will
							// fill the gaps in stream position order and not fragment
							// like this
							if ( len( m_receiverState.m_mapReliableStreamGaps ) >= k_nMaxReliableStreamGaps_Fragment )
							{
								// Stop processing the packet, and don't ack it
								SpewWarningRateLimited( usecNow, "[%s] decode pkt %lld abort.  Reliable stream already has %d fragments, first is [%lld,%lld), last is [%lld,%lld).  We don't want to fragment [%lld,%lld) with new segment [%lld,%lld)\n",
									GetDescription(),
									(long long)nPktNum,
									len( m_receiverState.m_mapReliableStreamGaps ),
									(long long)m_receiverState.m_mapReliableStreamGaps.begin()->first, (long long)m_receiverState.m_mapReliableStreamGaps.begin()->second,
									(long long)m_receiverState.m_mapReliableStreamGaps.rbegin()->first, (long long)m_receiverState.m_mapReliableStreamGaps.rbegin()->second,
									(long long)gapFilled->first, (long long)gapFilled->second,
									(long long)nSegBegin, (long long)nSegEnd
								);
								return false;  // DO NOT ACK THIS PACKET
							}

							// Save bounds of the right side
							int64 nRightHandBegin = nSegEnd;
							int64 nRightHandEnd = gapFilled->second;

							// Truncate the left side
							gapFilled->second = nSegBegin;

							// Add the right hand gap
							m_receiverState.m_mapReliableStreamGaps[ nRightHandBegin ] = nRightHandEnd;

							// And we know that we cannot possible have covered any more gaps
							break;
						}

						// In some rare cases we might fill more than one gap with a single segment.
						// So keep searching forward.
					} while ( gapFilled != m_receiverState.m_mapReliableStreamGaps.end() && gapFilled->first < nSegEnd );
				}
			}
		}
	}

	// Copy the data into the buffer.
	// It might be redundant, but if so, we aren't going to take the
	// time to figure that out.
	int nBufOffset = nSegBegin - m_receiverState.m_nReliableStreamPos;
	Assert( nBufOffset >= 0 );
	Assert( nBufOffset+cbSegmentSize <= len( m_receiverState.m_bufReliableStream ) );
	memcpy( &m_receiverState.m_bufReliableStream[nBufOffset], pSegmentData, cbSegmentSize );

	// Figure out how many valid bytes are at the head of the buffer
	int nNumReliableBytes;
	if ( m_receiverState.m_mapReliableStreamGaps.empty() )
	{
		nNumReliableBytes = len( m_receiverState.m_bufReliableStream );
	}
	else
	{
		auto firstGap = m_receiverState.m_mapReliableStreamGaps.begin();
		Assert( firstGap->first >= m_receiverState.m_nReliableStreamPos );
		if ( firstGap->first < nSegBegin )
		{
			// There's gap in front of us, and therefore if we didn't have
			// a complete reliable message before, we don't have one now.
			Assert( firstGap->second <= nSegBegin );
			return true;
		}

		// We do have a gap, but it's somewhere after this segment.
		Assert( firstGap->first >= nSegEnd );
		nNumReliableBytes = firstGap->first - m_receiverState.m_nReliableStreamPos;
		Assert( nNumReliableBytes > 0 );
		Assert( nNumReliableBytes < len( m_receiverState.m_bufReliableStream ) ); // The last byte in the buffer should always be valid!
	}
	Assert( nNumReliableBytes > 0 );

	// OK, now dispatch as many reliable messages as are now available
	do
	{

		// OK, if we get here, we have some data.  Attempt to decode a reliable message.
		// NOTE: If the message is really big, we will end up doing this parsing work
		// each time we get a new packet.  We could cache off the result if we find out
		// that it's worth while.  It should be pretty fast, though, so let's keep the
		// code simple until we know that it's worthwhile.
		uint8 *pReliableStart = &m_receiverState.m_bufReliableStream[0];
		uint8 *pReliableDecode = pReliableStart;
		uint8 *pReliableEnd = pReliableDecode + nNumReliableBytes;

		// Spew
		SpewDebugGroup( nLogLevelPacketDecode, "[%s]   decode pkt %lld valid reliable bytes = %d [%lld,%lld)\n",
			GetDescription(),
			(long long)nPktNum, nNumReliableBytes,
			(long long)m_receiverState.m_nReliableStreamPos,
			(long long)( m_receiverState.m_nReliableStreamPos + nNumReliableBytes ) );

		// Sanity check that we have a valid header byte.
		uint8 nHeaderByte = *(pReliableDecode++);
		if ( nHeaderByte & 0x80 )
		{
			ConnectionState_ProblemDetectedLocally( k_ESteamNetConnectionEnd_Misc_InternalError, "Invalid reliable message header byte 0x%02x", nHeaderByte );
			return false;
		}

		// Parse the message number
		int64 nMsgNum = m_receiverState.m_nLastRecvReliableMsgNum;
		if ( nHeaderByte & 0x40 )
		{
			uint64 nOffset;
			pReliableDecode = DeserializeVarInt( pReliableDecode, pReliableEnd, nOffset );
			if ( pReliableDecode == nullptr )
			{
				// We haven't received all of the message
				return true; // Packet OK and can be acked.
			}

			nMsgNum += nOffset;

			// Sanity check against a HUGE jump in the message number.
			// This is almost certainly bogus.  (OKOK, yes it is theoretically
			// possible.  But for now while this thing is still under development,
			// most likely it's a bug.  Eventually we can lessen these to handle
			// the case where the app decides to send literally a million unreliable
			// messages in between reliable messages.  The second condition is probably
			// legit, though.)
			if ( nOffset > 1000000 || nMsgNum > m_receiverState.m_nHighestSeenMsgNum+10000 )
			{
				ConnectionState_ProblemDetectedLocally( k_ESteamNetConnectionEnd_Misc_InternalError,
					"Reliable message number lurch.  Last reliable %lld, offset %llu, highest seen %lld",
					(long long)m_receiverState.m_nLastRecvReliableMsgNum, (unsigned long long)nOffset,
					(long long)m_receiverState.m_nHighestSeenMsgNum );
				return false;
			}
		}
		else
		{
			++nMsgNum;
		}

		// Check for updating highest message number seen, so we know how to interpret
		// message numbers from the sender with only the lowest N bits present.
		// And yes, we want to do this even if we end up not processing the entire message
		if ( nMsgNum > m_receiverState.m_nHighestSeenMsgNum )
			m_receiverState.m_nHighestSeenMsgNum = nMsgNum;

		// Parse message size.
		int cbMsgSize = nHeaderByte&0x1f;
		if ( nHeaderByte & 0x20 )
		{
			uint64 nMsgSizeUpperBits;
			pReliableDecode = DeserializeVarInt( pReliableDecode, pReliableEnd, nMsgSizeUpperBits );
			if ( pReliableDecode == nullptr )
			{
				// We haven't received all of the message
				return true; // Packet OK and can be acked.
			}

			// Sanity check size.  Note that we do this check before we shift,
			// to protect against overflow.
			// (Although DeserializeVarInt doesn't detect overflow...)
			if ( nMsgSizeUpperBits > (uint64)k_cbMaxMessageSizeRecv<<5 )
			{
				ConnectionState_ProblemDetectedLocally( k_ESteamNetConnectionEnd_Misc_InternalError,
					"Reliable message size too large.  (%llu<<5 + %d)",
					(unsigned long long)nMsgSizeUpperBits, cbMsgSize );
				return false;
			}

			// Compute total size, and check it again
			cbMsgSize += int( nMsgSizeUpperBits<<5 );
			if ( cbMsgSize > k_cbMaxMessageSizeRecv )
			{
				ConnectionState_ProblemDetectedLocally( k_ESteamNetConnectionEnd_Misc_InternalError,
					"Reliable message size %d too large.", cbMsgSize );
				return false;
			}
		}

		// Do we have the full thing?
		if ( pReliableDecode+cbMsgSize > pReliableEnd )
		{
			// Ouch, we did all that work and still don't have the whole message.
			return true; // packet is OK, can be acked, and continue processing it
		}

		// We have a full message!  Queue it
		if ( !ReceivedMessage( pReliableDecode, cbMsgSize, nMsgNum, k_nSteamNetworkingSend_Reliable, usecNow ) )
			return false; // Weird failure.  Most graceful response is to not ack this packet, and maybe we will work next on retry.
		pReliableDecode += cbMsgSize;
		int cbStreamConsumed = pReliableDecode-pReliableStart;

		// Advance bookkeeping
		m_receiverState.m_nLastRecvReliableMsgNum = nMsgNum;
		m_receiverState.m_nReliableStreamPos += cbStreamConsumed;

		// Remove the data from the from the front of the buffer
		pop_from_front( m_receiverState.m_bufReliableStream, cbStreamConsumed );

		// We might have more in the stream that is ready to dispatch right now.
		nNumReliableBytes -= cbStreamConsumed;
	} while ( nNumReliableBytes > 0 );

	return true; // packet is OK, can be acked, and continue processing it
}
bool CSteamNetworkConnectionBase::SNP_ReceiveReliableSegment( int64 nPktNum, int64 nSegBegin, const uint8 *pSegmentData, int cbSegmentSize, SteamNetworkingMicroseconds usecNow )
{
	int nLogLevelPacketDecode = m_connectionConfig.m_LogLevel_PacketDecode.Get();

	 
	int64 nSegEnd = nSegBegin + cbSegmentSize;

	 
	SpewVerboseGroup( nLogLevelPacketDecode, "[%s]   decode pkt %lld reliable range [%lld,%lld)\n",
		GetDescription(),
		(long long)nPktNum,
		(long long)nSegBegin, (long long)nSegEnd );

	 
	Assert( cbSegmentSize >= 0 );
	if ( cbSegmentSize <= 0 )
	{
		 
		SpewWarningRateLimited( usecNow, "[%s] decode pkt %lld empty reliable segment?\n",
			GetDescription(),
			(long long)nPktNum );
		return true;
	}

	 
	if ( GetState() != k_ESteamNetworkingConnectionState_Connected )
	{
		SpewVerboseGroup( nLogLevelPacketDecode, "[%s]   discarding pkt %lld [%lld,%lld) as connection is in state %d\n",
			GetDescription(),
			(long long)nPktNum,
			(long long)nSegBegin, (long long)nSegEnd,
			(int)GetState() );
		return true;
	}

	 
	 
	if ( nSegEnd <= m_receiverState.m_nReliableStreamPos )
		return true;

	 
	 
	 

	 
	const int64 nExpectNextStreamPos = m_receiverState.m_nReliableStreamPos + len( m_receiverState.m_bufReliableStream );

	 
	if ( nSegEnd > nExpectNextStreamPos )
	{
		int64 cbNewSize = nSegEnd - m_receiverState.m_nReliableStreamPos;
		Assert( cbNewSize > len( m_receiverState.m_bufReliableStream ) );

		 
		 
		 
		 
		 
		if ( cbNewSize > k_cbMaxBufferedReceiveReliableData )
		{
			 
			 
			 
			SpewWarningRateLimited( usecNow, "[%s] decode pkt %lld abort.  %lld bytes reliable data buffered [%lld-%lld), new size would be %lld to %lld\n",
				GetDescription(),
				(long long)nPktNum,
				(long long)m_receiverState.m_bufReliableStream.size(),
				(long long)m_receiverState.m_nReliableStreamPos,
				(long long)( m_receiverState.m_nReliableStreamPos + m_receiverState.m_bufReliableStream.size() ),
				(long long)cbNewSize, (long long)nSegEnd
			);
			return false;   
		}

		 
		if ( nSegBegin > nExpectNextStreamPos )
		{
			if ( !m_receiverState.m_mapReliableStreamGaps.empty() )
			{

				 
				 
				 
				Assert( m_receiverState.m_mapReliableStreamGaps.rbegin()->second < nExpectNextStreamPos );

				 
				if ( len( m_receiverState.m_mapReliableStreamGaps ) >= k_nMaxReliableStreamGaps_Extend )
				{
					 
					 
					 
					SpewWarningRateLimited( usecNow, "[%s] decode pkt %lld abort.  Reliable stream already has %d fragments, first is [%lld,%lld), last is [%lld,%lld), new segment is [%lld,%lld)\n",
						GetDescription(),
						(long long)nPktNum,
						len( m_receiverState.m_mapReliableStreamGaps ),
						(long long)m_receiverState.m_mapReliableStreamGaps.begin()->first, (long long)m_receiverState.m_mapReliableStreamGaps.begin()->second,
						(long long)m_receiverState.m_mapReliableStreamGaps.rbegin()->first, (long long)m_receiverState.m_mapReliableStreamGaps.rbegin()->second,
						(long long)nSegBegin, (long long)nSegEnd
					);
					return false;   
				}
			}

			 
			m_receiverState.m_mapReliableStreamGaps[ nExpectNextStreamPos ] = nSegBegin;
		}
		m_receiverState.m_bufReliableStream.resize( size_t( cbNewSize ) );
	}

	 
	 
	if ( nSegBegin < nExpectNextStreamPos )
	{

		 
		if ( nSegBegin < m_receiverState.m_nReliableStreamPos )
		{
			int nSkip = m_receiverState.m_nReliableStreamPos - nSegBegin;
			cbSegmentSize -= nSkip;
			pSegmentData += nSkip;
			nSegBegin += nSkip;
		}
		Assert( nSegBegin < nSegEnd );

		 
		if ( !m_receiverState.m_mapReliableStreamGaps.empty() )
		{
			auto gapFilled = m_receiverState.m_mapReliableStreamGaps.upper_bound( nSegBegin );
			if ( gapFilled != m_receiverState.m_mapReliableStreamGaps.begin() )
			{
				--gapFilled;
				Assert( gapFilled->first < gapFilled->second );  
				Assert( gapFilled->first <= nSegBegin );  
				if ( gapFilled->second > nSegBegin )  
				{
					do {

						 
						if ( nSegBegin == gapFilled->first )
						{
							if ( nSegEnd < gapFilled->second )
							{
								 
								 
								const_cast<int64&>( gapFilled->first ) = nSegEnd;
								break;
							}

							 
							 
							 
							 
							gapFilled = m_receiverState.m_mapReliableStreamGaps.erase( gapFilled );
						}
						else if ( nSegEnd >= gapFilled->second )
						{
							 
							Assert( nSegBegin < gapFilled->second );
							gapFilled->second = nSegBegin;

							 
							++gapFilled;
						}
						else
						{
							 
							Assert( nSegBegin > gapFilled->first );
							Assert( nSegEnd < gapFilled->second );

							 
							 
							 
							if ( len( m_receiverState.m_mapReliableStreamGaps ) >= k_nMaxReliableStreamGaps_Fragment )
							{
								 
								SpewWarningRateLimited( usecNow, "[%s] decode pkt %lld abort.  Reliable stream already has %d fragments, first is [%lld,%lld), last is [%lld,%lld).  We don't want to fragment [%lld,%lld) with new segment [%lld,%lld)\n",
									GetDescription(),
									(long long)nPktNum,
									len( m_receiverState.m_mapReliableStreamGaps ),
									(long long)m_receiverState.m_mapReliableStreamGaps.begin()->first, (long long)m_receiverState.m_mapReliableStreamGaps.begin()->second,
									(long long)m_receiverState.m_mapReliableStreamGaps.rbegin()->first, (long long)m_receiverState.m_mapReliableStreamGaps.rbegin()->second,
									(long long)gapFilled->first, (long long)gapFilled->second,
									(long long)nSegBegin, (long long)nSegEnd
								);
								return false;   
							}

							 
							int64 nRightHandBegin = nSegEnd;
							int64 nRightHandEnd = gapFilled->second;

							 
							gapFilled->second = nSegBegin;

							 
							m_receiverState.m_mapReliableStreamGaps[ nRightHandBegin ] = nRightHandEnd;

							 
							break;
						}

						 
						 
					} while ( gapFilled != m_receiverState.m_mapReliableStreamGaps.end() && gapFilled->first < nSegEnd );
				}
			}
		}
	}

	 
	 
	 
	int nBufOffset = nSegBegin - m_receiverState.m_nReliableStreamPos;
	Assert( nBufOffset >= 0 );
	Assert( nBufOffset+cbSegmentSize <= len( m_receiverState.m_bufReliableStream ) );
	memcpy( &m_receiverState.m_bufReliableStream[nBufOffset], pSegmentData, cbSegmentSize );

	 
	int nNumReliableBytes;
	if ( m_receiverState.m_mapReliableStreamGaps.empty() )
	{
		nNumReliableBytes = len( m_receiverState.m_bufReliableStream );
	}
	else
	{
		auto firstGap = m_receiverState.m_mapReliableStreamGaps.begin();
		Assert( firstGap->first >= m_receiverState.m_nReliableStreamPos );
		if ( firstGap->first < nSegBegin )
		{
			 
			 
			Assert( firstGap->second <= nSegBegin );
			return true;
		}

		 
		Assert( firstGap->first >= nSegEnd );
		nNumReliableBytes = firstGap->first - m_receiverState.m_nReliableStreamPos;
		Assert( nNumReliableBytes > 0 );
		Assert( nNumReliableBytes < len( m_receiverState.m_bufReliableStream ) );  
	}
	Assert( nNumReliableBytes > 0 );

	 
	do
	{

		 
		 
		 
		 
		 
		uint8 *pReliableStart = &m_receiverState.m_bufReliableStream[0];
		uint8 *pReliableDecode = pReliableStart;
		uint8 *pReliableEnd = pReliableDecode + nNumReliableBytes;

		 
		SpewDebugGroup( nLogLevelPacketDecode, "[%s]   decode pkt %lld valid reliable bytes = %d [%lld,%lld)\n",
			GetDescription(),
			(long long)nPktNum, nNumReliableBytes,
			(long long)m_receiverState.m_nReliableStreamPos,
			(long long)( m_receiverState.m_nReliableStreamPos + nNumReliableBytes ) );

		 
		uint8 nHeaderByte = *(pReliableDecode++);
		if ( nHeaderByte & 0x80 )
		{
			ConnectionState_ProblemDetectedLocally( k_ESteamNetConnectionEnd_Misc_InternalError, "Invalid reliable message header byte 0x%02x", nHeaderByte );
			return false;
		}

		 
		int64 nMsgNum = m_receiverState.m_nLastRecvReliableMsgNum;
		if ( nHeaderByte & 0x40 )
		{
			uint64 nOffset;
			pReliableDecode = DeserializeVarInt( pReliableDecode, pReliableEnd, nOffset );
			if ( pReliableDecode == nullptr )
			{
				 
				return true;  
			}

			nMsgNum += nOffset;

			 
			 
			 
			 
			 
			 
			 
			if ( nOffset > 1000000 || nMsgNum > m_receiverState.m_nHighestSeenMsgNum+10000 )
			{
				ConnectionState_ProblemDetectedLocally( k_ESteamNetConnectionEnd_Misc_InternalError,
					"Reliable message number lurch.  Last reliable %lld, offset %llu, highest seen %lld",
					(long long)m_receiverState.m_nLastRecvReliableMsgNum, (unsigned long long)nOffset,
					(long long)m_receiverState.m_nHighestSeenMsgNum );
				return false;
			}
		}
		else
		{
			++nMsgNum;
		}

		 
		 
		 
		if ( nMsgNum > m_receiverState.m_nHighestSeenMsgNum )
			m_receiverState.m_nHighestSeenMsgNum = nMsgNum;

		 
		int cbMsgSize = nHeaderByte&0x1f;
		if ( nHeaderByte & 0x20 )
		{
			uint64 nMsgSizeUpperBits;
			pReliableDecode = DeserializeVarInt( pReliableDecode, pReliableEnd, nMsgSizeUpperBits );
			if ( pReliableDecode == nullptr )
			{
				 
				return true;  
			}

			 
			 
			 
			if ( nMsgSizeUpperBits > (uint64)k_cbMaxMessageSizeRecv<<5 )
			{
				ConnectionState_ProblemDetectedLocally( k_ESteamNetConnectionEnd_Misc_InternalError,
					"Reliable message size too large.  (%llu<<5 + %d)",
					(unsigned long long)nMsgSizeUpperBits, cbMsgSize );
				return false;
			}

			 
			cbMsgSize += int( nMsgSizeUpperBits<<5 );
			if ( cbMsgSize > k_cbMaxMessageSizeRecv )
			{
				ConnectionState_ProblemDetectedLocally( k_ESteamNetConnectionEnd_Misc_InternalError,
					"Reliable message size %d too large.", cbMsgSize );
				return false;
			}
		}

		 
		if ( pReliableDecode+cbMsgSize > pReliableEnd )
		{
			 
			return true;  
		}

		 
		if ( !ReceivedMessage( pReliableDecode, cbMsgSize, nMsgNum, k_nSteamNetworkingSend_Reliable, usecNow ) )
			return false;  
		pReliableDecode += cbMsgSize;
		int cbStreamConsumed = pReliableDecode-pReliableStart;

		 
		m_receiverState.m_nLastRecvReliableMsgNum = nMsgNum;
		m_receiverState.m_nReliableStreamPos += cbStreamConsumed;

		 
		pop_from_front( m_receiverState.m_bufReliableStream, cbStreamConsumed );

		 
		nNumReliableBytes -= cbStreamConsumed;
	} while ( nNumReliableBytes > 0 );

	return true;  
}