--- /dev/null
+#ifndef CIRCULARBUFFER_H
+#define CIRCULARBUFFER_H
+
+#include <QSemaphore>
+
+namespace AniDBUdpClient {
+namespace HashPrivate {
+
+template<typename T, int SIZE> class CircularBuffer
+{
+public:
+ CircularBuffer()
+ {
+ // Set N to SIZE
+ free.release(SIZE);
+ r = w = 0;
+ m_end = false;
+ }
+
+ void put(T data, bool last = false)
+ {
+ if (m_end) return;
+
+ free.acquire();
+ buffer[w] = data;
+ m_end = last;
+ used.release();
+ w++;
+ w %= SIZE;
+ }
+
+ T get()
+ {
+ used.acquire();
+ T data = buffer[r];
+ free.release();
+ r++;
+ r %= SIZE;
+ return data;
+ }
+
+ bool end() const
+ {
+ return m_end && r == w;
+ }
+
+ bool reset()
+ {
+ if (!end())
+ return false;
+
+ m_end = false;
+ r = w = 0;
+ return true;
+ }
+
+private:
+ T buffer[SIZE];
+ QSemaphore free;
+ QSemaphore used;
+
+ int r;
+ int w;
+ bool m_end;
+};
+
+typedef CircularBuffer<QByteArray, 2> Buffer;
+
+} // namespace HashPrivate
+} // namesapce AniDBUdpClient
+
+#endif // CIRCULARBUFFER_H
--- /dev/null
+#include "hash.h"
+
+#include <QDebug>
+
+namespace AniDBUdpClient {
+
+Hash::Hash(QObject *parent) : QObject(parent)
+{
+ producer = 0;
+ consumer = 0;
+ buffer = 0;
+ hashing = false;
+ setUp();
+}
+
+Hash::~Hash()
+{
+ tearDown();
+}
+
+void Hash::hashFile(const QFileInfo &file)
+{
+qDebug() << "Hash::hashFile";
+ fileQueue.enqueue(file);
+
+ if (hashing)
+ return;
+
+ emit startHashing(fileQueue.first().absoluteFilePath());
+}
+
+void Hash::endHashing(const QByteArray &hash)
+{
+qDebug() << "Hash::endHashing";
+ QFileInfo f = fileQueue.dequeue();
+
+ if (!fileQueue.isEmpty())
+ {
+ emit startHashing(fileQueue.first().absoluteFilePath());
+ }
+ else
+ {
+ hashing = false;
+ }
+ emit fileHashed(f, hash);
+qDebug() << "FILE" << f.fileName() << "HASH" << hash;
+}
+
+
+void Hash::setUp()
+{
+ if (producer || consumer || buffer)
+ return;
+
+ buffer = new HashPrivate::Buffer;
+ producer = new HashPrivate::HashProducer(buffer, this);
+ consumer = new HashPrivate::HashConsumer(buffer, this);
+ connect(this, SIGNAL(startHashing(QString)), consumer, SLOT(hashFile(QString)), Qt::QueuedConnection);
+ connect(this, SIGNAL(startHashing(QString)), producer, SLOT(readFile(QString)), Qt::QueuedConnection);
+ connect(consumer, SIGNAL(finishedHashing(QByteArray)), this, SLOT(endHashing(QByteArray)), Qt::QueuedConnection);
+
+ producer->start();
+ consumer->start();
+}
+
+void Hash::tearDown()
+{
+ if (!producer || !consumer || !buffer)
+ return;
+
+ producer->stop();
+ consumer->stop();
+ producer->wait();
+ consumer->wait();
+
+ delete producer;
+ delete consumer;
+ delete buffer;
+
+ producer = 0;
+ consumer = 0;
+ buffer = 0;
+}
+
+} // namesapce AniDBUdpClient
--- /dev/null
+#ifndef HASH_H
+#define HASH_H
+
+#include "anidbudpclient_global.h"
+#include <QObject>
+#include <QQueue>
+#include <QMap>
+#include <QFileInfo>
+
+#include "hashproducer.h"
+#include "hashconsumer.h"
+
+namespace AniDBUdpClient {
+
+class ANIDBUDPCLIENTSHARED_EXPORT Hash : public QObject
+{
+
+ Q_OBJECT
+
+public:
+ Hash(QObject *parent = 0);
+ ~Hash();
+
+ void hashFile(const QFileInfo &file);
+
+signals:
+ void startHashing(const QString &file);
+ void fileHashed(const QFileInfo &file, const QByteArray &hash);
+
+private slots:
+ void endHashing(const QByteArray &hash);
+
+private:
+ void setUp();
+ void tearDown();
+
+ HashPrivate::Buffer *buffer;
+ HashPrivate::HashProducer *producer;
+ HashPrivate::HashConsumer *consumer;
+
+ QQueue<QFileInfo> fileQueue;
+ QMap<QFileInfo, QByteArray> hashedFiles;
+
+ bool hashing;
+};
+
+} // namesapce AniDBUdpClient
+
+#endif // HASH_H
--- /dev/null
+#include "hashconsumer.h"
+
+#include <QDebug>
+
+namespace AniDBUdpClient {
+namespace HashPrivate {
+
+HashConsumer::HashConsumer(Buffer *buffer, QObject *parent) : QThread(parent)
+{
+ this->buffer = buffer;
+ hash = new QCryptographicHash(QCryptographicHash::Md4);
+ connect(this, SIGNAL(startHashing()), this, SLOT(doHash()));
+}
+
+HashConsumer::~HashConsumer()
+{
+ delete hash;
+}
+
+void HashConsumer::hashFile(const QString &file)
+{
+qDebug() << "hashFile()";
+ fileSize = QFileInfo(file).size();
+
+ emit startHashing();
+}
+
+void HashConsumer::stop()
+{
+ m_stop = true;
+ quit();
+}
+
+void HashConsumer::run()
+{
+ exec();
+}
+
+void HashConsumer::doHash()
+{
+ while (!buffer->end())
+ {
+qDebug() << "doHash()->while(" << buffer->end() << ")";
+ hashSome();
+ }
+ buffer->reset();
+ hash->reset();
+}
+
+void HashConsumer::hashSome()
+{
+ QByteArray data = buffer->get();
+
+ hash->addData(QCryptographicHash::hash(data, QCryptographicHash::Md4));
+
+ if (buffer->end())
+ emit finishedHashing(hash->result());
+
+qDebug() << "hashSome()";
+}
+
+} // namespace HashPrivate
+} // namesapce AniDBUdpClient
--- /dev/null
+#ifndef HASHCONSUMER_H
+#define HASHCONSUMER_H
+
+#include "anidbudpclient_global.h"
+#include <QThread>
+#include <QCryptographicHash>
+#include <QFile>
+#include <QFileInfo>
+
+#include "circularbuffer.h"
+
+namespace AniDBUdpClient {
+namespace HashPrivate {
+
+class HashConsumer : public QThread
+{
+ Q_OBJECT
+public:
+ HashConsumer(Buffer *buffer, QObject *parent = 0);
+ ~HashConsumer();
+
+public slots:
+ void hashFile(const QString &file);
+
+ void stop();
+
+protected:
+ void run();
+
+signals:
+ void startHashing();
+ void finishedHashing(QByteArray hash);
+
+private slots:
+ void doHash();
+
+private:
+ void hashSome();
+
+ Buffer *buffer;
+ QCryptographicHash *hash;
+ qint64 fileSize;
+
+ bool m_stop;
+};
+
+} // namespace HashPrivate
+} // namesapce AniDBUdpClient
+
+#endif // HASHCONSUMER_H
--- /dev/null
+#include "hashproducer.h"
+
+#include <QDebug>
+
+namespace AniDBUdpClient {
+namespace HashPrivate {
+
+HashProducer::HashProducer(Buffer *buffer, QObject *parent) : QThread(parent)
+{
+ this->buffer = buffer;
+ connect(this, SIGNAL(startReading()), this, SLOT(doRead()));
+}
+
+void HashProducer::readFile(const QString &file)
+{
+qDebug() << "readFile";
+ this->file.setFileName(file);
+
+ fileSize = file.size();
+
+ if (!this->file.open(QIODevice::ReadOnly))
+ {
+qDebug() << "Failed toopen file" << this->file.fileName();
+ return;
+ }
+
+ emit startReading();
+}
+
+void HashProducer::stop()
+{
+ m_stop = true;
+ quit();
+}
+
+void HashProducer::run()
+{
+ exec();
+}
+
+void HashProducer::doRead()
+{
+ while (!this->file.atEnd())
+ {
+qDebug() << "doRead->while(" << (!this->file.atEnd()) << ")";
+ readSome();
+ }
+ this->file.close();
+}
+
+void HashProducer::readSome()
+{
+ QByteArray data = file.read(ED2K_PART_SIZE);
+qDebug() << "readSome";
+ buffer->put(data, file.atEnd());
+}
+
+} // namespace HashPrivate
+} // namesapce AniDBUdpClient
--- /dev/null
+#ifndef HASHPRODUCER_H
+#define HASHPRODUCER_H
+
+#include "anidbudpclient_global.h"
+#include <QThread>
+#include <QFile>
+#include <QFileInfo>
+
+#include "circularbuffer.h"
+
+namespace AniDBUdpClient {
+namespace HashPrivate {
+
+class HashProducer : public QThread
+{
+ Q_OBJECT
+
+public:
+ HashProducer(Buffer *buffer, QObject *parent = 0);
+
+public slots:
+ void readFile(const QString &file);
+
+ void stop();
+protected:
+ void run();
+
+signals:
+ void startReading();
+ void finishedReading();
+
+private slots:
+ void doRead();
+
+private:
+ void readSome();
+
+ Buffer *buffer;
+ QFile file;
+ qint64 fileSize;
+
+ bool m_stop;
+};
+
+} // namespace HashPrivate
+} // namesapce AniDBUdpClient
+
+#endif // HASHPRODUCER_H
--- /dev/null
+#include "../../hash.h"