-
Notifications
You must be signed in to change notification settings - Fork 800
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implemented a class ChecksumCalculator to allow stopping the checksum…
… calculation thread on parent destruction. Signed-off-by: alex-z <[email protected]>
- Loading branch information
1 parent
bc203a1
commit 5b8c1ea
Showing
7 changed files
with
332 additions
and
143 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
/* | ||
* Copyright (C) 2023 by Oleksandr Zolotov <[email protected]> | ||
* | ||
* 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. | ||
*/ | ||
#include "checksumcalculator.h" | ||
|
||
#include <zlib.h> | ||
|
||
#include <QCryptographicHash> | ||
#include <QFile> | ||
#include <QLoggingCategory> | ||
|
||
namespace | ||
{ | ||
constexpr qint64 bufSize = 500 * 1024; | ||
} | ||
|
||
namespace OCC { | ||
|
||
Q_LOGGING_CATEGORY(lcChecksumCalculator, "nextcloud.common.checksumcalculator", QtInfoMsg) | ||
|
||
static QCryptographicHash::Algorithm algorithmTypeToQCryptoHashAlgorithm(ChecksumCalculator::AlgorithmType algorithmType) | ||
{ | ||
switch (algorithmType) { | ||
case ChecksumCalculator::AlgorithmType::Undefined: | ||
case ChecksumCalculator::AlgorithmType::Adler32: | ||
qCWarning(lcChecksumCalculator) << "Invalid algorithm type" << static_cast<int>(algorithmType); | ||
return static_cast<QCryptographicHash::Algorithm>(-1); | ||
case ChecksumCalculator::AlgorithmType::MD5: | ||
return QCryptographicHash::Algorithm::Md5; | ||
case ChecksumCalculator::AlgorithmType::SHA1: | ||
return QCryptographicHash::Algorithm::Sha1; | ||
case ChecksumCalculator::AlgorithmType::SHA256: | ||
return QCryptographicHash::Algorithm::Sha256; | ||
case ChecksumCalculator::AlgorithmType::SHA3_256: | ||
return QCryptographicHash::Algorithm::Sha3_256; | ||
} | ||
return static_cast<QCryptographicHash::Algorithm>(-1); | ||
} | ||
|
||
ChecksumCalculator::ChecksumCalculator(QSharedPointer<QIODevice> sharedDevice, const QByteArray &checksumTypeName) | ||
: _device(sharedDevice) | ||
{ | ||
if (checksumTypeName == checkSumMD5C) { | ||
_algorithmType = AlgorithmType::MD5; | ||
} else if (checksumTypeName == checkSumSHA1C) { | ||
_algorithmType = AlgorithmType::SHA1; | ||
} else if (checksumTypeName == checkSumSHA2C) { | ||
_algorithmType = AlgorithmType::SHA256; | ||
} else if (checksumTypeName == checkSumSHA3C) { | ||
_algorithmType = AlgorithmType::SHA3_256; | ||
} else if (checksumTypeName == checkSumAdlerC) { | ||
_algorithmType = AlgorithmType::Adler32; | ||
} | ||
|
||
initChecksumAlgorithm(); | ||
} | ||
|
||
ChecksumCalculator::~ChecksumCalculator() | ||
{ | ||
QMutexLocker locker(&_deviceMutex); | ||
if (_device && _device->isOpen()) { | ||
_device->close(); | ||
} | ||
} | ||
|
||
QByteArray ChecksumCalculator::calculate() | ||
{ | ||
QByteArray result; | ||
|
||
if (!_isInitialized) { | ||
return result; | ||
} | ||
|
||
Q_ASSERT(!_device->isOpen()); | ||
if (_device->isOpen()) { | ||
qCWarning(lcChecksumCalculator) << "Device already open. Ignoring."; | ||
} | ||
|
||
if (!_device->isOpen() && !_device->open(QIODevice::ReadOnly)) { | ||
if (auto file = qobject_cast<QFile *>(_device.data())) { | ||
qCWarning(lcChecksumCalculator) << "Could not open file" << file->fileName() << "for reading to compute a checksum" << file->errorString(); | ||
} else { | ||
qCWarning(lcChecksumCalculator) << "Could not open device" << _device.data() << "for reading to compute a checksum" << _device->errorString(); | ||
} | ||
return result; | ||
} | ||
|
||
bool isAnyChunkAdded = false; | ||
|
||
for (;;) { | ||
QMutexLocker locker(&_deviceMutex); | ||
if (!_device->isOpen() || _device->atEnd()) { | ||
break; | ||
} | ||
const auto toRead = qMin(_device->bytesAvailable(), bufSize); | ||
if (toRead <= 0) { | ||
break; | ||
} | ||
QByteArray buf(toRead, Qt::Uninitialized); | ||
const auto sizeRead = _device->read(buf.data(), toRead); | ||
if (sizeRead <= 0) { | ||
break; | ||
} | ||
if (!addChunk(buf, sizeRead)) { | ||
break; | ||
} | ||
isAnyChunkAdded = true; | ||
} | ||
|
||
{ | ||
QMutexLocker locker(&_deviceMutex); | ||
if (!_device->isOpen()) { | ||
return result; | ||
} | ||
} | ||
|
||
if (isAnyChunkAdded) { | ||
if (_algorithmType == AlgorithmType::Adler32) { | ||
result = QByteArray::number(_adlerHash, 16); | ||
} else { | ||
Q_ASSERT(_cryptographicHash); | ||
if (_cryptographicHash) { | ||
result = _cryptographicHash->result().toHex(); | ||
} | ||
} | ||
} | ||
|
||
{ | ||
QMutexLocker locker(&_deviceMutex); | ||
if (_device->isOpen()) { | ||
_device->close(); | ||
} | ||
} | ||
|
||
return result; | ||
} | ||
|
||
void ChecksumCalculator::initChecksumAlgorithm() | ||
{ | ||
if (_algorithmType == AlgorithmType::Undefined) { | ||
qCWarning(lcChecksumCalculator) << "_algorithmType is Undefined, impossible to init Checksum Algorithm"; | ||
return; | ||
} | ||
|
||
{ | ||
QMutexLocker locker(&_deviceMutex); | ||
if (_device->size() == 0) { | ||
return; | ||
} | ||
} | ||
|
||
if (_algorithmType == AlgorithmType::Adler32) { | ||
_adlerHash = adler32(0L, Z_NULL, 0); | ||
} else { | ||
_cryptographicHash.reset(new QCryptographicHash(algorithmTypeToQCryptoHashAlgorithm(_algorithmType))); | ||
} | ||
|
||
_isInitialized = true; | ||
} | ||
|
||
bool ChecksumCalculator::addChunk(const QByteArray &chunk, const qint64 size) | ||
{ | ||
Q_ASSERT(_algorithmType != AlgorithmType::Undefined); | ||
if (_algorithmType == AlgorithmType::Undefined) { | ||
qCWarning(lcChecksumCalculator) << "_algorithmType is Undefined, impossible to add a chunk!"; | ||
return false; | ||
} | ||
|
||
if (_algorithmType == AlgorithmType::Adler32) { | ||
_adlerHash = adler32(_adlerHash, (const Bytef *)chunk.data(), size); | ||
return true; | ||
} else { | ||
Q_ASSERT(_cryptographicHash); | ||
if (_cryptographicHash) { | ||
_cryptographicHash->addData(chunk.data(), size); | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
/* | ||
* Copyright (C) 2023 by Oleksandr Zolotov <[email protected]> | ||
* | ||
* 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. | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include "ocsynclib.h" | ||
#include "config.h" | ||
#include "checksumconsts.h" | ||
|
||
#include <QObject> | ||
#include <QByteArray> | ||
#include <QFutureWatcher> | ||
#include <QMutex> | ||
|
||
#include <memory> | ||
|
||
class QCryptographicHash; | ||
|
||
namespace OCC { | ||
class OCSYNC_EXPORT ChecksumCalculator | ||
{ | ||
Q_DISABLE_COPY(ChecksumCalculator) | ||
|
||
public: | ||
enum class AlgorithmType { | ||
Undefined = -1, | ||
MD5, | ||
SHA1, | ||
SHA256, | ||
SHA3_256, | ||
Adler32, | ||
}; | ||
|
||
ChecksumCalculator(QSharedPointer<QIODevice> sharedDevice, const QByteArray &checksumTypeName); | ||
~ChecksumCalculator(); | ||
[[nodiscard]] QByteArray calculate(); | ||
|
||
private: | ||
void initChecksumAlgorithm(); | ||
bool addChunk(const QByteArray &chunk, const qint64 size); | ||
QSharedPointer<QIODevice> _device; | ||
QScopedPointer<QCryptographicHash> _cryptographicHash; | ||
unsigned int _adlerHash = 0; | ||
bool _isInitialized = false; | ||
AlgorithmType _algorithmType = AlgorithmType::Undefined; | ||
QMutex _deviceMutex; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
/* | ||
* Copyright (C) 2023 by Oleksandr Zolotov <[email protected]> | ||
* | ||
* 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. | ||
*/ | ||
|
||
#pragma once | ||
|
||
namespace OCC | ||
{ | ||
/** | ||
* Tags for checksum headers values. | ||
* They are here for being shared between Upload- and Download Job | ||
*/ | ||
static constexpr auto checkSumMD5C = "MD5"; | ||
static constexpr auto checkSumSHA1C = "SHA1"; | ||
static constexpr auto checkSumSHA2C = "SHA256"; | ||
static constexpr auto checkSumSHA3C = "SHA3-256"; | ||
static constexpr auto checkSumAdlerC = "Adler32"; | ||
} |
Oops, something went wrong.