Browse Source

redis subscribe

ZenZ 1 year ago
parent
commit
da299c3bca
31 changed files with 3501 additions and 11 deletions
  1. 1 0
      DataManagerMain/DataManagerProxy.cpp
  2. 557 0
      DataSubscribe/RedisSubscriber/Redis.cpp
  3. 51 0
      DataSubscribe/RedisSubscriber/Redis.h
  4. 70 9
      DataSubscribe/RedisSubscriber/RedisSubscriber.cpp
  5. 15 1
      DataSubscribe/RedisSubscriber/RedisSubscriber.h
  6. 11 1
      DataSubscribe/RedisSubscriber/RedisSubscriber.pro
  7. BIN
      lib/libhiredis.a
  8. BIN
      lib/libhiredis.dll.a
  9. 12 0
      lib/pkgconfig/hiredis.pc
  10. 130 0
      thirdparty/hiredis/include/hiredis/adapters/ae.h
  11. 156 0
      thirdparty/hiredis/include/hiredis/adapters/glib.h
  12. 84 0
      thirdparty/hiredis/include/hiredis/adapters/ivykis.h
  13. 188 0
      thirdparty/hiredis/include/hiredis/adapters/libev.h
  14. 175 0
      thirdparty/hiredis/include/hiredis/adapters/libevent.h
  15. 123 0
      thirdparty/hiredis/include/hiredis/adapters/libhv.h
  16. 171 0
      thirdparty/hiredis/include/hiredis/adapters/libuv.h
  17. 115 0
      thirdparty/hiredis/include/hiredis/adapters/macosx.h
  18. 197 0
      thirdparty/hiredis/include/hiredis/adapters/poll.h
  19. 135 0
      thirdparty/hiredis/include/hiredis/adapters/qt.h
  20. 96 0
      thirdparty/hiredis/include/hiredis/alloc.h
  21. 152 0
      thirdparty/hiredis/include/hiredis/async.h
  22. 366 0
      thirdparty/hiredis/include/hiredis/hiredis.h
  23. 129 0
      thirdparty/hiredis/include/hiredis/read.h
  24. 280 0
      thirdparty/hiredis/include/hiredis/sds.h
  25. 95 0
      thirdparty/hiredis/include/hiredis/sockcompat.h
  26. BIN
      thirdparty/hiredis/lib/libhiredis.a
  27. BIN
      thirdparty/hiredis/lib/libhiredis.dll.a
  28. 12 0
      thirdparty/hiredis/lib/pkgconfig/hiredis.pc
  29. 37 0
      thirdparty/hiredis/share/hiredis/hiredis-config.cmake
  30. 29 0
      thirdparty/hiredis/share/hiredis/hiredis-targets-release.cmake
  31. 114 0
      thirdparty/hiredis/share/hiredis/hiredis-targets.cmake

+ 1 - 0
DataManagerMain/DataManagerProxy.cpp

@@ -35,6 +35,7 @@ DataManagerInfo DataManagerProxy::loadModuleInfos(int id, std::string app)
     objDevice.ModuleInfo = objModule;
     objDevice.Properties.push_back(objDataItem);
 */
+
     ModuleInfo mi;
     mi.Name = "RedisSubscriber";
 

+ 557 - 0
DataSubscribe/RedisSubscriber/Redis.cpp

@@ -0,0 +1,557 @@
+#include "redis.h"
+#include <QThread>
+#include <QTimer>
+
+//void Test()
+//{
+//    struct st
+//    {
+//        int a;
+//        int b;
+//        char c;
+//        int d;
+//    };
+
+//    st t;
+//    t.a = 1;
+//    t.b = 2;
+//    t.c = 'a';
+//    t.d = 4;
+//    const char *v[3];
+//    size_t vlen[3];
+//    v[0] = "set";
+//    vlen[0] = strlen("set");
+
+//    v[1] = "st";
+//    vlen[1] = strlen("st");
+
+//    v[2] = (const char *)&t;
+//    vlen[2] = sizeof(struct st);
+
+//    redisReply *r = (redisReply *)redisCommandArgv(rc, sizeof(v) / sizeof(v[0]), v, vlen);
+
+//    r = (redisReply *)redisCommand(rc, "get st");
+//    if (!r) {
+//        printf("empty reply\n");
+//        exit(1);
+//    }
+//    st *t2 = (st*)(r->str);
+//    qDebug()<< "st2" << t2->a << t2->b << t2->c << t2->d;
+//}
+
+void getCallback(redisAsyncContext *ctx, void *r, void *privdata){
+
+    qDebug() << "getCallback called";
+    redisReply *reply = static_cast<redisReply*>(r);
+    if(reply == nullptr){
+
+        qDebug() << "The reply is nullptr";
+        return;
+    }
+    for(int i = 0; i < reply->elements; i++){
+        qDebug() << "key: " << reply->element[i]->str;
+    }
+    if (QString(reply->element[0]->str) == "message") //收到订阅消息
+    {
+        QString ch = QString(reply->element[1]->str);
+        if (ch == "DevCh")
+        {
+            qDebug()<<"devch...";
+//            qDebug()<<"img:" << ++cnt;
+//            QString js = QString(reply->element[2]->str);
+//            QJsonObject jsonObj = QJsonDocument::fromJson(js.toLocal8Bit()).object(); //转成本地编码(utf-8), 中文正常
+
+//            //quint32 type = jsonObj.value("type").toInt(); //toInt() 会丢数据
+//            //qDebug()<<"type:" << type;
+
+//            //QString base64Str = QString(reply->element[2]->str);
+//            QString base64Str = jsonObj["img"].toString();
+//            //qDebug()<<"str size:" << base64Str.size();
+//            QPixmap baseimage;
+//            baseimage.loadFromData(QByteArray::fromBase64(base64Str.toLatin1()));
+//            QPixmap scaledPixmap = baseimage.scaled(500, 500, Qt::KeepAspectRatio);  //设置图片大小并设置为按比例缩放
+//            lab->setPixmap(scaledPixmap); //label放置图片并显示
+        }
+        else if (ch == "Alarm")
+        {
+            qDebug()<<"alarm...";
+        }
+        else if (ch == "Led")
+        {
+            qDebug()<<"led...";
+        }
+    }
+
+    redisAsyncCommand(ctx, getCallback, nullptr, "blpop lTest 0");
+}
+
+void connectCallback(const redisAsyncContext *c, int status) {
+    if (status != REDIS_OK) {
+        qDebug("Error1: %s\n", c->errstr);
+        //redisOk = false;
+        return;
+    }
+    qDebug("Connected...\n");
+}
+
+void disconnectCallback(const redisAsyncContext *c, int status) {
+    if (status != REDIS_OK) {
+        qDebug("Error2: %s\n", c->errstr);
+        //redisOk = false;
+        return;
+    }
+    qDebug("Disconnected...\n");
+}
+
+Redis::Redis(QObject *parent) : QObject(parent)
+{
+    redisOk = false;
+//    conn();
+}
+
+Redis::~Redis()
+{
+    delete rc;
+    delete rac;
+}
+
+void Redis::conn()
+{
+    if (!redisOk) //未连接
+    {
+        //同步连接
+        rc = redisConnect(ip.toUtf8(), port);
+        if(rc == nullptr || rc->err)
+        {
+            qDebug()<< "redis err:" << rc->errstr;
+        }
+        else
+        {
+            QString authCmd = "auth " + auth; //认证
+            redisReply * reply = (redisReply*)redisCommand(rc, authCmd.toUtf8());
+            //qDebug()<< "auth:" << reply->str;
+            if (QString::compare(reply->str, "OK"))
+            {
+                qDebug() << "redis connect fail.";
+            }
+            else
+            {
+                redisOk = true;
+                qDebug() << "redis connect successfully.";
+            }
+        }
+    }
+    else
+    {
+        //qDebug()<< "~ ";
+    }
+}
+
+void Redis::start()
+{
+    conn();
+    //开启一个定时器, 每秒检测一次是否断开, 断开后自动连接redis服务器
+    QTimer *redisTimer = new QTimer();
+    connect(redisTimer, SIGNAL(timeout()), this, SLOT(conn()));
+    redisTimer->start(1000);
+}
+
+bool Redis::hset(QString m, QString k, QString v)
+{
+    //如果 field 是哈希表中的一个新建域,并且值设置成功,reply->integer为1
+    //如果哈希表中域 field 已经存在且旧值已被新值覆盖,reply->integer为0
+    //无法区别是否成功
+    bool ret = false;
+    redisReply * reply = (redisReply*)redisCommand(rc, QString("hset " + m + " " + k + " " + v).toStdString().c_str());
+    if (rc->err)
+    {
+        redisOk = false;
+        qDebug() << rc->errstr;
+    }
+    else
+    {
+        //qDebug() <<"type:" << reply->type << reply->integer; //type = 3
+        ret = true;
+    }
+    return ret;
+}
+
+QString Redis::hget(QString m, QString k)
+{
+    QString ret;
+    redisReply * reply = (redisReply*)redisCommand(rc, QString("hget " + m + " " + k).toStdString().c_str());
+    if (rc->err)
+    {
+        redisOk = false;
+        qDebug() << rc->errstr;
+    }
+    else
+    {
+        //type:4表示不存在,type:1表示返回的为字符串
+        //qDebug() <<"type:" << reply->type << reply->str << reply->len; //type:1
+        if (reply->len)
+        {
+            ret = QString(reply->str);
+        }
+    }
+    return ret;
+}
+
+//向队列尾(右)部加入字符串数据
+bool Redis::rpush(QString lData, QString js)
+{
+    bool ret = false;
+    redisReply * reply = (redisReply*)redisCommand(rc, QString("rpush " + lData + " " + js).toStdString().c_str());
+    if (rc->err)
+    {
+        redisOk = false;
+        qDebug() << rc->errstr;
+    }
+    else
+    {
+        //qDebug() <<"type:" << reply->type << reply->integer; //type = 3
+        //reply->integer为队列的个数,理论应该大于0
+        if (reply->integer)
+        {
+            ret = true;
+        }
+    }
+    return ret;
+}
+
+//向队列尾(右)部加入二进制数据
+bool Redis::rpushb(QString lData, QByteArray ba)
+{
+    bool ret = false;
+    const char *arg[3]; //3个参数(cmd, k, v)
+    size_t vlen[3]; //3个参数长度
+    arg[0] = "rpush";
+    vlen[0] = strlen("rpush");
+
+    arg[1] = lData.toStdString().c_str();
+    vlen[1] = lData.size();
+
+    arg[2] = (const char *)ba.data();
+    vlen[2] = ba.size();
+
+    redisReply *reply = (redisReply *)redisCommandArgv(rc, sizeof(arg) / sizeof(arg[0]), arg, vlen);
+    //redisReply * reply = (redisReply*)redisCommand(rc, QString("rpush " + lData + " " + js).toStdString().c_str());
+    if (rc->err)
+    {
+        redisOk = false;
+        qDebug() << rc->errstr;
+    }
+    else
+    {
+        //qDebug() <<"type:" << reply->type << reply->integer; //type = 3
+        //reply->integer为队列的个数,理论应该大于0
+        if (reply->integer)
+        {
+            ret = true;
+        }
+    }
+    return ret;
+}
+
+QString Redis::lpop(QString lData)
+{
+    QString ret;
+    redisReply * reply = (redisReply*)redisCommand(rc, QString("lpop " + lData).toStdString().c_str());
+    if (rc->err)
+    {
+        redisOk = false;
+        qDebug() << rc->errstr;
+    }
+    else if (reply->len)
+    {
+        ret = QString(reply->str);
+    }
+    return ret;
+}
+
+//阻塞从队列头部获取最早数据
+bool Redis::blpop(QString lData, redisCallbackFn *fn)
+{
+    if (rac == nullptr)
+    {
+        //异步连接
+        rac = redisAsyncConnect(ip.toUtf8(), port);
+        if(rac->err)
+        {
+            qDebug()<< "error: " << rac->errstr;
+            redisAsyncFree(rac);
+        }
+        else
+        {
+            adapter.setContext(rac);
+            redisAsyncSetConnectCallback(rac, connectCallback);
+            redisAsyncSetDisconnectCallback(rac, disconnectCallback);
+            QString authCmd = "auth " + auth; //认证
+            redisAsyncCommand(rac, nullptr, nullptr, authCmd.toUtf8());
+            qDebug()<< "redis ok.";
+            redisOk = true;
+        }
+    }
+    if (rac && redisOk)
+    {
+        redisAsyncCommand(rac, fn, nullptr, ("blpop " + lData + " 0").toStdString().c_str());
+        return true;
+    }
+    else
+    {
+        return false;
+    }
+}
+
+//同步阻塞一定时间返回数据
+QString Redis::blpop(QString lData, quint32 timeout)
+{
+    //最小为1,防止0时永久阻塞
+    if (timeout < 1)
+    {
+        timeout = 1;
+    }
+    QString ret;
+    redisReply * reply = (redisReply*)redisCommand(rc, QString("blpop " + lData + " " + QString::number(timeout)).toStdString().c_str());
+//    for(int i = 0; i < reply->elements; i++){
+//        qDebug() << "key: " << reply->element[i]->str;
+//    }
+    if (rc->err)
+    {
+        redisOk = false;
+        qDebug() << rc->errstr;
+    }
+    else if (reply->elements > 1)
+    {
+        ret = QString(reply->element[1]->str);
+    }
+    return ret;
+}
+
+bool Redis::set(QString k, QString v)
+{
+    bool ret = false;
+    redisReply * reply = (redisReply*)redisCommand(rc, QString("set " + k + " " + v).toStdString().c_str()); //支持空格
+    if (rc->err)
+    {
+        redisOk = false;
+        qDebug() << rc->errstr;
+    }
+    else
+    {
+        //qDebug() <<"type:" << reply->type << reply->str << reply->len; //type:5 str:OK len:2
+        if (reply->type == 6)
+        {
+            qDebug()<<"err:" << v;
+        }
+        //返回OK为成功,长度2为str长度
+        ret = true;
+    }
+    return ret;
+}
+
+bool Redis::setb(QString k, QByteArray &v)
+{
+    bool ret = false;
+    const char *arg[3]; //3个参数(cmd, k, v)
+    size_t vlen[3]; //3个参数长度
+    arg[0] = "set";
+    vlen[0] = strlen("set");
+
+    arg[1] = k.toStdString().c_str();
+    vlen[1] = k.size();
+
+    arg[2] = (const char *)v.data();
+    vlen[2] = v.size();
+
+    redisReply *reply = (redisReply *)redisCommandArgv(rc, sizeof(arg) / sizeof(arg[0]), arg, vlen);
+    if (rc->err)
+    {
+        redisOk = false;
+        qDebug() << rc->errstr;
+    }
+    else
+    {
+        //qDebug() <<"type:" << reply->type << reply->str << reply->len; //type:5 str:OK len:2
+        if (reply->type == 6)
+        {
+            qDebug()<<"err:" << v;
+        }
+        //返回OK为成功,长度2为str长度
+        ret = true;
+    }
+    return ret;
+}
+
+QString Redis::get(QString k)
+{
+    QString ret;
+    redisReply * reply = (redisReply*)redisCommand(rc, QString("get " + k).toStdString().c_str());
+    if (rc->err)
+    {
+        redisOk = false;
+        qDebug() << rc->errstr;
+    }
+    else if (reply->len)
+    {
+        ret = QString(reply->str);
+    }
+    return ret;
+}
+
+QByteArray Redis::getb(QString k)
+{
+    QByteArray ret;
+    redisReply * reply = (redisReply*)redisCommand(rc, QString("get " + k).toStdString().c_str());
+    if (rc->err)
+    {
+        redisOk = false;
+        qDebug() << rc->errstr;
+    }
+    else if (reply->len)
+    {
+        ret = QByteArray(reply->str, reply->len); //加上长度,保证二进制安全
+        //qDebug()<< reply->len << ret.size();
+    }
+    return ret;
+}
+
+bool Redis::expire(QString k,int sec)
+{
+    bool ret = false;
+    redisReply * reply = (redisReply*)redisCommand(rc, QString("expire " + k + " " + QString::number(sec, 10)).toStdString().c_str());
+    if (rc->err)
+    {
+        redisOk = false;
+        qDebug() << rc->errstr;
+    }
+    else
+    {
+        //qDebug() <<"type:" << reply->type << reply->integer; //type = 3
+        //K不存在为0,存在为1
+        if (reply->integer)
+        {
+            ret = true;
+        }
+    }
+    return ret;
+}
+
+bool Redis::publish(QString ch, QString js)
+{
+    bool ret = false;
+    QString pub = QString("publish " + ch + " " + js);
+    redisReply * reply = (redisReply*)redisCommand(rc, pub.toStdString().c_str());
+
+    qDebug() << __FUNCTION__ << pub;
+    if (rc->err)
+    {
+        redisOk = false;
+        qDebug() << rc->errstr;
+    }
+    else
+    {
+        //qDebug() <<"type:" << reply->type << reply->integer; //type:3, integer:2
+        //integer为2,不懂什么意思,这个用于实时,无需判断是否成功,连接正常即可
+        ret = true;
+    }
+    return ret;
+}
+
+bool Redis::publishb(QString ch, QByteArray ba)
+{
+    bool ret = false;
+    const char *arg[3]; //3个参数(cmd, k, v)
+    size_t vlen[3]; //3个参数长度
+    arg[0] = "publish";
+    vlen[0] = strlen("publish");
+
+    arg[1] = ch.toStdString().c_str();
+    vlen[1] = ch.size();
+
+    arg[2] = (const char *)ba.data();
+    vlen[2] = ba.size();
+
+    redisReply *reply = (redisReply *)redisCommandArgv(rc, sizeof(arg) / sizeof(arg[0]), arg, vlen);
+    //redisReply * reply = (redisReply*)redisCommand(rc, QString("publish " + ch + " " + js).toStdString().c_str());
+    if (rc->err)
+    {
+        redisOk = false;
+        qDebug() << rc->errstr;
+    }
+    else
+    {
+        qDebug() <<"type:" << reply->type << reply->integer; //type:3, integer:2
+        //integer为2,不懂什么意思,这个用于实时,无需判断是否成功,连接正常即可
+        ret = true;
+    }
+    return ret;
+}
+
+void Redis::subscribe(QString ch, redisCallbackFn *fn, void* data)
+{
+    if (rac == nullptr)
+    {
+        //异步连接
+        rac = redisAsyncConnect(ip.toUtf8(), port);
+        if(rac->err)
+        {
+            qDebug()<< "error: " << rac->errstr;
+            redisAsyncFree(rac);
+        }
+        else
+        {
+            adapter.setContext(rac);
+
+            redisAsyncSetConnectCallback(rac, connectCallback);
+            redisAsyncSetDisconnectCallback(rac, disconnectCallback);
+            QString authCmd = "auth " + auth; //认证
+            redisAsyncCommand(rac, nullptr, data, authCmd.toUtf8());
+            qDebug()<< "redis ok.";
+            redisOk = true;
+        }
+    }
+    if (rac && redisOk)
+    {
+        redisAsyncCommand(rac, fn, data, QString("subscribe " + ch).toStdString().c_str());
+    }
+}
+
+void Redis::psubscribe(QString ch, redisCallbackFn *fn, void* data)
+{
+    if (rac == nullptr)
+    {
+        //异步连接
+        rac = redisAsyncConnect(ip.toUtf8(), port);
+        if(rac->err)
+        {
+            qDebug()<< "error: " << rac->errstr;
+            redisAsyncFree(rac);
+        }
+        else
+        {
+            adapter.setContext(rac);
+
+            redisAsyncSetConnectCallback(rac, connectCallback);
+            redisAsyncSetDisconnectCallback(rac, disconnectCallback);
+            QString authCmd = "auth " + auth; //认证
+            redisAsyncCommand(rac, nullptr, data, authCmd.toUtf8());
+
+            qDebug()<< "redis ok.";
+            redisOk = true;
+        }
+    }
+    if (rac && redisOk)
+    {
+        redisAsyncCommand(rac, fn, data, QString("psubscribe " + ch).toStdString().c_str());
+    }
+}
+
+void Redis::Setup(std::string addr, uint port, std::string password)
+{
+    ip = addr.c_str();
+    port = port;
+    auth = password.c_str();
+
+    redisOk = false;
+}

