Beispiel #1
0
void delkeysCommand(redisClient *c) {
    dictIterator *di;
    dictEntry *de;
    sds pattern = c->argv[1]->ptr;
    int plen = sdslen(pattern), allkeys;
    unsigned long deleted = 0;

    di = dictGetIterator(c->db->dict);
    allkeys = (pattern[0] == '*' && pattern[1] == '\0');
    while((de = dictNext(di)) != NULL) {
        sds key = dictGetEntryKey(de);
        robj *keyobj;

        if (allkeys || stringmatchlen(pattern,plen,key,sdslen(key),0)) {
            keyobj = createStringObject(key,sdslen(key));
            if (dbDelete(c->db,keyobj)) {
                touchWatchedKey(c->db,keyobj);
                server.dirty++;
                deleted++;
            }
            decrRefCount(keyobj);
        }
    }
    dictReleaseIterator(di);
    addReplyLongLong(c,deleted);
}
Beispiel #2
0
void RLRequest::rl_keys(){

    if(args.size()!=1){
        connection->write_error("ERR wrong number of arguments for 'keys' command");
        return;
    }

    std::vector<std::string> keys;
    size_t arg_len=args[0].size();
    bool allkeys = (arg_len==1 && args[0][0]=='*');
    if(arg_len>0){
        leveldb::Iterator *kit = connection->server->db[connection->db_index]->NewIterator(
            connection->server->read_options);
        leveldb::Slice key;
        kit->SeekToFirst();
        while(kit->Valid()) {
            key = kit->key();
            if(allkeys || stringmatchlen(args[0].c_str(), arg_len, key.data(), key.size(), 0)){
                keys.push_back(key.ToString());
            }
            kit->Next();
        }
        delete kit;
    }else{
        keys.push_back("");
    }

    connection->write_mbulk_header(keys.size());
    //std::for_each(keys.begin(), keys.end(), std::bind1st(std::mem_fun(&RLConnection::write_bulk),connection));
    std::vector<std::string>::iterator it=keys.begin();
    while(it!=keys.end())connection->write_bulk(*it++);
}
Beispiel #3
0
    int Ardb::PublishMessage(Context& ctx, const std::string& channel, const std::string& message)
    {
        ReadLockGuard<SpinRWLock> guard(m_pubsub_ctx_lock);
        PubsubContextTable::iterator fit = m_pubsub_channels.find(channel);

        int receiver = 0;
        if (fit != m_pubsub_channels.end())
        {
            ContextSet::iterator cit = fit->second.begin();
            while (cit != fit->second.end())
            {
                Context* cc = *cit;
                if (NULL != cc && cc->client != NULL)
                {
                    RedisReply* r = NULL;
                    NEW(r, RedisReply);
                    RedisReply& r1 = r->AddMember();
                    RedisReply& r2 = r->AddMember();
                    RedisReply& r3 = r->AddMember();
                    fill_str_reply(r1, "message");
                    fill_str_reply(r2, channel);
                    fill_str_reply(r3, message);
                    cc->client->GetService().AsyncIO(cc->client->GetID(), async_write_message, r);
                    receiver++;
                }
                cit++;
            }
        }
        PubsubContextTable::iterator pit = m_pubsub_patterns.begin();
        while (pit != m_pubsub_patterns.end())
        {
            const std::string& pattern = pit->first;
            if (stringmatchlen(pattern.c_str(), pattern.size(), channel.c_str(), channel.size(), 0))
            {
                ContextSet::iterator cit = pit->second.begin();
                while (cit != pit->second.end())
                {
                    Context* cc = *cit;
                    if (NULL != cc && cc->client != NULL)
                    {
                        RedisReply* r = NULL;
                        NEW(r, RedisReply);
                        RedisReply& r1 = r->AddMember();
                        RedisReply& r2 = r->AddMember();
                        RedisReply& r3 = r->AddMember();
                        RedisReply& r4 = r->AddMember();
                        fill_str_reply(r1, "pmessage");
                        fill_str_reply(r2, pattern);
                        fill_str_reply(r3, channel);
                        fill_str_reply(r4, message);
                        cc->client->GetService().AsyncIO(cc->client->GetID(), async_write_message, r);
                        receiver++;
                    }
                    cit++;
                }
            }
            pit++;
        }
        return receiver;
    }
Beispiel #4
0
void keysCommand(redisClient *c) {
    dictIterator *di;
    dictEntry *de;
    sds pattern = c->argv[1]->ptr;
    int plen = sdslen(pattern), allkeys;
    unsigned long numkeys = 0;
    void *replylen = addDeferredMultiBulkLength(c);

    di = dictGetSafeIterator(c->db->dict);
    allkeys = (pattern[0] == '*' && pattern[1] == '\0');
    while((de = dictNext(di)) != NULL) {
        sds key = dictGetKey(de);
        robj *keyobj;

        if (allkeys || stringmatchlen(pattern,plen,key,sdslen(key),0)) {
            keyobj = createStringObject(key,sdslen(key));
            if (expireIfNeeded(c->db,keyobj) == 0) {
                addReplyBulk(c,keyobj);
                numkeys++;
            }
            decrRefCount(keyobj);
        }
    }
    dictReleaseIterator(di);
    setDeferredMultiBulkLength(c,replylen,numkeys);
}
Beispiel #5
0
void keysCommand(redisClient *c) {
    dictIterator *di;
    dictEntry *de;
    sds pattern = c->argv[1]->ptr;
    int plen = sdslen(pattern);
    unsigned long numkeys = 0;
    robj *lenobj = createObject(REDIS_STRING,NULL);

    di = dictGetIterator(c->db->dict);
    addReply(c,lenobj);
    decrRefCount(lenobj);
    while((de = dictNext(di)) != NULL) {
        sds key = dictGetEntryKey(de);
        robj *keyobj;

        if ((pattern[0] == '*' && pattern[1] == '\0') ||
            stringmatchlen(pattern,plen,key,sdslen(key),0)) {
            keyobj = createStringObject(key,sdslen(key));
            if (expireIfNeeded(c->db,keyobj) == 0) {
                addReplyBulk(c,keyobj);
                numkeys++;
            }
            decrRefCount(keyobj);
        }
    }
    dictReleaseIterator(di);
    lenobj->ptr = sdscatprintf(sdsempty(),"*%lu\r\n",numkeys);
}
/*	发布一则消息,即将消息发送给所有订阅了指定频道channel的所有客户端以及所有订阅了和指定频道匹配的模式的客户端。*/
int pubsubPublishMessage(robj *channel, robj *message) {
    int receivers = 0;
    dictEntry *de;
    listNode *ln;
    listIter li;

    /* Send to clients listening for that channel */
    // 取出订阅指定频道的客户端链表
    de = dictFind(server.pubsub_channels,channel);
    if (de) {
        list *list = dictGetVal(de);
        listNode *ln;
        listIter li;

        listRewind(list,&li);
        // 遍历该客户端链表,并将消息逐一发送给这些客户端
        while ((ln = listNext(&li)) != NULL) {
            redisClient *c = ln->value;

            // 发送消息
            addReply(c,shared.mbulkhdr[3]);
            // 回复“message”字符串
            addReply(c,shared.messagebulk);
            // 回复消息的来源频道
            addReplyBulk(c,channel);
            // 回复消息内容
            addReplyBulk(c,message);
            // 统计接收客户端的数量
            receivers++;
        }
    }
    /* Send to clients listening to matching channels */
    // 将消息发送个订阅了和指定频道匹配的模式的客户端
    if (listLength(server.pubsub_patterns)) {
        listRewind(server.pubsub_patterns,&li);
        channel = getDecodedObject(channel);
        // 遍历server.pubsub_patterns
        while ((ln = listNext(&li)) != NULL) {
        	// 取出当前模式
            pubsubPattern *pat = ln->value;

            // 判断当前模式是否和给定频道相匹配
            if (stringmatchlen((char*)pat->pattern->ptr,
                                sdslen(pat->pattern->ptr),
                                (char*)channel->ptr,
                                sdslen(channel->ptr),0)) {
            	// 发送消息
                addReply(pat->client,shared.mbulkhdr[4]);
                addReply(pat->client,shared.pmessagebulk);
                addReplyBulk(pat->client,pat->pattern);
                addReplyBulk(pat->client,channel);
                addReplyBulk(pat->client,message);
                receivers++;
            }
        }
        decrRefCount(channel);
    }
    return receivers;
}
/* PUBSUB command for Pub/Sub introspection. */
void pubsubCommand(redisClient *c) {

	// 处理PUBSUB CHANNELS [pattern]命令
    if (!strcasecmp(c->argv[1]->ptr,"channels") &&
        (c->argc == 2 || c->argc ==3))
    {
        /* PUBSUB CHANNELS [<pattern>] */
        // 获取pattern参数,如果没有则为NULL
        sds pat = (c->argc == 2) ? NULL : c->argv[2]->ptr;
        dictIterator *di = dictGetIterator(server.pubsub_channels);
        dictEntry *de;
        long mblen = 0;
        void *replylen;

        replylen = addDeferredMultiBulkLength(c);
        // 遍历server.pubsub_channels字典
        while((de = dictNext(di)) != NULL) {
        	// 取出当前频道channel
            robj *cobj = dictGetKey(de);
            sds channel = cobj->ptr;

            // 如果没有给定pattern参数,则打印出所有频道
            // 如果给定pattern参数,则打印出与pattern参数相匹配的频道
            if (!pat || stringmatchlen(pat, sdslen(pat),
                                       channel, sdslen(channel),0))
            {
                addReplyBulk(c,cobj);
                mblen++;
            }
        }
        dictReleaseIterator(di);
        setDeferredMultiBulkLength(c,replylen,mblen);
    } 
    // 处理PUBSUB NUMSUB [Channel_1 ... Channel_N]命令
    else if (!strcasecmp(c->argv[1]->ptr,"numsub") && c->argc >= 2) {
        /* PUBSUB NUMSUB [Channel_1 ... Channel_N] */
        int j;

        addReplyMultiBulkLen(c,(c->argc-2)*2);
        for (j = 2; j < c->argc; j++) {
            list *l = dictFetchValue(server.pubsub_channels,c->argv[j]);

            addReplyBulk(c,c->argv[j]);
            addReplyLongLong(c,l ? listLength(l) : 0);
        }
    } 
    // 处理PUBSUB NUMPA命令
    else if (!strcasecmp(c->argv[1]->ptr,"numpat") && c->argc == 2) {
        /* PUBSUB NUMPAT */
        addReplyLongLong(c,listLength(server.pubsub_patterns));
    } else {
        addReplyErrorFormat(c,
            "Unknown PUBSUB subcommand or wrong number of arguments for '%s'",
            (char*)c->argv[1]->ptr);
    }
}
Beispiel #8
0
void movekeysCommand(redisClient *c) {
    redisDb *src, *dst;
    int srcid;

    dictIterator *di;
    dictEntry *de;
    sds pattern = c->argv[1]->ptr;
    int plen = sdslen(pattern), allkeys;
    unsigned long numkeys = 0;

    /* Obtain source and target DB pointers */
    src = c->db;
    srcid = c->db->id;
    if (selectDb(c,atoi(c->argv[2]->ptr)) == REDIS_ERR) {
        addReply(c,shared.outofrangeerr);
        return;
    }
    dst = c->db;
    selectDb(c,srcid); /* Back to the source DB */

    /* If the user is moving using as target the same
     * DB as the source DB it is probably an error. */
    if (src == dst) {
        addReply(c,shared.sameobjecterr);
        return;
    }

    di = dictGetIterator(c->db->dict);
    allkeys = (pattern[0] == '*' && pattern[1] == '\0');
    while((de = dictNext(di)) != NULL) {
        sds key = dictGetEntryKey(de);
        robj *keyobj;

        if (allkeys || stringmatchlen(pattern,plen,key,sdslen(key),0)) {
            keyobj = createStringObject(key,sdslen(key));
            if (expireIfNeeded(c->db,keyobj) == 0) {
                robj *val = dictGetEntryVal(de);

                /* Try to add the element to the target DB */
                if (dbAdd(dst,keyobj,val) != REDIS_ERR) {
                    incrRefCount(val);

                    /* OK! key moved, free the entry in the source DB */
                    dbDelete(src,keyobj);
                    server.dirty++;
                    numkeys++;
                }
            }
            decrRefCount(keyobj);
        }
    }
    dictReleaseIterator(di);
    addReplyLongLong(c,numkeys);
}
Beispiel #9
0
 int Ardb::MatchValueByPattern(Context& ctx, const Slice& key_pattern, const Slice& value_pattern, Data& subst,
         Data& value)
 {
     if (0 != GetValueByPattern(ctx, key_pattern, subst, value))
     {
         return -1;
     }
     std::string str;
     subst.GetDecodeString(str);
     std::string vpattern(value_pattern.data(), value_pattern.size());
     return stringmatchlen(vpattern.c_str(), vpattern.size(), str.c_str(),str.size(), 0) == 1 ? 0 : -1;
 }
Beispiel #10
0
/*
 * 发布一个消息至指定频道
 */
int pubsubPublishMessage(robj *channel, robj *message) {
    int receivers = 0;
    dictEntry *de;
    listNode *ln;
    listIter li;

    /* Send to clients listening for that channel */
    /* 发布给监听指定频道的客户端 */
    de = dictFind(server.pubsub_channels,channel);
    if (de) {
        list *list = dictGetVal(de);
        listNode *ln;
        listIter li;

        listRewind(list,&li);
        while ((ln = listNext(&li)) != NULL) {
            redisClient *c = ln->value;

            addReply(c,shared.mbulkhdr[3]);
            addReply(c,shared.messagebulk);
            addReplyBulk(c,channel);
            addReplyBulk(c,message);
            receivers++;
        }
    }
    /* Send to clients listening to matching channels */
    /* 发布给监听模式匹配的客户端 */
    if (listLength(server.pubsub_patterns)) {
        listRewind(server.pubsub_patterns,&li);
        channel = getDecodedObject(channel);
        while ((ln = listNext(&li)) != NULL) {
            pubsubPattern *pat = ln->value;

            if (stringmatchlen((char*)pat->pattern->ptr,
                                sdslen(pat->pattern->ptr),
                                (char*)channel->ptr,
                                sdslen(channel->ptr),0)) {
                addReply(pat->client,shared.mbulkhdr[4]);
                addReply(pat->client,shared.pmessagebulk);
                addReplyBulk(pat->client,pat->pattern);
                addReplyBulk(pat->client,channel);
                addReplyBulk(pat->client,message);
                receivers++;
            }
        }
        decrRefCount(channel);
    }
    return receivers;
}
Beispiel #11
0
void expirekeysGenericCommand(redisClient *c, robj *keypattern, robj *param, long offset) {
    dictIterator *di;
    dictEntry *de;
    sds pattern = keypattern->ptr;
    int plen = sdslen(pattern);
    unsigned long numkeys = 0, allkeys;
    time_t seconds;
    time_t when;

    if (getLongFromObjectOrReply(c, param, &seconds, NULL) != REDIS_OK) return;

    seconds -= offset;
    when = time(NULL)+seconds;

    di = dictGetIterator(c->db->dict);
    allkeys = (pattern[0] == '*' && pattern[1] == '\0');
    while((de = dictNext(di)) != NULL) {
        sds key = dictGetEntryKey(de);
        robj *keyobj;

        if (allkeys || stringmatchlen(pattern,plen,key,sdslen(key),0)) {
            keyobj = createStringObject(key,sdslen(key));
            if (seconds <= 0 && !server.loading && !server.masterhost) {
                robj *aux;

                redisAssert(dbDelete(c->db,keyobj));
                server.dirty++;
                numkeys++;

                /* Replicate/AOF this as an explicit DEL. */
                aux = createStringObject("DEL",3);
                rewriteClientCommandVector(c,2,aux,keyobj);
                decrRefCount(aux);
                touchWatchedKey(c->db,keyobj);
            } else {
                time_t when = time(NULL)+seconds;
                setExpire(c->db,keyobj,when);
                touchWatchedKey(c->db,keyobj);
                server.dirty++;
                numkeys++;
            }
            decrRefCount(keyobj);
        }
    }
    dictReleaseIterator(di);
    addReplyLongLong(c,numkeys);
}
Beispiel #12
0
/*
 * PUBSUB 命令, 内省命令
 */
