summaryrefslogtreecommitdiffstats
path: root/libktorrent/torrent/chunkmanager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libktorrent/torrent/chunkmanager.cpp')
-rw-r--r--libktorrent/torrent/chunkmanager.cpp1157
1 files changed, 1157 insertions, 0 deletions
diff --git a/libktorrent/torrent/chunkmanager.cpp b/libktorrent/torrent/chunkmanager.cpp
new file mode 100644
index 0000000..08aac97
--- /dev/null
+++ b/libktorrent/torrent/chunkmanager.cpp
@@ -0,0 +1,1157 @@
+/***************************************************************************
+ * 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 <qstringlist.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 <klocale.h>
+
+namespace bt
+{
+
+ Uint32 ChunkManager::max_chunk_size_for_data_check = 0;
+
+
+ ChunkManager::ChunkManager(
+ Torrent & tor,
+ const QString & tmpdir,
+ const QString & 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,SIGNAL(downloadPriorityChanged(TorrentFile*, Priority, Priority )),
+ this,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;
+ }
+
+ QString ChunkManager::getDataDir() const
+ {
+ return cache->getDataDir();
+ }
+
+ void ChunkManager::changeDataDir(const QString & 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";
+ }
+
+ KIO::Job* ChunkManager::moveDataFiles(const QString & ndir)
+ {
+ return cache->moveDataFiles(ndir);
+ }
+
+ void ChunkManager::moveDataFilesCompleted(KIO::Job* job)
+ {
+ cache->moveDataFilesCompleted(job);
+ }
+
+ void ChunkManager::changeOutputPath(const QString & 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,SIGNAL(downloadPriorityChanged(TorrentFile*, Priority, Priority )),
+ this,SLOT(downloadPriorityChanged(TorrentFile*, Priority, Priority )));
+
+ if (tf.getPriority() != NORMAL_PRIORITY)
+ {
+ downloadPriorityChanged(&tf,tf.getPriority(),tf.getOldPriority());
+ }
+ }
+ }
+ }
+
+ bool ChunkManager::hasMissingFiles(QStringList & 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;
+ QMap<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);
+ QMap<Uint32,TimeStamp>::iterator j = i;
+ i++;
+ loaded.erase(j);
+ num_removed++;
+ }
+ else
+ {
+ i++;
+ }
+ }
+ // Uint32 num_in_mem = loaded.count();
+ // Out() << QString("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(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
+ QValueList<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 (QValueList<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 (QValueList<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
+ QValueList<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 (QValueList<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 (QValueList<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);
+ }
+
+ QString 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"