+ 51 - 0
DataSubscribe/RedisSubscriber/Redis.h

@@ -0,0 +1,51 @@
+#pragma once
+
+#include "hiredis/adapters/qt.h"
+#include "hiredis/async.h"
+#include "hiredis/hiredis.h"
+#include <QObject>
+#include <QDebug>
+#include <QJsonObject>
+#include <QJsonDocument>
+
+class Redis : public QObject
+{
+    Q_OBJECT
+public:
+    explicit Redis(QObject *parent = 0);
+    ~Redis();
+
+public slots:
+    void start();
+    void conn();
+    bool hset(QString m, QString k, QString v);
+    QString hget(QString m, QString k);
+    bool set(QString k, QString v);
+    bool setb(QString k, QByteArray &v);
+    QString get(QString k);
+    QByteArray getb(QString k);
+    bool rpush(QString lData, QString js);                  //向队列尾部加入字符串数据
+    bool rpushb(QString lData, QByteArray ba);              //向队列尾部加入二进制数据
+    QString lpop(QString lData);
+    QString blpop(QString lData, quint32 timeout);          //同步阻塞一定时间返回数据
+    bool blpop(QString lData, redisCallbackFn *fn);         //阻塞从队列头部获取最早数据
+    bool publish(QString ch, QString js);
+    bool publishb(QString ch, QByteArray ba);
+    bool expire(QString k,int sec);
+    void subscribe(QString ch, redisCallbackFn *fn, void* data = nullptr);        // 订阅
+    void psubscribe(QString ch, redisCallbackFn *fn, void* priData = nullptr);       // 订阅:模式匹配
+
+private:
+    //QString ip = "192.168.9.56";
+    QString ip = "127.0.0.1";
+    quint16 port = 16379;
+    QString auth = "<LanPeng&&CeKong>";
+
+    bool redisOk = false;               //redis全局状态,操作出错时置为false, redis线程内会自动重连
+    redisContext *rc;                   //同步连接
+    redisAsyncContext *rac = nullptr;   //异步连接
+    RedisQtAdapter adapter;             //异步事件驱动适配器
+
+public:
+    void Setup(std::string addr, uint port, std::string password);
+};

+ 70 - 9
DataSubscribe/RedisSubscriber/RedisSubscriber.cpp

@@ -1,16 +1,64 @@
 #include "RedisSubscriber.h"
 #include <QDebug>
-RedisSubscriber::RedisSubscriber() {}
+RedisSubscriber::RedisSubscriber() {
 
-// void RedisSubscriber::shares(SharedData *share_)
-// {
-//     share = share_;
-// }
+}
 
-void RedisSubscriber::Run()
+
+#define REDISTOPIC "test"
+
+static void fnRedisCallback(redisAsyncContext* ctx, void* r, void* data){
+    redisReply *reply = static_cast<redisReply*>(r);
+    RedisSubscriber* subscriber = static_cast<RedisSubscriber*>(data);
+    if(reply == nullptr){
+        qDebug() << "The reply is nullptr";
+        return;
+    }
+
+    qDebug() << reply->element[0]->str << " "
+             << reply->element[1]->str << " "
+             << reply->element[2]->str << " ";
+
+    if (QString(reply->element[0]->str) == "message"){ //收到订阅消息
+
+        QString ch = QString(reply->element[1]->str);
+        //        QString cmd = QString(reply->element[2]->str);
+        QString js = QString(reply->element[2]->str);
+
+        //qDebug() << js;
+        QJsonDocument doc = QJsonDocument::fromJson(js.toLatin1());
+
+        if(ch == REDISTOPIC){
+            QJsonObject jsonObj = doc.object(); //转成本地编码(utf-8), 中文正常
+            if(jsonObj["engine"].toString().toLower() == "on"){
+                subscriber->enqueue(ch, js);
+            }else if(jsonObj["engine"].toString().toLower() == "off"){
+                subscriber->enqueue(ch, js);
+            }else{
+                subscriber->enqueue(ch, js);
+            }
+        }else{
+            subscriber->enqueue(ch, js);
+        }
+    }else{
+        qDebug() << "NOT A MESSAGE";
+    }
+}
+
+void RedisSubscriber::enqueue(const QString &key, const QString &val)
 {
+    mutex.lock();
+    msgQueue.push_back({key, val});
+    cond.wakeAll();
+    mutex.unlock();
+}
 
+void RedisSubscriber::Run()
+{
     //subscribe redis topic
+    redis = new Redis();
+    redis->start();
+    redis->subscribe(REDISTOPIC, fnRedisCallback, this);
     start();
 }
 
@@ -26,11 +74,24 @@ void RedisSubscriber::run(){
         QString usr = "Redis";
         QString key = "key";
         QVariant val = "val";
+        Msg msg;
+        mutex.lock();
+        if(cond.wait(&mutex), 500)
+        {
+            msg = msgQueue.dequeue();
+            emit pubData(usr, msg.key, msg.val);
+            qDebug() << __FILE__ << __FUNCTION__;
+        };
+
+        mutex.unlock();
+
+        /*if(!key.isEmpty()){
+            emit pubData(usr, key, val);
 
-        //qDebug() << __FILE__ << __FUNCTION__;
+            key.clear();
+        }*/
 
-        emit pubData(usr, key, val);
-        QThread::msleep(1000);
+        //QThread::msleep(1000);
     }
 }
 

+ 15 - 1
DataSubscribe/RedisSubscriber/RedisSubscriber.h

@@ -1,6 +1,14 @@
 #pragma once
 #include "RedisSubscriber_global.h"
 #include "Publisher.h"
+#include "Redis.h"
+#include <QQueue>
+#include <QMutex>
+#include <QWaitCondition>
+struct Msg{
+    QString key;
+    QString val;
+};
 class REDISSUBSCRIBER_EXPORT RedisSubscriber : public Publisher
 {
     Q_OBJECT
@@ -9,13 +17,19 @@ public:
     // virtual void shares(SharedData * share);
     virtual void Run();
     virtual void setLoader(QLibrary*);
+    void enqueue(const QString& key,const QString& val);
 protected:
     virtual void run();
 signals:
     void pubData(const QString& ,const QString& ,const QVariant&);
-
+private:
+    Redis* redis;
+    QMutex mutex;
+    QWaitCondition cond;
+    QQueue<Msg> msgQueue;
 };
 
