File indexing completed on 2024-04-28 15:39:58

0001 /* SPDX-FileCopyrightText: 2018 Johannes Zarl-Zierl <johannes@zarl-zierl.at>
0002    SPDX-FileCopyrightText: 2007-2010 Tuomas Suutari <thsuut@utu.fi>
0003 
0004    SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "MD5.h"
0008 
0009 #include <kpabase/FileName.h>
0010 
0011 #include <QCryptographicHash>
0012 #include <QFile>
0013 #include <QHash>
0014 #include <QIODevice>
0015 #include <QMutex>
0016 
0017 static QMutex s_MD5CacheMutex;
0018 static QHash<DB::FileName, DB::MD5> s_MD5Cache;
0019 
0020 DB::MD5::MD5()
0021     : m_isNull(true)
0022     , m_v0(0)
0023     , m_v1(0)
0024 {
0025 }
0026 
0027 DB::MD5::MD5(const QString &md5str)
0028     : m_isNull(md5str.isEmpty())
0029     , m_v0(md5str.leftRef(16).toULongLong(nullptr, 16))
0030     , m_v1(md5str.midRef(16, 16).toULongLong(nullptr, 16))
0031 {
0032 }
0033 
0034 bool DB::MD5::isNull() const
0035 {
0036     return m_isNull;
0037 }
0038 
0039 DB::MD5 &DB::MD5::operator=(const QString &md5str)
0040 {
0041     if (md5str.isEmpty()) {
0042         m_isNull = true;
0043     } else {
0044         m_isNull = false;
0045         m_v0 = md5str.leftRef(16).toULongLong(nullptr, 16);
0046         m_v1 = md5str.midRef(16, 16).toULongLong(nullptr, 16);
0047     }
0048     return *this;
0049 }
0050 
0051 QString DB::MD5::toHexString() const
0052 {
0053     QString res;
0054     static QChar ZERO(QChar::fromLatin1('0'));
0055     if (!isNull()) {
0056         res += QString::number(m_v0, 16).rightJustified(16, ZERO);
0057         res += QString::number(m_v1, 16).rightJustified(16, ZERO);
0058     }
0059     return res;
0060 }
0061 
0062 bool DB::MD5::operator==(const DB::MD5 &other) const
0063 {
0064     if (isNull() || other.isNull())
0065         return isNull() == other.isNull();
0066 
0067     return (m_v0 == other.m_v0 && m_v1 == other.m_v1);
0068 }
0069 
0070 bool DB::MD5::operator!=(const DB::MD5 &other) const
0071 {
0072     return !(*this == other);
0073 }
0074 
0075 bool DB::MD5::operator<(const DB::MD5 &other) const
0076 {
0077     if (isNull() || other.isNull())
0078         return isNull() && !other.isNull();
0079 
0080     return (m_v0 < other.m_v0 || (m_v0 == other.m_v0 && (m_v1 < other.m_v1)));
0081 }
0082 
0083 void DB::MD5::resetMD5Cache()
0084 {
0085     QMutexLocker locker(&s_MD5CacheMutex);
0086     s_MD5Cache.clear();
0087 }
0088 
0089 namespace
0090 {
0091 // Determined experimentally to yield best results (on Seagate 2TB 2.5" disk,
0092 // 5400 RPM).  Performance is very similar at 524288.  Above that, performance
0093 // was significantly worse.  Below that, performance also deteriorated.
0094 // This assumes use of one image scout thread (see DB/ImageScout.cpp).  Without
0095 // a scout thread, performance was about 10-15% worse.
0096 constexpr int MD5_BUFFER_SIZE = 262144;
0097 }
0098 
0099 DB::MD5 DB::MD5Sum(const DB::FileName &fileName)
0100 {
0101     QMutexLocker locker(&s_MD5CacheMutex);
0102     if (s_MD5Cache.contains(fileName)) {
0103         return s_MD5Cache[fileName];
0104     }
0105     locker.unlock();
0106     // It's possible that the checksum will be added between now
0107     // and when we add it, but as long as the file contents don't change
0108     // during that interval, the checksums will match.
0109     // Holding the lock while the checksum is being computed will
0110     // defeat the whole purpose.
0111     DB::MD5 checksum;
0112     QFile file(fileName.absolute());
0113     if (file.open(QIODevice::ReadOnly)) {
0114         QCryptographicHash md5calculator(QCryptographicHash::Md5);
0115         while (!file.atEnd()) {
0116             QByteArray md5Buffer(file.read(MD5_BUFFER_SIZE));
0117             md5calculator.addData(md5Buffer);
0118         }
0119         file.close();
0120         checksum = DB::MD5(QString::fromLatin1(md5calculator.result().toHex()));
0121     }
0122     locker.relock();
0123     s_MD5Cache[fileName] = checksum;
0124     return checksum;
0125 }
0126 
0127 void DB::PreloadMD5Sum(const DB::FileName &fileName)
0128 {
0129     (void)MD5Sum(fileName);
0130 }