Fix silly error in bit fiddling
[qt-netbsd.git] / examples / network / torrent / filemanager.cpp
blobbe50c66eb63ae64749c3d39e96af68248225982e
1 /****************************************************************************
2 **
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the examples of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
38 ** $QT_END_LICENSE$
40 ****************************************************************************/
42 #include "filemanager.h"
43 #include "metainfo.h"
45 #include <QByteArray>
46 #include <QDir>
47 #include <QFile>
48 #include <QTimer>
49 #include <QTimerEvent>
50 #include <QCryptographicHash>
52 FileManager::FileManager(QObject *parent)
53 : QThread(parent)
55 quit = false;
56 totalLength = 0;
57 readId = 0;
58 startVerification = false;
59 wokeUp = false;
60 newFile = false;
61 numPieces = 0;
62 verifiedPieces.fill(false);
65 FileManager::~FileManager()
67 quit = true;
68 cond.wakeOne();
69 wait();
71 foreach (QFile *file, files) {
72 file->close();
73 delete file;
77 int FileManager::read(int pieceIndex, int offset, int length)
79 ReadRequest request;
80 request.pieceIndex = pieceIndex;
81 request.offset = offset;
82 request.length = length;
84 QMutexLocker locker(&mutex);
85 request.id = readId++;
86 readRequests << request;
88 if (!wokeUp) {
89 wokeUp = true;
90 QMetaObject::invokeMethod(this, "wakeUp", Qt::QueuedConnection);
93 return request.id;
96 void FileManager::write(int pieceIndex, int offset, const QByteArray &data)
98 WriteRequest request;
99 request.pieceIndex = pieceIndex;
100 request.offset = offset;
101 request.data = data;
103 QMutexLocker locker(&mutex);
104 writeRequests << request;
106 if (!wokeUp) {
107 wokeUp = true;
108 QMetaObject::invokeMethod(this, "wakeUp", Qt::QueuedConnection);
112 void FileManager::verifyPiece(int pieceIndex)
114 QMutexLocker locker(&mutex);
115 pendingVerificationRequests << pieceIndex;
116 startVerification = true;
118 if (!wokeUp) {
119 wokeUp = true;
120 QMetaObject::invokeMethod(this, "wakeUp", Qt::QueuedConnection);
124 int FileManager::pieceLengthAt(int pieceIndex) const
126 QMutexLocker locker(&mutex);
127 return (sha1s.size() == pieceIndex + 1)
128 ? (totalLength % pieceLength) : pieceLength;
131 QBitArray FileManager::completedPieces() const
133 QMutexLocker locker(&mutex);
134 return verifiedPieces;
137 void FileManager::setCompletedPieces(const QBitArray &pieces)
139 QMutexLocker locker(&mutex);
140 verifiedPieces = pieces;
143 QString FileManager::errorString() const
145 return errString;
148 void FileManager::run()
150 if (!generateFiles())
151 return;
153 do {
155 // Go to sleep if there's nothing to do.
156 QMutexLocker locker(&mutex);
157 if (!quit && readRequests.isEmpty() && writeRequests.isEmpty() && !startVerification)
158 cond.wait(&mutex);
161 // Read pending read requests
162 mutex.lock();
163 QList<ReadRequest> newReadRequests = readRequests;
164 readRequests.clear();
165 mutex.unlock();
166 while (!newReadRequests.isEmpty()) {
167 ReadRequest request = newReadRequests.takeFirst();
168 QByteArray block = readBlock(request.pieceIndex, request.offset, request.length);
169 emit dataRead(request.id, request.pieceIndex, request.offset, block);
172 // Write pending write requests
173 mutex.lock();
174 QList<WriteRequest> newWriteRequests = writeRequests;
175 writeRequests.clear();
176 while (!quit && !newWriteRequests.isEmpty()) {
177 WriteRequest request = newWriteRequests.takeFirst();
178 writeBlock(request.pieceIndex, request.offset, request.data);
181 // Process pending verification requests
182 if (startVerification) {
183 newPendingVerificationRequests = pendingVerificationRequests;
184 pendingVerificationRequests.clear();
185 verifyFileContents();
186 startVerification = false;
188 mutex.unlock();
189 newPendingVerificationRequests.clear();
191 } while (!quit);
193 // Write pending write requests
194 mutex.lock();
195 QList<WriteRequest> newWriteRequests = writeRequests;
196 writeRequests.clear();
197 mutex.unlock();
198 while (!newWriteRequests.isEmpty()) {
199 WriteRequest request = newWriteRequests.takeFirst();
200 writeBlock(request.pieceIndex, request.offset, request.data);
204 void FileManager::startDataVerification()
206 QMutexLocker locker(&mutex);
207 startVerification = true;
208 cond.wakeOne();
211 bool FileManager::generateFiles()
213 numPieces = -1;
215 // Set up the thread local data
216 if (metaInfo.fileForm() == MetaInfo::SingleFileForm) {
217 QMutexLocker locker(&mutex);
218 MetaInfoSingleFile singleFile = metaInfo.singleFile();
220 QString prefix;
221 if (!destinationPath.isEmpty()) {
222 prefix = destinationPath;
223 if (!prefix.endsWith("/"))
224 prefix += "/";
225 QDir dir;
226 if (!dir.mkpath(prefix)) {
227 errString = tr("Failed to create directory %1").arg(prefix);
228 emit error();
229 return false;
232 QFile *file = new QFile(prefix + singleFile.name);
233 if (!file->open(QFile::ReadWrite)) {
234 errString = tr("Failed to open/create file %1: %2")
235 .arg(file->fileName()).arg(file->errorString());
236 emit error();
237 return false;
240 if (file->size() != singleFile.length) {
241 newFile = true;
242 if (!file->resize(singleFile.length)) {
243 errString = tr("Failed to resize file %1: %2")
244 .arg(file->fileName()).arg(file->errorString());
245 emit error();
246 return false;
249 fileSizes << file->size();
250 files << file;
251 file->close();
253 pieceLength = singleFile.pieceLength;
254 totalLength = singleFile.length;
255 sha1s = singleFile.sha1Sums;
256 } else {
257 QMutexLocker locker(&mutex);
258 QDir dir;
259 QString prefix;
261 if (!destinationPath.isEmpty()) {
262 prefix = destinationPath;
263 if (!prefix.endsWith("/"))
264 prefix += "/";
266 if (!metaInfo.name().isEmpty()) {
267 prefix += metaInfo.name();
268 if (!prefix.endsWith("/"))
269 prefix += "/";
271 if (!dir.mkpath(prefix)) {
272 errString = tr("Failed to create directory %1").arg(prefix);
273 emit error();
274 return false;
277 foreach (const MetaInfoMultiFile &entry, metaInfo.multiFiles()) {
278 QString filePath = QFileInfo(prefix + entry.path).path();
279 if (!QFile::exists(filePath)) {
280 if (!dir.mkpath(filePath)) {
281 errString = tr("Failed to create directory %1").arg(filePath);
282 emit error();
283 return false;
287 QFile *file = new QFile(prefix + entry.path);
288 if (!file->open(QFile::ReadWrite)) {
289 errString = tr("Failed to open/create file %1: %2")
290 .arg(file->fileName()).arg(file->errorString());
291 emit error();
292 return false;
295 if (file->size() != entry.length) {
296 newFile = true;
297 if (!file->resize(entry.length)) {
298 errString = tr("Failed to resize file %1: %2")
299 .arg(file->fileName()).arg(file->errorString());
300 emit error();
301 return false;
304 fileSizes << file->size();
305 files << file;
306 file->close();
308 totalLength += entry.length;
311 sha1s = metaInfo.sha1Sums();
312 pieceLength = metaInfo.pieceLength();
314 numPieces = sha1s.size();
315 return true;
318 QByteArray FileManager::readBlock(int pieceIndex, int offset, int length)
320 QByteArray block;
321 qint64 startReadIndex = (quint64(pieceIndex) * pieceLength) + offset;
322 qint64 currentIndex = 0;
324 for (int i = 0; !quit && i < files.size() && length > 0; ++i) {
325 QFile *file = files[i];
326 qint64 currentFileSize = fileSizes.at(i);
327 if ((currentIndex + currentFileSize) > startReadIndex) {
328 if (!file->isOpen()) {
329 if (!file->open(QFile::ReadWrite)) {
330 errString = tr("Failed to read from file %1: %2")
331 .arg(file->fileName()).arg(file->errorString());
332 emit error();
333 break;
337 file->seek(startReadIndex - currentIndex);
338 QByteArray chunk = file->read(qMin<qint64>(length, currentFileSize - file->pos()));
339 file->close();
341 block += chunk;
342 length -= chunk.size();
343 startReadIndex += chunk.size();
344 if (length < 0) {
345 errString = tr("Failed to read from file %1 (read %3 bytes): %2")
346 .arg(file->fileName()).arg(file->errorString()).arg(length);
347 emit error();
348 break;
351 currentIndex += currentFileSize;
353 return block;
356 bool FileManager::writeBlock(int pieceIndex, int offset, const QByteArray &data)
358 qint64 startWriteIndex = (qint64(pieceIndex) * pieceLength) + offset;
359 qint64 currentIndex = 0;
360 int bytesToWrite = data.size();
361 int written = 0;
363 for (int i = 0; !quit && i < files.size(); ++i) {
364 QFile *file = files[i];
365 qint64 currentFileSize = fileSizes.at(i);
367 if ((currentIndex + currentFileSize) > startWriteIndex) {
368 if (!file->isOpen()) {
369 if (!file->open(QFile::ReadWrite)) {
370 errString = tr("Failed to write to file %1: %2")
371 .arg(file->fileName()).arg(file->errorString());
372 emit error();
373 break;
377 file->seek(startWriteIndex - currentIndex);
378 qint64 bytesWritten = file->write(data.constData() + written,
379 qMin<qint64>(bytesToWrite, currentFileSize - file->pos()));
380 file->close();
382 if (bytesWritten <= 0) {
383 errString = tr("Failed to write to file %1: %2")
384 .arg(file->fileName()).arg(file->errorString());
385 emit error();
386 return false;
389 written += bytesWritten;
390 startWriteIndex += bytesWritten;
391 bytesToWrite -= bytesWritten;
392 if (bytesToWrite == 0)
393 break;
395 currentIndex += currentFileSize;
397 return true;
400 void FileManager::verifyFileContents()
402 // Verify all pieces the first time
403 if (newPendingVerificationRequests.isEmpty()) {
404 if (verifiedPieces.count(true) == 0) {
405 verifiedPieces.resize(sha1s.size());
407 int oldPercent = 0;
408 if (!newFile) {
409 int numPieces = sha1s.size();
411 for (int index = 0; index < numPieces; ++index) {
412 verifySinglePiece(index);
414 int percent = ((index + 1) * 100) / numPieces;
415 if (oldPercent != percent) {
416 emit verificationProgress(percent);
417 oldPercent = percent;
422 emit verificationDone();
423 return;
426 // Verify all pending pieces
427 foreach (int index, newPendingVerificationRequests)
428 emit pieceVerified(index, verifySinglePiece(index));
431 bool FileManager::verifySinglePiece(int pieceIndex)
433 QByteArray block = readBlock(pieceIndex, 0, pieceLength);
434 QByteArray sha1Sum = QCryptographicHash::hash(block, QCryptographicHash::Sha1);
436 if (sha1Sum != sha1s.at(pieceIndex))
437 return false;
438 verifiedPieces.setBit(pieceIndex);
439 return true;
442 void FileManager::wakeUp()
444 QMutexLocker locker(&mutex);
445 wokeUp = false;
446 cond.wakeOne();