+
 extern "C" {//一定要添加上
 REDISSUBSCRIBER_EXPORT Publisher* instance();
 REDISSUBSCRIBER_EXPORT void destroy(Publisher*);

+ 11 - 1
DataSubscribe/RedisSubscriber/RedisSubscriber.pro

@@ -13,15 +13,25 @@ INCLUDEPATH += ../../include
 
 HEADERS += \
     ../../include/LibraryLoader.h \
-    ../../include/Publisher.h
+    ../../include/Publisher.h \
+    Redis.h
+
+HIREDIS_DIR =$$PWD/../../thirdparty/hiredis
+HEADERS += $${HIREDIS_DIR}/include/hiredis/adapters/qt.h
+
+LIBS += -L$${HIREDIS_DIR}/lib -lhiredis
+#LIBS += ../thirdparty/hiredis/lib/libhiredis.a
+INCLUDEPATH += $${HIREDIS_DIR}/include
 
 SOURCES += \
+    Redis.cpp \
     RedisSubscriber.cpp
 
 HEADERS += \
     RedisSubscriber_global.h \
     RedisSubscriber.h
 
+
 # Default rules for deployment.
 unix {
     target.path = /usr/lib

BIN
lib/libhiredis.a


BIN
lib/libhiredis.dll.a


+ 12 - 0
lib/pkgconfig/hiredis.pc

@@ -0,0 +1,12 @@
+prefix=D:/dev/hiredis
+install_libdir=lib
+exec_prefix=${prefix}
+libdir=${exec_prefix}/${install_libdir}
+includedir=${prefix}/include
+pkgincludedir=${includedir}/hiredis
+
+Name: hiredis
+Description: Minimalistic C client library for Redis.
+Version: 1.1.0
+Libs: -L${libdir} -lhiredis
+Cflags: -I${pkgincludedir} -I${includedir} -D_FILE_OFFSET_BITS=64

+ 130 - 0
thirdparty/hiredis/include/hiredis/adapters/ae.h

@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Redis nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __HIREDIS_AE_H__
+#define __HIREDIS_AE_H__
+#include <sys/types.h>
+#include <ae.h>
+#include "../hiredis.h"
+#include "../async.h"
+
+typedef struct redisAeEvents {
+    redisAsyncContext *context;
+    aeEventLoop *loop;
+    int fd;
+    int reading, writing;
+} redisAeEvents;
+
+static void redisAeReadEvent(aeEventLoop *el, int fd, void *privdata, int mask) {
+    ((void)el); ((void)fd); ((void)mask);
+
+    redisAeEvents *e = (redisAeEvents*)privdata;
+    redisAsyncHandleRead(e->context);
+}
+
+static void redisAeWriteEvent(aeEventLoop *el, int fd, void *privdata, int mask) {
+    ((void)el); ((void)fd); ((void)mask);
+
+    redisAeEvents *e = (redisAeEvents*)privdata;
+    redisAsyncHandleWrite(e->context);
+}
+
+static void redisAeAddRead(void *privdata) {
+    redisAeEvents *e = (redisAeEvents*)privdata;
+    aeEventLoop *loop = e->loop;
+    if (!e->reading) {
+        e->reading = 1;
+        aeCreateFileEvent(loop,e->fd,AE_READABLE,redisAeReadEvent,e);
+    }
+}
+
+static void redisAeDelRead(void *privdata) {
+    redisAeEvents *e = (redisAeEvents*)privdata;
+    aeEventLoop *loop = e->loop;
+    if (e->reading) {
+        e->reading = 0;
+        aeDeleteFileEvent(loop,e->fd,AE_READABLE);
+    }
+}
+
+static void redisAeAddWrite(void *privdata) {
+    redisAeEvents *e = (redisAeEvents*)privdata;
+    aeEventLoop *loop = e->loop;
+    if (!e->writing) {
+        e->writing = 1;
+        aeCreateFileEvent(loop,e->fd,AE_WRITABLE,redisAeWriteEvent,e);
+    }
+}
+
+static void redisAeDelWrite(void *privdata) {
+    redisAeEvents *e = (redisAeEvents*)privdata;
+    aeEventLoop *loop = e->loop;
+    if (e->writing) {
+        e->writing = 0;
+        aeDeleteFileEvent(loop,e->fd,AE_WRITABLE);
+    }
+}
+
+static void redisAeCleanup(void *privdata) {
+    redisAeEvents *e = (redisAeEvents*)privdata;
+    redisAeDelRead(privdata);
+    redisAeDelWrite(privdata);
+    hi_free(e);
+}
+
+static int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) {
+    redisContext *c = &(ac->c);
+    redisAeEvents *e;
+
+    /* Nothing should be attached when something is already attached */
+    if (ac->ev.data != NULL)
+        return REDIS_ERR;
+
+    /* Create container for context and r/w events */
+    e = (redisAeEvents*)hi_malloc(sizeof(*e));
+    if (e == NULL)
+        return REDIS_ERR;
+
+    e->context = ac;
+    e->loop = loop;
+    e->fd = c->fd;
+    e->reading = e->writing = 0;
+
+    /* Register functions to start/stop listening for events */
+    ac->ev.addRead = redisAeAddRead;
+    ac->ev.delRead = redisAeDelRead;
+    ac->ev.addWrite = redisAeAddWrite;
+    ac->ev.delWrite = redisAeDelWrite;
+    ac->ev.cleanup = redisAeCleanup;
+    ac->ev.data = e;
+
+    return REDIS_OK;
+}
+#endif

+ 156 - 0
thirdparty/hiredis/include/hiredis/adapters/glib.h

@@ -0,0 +1,156 @@
+#ifndef __HIREDIS_GLIB_H__
+#define __HIREDIS_GLIB_H__
+
+#include <glib.h>
+
+#include "../hiredis.h"
+#include "../async.h"
+
+typedef struct
+{
+    GSource source;
+    redisAsyncContext *ac;
+    GPollFD poll_fd;
+} RedisSource;
+
+static void
+redis_source_add_read (gpointer data)
+{
+    RedisSource *source = (RedisSource *)data;
+    g_return_if_fail(source);
+    source->poll_fd.events |= G_IO_IN;
+    g_main_context_wakeup(g_source_get_context((GSource *)data));
+}
+
+static void
+redis_source_del_read (gpointer data)
+{
+    RedisSource *source = (RedisSource *)data;
+    g_return_if_fail(source);
+    source->poll_fd.events &= ~G_IO_IN;
+    g_main_context_wakeup(g_source_get_context((GSource *)data));
+}
+
+static void
+redis_source_add_write (gpointer data)
+{
+    RedisSource *source = (RedisSource *)data;
+    g_return_if_fail(source);
+    source->poll_fd.events |= G_IO_OUT;
+    g_main_context_wakeup(g_source_get_context((GSource *)data));
+}
+
+static void
+redis_source_del_write (gpointer data)
+{
+    RedisSource *source = (RedisSource *)data;
+    g_return_if_fail(source);
+    source->poll_fd.events &= ~G_IO_OUT;
+    g_main_context_wakeup(g_source_get_context((GSource *)data));
+}
+
+static void
+redis_source_cleanup (gpointer data)
+{
+    RedisSource *source = (RedisSource *)data;
+
+    g_return_if_fail(source);
+
+    redis_source_del_read(source);
+    redis_source_del_write(source);
+    /*
+     * It is not our responsibility to remove ourself from the
+     * current main loop. However, we will remove the GPollFD.
+     */
+    if (source->poll_fd.fd >= 0) {
+        g_source_remove_poll((GSource *)data, &source->poll_fd);
+        source->poll_fd.fd = -1;
+    }
+}
+
+static gboolean
+redis_source_prepare (GSource *source,
+                      gint    *timeout_)
+{
+    RedisSource *redis = (RedisSource *)source;
+    *timeout_ = -1;
+    return !!(redis->poll_fd.events & redis->poll_fd.revents);
+}
+
+static gboolean
+redis_source_check (GSource *source)
+{
+    RedisSource *redis = (RedisSource *)source;
+    return !!(redis->poll_fd.events & redis->poll_fd.revents);
+}
+
+static gboolean
+redis_source_dispatch (GSource      *source,
+                       GSourceFunc   callback,
+                       gpointer      user_data)
+{
+    RedisSource *redis = (RedisSource *)source;
+
+    if ((redis->poll_fd.revents & G_IO_OUT)) {
+        redisAsyncHandleWrite(redis->ac);
+        redis->poll_fd.revents &= ~G_IO_OUT;
+    }
+
+    if ((redis->poll_fd.revents & G_IO_IN)) {
+        redisAsyncHandleRead(redis->ac);
+        redis->poll_fd.revents &= ~G_IO_IN;
+    }
+
+    if (callback) {
+        return callback(user_data);
+    }
+
+    return TRUE;
+}
+
+static void
+redis_source_finalize (GSource *source)
+{
+    RedisSource *redis = (RedisSource *)source;
+
+    if (redis->poll_fd.fd >= 0) {
+        g_source_remove_poll(source, &redis->poll_fd);
+        redis->poll_fd.fd = -1;
+    }
+}
+
+static GSource *
+redis_source_new (redisAsyncContext *ac)
+{
+    static GSourceFuncs source_funcs = {
+        .prepare  = redis_source_prepare,
+        .check     = redis_source_check,
+        .dispatch = redis_source_dispatch,
+        .finalize = redis_source_finalize,
+    };
+    redisContext *c = &ac->c;
+    RedisSource *source;
+
+    g_return_val_if_fail(ac != NULL, NULL);
+
+    source = (RedisSource *)g_source_new(&source_funcs, sizeof *source);
+    if (source == NULL)
+        return NULL;
+
+    source->ac = ac;
+    source->poll_fd.fd = c->fd;
+    source->poll_fd.events = 0;
+    source->poll_fd.revents = 0;
+    g_source_add_poll((GSource *)source, &source->poll_fd);
+
+    ac->ev.addRead = redis_source_add_read;
+    ac->ev.delRead = redis_source_del_read;
+    ac->ev.addWrite = redis_source_add_write;
+    ac->ev.delWrite = redis_source_del_write;
+    ac->ev.cleanup = redis_source_cleanup;
+    ac->ev.data = source;
+
+    return (GSource *)source;
+}
+
+#endif /* __HIREDIS_GLIB_H__ */

+ 84 - 0
thirdparty/hiredis/include/hiredis/adapters/ivykis.h

@@ -0,0 +1,84 @@
+#ifndef __HIREDIS_IVYKIS_H__
+#define __HIREDIS_IVYKIS_H__
+#include <iv.h>
+#include "../hiredis.h"
+#include "../async.h"
+
+typedef struct redisIvykisEvents {
+    redisAsyncContext *context;
+    struct iv_fd fd;
+} redisIvykisEvents;
+
+static void redisIvykisReadEvent(void *arg) {
+    redisAsyncContext *context = (redisAsyncContext *)arg;
+    redisAsyncHandleRead(context);
+}
+
+static void redisIvykisWriteEvent(void *arg) {
+    redisAsyncContext *context = (redisAsyncContext *)arg;
+    redisAsyncHandleWrite(context);
+}
+
+static void redisIvykisAddRead(void *privdata) {
+    redisIvykisEvents *e = (redisIvykisEvents*)privdata;
+    iv_fd_set_handler_in(&e->fd, redisIvykisReadEvent);
+}
+
+static void redisIvykisDelRead(void *privdata) {
+    redisIvykisEvents *e = (redisIvykisEvents*)privdata;
+    iv_fd_set_handler_in(&e->fd, NULL);
+}
+
+static void redisIvykisAddWrite(void *privdata) {
+    redisIvykisEvents *e = (redisIvykisEvents*)privdata;
+    iv_fd_set_handler_out(&e->fd, redisIvykisWriteEvent);
+}
+
+static void redisIvykisDelWrite(void *privdata) {
+    redisIvykisEvents *e = (redisIvykisEvents*)privdata;
+    iv_fd_set_handler_out(&e->fd, NULL);
+}
+
+static void redisIvykisCleanup(void *privdata) {
+    redisIvykisEvents *e = (redisIvykisEvents*)privdata;
+
+    iv_fd_unregister(&e->fd);
+    hi_free(e);
+}
+
+static int redisIvykisAttach(redisAsyncContext *ac) {
+    redisContext *c = &(ac->c);
+    redisIvykisEvents *e;
+
+    /* Nothing should be attached when something is already attached */
+    if (ac->ev.data != NULL)
+        return REDIS_ERR;
+
+    /* Create container for context and r/w events */
+    e = (redisIvykisEvents*)hi_malloc(sizeof(*e));
+    if (e == NULL)
+        return REDIS_ERR;
+
+    e->context = ac;
+
+    /* Register functions to start/stop listening for events */
+    ac->ev.addRead = redisIvykisAddRead;
+    ac->ev.delRead = redisIvykisDelRead;
+    ac->ev.addWrite = redisIvykisAddWrite;
+    ac->ev.delWrite = redisIvykisDelWrite;
+    ac->ev.cleanup = redisIvykisCleanup;
+    ac->ev.data = e;
+
+    /* Initialize and install read/write events */
+    IV_FD_INIT(&e->fd);
+    e->fd.fd = c->fd;
+    e->fd.handler_in = redisIvykisReadEvent;
+    e->fd.handler_out = redisIvykisWriteEvent;
+    e->fd.handler_err = NULL;
+    e->fd.cookie = e->context;
+
+    iv_fd_register(&e->fd);
+
+    return REDIS_OK;
+}
+#endif

+ 188 - 0
thirdparty/hiredis/include/hiredis/adapters/libev.h

@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Redis nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __HIREDIS_LIBEV_H__
+#define __HIREDIS_LIBEV_H__
+#include <stdlib.h>
+#include <sys/types.h>
+#include <ev.h>
+#include "../hiredis.h"
+#include "../async.h"
+
+typedef struct redisLibevEvents {
+    redisAsyncContext *context;
+    struct ev_loop *loop;
+    int reading, writing;
+    ev_io rev, wev;
+    ev_timer timer;
+} redisLibevEvents;
+
+static void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) {
+#if EV_MULTIPLICITY
+    ((void)EV_A);
+#endif
+    ((void)revents);
+
+    redisLibevEvents *e = (redisLibevEvents*)watcher->data;
+    redisAsyncHandleRead(e->context);
+}
+
+static void redisLibevWriteEvent(EV_P_ ev_io *watcher, int revents) {
+#if EV_MULTIPLICITY
+    ((void)EV_A);
+#endif
+    ((void)revents);
+
+    redisLibevEvents *e = (redisLibevEvents*)watcher->data;
+    redisAsyncHandleWrite(e->context);
+}
+
+static void redisLibevAddRead(void *privdata) {
+    redisLibevEvents *e = (redisLibevEvents*)privdata;
+#if EV_MULTIPLICITY
+    struct ev_loop *loop = e->loop;
+#endif
+    if (!e->reading) {
+        e->reading = 1;
+        ev_io_start(EV_A_ &e->rev);
+    }
+}
+
+static void redisLibevDelRead(void *privdata) {
+    redisLibevEvents *e = (redisLibevEvents*)privdata;
+#if EV_MULTIPLICITY
+    struct ev_loop *loop = e->loop;
+#endif
+    if (e->reading) {
+        e->reading = 0;
+        ev_io_stop(EV_A_ &e->rev);
+    }
+}
+
+static void redisLibevAddWrite(void *privdata) {
+    redisLibevEvents *e = (redisLibevEvents*)privdata;
+#if EV_MULTIPLICITY
+    struct ev_loop *loop = e->loop;
+#endif
+    if (!e->writing) {
+        e->writing = 1;
+        ev_io_start(EV_A_ &e->wev);
+    }
+}
+
+static void redisLibevDelWrite(void *privdata) {
+    redisLibevEvents *e = (redisLibevEvents*)privdata;
+#if EV_MULTIPLICITY
+    struct ev_loop *loop = e->loop;
+#endif
+    if (e->writing) {
+        e->writing = 0;
+        ev_io_stop(EV_A_ &e->wev);
+    }
+}
+
+static void redisLibevStopTimer(void *privdata) {
+    redisLibevEvents *e = (redisLibevEvents*)privdata;
+#if EV_MULTIPLICITY
+    struct ev_loop *loop = e->loop;
+#endif
+    ev_timer_stop(EV_A_ &e->timer);
+}
+
+static void redisLibevCleanup(void *privdata) {
+    redisLibevEvents *e = (redisLibevEvents*)privdata;
+    redisLibevDelRead(privdata);
+    redisLibevDelWrite(privdata);
+    redisLibevStopTimer(privdata);
+    hi_free(e);
+}
+
+static void redisLibevTimeout(EV_P_ ev_timer *timer, int revents) {
+#if EV_MULTIPLICITY
+    ((void)EV_A);
+#endif
+    ((void)revents);
+    redisLibevEvents *e = (redisLibevEvents*)timer->data;
+    redisAsyncHandleTimeout(e->context);
+}
+
+static void redisLibevSetTimeout(void *privdata, struct timeval tv) {
+    redisLibevEvents *e = (redisLibevEvents*)privdata;
+#if EV_MULTIPLICITY
+    struct ev_loop *loop = e->loop;
+#endif
+
+    if (!ev_is_active(&e->timer)) {
+        ev_init(&e->timer, redisLibevTimeout);
+        e->timer.data = e;
+    }
+
+    e->timer.repeat = tv.tv_sec + tv.tv_usec / 1000000.00;
+    ev_timer_again(EV_A_ &e->timer);
+}
+
+static int redisLibevAttach(EV_P_ redisAsyncContext *ac) {
+    redisContext *c = &(ac->c);
+    redisLibevEvents *e;
+
+    /* Nothing should be attached when something is already attached */
+    if (ac->ev.data != NULL)
+        return REDIS_ERR;
+
+    /* Create container for context and r/w events */
+    e = (redisLibevEvents*)hi_calloc(1, sizeof(*e));
+    if (e == NULL)
+        return REDIS_ERR;
+
+    e->context = ac;
+#if EV_MULTIPLICITY
+    e->loop = EV_A;
+#else
+    e->loop = NULL;
+#endif
+    e->rev.data = e;
+    e->wev.data = e;
+
+    /* Register functions to start/stop listening for events */
+    ac->ev.addRead = redisLibevAddRead;
+    ac->ev.delRead = redisLibevDelRead;
+    ac->ev.addWrite = redisLibevAddWrite;
+    ac->ev.delWrite = redisLibevDelWrite;
+    ac->ev.cleanup = redisLibevCleanup;
+    ac->ev.scheduleTimer = redisLibevSetTimeout;
+    ac->ev.data = e;
+
+    /* Initialize read/write events */
+    ev_io_init(&e->rev,redisLibevReadEvent,c->fd,EV_READ);
+    ev_io_init(&e->wev,redisLibevWriteEvent,c->fd,EV_WRITE);
+    return REDIS_OK;
+}
+
+#endif

