In extent to the changes provided below you also have to update the header files. In order to implement this you will need to know C++ as you have to do some coding yourself to adapt to the chunkselection mecanism used in your client.
DownloadClient.cpp - CUpDownClient::CreateBlockRequests
Just some changes to collect info about how much data we are waiting on to receive (requested blocks not yet processed) from the remote client.
Quote
{
ASSERT( iMaxBlocks >= 1 /*&& iMaxBlocks <= 3*/ );
// MOD BEGIN netfinity: Dynamic Block Requests
uint32 pendingBytes = 0;
uint32 transferredBytes = 0;
for (POSITION pos = m_PendingBlocks_list.GetHeadPosition(); pos != 0;){
Requested_Block_Struct* block = m_PendingBlocks_list.GetNext(pos)->block;
pendingBytes += (block->EndOffset - block->StartOffset + 1);
transferredBytes += block->transferred;
}
if(transferredBytes < pendingBytes)
pendingBytes -= transferredBytes;
else
pendingBytes = 0;
// MOD END netfinity
if (m_DownloadBlocks_list.IsEmpty())
{
uint16 count = iMaxBlocks - m_PendingBlocks_list.GetCount();
Requested_Block_Struct** toadd = new Requested_Block_Struct*[count];
if (reqfile->GetNextRequestedBlock(this,toadd,&count,pendingBytes)){ // netfinity: Dynamic Block Requests
for (int i = 0; i < count; i++)
m_DownloadBlocks_list.AddTail(toadd[ i]);
}
delete[] toadd;
}
while (m_PendingBlocks_list.GetCount() < iMaxBlocks && !m_DownloadBlocks_list.IsEmpty())
{
Pending_Block_Struct* pblock = new Pending_Block_Struct;
pblock->block = m_DownloadBlocks_list.RemoveHead();
m_PendingBlocks_list.AddTail(pblock);
}
}
Partfile.cpp - CPartFile::GetNextRequestedBlock
This part is completly rewritten and it's here the calculations are done.
Note, that part selection has moved to MOD_FindNextPart() function!
Quote
{
if (!(*count)) return false; // added as in 29a
if (!(sender->GetPartStatus())) return false; // added as in 29a
uint16 requestedCount = *count;
uint16 newblockcount = 0;
*count = 0;
// MOD BEGIN netfinity: Seed random generator
if (sender->m_lastPartAsked == 0xFFFF)
srand((unsigned int) sender + ::GetTickCount());
// MOD END netfinity
// MOD BEGIN netfinity: Check if an other part is to be chosen
if (sender->m_lastPartAsked == 0xFFFF || MOD_GetRequestedSizeInPart(sender->m_lastPartAsked) >= GetTotalGapSizeInPart(sender->m_lastPartAsked))
{
if (!MOD_FindNextPart(sender))
{
//AddDebugLogLine(false, _T("PartFile::GetNextRequestedBlock - No needed part"));
return false;
}
//AddDebugLogLine(false, _T("PartFile::GetNextRequestedBlock - New part chosen: part = %d"), sender->m_lastPartAsked);
}
// MOD END netfinity
// MOD BEGIN netfinity: Dynamic Block Requests - Calculate block request size
uint32 bytesToRequest = 3 * EMBLOCKSIZE;
uint32 bytesToDownloadInPart = GetTotalGapSizeInPart(sender->m_lastPartAsked) - MOD_GetRequestedSizeInPart(sender->m_lastPartAsked);
uint32 bytesToDownloadInFile = GetTotalGapSizeInPart(sender->m_lastPartAsked) - MOD_GetRequestedSizeInFile();
uint16 downloadingSourcesInPart = MOD_GetDownloadingSourcesInPart(sender->m_lastPartAsked, sender);
uint32 sourceDatarate = sender->GetDownloadDatarate();
uint32 fileDatarate = GetDatarate();
float rateFactor = 1.0;
if (fileDatarate > 0)
rateFactor = (float) sourceDatarate / (float) fileDatarate;
bytesToRequest = min(bytesToRequest, bytesToDownloadInFile * rateFactor / 2);
bytesToRequest = min(bytesToRequest, sourceDatarate * 60);
bytesToRequest = min(bytesToRequest, bytesToDownloadInPart / (downloadingSourcesInPart + 2));
if (sourceDatarate < 1024 || bytesToRequest < 10240)
bytesToRequest = 10240;
if (bytesToRequest <= pendingBytes)
{
//AddDebugLogLine(false, _T("PartFile::GetNextRequestedBlock - Request already large enough"));
return false;
}
bytesToRequest -= pendingBytes;
// Check if we should drop source to speed up file completion
//if (fileDatarate > 0 && downloadingSourcesInPart > 1 && sourceDatarate < 1024 && rateFactor * downloadingSourcesInPart < 0.3 && bytesToDownloadInFile / fileDatarate < 20)
//{
// AddDebugLogLine(false, _T("PartFile::GetNextRequestedBlock - Too slow"));
// return false;
//}
// MOD END netfinity
// MOD BEGIN netfinity: Remember last asked part (from Maella)
while(sender->m_lastPartAsked != 0xFFFF && newblockcount < requestedCount && bytesToRequest > 0)
{
Requested_Block_Struct* pBlock = new Requested_Block_Struct;
if(GetNextEmptyBlockInPart(sender->m_lastPartAsked, pBlock, bytesToRequest / (requestedCount - newblockcount)) == true){
// netfinity: Decrease bytes to request
uint32 bytesRequested = pBlock->EndOffset - pBlock->StartOffset + 1;
if (bytesToRequest >= bytesRequested)
bytesToRequest -= bytesRequested;
else
bytesToRequest = 0;
// Keep a track of all pending requested blocks
requestedblocks_list.AddTail(pBlock);
// Update list of blocks to return
newblocks[newblockcount++] = pBlock;
*count = newblockcount;
if (newblockcount == requestedCount)
return true;
// Skip end of loop (=> CPU load)
continue;
}
else {
// All blocks for this chunk have been already requested
delete pBlock;
// => Try to select another chunk
sender->m_lastPartAsked = 0xffff;
break;
}
}
// MOD END netfinity
if (!(*count))
{
AddDebugLogLine(false, _T("PartFile::GetNextRequestedBlock - Unexpected NNP"));
return false;
}
return true;
}
Partfile.cpp - CPartFile::MOD_FindNextPart
This function is the original chunk selection code from CPartFile::GetNextRequestedBlock with the allocation of block requests removed. This function should return true if a chunk was found. It should also set sender->m_lastPartAsked to the chunk found.
Quote
Partfile.cpp - CPartFile::GetNextRequestedBlock
This is the NetF way to collect blocks, but you can easily the original code and just do an adaption where the resulting block is limited to meet the bytesRequested.
Quote
{
Gap_Struct *firstGap = NULL;
Gap_Struct *currentGap;
uint32 block;
uint32 start;
uint32 end;
//uint32 blockLimit;
// Find start of this part
uint32 partStart = (PARTSIZE * partNumber);
// What is the end limit of this block, i.e. can't go outside part (or filesize)
uint32 partEnd = (PARTSIZE * (partNumber + 1)) - 1;
if (partEnd >= GetFileSize())
partEnd = GetFileSize() - 1;
if(partStart > partEnd)
{
AddDebugLogLine(false, _T("CPartFile::GetNextEmptyBlockInPart - Part beyond end of file!"));
return false;
}
// Don't bother to check complete parts
if(GetTotalGapSizeInPart(partNumber) == 0)
{
// Part is complete
return false;
}
// Fast return if no block offset is needed
if(result == NULL)
{
if (MOD_GetRequestedSizeInPart(partNumber) < GetTotalGapSizeInPart(partNumber))
return true; // There are still unrequested blocks
else
return false; // All blocks have been requested
}
CList<struct dataBlock> availableBlocks;
// Find the first gap from the start position
for (POSITION pos = gaplist.GetHeadPosition(); pos != 0; )
{
currentGap = gaplist.GetNext(pos);
if(currentGap == NULL)
break; // We are done!
if(currentGap->start <= partEnd && currentGap->end >= partStart)
{
for(uint32 i = 0; i < 53; ++i)
{
uint32 blockStart = partStart + i * EMBLOCKSIZE;
uint32 blockEnd = min(blockStart + EMBLOCKSIZE - 1, partEnd);
if(blockStart > partEnd) break;
if ((currentGap->start <= blockEnd) && (currentGap->end >= blockStart))
{
uint32 start = max(blockStart, currentGap->start);
uint32 end = min(blockEnd, currentGap->end);
struct dataBlock newBlock;
newBlock.start = start;
newBlock.end = end;
availableBlocks.AddTail(newBlock);
}
}
}
}
for (POSITION i = requestedblocks_list.GetHeadPosition();i != 0; )
{
const Requested_Block_Struct* cur_block = requestedblocks_list.GetNext(i);
for (POSITION j = availableBlocks.GetHeadPosition();j != 0; )
{
const dataBlock avail_block = availableBlocks.GetAt(j);
if (avail_block.start <= cur_block->EndOffset && avail_block.end >=cur_block->StartOffset)
{
availableBlocks.RemoveAt(j);
struct dataBlock newBlock;
if (avail_block.start < cur_block->StartOffset)
{
newBlock.start = avail_block.start;
newBlock.end = cur_block->StartOffset - 1;
availableBlocks.AddTail(newBlock);
}
if (avail_block.end > cur_block->EndOffset)
{
newBlock.start = cur_block->EndOffset + 1;
newBlock.end = avail_block.end;
availableBlocks.AddTail(newBlock);
}
j = availableBlocks.GetHeadPosition();
}
else
availableBlocks.GetNext(j);
}
}
if(availableBlocks.IsEmpty())
{
AddDebugLogLine(false, _T("CPartFile::GetNextEmptyBlockInPart - No block found"));
AddDebugLogLine(false, _T("CPartFile::GetNextEmptyBlockInPart - Part requested: %d Part left: %d"), MOD_GetRequestedSizeInPart(partNumber), GetTotalGapSizeInPart(partNumber));
return false;
}
block = (rand() + ::GetTickCount()) % availableBlocks.GetCount(); // netfinity: Use time to get more randomness
POSITION idx = availableBlocks.FindIndex(block);
const dataBlock avail_block = availableBlocks.GetAt(idx);
start = avail_block.start;
end = avail_block.end;
bytesRequested -= bytesRequested % 10240;
if (bytesRequested < 10240) bytesRequested = 10240;
if (bytesRequested > EMBLOCKSIZE) bytesRequested = EMBLOCKSIZE;
if(start + bytesRequested - 1 < end)
end = start + bytesRequested - 1;
if(start > end || (start - partStart) / EMBLOCKSIZE != (end - partStart) / EMBLOCKSIZE)
{
AddDebugLogLine(false, _T("CPartFile::GetNextEmptyBlockInPart - Block boundaries voilated: start = %d, stop = %d"), start, end);
return false;
}
//AddDebugLogLine(false, _T("Found block = %d"), i);
if (result != NULL)
{
result->StartOffset = start;
result->EndOffset = end;
md4cpy(result->FileID, GetFileHash());
result->transferred = 0;
}
return true;
}
Additional functions to add in Partfile.cpp
Quote
{
uint32 requestedSize = 0;
// Find start of this part
uint32 partStart = (PARTSIZE * part);
// What is the end limit of this block, i.e. can't go outside part (or filesize)
uint32 partEnd = (PARTSIZE * (part + 1)) - 1;
if (partEnd >= GetFileSize())
partEnd = GetFileSize() - 1;
for (POSITION pos = requestedblocks_list.GetHeadPosition();pos != 0; )
{
const Requested_Block_Struct* cur_block = requestedblocks_list.GetNext(pos);
if ((partStart <= cur_block->StartOffset) && (partEnd >= cur_block->EndOffset))
{
requestedSize += (cur_block->EndOffset - cur_block->StartOffset + 1) - cur_block->transferred;
}
}
return requestedSize;
}
uint32 CPartFile::MOD_GetRequestedSizeInFile() const
{
uint32 requestedSize = 0;
for (POSITION pos = requestedblocks_list.GetHeadPosition();pos != 0; )
{
const Requested_Block_Struct* cur_block = requestedblocks_list.GetNext(pos);
requestedSize += (cur_block->EndOffset - cur_block->StartOffset + 1) - cur_block->transferred;
}
return requestedSize;
}
uint16 CPartFile::MOD_GetDownloadingSourcesInPart(uint16 part, CUpDownClient* exclude_src) const
{
uint16 sourceCount = 0;
for(POSITION pos = m_downloadingSourceList.GetHeadPosition();pos!=0;)
{
CUpDownClient* cur_src = m_downloadingSourceList.GetNext(pos);
if (thePrefs.m_iDbgHeap >= 2)
ASSERT_VALID( cur_src );
if (cur_src && cur_src->GetDownloadState() == DS_DOWNLOADING && cur_src != exclude_src)
{
if (cur_src->m_lastPartAsked == part)
sourceCount++;
}
}
return sourceCount;
}