void pubsubCommand(redisClient *c) {
    if (!strcasecmp(c->argv[1]->ptr,"channels") &&
        (c->argc == 2 || c->argc ==3))              //列出当前活跃的频道,每个频道至少有一个订阅者,订阅模式的客户端不计算在内
    {
        /* PUBSUB CHANNELS [<pattern>] */
        sds pat = (c->argc == 2) ? NULL : c->argv[2]->ptr;
        dictIterator *di = dictGetIterator(server.pubsub_channels);
        dictEntry *de;
        long mblen = 0;
        void *replylen;

        replylen = addDeferredMultiBulkLength(c);
        while((de = dictNext(di)) != NULL) {
            robj *cobj = dictGetKey(de);
            sds channel = cobj->ptr;

            if (!pat || stringmatchlen(pat, sdslen(pat),
                                       channel, sdslen(channel),0))
            {
                addReplyBulk(c,cobj);
                mblen++;
            }
        }
        dictReleaseIterator(di);
        setDeferredMultiBulkLength(c,replylen,mblen);
    } else if (!strcasecmp(c->argv[1]->ptr,"numsub") && c->argc >= 2) {     //返回给定频道的订阅者数量,订阅模式的客户端不计算在内
        /* PUBSUB NUMSUB [Channel_1 ... Channel_N] */
        int j;

        addReplyMultiBulkLen(c,(c->argc-2)*2);
        for (j = 2; j < c->argc; j++) {
            list *l = dictFetchValue(server.pubsub_channels,c->argv[j]);

            addReplyBulk(c,c->argv[j]);
            addReplyLongLong(c,l ? listLength(l) : 0);
        }
    } else if (!strcasecmp(c->argv[1]->ptr,"numpat") && c->argc == 2) {     //返回订阅模式的数量
        /* PUBSUB NUMPAT */
        addReplyLongLong(c,listLength(server.pubsub_patterns));
    } else {
        addReplyErrorFormat(c,
            "Unknown PUBSUB subcommand or wrong number of arguments for '%s'",
            (char*)c->argv[1]->ptr);
    }
}
Beispiel #13
0
 int64_t MMKVImpl::SScan(DBID db, const Data& key, int64_t cursor, const std::string& pattern, int32_t limit_count,
         const StringArrayResult& results)
 {
     int err = 0;
     RWLockGuard<MemorySegmentManager, READ_LOCK> keylock_guard(m_segment);
     StringSet* set = GetObject<StringSet>(db, key, V_TYPE_SET, false, err)();
     if (NULL == set || 0 != err)
     {
         return err;
     }
     StringSet::iterator it = set->begin();
     if (cursor >= set->size())
     {
         return 0;
     }
     it.increment_by(cursor);
     int match_count = 0;
     while (it != set->end())
     {
         std::string key_str;
         it->ToString(key_str);
         if (pattern == ""
                 || stringmatchlen(pattern.c_str(), pattern.size(), key_str.c_str(), key_str.size(), 0) == 1)
         {
             std::string& ss = results.Get();
             ss = key_str;
             match_count++;
             if (limit_count > 0 && match_count >= limit_count)
             {
                 break;
             }
         }
         cursor++;
         it++;
     }
     return it != set->end() ? cursor : 0;
 }
Beispiel #14
0
void countCommand(redisClient *c) {
    dictIterator *di;
    dictEntry *de;
    sds pattern = c->argv[1]->ptr;
    int plen = sdslen(pattern), allkeys;
    unsigned long numkeys = 0;

    di = dictGetIterator(c->db->dict);
    allkeys = (pattern[0] == '*' && pattern[1] == '\0');
    while((de = dictNext(di)) != NULL) {
        sds key = dictGetEntryKey(de);
        robj *keyobj;

        if (allkeys || stringmatchlen(pattern,plen,key,sdslen(key),0)) {
            keyobj = createStringObject(key,sdslen(key));
            if (expireIfNeeded(c->db,keyobj) == 0) {
                numkeys++;
            }
            decrRefCount(keyobj);
        }
    }
    dictReleaseIterator(di);
    addReplyLongLong(c,numkeys);
}
Beispiel #15
0
    int Ardb::Scan(Context& ctx, RedisCommandFrame& cmd)
    {
        StringArray keys;
        std::string pattern;
        uint32 limit = 10000; //return max 10000 keys one time
        if (cmd.GetArguments().size() > 1)
        {
            for (uint32 i = 1; i < cmd.GetArguments().size(); i++)
            {
                if (!strcasecmp(cmd.GetArguments()[i].c_str(), "count"))
                {
                    if (i + 1 >= cmd.GetArguments().size() || !string_touint32(cmd.GetArguments()[i + 1], limit))
                    {
                        fill_error_reply(ctx.reply, "value is not an integer or out of range");
                        return 0;
                    }
                    i++;
                }
                else if (!strcasecmp(cmd.GetArguments()[i].c_str(), "match"))
                {
                    if (i + 1 >= cmd.GetArguments().size())
                    {
                        fill_error_reply(ctx.reply, "'MATCH' need one args followed");
                        return 0;
                    }
                    pattern = cmd.GetArguments()[i + 1];
                    i++;
                }
                else
                {
                    fill_error_reply(ctx.reply, "Syntax error, try scan 0 ");
                    return 0;
                }
            }
        }
        RedisReply& r1 = ctx.reply.AddMember();
        RedisReply& r2 = ctx.reply.AddMember();
        r2.type = REDIS_REPLY_ARRAY;

        KeyObject from;
        from.db = ctx.currentDB;
        from.type = KEY_META;
        const std::string& cursor = cmd.GetArguments()[0];
        std::string scan_start_cursor;
        if (cmd.GetArguments()[0] != "0")
        {
            if (m_cfg.scan_redis_compatible)
            {
                FindElementByRedisCursor(cursor, scan_start_cursor);
            }
            from.key = scan_start_cursor;
        }
        Iterator* iter = IteratorKeyValue(from, false);
        bool reachend = false;
        std::string tmpkey;
        while (NULL != iter && iter->Valid())
        {
            KeyObject kk;
            if (!decode_key(iter->Key(), kk) || kk.db != ctx.currentDB || kk.type != KEY_META)
            {
                reachend = true;
                break;
            }
            tmpkey.clear();
            tmpkey.assign(kk.key.data(), kk.key.size());
            if (r2.MemberSize() >= limit)
            {
                break;
            }
            if ((pattern.empty()
                    || stringmatchlen(pattern.c_str(), pattern.size(), tmpkey.c_str(), tmpkey.size(), 0) == 1))
            {
                RedisReply& rr1 = r2.AddMember();
                fill_str_reply(rr1, tmpkey);
            }
            iter->Next();
        }
        if (reachend || !iter->Valid())
        {
            fill_str_reply(r1, "0");
        }
        else
        {
            if (m_cfg.scan_redis_compatible)
            {
                uint64 newcursor = GetNewRedisCursor(tmpkey);
                fill_str_reply(r1, stringfromll(newcursor));
            }
            else
            {
                fill_str_reply(r1, tmpkey);
            }
        }
        DELETE(iter);
        return 0;
    }
Beispiel #16
0
    int Ardb::KeysOperation(Context& ctx, const KeysOptions& options)
    {
        KeyObject from;
        from.db = ctx.currentDB;
        from.type = KEY_META;

        std::string::size_type cursor = options.pattern.find("*");
        if (cursor == std::string::npos)
        {
            from.key = options.pattern;
        }
        else
        {
            if (options.pattern != "*" && cursor > 0)
            {
                from.key = options.pattern.substr(0, cursor);
            }
        }
        Iterator* iter = IteratorKeyValue(from, false);
        std::string tmpkey;
        uint32 count = 0;
        while (NULL != iter && iter->Valid())
        {
            KeyObject kk;
            if (!decode_key(iter->Key(), kk) || kk.db != ctx.currentDB || kk.type != KEY_META)
            {
                break;
            }
            tmpkey.clear();
            tmpkey.assign(kk.key.data(), kk.key.size());
            if ((options.pattern == "*"
                    || stringmatchlen(options.pattern.c_str(), options.pattern.size(), tmpkey.data(), tmpkey.size(), 0)
                            == 1))
            {
                if (options.op == OP_GET)
                {
                    RedisReply& r = ctx.reply.AddMember();
                    fill_str_reply(r, tmpkey);
                }
                else
                {
                    count++;
                }
            }
            else
            {
                /*
                 * If the '*' is the last char, we can break iteration now
                 */
                if (cursor == options.pattern.size() - 1)
                {
                    break;
                }
            }
            iter->Next();
        }
        DELETE(iter);
        if (options.op == OP_GET)
        {
            ctx.reply.type = REDIS_REPLY_ARRAY;
        }
        else
        {
            fill_int_reply(ctx.reply, count);
        }
        return 0;
    }
Beispiel #17
0
/*支持glob-style的通配符格式,如*表示任意一个或多个字符,?表示任意字符,[abc]表示方括号中任意一个字母。*/
int stringmatchlen(const char *pattern, int patternLen,
        const char *string, int stringLen, int nocase)
{
    while(patternLen) {
        switch(pattern[0]) {
        case '*':
            while (pattern[1] == '*') {
            	//如果出现的是**,说明一定匹配
                pattern++;
                patternLen--;
            }
            if (patternLen == 1)
                return 1; /* match */
            while(stringLen) {
                if (stringmatchlen(pattern+1, patternLen-1,
                            string, stringLen, nocase))
                    return 1; /* match */
                string++;
                stringLen--;
            }
            return 0; /* no match */
            break;
        case '?':
            if (stringLen == 0)
                return 0; /* no match */
            /* 因为?能代表任何字符,所以,匹配的字符再往后挪一个字符 */
            string++;
            stringLen--;
            break;
        case '[':
        {
            int not, match;

            pattern++;
            patternLen--;
            not = pattern[0] == '^';
            if (not) {
                pattern++;
                patternLen--;
            }
            match = 0;
            while(1) {
                if (pattern[0] == '\\') {
                	//如果遇到转义符,则模式字符往后移一个位置
                    pattern++;
                    patternLen--;
                    if (pattern[0] == string[0])
                        match = 1;
                } else if (pattern[0] == ']') {
                	//直到遇到另外一个我中括号,则停止
                    break;
                } else if (patternLen == 0) {
                    pattern--;
                    patternLen++;
                    break;
                } else if (pattern[1] == '-' && patternLen >= 3) {
                    int start = pattern[0];
                    int end = pattern[2];
                    int c = string[0];
                    if (start > end) {
                        int t = start;
                        start = end;
                        end = t;
                    }
                    if (nocase) {
                        start = tolower(start);
                        end = tolower(end);
                        c = tolower(c);
                    }
                    pattern += 2;
                    patternLen -= 2;
                    if (c >= start && c <= end)
                        match = 1;
                } else {
                    if (!nocase) {
                        if (pattern[0] == string[0])
                            match = 1;
                    } else {
                        if (tolower((int)pattern[0]) == tolower((int)string[0]))
                            match = 1;
                    }
                }
                pattern++;
                patternLen--;
            }
            if (not)
                match = !match;
            if (!match)
                return 0; /* no match */
            string++;
            stringLen--;
            break;
        }
        case '\\':
            if (patternLen >= 2) {
                pattern++;
                patternLen--;
            }
            /* fall through */
        default:
        	/* 如果没有正则表达式的关键字符,则直接比较 */
            if (!nocase) {
                if (pattern[0] != string[0])
                	//不相等,直接不匹配
                    return 0; /* no match */
            } else {
                if (tolower((int)pattern[0]) != tolower((int)string[0]))
                    return 0; /* no match */
            }
            string++;
            stringLen--;
            break;
        }
        pattern++;
        patternLen--;
        if (stringLen == 0) {
            while(*pattern == '*') {
                pattern++;
                patternLen--;
            }
            break;
        }
    }
    if (patternLen == 0 && stringLen == 0)
    	//如果匹配字符和模式字符匹配的长度都减少到0了,说明匹配成功了
        return 1;
    return 0;
}
Beispiel #18
0
/* This command implements SCAN, HSCAN and SSCAN commands.
 * If object 'o' is passed, then it must be a Hash or Set object, otherwise
 * if 'o' is NULL the command will operate on the dictionary associated with
 * the current database.
 *
 * When 'o' is not NULL the function assumes that the first argument in
 * the client arguments vector is a key so it skips it before iterating
 * in order to parse options.
 *
 * In the case of a Hash object the function returns both the field and value
 * of every element on the Hash. */
void scanGenericCommand(client *c, robj *o, unsigned long cursor) {
    int i, j;
    list *keys = listCreate();
    listNode *node, *nextnode;
    long count = 10;
    sds pat = NULL;
    int patlen = 0, use_pattern = 0;
    dict *ht;

    /* Object must be NULL (to iterate keys names), or the type of the object
     * must be Set, Sorted Set, or Hash. */
    serverAssert(o == NULL || o->type == OBJ_SET || o->type == OBJ_HASH ||
                o->type == OBJ_ZSET);

    /* Set i to the first option argument. The previous one is the cursor. */
    i = (o == NULL) ? 2 : 3; /* Skip the key argument if needed. */

    /* Step 1: Parse options. */
    while (i < c->argc) {
        j = c->argc - i;
        if (!strcasecmp(c->argv[i]->ptr, "count") && j >= 2) {
            if (getLongFromObjectOrReply(c, c->argv[i+1], &count, NULL)
                != C_OK)
            {
                goto cleanup;
            }

            if (count < 1) {
                addReply(c,shared.syntaxerr);
                goto cleanup;
            }

            i += 2;
        } else if (!strcasecmp(c->argv[i]->ptr, "match") && j >= 2) {
            pat = c->argv[i+1]->ptr;
            patlen = sdslen(pat);

            /* The pattern always matches if it is exactly "*", so it is
             * equivalent to disabling it. */
            use_pattern = !(pat[0] == '*' && patlen == 1);

            i += 2;
        } else {
            addReply(c,shared.syntaxerr);
            goto cleanup;
        }
    }

    /* Step 2: Iterate the collection.
     *
     * Note that if the object is encoded with a ziplist, intset, or any other
     * representation that is not a hash table, we are sure that it is also
     * composed of a small number of elements. So to avoid taking state we
     * just return everything inside the object in a single call, setting the
     * cursor to zero to signal the end of the iteration. */

    /* Handle the case of a hash table. */
    ht = NULL;
    if (o == NULL) {
        ht = c->db->dict;
    } else if (o->type == OBJ_SET && o->encoding == OBJ_ENCODING_HT) {
        ht = o->ptr;
    } else if (o->type == OBJ_HASH && o->encoding == OBJ_ENCODING_HT) {
        ht = o->ptr;
        count *= 2; /* We return key / value for this type. */
    } else if (o->type == OBJ_ZSET && o->encoding == OBJ_ENCODING_SKIPLIST) {
        zset *zs = o->ptr;
        ht = zs->dict;
        count *= 2; /* We return key / value for this type. */
    }

    if (ht) {
        void *privdata[2];
        /* We set the max number of iterations to ten times the specified
         * COUNT, so if the hash table is in a pathological state (very
         * sparsely populated) we avoid to block too much time at the cost
         * of returning no or very few elements. */
        long maxiterations = count*10;

        /* We pass two pointers to the callback: the list to which it will
         * add new elements, and the object containing the dictionary so that
         * it is possible to fetch more data in a type-dependent way. */
        privdata[0] = keys;
        privdata[1] = o;
        do {
            cursor = dictScan(ht, cursor, scanCallback, privdata);
        } while (cursor &&
              maxiterations-- &&
              listLength(keys) < (unsigned long)count);
    } else if (o->type == OBJ_SET) {
        int pos = 0;
        int64_t ll;

        while(intsetGet(o->ptr,pos++,&ll))
            listAddNodeTail(keys,createStringObjectFromLongLong(ll));
        cursor = 0;
    } else if (o->type == OBJ_HASH || o->type == OBJ_ZSET) {
        unsigned char *p = ziplistIndex(o->ptr,0);
        unsigned char *vstr;
        unsigned int vlen;
        long long vll;

        while(p) {
            ziplistGet(p,&vstr,&vlen,&vll);
            listAddNodeTail(keys,
                (vstr != NULL) ? createStringObject((char*)vstr,vlen) :
                                 createStringObjectFromLongLong(vll));
            p = ziplistNext(o->ptr,p);
        }
        cursor = 0;
    } else {
        serverPanic("Not handled encoding in SCAN.");
    }

    /* Step 3: Filter elements. */
    node = listFirst(keys);
    while (node) {
        robj *kobj = listNodeValue(node);
        nextnode = listNextNode(node);
        int filter = 0;

        /* Filter element if it does not match the pattern. */
        if (!filter && use_pattern) {
            if (sdsEncodedObject(kobj)) {
                if (!stringmatchlen(pat, patlen, kobj->ptr, sdslen(kobj->ptr), 0))
                    filter = 1;
            } else {
                char buf[LONG_STR_SIZE];
                int len;

                serverAssert(kobj->encoding == OBJ_ENCODING_INT);
                len = ll2string(buf,sizeof(buf),(long)kobj->ptr);
                if (!stringmatchlen(pat, patlen, buf, len, 0)) filter = 1;
            }
        }

        /* Filter element if it is an expired key. */
        if (!filter && o == NULL && expireIfNeeded(c->db, kobj)) filter = 1;

        /* Remove the element and its associted value if needed. */
        if (filter) {
            decrRefCount(kobj);
            listDelNode(keys, node);
        }

        /* If this is a hash or a sorted set, we have a flat list of
         * key-value elements, so if this element was filtered, remove the
         * value, or skip it if it was not filtered: we only match keys. */
        if (o && (o->type == OBJ_ZSET || o->type == OBJ_HASH)) {
            node = nextnode;
            nextnode = listNextNode(node);
            if (filter) {
                kobj = listNodeValue(node);
                decrRefCount(kobj);
                listDelNode(keys, node);
            }
        }
        node = nextnode;
    }

    /* Step 4: Reply to the client. */
    addReplyMultiBulkLen(c, 2);
    addReplyBulkLongLong(c,cursor);

    addReplyMultiBulkLen(c, listLength(keys));
    while ((node = listFirst(keys)) != NULL) {
        robj *kobj = listNodeValue(node);
        addReplyBulk(c, kobj);
        decrRefCount(kobj);
        listDelNode(keys, node);
    }

cleanup:
    listSetFreeMethod(keys,decrRefCountVoid);
    listRelease(keys);
}
Beispiel #19
0
int stringmatch(const char *pattern, const char *string, int nocase) {
    return stringmatchlen(pattern,(int)strlen(pattern),string,(int)strlen(string),nocase); WIN_PORT_FIX /* cast (int) */
}
/* PUBSUB command for Pub/Sub introspection. */
void pubsubCommand(redisClient *c) {

    // PUBSUB CHANNELS [pattern] 子命令
    if (!strcasecmp(c->argv[1]->ptr,"channels") &&
        (c->argc == 2 || c->argc ==3))
    {
        /* PUBSUB CHANNELS [<pattern>] */
        // 检查命令请求是否给定了 pattern 参数
        // 如果没有给定的话,就设为 NULL
        sds pat = (c->argc == 2) ? NULL : c->argv[2]->ptr;

        // 创建 pubsub_channels 的字典迭代器
        // 该字典的键为频道,值为链表
        // 链表中保存了所有订阅键所对应的频道的客户端
        dictIterator *di = dictGetIterator(server.pubsub_channels);
        dictEntry *de;
        long mblen = 0;
        void *replylen;

        replylen = addDeferredMultiBulkLength(c);
        // 从迭代器中获取一个客户端
        while((de = dictNext(di)) != NULL) {

            // 从字典中取出客户端所订阅的频道
            robj *cobj = dictGetKey(de);
            sds channel = cobj->ptr;

            // 顺带一提
            // 因为 Redis 的字典实现只能遍历字典的值(客户端)
            // 所以这里才会有遍历字典值然后通过字典值取出字典键(频道)的蹩脚用法

            // 如果没有给定 pattern 参数,那么打印所有找到的频道
            // 如果给定了 pattern 参数,那么只打印和 pattern 相匹配的频道
            if (!pat || stringmatchlen(pat, sdslen(pat),
                                       channel, sdslen(channel),0))
            {
                // 向客户端输出频道
                addReplyBulk(c,cobj);
                mblen++;
            }
        }
        // 释放字典迭代器
        dictReleaseIterator(di);
        setDeferredMultiBulkLength(c,replylen,mblen);

    // PUBSUB NUMSUB [channel-1 channel-2 ... channel-N] 子命令
    } else if (!strcasecmp(c->argv[1]->ptr,"numsub") && c->argc >= 2) {
        /* PUBSUB NUMSUB [Channel_1 ... Channel_N] */
        int j;

        addReplyMultiBulkLen(c,(c->argc-2)*2);
        for (j = 2; j < c->argc; j++) {

            // c->argv[j] 也即是客户端输入的第 N 个频道名字
            // pubsub_channels 的字典为频道名字
            // 而值则是保存了 c->argv[j] 频道所有订阅者的链表
            // 而调用 dictFetchValue 也就是取出所有订阅给定频道的客户端
            list *l = dictFetchValue(server.pubsub_channels,c->argv[j]);

            addReplyBulk(c,c->argv[j]);
            // 向客户端返回链表的长度属性
            // 这个属性就是某个频道的订阅者数量
            // 例如:如果一个频道有三个订阅者,那么链表的长度就是 3
            // 而返回给客户端的数字也是三
            addReplyBulkLongLong(c,l ? listLength(l) : 0);
        }

    // PUBSUB NUMPAT 子命令
    } else if (!strcasecmp(c->argv[1]->ptr,"numpat") && c->argc == 2) {
        /* PUBSUB NUMPAT */

        // pubsub_patterns 链表保存了服务器中所有被订阅的模式
        // pubsub_patterns 的长度就是服务器中被订阅模式的数量
        addReplyLongLong(c,listLength(server.pubsub_patterns));

    // 错误处理
    } else {
        addReplyErrorFormat(c,
            "Unknown PUBSUB subcommand or wrong number of arguments for '%s'",
            (char*)c->argv[1]->ptr);
    }
}
/* Publish a message 
 *
 * 将 message 发送到所有订阅频道 channel 的客户端,
 * 以及所有订阅了和 channel 频道匹配的模式的客户端。

 pubsubPublishMessage函数是PUBLISH命令的实现函数,执行这个函数
等同于执行PUBLISH命令,订阅数据库通知的客户端收到的信息就是由这个函数发出的
 */
int pubsubPublishMessage(robj *channel, robj *message) {
    int receivers = 0;
    dictEntry *de;
    listNode *ln;
    listIter li;

    /* Send to clients listening for that channel */
    // 取出包含所有订阅频道 channel 的客户端的链表
    // 并将消息发送给它们
    de = dictFind(server.pubsub_channels,channel);
    if (de) {
        list *list = dictGetVal(de);
        listNode *ln;
        listIter li;

        // 遍历客户端链表,将 message 发送给它们
        listRewind(list,&li);
        while ((ln = listNext(&li)) != NULL) {
            redisClient *c = ln->value;

            // 回复客户端。
            // 示例:
            // 1) "message"
            // 2) "xxx"
            // 3) "hello"
            addReply(c,shared.mbulkhdr[3]);
            // "message" 字符串
            addReply(c,shared.messagebulk);
            // 消息的来源频道
            addReplyBulk(c,channel);
            // 消息内容
            addReplyBulk(c,message);

            // 接收客户端计数
            receivers++;
        }
    }

    /* Send to clients listening to matching channels */
    // 将消息也发送给那些和频道匹配的模式
    if (listLength(server.pubsub_patterns)) {

        // 遍历模式链表
        listRewind(server.pubsub_patterns,&li);
        channel = getDecodedObject(channel);
        while ((ln = listNext(&li)) != NULL) {

            // 取出 pubsubPattern
            pubsubPattern *pat = ln->value;

            // 如果 channel 和 pattern 匹配
            // 就给所有订阅该 pattern 的客户端发送消息
            if (stringmatchlen((char*)pat->pattern->ptr,
                                sdslen(pat->pattern->ptr),
                                (char*)channel->ptr,
                                sdslen(channel->ptr),0)) {

                // 回复客户端
                // 示例:
                // 1) "pmessage"
                // 2) "*"
                // 3) "xxx"
                // 4) "hello"
                addReply(pat->client,shared.mbulkhdr[4]);
                addReply(pat->client,shared.pmessagebulk);
                addReplyBulk(pat->client,pat->pattern);
                addReplyBulk(pat->client,channel);
                addReplyBulk(pat->client,message);

                // 对接收消息的客户端进行计数
                receivers++;
            }
        }

        decrRefCount(channel);
    }

    // 返回计数
    return receivers;
}
Beispiel #22
0
//检测string是不是符合pattern的模式(glob-style)
int stringmatchlen(const char *pattern, int patternLen,
        const char *string, int stringLen, int nocase)
{
    while(patternLen) {
        switch(pattern[0]) {
        case '*':
            while (pattern[1] == '*') {
                //多个*等同与一个*
            	pattern++;
                patternLen--;
            }
            if (patternLen == 1)
                return 1; /* match */
            while(stringLen) {
            	//先忽略pattern的*,看string是否匹配后面的pattern,即pattern+1
                if (stringmatchlen(pattern+1, patternLen-1,
                            string, stringLen, nocase))
                    return 1; /* match */

                //*匹配string的一个字符
                string++;
                stringLen--;
            }

            //到了这里说明没有匹配,否则应该在上面的递归调用返回
            return 0; /* no match */
            break;
        case '?':
            if (stringLen == 0)
                return 0; /* no match */

            //匹配任何字符,因此跳过当前字符
            string++;
            stringLen--;
            break;
        case '[':
        //[xxx]这种情况
        {
            int not, match;

            pattern++;
            patternLen--;
            not = pattern[0] == '^';
            if (not) {
            	//[^xxx]这种情况
                pattern++;
                patternLen--;
            }
            match = 0;
            while(1) {
                if (pattern[0] == '\\') {
                	//跳过当前"\"字符
                    pattern++;
                    patternLen--;
                    //下一个字符匹配,则将match设为1。即模式中的'\x' 匹配string中的'x',x本身在模式中有特别含义故需转义
                    if (pattern[0] == string[0])
                        match = 1;
                } else if (pattern[0] == ']') {
                	//到了右括号,跳出while
                    break;
                } else if (patternLen == 0) {
                    pattern--;
                    patternLen++;
                    break;
                } else if (pattern[1] == '-' && patternLen >= 3) {
                	//[a-z]这种情况
                    int start = pattern[0];
                    int end = pattern[2];
                    int c = string[0];
                    if (start > end) {
                        int t = start;
                        start = end;
                        end = t;
                    }
                    if (nocase) {
                        start = tolower(start);
                        end = tolower(end);
                        c = tolower(c);
                    }
                    pattern += 2;
                    patternLen -= 2;
                    //c在范围内,将match设为1
                    if (c >= start && c <= end)
                        match = 1;
                } else {
                    if (!nocase) {
                        if (pattern[0] == string[0])
                            match = 1;
                    } else {
                        if (tolower((int)pattern[0]) == tolower((int)string[0]))
                            match = 1;
                    }
                }
                pattern++;
                patternLen--;
            }
            if (not)
                match = !match;
            if (!match)
                return 0; /* no match */
            string++;
            stringLen--;
            break;
        }
        case '\\':
        	// "\"后面还有字符,那么跳过当前的字符
            if (patternLen >= 2) {
                pattern++;
                patternLen--;
            }
            /* fall through */
        default:
            if (!nocase) {
            	//比较当前字符
                if (pattern[0] != string[0])
                    return 0; /* no match */
            } else {
            	//不考虑大小写,比较当前字符
                if (tolower((int)pattern[0]) != tolower((int)string[0]))
                    return 0; /* no match */
            }
            string++;
            stringLen--;
            break;
        }
        pattern++;
        patternLen--;
        if (stringLen == 0) {
        	//当string长度为0了,但是pattern还有*,跳过所有的*
            while(*pattern == '*') {
                pattern++;
                patternLen--;
            }
            break;
        }
    }
    //遍历结束后pattern和string长度都为0,说明匹配
    if (patternLen == 0 && stringLen == 0)
        return 1;
    return 0;
}
Beispiel #23
0
/* Glob-style pattern matching. */
int stringmatchlen(const char *pattern, int patternLen,
        const char *string, int stringLen, int nocase)
{
    while(patternLen) {
        switch(pattern[0]) {
        case '*':
            // 跳过所有的 *
            while (pattern[1] == '*') {
                pattern++;
                patternLen--;
            }
            if (patternLen == 1)
                return 1; /* match */
            while(stringLen) {
                // 递归
                if (stringmatchlen(pattern+1, patternLen-1,
                            string, stringLen, nocase))
                    return 1; /* match */
                string++;
                stringLen--;
            }
            return 0; /* no match */
            break;
        case '?':
            if (stringLen == 0)
                return 0; /* no match */
            // 只跳过一个字符
            string++;
            stringLen--;
            break;
        case '[':
        {
            int not, match;

            pattern++;
            patternLen--;
            not = pattern[0] == '^';
            if (not) { // skip ^
                pattern++;
                patternLen--;
            }
            match = 0;
            while(1) {
                if (pattern[0] == '\\') {
                    pattern++; // skip \\
                    patternLen--;
                    if (pattern[0] == string[0])
                        match = 1;
                } else if (pattern[0] == ']') { // end
                    break;
                } else if (patternLen == 0) {
                    pattern--; // protect
                    patternLen++;
                    break;
                } else if (pattern[1] == '-' && patternLen >= 3) {
                    int start = pattern[0];
                    int end = pattern[2];
                    int c = string[0];
                    if (start > end) { // swap
                        int t = start;
                        start = end;
                        end = t;
                    }
                    if (nocase) { // tolower
                        start = tolower(start);
                        end = tolower(end);
                        c = tolower(c);
                    }
                    pattern += 2;
                    patternLen -= 2;
                    if (c >= start && c <= end)
                        match = 1;
                } else {
                    if (!nocase) { // case
                        if (pattern[0] == string[0])
                            match = 1;
                    } else {
                        if (tolower((int)pattern[0]) == tolower((int)string[0]))
                            match = 1;
                    }
                }
                pattern++;
                patternLen--;
            }

            // [^xxxx] 不能匹配 xxxx,match!=0 表示 string 中出现 xxxx,
            // 匹配失败
            if (not)
                match = !match;
            if (!match)
                return 0; /* no match */
            string++;
            stringLen--;
            break;
        }
        case '\\':
            if (patternLen >= 2) { // skip \\
                pattern++;
                patternLen--;
            }
            /* fall through */
        default:
            if (!nocase) {
                if (pattern[0] != string[0])
                    return 0; /* no match */
            } else {
                if (tolower((int)pattern[0]) != tolower((int)string[0]))
                    return 0; /* no match */
            }
            string++;
            stringLen--;
            break;
        }
        pattern++;
        patternLen--;
        if (stringLen == 0) {
            while(*pattern == '*') { // skip all *
                pattern++;
                patternLen--;
            }
            break;
        }
    }
    if (patternLen == 0 && stringLen == 0)
        return 1;
    return 0;
}
Beispiel #24
0
int stringmatch(const char *pattern, const char *string, int nocase) {
    return stringmatchlen(pattern,strlen(pattern),string,strlen(string),nocase);
}
Beispiel #25
0
/* Glob-style pattern matching. */
int stringmatchlen(const char *pattern, int patternLen,
        const char *string, int stringLen, int nocase)
{
    while(patternLen) {
        switch(pattern[0]) {
        case '*':
            while (pattern[1] == '*') {
                pattern++;
                patternLen--;
            }
            if (patternLen == 1)
                return 1; /* match */
            while(stringLen) {
                if (stringmatchlen(pattern+1, patternLen-1,
                            string, stringLen, nocase))
                    return 1; /* match */
                string++;
                stringLen--;
            }
            return 0; /* no match */
            break;
        case '?':
            if (stringLen == 0)
                return 0; /* no match */
            string++;
            stringLen--;
            break;
        case '[':
        {
            int not, match;

            pattern++;
            patternLen--;
            not = pattern[0] == '^';
            if (not) {
                pattern++;
                patternLen--;
            }
            match = 0;
            while(1) {
                if (pattern[0] == '\\') {
                    pattern++;
                    patternLen--;
                    if (pattern[0] == string[0])
                        match = 1;
                } else if (pattern[0] == ']') {
                    break;
                } else if (patternLen == 0) {
                    pattern--;
                    patternLen++;
                    break;
                } else if (pattern[1] == '-' && patternLen >= 3) {
                    int start = pattern[0];
                    int end = pattern[2];
                    int c = string[0];
                    if (start > end) {
                        int t = start;
                        start = end;
                        end = t;
                    }
                    if (nocase) {
                        start = tolower(start);
                        end = tolower(end);
                        c = tolower(c);
                    }
                    pattern += 2;
                    patternLen -= 2;
                    if (c >= start && c <= end)
                        match = 1;
                } else {
                    if (!nocase) {
                        if (pattern[0] == string[0])
                            match = 1;
                    } else {
                        if (tolower((int)pattern[0]) == tolower((int)string[0]))
                            match = 1;
                    }
                }
                pattern++;
                patternLen--;
            }
            if (not)
                match = !match;
            if (!match)
                return 0; /* no match */
            string++;
            stringLen--;
            break;
        }
        case '\\':
            if (patternLen >= 2) {
                pattern++;
                patternLen--;
            }
            /* fall through */
        default:
            if (!nocase) {
                if (pattern[0] != string[0])
                    return 0; /* no match */
            } else {
                if (tolower((int)pattern[0]) != tolower((int)string[0]))
                    return 0; /* no match */
            }
            string++;
            stringLen--;
            break;
        }
        pattern++;
        patternLen--;
        if (stringLen == 0) {
            while(*pattern == '*') {
                pattern++;
                patternLen--;
            }
            break;
        }
    }
    if (patternLen == 0 && stringLen == 0)
        return 1;
    return 0;
}
static int check_response(redis_node *rnode, struct msg *r)
{
    int ret;
    rmtContext *ctx = rnode->ctx;
    struct msg *resp, *msg = NULL;
    check_data *chdata;
    check_unit *cunit;
    char extra_err[50];
    struct array args;
    sds *arg;
    struct array *bulks1 = NULL, *bulks2 = NULL;
    sds *bulk1, *bulk2;

    if (r == NULL) {
        return RMT_ERROR;
    }

    extra_err[0] = '\0';

    resp = r->peer;
    r->peer = NULL;
    array_init(&args, 3, sizeof(sds));

    ASSERT(r->request && r->sent);
    ASSERT(resp != NULL && resp->request == 0);

    cunit = (check_unit *)r->ptr;
    chdata = cunit->cdata->data;

    if(resp->type == MSG_RSP_REDIS_ERROR){
        log_warn("Response from node[%s] for %s is error",
            rnode->addr, msg_type_string(r->type));
        goto error;
    }

    if (cunit->state == CHECK_UNIT_STATE_GET_KEY) {
        ASSERT(cunit->key == NULL);
        ASSERT(cunit->key_type == -1);
        ASSERT(cunit->result1 == NULL && cunit->result2 == NULL);
        ASSERT(cunit->srnode == rnode);

        if (resp->type != MSG_RSP_REDIS_BULK) {
            log_error("ERROR: the response type for command 'randomkey' from node[%s] is error: %s", 
                rnode->addr, msg_type_string(resp->type));
            goto error;
        }

        if (msg_cmp_str(resp, (const uint8_t*)REDIS_REPLY_BULK_NULL, 
            rmt_strlen(REDIS_REPLY_BULK_NULL)) == 0) {
            /* source group may have no keys, stop it */
            cunit->cdata->keys_count --;
            goto done;
        }

        cunit->key = redis_msg_response_get_bulk_string(resp);
        if (cunit->key == NULL) {
            log_error("ERROR: get bulk string from response of node[%s] failed, "
                "bulk_len: %"PRIu32", bulk_start: %p", 
                rnode->addr, resp->bulk_len, resp->bulk_start);
            goto error;
        }

        if (ctx->filter != NULL && !stringmatchlen(ctx->filter, sdslen(ctx->filter), 
            cunit->key, sdslen(cunit->key), 0)) {
            goto done;
        }

        ASSERT(sdslen(cunit->key) == resp->bulk_len);

        msg = msg_get(r->mb, 1, REDIS_DATA_TYPE_CMD);
        if (msg == NULL) {
            log_error("ERROR: out of memory.");
            goto error;
        }

        arg = array_push(&args);
        *arg = sdsnew("type");
        arg = array_push(&args);
        *arg = sdsdup(cunit->key);
        ret = redis_msg_append_command_full_safe(msg, &args);
        if (ret != RMT_OK) {
            log_error("ERROR: msg append multi bulk len failed.");
            goto error;
        }
        while (array_n(&args) > 0) {
            arg = array_pop(&args);
            sdsfree(*arg);
        }
        
        msg->ptr = cunit;
        msg->resp_check = check_response;

        ret = prepare_send_msg(rnode, msg, rnode);
        if (ret != RMT_OK) {
            log_error("ERROR: prepare send msg node[%s] failed.", rnode->addr);
            goto error;
        }

        cunit->state = CHECK_UNIT_STATE_GET_TYPE;
        goto next_step;
    }

    if (cunit->state == CHECK_UNIT_STATE_GET_TYPE) {
        ASSERT(cunit->key != NULL);
        ASSERT(cunit->key_type == -1);
        ASSERT(cunit->result1 == NULL && cunit->result2 == NULL);
        ASSERT(cunit->srnode == rnode);
        
        if (resp->type != MSG_RSP_REDIS_STATUS) {
            log_error("ERROR: the response type for command 'type' from node[%s] is error: %s", 
                rnode->addr, msg_type_string(resp->type));
            goto error;
        }

        if (msg_cmp_str(resp, (const uint8_t*)REDIS_REPLY_STATUS_NONE, 
            rmt_strlen(REDIS_REPLY_STATUS_NONE)) == 0) {
            /* This key doesn't exit, may be expired or evicted */
            goto done;
        }

        msg = msg_get(r->mb, 1, REDIS_DATA_TYPE_CMD);
        if (msg == NULL) {
            log_error("ERROR: out of memory.");
            goto error;
        }

        if (msg_cmp_str(resp, (const uint8_t*)REDIS_REPLY_STATUS_STRING, 
            rmt_strlen(REDIS_REPLY_STATUS_STRING)) == 0) {
            cunit->key_type = REDIS_STRING;

            arg = array_push(&args);
            *arg = sdsnew("get");
            arg = array_push(&args);
            *arg = sdsdup(cunit->key);
            ret = redis_msg_append_command_full_safe(msg, &args);
            if (ret != RMT_OK) {
                log_error("ERROR: msg append multi bulk len failed.");
                goto error;
            }
            while (array_n(&args) > 0) {
                arg = array_pop(&args);
                sdsfree(*arg);
            }
        } else if (msg_cmp_str(resp, (const uint8_t*)REDIS_REPLY_STATUS_LIST, 
            rmt_strlen(REDIS_REPLY_STATUS_LIST)) == 0) {
            cunit->key_type = REDIS_LIST;

            arg = array_push(&args);
            *arg = sdsnew("lrange");
            arg = array_push(&args);
            *arg = sdsdup(cunit->key);
            arg = array_push(&args);
            *arg = sdsnew("0");
            arg = array_push(&args);
            *arg = sdsnew("-1");
            ret = redis_msg_append_command_full_safe(msg, &args);
            if (ret != RMT_OK) {
                log_error("ERROR: msg append multi bulk len failed.");
                goto error;
            }
            while (array_n(&args) > 0) {
                arg = array_pop(&args);
                sdsfree(*arg);
            }
        } else if (msg_cmp_str(resp, (const uint8_t*)REDIS_REPLY_STATUS_SET, 
            rmt_strlen(REDIS_REPLY_STATUS_SET)) == 0) {
            cunit->key_type = REDIS_SET;

            arg = array_push(&args);
            *arg = sdsnew("smembers");
            arg = array_push(&args);
            *arg = sdsdup(cunit->key);
            ret = redis_msg_append_command_full_safe(msg, &args);
            if (ret != RMT_OK) {
                log_error("ERROR: msg append multi bulk len failed.");
                goto error;
            }
            while (array_n(&args) > 0) {
                arg = array_pop(&args);
                sdsfree(*arg);
            }
        } else if (msg_cmp_str(resp, (const uint8_t*)REDIS_REPLY_STATUS_ZSET, 
            rmt_strlen(REDIS_REPLY_STATUS_ZSET)) == 0) {
            cunit->key_type = REDIS_ZSET;

            arg = array_push(&args);
            *arg = sdsnew("zrange");
            arg = array_push(&args);
            *arg = sdsdup(cunit->key);
            arg = array_push(&args);
            *arg = sdsnew("0");
            arg = array_push(&args);
            *arg = sdsnew("-1");
            ret = redis_msg_append_command_full_safe(msg, &args);
            if (ret != RMT_OK) {
                log_error("ERROR: msg append multi bulk len failed.");
                goto error;
            }
            while (array_n(&args) > 0) {
                arg = array_pop(&args);
                sdsfree(*arg);
            }
        } else if (msg_cmp_str(resp, (const uint8_t*)REDIS_REPLY_STATUS_HASH, 
            rmt_strlen(REDIS_REPLY_STATUS_HASH)) == 0) {
            cunit->key_type = REDIS_HASH;

            arg = array_push(&args);
            *arg = sdsnew("hgetall");
            arg = array_push(&args);
            *arg = sdsdup(cunit->key);
            ret = redis_msg_append_command_full_safe(msg, &args);
            if (ret != RMT_OK) {
                log_error("ERROR: msg append multi bulk len failed.");
                goto error;
            }
            while (array_n(&args) > 0) {
                arg = array_pop(&args);
                sdsfree(*arg);
            }
        } else {
            log_error("ERROR: response key type from node[%s] is error: ",
                rnode->addr);
            goto error;
        }

        msg->ptr = cunit;
        msg->resp_check = check_response;
        
        ret = send_msg_to_all(cunit, msg);
        if (ret != RMT_OK) {
            log_error("ERROR: send msg to source and target group failed.");
            goto error;
        }

        cunit->state = CHECK_UNIT_STATE_GET_VALUE;
        goto next_step;
    }

    if (cunit->state == CHECK_UNIT_STATE_GET_VALUE) {
        ASSERT(cunit->key != NULL);
        ASSERT(cunit->key_type >= 0);
        ASSERT(cunit->result1 == NULL || cunit->result2 == NULL);
        
        if (cunit->key_type == REDIS_STRING) {
            if (resp->type != MSG_RSP_REDIS_BULK) {
                log_error("ERROR: the response type for %s from node[%s] is error: %s", 
                    rnode->addr, msg_type_string(r->type), msg_type_string(resp->type));
                goto error;
            }
        } else if (cunit->key_type == REDIS_LIST) {

        } else if (cunit->key_type == REDIS_SET) {
            
        } else if (cunit->key_type == REDIS_ZSET) {
            
        } else if (cunit->key_type == REDIS_HASH) {
            
        } else {
            NOT_REACHED();
        }

        if (cunit->result1 == NULL) {
            cunit->result1 = resp;
            resp = NULL;
        } else if (cunit->result2 == NULL) {
            cunit->result2 = resp;
            resp = NULL;
        } else {
            NOT_REACHED();
        }
    
        if (cunit->result1 != NULL && cunit->result2 != NULL) {
            if (cunit->key_type == REDIS_SET) {
                uint32_t j;

                bulks1 = get_multi_bulk_array_from_mbuf_list(cunit->result1->data);
                bulks2 = get_multi_bulk_array_from_mbuf_list(cunit->result2->data);
                if (bulks1 == NULL || bulks2 == NULL) {
                    log_error("ERROR: get multi bulk array from mbufs failed");
                    goto error;
                }

                if (array_n(bulks1) != array_n(bulks2)) {
                    chdata->err_inconsistent_value_keys_count ++;
                    rmt_safe_snprintf(extra_err, 50, ", value is inconsistent\0");
                    goto error;
                }
                
                array_sort(bulks1, string_binary_cmp);
                array_sort(bulks2, string_binary_cmp);

                for (j = 0; j < array_n(bulks1); j ++) {
                    bulk1 = array_get(bulks1, j);
                    bulk2 = array_get(bulks2, j);
                    if (string_binary_cmp(bulk1, bulk2) != 0) {
                        chdata->err_inconsistent_value_keys_count ++;
                        rmt_safe_snprintf(extra_err, 50, ", value is inconsistent\0");
                        goto error;
                    }
                }
            } else if (cunit->key_type == REDIS_HASH) {
                struct array *hash_datas1, *hash_datas2;
                uint32_t hash_len;
                uint32_t j;
                struct hash_data *hd1, *hd2;

                hash_datas1 = hash_datas2 = NULL;
            
                bulks1 = get_multi_bulk_array_from_mbuf_list(cunit->result1->data);
                bulks2 = get_multi_bulk_array_from_mbuf_list(cunit->result2->data);
                if (bulks1 == NULL || bulks2 == NULL) {
                    log_error("ERROR: get multi bulk array from mbufs failed");
                    goto error;
                }

                if (array_n(bulks1)%2 != 0 || array_n(bulks2)%2 != 0) {
                    log_error("ERROR: bad hash value");
                    goto error;
                }

                if (array_n(bulks1) != array_n(bulks2)) {
                    chdata->err_inconsistent_value_keys_count ++;
                    rmt_safe_snprintf(extra_err, 50, ", value is inconsistent\0");
                    goto error;
                }

                hash_len = array_n(bulks1)/2;
                hash_datas1 = array_create(hash_len, sizeof(struct hash_data));
                hash_datas2 = array_create(hash_len, sizeof(struct hash_data));

                for (j = 0; j < hash_len; j ++) {
                    hd1 = array_push(hash_datas1);
                    hd2 = array_push(hash_datas2);

                    bulk1 = array_pop(bulks1);
                    bulk2 = array_pop(bulks1);
                    hd1->field = *bulk1;
                    hd1->value = *bulk2;

                    bulk1 = array_pop(bulks2);
                    bulk2 = array_pop(bulks2);
                    hd2->field = *bulk1;
                    hd2->value = *bulk2;
                }

                array_sort(hash_datas1, hash_data_field_cmp);
                array_sort(hash_datas2, hash_data_field_cmp);

                for (j = 0; j < array_n(bulks1); j ++) {
                    hd1 = array_get(hash_datas1, j);
                    hd2 = array_get(hash_datas2, j);
                    if (string_binary_cmp(hd1->field, hd2->field) != 0) {
                        chdata->err_inconsistent_value_keys_count ++;
                        rmt_safe_snprintf(extra_err, 50, ", value is inconsistent\0");
                        if (hash_datas1 != NULL) {
                            while (array_n(hash_datas1) > 0) {
                                hd1 = array_pop(hash_datas1);
                                sdsfree(hd1->field);
                                sdsfree(hd1->value);
                            }
                            array_destroy(hash_datas1);
                            hash_datas1 = NULL;
                        }
                        if (hash_datas2 != NULL) {
                            while (array_n(hash_datas2) > 0) {
                                hd2 = array_pop(hash_datas2);
                                sdsfree(hd2->field);
                                sdsfree(hd2->value);
                            }
                            array_destroy(hash_datas2);
                            hash_datas2 = NULL;
                        }
                        goto error;
                    }
                    if (string_binary_cmp(hd1->value, hd2->value) != 0) {
                        chdata->err_inconsistent_value_keys_count ++;
                        rmt_safe_snprintf(extra_err, 50, ", value is inconsistent\0");
                        if (hash_datas1 != NULL) {
                            while (array_n(hash_datas1) > 0) {
                                hd1 = array_pop(hash_datas1);
                                sdsfree(hd1->field);
                                sdsfree(hd1->value);
                            }
                            array_destroy(hash_datas1);
                            hash_datas1 = NULL;
                        }
                        if (hash_datas2 != NULL) {
                            while (array_n(hash_datas2) > 0) {
                                hd2 = array_pop(hash_datas2);
                                sdsfree(hd2->field);
                                sdsfree(hd2->value);
                            }
                            array_destroy(hash_datas2);
                            hash_datas2 = NULL;
                        }
                        goto error;
                    }
                }

                if (hash_datas1 != NULL) {
                    while (array_n(hash_datas1) > 0) {
                        hd1 = array_pop(hash_datas1);
                        sdsfree(hd1->field);
                        sdsfree(hd1->value);
                    }
                    array_destroy(hash_datas1);
                    hash_datas1 = NULL;
                }
                if (hash_datas2 != NULL) {
                    while (array_n(hash_datas2) > 0) {
                        hd2 = array_pop(hash_datas2);
                        sdsfree(hd2->field);
                        sdsfree(hd2->value);
                    }
                    array_destroy(hash_datas2);
                    hash_datas2 = NULL;
                }
            } else if (msg_data_compare(cunit->result1, cunit->result2) != 0) {
                chdata->err_inconsistent_value_keys_count ++;
                rmt_safe_snprintf(extra_err, 50, ", value is inconsistent\0");
                goto error;
            }

            msg_put(cunit->result1);
            msg_free(cunit->result1);
            cunit->result1 = NULL;
            msg_put(cunit->result2);
            msg_free(cunit->result2);
            cunit->result2 = NULL;

            if (bulks1 != NULL) {
                while (array_n(bulks1) > 0) {
                    bulk1 = array_pop(bulks1);
                    sdsfree(*bulk1);
                }
                array_destroy(bulks1);
                bulks1 = NULL;
            }
            if (bulks2 != NULL) {
                while (array_n(bulks2) > 0) {
                    bulk2 = array_pop(bulks2);
                    sdsfree(*bulk2);
                }
                array_destroy(bulks2);
                bulks2 = NULL;
            }

            msg = msg_get(r->mb, 1, REDIS_DATA_TYPE_CMD);
            if (msg == NULL) {
                log_error("ERROR: out of memory.");
                goto error;
            }

            arg = array_push(&args);
            *arg = sdsnew("ttl");
            arg = array_push(&args);
            *arg = sdsdup(cunit->key);
            ret = redis_msg_append_command_full_safe(msg, &args);
            if (ret != RMT_OK) {
                log_error("ERROR: msg append multi bulk len failed.");
                goto error;
            }
            while (array_n(&args) > 0) {
                arg = array_pop(&args);
                sdsfree(*arg);
            }
            
            msg->ptr = cunit;
            msg->resp_check = check_response;
            
            ret = send_msg_to_all(cunit, msg);
            if (ret != RMT_OK) {
                log_error("ERROR: send msg to source and target group failed.");
                goto error;
            }
            cunit->state = CHECK_UNIT_STATE_GET_EXPIRE;
        }

        goto next_step;
    }

    if (cunit->state == CHECK_UNIT_STATE_GET_EXPIRE) {
        ASSERT(cunit->key != NULL);
        ASSERT(cunit->key_type >= 0);
        ASSERT(cunit->result1 == NULL || cunit->result2 == NULL);

        if (resp->type != MSG_RSP_REDIS_INTEGER) {
            log_error("ERROR: the response type for command 'ttl' from node[%s] is error: %s", 
                rnode->addr, msg_type_string(resp->type));
            goto error;
        }

        if (cunit->result1 == NULL) {
            cunit->result1 = resp;
            resp = NULL;
        } else if (cunit->result2 == NULL) {
            cunit->result2 = resp;
            resp = NULL;
        } else {
            NOT_REACHED();
        }

        if (cunit->result1 != NULL && cunit->result2 != NULL) {
            if (msg_data_compare(cunit->result1, cunit->result2) != 0) { 
                int mistake = (int)cunit->result1->integer - (int)cunit->result2->integer;
                ASSERT(mistake != 0);

                if (abs(mistake) > TTL_MISTAKE_CAN_BE_ACCEPT) {
                    chdata->err_inconsistent_expire_keys_count ++;
                    rmt_safe_snprintf(extra_err, 50, 
                        ", remaining time are %"PRIu32" and %"PRIu32"\0", 
                        cunit->result1->integer, cunit->result2->integer);
                    goto error;
                }
            }

            /* OK, this key is consistent between source group and target group */
            goto done;
        }

        goto next_step;
    }

done:

    check_unit_destroy(cunit);

next_step:
    
    msg_put(r);
    msg_free(r);
    if (resp != NULL) {
        msg_put(resp);
        msg_free(resp);
    }

    array_deinit(&args);
    
    return RMT_OK;

error:

    chdata->err_check_keys_count ++;

    if (cunit->key != NULL) {        
        log_error("ERROR: key checked failed: %s%s. key(len:%zu, type:%s): %.*s",  
            get_check_error(cunit), extra_err, 
            sdslen(cunit->key), get_redis_type_string(cunit->key_type),
            sdslen(cunit->key), cunit->key);
    } else {
        log_error("ERROR: key checked failed: %s%s.", 
            get_check_error(cunit), extra_err);
    }
    MSG_DUMP(r, LOG_ERR, 1);
    msg_put(r);
    msg_free(r);
    if (resp != NULL) {
        MSG_DUMP(resp, LOG_ERR, 1);
        msg_put(resp);
        msg_free(resp);
    }

    if (msg != NULL) {
        msg_put(msg);
        msg_free(msg);
    }

    check_unit_destroy(cunit);
    
    while (array_n(&args) > 0) {
        arg = array_pop(&args);
        sdsfree(*arg);
    }
    array_deinit(&args);

    if (bulks1 != NULL) {
        while (array_n(bulks1) > 0) {
            bulk1 = array_pop(bulks1);
            sdsfree(*bulk1);
        }
        array_destroy(bulks1);
        bulks1 = NULL;
    }
    if (bulks2 != NULL) {
        while (array_n(bulks2) > 0) {
            bulk2 = array_pop(bulks2);
            sdsfree(*bulk2);
        }
        array_destroy(bulks2);
        bulks2 = NULL;
    }
    
    return RMT_OK;
}