+ 175 - 0
thirdparty/hiredis/include/hiredis/adapters/libevent.h

@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Redis nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __HIREDIS_LIBEVENT_H__
+#define __HIREDIS_LIBEVENT_H__
+#include <event2/event.h>
+#include "../hiredis.h"
+#include "../async.h"
+
+#define REDIS_LIBEVENT_DELETED 0x01
+#define REDIS_LIBEVENT_ENTERED 0x02
+
+typedef struct redisLibeventEvents {
+    redisAsyncContext *context;
+    struct event *ev;
+    struct event_base *base;
+    struct timeval tv;
+    short flags;
+    short state;
+} redisLibeventEvents;
+
+static void redisLibeventDestroy(redisLibeventEvents *e) {
+    hi_free(e);
+}
+
+static void redisLibeventHandler(evutil_socket_t fd, short event, void *arg) {
+    ((void)fd);
+    redisLibeventEvents *e = (redisLibeventEvents*)arg;
+    e->state |= REDIS_LIBEVENT_ENTERED;
+
+    #define CHECK_DELETED() if (e->state & REDIS_LIBEVENT_DELETED) {\
+        redisLibeventDestroy(e);\
+        return; \
+    }
+
+    if ((event & EV_TIMEOUT) && (e->state & REDIS_LIBEVENT_DELETED) == 0) {
+        redisAsyncHandleTimeout(e->context);
+        CHECK_DELETED();
+    }
+
+    if ((event & EV_READ) && e->context && (e->state & REDIS_LIBEVENT_DELETED) == 0) {
+        redisAsyncHandleRead(e->context);
+        CHECK_DELETED();
+    }
+
+    if ((event & EV_WRITE) && e->context && (e->state & REDIS_LIBEVENT_DELETED) == 0) {
+        redisAsyncHandleWrite(e->context);
+        CHECK_DELETED();
+    }
+
+    e->state &= ~REDIS_LIBEVENT_ENTERED;
+    #undef CHECK_DELETED
+}
+
+static void redisLibeventUpdate(void *privdata, short flag, int isRemove) {
+    redisLibeventEvents *e = (redisLibeventEvents *)privdata;
+    const struct timeval *tv = e->tv.tv_sec || e->tv.tv_usec ? &e->tv : NULL;
+
+    if (isRemove) {
+        if ((e->flags & flag) == 0) {
+            return;
+        } else {
+            e->flags &= ~flag;
+        }
+    } else {
+        if (e->flags & flag) {
+            return;
+        } else {
+            e->flags |= flag;
+        }
+    }
+
+    event_del(e->ev);
+    event_assign(e->ev, e->base, e->context->c.fd, e->flags | EV_PERSIST,
+                 redisLibeventHandler, privdata);
+    event_add(e->ev, tv);
+}
+
+static void redisLibeventAddRead(void *privdata) {
+    redisLibeventUpdate(privdata, EV_READ, 0);
+}
+
+static void redisLibeventDelRead(void *privdata) {
+    redisLibeventUpdate(privdata, EV_READ, 1);
+}
+
+static void redisLibeventAddWrite(void *privdata) {
+    redisLibeventUpdate(privdata, EV_WRITE, 0);
+}
+
+static void redisLibeventDelWrite(void *privdata) {
+    redisLibeventUpdate(privdata, EV_WRITE, 1);
+}
+
+static void redisLibeventCleanup(void *privdata) {
+    redisLibeventEvents *e = (redisLibeventEvents*)privdata;
+    if (!e) {
+        return;
+    }
+    event_del(e->ev);
+    event_free(e->ev);
+    e->ev = NULL;
+
+    if (e->state & REDIS_LIBEVENT_ENTERED) {
+        e->state |= REDIS_LIBEVENT_DELETED;
+    } else {
+        redisLibeventDestroy(e);
+    }
+}
+
+static void redisLibeventSetTimeout(void *privdata, struct timeval tv) {
+    redisLibeventEvents *e = (redisLibeventEvents *)privdata;
+    short flags = e->flags;
+    e->flags = 0;
+    e->tv = tv;
+    redisLibeventUpdate(e, flags, 0);
+}
+
+static int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) {
+    redisContext *c = &(ac->c);
+    redisLibeventEvents *e;
+
+    /* Nothing should be attached when something is already attached */
+    if (ac->ev.data != NULL)
+        return REDIS_ERR;
+
+    /* Create container for context and r/w events */
+    e = (redisLibeventEvents*)hi_calloc(1, sizeof(*e));
+    if (e == NULL)
+        return REDIS_ERR;
+
+    e->context = ac;
+
+    /* Register functions to start/stop listening for events */
+    ac->ev.addRead = redisLibeventAddRead;
+    ac->ev.delRead = redisLibeventDelRead;
+    ac->ev.addWrite = redisLibeventAddWrite;
+    ac->ev.delWrite = redisLibeventDelWrite;
+    ac->ev.cleanup = redisLibeventCleanup;
+    ac->ev.scheduleTimer = redisLibeventSetTimeout;
+    ac->ev.data = e;
+
+    /* Initialize and install read/write events */
+    e->ev = event_new(base, c->fd, EV_READ | EV_WRITE, redisLibeventHandler, e);
+    e->base = base;
+    return REDIS_OK;
+}
+#endif

+ 123 - 0
thirdparty/hiredis/include/hiredis/adapters/libhv.h

@@ -0,0 +1,123 @@
+#ifndef __HIREDIS_LIBHV_H__
+#define __HIREDIS_LIBHV_H__
+
+#include <hv/hloop.h>
+#include "../hiredis.h"
+#include "../async.h"
+
+typedef struct redisLibhvEvents {
+    hio_t *io;
+    htimer_t *timer;
+} redisLibhvEvents;
+
+static void redisLibhvHandleEvents(hio_t* io) {
+    redisAsyncContext* context = (redisAsyncContext*)hevent_userdata(io);
+    int events = hio_events(io);
+    int revents = hio_revents(io);
+    if (context && (events & HV_READ) && (revents & HV_READ)) {
+        redisAsyncHandleRead(context);
+    }
+    if (context && (events & HV_WRITE) && (revents & HV_WRITE)) {
+        redisAsyncHandleWrite(context);
+    }
+}
+
+static void redisLibhvAddRead(void *privdata) {
+    redisLibhvEvents* events = (redisLibhvEvents*)privdata;
+    hio_add(events->io, redisLibhvHandleEvents, HV_READ);
+}
+
+static void redisLibhvDelRead(void *privdata) {
+    redisLibhvEvents* events = (redisLibhvEvents*)privdata;
+    hio_del(events->io, HV_READ);
+}
+
+static void redisLibhvAddWrite(void *privdata) {
+    redisLibhvEvents* events = (redisLibhvEvents*)privdata;
+    hio_add(events->io, redisLibhvHandleEvents, HV_WRITE);
+}
+
+static void redisLibhvDelWrite(void *privdata) {
+    redisLibhvEvents* events = (redisLibhvEvents*)privdata;
+    hio_del(events->io, HV_WRITE);
+}
+
+static void redisLibhvCleanup(void *privdata) {
+    redisLibhvEvents* events = (redisLibhvEvents*)privdata;
+
+    if (events->timer)
+        htimer_del(events->timer);
+
+    hio_close(events->io);
+    hevent_set_userdata(events->io, NULL);
+
+    hi_free(events);
+}
+
+static void redisLibhvTimeout(htimer_t* timer) {
+    hio_t* io = (hio_t*)hevent_userdata(timer);
+    redisAsyncHandleTimeout((redisAsyncContext*)hevent_userdata(io));
+}
+
+static void redisLibhvSetTimeout(void *privdata, struct timeval tv) {
+    redisLibhvEvents* events;
+    uint32_t millis;
+    hloop_t* loop;
+
+    events = (redisLibhvEvents*)privdata;
+    millis = tv.tv_sec * 1000 + tv.tv_usec / 1000;
+
+    if (millis == 0) {
+        /* Libhv disallows zero'd timers so treat this as a delete or NO OP */
+        if (events->timer) {
+            htimer_del(events->timer);
+            events->timer = NULL;
+        }
+    } else if (events->timer == NULL) {
+        /* Add new timer */
+        loop = hevent_loop(events->io);
+        events->timer = htimer_add(loop, redisLibhvTimeout, millis, 1);
+        hevent_set_userdata(events->timer, events->io);
+    } else {
+        /* Update existing timer */
+        htimer_reset(events->timer, millis);
+    }
+}
+
+static int redisLibhvAttach(redisAsyncContext* ac, hloop_t* loop) {
+    redisContext *c = &(ac->c);
+    redisLibhvEvents *events;
+    hio_t* io = NULL;
+
+    if (ac->ev.data != NULL) {
+        return REDIS_ERR;
+    }
+
+    /* Create container struct to keep track of our io and any timer */
+    events = (redisLibhvEvents*)hi_malloc(sizeof(*events));
+    if (events == NULL) {
+        return REDIS_ERR;
+    }
+
+    io = hio_get(loop, c->fd);
+    if (io == NULL) {
+        hi_free(events);
+        return REDIS_ERR;
+    }
+
+    hevent_set_userdata(io, ac);
+
+    events->io = io;
+    events->timer = NULL;
+
+    ac->ev.addRead  = redisLibhvAddRead;
+    ac->ev.delRead  = redisLibhvDelRead;
+    ac->ev.addWrite = redisLibhvAddWrite;
+    ac->ev.delWrite = redisLibhvDelWrite;
+    ac->ev.cleanup  = redisLibhvCleanup;
+    ac->ev.scheduleTimer = redisLibhvSetTimeout;
+    ac->ev.data = events;
+
+    return REDIS_OK;
+}
+#endif

+ 171 - 0
thirdparty/hiredis/include/hiredis/adapters/libuv.h

