Compare commits
10 Commits
ac876e196e
...
bdb13d9e36
| Author | SHA1 | Date |
|---|---|---|
|
|
bdb13d9e36 | |
|
|
953316e4d0 | |
|
|
be19213978 | |
|
|
744781a03d | |
|
|
1374064c0b | |
|
|
fa702fbe15 | |
|
|
d42dc66f4e | |
|
|
5345c07c9f | |
|
|
3458be07d8 | |
|
|
12df269ae0 |
|
|
@ -18,3 +18,4 @@ moc_*.cpp
|
||||||
qrc_*.cpp
|
qrc_*.cpp
|
||||||
Makefile
|
Makefile
|
||||||
*-build-*
|
*-build-*
|
||||||
|
build/
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@ set(QREDIS_PATCH 0)
|
||||||
set(QREDIS_VERSION "${QREDIS_MAJOR}.${QREDIS_MINOR}.${QREDIS_PATCH}")
|
set(QREDIS_VERSION "${QREDIS_MAJOR}.${QREDIS_MINOR}.${QREDIS_PATCH}")
|
||||||
|
|
||||||
# The QtCore and QtNetwork libraries are both required.
|
# The QtCore and QtNetwork libraries are both required.
|
||||||
find_package(Qt5Core REQUIRED)
|
find_package(Qt6Core REQUIRED)
|
||||||
find_package(Qt5Network REQUIRED)
|
find_package(Qt6Network REQUIRED)
|
||||||
|
|
||||||
# Specify where QRedis includes are located.
|
# Specify where QRedis includes are located.
|
||||||
include_directories(include)
|
include_directories(include)
|
||||||
|
|
@ -27,7 +27,7 @@ set(QREDIS_SRC
|
||||||
src/request.cpp)
|
src/request.cpp)
|
||||||
|
|
||||||
# QRedis header files requiring MOC.
|
# QRedis header files requiring MOC.
|
||||||
qt5_wrap_cpp(QREDIS_MOC
|
qt6_wrap_cpp(QREDIS_MOC
|
||||||
include/qredis/client.h
|
include/qredis/client.h
|
||||||
include/qredis/request.h
|
include/qredis/request.h
|
||||||
src/client_p.h
|
src/client_p.h
|
||||||
|
|
@ -46,7 +46,7 @@ set_target_properties(qredis PROPERTIES
|
||||||
SOVERSION ${QREDIS_MAJOR})
|
SOVERSION ${QREDIS_MAJOR})
|
||||||
|
|
||||||
# This causes the appropriate libraries to be included in the link process.
|
# This causes the appropriate libraries to be included in the link process.
|
||||||
qt5_use_modules(qredis Core Network)
|
target_link_libraries(qredis Qt6::Core Qt6::Network)
|
||||||
|
|
||||||
# Specify the proper installation paths.
|
# Specify the proper installation paths.
|
||||||
install(TARGETS qredis
|
install(TARGETS qredis
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
## QRedis
|
## QRedis
|
||||||
|
|
||||||
QRedis provides a modern Qt client library for communicating with a [Redis server](http://redis.io/). The code compiles exclusively with Qt 5, ensuring years of compatibility down the road.
|
QRedis provides a modern Qt client library for communicating with a [Redis server](http://redis.io/). The code compiles exclusively with Qt 5, ensuring years of compatibility down the road. Modified with Qt 6 compatibility by Jesse Qu.
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
#ifndef QREDIS_REPLY_H
|
||||||
|
#define QREDIS_REPLY_H
|
||||||
|
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
#include "qredis_export.h"
|
||||||
|
|
||||||
|
namespace QRedis
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @brief Represents a Redis reply
|
||||||
|
*/
|
||||||
|
class QREDIS_EXPORT Reply
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reply types
|
||||||
|
*/
|
||||||
|
enum Type {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An invalid reply
|
||||||
|
*
|
||||||
|
* This value is only set when the default constructor is used.
|
||||||
|
*/
|
||||||
|
Invalid,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A status reply
|
||||||
|
*
|
||||||
|
* The value property will contain the status message returned
|
||||||
|
* by the server as a QString.
|
||||||
|
*/
|
||||||
|
Status,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An error reply
|
||||||
|
*
|
||||||
|
* The value property will contain the error message returned by
|
||||||
|
* the server as a QString.
|
||||||
|
*/
|
||||||
|
Error,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An integer reply
|
||||||
|
*
|
||||||
|
* The value property will contain the integer value returned by
|
||||||
|
* the server as a qlonglong.
|
||||||
|
*/
|
||||||
|
Integer,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A bulk reply
|
||||||
|
*
|
||||||
|
* The value property will contain the bulk reply returned by
|
||||||
|
* the server as a QByteArray.
|
||||||
|
*/
|
||||||
|
Bulk,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A multi-bulk reply
|
||||||
|
*
|
||||||
|
* The value property will contain the multi-bulk reply returned
|
||||||
|
* by the server as a QVariantList. Each entry in the list is of
|
||||||
|
* type Reply.
|
||||||
|
*/
|
||||||
|
MultiBulk
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates an empty reply
|
||||||
|
*/
|
||||||
|
Reply() : _type(Invalid) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initializes the reply
|
||||||
|
* @param type the type of the reply
|
||||||
|
*/
|
||||||
|
Reply(Type type) : _type(type) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the type of the reply
|
||||||
|
* @return the reply type
|
||||||
|
*/
|
||||||
|
Type type() const { return _type; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a reference to the value of the reply
|
||||||
|
* @return the reply value
|
||||||
|
*/
|
||||||
|
QVariant & value() { return _value; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
Type _type;
|
||||||
|
QVariant _value;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(QRedis::Reply)
|
||||||
|
|
||||||
|
#endif // QREDIS_REPLY_H
|
||||||
|
|
@ -3,8 +3,8 @@
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QScopedPointer>
|
#include <QScopedPointer>
|
||||||
#include <QVariantList>
|
|
||||||
|
|
||||||
|
#include <qredis/reply.h>
|
||||||
#include "qredis_export.h"
|
#include "qredis_export.h"
|
||||||
|
|
||||||
namespace QRedis
|
namespace QRedis
|
||||||
|
|
@ -12,7 +12,7 @@ namespace QRedis
|
||||||
class QREDIS_EXPORT RequestPrivate;
|
class QREDIS_EXPORT RequestPrivate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Represents a request and its reply
|
* @brief Represents a Redis command and its response
|
||||||
*/
|
*/
|
||||||
class QREDIS_EXPORT Request : public QObject
|
class QREDIS_EXPORT Request : public QObject
|
||||||
{
|
{
|
||||||
|
|
@ -21,8 +21,8 @@ namespace QRedis
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Creates a request
|
* @brief Initializes the request
|
||||||
* @param parent
|
* @param parent the parent QObject
|
||||||
*/
|
*/
|
||||||
explicit Request(QObject * parent = 0);
|
explicit Request(QObject * parent = 0);
|
||||||
|
|
||||||
|
|
@ -41,39 +41,10 @@ namespace QRedis
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Emitted when a bulk reply is received
|
* @brief Emitted when a reply is received
|
||||||
* @param value the value as a byte array
|
* @param reply the reply received
|
||||||
*/
|
*/
|
||||||
void bulk(const QByteArray & value);
|
void reply(Reply & reply);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Emitted when an error reply is received
|
|
||||||
* @param message a descriptive error message
|
|
||||||
*/
|
|
||||||
void error(const QString & message);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Emitted when an integer reply is received
|
|
||||||
* @param value the integer value
|
|
||||||
*/
|
|
||||||
void integer(qlonglong value);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Emitted when a multi-bulk reply is received
|
|
||||||
* @param a list of bulk values
|
|
||||||
*/
|
|
||||||
void multiBulk(const QVariantList & values);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Emitted when any reply is received
|
|
||||||
*/
|
|
||||||
void reply();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Emitted when a status reply is received
|
|
||||||
* @param message a descriptive status message
|
|
||||||
*/
|
|
||||||
void status(const QString & message);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
|
||||||
120
src/client.cpp
120
src/client.cpp
|
|
@ -4,124 +4,16 @@
|
||||||
using namespace QRedis;
|
using namespace QRedis;
|
||||||
|
|
||||||
ClientPrivate::ClientPrivate(Client * client)
|
ClientPrivate::ClientPrivate(Client * client)
|
||||||
: q(client)
|
: lexer(&socket), parser(&lexer)
|
||||||
{
|
{
|
||||||
connect(&socket, &QTcpSocket::connected, q, &Client::connected);
|
connect(&socket, SIGNAL(connected()), client, SIGNAL(connected()));
|
||||||
connect(&socket, &QTcpSocket::disconnected, q, &Client::disconnected);
|
connect(&socket, SIGNAL(disconnected()), client, SIGNAL(disconnected()));
|
||||||
connect(&socket, &QTcpSocket::disconnected, this, &ClientPrivate::reset);
|
connect(&parser, SIGNAL(reply(QRedis::Reply&)), SLOT(sendReply(Reply&)));
|
||||||
connect(&socket, &QTcpSocket::readyRead, this, &ClientPrivate::readReply);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int ClientPrivate::readInteger(qlonglong & value)
|
void ClientPrivate::sendReply(Reply & reply)
|
||||||
{
|
{
|
||||||
int pos = buffer.indexOf("\r\n");
|
emit queue.dequeue()->reply(reply);
|
||||||
if(pos != -1)
|
|
||||||
{
|
|
||||||
value = buffer.mid(0, pos).toLongLong();
|
|
||||||
buffer.remove(0, pos + 2);
|
|
||||||
}
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Note: error replies actually contain a type and then the error description
|
|
||||||
* but we just combine them here for simplicity.
|
|
||||||
*/
|
|
||||||
|
|
||||||
bool ClientPrivate::readStatusOrErrorReply()
|
|
||||||
{
|
|
||||||
/* Check if the reply contains \r\n. */
|
|
||||||
int pos = buffer.indexOf("\r\n");
|
|
||||||
if(pos != -1)
|
|
||||||
{
|
|
||||||
Request * request = queue.dequeue();
|
|
||||||
|
|
||||||
if(buffer[0] == '+')
|
|
||||||
emit request->status(buffer.mid(1, pos - 1));
|
|
||||||
else
|
|
||||||
emit request->error(buffer.mid(1, pos - 1));
|
|
||||||
|
|
||||||
buffer.remove(0, pos + 2);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClientPrivate::readIntegerReply()
|
|
||||||
{
|
|
||||||
/* Check if the reply contains \r\n. */
|
|
||||||
int pos = buffer.indexOf("\r\n");
|
|
||||||
if(pos != -1)
|
|
||||||
{
|
|
||||||
emit queue.dequeue()->integer(buffer.mid(1, pos -1).toLongLong());
|
|
||||||
|
|
||||||
buffer.remove(0, pos + 2);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClientPrivate::readBulkReply()
|
|
||||||
{
|
|
||||||
/* Check if the reply contains \r\n. */
|
|
||||||
int pos = buffer.indexOf("\r\n");
|
|
||||||
if(pos != -1)
|
|
||||||
{
|
|
||||||
int length = buffer.mid(1, pos -1).toInt();
|
|
||||||
if(buffer.size() >= pos + length + 4)
|
|
||||||
{
|
|
||||||
emit queue.dequeue()->bulk(buffer.mid(pos + 2, length));
|
|
||||||
|
|
||||||
buffer.remove(0, pos + length + 4);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClientPrivate::readMultiBulkReply()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientPrivate::reset()
|
|
||||||
{
|
|
||||||
foreach(Request * request, queue)
|
|
||||||
request->deleteLater();
|
|
||||||
queue.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: unrecognized replies in the switch should be handled.
|
|
||||||
|
|
||||||
void ClientPrivate::readReply()
|
|
||||||
{
|
|
||||||
buffer.append(socket.readAll());
|
|
||||||
while(!buffer.isEmpty())
|
|
||||||
{
|
|
||||||
bool finished;
|
|
||||||
switch(buffer[0])
|
|
||||||
{
|
|
||||||
case '+':
|
|
||||||
case '-':
|
|
||||||
finished = readStatusOrErrorReply();
|
|
||||||
break;
|
|
||||||
case ':':
|
|
||||||
finished = readIntegerReply();
|
|
||||||
break;
|
|
||||||
case '$':
|
|
||||||
finished = readBulkReply();
|
|
||||||
break;
|
|
||||||
case '*':
|
|
||||||
finished = readMultiBulkReply();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!finished)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Client::Client(QObject * parent)
|
Client::Client(QObject * parent)
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,10 @@
|
||||||
#include <QTcpSocket>
|
#include <QTcpSocket>
|
||||||
|
|
||||||
#include <qredis/client.h>
|
#include <qredis/client.h>
|
||||||
|
#include <qredis/reply.h>
|
||||||
#include <qredis/request.h>
|
#include <qredis/request.h>
|
||||||
|
#include "lexer.h"
|
||||||
|
#include "parser.h"
|
||||||
|
|
||||||
namespace QRedis
|
namespace QRedis
|
||||||
{
|
{
|
||||||
|
|
@ -18,27 +21,15 @@ namespace QRedis
|
||||||
|
|
||||||
ClientPrivate(Client *);
|
ClientPrivate(Client *);
|
||||||
|
|
||||||
/* Utility methods for reading items from the buffer. */
|
|
||||||
int readInteger(qlonglong &);
|
|
||||||
|
|
||||||
bool readStatusOrErrorReply();
|
|
||||||
bool readIntegerReply();
|
|
||||||
bool readBulkReply();
|
|
||||||
bool readMultiBulkReply();
|
|
||||||
|
|
||||||
QTcpSocket socket;
|
QTcpSocket socket;
|
||||||
|
|
||||||
QQueue<Request *> queue;
|
QQueue<Request *> queue;
|
||||||
QByteArray buffer;
|
|
||||||
|
|
||||||
public Q_SLOTS:
|
Lexer lexer;
|
||||||
|
Parser parser;
|
||||||
|
|
||||||
void reset();
|
private Q_SLOTS:
|
||||||
void readReply();
|
|
||||||
|
|
||||||
private:
|
void sendReply(Reply &);
|
||||||
|
|
||||||
Client * const q;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
#include "lexer.h"
|
#include "lexer.h"
|
||||||
|
|
||||||
|
using namespace QRedis;
|
||||||
|
|
||||||
Lexer::Lexer(QIODevice * device, QObject * parent)
|
Lexer::Lexer(QIODevice * device, QObject * parent)
|
||||||
: QObject(parent), device(device), state(DoingNothing), crlf(0)
|
: QObject(parent), device(device), state(DoingNothing), crlf(0)
|
||||||
{
|
{
|
||||||
|
|
@ -22,8 +24,8 @@ void Lexer::readData()
|
||||||
switch(state)
|
switch(state)
|
||||||
{
|
{
|
||||||
case ReadingLength:
|
case ReadingLength:
|
||||||
case ReadingUnsafeString: if(!readUnsafeString()) return; break;
|
case ReadingUnsafeString: if(!readUnsafeString()) return;
|
||||||
case ReadingSafeString: if(!readSafeString()) return; break;
|
case ReadingSafeString: if(!readSafeString()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(state != ReadingSafeString)
|
if(state != ReadingSafeString)
|
||||||
|
|
@ -73,6 +75,7 @@ bool Lexer::readUnsafeString()
|
||||||
else
|
else
|
||||||
emit unsafeString(s);
|
emit unsafeString(s);
|
||||||
|
|
||||||
|
crlf = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -85,5 +88,7 @@ bool Lexer::readSafeString()
|
||||||
buffer.remove(0, length + 2);
|
buffer.remove(0, length + 2);
|
||||||
|
|
||||||
emit safeString(d);
|
emit safeString(d);
|
||||||
|
|
||||||
|
state = DoingNothing;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
63
src/lexer.h
63
src/lexer.h
|
|
@ -1,47 +1,50 @@
|
||||||
#ifndef LEXER_H
|
#ifndef QREDIS_LEXER_H
|
||||||
#define LEXER_H
|
#define QREDIS_LEXER_H
|
||||||
|
|
||||||
#include <QIODevice>
|
#include <QIODevice>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
class Lexer : public QObject
|
namespace QRedis
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
class Lexer : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Lexer(QIODevice *, QObject * = 0);
|
Lexer(QIODevice *, QObject * = 0);
|
||||||
virtual ~Lexer();
|
virtual ~Lexer();
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
|
|
||||||
void character(char);
|
void character(char);
|
||||||
void unsafeString(const QString &);
|
void unsafeString(const QString &);
|
||||||
void safeString(const QByteArray &);
|
void safeString(const QByteArray &);
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
|
|
||||||
void readData();
|
void readData();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
bool readCharacter();
|
bool readCharacter();
|
||||||
bool readLength();
|
bool readLength();
|
||||||
bool readUnsafeString();
|
bool readUnsafeString();
|
||||||
bool readSafeString();
|
bool readSafeString();
|
||||||
|
|
||||||
QIODevice * device;
|
QIODevice * device;
|
||||||
QByteArray buffer;
|
QByteArray buffer;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
DoingNothing,
|
DoingNothing,
|
||||||
ReadingLength,
|
ReadingLength,
|
||||||
ReadingUnsafeString,
|
ReadingUnsafeString,
|
||||||
ReadingSafeString
|
ReadingSafeString
|
||||||
} state;
|
} state;
|
||||||
|
|
||||||
int crlf;
|
int crlf;
|
||||||
int length;
|
int length;
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#endif // LEXER_H
|
#endif // QREDIS_LEXER_H
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
|
|
||||||
|
using namespace QRedis;
|
||||||
|
|
||||||
Parser::Parser(Lexer * lexer, QObject * parent)
|
Parser::Parser(Lexer * lexer, QObject * parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
{
|
{
|
||||||
|
|
@ -16,70 +18,48 @@ void Parser::readCharacter(char c)
|
||||||
{
|
{
|
||||||
switch(c)
|
switch(c)
|
||||||
{
|
{
|
||||||
case '+': stack.append(Task(Task::ReadStatus)); break;
|
case '+': stack.append(Task(Reply::Status)); break;
|
||||||
case '-': stack.append(Task(Task::ReadError)); break;
|
case '-': stack.append(Task(Reply::Error)); break;
|
||||||
case ':': stack.append(Task(Task::ReadInteger)); break;
|
case ':': stack.append(Task(Reply::Integer)); break;
|
||||||
case '$': stack.append(Task(Task::ReadBulk)); break;
|
case '$': stack.append(Task(Reply::Bulk)); break;
|
||||||
case '*': stack.append(Task(Task::ReadMultiBulk)); break;
|
case '*': stack.append(Task(Reply::MultiBulk)); break;
|
||||||
default:
|
default:
|
||||||
emit warning(tr("Skipping unexpected character '%1'").arg(static_cast<int>(c), 0, 16));
|
qWarning("Skipping unexpected character '%x'", static_cast<int>(c));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Parser::readUnsafeString(const QString & value)
|
void Parser::readUnsafeString(const QString & value)
|
||||||
{
|
{
|
||||||
if(tos().action == Task::ReadMultiBulk)
|
if(tos().reply.type() == Reply::MultiBulk)
|
||||||
tos().count = value.toInt();
|
tos().count = value.toInt();
|
||||||
else
|
else
|
||||||
{
|
tos().reply.value() = value;
|
||||||
stack.removeLast();
|
|
||||||
|
|
||||||
if(tos().action == Task::ReadStatus)
|
descend();
|
||||||
emit status(value);
|
|
||||||
else if(tos().action == Task::ReadError)
|
|
||||||
{
|
|
||||||
int pos = value.indexOf(' ');
|
|
||||||
emit error(value.left(pos), value.right(pos + 1));
|
|
||||||
}
|
|
||||||
else if(!stack.count())
|
|
||||||
emit integer(value.toLongLong());
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tos().value.toList().append(value);
|
|
||||||
descend();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Parser::readSafeString(const QByteArray & value)
|
void Parser::readSafeString(const QByteArray & value)
|
||||||
{
|
{
|
||||||
stack.removeLast();
|
tos().reply.value() = value;
|
||||||
|
descend();
|
||||||
if(!stack.count())
|
|
||||||
emit bulk(value);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tos().value.toList().append(value);
|
|
||||||
descend();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Parser::descend()
|
void Parser::descend()
|
||||||
{
|
{
|
||||||
while(true)
|
while(true)
|
||||||
{
|
{
|
||||||
if(tos().value.toList().count() < tos().count)
|
if(tos().reply.type() == Reply::MultiBulk &&
|
||||||
break;
|
tos().reply.value().toList().count() < tos().count)
|
||||||
|
return;
|
||||||
|
|
||||||
if(stack.count() == 1)
|
if(stack.count() == 1)
|
||||||
{
|
{
|
||||||
emit multiBulk(tos().value.toList());
|
emit reply(stack.takeLast().reply);
|
||||||
stack.removeLast();
|
return;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Task task = stack.takeLast();
|
Reply r = stack.takeLast().reply;
|
||||||
tos().value.toList().append(task.value);
|
tos().reply.value().toList().append(QVariant::fromValue(r));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
75
src/parser.h
75
src/parser.h
|
|
@ -1,66 +1,55 @@
|
||||||
#ifndef PARSER_H
|
#ifndef QREDIS_PARSER_H
|
||||||
#define PARSER_H
|
#define QREDIS_PARSER_H
|
||||||
|
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QPair>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QVariantList>
|
|
||||||
|
|
||||||
|
#include <qredis/reply.h>
|
||||||
#include "lexer.h"
|
#include "lexer.h"
|
||||||
|
|
||||||
class Parser : public QObject
|
namespace QRedis
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
class Parser : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Parser(Lexer *, QObject * = 0);
|
Parser(Lexer *, QObject * = 0);
|
||||||
virtual ~Parser();
|
virtual ~Parser();
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
|
|
||||||
void status(const QString &);
|
void reply(const Reply &);
|
||||||
void error(const QString &, const QString &);
|
|
||||||
void integer(qlonglong);
|
|
||||||
void bulk(const QByteArray &);
|
|
||||||
void multiBulk(const QVariantList &);
|
|
||||||
|
|
||||||
void warning(const QString &);
|
private Q_SLOTS:
|
||||||
|
|
||||||
private Q_SLOTS:
|
void readCharacter(char);
|
||||||
|
void readUnsafeString(const QString &);
|
||||||
|
void readSafeString(const QByteArray &);
|
||||||
|
|
||||||
void readCharacter(char);
|
private:
|
||||||
void readUnsafeString(const QString &);
|
|
||||||
void readSafeString(const QByteArray &);
|
|
||||||
|
|
||||||
private:
|
void descend();
|
||||||
|
|
||||||
void descend();
|
class Task
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
class Task
|
enum { Unknown = -2 };
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
enum Action {
|
Task(Reply::Type type) : reply(type), count(Unknown) {}
|
||||||
ReadStatus,
|
|
||||||
ReadError,
|
|
||||||
ReadInteger,
|
|
||||||
ReadBulk,
|
|
||||||
ReadMultiBulk
|
|
||||||
};
|
|
||||||
|
|
||||||
enum { Unknown = -2 };
|
Reply reply;
|
||||||
|
int count;
|
||||||
|
};
|
||||||
|
|
||||||
Task(Action action) : action(action), count(Unknown) {}
|
QList<Task> stack;
|
||||||
|
|
||||||
Action action;
|
Task & tos() { return stack.last(); }
|
||||||
int count;
|
};
|
||||||
QVariant value;
|
}
|
||||||
};
|
|
||||||
|
|
||||||
QList<Task> stack;
|
#endif // QREDIS_PARSER_H
|
||||||
|
|
||||||
inline Task & tos() { return stack.last(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // PARSER_H
|
|
||||||
|
|
|
||||||
|
|
@ -13,19 +13,7 @@ void RequestPrivate::quitEventLoop()
|
||||||
Request::Request(QObject * parent)
|
Request::Request(QObject * parent)
|
||||||
: QObject(parent), d(new RequestPrivate)
|
: QObject(parent), d(new RequestPrivate)
|
||||||
{
|
{
|
||||||
/*
|
connect(this, SIGNAL(reply(Reply&)), SLOT(deleteLater()));
|
||||||
* Each of these signals also causes the generic reply() signal to be emitted.
|
|
||||||
*/
|
|
||||||
connect(this, SIGNAL(bulk(QByteArray)), SIGNAL(reply()));
|
|
||||||
connect(this, SIGNAL(error(QString)), SIGNAL(reply()));
|
|
||||||
connect(this, SIGNAL(integer(qlonglong)), SIGNAL(reply()));
|
|
||||||
connect(this, SIGNAL(multiBulk(QVariantList)), SIGNAL(reply()));
|
|
||||||
connect(this, SIGNAL(status(QString)), SIGNAL(reply()));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We also need to ensure that this object is deleted when the reply is received.
|
|
||||||
*/
|
|
||||||
connect(this, SIGNAL(reply()), SLOT(deleteLater()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Request::~Request()
|
Request::~Request()
|
||||||
|
|
@ -38,12 +26,12 @@ bool Request::waitForReply(int msecs)
|
||||||
timer.setInterval(msecs);
|
timer.setInterval(msecs);
|
||||||
timer.setSingleShot(true);
|
timer.setSingleShot(true);
|
||||||
|
|
||||||
connect(&timer, SIGNAL(timeout()), &d->loop, SLOT(quit()));
|
connect(&timer, SIGNAL(timeout()), &d->loop, SLOT(quit()));
|
||||||
connect(this, SIGNAL(reply()), d.data(), SLOT(quitEventLoop()));
|
connect(this, SIGNAL(reply(Reply&)), d.data(), SLOT(quitEventLoop()));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the timer fires, the return value will be 0.
|
* If the timer fires, the return value will be 0.
|
||||||
* Otherwise, quitEventLoop() will terminate the loop with 1.
|
* Otherwise, quitEventLoop() will terminate the loop with 1.
|
||||||
*/
|
*/
|
||||||
return d->loop.exec();
|
return d->loop.exec(QEventLoop::ExcludeUserInputEvents);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# The QtTest libraries are required for building the tests.
|
# The QtTest libraries are required for building the tests.
|
||||||
find_package(Qt5Test REQUIRED)
|
find_package(Qt6Test REQUIRED)
|
||||||
|
|
||||||
# Specify the source files for the tests.
|
# Specify the source files for the tests.
|
||||||
set(QREDIS_TESTS_SRC
|
set(QREDIS_TESTS_SRC
|
||||||
|
|
@ -7,7 +7,7 @@ set(QREDIS_TESTS_SRC
|
||||||
testclient.cpp)
|
testclient.cpp)
|
||||||
|
|
||||||
# Specify the files that need to be MOC'd.
|
# Specify the files that need to be MOC'd.
|
||||||
qt5_wrap_cpp(QREDIS_TESTS_MOC
|
qt6_wrap_cpp(QREDIS_TESTS_MOC
|
||||||
testclient.h)
|
testclient.h)
|
||||||
|
|
||||||
# Create the test executable.
|
# Create the test executable.
|
||||||
|
|
@ -16,7 +16,7 @@ add_executable(qredis-tests
|
||||||
${QREDIS_TESTS_MOC})
|
${QREDIS_TESTS_MOC})
|
||||||
|
|
||||||
# Specify the Qt modules the test executable links against.
|
# Specify the Qt modules the test executable links against.
|
||||||
qt5_use_modules(qredis-tests Core Test)
|
target_link_libraries(qredis-tests Qt6::Core Qt6::Test)
|
||||||
|
|
||||||
# Naturally, we also link against the QRedis library.
|
# Naturally, we also link against the QRedis library.
|
||||||
target_link_libraries(qredis-tests qredis)
|
target_link_libraries(qredis-tests qredis)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue