void CSteamNetworkConnectionBase::SNP_GatherAckBlocks( SNPAckSerializerHelper &helper, SteamNetworkingMicroseconds usecNow )
{
	helper.m_nBlocks = 0;
	helper.m_nBlocksNeedToAck = 0;

	// Fast case for no packet loss we need to ack, which will (hopefully!) be a common case
	int n = len( m_receiverState.m_mapPacketGaps ) - 1;
	if ( n <= 0 )
		return;

	// Let's not just flush the acks that are due right now.  Let's flush all of them
	// that will be due any time before we have the bandwidth to send the next packet.
	// (Assuming that we send the max packet size here.)
	SteamNetworkingMicroseconds usecSendAcksDueBefore = usecNow;
	SteamNetworkingMicroseconds usecTimeUntilNextPacket = SteamNetworkingMicroseconds( ( m_senderState.m_flTokenBucket - (float)m_cbMTUPacketSize ) / (float)m_senderState.m_n_x * -1e6 );
	if ( usecTimeUntilNextPacket > 0 )
		usecSendAcksDueBefore += usecTimeUntilNextPacket;

	m_receiverState.DebugCheckPackGapMap();

	n = std::min( (int)helper.k_nMaxBlocks, n );
	auto itNext = m_receiverState.m_mapPacketGaps.begin();

	int cbEncodedSize = helper.k_cbHeaderSize;
	while ( n > 0 )
	{
		--n;
		auto itCur = itNext;
		++itNext;

		Assert( itCur->first < itCur->second.m_nEnd );

		// Do we need to report on this block now?
		bool bNeedToReport = ( itNext->second.m_usecWhenAckPrior <= usecSendAcksDueBefore );

		// Should we wait to NACK this?
		if ( itCur == m_receiverState.m_itPendingNack )
		{

			// Wait to NACK this?
			if ( !bNeedToReport )
			{
				if ( usecNow < itCur->second.m_usecWhenOKToNack )
					break;
				bNeedToReport = true;
			}

			// Go ahead and NACK it.  If the packet arrives, we will use it.
			// But our NACK may cause the sender to retransmit.
			++m_receiverState.m_itPendingNack;
		}

		SNPAckSerializerHelper::Block &block = helper.m_arBlocks[ helper.m_nBlocks ];
		block.m_nNack = uint32( itCur->second.m_nEnd - itCur->first );

		int64 nAckEnd;
		SteamNetworkingMicroseconds usecWhenSentLast;
		if ( n == 0 )
		{
			// itNext should be the sentinel
			Assert( itNext->first == INT64_MAX );
			nAckEnd = m_statsEndToEnd.m_nMaxRecvPktNum+1;
			usecWhenSentLast = m_statsEndToEnd.m_usecTimeLastRecvSeq;
		}
		else
		{
			nAckEnd = itNext->first;
			usecWhenSentLast = itNext->second.m_usecWhenReceivedPktBefore;
		}
		Assert( itCur->second.m_nEnd < nAckEnd );
		block.m_nAck = uint32( nAckEnd - itCur->second.m_nEnd );

		block.m_nLatestPktNum = uint32( nAckEnd-1 );
		block.m_nEncodedTimeSinceLatestPktNum = SNPAckSerializerHelper::EncodeTimeSince( usecNow, usecWhenSentLast );

		// When we encode 7+ blocks, the header grows by one byte
		// to store an explicit count
		if ( helper.m_nBlocks == 6 )
			++cbEncodedSize;

		// This block
		++cbEncodedSize;
		if ( block.m_nAck > 7 )
			cbEncodedSize += VarIntSerializedSize( block.m_nAck>>3 );
		if ( block.m_nNack > 7 )
			cbEncodedSize += VarIntSerializedSize( block.m_nNack>>3 );
		block.m_cbTotalEncodedSize = cbEncodedSize;

		// FIXME Here if the caller knows they are working with limited space,
		// they could tell us how much space they have and we could bail
		// if we already know we're over

		++helper.m_nBlocks;

		// Do we really need to try to flush the ack/nack for that block out now?
		if ( bNeedToReport )
			helper.m_nBlocksNeedToAck = helper.m_nBlocks;
	}
}