@@ -0,0 +1,171 @@
+#ifndef __HIREDIS_LIBUV_H__
+#define __HIREDIS_LIBUV_H__
+#include <stdlib.h>
+#include <uv.h>
+#include "../hiredis.h"
+#include "../async.h"
+#include <string.h>
+
+typedef struct redisLibuvEvents {
+    redisAsyncContext* context;
+    uv_poll_t          handle;
+    uv_timer_t         timer;
+    int                events;
+} redisLibuvEvents;
+
+
+static void redisLibuvPoll(uv_poll_t* handle, int status, int events) {
+    redisLibuvEvents* p = (redisLibuvEvents*)handle->data;
+    int ev = (status ? p->events : events);
+
+    if (p->context != NULL && (ev & UV_READABLE)) {
+        redisAsyncHandleRead(p->context);
+    }
+    if (p->context != NULL && (ev & UV_WRITABLE)) {
+        redisAsyncHandleWrite(p->context);
+    }
+}
+
+
+static void redisLibuvAddRead(void *privdata) {
+    redisLibuvEvents* p = (redisLibuvEvents*)privdata;
+
+    if (p->events & UV_READABLE) {
+        return;
+    }
+
+    p->events |= UV_READABLE;
+
+    uv_poll_start(&p->handle, p->events, redisLibuvPoll);
+}
+
+
+static void redisLibuvDelRead(void *privdata) {
+    redisLibuvEvents* p = (redisLibuvEvents*)privdata;
+
+    p->events &= ~UV_READABLE;
+
+    if (p->events) {
+        uv_poll_start(&p->handle, p->events, redisLibuvPoll);
+    } else {
+        uv_poll_stop(&p->handle);
+    }
+}
+
+
+static void redisLibuvAddWrite(void *privdata) {
+    redisLibuvEvents* p = (redisLibuvEvents*)privdata;
+
+    if (p->events & UV_WRITABLE) {
+        return;
+    }
+
+    p->events |= UV_WRITABLE;
+
+    uv_poll_start(&p->handle, p->events, redisLibuvPoll);
+}
+
+
+static void redisLibuvDelWrite(void *privdata) {
+    redisLibuvEvents* p = (redisLibuvEvents*)privdata;
+
+    p->events &= ~UV_WRITABLE;
+
+    if (p->events) {
+        uv_poll_start(&p->handle, p->events, redisLibuvPoll);
+    } else {
+        uv_poll_stop(&p->handle);
+    }
+}
+
+static void on_timer_close(uv_handle_t *handle) {
+    redisLibuvEvents* p = (redisLibuvEvents*)handle->data;
+    p->timer.data = NULL;
+    if (!p->handle.data) {
+        // both timer and handle are closed
+        hi_free(p);
+    }
+    // else, wait for `on_handle_close`
+}
+
+static void on_handle_close(uv_handle_t *handle) {
+    redisLibuvEvents* p = (redisLibuvEvents*)handle->data;
+    p->handle.data = NULL;
+    if (!p->timer.data) {
+        // timer never started, or timer already destroyed
+        hi_free(p);
+    }
+    // else, wait for `on_timer_close`
+}
+
+// libuv removed `status` parameter since v0.11.23
+// see: https://github.com/libuv/libuv/blob/v0.11.23/include/uv.h
+#if (UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR < 11) || \
+    (UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR == 11 && UV_VERSION_PATCH < 23)
+static void redisLibuvTimeout(uv_timer_t *timer, int status) {
+    (void)status; // unused
+#else
+static void redisLibuvTimeout(uv_timer_t *timer) {
+#endif
+    redisLibuvEvents *e = (redisLibuvEvents*)timer->data;
+    redisAsyncHandleTimeout(e->context);
+}
+
+static void redisLibuvSetTimeout(void *privdata, struct timeval tv) {
+    redisLibuvEvents* p = (redisLibuvEvents*)privdata;
+
+    uint64_t millsec = tv.tv_sec * 1000 + tv.tv_usec / 1000.0;
+    if (!p->timer.data) {
+        // timer is uninitialized
+        if (uv_timer_init(p->handle.loop, &p->timer) != 0) {
+            return;
+        }
+        p->timer.data = p;
+    }
+    // updates the timeout if the timer has already started
+    // or start the timer
+    uv_timer_start(&p->timer, redisLibuvTimeout, millsec, 0);
+}
+
+static void redisLibuvCleanup(void *privdata) {
+    redisLibuvEvents* p = (redisLibuvEvents*)privdata;
+
+    p->context = NULL; // indicate that context might no longer exist
+    if (p->timer.data) {
+        uv_close((uv_handle_t*)&p->timer, on_timer_close);
+    }
+    uv_close((uv_handle_t*)&p->handle, on_handle_close);
+}
+
+
+static int redisLibuvAttach(redisAsyncContext* ac, uv_loop_t* loop) {
+    redisContext *c = &(ac->c);
+
+    if (ac->ev.data != NULL) {
+        return REDIS_ERR;
+    }
+
+    ac->ev.addRead        = redisLibuvAddRead;
+    ac->ev.delRead        = redisLibuvDelRead;
+    ac->ev.addWrite       = redisLibuvAddWrite;
+    ac->ev.delWrite       = redisLibuvDelWrite;
+    ac->ev.cleanup        = redisLibuvCleanup;
+    ac->ev.scheduleTimer  = redisLibuvSetTimeout;
+
+    redisLibuvEvents* p = (redisLibuvEvents*)hi_malloc(sizeof(*p));
+    if (p == NULL)
+        return REDIS_ERR;
+
+    memset(p, 0, sizeof(*p));
+
+    if (uv_poll_init_socket(loop, &p->handle, c->fd) != 0) {
+        return REDIS_ERR;
+    }
+
+    ac->ev.data    = p;
+    p->handle.data = p;
+    p->context     = ac;
+
+    return REDIS_OK;
+}
+#endif

+ 115 - 0
thirdparty/hiredis/include/hiredis/adapters/macosx.h

@@ -0,0 +1,115 @@
+//
+//  Created by Дмитрий Бахвалов on 13.07.15.
+//  Copyright (c) 2015 Dmitry Bakhvalov. All rights reserved.
+//
+
+#ifndef __HIREDIS_MACOSX_H__
+#define __HIREDIS_MACOSX_H__
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include "../hiredis.h"
+#include "../async.h"
+
+typedef struct {
+    redisAsyncContext *context;
+    CFSocketRef socketRef;
+    CFRunLoopSourceRef sourceRef;
+} RedisRunLoop;
+
+static int freeRedisRunLoop(RedisRunLoop* redisRunLoop) {
+    if( redisRunLoop != NULL ) {
+        if( redisRunLoop->sourceRef != NULL ) {
+            CFRunLoopSourceInvalidate(redisRunLoop->sourceRef);
+            CFRelease(redisRunLoop->sourceRef);
+        }
+        if( redisRunLoop->socketRef != NULL ) {
+            CFSocketInvalidate(redisRunLoop->socketRef);
+            CFRelease(redisRunLoop->socketRef);
+        }
+        hi_free(redisRunLoop);
+    }
+    return REDIS_ERR;
+}
+
+static void redisMacOSAddRead(void *privdata) {
+    RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
+    CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack);
+}
+
+static void redisMacOSDelRead(void *privdata) {
+    RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
+    CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack);
+}
+
+static void redisMacOSAddWrite(void *privdata) {
+    RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
+    CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack);
+}
+
+static void redisMacOSDelWrite(void *privdata) {
+    RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
+    CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack);
+}
+
+static void redisMacOSCleanup(void *privdata) {
+    RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
+    freeRedisRunLoop(redisRunLoop);
+}
+
+static void redisMacOSAsyncCallback(CFSocketRef __unused s, CFSocketCallBackType callbackType, CFDataRef __unused address, const void __unused *data, void *info) {
+    redisAsyncContext* context = (redisAsyncContext*) info;
+
+    switch (callbackType) {
+        case kCFSocketReadCallBack:
+            redisAsyncHandleRead(context);
+            break;
+
+        case kCFSocketWriteCallBack:
+            redisAsyncHandleWrite(context);
+            break;
+
+        default:
+            break;
+    }
+}
+
+static int redisMacOSAttach(redisAsyncContext *redisAsyncCtx, CFRunLoopRef runLoop) {
+    redisContext *redisCtx = &(redisAsyncCtx->c);
+
+    /* Nothing should be attached when something is already attached */
+    if( redisAsyncCtx->ev.data != NULL ) return REDIS_ERR;
+
+    RedisRunLoop* redisRunLoop = (RedisRunLoop*) hi_calloc(1, sizeof(RedisRunLoop));
+    if (redisRunLoop == NULL)
+        return REDIS_ERR;
+
+    /* Setup redis stuff */
+    redisRunLoop->context = redisAsyncCtx;
+
+    redisAsyncCtx->ev.addRead  = redisMacOSAddRead;
+    redisAsyncCtx->ev.delRead  = redisMacOSDelRead;
+    redisAsyncCtx->ev.addWrite = redisMacOSAddWrite;
+    redisAsyncCtx->ev.delWrite = redisMacOSDelWrite;
+    redisAsyncCtx->ev.cleanup  = redisMacOSCleanup;
+    redisAsyncCtx->ev.data     = redisRunLoop;
+
+    /* Initialize and install read/write events */
+    CFSocketContext socketCtx = { 0, redisAsyncCtx, NULL, NULL, NULL };
+
+    redisRunLoop->socketRef = CFSocketCreateWithNative(NULL, redisCtx->fd,
+                                                       kCFSocketReadCallBack | kCFSocketWriteCallBack,
+                                                       redisMacOSAsyncCallback,
+                                                       &socketCtx);
+    if( !redisRunLoop->socketRef ) return freeRedisRunLoop(redisRunLoop);
+
+    redisRunLoop->sourceRef = CFSocketCreateRunLoopSource(NULL, redisRunLoop->socketRef, 0);
+    if( !redisRunLoop->sourceRef ) return freeRedisRunLoop(redisRunLoop);
+
+    CFRunLoopAddSource(runLoop, redisRunLoop->sourceRef, kCFRunLoopDefaultMode);
+
+    return REDIS_OK;
+}
+
+#endif
+

+ 197 - 0
thirdparty/hiredis/include/hiredis/adapters/poll.h

@@ -0,0 +1,197 @@
+
+#ifndef HIREDIS_POLL_H
+#define HIREDIS_POLL_H
+
+#include "../async.h"
+#include "../sockcompat.h"
+#include <string.h> // for memset
+#include <errno.h>
+
+/* Values to return from redisPollTick */
+#define REDIS_POLL_HANDLED_READ    1
+#define REDIS_POLL_HANDLED_WRITE   2
+#define REDIS_POLL_HANDLED_TIMEOUT 4
+
+/* An adapter to allow manual polling of the async context by checking the state
+ * of the underlying file descriptor.  Useful in cases where there is no formal
+ * IO event loop but regular ticking can be used, such as in game engines. */
+
+typedef struct redisPollEvents {
+    redisAsyncContext *context;
+    redisFD fd;
+    char reading, writing;
+    char in_tick;
+    char deleted;
+    double deadline;
+} redisPollEvents;
+
+static double redisPollTimevalToDouble(struct timeval *tv) {
+    if (tv == NULL)
+        return 0.0;
+    return tv->tv_sec + tv->tv_usec / 1000000.00;
+}
+
+static double redisPollGetNow(void) {
+#ifndef _MSC_VER
+    struct timeval tv;
+    gettimeofday(&tv,NULL);
+    return redisPollTimevalToDouble(&tv);
+#else
+    FILETIME ft;
+    ULARGE_INTEGER li;
+    GetSystemTimeAsFileTime(&ft);
+    li.HighPart = ft.dwHighDateTime;
+    li.LowPart = ft.dwLowDateTime;
+    return (double)li.QuadPart * 1e-7;
+#endif
+}
+
+/* Poll for io, handling any pending callbacks.  The timeout argument can be
+ * positive to wait for a maximum given time for IO, zero to poll, or negative
+ * to wait forever */
+static int redisPollTick(redisAsyncContext *ac, double timeout) {
+    int reading, writing;
+    struct pollfd pfd;
+    int handled;
+    int ns;
+    int itimeout;
+
+    redisPollEvents *e = (redisPollEvents*)ac->ev.data;
+    if (!e)
+        return 0;
+
+    /* local flags, won't get changed during callbacks */
+    reading = e->reading;
+    writing = e->writing;
+    if (!reading && !writing)
+        return 0;
+
+    pfd.fd = e->fd;
+    pfd.events = 0;
+    if (reading)
+        pfd.events = POLLIN;   
+    if (writing)
+        pfd.events |= POLLOUT;
+
+    if (timeout >= 0.0) {
+        itimeout = (int)(timeout * 1000.0);
+    } else {
+        itimeout = -1;
+    }
+
+    ns = poll(&pfd, 1, itimeout);
+    if (ns < 0) {
+        /* ignore the EINTR error */
+        if (errno != EINTR)
+            return ns;
+        ns = 0;
+    }
+    
+    handled = 0;
+    e->in_tick = 1;
+    if (ns) {
+        if (reading && (pfd.revents & POLLIN)) {
+            redisAsyncHandleRead(ac);
+            handled |= REDIS_POLL_HANDLED_READ;
+        }
+        /* on Windows, connection failure is indicated with the Exception fdset.
+         * handle it the same as writable. */
+        if (writing && (pfd.revents & (POLLOUT | POLLERR))) {
+            /* context Read callback may have caused context to be deleted, e.g.
+               by doing an redisAsyncDisconnect() */
+            if (!e->deleted) {
+                redisAsyncHandleWrite(ac);
+                handled |= REDIS_POLL_HANDLED_WRITE;
+            }
+        }
+    }
+
+    /* perform timeouts */
+    if (!e->deleted && e->deadline != 0.0) {
+        double now = redisPollGetNow();
+        if (now >= e->deadline) {
+            /* deadline has passed.  disable timeout and perform callback */
+            e->deadline = 0.0;
+            redisAsyncHandleTimeout(ac);
+            handled |= REDIS_POLL_HANDLED_TIMEOUT;
+        }
+    }
+
+    /* do a delayed cleanup if required */
+    if (e->deleted)
+        hi_free(e);
+    else
+        e->in_tick = 0;
+
+    return handled;
+}
+
+static void redisPollAddRead(void *data) {
+    redisPollEvents *e = (redisPollEvents*)data;
+    e->reading = 1;
+}
+
+static void redisPollDelRead(void *data) {
+    redisPollEvents *e = (redisPollEvents*)data;
+    e->reading = 0;
+}
+
+static void redisPollAddWrite(void *data) {
+    redisPollEvents *e = (redisPollEvents*)data;
+    e->writing = 1;
+}
+
+static void redisPollDelWrite(void *data) {
+    redisPollEvents *e = (redisPollEvents*)data;
+    e->writing = 0;
+}
+
+static void redisPollCleanup(void *data) {
+    redisPollEvents *e = (redisPollEvents*)data;
+
+    /* if we are currently processing a tick, postpone deletion */
+    if (e->in_tick)
+        e->deleted = 1;
+    else
+        hi_free(e);
+}
+
+static void redisPollScheduleTimer(void *data, struct timeval tv)
+{
+    redisPollEvents *e = (redisPollEvents*)data;
+    double now = redisPollGetNow();
+    e->deadline = now + redisPollTimevalToDouble(&tv);
+}
+
+static int redisPollAttach(redisAsyncContext *ac) {
+    redisContext *c = &(ac->c);
+    redisPollEvents *e;
+
+    /* Nothing should be attached when something is already attached */
+    if (ac->ev.data != NULL)
+        return REDIS_ERR;
+
+    /* Create container for context and r/w events */
+    e = (redisPollEvents*)hi_malloc(sizeof(*e));
+    if (e == NULL)
+        return REDIS_ERR;
+    memset(e, 0, sizeof(*e));
+
+    e->context = ac;
+    e->fd = c->fd;
+    e->reading = e->writing = 0;
+    e->in_tick = e->deleted = 0;
+    e->deadline = 0.0;
+
+    /* Register functions to start/stop listening for events */
+    ac->ev.addRead = redisPollAddRead;
+    ac->ev.delRead = redisPollDelRead;
+    ac->ev.addWrite = redisPollAddWrite;
+    ac->ev.delWrite = redisPollDelWrite;
+    ac->ev.scheduleTimer = redisPollScheduleTimer;
+    ac->ev.cleanup = redisPollCleanup;
+    ac->ev.data = e;
+
+    return REDIS_OK;
+}
+#endif /* HIREDIS_POLL_H */

+ 135 - 0
thirdparty/hiredis/include/hiredis/adapters/qt.h

@@ -0,0 +1,135 @@
+/*-
+ * Copyright (C) 2014 Pietro Cerutti <gahr@gahr.ch>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __HIREDIS_QT_H__
+#define __HIREDIS_QT_H__
+#include <QSocketNotifier>
+#include "../async.h"
+
+static void RedisQtAddRead(void *);
+static void RedisQtDelRead(void *);
+static void RedisQtAddWrite(void *);
+static void RedisQtDelWrite(void *);
+static void RedisQtCleanup(void *);
+
+class RedisQtAdapter : public QObject {
+
+    Q_OBJECT
+
+    friend
+    void RedisQtAddRead(void * adapter) {
+        RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
+        a->addRead();
+    }
+
+    friend
+    void RedisQtDelRead(void * adapter) {
+        RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
+        a->delRead();
+    }
+
+    friend
+    void RedisQtAddWrite(void * adapter) {
+        RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
+        a->addWrite();
+    }
+
+    friend
+    void RedisQtDelWrite(void * adapter) {
+        RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
+        a->delWrite();
+    }
+
+    friend
+    void RedisQtCleanup(void * adapter) {
+        RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
+        a->cleanup();
+    }
+
+    public:
+        RedisQtAdapter(QObject * parent = 0)
+            : QObject(parent), m_ctx(0), m_read(0), m_write(0) { }
+
+        ~RedisQtAdapter() {
+            if (m_ctx != 0) {
+                m_ctx->ev.data = NULL;
+            }
+        }
+
+        int setContext(redisAsyncContext * ac) {
+            if (ac->ev.data != NULL) {
+                return REDIS_ERR;
+            }
+            m_ctx = ac;
+            m_ctx->ev.data = this;
+            m_ctx->ev.addRead = RedisQtAddRead;
+            m_ctx->ev.delRead = RedisQtDelRead;
+            m_ctx->ev.addWrite = RedisQtAddWrite;
+            m_ctx->ev.delWrite = RedisQtDelWrite;
+            m_ctx->ev.cleanup = RedisQtCleanup;
+            return REDIS_OK;
+        }
+
+    private:
+        void addRead() {
+            if (m_read) return;
+            m_read = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Read, 0);
+            connect(m_read, SIGNAL(activated(int)), this, SLOT(read()));
+        }
+
+        void delRead() {
+            if (!m_read) return;
+            delete m_read;
+            m_read = 0;
+        }
+
+        void addWrite() {
+            if (m_write) return;
+            m_write = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Write, 0);
+            connect(m_write, SIGNAL(activated(int)), this, SLOT(write()));
+        }
+
+        void delWrite() {
+            if (!m_write) return;
+            delete m_write;
+            m_write = 0;
+        }
+
+        void cleanup() {
+            delRead();
+            delWrite();
+        }
+
+    private slots:
+        void read() { redisAsyncHandleRead(m_ctx); }
+        void write() { redisAsyncHandleWrite(m_ctx); }
+
+    private:
+        redisAsyncContext * m_ctx;
+        QSocketNotifier * m_read;
+        QSocketNotifier * m_write;
+};
+
+#endif /* !__HIREDIS_QT_H__ */

