1 /****************************************************************************
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the examples of the Qt Toolkit.
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
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.
40 ****************************************************************************/
42 #include "filemanager.h"
49 #include <QTimerEvent>
50 #include <QCryptographicHash>
52 FileManager::FileManager(QObject
*parent
)
58 startVerification
= false;
62 verifiedPieces
.fill(false);
65 FileManager::~FileManager()
71 foreach (QFile
*file
, files
) {
77 int FileManager::read(int pieceIndex
, int offset
, int length
)
80 request
.pieceIndex
= pieceIndex
;
81 request
.offset
= offset
;
82 request
.length
= length
;
84 QMutexLocker
locker(&mutex
);
85 request
.id
= readId
++;
86 readRequests
<< request
;
90 QMetaObject::invokeMethod(this, "wakeUp", Qt::QueuedConnection
);
96 void FileManager::write(int pieceIndex
, int offset
, const QByteArray
&data
)
99 request
.pieceIndex
= pieceIndex
;
100 request
.offset
= offset
;
103 QMutexLocker
locker(&mutex
);
104 writeRequests
<< request
;
108 QMetaObject::invokeMethod(this, "wakeUp", Qt::QueuedConnection
);
112 void FileManager::verifyPiece(int pieceIndex
)
114 QMutexLocker
locker(&mutex
);
115 pendingVerificationRequests
<< pieceIndex
;
116 startVerification
= 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
148 void FileManager::run()
150 if (!generateFiles())
155 // Go to sleep if there's nothing to do.
156 QMutexLocker
locker(&mutex
);
157 if (!quit
&& readRequests
.isEmpty() && writeRequests
.isEmpty() && !startVerification
)
161 // Read pending read requests
163 QList
<ReadRequest
> newReadRequests
= readRequests
;
164 readRequests
.clear();
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
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;
189 newPendingVerificationRequests
.clear();
193 // Write pending write requests
195 QList
<WriteRequest
> newWriteRequests
= writeRequests
;
196 writeRequests
.clear();
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;
211 bool FileManager::generateFiles()
215 // Set up the thread local data
216 if (metaInfo
.fileForm() == MetaInfo::SingleFileForm
) {
217 QMutexLocker
locker(&mutex
);
218 MetaInfoSingleFile singleFile
= metaInfo
.singleFile();
221 if (!destinationPath
.isEmpty()) {
222 prefix
= destinationPath
;
223 if (!prefix
.endsWith("/"))
226 if (!dir
.mkpath(prefix
)) {
227 errString
= tr("Failed to create directory %1").arg(prefix
);
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());
240 if (file
->size() != singleFile
.length
) {
242 if (!file
->resize(singleFile
.length
)) {
243 errString
= tr("Failed to resize file %1: %2")
244 .arg(file
->fileName()).arg(file
->errorString());
249 fileSizes
<< file
->size();
253 pieceLength
= singleFile
.pieceLength
;
254 totalLength
= singleFile
.length
;
255 sha1s
= singleFile
.sha1Sums
;
257 QMutexLocker
locker(&mutex
);
261 if (!destinationPath
.isEmpty()) {
262 prefix
= destinationPath
;
263 if (!prefix
.endsWith("/"))
266 if (!metaInfo
.name().isEmpty()) {
267 prefix
+= metaInfo
.name();
268 if (!prefix
.endsWith("/"))
271 if (!dir
.mkpath(prefix
)) {
272 errString
= tr("Failed to create directory %1").arg(prefix
);
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
);
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());
295 if (file
->size() != entry
.length
) {
297 if (!file
->resize(entry
.length
)) {
298 errString
= tr("Failed to resize file %1: %2")
299 .arg(file
->fileName()).arg(file
->errorString());
304 fileSizes
<< file
->size();
308 totalLength
+= entry
.length
;
311 sha1s
= metaInfo
.sha1Sums();
312 pieceLength
= metaInfo
.pieceLength();
314 numPieces
= sha1s
.size();
318 QByteArray
FileManager::readBlock(int pieceIndex
, int offset
, int length
)
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());
337 file
->seek(startReadIndex
- currentIndex
);
338 QByteArray chunk
= file
->read(qMin
<qint64
>(length
, currentFileSize
- file
->pos()));
342 length
-= chunk
.size();
343 startReadIndex
+= chunk
.size();
345 errString
= tr("Failed to read from file %1 (read %3 bytes): %2")
346 .arg(file
->fileName()).arg(file
->errorString()).arg(length
);
351 currentIndex
+= currentFileSize
;
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();
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());
377 file
->seek(startWriteIndex
- currentIndex
);
378 qint64 bytesWritten
= file
->write(data
.constData() + written
,
379 qMin
<qint64
>(bytesToWrite
, currentFileSize
- file
->pos()));
382 if (bytesWritten
<= 0) {
383 errString
= tr("Failed to write to file %1: %2")
384 .arg(file
->fileName()).arg(file
->errorString());
389 written
+= bytesWritten
;
390 startWriteIndex
+= bytesWritten
;
391 bytesToWrite
-= bytesWritten
;
392 if (bytesToWrite
== 0)
395 currentIndex
+= currentFileSize
;
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());
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();
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
))
438 verifiedPieces
.setBit(pieceIndex
);
442 void FileManager::wakeUp()
444 QMutexLocker
locker(&mutex
);