Developers.Code.CG1PacketBuffer

From Shareaza Wiki
Jump to navigation Jump to search

CG1PacketBuffer and ]]CG1PacketBufferType[[

Two classes are defined in PacketBuffer.h: CG1PacketBuffer and CG1PacketBufferType. Here's what they do:

CG1PacketBuffer holds an array of 9 CG1PacketBufferType objects. There are 9 because that's one for each type of packet - one for Ping packets, one for Pong packets, and so on.

CG1PacketBufferType holds an array of 64 pointers to packets. They are all of the same type, and all are ready to send to the remote computer. The object also holds an array of 64 times. This is the time that each packet will expire, meaning we don't have to send it to the remote computer at all anymore.

The Outbound Packet Buffer

The CG1Neighbour class includes a CG1PacketBuffer object:

<source lang="c"> class CG1Neighbour : public CNeighbour {

   ...
   // Holds the packets we are going to send to the remote computer
   CG1PacketBuffer* m_pOutbound;

</source>

And the CG1Neighbour constructor sets it up:

<source lang="c"> m_pOutbound = new CG1PacketBuffer( m_pZOutput ? m_pZOutput : m_pOutput ); </source>

From that point, the two CG1Neighbour methods that use the outbound packet buffer are Send and OnWrite. Send takes a packet and puts it into the packet buffer.

<source lang="c"> BOOL CG1Neighbour::Send(CPacket* pPacket, BOOL bRelease, BOOL bBuffered) {

   ...
   m_pOutbound->Add( pPacketG1, bBuffered );

</source>

This uses the packet buffer's Add method.

OnWrite takes all the packets out of the buffer and sends them into the socket.

<source lang="c"> BOOL CG1Neighbour::OnWrite() {

   ...
   // Loop while the output buffer is empty, but the outbound packet buffer still has packets to send
   while ( ... && m_pOutbound->m_nTotal > 0 )
   {
       // Get a packet from the outbound packet buffer
       CG1Packet* pPacket = m_pOutbound->GetPacketToSend( nExpire );
       if ( ! pPacket ) break;
       // Write the packet into the output buffer, and release it
       pPacket->ToBuffer( pOutput );

</source>

This uses the packet buffer's GetPacketToSend method.

Going straight to the buffer

Look at the code with Send calling Add again:

<source lang="c"> BOOL CG1Neighbour::Send(CPacket* pPacket, BOOL bRelease, BOOL bBuffered) {

   ...
   m_pOutbound->Add( pPacketG1, bBuffered );

</source>

One of the arguments, bBuffered, gets passed from the caller all the way into Add. I can't figure out who calls Send, and don't know if bBuffered is always TRUE, always FALSE, or a mixture of the two. But, here's what Add does with it.

<source lang="c"> void CG1PacketBuffer::Add(CG1Packet* pPacket, BOOL bBuffered) {

   // If the packet isn't buffered yet, or doesn't have a type like ping or pong yet
   if ( ! bBuffered || ! pPacket->m_nTypeIndex )
   {
       // Write the packet directly into the buffer, and we're done
       pPacket->ToBuffer( m_pBuffer );
       return;
   }

</source>

if bBuffered is FALSE, Add writes the packet into the output buffer right now, and returns! The 9 arrays of 64 packets each are completely avoided. It's possible that bBuffered is always FALSE, and all the code in PacketBuffer.h and PacketBuffer.cpp isn't being used at all.

How CG1PacketBufferType fills its array

A CG1PacketBufferType object has an array of 64 packets. They are all of the same type, like ping or pong. Here are the key members of the class:

<source lang="c"> class CG1PacketBufferType {

   // Two arrays that hold information about 64 Gnutella packets
   CG1Packet** m_pBuffer; // This is a pointer to an array of 64 pointers to Gnutella packets
   DWORD*      m_pTime;   // The time each packet will expire, which is 1 minute after it is added
   // Indices and counts for those arrays
   int m_nHead;     // The index in the array where we added information about a packet most recently
   int m_nCount;    // The number of spots in the array filled with a packet pointer and time
   int m_nCapacity; // The size of the arrays we allocated from settings, 64 by default

</source>

There are actually 2 arrays. They both hold information about 64 packets - you can think of them being side-by-side. The first, m_pBuffer, is the array of 64 packet pointers. The second, m_pTime is the tick count when each of these packets will expire.

Three indices and counts manage these arrays. m_nHead is the array index where Add last added a packet. m_nCount is the number of packets in the array, 0 through 64. m_nCapacity is 64, the size of the array.

By default, Shareaza settings will make m_nCapacity 64. The CG1PacketBufferType initializes m_nCount and m_nHead to 0. What happens next is a little complicated. In Add, the computer runs these lines of code:

<source lang="c"> // If m_nHead is 0, add m_nCapacity to it, and also decrement it either way if ( ! m_nHead-- ) m_nHead += m_nCapacity; // So, the first time Add runs, m_nHead will go from 0 to 63

// Record there will be one more packet stored here m_nCount++; // So, the first time Add runs, m_nCount will go from 0 to 1 </source>

Packets are always added at the m_nHead index. When the first packet is added, count is 1 and head is 63. For the second packet, count is 2 and head is 62. Count keeps going up and head keeps moving back until the 64th packet is added at the start of the array: count 64 and head 0.

The array has 64 places. The first packet goes in the last place, which has index 63. The second packet goes in the second to last place, which has index 62. This process of filling the array from the end to the start continues until the 64th packet goes in the first place, which has index 0.

The 65th time Add is called, the lines of code run again. Now, the arrays are full. Before the code above runs, this if statement is executed:

<source lang="c"> // If the arrays are full if ( m_nCount == m_nCapacity ) // There isn't room for another packet in the arrays { m_nCount--; m_pBuffer[ ( m_nHead + m_nCount ) % m_nCapacity ]->Release(); bFresh = FALSE; } </source>

What happens when Shareaza wants to add the 65th packet? The Add method returns the the end of the array, releases the packet there, and puts the new one there instead. The packet after that overwrites the second to last place. So, Add keeps sweeping up the array, from end to start.