+ 96 - 0
thirdparty/hiredis/include/hiredis/alloc.h

@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2020, Michael Grunder <michael dot grunder at gmail dot com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Redis nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef HIREDIS_ALLOC_H
+#define HIREDIS_ALLOC_H
+
+#include <stddef.h> /* for size_t */
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Structure pointing to our actually configured allocators */
+typedef struct hiredisAllocFuncs {
+    void *(*mallocFn)(size_t);
+    void *(*callocFn)(size_t,size_t);
+    void *(*reallocFn)(void*,size_t);
+    char *(*strdupFn)(const char*);
+    void (*freeFn)(void*);
+} hiredisAllocFuncs;
+
+hiredisAllocFuncs hiredisSetAllocators(hiredisAllocFuncs *ha);
+void hiredisResetAllocators(void);
+
+#ifndef _WIN32
+
+/* Hiredis' configured allocator function pointer struct */
+extern hiredisAllocFuncs hiredisAllocFns;
+
+static inline void *hi_malloc(size_t size) {
+    return hiredisAllocFns.mallocFn(size);
+}
+
+static inline void *hi_calloc(size_t nmemb, size_t size) {
+    /* Overflow check as the user can specify any arbitrary allocator */
+    if (SIZE_MAX / size < nmemb)
+        return NULL;
+
+    return hiredisAllocFns.callocFn(nmemb, size);
+}
+
+static inline void *hi_realloc(void *ptr, size_t size) {
+    return hiredisAllocFns.reallocFn(ptr, size);
+}
+
+static inline char *hi_strdup(const char *str) {
+    return hiredisAllocFns.strdupFn(str);
+}
+
+static inline void hi_free(void *ptr) {
+    hiredisAllocFns.freeFn(ptr);
+}
+
+#else
+
+void *hi_malloc(size_t size);
+void *hi_calloc(size_t nmemb, size_t size);
+void *hi_realloc(void *ptr, size_t size);
+char *hi_strdup(const char *str);
+void hi_free(void *ptr);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HIREDIS_ALLOC_H */

+ 152 - 0
thirdparty/hiredis/include/hiredis/async.h

@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
+ * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Redis nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __HIREDIS_ASYNC_H
+#define __HIREDIS_ASYNC_H
+#include "hiredis.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct redisAsyncContext; /* need forward declaration of redisAsyncContext */
+struct dict; /* dictionary header is included in async.c */
+
+/* Reply callback prototype and container */
+typedef void (redisCallbackFn)(struct redisAsyncContext*, void*, void*);
+typedef struct redisCallback {
+    struct redisCallback *next; /* simple singly linked list */
+    redisCallbackFn *fn;
+    int pending_subs;
+    int unsubscribe_sent;
+    void *privdata;
+} redisCallback;
+
+/* List of callbacks for either regular replies or pub/sub */
+typedef struct redisCallbackList {
+    redisCallback *head, *tail;
+} redisCallbackList;
+
+/* Connection callback prototypes */
+typedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status);
+typedef void (redisConnectCallback)(const struct redisAsyncContext*, int status);
+typedef void (redisConnectCallbackNC)(struct redisAsyncContext *, int status);
+typedef void(redisTimerCallback)(void *timer, void *privdata);
+
+/* Context for an async connection to Redis */
+typedef struct redisAsyncContext {
+    /* Hold the regular context, so it can be realloc'ed. */
+    redisContext c;
+
+    /* Setup error flags so they can be used directly. */
+    int err;
+    char *errstr;
+
+    /* Not used by hiredis */
+    void *data;
+    void (*dataCleanup)(void *privdata);
+
+    /* Event library data and hooks */
+    struct {
+        void *data;
+
+        /* Hooks that are called when the library expects to start
+         * reading/writing. These functions should be idempotent. */
+        void (*addRead)(void *privdata);
+        void (*delRead)(void *privdata);
+        void (*addWrite)(void *privdata);
+        void (*delWrite)(void *privdata);
+        void (*cleanup)(void *privdata);
+        void (*scheduleTimer)(void *privdata, struct timeval tv);
+    } ev;
+
+    /* Called when either the connection is terminated due to an error or per
+     * user request. The status is set accordingly (REDIS_OK, REDIS_ERR). */
+    redisDisconnectCallback *onDisconnect;
+
+    /* Called when the first write event was received. */
+    redisConnectCallback *onConnect;
+    redisConnectCallbackNC *onConnectNC;
+
+    /* Regular command callbacks */
+    redisCallbackList replies;
+
+    /* Address used for connect() */
+    struct sockaddr *saddr;
+    size_t addrlen;
+
+    /* Subscription callbacks */
+    struct {
+        redisCallbackList replies;
+        struct dict *channels;
+        struct dict *patterns;
+        int pending_unsubs;
+    } sub;
+
+    /* Any configured RESP3 PUSH handler */
+    redisAsyncPushFn *push_cb;
+} redisAsyncContext;
+
+/* Functions that proxy to hiredis */
+redisAsyncContext *redisAsyncConnectWithOptions(const redisOptions *options);
+redisAsyncContext *redisAsyncConnect(const char *ip, int port);
+redisAsyncContext *redisAsyncConnectBind(const char *ip, int port, const char *source_addr);
+redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port,
+                                                  const char *source_addr);
+redisAsyncContext *redisAsyncConnectUnix(const char *path);
+int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
+int redisAsyncSetConnectCallbackNC(redisAsyncContext *ac, redisConnectCallbackNC *fn);
+int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
+
+redisAsyncPushFn *redisAsyncSetPushCallback(redisAsyncContext *ac, redisAsyncPushFn *fn);
+int redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv);
+void redisAsyncDisconnect(redisAsyncContext *ac);
+void redisAsyncFree(redisAsyncContext *ac);
+
+/* Handle read/write events */
+void redisAsyncHandleRead(redisAsyncContext *ac);
+void redisAsyncHandleWrite(redisAsyncContext *ac);
+void redisAsyncHandleTimeout(redisAsyncContext *ac);
+void redisAsyncRead(redisAsyncContext *ac);
+void redisAsyncWrite(redisAsyncContext *ac);
+
+/* Command functions for an async context. Write the command to the
+ * output buffer and register the provided callback. */
+int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap);
+int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...);
+int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen);
+int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 366 - 0
thirdparty/hiredis/include/hiredis/hiredis.h

@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
+ * Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ * Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,
+ *                     Jan-Erik Rediger <janerik at fnordig dot com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Redis nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __HIREDIS_H
+#define __HIREDIS_H
+#include "read.h"
+#include <stdarg.h> /* for va_list */
+#ifndef _MSC_VER
+#include <sys/time.h> /* for struct timeval */
+#else
+struct timeval; /* forward declaration */
+typedef long long ssize_t;
+#endif
+#include <stdint.h> /* uintXX_t, etc */
+#include "sds.h" /* for sds */
+#include "alloc.h" /* for allocation wrappers */
+
+#define HIREDIS_MAJOR 1
+#define HIREDIS_MINOR 1
+#define HIREDIS_PATCH 0
+#define HIREDIS_SONAME 1.1.0
+
+/* Connection type can be blocking or non-blocking and is set in the
+ * least significant bit of the flags field in redisContext. */
+#define REDIS_BLOCK 0x1
+
+/* Connection may be disconnected before being free'd. The second bit
+ * in the flags field is set when the context is connected. */
+#define REDIS_CONNECTED 0x2
+
+/* The async API might try to disconnect cleanly and flush the output
+ * buffer and read all subsequent replies before disconnecting.
+ * This flag means no new commands can come in and the connection
+ * should be terminated once all replies have been read. */
+#define REDIS_DISCONNECTING 0x4
+
+/* Flag specific to the async API which means that the context should be clean
+ * up as soon as possible. */
+#define REDIS_FREEING 0x8
+
+/* Flag that is set when an async callback is executed. */
+#define REDIS_IN_CALLBACK 0x10
+
+/* Flag that is set when the async context has one or more subscriptions. */
+#define REDIS_SUBSCRIBED 0x20
+
+/* Flag that is set when monitor mode is active */
+#define REDIS_MONITORING 0x40
+
+/* Flag that is set when we should set SO_REUSEADDR before calling bind() */
+#define REDIS_REUSEADDR 0x80
+
+/* Flag that is set when the async connection supports push replies. */
+#define REDIS_SUPPORTS_PUSH 0x100
+
+/**
+ * Flag that indicates the user does not want the context to
+ * be automatically freed upon error
+ */
+#define REDIS_NO_AUTO_FREE 0x200
+
+/* Flag that indicates the user does not want replies to be automatically freed */
+#define REDIS_NO_AUTO_FREE_REPLIES 0x400
+
+/* Flags to prefer IPv6 or IPv4 when doing DNS lookup. (If both are set,
+ * AF_UNSPEC is used.) */
+#define REDIS_PREFER_IPV4 0x800
+#define REDIS_PREFER_IPV6 0x1000
+
+#define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */
+
+/* number of times we retry to connect in the case of EADDRNOTAVAIL and
+ * SO_REUSEADDR is being used. */
+#define REDIS_CONNECT_RETRIES  10
+
+/* Forward declarations for structs defined elsewhere */
+struct redisAsyncContext;
+struct redisContext;
+
+/* RESP3 push helpers and callback prototypes */
+#define redisIsPushReply(r) (((redisReply*)(r))->type == REDIS_REPLY_PUSH)
+typedef void (redisPushFn)(void *, void *);
+typedef void (redisAsyncPushFn)(struct redisAsyncContext *, void *);
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* This is the reply object returned by redisCommand() */
+typedef struct redisReply {
+    int type; /* REDIS_REPLY_* */
+    long long integer; /* The integer when type is REDIS_REPLY_INTEGER */
+    double dval; /* The double when type is REDIS_REPLY_DOUBLE */
+    size_t len; /* Length of string */
+    char *str; /* Used for REDIS_REPLY_ERROR, REDIS_REPLY_STRING
+                  REDIS_REPLY_VERB, REDIS_REPLY_DOUBLE (in additional to dval),
+                  and REDIS_REPLY_BIGNUM. */
+    char vtype[4]; /* Used for REDIS_REPLY_VERB, contains the null
+                      terminated 3 character content type, such as "txt". */
+    size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */
+    struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */
+} redisReply;
+
+redisReader *redisReaderCreate(void);
+
+/* Function to free the reply objects hiredis returns by default. */
+void freeReplyObject(void *reply);
+
+/* Functions to format a command according to the protocol. */
+int redisvFormatCommand(char **target, const char *format, va_list ap);
+int redisFormatCommand(char **target, const char *format, ...);
+long long redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen);
+long long redisFormatSdsCommandArgv(sds *target, int argc, const char ** argv, const size_t *argvlen);
+void redisFreeCommand(char *cmd);
+void redisFreeSdsCommand(sds cmd);
+
+enum redisConnectionType {
+    REDIS_CONN_TCP,
+    REDIS_CONN_UNIX,
+    REDIS_CONN_USERFD
+};
+
+struct redisSsl;
+
+#define REDIS_OPT_NONBLOCK 0x01
+#define REDIS_OPT_REUSEADDR 0x02
+#define REDIS_OPT_PREFER_IPV4 0x04
+#define REDIS_OPT_PREFER_IPV6 0x08
+#define REDIS_OPT_PREFER_IP_UNSPEC (REDIS_OPT_PREFER_IPV4 | REDIS_OPT_PREFER_IPV6)
+
+/**
+ * Don't automatically free the async object on a connection failure,
+ * or other implicit conditions. Only free on an explicit call to disconnect() or free()
+ */
+#define REDIS_OPT_NOAUTOFREE 0x04
+
+/* Don't automatically intercept and free RESP3 PUSH replies. */
+#define REDIS_OPT_NO_PUSH_AUTOFREE 0x08
+
+/**
+ * Don't automatically free replies
+ */
+#define REDIS_OPT_NOAUTOFREEREPLIES 0x10
+
+/* In Unix systems a file descriptor is a regular signed int, with -1
+ * representing an invalid descriptor. In Windows it is a SOCKET
+ * (32- or 64-bit unsigned integer depending on the architecture), where
+ * all bits set (~0) is INVALID_SOCKET.  */
+#ifndef _WIN32
+typedef int redisFD;
+#define REDIS_INVALID_FD -1
+#else
+#ifdef _WIN64
+typedef unsigned long long redisFD; /* SOCKET = 64-bit UINT_PTR */
+#else
+typedef unsigned long redisFD;      /* SOCKET = 32-bit UINT_PTR */
+#endif
+#define REDIS_INVALID_FD ((redisFD)(~0)) /* INVALID_SOCKET */
+#endif
+
+typedef struct {
+    /*
+     * the type of connection to use. This also indicates which
+     * `endpoint` member field to use
+     */
+    int type;
+    /* bit field of REDIS_OPT_xxx */
+    int options;
+    /* timeout value for connect operation. If NULL, no timeout is used */
+    const struct timeval *connect_timeout;
+    /* timeout value for commands. If NULL, no timeout is used.  This can be
+     * updated at runtime with redisSetTimeout/redisAsyncSetTimeout. */
+    const struct timeval *command_timeout;
+    union {
+        /** use this field for tcp/ip connections */
+        struct {
+            const char *source_addr;
+            const char *ip;
+            int port;
+        } tcp;
+        /** use this field for unix domain sockets */
+        const char *unix_socket;
+        /**
+         * use this field to have hiredis operate an already-open
+         * file descriptor */
+        redisFD fd;
+    } endpoint;
+
+    /* Optional user defined data/destructor */
+    void *privdata;
+    void (*free_privdata)(void *);
+
+    /* A user defined PUSH message callback */
+    redisPushFn *push_cb;
+    redisAsyncPushFn *async_push_cb;
+} redisOptions;
+
+/**
+ * Helper macros to initialize options to their specified fields.
+ */
+#define REDIS_OPTIONS_SET_TCP(opts, ip_, port_) do { \
+        (opts)->type = REDIS_CONN_TCP;               \
+        (opts)->endpoint.tcp.ip = ip_;               \
+        (opts)->endpoint.tcp.port = port_;           \
+    } while(0)
+
+#define REDIS_OPTIONS_SET_UNIX(opts, path) do { \
+        (opts)->type = REDIS_CONN_UNIX;         \
+        (opts)->endpoint.unix_socket = path;    \
+    } while(0)
+
+#define REDIS_OPTIONS_SET_PRIVDATA(opts, data, dtor) do {  \
+        (opts)->privdata = data;                           \
+        (opts)->free_privdata = dtor;                      \
+    } while(0)
+
+typedef struct redisContextFuncs {
+    void (*close)(struct redisContext *);
+    void (*free_privctx)(void *);
+    void (*async_read)(struct redisAsyncContext *);
+    void (*async_write)(struct redisAsyncContext *);
+
+    /* Read/Write data to the underlying communication stream, returning the
+     * number of bytes read/written.  In the event of an unrecoverable error
+     * these functions shall return a value < 0.  In the event of a
+     * recoverable error, they should return 0. */
+    ssize_t (*read)(struct redisContext *, char *, size_t);
+    ssize_t (*write)(struct redisContext *);
+} redisContextFuncs;
+
+
+/* Context for a connection to Redis */
+typedef struct redisContext {
+    const redisContextFuncs *funcs;   /* Function table */
+
+    int err; /* Error flags, 0 when there is no error */
+    char errstr[128]; /* String representation of error when applicable */
+    redisFD fd;
+    int flags;
+    char *obuf; /* Write buffer */
+    redisReader *reader; /* Protocol reader */
+
+    enum redisConnectionType connection_type;
+    struct timeval *connect_timeout;
+    struct timeval *command_timeout;
+
+    struct {
+        char *host;
+        char *source_addr;
+        int port;
+    } tcp;
+
+    struct {
+        char *path;
+    } unix_sock;
+
+    /* For non-blocking connect */
+    struct sockaddr *saddr;
+    size_t addrlen;
+
+    /* Optional data and corresponding destructor users can use to provide
+     * context to a given redisContext.  Not used by hiredis. */
+    void *privdata;
+    void (*free_privdata)(void *);
+
+    /* Internal context pointer presently used by hiredis to manage
+     * SSL connections. */
+    void *privctx;
+
+    /* An optional RESP3 PUSH handler */
+    redisPushFn *push_cb;
+} redisContext;
+
+redisContext *redisConnectWithOptions(const redisOptions *options);
+redisContext *redisConnect(const char *ip, int port);
+redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv);
+redisContext *redisConnectNonBlock(const char *ip, int port);
+redisContext *redisConnectBindNonBlock(const char *ip, int port,
+                                       const char *source_addr);
+redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,
+                                                const char *source_addr);
+redisContext *redisConnectUnix(const char *path);
+redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv);
+redisContext *redisConnectUnixNonBlock(const char *path);
+redisContext *redisConnectFd(redisFD fd);
+
+/**
+ * Reconnect the given context using the saved information.
+ *
+ * This re-uses the exact same connect options as in the initial connection.
+ * host, ip (or path), timeout and bind address are reused,
+ * flags are used unmodified from the existing context.
+ *
+ * Returns REDIS_OK on successful connect or REDIS_ERR otherwise.
+ */
+int redisReconnect(redisContext *c);
+
+redisPushFn *redisSetPushCallback(redisContext *c, redisPushFn *fn);
+int redisSetTimeout(redisContext *c, const struct timeval tv);
+int redisEnableKeepAlive(redisContext *c);
+void redisFree(redisContext *c);
+redisFD redisFreeKeepFd(redisContext *c);
+int redisBufferRead(redisContext *c);
+int redisBufferWrite(redisContext *c, int *done);
+
+/* In a blocking context, this function first checks if there are unconsumed
+ * replies to return and returns one if so. Otherwise, it flushes the output
+ * buffer to the socket and reads until it has a reply. In a non-blocking
+ * context, it will return unconsumed replies until there are no more. */
+int redisGetReply(redisContext *c, void **reply);
+int redisGetReplyFromReader(redisContext *c, void **reply);
+
+/* Write a formatted command to the output buffer. Use these functions in blocking mode
+ * to get a pipeline of commands. */
+int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len);
+
+/* Write a command to the output buffer. Use these functions in blocking mode
+ * to get a pipeline of commands. */
+int redisvAppendCommand(redisContext *c, const char *format, va_list ap);
+int redisAppendCommand(redisContext *c, const char *format, ...);
+int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
+
+/* Issue a command to Redis. In a blocking context, it is identical to calling
+ * redisAppendCommand, followed by redisGetReply. The function will return
+ * NULL if there was an error in performing the request, otherwise it will
+ * return the reply. In a non-blocking context, it is identical to calling
+ * only redisAppendCommand and will always return NULL. */
+void *redisvCommand(redisContext *c, const char *format, va_list ap);
+void *redisCommand(redisContext *c, const char *format, ...);
+void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 129 - 0
thirdparty/hiredis/include/hiredis/read.h

