Introduce basic (not-yet-compressing) implementation of Compressor
[quassel.git] / src / common / compressor.cpp
diff --git a/src/common/compressor.cpp b/src/common/compressor.cpp
new file mode 100644 (file)
index 0000000..9ee877c
--- /dev/null
@@ -0,0 +1,118 @@
+/***************************************************************************
+ *   Copyright (C) 2005-2014 by the Quassel Project                        *
+ *   devel@quassel-irc.org                                                 *
+ *                                                                         *
+ *   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) version 3.                                           *
+ *                                                                         *
+ *   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.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
+ ***************************************************************************/
+
+#include "compressor.h"
+
+#include <QTcpSocket>
+#include <QTimer>
+
+const qint64 maxBufferSize = 64 * 1024 * 1024; // protect us from zip bombs
+
+Compressor::Compressor(QTcpSocket *socket, Compressor::CompressionLevel level, QObject *parent)
+    : QObject(parent),
+    _socket(socket),
+    _level(level)
+{
+    _level = NoCompression; // compression not implemented yet
+
+    connect(socket, SIGNAL(readyRead()), SLOT(readData()));
+
+    // It's possible that more data has already arrived during the handshake, so readyRead() wouldn't be triggered.
+    // However, we want to give RemotePeer a chance to connect to our signals, so trigger this asynchronously.
+    if (socket->bytesAvailable())
+        QTimer::singleShot(0, this, SLOT(readData()));
+}
+
+
+qint64 Compressor::bytesAvailable() const
+{
+    return _readBuffer.size();
+}
+
+
+qint64 Compressor::read(char *data, qint64 maxSize)
+{
+    if (maxSize <= 0)
+        maxSize = _readBuffer.size();
+
+    qint64 n = qMin(maxSize, (qint64)_readBuffer.size());
+    memcpy(data, _readBuffer.constData(), n);
+
+    // TODO: don't copy for every read
+    if (n == _readBuffer.size())
+        _readBuffer.clear();
+    else
+        _readBuffer = _readBuffer.mid(n);
+
+    // If there's still data left in the socket buffer, make sure to schedule a read
+    if (_socket->bytesAvailable())
+        QTimer::singleShot(0, this, SLOT(readData()));
+
+    return n;
+}
+
+
+// The usual usage pattern is to write a blocksize first, followed by the actual data.
+// By setting NoFlush, one can indicate that the write buffer should not immediately be
+// written, which should make things a bit more efficient.
+qint64 Compressor::write(const char *data, qint64 count, WriteBufferHint flush)
+{
+    int pos = _writeBuffer.size();
+    _writeBuffer.resize(pos + count);
+    memcpy(_writeBuffer.data() + pos, data, count);
+
+    if (flush != NoFlush)
+        writeData();
+
+    return count;
+}
+
+
+void Compressor::readData()
+{
+    // don't try to read more data if we're already closing
+    if (_socket->state() !=  QAbstractSocket::ConnectedState)
+        return;
+
+    if (!_socket->bytesAvailable() || _readBuffer.size() >= maxBufferSize)
+        return;
+
+    if (compressionLevel() == NoCompression)
+        _readBuffer.append(_socket->read(maxBufferSize - _readBuffer.size()));
+
+    emit readyRead();
+}
+
+
+void Compressor::writeData()
+{
+    if (compressionLevel() == NoCompression) {
+        _socket->write(_writeBuffer);
+        _writeBuffer.clear();
+    }
+}
+
+
+void Compressor::flush()
+{
+    if (compressionLevel() == NoCompression && _socket->state() == QAbstractSocket::ConnectedState)
+        _socket->flush();
+
+}