Download improvements - Updated 19th January 2007

Before you read the code on this page please note: I have formatted this code in HTML for this page,converting all the >< characters etc, in doing so I may have introduced errors, changed a < for a &gt;. So if you want to use this code, you may want to copy it from my source code! A version of this code was posted to the eMule forum in this thread, (you need to be a member to read it) other code is discussed in this thread which you may also find interesting. For more on Netfinity's Dynamic Block Requests and more on ideas added to this code check this thread.


In DownClient.cpp
The code added to CreateBlockRequests() in conjunction with a helper function SwitchBlocks() in PartFile.cpp coverts a single block request into its equivelnt 3 block request for none eMule clients. This removes the need for the eMule only clause for single block requests that exists in the official.
void CUpDownClient::CreateBlockRequests(int iMaxBlocks)
{
  ASSERT( iMaxBlocks >= 1 /*&& iMaxBlocks <= 3*/ );
  if(m_DownloadBlocks_list.IsEmpty())
    {
      uint16 count;
      //START block splitting code since 1.5a only split blocks for none eMule clients save overhead
      if(iMaxBlocks==1 && !m_PendingBlocks_list.GetCount() && (!IsEmuleClient() || m_byCompatibleClient!=0))
        {//Block count reduction has activated convert the 1 block request to 3 blocks
        count = iMaxBlocks;
        Requested_Block_Struct** toadd = new Requested_Block_Struct*[1];
        if(reqfile->GetNextRequestedBlock(this,toadd,&count))
          {//splits 1 block into 3. generally 1 180Kb block into 3 60Kb blocks
            Requested_Block_Struct* block_one = new Requested_Block_Struct;
            Requested_Block_Struct* block_two = new Requested_Block_Struct;
            Requested_Block_Struct* block_thr = new Requested_Block_Struct;
            uint32 thirdblocksize = (toadd[0]->EndOffset - toadd[0]->StartOffset)/3;
            /*First Block*/
            block_one->StartOffset = toadd[0]->StartOffset;
            block_one->EndOffset = toadd[0]->StartOffset + thirdblocksize;
            md4cpy(block_one->FileID, toadd[0]->FileID);
            block_one->transferred = 0;
            /*Second Block*/
            block_two->StartOffset = block_one->EndOffset + 1;
            block_two->EndOffset = block_two->StartOffset + thirdblocksize;
            md4cpy(block_two->FileID, toadd[0]->FileID);
            block_two->transferred = 0;
            /*Third Block*/
            block_thr->StartOffset = block_two->EndOffset + 1;
            block_thr->EndOffset = toadd[0]->EndOffset;
            md4cpy( block_thr->FileID, toadd[0]->FileID);
            block_thr->transferred = 0;
            //add the 3 blocks
            reqfile->SwitchBlocks( block_one, block_two, block_thr, toadd );
            delete[] toadd;
            m_DownloadBlocks_list.AddTail( block_one );
            m_DownloadBlocks_list.AddTail( block_two );
            m_DownloadBlocks_list.AddTail( block_thr );
            while(m_PendingBlocks_list.GetCount() < 3 && !m_DownloadBlocks_list.IsEmpty())
                {
                  Pending_Block_Struct* pblock = new Pending_Block_Struct;
                  pblock->block = m_DownloadBlocks_list.RemoveHead();
                  m_PendingBlocks_list.AddTail(pblock);
                  }
            return;
            } else
                    {
                      delete[] toadd;
                        return;
                        }
    }//END block splitting code

  if(iMaxBlocks > m_PendingBlocks_list.GetCount()) count = iMaxBlocks - m_PendingBlocks_list.GetCount();
    else count = 0;
 
  Requested_Block_Struct** toadd = new Requested_Block_Struct*[count];

  if(reqfile->GetNextRequestedBlock(this,toadd,&count))
  {
    for (int i = 0; i < count; i++)
    m_DownloadBlocks_list.AddTail(toadd[ i ]);
    }

delete[] toadd;
}

in DownClient.cpp void CUpDownClient::SendBlockRequests().
You can change this code in a few ways, the simplest below is after adding the block splitting code,
you just remove the eMule only clause from the one block request line,(in red). 
In my TK4 mod the code has all been replaced with a relative comaprison with a fix comparison fall back. See below

// prevent locking of too many blocks when we are on a slow (probably standby/trickle) slot
    int blockCount = 3;
     if(reqfile->GetFileSize()-reqfile->GetCompletedSize() <= PARTSIZE*4) {
         if(GetDownloadDatarate(912) < 600) {
            blockCount = 1;
          }  else if(IsEmuleClient() && m_byCompatibleClient==0 && GetDownloadDatarate() < 1200) {
                      blockCount = 2;
                    }
    }
	


in PartFile.h
 
public:
void SwitchBlocks(Requested_Block_Struct* block_one,Requested_Block_Struct* block_two,Requested_Block_Struct* block_thr,Requested_Block_Struct** toadd );


in PartFile.cpp 

void CPartFile::SwitchBlocks(Requested_Block_Struct* block_one,Requested_Block_Struct* block_two,Requested_Block_Struct* block_thr,Requested_Block_Struct** toadd )
{
    POSITION pos;
    POSITION posLast;
    //find the 1 full size block added
    posLast = pos = requestedblocks_list.GetTailPosition();
    Requested_Block_Struct* block = requestedblocks_list.GetNext(pos);
    if((toadd[0]->StartOffset == block->StartOffset && toadd[0]->EndOffset == block->EndOffset)
      {
        delete block; //delete full size block
        requestedblocks_list.RemoveAt(posLast); //remove it from the list
      } else
                {//RemoveBlockFromList() + delete block;
                  for(pos = requestedblocks_list.GetHeadPosition(); pos != NULL; )
                    {
                      posLast = pos;
                      Requested_Block_Struct* block = requestedblocks_list.GetNext(pos);
                      if((block->StartOffset <= toadd[0]->StartOffset && block->EndOffset >= toadd[0]->EndOffset)
                        {
                          delete block; //delete full size block
                          requestedblocks_list.RemoveAt(posLast);
                          }
                      }
                  }
        //add the 3 1/3 size blocks
        requestedblocks_list.AddTail(block_one);
        requestedblocks_list.AddTail(block_two);
        requestedblocks_list.AddTail(block_thr);
}

	
Alternative comparision code.


In Partfile.h

public:
bool    ReduceAllocatedBlocks(CUpDownClient* calling_source,uint16  m_PartAsked,UINT fixComparison);

In Partfile.cpp

/*TK4 Mod: Returns true when the calling source would not complete it's 3 blocks before 'total transfer rate
for part' would complete the part minus those three blocks.This helps prevent a slow source locking blocks at the end of a part
causing sources which may not have any other parts we need to stop transfering. It also helps maintain a higher cumulative download
speed at the end of file and sometimes,(where a source has no other parts we need); at the end of part.*/
bool CPartFile::ReduceAllocatedBlocks(CUpDownClient* calling_source,uint16  m_PartAsked, UINT fixComparison)
{
  // no 'part last asked for'
 In 46c
 if(m_PartAsked == 0xffff)
 {//improved for 1.5b
   if(calling_source->IsEmuleClient()) return true;
   else return false; //for hoard clients & to prevent fragmentation from 10k DBR
  }

 In 47a
 if(m_PartAsked == (uint16)-1)
  {//improved for 1.5b
   if(calling_source->IsEmuleClient()) return true;
   else return false; //for hoard clients & to prevent fragmentation from 10k DBR
  }
 
  //Quick Check, if there is only one source overall return no 'part last asked for'
  if(m_downloadingSourceList.GetCount()<2)
    {//if this source is the only source don't let it lock blocks if its too slow
      if(calling_source->GetDownloadDatarate() < fixComparison) return true;
        else return false;
    } 
 
  uint16 sourcecount = 0;
  uint32 remainingdata = 0;
  uint32 otherstransferrate = 1;// a value of 1 to prevent 'divide by zero' (saves 1 + otherstransferrate later)
  const uint32 threeblocks = EMBLOCKSIZE * 3;

  /*Calculate total transfering sources for this part and overall transfer speed.*/
  for(POSITION pos = m_downloadingSourceList.GetHeadPosition(); pos != NULL;)
    {
      //get next downloading source - Bug Fix by WiZaRd - version 2.0c
      CUpDownClient* cur_src = m_downloadingSourceList.GetNext(pos);
      //is this source sending data for the same part as the calling source but is not the calling source
      if(cur_src->m_lastPartAsked==m_PartAsked && cur_src!=calling_source)
        {
          sourcecount++;//increment sources for this part
          otherstransferrate += cur_src->GetDownloadDatarate();//add this clients transfer rate to the total
        }
      }
 
  //if calling source is the only currently transfering source for this part use fixed reference
  if(sourcecount < 1)
    {//if this source is the only source don't let it lock blocks if its too slow
      if(calling_source->GetDownloadDatarate() < fixComparison) return true;
        else return false;
    }

  /* Calculate how much of the part is left in bytes (based on code from GetNextRequestedBlock() )*/
  // Offsets of 'this' chunk
 In 46c ->const uint32 uStart  = m_PartAsked * PARTSIZE;
 In 46c ->const uint32 uEnd    = (GetFileSize() - 1 < (uStart + PARTSIZE - 1)) ? GetFileSize() - 1 : (uStart + PARTSIZE - 1);
 
 In 47a ->const uint64 uStart  = (uint64)m_PartAsked * PARTSIZE;
 In 47a ->const uint64 uEnd    = (GetFileSize() - (uint64)1 < (uStart + PARTSIZE - 1)) ? GetFileSize() - (uint64)1 : (uStart + PARTSIZE - 1);
  
  ASSERT( uStart <= uEnd );
  if(uStart >= uEnd) return false;

  //gets bytes remaining
  for(POSITION pos = gaplist.GetHeadPosition(); pos != NULL; )
    {
      const Gap_Struct* cur_gap = gaplist.GetNext(pos);
      //Check if Gap is into the limit
      if(cur_gap->start < uStart)
        {
          if(cur_gap->end > uStart && cur_gap->end < uEnd) remainingdata += cur_gap->end - uStart + 1;
            else if(cur_gap->end >= uEnd) return false;
          } else
                    if(cur_gap->start <= uEnd)
                      {
                        if(cur_gap->end < uEnd) remainingdata += cur_gap->end - cur_gap->start + 1;
                          else                  remainingdata += uEnd - cur_gap->start + 1;
                        }
      }

 
  if(threeblocks > remainingdata) return true;//keep up combined download speed for as long as possible
    else remainingdata -= threeblocks;            //remainingdata to equal the remaining data after this client gets allocated 3 blocks

  //if(3 * EMBLOCKSIZE / 'calling source speed' >= 'remaining incomplete part size - 3 blocks'/'total other sources transfer rate') return true
  //if((uint32)(threeblocks/(1 + calling_source->GetDownloadDatarate())) >= (uint32)(remainingdata/otherstransferrate)) return true;

  //return false;
  
  //Wizard's Optimize!
  return( (uint32)(threeblocks/(1 + calling_source->GetDownloadDatarate())) >= (uint32)(remainingdata/otherstransferrate) );
}

The code snippet below should add further part completion speed. In bool CPartFile::GetNextRequestedBlock() The highlighted additional code reduces the requested block size to 1/6. The purple code is only needed if you are using Netfinity's excellent Dynamic Block Requests code; it prevents this code working if Netfinity's code has already reduced the block size. Outside of DBR working range any source transfering at 400b/s or slower, or any new source requesting with less that a third of the part left will have a smaller request made of it. In the case of a new client it has to be an eMule client,(as hoard clients only give one block).Thanks to SiRoB and Netfinity for ideas which I used in writting this code. uint16 PartAsked = sender->m_lastPartAsked;//so we know if it's a new source later // Main loop uint16 newBlockCount = 0; while(newBlockCount != *count){ // Create a request block stucture if a chunk has been previously selected if(sender->m_lastPartAsked != (uint16)-1){ //Reduce end of part block size if(bytesPerRequest == EMBLOCKSIZE && ((sender->GetDownloadDatarate()==0 && sender->IsEmuleClient() && DataRemainingInPart(sender->m_lastPartAsked)<3072000) || (sender->GetDownloadDatarate()<400 && PartAsked!=(uint16)-1))) { bytesPerRequest = 30720; } Requested_Block_Struct* pBlock = new Requested_Block_Struct; if(GetNextEmptyBlockInPart(sender->m_lastPartAsked, pBlock, bytesPerRequest) == true){ // Keep a track of all pending requested blocks requestedblocks_list.AddTail(pBlock); // Update list of blocks to return newblocks[newBlockCount++] = pBlock; // Skip end of loop (=> CPU load) continue; } else


in DownClient.cpp void CUpDownClient::SendBlockRequests(). Add the changes below to intergrate the above comparison code and of course the 1 to 3 block conversion code. // prevent locking of too many blocks when we are on a slow (probably standby/trickle) slot int blockCount = 3; if( reqfile->ReduceAllocatedBlocks(this ,this->m_lastPartAsked, 784) ){ //near end of part with multiply sources, share the blocks out, slower sources have blockcount reduced first. blockCount = 1; } CreateBlockRequests(blockCount);