@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
+ * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Redis nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#ifndef __HIREDIS_READ_H
+#define __HIREDIS_READ_H
+#include <stdio.h> /* for size_t */
+
+#define REDIS_ERR -1
+#define REDIS_OK 0
+
+/* When an error occurs, the err flag in a context is set to hold the type of
+ * error that occurred. REDIS_ERR_IO means there was an I/O error and you
+ * should use the "errno" variable to find out what is wrong.
+ * For other values, the "errstr" field will hold a description. */
+#define REDIS_ERR_IO 1 /* Error in read or write */
+#define REDIS_ERR_EOF 3 /* End of file */
+#define REDIS_ERR_PROTOCOL 4 /* Protocol error */
+#define REDIS_ERR_OOM 5 /* Out of memory */
+#define REDIS_ERR_TIMEOUT 6 /* Timed out */
+#define REDIS_ERR_OTHER 2 /* Everything else... */
+
+#define REDIS_REPLY_STRING 1
+#define REDIS_REPLY_ARRAY 2
+#define REDIS_REPLY_INTEGER 3
+#define REDIS_REPLY_NIL 4
+#define REDIS_REPLY_STATUS 5
+#define REDIS_REPLY_ERROR 6
+#define REDIS_REPLY_DOUBLE 7
+#define REDIS_REPLY_BOOL 8
+#define REDIS_REPLY_MAP 9
+#define REDIS_REPLY_SET 10
+#define REDIS_REPLY_ATTR 11
+#define REDIS_REPLY_PUSH 12
+#define REDIS_REPLY_BIGNUM 13
+#define REDIS_REPLY_VERB 14
+
+/* Default max unused reader buffer. */
+#define REDIS_READER_MAX_BUF (1024*16)
+
+/* Default multi-bulk element limit */
+#define REDIS_READER_MAX_ARRAY_ELEMENTS ((1LL<<32) - 1)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct redisReadTask {
+    int type;
+    long long elements; /* number of elements in multibulk container */
+    int idx; /* index in parent (array) object */
+    void *obj; /* holds user-generated value for a read task */
+    struct redisReadTask *parent; /* parent task */
+    void *privdata; /* user-settable arbitrary field */
+} redisReadTask;
+
+typedef struct redisReplyObjectFunctions {
+    void *(*createString)(const redisReadTask*, char*, size_t);
+    void *(*createArray)(const redisReadTask*, size_t);
+    void *(*createInteger)(const redisReadTask*, long long);
+    void *(*createDouble)(const redisReadTask*, double, char*, size_t);
+    void *(*createNil)(const redisReadTask*);
+    void *(*createBool)(const redisReadTask*, int);
+    void (*freeObject)(void*);
+} redisReplyObjectFunctions;
+
+typedef struct redisReader {
+    int err; /* Error flags, 0 when there is no error */
+    char errstr[128]; /* String representation of error when applicable */
+
+    char *buf; /* Read buffer */
+    size_t pos; /* Buffer cursor */
+    size_t len; /* Buffer length */
+    size_t maxbuf; /* Max length of unused buffer */
+    long long maxelements; /* Max multi-bulk elements */
+
+    redisReadTask **task;
+    int tasks;
+
+    int ridx; /* Index of current read task */
+    void *reply; /* Temporary reply pointer */
+
+    redisReplyObjectFunctions *fn;
+    void *privdata;
+} redisReader;
+
+/* Public API for the protocol parser. */
+redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn);
+void redisReaderFree(redisReader *r);
+int redisReaderFeed(redisReader *r, const char *buf, size_t len);
+int redisReaderGetReply(redisReader *r, void **reply);
+
+#define redisReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p))
+#define redisReaderGetObject(_r) (((redisReader*)(_r))->reply)
+#define redisReaderGetError(_r) (((redisReader*)(_r))->errstr)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 280 - 0
thirdparty/hiredis/include/hiredis/sds.h

@@ -0,0 +1,280 @@
+/* SDSLib 2.0 -- A C dynamic strings library
+ *
+ * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>
+ * Copyright (c) 2015, Oran Agra
+ * Copyright (c) 2015, Redis Labs, Inc
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Redis nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __SDS_H
+#define __SDS_H
+
+#define SDS_MAX_PREALLOC (1024*1024)
+#ifdef _MSC_VER
+typedef long long ssize_t;
+#define SSIZE_MAX (LLONG_MAX >> 1)
+#ifndef __clang__
+#define __attribute__(x)
+#endif
+#endif
+
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stdint.h>
+
+typedef char *sds;
+
+/* Note: sdshdr5 is never used, we just access the flags byte directly.
+ * However is here to document the layout of type 5 SDS strings. */
+struct __attribute__ ((__packed__)) sdshdr5 {
+    unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
+    char buf[];
+};
+struct __attribute__ ((__packed__)) sdshdr8 {
+    uint8_t len; /* used */
+    uint8_t alloc; /* excluding the header and null terminator */
+    unsigned char flags; /* 3 lsb of type, 5 unused bits */
+    char buf[];
+};
+struct __attribute__ ((__packed__)) sdshdr16 {
+    uint16_t len; /* used */
+    uint16_t alloc; /* excluding the header and null terminator */
+    unsigned char flags; /* 3 lsb of type, 5 unused bits */
+    char buf[];
+};
+struct __attribute__ ((__packed__)) sdshdr32 {
+    uint32_t len; /* used */
+    uint32_t alloc; /* excluding the header and null terminator */
+    unsigned char flags; /* 3 lsb of type, 5 unused bits */
+    char buf[];
+};
+struct __attribute__ ((__packed__)) sdshdr64 {
+    uint64_t len; /* used */
+    uint64_t alloc; /* excluding the header and null terminator */
+    unsigned char flags; /* 3 lsb of type, 5 unused bits */
+    char buf[];
+};
+
+#define SDS_TYPE_5  0
+#define SDS_TYPE_8  1
+#define SDS_TYPE_16 2
+#define SDS_TYPE_32 3
+#define SDS_TYPE_64 4
+#define SDS_TYPE_MASK 7
+#define SDS_TYPE_BITS 3
+#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)));
+#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))
+#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)
+
+static inline size_t sdslen(const sds s) {
+    unsigned char flags = s[-1];
+    switch(flags&SDS_TYPE_MASK) {
+        case SDS_TYPE_5:
+            return SDS_TYPE_5_LEN(flags);
+        case SDS_TYPE_8:
+            return SDS_HDR(8,s)->len;
+        case SDS_TYPE_16:
+            return SDS_HDR(16,s)->len;
+        case SDS_TYPE_32:
+            return SDS_HDR(32,s)->len;
+        case SDS_TYPE_64:
+            return SDS_HDR(64,s)->len;
+    }
+    return 0;
+}
+
+static inline size_t sdsavail(const sds s) {
+    unsigned char flags = s[-1];
+    switch(flags&SDS_TYPE_MASK) {
+        case SDS_TYPE_5: {
+            return 0;
+        }
+        case SDS_TYPE_8: {
+            SDS_HDR_VAR(8,s);
+            return sh->alloc - sh->len;
+        }
+        case SDS_TYPE_16: {
+            SDS_HDR_VAR(16,s);
+            return sh->alloc - sh->len;
+        }
+        case SDS_TYPE_32: {
+            SDS_HDR_VAR(32,s);
+            return sh->alloc - sh->len;
+        }
+        case SDS_TYPE_64: {
+            SDS_HDR_VAR(64,s);
+            return sh->alloc - sh->len;
+        }
+    }
+    return 0;
+}
+
+static inline void sdssetlen(sds s, size_t newlen) {
+    unsigned char flags = s[-1];
+    switch(flags&SDS_TYPE_MASK) {
+        case SDS_TYPE_5:
+            {
+                unsigned char *fp = ((unsigned char*)s)-1;
+                *fp = (unsigned char)(SDS_TYPE_5 | (newlen << SDS_TYPE_BITS));
+            }
+            break;
+        case SDS_TYPE_8:
+            SDS_HDR(8,s)->len = (uint8_t)newlen;
+            break;
+        case SDS_TYPE_16:
+            SDS_HDR(16,s)->len = (uint16_t)newlen;
+            break;
+        case SDS_TYPE_32:
+            SDS_HDR(32,s)->len = (uint32_t)newlen;
+            break;
+        case SDS_TYPE_64:
+            SDS_HDR(64,s)->len = (uint64_t)newlen;
+            break;
+    }
+}
+
+static inline void sdsinclen(sds s, size_t inc) {
+    unsigned char flags = s[-1];
+    switch(flags&SDS_TYPE_MASK) {
+        case SDS_TYPE_5:
+            {
+                unsigned char *fp = ((unsigned char*)s)-1;
+                unsigned char newlen = SDS_TYPE_5_LEN(flags)+(unsigned char)inc;
+                *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
+            }
+            break;
+        case SDS_TYPE_8:
+            SDS_HDR(8,s)->len += (uint8_t)inc;
+            break;
+        case SDS_TYPE_16:
+            SDS_HDR(16,s)->len += (uint16_t)inc;
+            break;
+        case SDS_TYPE_32:
+            SDS_HDR(32,s)->len += (uint32_t)inc;
+            break;
+        case SDS_TYPE_64:
+            SDS_HDR(64,s)->len += (uint64_t)inc;
+            break;
+    }
+}
+
+/* sdsalloc() = sdsavail() + sdslen() */
+static inline size_t sdsalloc(const sds s) {
+    unsigned char flags = s[-1];
+    switch(flags&SDS_TYPE_MASK) {
+        case SDS_TYPE_5:
+            return SDS_TYPE_5_LEN(flags);
+        case SDS_TYPE_8:
+            return SDS_HDR(8,s)->alloc;
+        case SDS_TYPE_16:
+            return SDS_HDR(16,s)->alloc;
+        case SDS_TYPE_32:
+            return SDS_HDR(32,s)->alloc;
+        case SDS_TYPE_64:
+            return SDS_HDR(64,s)->alloc;
+    }
+    return 0;
+}
+
+static inline void sdssetalloc(sds s, size_t newlen) {
+    unsigned char flags = s[-1];
+    switch(flags&SDS_TYPE_MASK) {
+        case SDS_TYPE_5:
+            /* Nothing to do, this type has no total allocation info. */
+            break;
+        case SDS_TYPE_8:
+            SDS_HDR(8,s)->alloc = (uint8_t)newlen;
+            break;
+        case SDS_TYPE_16:
+            SDS_HDR(16,s)->alloc = (uint16_t)newlen;
+            break;
+        case SDS_TYPE_32:
+            SDS_HDR(32,s)->alloc = (uint32_t)newlen;
+            break;
+        case SDS_TYPE_64:
+            SDS_HDR(64,s)->alloc = (uint64_t)newlen;
+            break;
+    }
+}
+
+sds sdsnewlen(const void *init, size_t initlen);
+sds sdsnew(const char *init);
+sds sdsempty(void);
+sds sdsdup(const sds s);
+void sdsfree(sds s);
+sds sdsgrowzero(sds s, size_t len);
+sds sdscatlen(sds s, const void *t, size_t len);
+sds sdscat(sds s, const char *t);
+sds sdscatsds(sds s, const sds t);
+sds sdscpylen(sds s, const char *t, size_t len);
+sds sdscpy(sds s, const char *t);
+
+sds sdscatvprintf(sds s, const char *fmt, va_list ap);
+#ifdef __GNUC__
+sds sdscatprintf(sds s, const char *fmt, ...)
+    __attribute__((format(printf, 2, 3)));
+#else
+sds sdscatprintf(sds s, const char *fmt, ...);
+#endif
+
+sds sdscatfmt(sds s, char const *fmt, ...);
+sds sdstrim(sds s, const char *cset);
+int sdsrange(sds s, ssize_t start, ssize_t end);
+void sdsupdatelen(sds s);
+void sdsclear(sds s);
+int sdscmp(const sds s1, const sds s2);
+sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count);
+void sdsfreesplitres(sds *tokens, int count);
+void sdstolower(sds s);
+void sdstoupper(sds s);
+sds sdsfromlonglong(long long value);
+sds sdscatrepr(sds s, const char *p, size_t len);
+sds *sdssplitargs(const char *line, int *argc);
+sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen);
+sds sdsjoin(char **argv, int argc, char *sep);
+sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen);
+
+/* Low level functions exposed to the user API */
+sds sdsMakeRoomFor(sds s, size_t addlen);
+void sdsIncrLen(sds s, int incr);
+sds sdsRemoveFreeSpace(sds s);
+size_t sdsAllocSize(sds s);
+void *sdsAllocPtr(sds s);
+
+/* Export the allocator used by SDS to the program using SDS.
+ * Sometimes the program SDS is linked to, may use a different set of
+ * allocators, but may want to allocate or free things that SDS will
+ * respectively free or allocate. */
+void *sds_malloc(size_t size);
+void *sds_realloc(void *ptr, size_t size);
+void sds_free(void *ptr);
+
+#ifdef REDIS_TEST
+int sdsTest(int argc, char *argv[]);
+#endif
+
+#endif

