You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ktorrent/libktorrent/torrent/chunkmanager.cpp

1158 lines
27 KiB

/***************************************************************************
* Copyright (C) 2005 by *
* Joris Guisson <joris.guisson@gmail.com> *
* Ivan Vasic <ivasic@gmail.com> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
***************************************************************************/
#include <algorithm>
#include <util/file.h>
#include <util/array.h>
#include <tqstringlist.h>
#include "chunkmanager.h"
#include "torrent.h"
#include <util/error.h>
#include <util/bitset.h>
#include <util/fileops.h>
#include "singlefilecache.h"
#include "multifilecache.h"
#include <util/log.h>
#include <util/functions.h>
#include "globals.h"
#include <tdelocale.h>
namespace bt
{
Uint32 ChunkManager::max_chunk_size_for_data_check = 0;
ChunkManager::ChunkManager(
Torrent & tor,
const TQString & tmpdir,
const TQString & datadir,
bool custom_output_name)
: tor(tor),chunks(tor.getNumChunks()),
bitset(tor.getNumChunks()),excluded_chunks(tor.getNumChunks()),only_seed_chunks(tor.getNumChunks()),todo(tor.getNumChunks())
{
during_load = false;
only_seed_chunks.setAll(false);
todo.setAll(true);
if (tor.isMultiFile())
cache = new MultiFileCache(tor,tmpdir,datadir,custom_output_name);
else
cache = new SingleFileCache(tor,tmpdir,datadir);
index_file = tmpdir + "index";
file_info_file = tmpdir + "file_info";
file_priority_file = tmpdir + "file_priority";
Uint64 tsize = tor.getFileLength(); // total size
Uint64 csize = tor.getChunkSize(); // chunk size
Uint64 lsize = tsize - (csize * (tor.getNumChunks() - 1)); // size of last chunk
for (Uint32 i = 0;i < tor.getNumChunks();i++)
{
if (i + 1 < tor.getNumChunks())
chunks.insert(i,new Chunk(i,csize));
else
chunks.insert(i,new Chunk(i,lsize));
}
chunks.setAutoDelete(true);
chunks_left = 0;
recalc_chunks_left = true;
corrupted_count = recheck_counter = 0;
for (Uint32 i = 0;i < tor.getNumFiles();i++)
{
TorrentFile & tf = tor.getFile(i);
connect(&tf,TQ_SIGNAL(downloadPriorityChanged(TorrentFile*, Priority, Priority )),
this,TQ_SLOT(downloadPriorityChanged(TorrentFile*, Priority, Priority )));
if (tf.getPriority() != NORMAL_PRIORITY)
{
downloadPriorityChanged(&tf,tf.getPriority(),tf.getOldPriority());
}
}
if(tor.isMultiFile())
{
for(Uint32 i=0; i<tor.getNumFiles(); ++i)
{
bt::TorrentFile & file = tor.getFile(i);
if (!file.isMultimedia() || file.getPriority() == bt::ONLY_SEED_PRIORITY)
continue;
if (file.getFirstChunk() == file.getLastChunk())
{
// prioritise whole file
prioritise(file.getFirstChunk(),file.getLastChunk(),PREVIEW_PRIORITY);
}
else
{
Uint32 chunkOffset;
chunkOffset = ((file.getLastChunk() - file.getFirstChunk()) / 100) + 1;
prioritise(file.getFirstChunk(), file.getFirstChunk()+chunkOffset, PREVIEW_PRIORITY);
if (file.getLastChunk() - file.getFirstChunk() > chunkOffset)
{
prioritise(file.getLastChunk() - chunkOffset, file.getLastChunk(), PREVIEW_PRIORITY);
}
}
}
}
else
{
if(tor.isMultimedia())
{
Uint32 chunkOffset;
chunkOffset = (tor.getNumChunks() / 100) + 1;
prioritise(0,chunkOffset,PREVIEW_PRIORITY);
if (tor.getNumChunks() > chunkOffset)
{
prioritise(tor.getNumChunks() - chunkOffset, tor.getNumChunks() - 1,PREVIEW_PRIORITY);
}
}
}
}
ChunkManager::~ChunkManager()
{
delete cache;
}
TQString ChunkManager::getDataDir() const
{
return cache->getDataDir();
}
void ChunkManager::changeDataDir(const TQString & data_dir)
{
cache->changeTmpDir(data_dir);
index_file = data_dir + "index";
file_info_file = data_dir + "file_info";
file_priority_file = data_dir + "file_priority";
}
TDEIO::Job* ChunkManager::moveDataFiles(const TQString & ndir)
{
return cache->moveDataFiles(ndir);
}
void ChunkManager::moveDataFilesCompleted(TDEIO::Job* job)
{
cache->moveDataFilesCompleted(job);
}
void ChunkManager::changeOutputPath(const TQString & output_path)
{
cache->changeOutputPath(output_path);
}
void ChunkManager::loadIndexFile()
{
during_load = true;
loadPriorityInfo();
File fptr;
if (!fptr.open(index_file,"rb"))
{
// no index file, so assume it's empty
bt::Touch(index_file,true);
Out(SYS_DIO|LOG_IMPORTANT) << "Can't open index file : " << fptr.errorString() << endl;
during_load = false;
return;
}
if (fptr.seek(File::END,0) != 0)
{
fptr.seek(File::BEGIN,0);
while (!fptr.eof())
{
NewChunkHeader hdr;
fptr.read(&hdr,sizeof(NewChunkHeader));
Chunk* c = getChunk(hdr.index);
if (c)
{
c->setStatus(Chunk::ON_DISK);
bitset.set(hdr.index,true);
todo.set(hdr.index,false);
recalc_chunks_left = true;
}
}
}
tor.updateFilePercentage(bitset);
during_load = false;
}
void ChunkManager::saveIndexFile()
{
File fptr;
if (!fptr.open(index_file,"wb"))
throw Error(i18n("Cannot open index file %1 : %2").arg(index_file).arg(fptr.errorString()));
for (unsigned int i = 0;i < tor.getNumChunks();i++)
{
Chunk* c = getChunk(i);
if (c->getStatus() != Chunk::NOT_DOWNLOADED)
{
NewChunkHeader hdr;
hdr.index = i;
fptr.write(&hdr,sizeof(NewChunkHeader));
}
}
savePriorityInfo();
}
void ChunkManager::createFiles(bool check_priority)
{
if (!bt::Exists(index_file))
{
File fptr;
fptr.open(index_file,"wb");
}
cache->create();
if (check_priority)
{
for (Uint32 i = 0;i < tor.getNumFiles();i++)
{
TorrentFile & tf = tor.getFile(i);
connect(&tf,TQ_SIGNAL(downloadPriorityChanged(TorrentFile*, Priority, Priority )),
this,TQ_SLOT(downloadPriorityChanged(TorrentFile*, Priority, Priority )));
if (tf.getPriority() != NORMAL_PRIORITY)
{
downloadPriorityChanged(&tf,tf.getPriority(),tf.getOldPriority());
}
}
}
}
bool ChunkManager::hasMissingFiles(TQStringList & sl)
{
return cache->hasMissingFiles(sl);
}
Chunk* ChunkManager::getChunk(unsigned int i)
{
if (i >= chunks.count())
return 0;
else
return chunks[i];
}
void ChunkManager::start()
{
cache->open();
}
void ChunkManager::stop()
{
// unmmap all chunks which can
for (Uint32 i = 0;i < bitset.getNumBits();i++)
{
Chunk* c = chunks[i];
if (c->getStatus() == Chunk::MMAPPED)
{
cache->save(c);
c->clear();
c->setStatus(Chunk::ON_DISK);
}
else if (c->getStatus() == Chunk::BUFFERED)
{
c->clear();
c->setStatus(Chunk::ON_DISK);
}
}
cache->close();
}
Chunk* ChunkManager::grabChunk(unsigned int i)
{
if (i >= chunks.size())
return 0;
Chunk* c = chunks[i];
if (c->getStatus() == Chunk::NOT_DOWNLOADED || c->isExcluded())
{
return 0;
}
else if (c->getStatus() == Chunk::ON_DISK)
{
// load the chunk if it is on disk
cache->load(c);
loaded.insert(i,bt::GetCurrentTime());
bool check_allowed = (max_chunk_size_for_data_check == 0 || tor.getChunkSize() <= max_chunk_size_for_data_check);
// when no corruptions have been found, only check once every 5 chunks
if (check_allowed && recheck_counter < 5 && corrupted_count == 0)
check_allowed = false;
if (c->getData() && check_allowed)
{
recheck_counter = 0;
if (!c->checkHash(tor.getHash(i)))
{
Out(SYS_DIO|LOG_IMPORTANT) << "Chunk " << i
<< " has been found invalid, redownloading" << endl;
resetChunk(i);
tor.updateFilePercentage(i,bitset);
saveIndexFile();
recalc_chunks_left = true;
corrupted_count++;
corrupted(i);
return 0;
}
}
else
{
recheck_counter++;
}
}
loaded.insert(i,bt::GetCurrentTime());
return c;
}
void ChunkManager::releaseChunk(unsigned int i)
{
if (i >= chunks.size())
return;
Chunk* c = chunks[i];
if (!c->taken())
{
if (c->getStatus() == Chunk::MMAPPED)
cache->save(c);
c->clear();
c->setStatus(Chunk::ON_DISK);
loaded.remove(i);
}
}
void ChunkManager::resetChunk(unsigned int i)
{
if (i >= chunks.size())
return;
Chunk* c = chunks[i];
if (c->getStatus() == Chunk::MMAPPED)
cache->save(c);
c->clear();
c->setStatus(Chunk::NOT_DOWNLOADED);
bitset.set(i,false);
todo.set(i,!excluded_chunks.get(i) && !only_seed_chunks.get(i));
loaded.remove(i);
tor.updateFilePercentage(i,bitset);
}
void ChunkManager::checkMemoryUsage()
{
Uint32 num_removed = 0;
TQMap<Uint32,TimeStamp>::iterator i = loaded.begin();
while (i != loaded.end())
{
Chunk* c = chunks[i.key()];
// get rid of chunk if nobody asked for it in the last 5 seconds
if (!c->taken() && bt::GetCurrentTime() - i.data() > 5000)
{
if (c->getStatus() == Chunk::MMAPPED)
cache->save(c);
c->clear();
c->setStatus(Chunk::ON_DISK);
TQMap<Uint32,TimeStamp>::iterator j = i;
i++;
loaded.erase(j);
num_removed++;
}
else
{
i++;
}
}
// Uint32 num_in_mem = loaded.count();
// Out() << TQString("Cleaned %1 chunks, %2 still in memory").arg(num_removed).arg(num_in_mem) << endl;
}
void ChunkManager::saveChunk(unsigned int i,bool update_index)
{
if (i >= chunks.size())
return;
Chunk* c = chunks[i];
if (!c->isExcluded())
{
cache->save(c);
// update the index file
if (update_index)
{
bitset.set(i,true);
todo.set(i,false);
recalc_chunks_left = true;
writeIndexFileEntry(c);
tor.updateFilePercentage(i,bitset);
}
}
else
{
c->clear();
c->setStatus(Chunk::NOT_DOWNLOADED);
Out(SYS_DIO|LOG_IMPORTANT) << "Warning: attempted to save a chunk which was excluded" << endl;
}
}
void ChunkManager::writeIndexFileEntry(Chunk* c)
{
File fptr;
if (!fptr.open(index_file,"r+b"))
{
// no index file, so assume it's empty
bt::Touch(index_file,true);
Out(SYS_DIO|LOG_IMPORTANT) << "Can't open index file : " << fptr.errorString() << endl;
// try again
if (!fptr.open(index_file,"r+b"))
// panick if it failes
throw Error(i18n("Cannot open index file %1 : %2").arg(index_file).arg(fptr.errorString()));
}
fptr.seek(File::END,0);
NewChunkHeader hdr;
hdr.index = c->getIndex();
fptr.write(&hdr,sizeof(NewChunkHeader));
}
Uint32 ChunkManager::onlySeedChunks() const
{
return only_seed_chunks.numOnBits();
}
bool ChunkManager::completed() const
{
return todo.numOnBits() == 0 && bitset.numOnBits() > 0;
}
Uint64 ChunkManager::bytesLeft() const
{
Uint32 num_left = bitset.getNumBits() - bitset.numOnBits();
Uint32 last = chunks.size() - 1;
if (last < chunks.size() && !bitset.get(last))
{
Chunk* c = chunks[last];
if (c)
return (num_left - 1)*tor.getChunkSize() + c->getSize();
else
return num_left*tor.getChunkSize();
}
else
{
return num_left*tor.getChunkSize();
}
}
Uint64 ChunkManager::bytesLeftToDownload() const
{
Uint32 num_left = todo.numOnBits();
Uint32 last = chunks.size() - 1;
if (last < chunks.size() && todo.get(last))
{
Chunk* c = chunks[last];
if (c)
return (num_left - 1)*tor.getChunkSize() + c->getSize();
else
return num_left*tor.getChunkSize();
}
else
{
return num_left*tor.getChunkSize();
}
}
Uint32 ChunkManager::chunksLeft() const
{
if (!recalc_chunks_left)
return chunks_left;
Uint32 num = 0;
Uint32 tot = chunks.size();
for (Uint32 i = 0;i < tot;i++)
{
const Chunk* c = chunks[i];
if (!bitset.get(i) && !c->isExcluded())
num++;
}
chunks_left = num;
recalc_chunks_left = false;
return num;
}
bool ChunkManager::haveAllChunks() const
{
return bitset.numOnBits() == bitset.getNumBits();
}
Uint64 ChunkManager::bytesExcluded() const
{
Uint64 excl = 0;
if (excluded_chunks.get(tor.getNumChunks() - 1))
{
Chunk* c = chunks[tor.getNumChunks() - 1];
Uint32 num = excluded_chunks.numOnBits() - 1;
excl = tor.getChunkSize() * num + c->getSize();
}
else
{
excl = tor.getChunkSize() * excluded_chunks.numOnBits();
}
if (only_seed_chunks.get(tor.getNumChunks() - 1))
{
Chunk* c = chunks[tor.getNumChunks() - 1];
Uint32 num = only_seed_chunks.numOnBits() - 1;
excl += tor.getChunkSize() * num + c->getSize();
}
else
{
excl += tor.getChunkSize() * only_seed_chunks.numOnBits();
}
return excl;
}
Uint32 ChunkManager::chunksExcluded() const
{
return excluded_chunks.numOnBits() + only_seed_chunks.numOnBits();
}
Uint32 ChunkManager::chunksDownloaded() const
{
return bitset.numOnBits();
}
void ChunkManager::debugPrintMemUsage()
{
Out(SYS_DIO|LOG_DEBUG) << "Active Chunks : " << loaded.count()<< endl;
}
void ChunkManager::prioritise(Uint32 from,Uint32 to,Priority priority)
{
if (from > to)
std::swap(from,to);
Uint32 i = from;
while (i <= to && i < chunks.count())
{
Chunk* c = chunks[i];
c->setPriority(priority);
if (priority == ONLY_SEED_PRIORITY)
{
only_seed_chunks.set(i,true);
todo.set(i,false);
}
else if (priority == EXCLUDED)
{
only_seed_chunks.set(i,false);
todo.set(i,false);
}
else
{
only_seed_chunks.set(i,false);
todo.set(i,!bitset.get(i));
}
i++;
}
updateStats();
}
void ChunkManager::exclude(Uint32 from,Uint32 to)
{
if (from > to)
std::swap(from,to);
Uint32 i = from;
while (i <= to && i < chunks.count())
{
Chunk* c = chunks[i];
c->setExclude(true);
excluded_chunks.set(i,true);
only_seed_chunks.set(i,false);
todo.set(i,false);
bitset.set(i,false);
i++;
}
recalc_chunks_left = true;
excluded(from,to);
updateStats();
}
void ChunkManager::include(Uint32 from,Uint32 to)
{
if (from > to)
std::swap(from,to);
Uint32 i = from;
while (i <= to && i < chunks.count())
{
Chunk* c = chunks[i];
c->setExclude(false);
excluded_chunks.set(i,false);
if (!bitset.get(i))
todo.set(i,true);
i++;
}
recalc_chunks_left = true;
updateStats();
included(from,to);
}
void ChunkManager::saveFileInfo()
{
// saves which TorrentFiles do not need to be downloaded
File fptr;
if (!fptr.open(file_info_file,"wb"))
{
Out(SYS_DIO|LOG_IMPORTANT) << "Warning : Can't save chunk_info file : " << fptr.errorString() << endl;
return;
}
// first write the number of excluded ones
// don't know this yet, so write 0 for the time being
Uint32 tmp = 0;
fptr.write(&tmp,sizeof(Uint32));
Uint32 i = 0;
Uint32 cnt = 0;
while (i < tor.getNumFiles())
{
if (tor.getFile(i).doNotDownload())
{
fptr.write(&i,sizeof(Uint32));
cnt++;
}
i++;
}
// go back to the beginning and write the number of files
fptr.seek(File::BEGIN,0);
fptr.write(&cnt,sizeof(Uint32));
fptr.flush();
}
void ChunkManager::loadFileInfo()
{
if (during_load)
return;
File fptr;
if (!fptr.open(file_info_file,"rb"))
return;
Uint32 num = 0,tmp = 0;
// first read the number of dnd files
if (fptr.read(&num,sizeof(Uint32)) != sizeof(Uint32))
{
Out(SYS_DIO|LOG_IMPORTANT) << "Warning : error reading chunk_info file" << endl;
return;
}
for (Uint32 i = 0;i < num;i++)
{
if (fptr.read(&tmp,sizeof(Uint32)) != sizeof(Uint32))
{
Out(SYS_DIO|LOG_IMPORTANT) << "Warning : error reading chunk_info file" << endl;
return;
}
bt::TorrentFile & tf = tor.getFile(tmp);
if (!tf.isNull())
{
Out(SYS_DIO|LOG_DEBUG) << "Excluding : " << tf.getPath() << endl;
tf.setDoNotDownload(true);
}
}
}
void ChunkManager::savePriorityInfo()
{
if (during_load)
return;
//save priority info and call saveFileInfo
saveFileInfo();
File fptr;
if (!fptr.open(file_priority_file,"wb"))
{
Out(SYS_DIO|LOG_IMPORTANT) << "Warning : Can't save chunk_info file : " << fptr.errorString() << endl;
return;
}
try
{
// first write the number of excluded ones
// don't know this yet, so write 0 for the time being
Uint32 tmp = 0;
fptr.write(&tmp,sizeof(Uint32));
Uint32 i = 0;
Uint32 cnt = 0;
while (i < tor.getNumFiles())
{
const TorrentFile & tf = tor.getFile(i);
if (tf.getPriority() != NORMAL_PRIORITY)
{
tmp = tf.getPriority();
fptr.write(&i,sizeof(Uint32));
fptr.write(&tmp,sizeof(Uint32));
cnt+=2;
}
i++;
}
// go back to the beginning and write the number of items
fptr.seek(File::BEGIN,0);
fptr.write(&cnt,sizeof(Uint32));
fptr.flush();
}
catch (bt::Error & err)
{
Out(SYS_DIO|LOG_IMPORTANT) << "Failed to save priority file " << err.toString() << endl;
bt::Delete(file_priority_file,true);
}
}
void ChunkManager::loadPriorityInfo()
{
//load priority info and if that fails load file info
File fptr;
if (!fptr.open(file_priority_file,"rb"))
{
loadFileInfo();
return;
}
Uint32 num = 0;
// first read the number of lines
if (fptr.read(&num,sizeof(Uint32)) != sizeof(Uint32) || num > 2*tor.getNumFiles())
{
Out(SYS_DIO|LOG_IMPORTANT) << "Warning : error reading chunk_info file" << endl;
loadFileInfo();
return;
}
Array<Uint32> buf(num);
if (fptr.read(buf,sizeof(Uint32)*num) != sizeof(Uint32)*num)
{
Out(SYS_DIO|LOG_IMPORTANT) << "Warning : error reading chunk_info file" << endl;
loadFileInfo();
return;
}
fptr.close();
for (Uint32 i = 0;i < num;i += 2)
{
Uint32 idx = buf[i];
if (idx >= tor.getNumFiles())
{
Out(SYS_DIO|LOG_IMPORTANT) << "Warning : error reading chunk_info file" << endl;
loadFileInfo();
return;
}
bt::TorrentFile & tf = tor.getFile(idx);
if (!tf.isNull())
{
// numbers are to be compatible with old chunk info files
switch((Int32)buf[i+1])
{
case FIRST_PRIORITY:
case 3:
tf.setPriority(FIRST_PRIORITY);
break;
case NORMAL_PRIORITY:
case 2:
tf.setPriority(NORMAL_PRIORITY);
break;
case EXCLUDED:
case 0:
//tf.setDoNotDownload(true);
tf.setPriority(EXCLUDED);
break;
case ONLY_SEED_PRIORITY:
case -1:
tf.setPriority(ONLY_SEED_PRIORITY);
break;
default:
tf.setPriority(LAST_PRIORITY);
break;
}
}
}
}
void ChunkManager::downloadStatusChanged(TorrentFile* tf,bool download)
{
Uint32 first = tf->getFirstChunk();
Uint32 last = tf->getLastChunk();
if (download)
{
// include the chunks
include(first,last);
// if it is a multimedia file, prioritise first and last chunks of file
if (tf->isMultimedia())
{
Uint32 chunkOffset;
chunkOffset = ((last - first) / 100) + 1;
prioritise(first,first+chunkOffset,PREVIEW_PRIORITY);
if (last - first > 2)
{
prioritise(last - chunkOffset, last, PREVIEW_PRIORITY);
//prioritise(last -1,last, PREVIEW_PRIORITY);
}
}
}
else
{
// Out(SYS_DIO|LOG_DEBUG) << "Excluding chunks " << first << " to " << last << endl;
// first and last chunk may be part of multiple files
// so we can't just exclude them
TQValueList<Uint32> files,last_files;
// get list of files where first chunk lies in
tor.calcChunkPos(first,files);
tor.calcChunkPos(last,last_files);
// check for exceptional case which causes very long loops
if (first == last && files.count() > 1)
{
cache->downloadStatusChanged(tf,download);
savePriorityInfo();
return;
}
// go over all chunks from first to last and mark them as not downloaded
// (first and last not included)
for (Uint32 i = first + 1;i < last;i++)
resetChunk(i);
// if the first chunk only lies in one file, reset it
if (files.count() == 1 && first != 0)
{
// Out(SYS_DIO|LOG_DEBUG) << "Resetting first " << first << endl;
resetChunk(first);
}
// if the last chunk only lies in one file reset it
if (last != first && last_files.count() == 1)
{
// Out(SYS_DIO|LOG_DEBUG) << "Resetting last " << last << endl;
resetChunk(last);
}
Priority maxp = ONLY_SEED_PRIORITY;
bool reprioritise_border_chunk = false;
bool modified = false;
// if one file in the list needs to be downloaded,increment first
for (TQValueList<Uint32>::iterator i = files.begin();i != files.end();i++)
{
if (*i == tf->getIndex())
continue;
const TorrentFile & other = tor.getFile(*i);
if (!other.doNotDownload())
{
if (first != last && !modified)
{
first++;
reprioritise_border_chunk = true;
modified = true;
}
if (other.getPriority() > maxp)
maxp = other.getPriority();
}
}
// in case we have incremented first, we better reprioritise the border chunk
if (reprioritise_border_chunk)
prioritise(first-1,first-1,maxp);
maxp = ONLY_SEED_PRIORITY;
reprioritise_border_chunk = false;
modified = false;
// if one file in the list needs to be downloaded,decrement last
for (TQValueList<Uint32>::iterator i = last_files.begin();i != last_files.end();i++)
{
if (*i == tf->getIndex())
continue;
const TorrentFile & other = tor.getFile(*i);
if (!other.doNotDownload())
{
if (first != last && last > 0 && !modified)
{
last--;
reprioritise_border_chunk = true;
modified = true;
}
if (other.getPriority() > maxp)
maxp = other.getPriority();
}
}
if (reprioritise_border_chunk)
prioritise(last+1,last+1,maxp);
// last smaller then first is not normal, so just return
if (last < first)
{
cache->downloadStatusChanged(tf,download);
savePriorityInfo();
return;
}
// Out(SYS_DIO|LOG_DEBUG) << "exclude " << first << " to " << last << endl;
exclude(first,last);
}
// alert the cache but first put things in critical operation mode
cache->downloadStatusChanged(tf,download);
savePriorityInfo();
}
void ChunkManager::downloadPriorityChanged(TorrentFile* tf,Priority newpriority,Priority oldpriority)
{
if (newpriority == EXCLUDED)
{
downloadStatusChanged(tf, false);
return;
}
if (oldpriority == EXCLUDED)
{
downloadStatusChanged(tf, true);
return;
}
savePriorityInfo();
Uint32 first = tf->getFirstChunk();
Uint32 last = tf->getLastChunk();
// first and last chunk may be part of multiple files
// so we can't just exclude them
TQValueList<Uint32> files;
// get list of files where first chunk lies in
tor.calcChunkPos(first,files);
Chunk* c = chunks[first];
// if one file in the list needs to be downloaded,increment first
for (TQValueList<Uint32>::iterator i = files.begin();i != files.end();i++)
{
Priority np = tor.getFile(*i).getPriority();
if (np > newpriority && *i != tf->getIndex())
{
// make sure we don't go past last
if (first == last)
return;
first++;
break;
}
}
files.clear();
// get list of files where last chunk lies in
tor.calcChunkPos(last,files);
c = chunks[last];
// if one file in the list needs to be downloaded,decrement last
for (TQValueList<Uint32>::iterator i = files.begin();i != files.end();i++)
{
Priority np = tor.getFile(*i).getPriority();
if (np > newpriority && *i != tf->getIndex())
{
// make sure we don't wrap around
if (last == 0 || last == first)
return;
last--;
break;
}
}
// last smaller then first is not normal, so just return
if (last < first)
{
return;
}
prioritise(first,last,newpriority);
if (newpriority == ONLY_SEED_PRIORITY)
excluded(first,last);
}
bool ChunkManager::prepareChunk(Chunk* c,bool allways)
{
if (!allways && c->getStatus() != Chunk::NOT_DOWNLOADED)
return false;
return cache->prep(c);
}
TQString ChunkManager::getOutputPath() const
{
return cache->getOutputPath();
}
void ChunkManager::preallocateDiskSpace(PreallocationThread* prealloc)
{
cache->preallocateDiskSpace(prealloc);
}
void ChunkManager::dataChecked(const BitSet & ok_chunks)
{
// go over all chunks at check each of them
for (Uint32 i = 0;i < chunks.count();i++)
{
Chunk* c = chunks[i];
if (ok_chunks.get(i) && !bitset.get(i))
{
// We think we do not hae a chunk, but we do have it
bitset.set(i,true);
todo.set(i,false);
// the chunk must be on disk
c->setStatus(Chunk::ON_DISK);
tor.updateFilePercentage(i,bitset);
}
else if (!ok_chunks.get(i) && bitset.get(i))
{
Out(SYS_DIO|LOG_IMPORTANT) << "Previously OK chunk " << i << " is corrupt !!!!!" << endl;
// We think we have a chunk, but we don't
bitset.set(i,false);
todo.set(i,!only_seed_chunks.get(i) && !excluded_chunks.get(i));
if (c->getStatus() == Chunk::ON_DISK)
{
c->setStatus(Chunk::NOT_DOWNLOADED);
tor.updateFilePercentage(i,bitset);
}
else if (c->getStatus() == Chunk::MMAPPED || c->getStatus() == Chunk::BUFFERED)
{
resetChunk(i);
}
else
{
tor.updateFilePercentage(i,bitset);
}
}
}
recalc_chunks_left = true;
try
{
saveIndexFile();
}
catch (bt::Error & err)
{
Out(SYS_DIO|LOG_DEBUG) << "Failed to save index file : " << err.toString() << endl;
}
catch (...)
{
Out(SYS_DIO|LOG_DEBUG) << "Failed to save index file : unkown exception" << endl;
}
chunksLeft();
corrupted_count = 0;
}
bool ChunkManager::hasExistingFiles() const
{
return cache->hasExistingFiles();
}
void ChunkManager::recreateMissingFiles()
{
createFiles();
if (tor.isMultiFile())
{
// loop over all files and mark all chunks of all missing files as
// not downloaded
for (Uint32 i = 0;i < tor.getNumFiles();i++)
{
TorrentFile & tf = tor.getFile(i);
if (!tf.isMissing())
continue;
for (Uint32 j = tf.getFirstChunk(); j <= tf.getLastChunk();j++)
resetChunk(j);
tf.setMissing(false);
}
}
else
{
// reset all chunks in case of single file torrent
for (Uint32 j = 0; j < tor.getNumChunks();j++)
resetChunk(j);
}
saveIndexFile();
recalc_chunks_left = true;
chunksLeft();
}
void ChunkManager::dndMissingFiles()
{
// createFiles(); // create them again
// loop over all files and mark all chunks of all missing files as
// not downloaded
for (Uint32 i = 0;i < tor.getNumFiles();i++)
{
TorrentFile & tf = tor.getFile(i);
if (!tf.isMissing())
continue;
for (Uint32 j = tf.getFirstChunk(); j <= tf.getLastChunk();j++)
resetChunk(j);
tf.setMissing(false);
tf.setDoNotDownload(true); // set do not download
}
savePriorityInfo();
saveIndexFile();
recalc_chunks_left = true;
chunksLeft();
}
void ChunkManager::deleteDataFiles()
{
cache->deleteDataFiles();
}
Uint64 ChunkManager::diskUsage()
{
return cache->diskUsage();
}
}
#include "chunkmanager.moc"