+ 95 - 0
thirdparty/hiredis/include/hiredis/sockcompat.h

@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2019, Marcus Geelnard <m at bitsnbites dot eu>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Redis nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __SOCKCOMPAT_H
+#define __SOCKCOMPAT_H
+
+#ifndef _WIN32
+/* For POSIX systems we use the standard BSD socket API. */
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <poll.h>
+#else
+/* For Windows we use winsock. */
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0600 /* To get WSAPoll etc. */
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <stddef.h>
+#include <errno.h>
+#include <mstcpip.h>
+
+#ifdef _MSC_VER
+typedef long long ssize_t;
+#endif
+
+/* Emulate the parts of the BSD socket API that we need (override the winsock signatures). */
+int win32_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res);
+const char *win32_gai_strerror(int errcode);
+void win32_freeaddrinfo(struct addrinfo *res);
+SOCKET win32_socket(int domain, int type, int protocol);
+int win32_ioctl(SOCKET fd, unsigned long request, unsigned long *argp);
+int win32_bind(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen);
+int win32_connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen);
+int win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, socklen_t *optlen);
+int win32_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen);
+int win32_close(SOCKET fd);
+ssize_t win32_recv(SOCKET sockfd, void *buf, size_t len, int flags);
+ssize_t win32_send(SOCKET sockfd, const void *buf, size_t len, int flags);
+typedef ULONG nfds_t;
+int win32_poll(struct pollfd *fds, nfds_t nfds, int timeout);
+
+int win32_redisKeepAlive(SOCKET sockfd, int interval_ms);
+
+#ifndef REDIS_SOCKCOMPAT_IMPLEMENTATION
+#define getaddrinfo(node, service, hints, res) win32_getaddrinfo(node, service, hints, res)
+#undef gai_strerror
+#define gai_strerror(errcode) win32_gai_strerror(errcode)
+#define freeaddrinfo(res) win32_freeaddrinfo(res)
+#define socket(domain, type, protocol) win32_socket(domain, type, protocol)
+#define ioctl(fd, request, argp) win32_ioctl(fd, request, argp)
+#define bind(sockfd, addr, addrlen) win32_bind(sockfd, addr, addrlen)
+#define connect(sockfd, addr, addrlen) win32_connect(sockfd, addr, addrlen)
+#define getsockopt(sockfd, level, optname, optval, optlen) win32_getsockopt(sockfd, level, optname, optval, optlen)
+#define setsockopt(sockfd, level, optname, optval, optlen) win32_setsockopt(sockfd, level, optname, optval, optlen)
+#define close(fd) win32_close(fd)
+#define recv(sockfd, buf, len, flags) win32_recv(sockfd, buf, len, flags)
+#define send(sockfd, buf, len, flags) win32_send(sockfd, buf, len, flags)
+#define poll(fds, nfds, timeout) win32_poll(fds, nfds, timeout)
+#endif /* REDIS_SOCKCOMPAT_IMPLEMENTATION */
+#endif /* _WIN32 */
+
+#endif /* __SOCKCOMPAT_H */

BIN
thirdparty/hiredis/lib/libhiredis.a


BIN
thirdparty/hiredis/lib/libhiredis.dll.a


+ 12 - 0
thirdparty/hiredis/lib/pkgconfig/hiredis.pc

@@ -0,0 +1,12 @@
+prefix=D:/dev/hiredis
+install_libdir=lib
+exec_prefix=${prefix}
+libdir=${exec_prefix}/${install_libdir}
+includedir=${prefix}/include
+pkgincludedir=${includedir}/hiredis
+
+Name: hiredis
+Description: Minimalistic C client library for Redis.
+Version: 1.1.0
+Libs: -L${libdir} -lhiredis
+Cflags: -I${pkgincludedir} -I${includedir} -D_FILE_OFFSET_BITS=64

+ 37 - 0
thirdparty/hiredis/share/hiredis/hiredis-config.cmake

@@ -0,0 +1,37 @@
+
+####### Expanded from @PACKAGE_INIT@ by configure_package_config_file() #######
+####### Any changes to this file will be overwritten by the next CMake run ####
+####### The input file was hiredis-config.cmake.in                            ########
+
+get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../../" ABSOLUTE)
+
+macro(set_and_check _var _file)
+  set(${_var} "${_file}")
+  if(NOT EXISTS "${_file}")
+    message(FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist !")
+  endif()
+endmacro()
+
+macro(check_required_components _NAME)
+  foreach(comp ${${_NAME}_FIND_COMPONENTS})
+    if(NOT ${_NAME}_${comp}_FOUND)
+      if(${_NAME}_FIND_REQUIRED_${comp})
+        set(${_NAME}_FOUND FALSE)
+      endif()
+    endif()
+  endforeach()
+endmacro()
+
+####################################################################################
+
+set_and_check(hiredis_INCLUDEDIR "${PACKAGE_PREFIX_DIR}/include")
+
+IF (NOT TARGET hiredis::hiredis)
+	INCLUDE(${CMAKE_CURRENT_LIST_DIR}/hiredis-targets.cmake)
+ENDIF()
+
+SET(hiredis_LIBRARIES hiredis::hiredis)
+SET(hiredis_INCLUDE_DIRS ${hiredis_INCLUDEDIR})
+
+check_required_components(hiredis)
+

+ 29 - 0
thirdparty/hiredis/share/hiredis/hiredis-targets-release.cmake

@@ -0,0 +1,29 @@
+#----------------------------------------------------------------
+# Generated CMake target import file for configuration "Release".
+#----------------------------------------------------------------
+
+# Commands may need to know the format version.
+set(CMAKE_IMPORT_FILE_VERSION 1)
+
+# Import target "hiredis::hiredis" for configuration "Release"
+set_property(TARGET hiredis::hiredis APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
+set_target_properties(hiredis::hiredis PROPERTIES
+  IMPORTED_IMPLIB_RELEASE "${_IMPORT_PREFIX}/lib/libhiredis.dll.a"
+  IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/bin/libhiredis.dll"
+  )
+
+list(APPEND _cmake_import_check_targets hiredis::hiredis )
+list(APPEND _cmake_import_check_files_for_hiredis::hiredis "${_IMPORT_PREFIX}/lib/libhiredis.dll.a" "${_IMPORT_PREFIX}/bin/libhiredis.dll" )
+
+# Import target "hiredis::hiredis_static" for configuration "Release"
+set_property(TARGET hiredis::hiredis_static APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
+set_target_properties(hiredis::hiredis_static PROPERTIES
+  IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C"
+  IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/libhiredis.a"
+  )
+
+list(APPEND _cmake_import_check_targets hiredis::hiredis_static )
+list(APPEND _cmake_import_check_files_for_hiredis::hiredis_static "${_IMPORT_PREFIX}/lib/libhiredis.a" )
+
+# Commands beyond this point should not need to know the version.
+set(CMAKE_IMPORT_FILE_VERSION)

+ 114 - 0
thirdparty/hiredis/share/hiredis/hiredis-targets.cmake

@@ -0,0 +1,114 @@
+# Generated by CMake
+
+if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 2.8)
+   message(FATAL_ERROR "CMake >= 2.8.0 required")
+endif()
+if(CMAKE_VERSION VERSION_LESS "2.8.3")
+   message(FATAL_ERROR "CMake >= 2.8.3 required")
+endif()
+cmake_policy(PUSH)
+cmake_policy(VERSION 2.8.3...3.22)
+#----------------------------------------------------------------
+# Generated CMake target import file.
+#----------------------------------------------------------------
+
+# Commands may need to know the format version.
+set(CMAKE_IMPORT_FILE_VERSION 1)
+
+# Protect against multiple inclusion, which would fail when already imported targets are added once more.
+set(_cmake_targets_defined "")
+set(_cmake_targets_not_defined "")
+set(_cmake_expected_targets "")
+foreach(_cmake_expected_target IN ITEMS hiredis::hiredis hiredis::hiredis_static)
+  list(APPEND _cmake_expected_targets "${_cmake_expected_target}")
+  if(TARGET "${_cmake_expected_target}")
+    list(APPEND _cmake_targets_defined "${_cmake_expected_target}")
+  else()
+    list(APPEND _cmake_targets_not_defined "${_cmake_expected_target}")
+  endif()
+endforeach()
+unset(_cmake_expected_target)
+if(_cmake_targets_defined STREQUAL _cmake_expected_targets)
+  unset(_cmake_targets_defined)
+  unset(_cmake_targets_not_defined)
+  unset(_cmake_expected_targets)
+  unset(CMAKE_IMPORT_FILE_VERSION)
+  cmake_policy(POP)
+  return()
+endif()
+if(NOT _cmake_targets_defined STREQUAL "")
+  string(REPLACE ";" ", " _cmake_targets_defined_text "${_cmake_targets_defined}")
+  string(REPLACE ";" ", " _cmake_targets_not_defined_text "${_cmake_targets_not_defined}")
+  message(FATAL_ERROR "Some (but not all) targets in this export set were already defined.\nTargets Defined: ${_cmake_targets_defined_text}\nTargets not yet defined: ${_cmake_targets_not_defined_text}\n")
+endif()
+unset(_cmake_targets_defined)
+unset(_cmake_targets_not_defined)
+unset(_cmake_expected_targets)
+
+
+# Compute the installation prefix relative to this file.
+get_filename_component(_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH)
+get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
+get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
+if(_IMPORT_PREFIX STREQUAL "/")
+  set(_IMPORT_PREFIX "")
+endif()
+
+# Create imported target hiredis::hiredis
+add_library(hiredis::hiredis SHARED IMPORTED)
+
+set_target_properties(hiredis::hiredis PROPERTIES
+  INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
+  INTERFACE_LINK_LIBRARIES "ws2_32;crypt32"
+)
+
+# Create imported target hiredis::hiredis_static
+add_library(hiredis::hiredis_static STATIC IMPORTED)
+
+set_target_properties(hiredis::hiredis_static PROPERTIES
+  INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
+  INTERFACE_LINK_LIBRARIES "ws2_32;crypt32"
+)
+
+if(CMAKE_VERSION VERSION_LESS 2.8.12)
+  message(FATAL_ERROR "This file relies on consumers using CMake 2.8.12 or greater.")
+endif()
+
+# Load information for each installed configuration.
+file(GLOB _cmake_config_files "${CMAKE_CURRENT_LIST_DIR}/hiredis-targets-*.cmake")
+foreach(_cmake_config_file IN LISTS _cmake_config_files)
+  include("${_cmake_config_file}")
+endforeach()
+unset(_cmake_config_file)
+unset(_cmake_config_files)
+
+# Cleanup temporary variables.
+set(_IMPORT_PREFIX)
+
+# Loop over all imported files and verify that they actually exist
+foreach(_cmake_target IN LISTS _cmake_import_check_targets)
+  foreach(_cmake_file IN LISTS "_cmake_import_check_files_for_${_cmake_target}")
+    if(NOT EXISTS "${_cmake_file}")
+      message(FATAL_ERROR "The imported target \"${_cmake_target}\" references the file
+   \"${_cmake_file}\"
+but this file does not exist.  Possible reasons include:
+* The file was deleted, renamed, or moved to another location.
+* An install or uninstall procedure did not complete successfully.
+* The installation package was faulty and contained
+   \"${CMAKE_CURRENT_LIST_FILE}\"
+but not all the files it references.
+")
+    endif()
+  endforeach()
+  unset(_cmake_file)
+  unset("_cmake_import_check_files_for_${_cmake_target}")
+endforeach()
+unset(_cmake_target)
+unset(_cmake_import_check_targets)
+
+# This file does not depend on other imported targets which have
+# been exported from the same project but in a separate export set.
+
+# Commands beyond this point should not need to know the version.
+set(CMAKE_IMPORT_FILE_VERSION)
+cmake_policy(POP)