// File: /workspace/github_c_makefile_star2_O3/nilyang[P]redis-tdd-annotation/src/redis.c
// Function: processCommand
int processCommand(redisClient *c) {
    /* The QUIT command is handled separately. Normal command procs will
     * go through checking for replication and QUIT will cause trouble
     * when FORCE_REPLICATION is enabled and would be implemented in
     * a regular command proc. */
    // 特别处理 quit 命令
    if (!strcasecmp(c->argv[0]->ptr,"quit")) {
        addReply(c,shared.ok);
        c->flags |= REDIS_CLOSE_AFTER_REPLY;
        return REDIS_ERR;
    }

    /* Now lookup the command and check ASAP about trivial error conditions
     * such as wrong arity, bad command name and so forth. */
    // 查找命令，并进行命令合法性检查，以及命令参数个数检查
    c->cmd = c->lastcmd = lookupCommand(c->argv[0]->ptr);
    if (!c->cmd) {
        // 没找到指定的命令
        flagTransaction(c);
        addReplyErrorFormat(c,"unknown command '%s'",
            (char*)c->argv[0]->ptr);
        return REDIS_OK;
    } else if ((c->cmd->arity > 0 && c->cmd->arity != c->argc) ||
               (c->argc < -c->cmd->arity)) {
        // 参数个数错误
        flagTransaction(c);
        addReplyErrorFormat(c,"wrong number of arguments for '%s' command",
            c->cmd->name);
        return REDIS_OK;
    }

    /* Check if the user is authenticated */
    // 检查认证信息
    if (server.requirepass && !c->authenticated && c->cmd->proc != authCommand)
    {
        flagTransaction(c);
        addReply(c,shared.noautherr);
        return REDIS_OK;
    }

    /* If cluster is enabled perform the cluster redirection here.
     *
     * 如果开启了集群模式，那么在这里进行转向操作。
     *
     * However we don't perform the redirection if:
     *
     * 不过，如果有以下情况出现，那么节点不进行转向：
     *
     * 1) The sender of this command is our master.
     *    命令的发送者是本节点的主节点
     *
     * 2) The command has no key arguments. 
     *    命令没有 key 参数
     */
    if (server.cluster_enabled &&
        !(c->flags & REDIS_MASTER) &&
        !(c->cmd->getkeys_proc == NULL && c->cmd->firstkey == 0))
    {
        int hashslot;

        // 集群已下线
        if (server.cluster->state != REDIS_CLUSTER_OK) {
            flagTransaction(c);
            addReplySds(c,sdsnew("-CLUSTERDOWN The cluster is down. Use CLUSTER INFO for more information\r\n"));
            return REDIS_OK;

        // 集群运作正常
        } else {
            int error_code;
            clusterNode *n = getNodeByQuery(c,c->cmd,c->argv,c->argc,&hashslot,&error_code);
            // 不能执行多键处理命令
            if (n == NULL) {
                flagTransaction(c);
                if (error_code == REDIS_CLUSTER_REDIR_CROSS_SLOT) {
                    addReplySds(c,sdsnew("-CROSSSLOT Keys in request don't hash to the same slot\r\n"));
                } else if (error_code == REDIS_CLUSTER_REDIR_UNSTABLE) {
                    /* The request spawns mutliple keys in the same slot,
                     * but the slot is not "stable" currently as there is
                     * a migration or import in progress. */
                    addReplySds(c,sdsnew("-TRYAGAIN Multiple keys request during rehashing of slot\r\n"));
                } else {
                    redisPanic("getNodeByQuery() unknown error.");
                }
                return REDIS_OK;

            // 命令针对的槽和键不是本节点处理的，进行转向
            } else if (n != server.cluster->myself) {
                flagTransaction(c);
                // -<ASK or MOVED> <slot> <ip>:<port>
                // 例如 -ASK 10086 127.0.0.1:12345
                addReplySds(c,sdscatprintf(sdsempty(),
                    "-%s %d %s:%d\r\n",
                    (error_code == REDIS_CLUSTER_REDIR_ASK) ? "ASK" : "MOVED",
                    hashslot,n->ip,n->port));

                return REDIS_OK;
            }

            // 如果执行到这里，说明键 key 所在的槽由本节点处理
            // 或者客户端执行的是无参数命令
        }
    }

    /* Handle the maxmemory directive.
     *
     * First we try to free some memory if possible (if there are volatile
     * keys in the dataset). If there are not the only thing we can do
     * is returning an error. */
    // 如果设置了最大内存，那么检查内存是否超过限制，并做相应的操作
    if (server.maxmemory) {
        // 如果内存已超过限制，那么尝试通过删除过期键来释放内存
        int retval = freeMemoryIfNeeded();
        // 如果即将要执行的命令可能占用大量内存（REDIS_CMD_DENYOOM）
        // 并且前面的内存释放失败的话
        // 那么向客户端返回内存错误
        if ((c->cmd->flags & REDIS_CMD_DENYOOM) && retval == REDIS_ERR) {
            flagTransaction(c);
            addReply(c, shared.oomerr);
            return REDIS_OK;
        }
    }

    /* Don't accept write commands if there are problems persisting on disk
     * and if this is a master instance. */
    // 如果这是一个主服务器，并且这个服务器之前执行 BGSAVE 时发生了错误
    // 那么不执行写命令
    if (((server.stop_writes_on_bgsave_err &&
          server.saveparamslen > 0 &&
          server.lastbgsave_status == REDIS_ERR) ||
          server.aof_last_write_status == REDIS_ERR) &&
        server.masterhost == NULL &&
        (c->cmd->flags & REDIS_CMD_WRITE ||
         c->cmd->proc == pingCommand))
    {
        flagTransaction(c);
        if (server.aof_last_write_status == REDIS_OK)
            addReply(c, shared.bgsaveerr);
        else
            addReplySds(c,
                sdscatprintf(sdsempty(),
                "-MISCONF Errors writing to the AOF file: %s\r\n",
                strerror(server.aof_last_write_errno)));
        return REDIS_OK;
    }

    /* Don't accept write commands if there are not enough good slaves and
     * user configured the min-slaves-to-write option. */
    // 如果服务器没有足够多的状态良好服务器
    // 并且 min-slaves-to-write 选项已打开
    if (server.repl_min_slaves_to_write &&
        server.repl_min_slaves_max_lag &&
        c->cmd->flags & REDIS_CMD_WRITE &&
        server.repl_good_slaves_count < server.repl_min_slaves_to_write)
    {
        flagTransaction(c);
        addReply(c, shared.noreplicaserr);
        return REDIS_OK;
    }

    /* Don't accept write commands if this is a read only slave. But
     * accept write commands if this is our master. */
    // 如果这个服务器是一个只读 slave 的话，那么拒绝执行写命令
    if (server.masterhost && server.repl_slave_ro &&
        !(c->flags & REDIS_MASTER) &&
        c->cmd->flags & REDIS_CMD_WRITE)
    {
        addReply(c, shared.roslaveerr);
        return REDIS_OK;
    }

    /* Only allow SUBSCRIBE and UNSUBSCRIBE in the context of Pub/Sub */
    // 在订阅于发布模式的上下文中，只能执行订阅和退订相关的命令
    if ((dictSize(c->pubsub_channels) > 0 || listLength(c->pubsub_patterns) > 0)
        &&
        c->cmd->proc != subscribeCommand &&
        c->cmd->proc != unsubscribeCommand &&
        c->cmd->proc != psubscribeCommand &&
        c->cmd->proc != punsubscribeCommand) {
        addReplyError(c,"only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context");
        return REDIS_OK;
    }

    /* Only allow INFO and SLAVEOF when slave-serve-stale-data is no and
     * we are a slave with a broken link with master. */
    if (server.masterhost && server.repl_state != REDIS_REPL_CONNECTED &&
        server.repl_serve_stale_data == 0 &&
        !(c->cmd->flags & REDIS_CMD_STALE))
    {
        flagTransaction(c);
        addReply(c, shared.masterdownerr);
        return REDIS_OK;
    }

    /* Loading DB? Return an error if the command has not the
     * REDIS_CMD_LOADING flag. */
    // 如果服务器正在载入数据到数据库，那么只执行带有 REDIS_CMD_LOADING
    // 标识的命令，否则将出错
    if (server.loading && !(c->cmd->flags & REDIS_CMD_LOADING)) {
        addReply(c, shared.loadingerr);
        return REDIS_OK;
    }

    /* Lua script too slow? Only allow a limited number of commands. */
    // Lua 脚本超时，只允许执行限定的操作，比如 SHUTDOWN 和 SCRIPT KILL
    if (server.lua_timedout &&
          c->cmd->proc != authCommand &&
          c->cmd->proc != replconfCommand &&
        !(c->cmd->proc == shutdownCommand &&
          c->argc == 2 &&
          tolower(((char*)c->argv[1]->ptr)[0]) == 'n') &&
        !(c->cmd->proc == scriptCommand &&
          c->argc == 2 &&
          tolower(((char*)c->argv[1]->ptr)[0]) == 'k'))
    {
        flagTransaction(c);
        addReply(c, shared.slowscripterr);
        return REDIS_OK;
    }

    /* Exec the command */
    if (c->flags & REDIS_MULTI &&
        c->cmd->proc != execCommand && c->cmd->proc != discardCommand &&
        c->cmd->proc != multiCommand && c->cmd->proc != watchCommand)
    {
        // 在事务上下文中
        // 除 EXEC 、 DISCARD 、 MULTI 和 WATCH 命令之外
        // 其他所有命令都会被入队到事务队列中
        queueMultiCommand(c);
        addReply(c,shared.queued);
    } else {
        // 执行命令
        call(c,REDIS_CALL_FULL);

        c->woff = server.master_repl_offset;
        // 处理那些解除了阻塞的键
        if (listLength(server.ready_keys))
            handleClientsBlockedOnLists();
    }

    return REDIS_OK;
}

// Assembly:
pushq %rbp
pushq %r14
pushq %rbx
subq $0x10, %rsp
movq %rdi, %rbx
movq 0x38(%rdi), %rax
movq (%rax), %rax
movq 0x8(%rax), %r14
leaq 0x6f268(%rip), %rsi     # 0x95202
movq %r14, %rdi
callq 0x1f2e0
testl %eax, %eax
je 0x25ff0
movq 0xa4013(%rip), %rdi     # 0xc9fc0
movq %r14, %rsi
callq 0x229cf
movq %rax, 0x48(%rbx)
movq %rax, 0x40(%rbx)
testq %rax, %rax
je 0x2600d
movl 0x10(%rax), %edx
movl 0x30(%rbx), %ecx
testl %edx, %edx
jle 0x25fd2
cmpl %ecx, %edx
movl %edx, %ecx
jne 0x25fd8
negl %edx
cmpl %edx, %ecx
jge 0x2603e
movq %rbx, %rdi
callq 0x4be2c
movq 0x40(%rbx), %rax
movq (%rax), %rdx
leaq 0x6f22e(%rip), %rsi     # 0x9521c
jmp 0x26027
movq 0xa4811(%rip), %rsi     # 0xca808
movq %rbx, %rdi
callq 0x2d97c
orb $0x40, 0x90(%rbx)
movl $0xffffffff, %ebp       # imm = 0xFFFFFFFF
jmp 0x26033
movq %rbx, %rdi
callq 0x4be2c
movq 0x38(%rbx), %rax
movq (%rax), %rax
movq 0x8(%rax), %rdx
leaq 0x6f1e0(%rip), %rsi     # 0x95207
xorl %ebp, %ebp
movq %rbx, %rdi
xorl %eax, %eax
callq 0x2dc8a
movl %ebp, %eax
addq $0x10, %rsp
popq %rbx
popq %r14
popq %rbp
retq
cmpq $0x0, 0xa3fa2(%rip)     # 0xc9fe8
je 0x26051
cmpl $0x0, 0x94(%rbx)
je 0x260ad
cmpl $0x0, 0xa470c(%rip)     # 0xca764
je 0x2614e
testb $0x2, 0x90(%rbx)
jne 0x2614e
cmpq $0x0, 0x28(%rax)
jne 0x2607c
cmpl $0x0, 0x30(%rax)
je 0x2614e
movq 0xa46f5(%rip), %rdx     # 0xca778
cmpl $0x0, 0x10(%rdx)
je 0x260ce
movq %rbx, %rdi
callq 0x4be2c
leaq 0x6f1af(%rip), %rdi     # 0x95247
callq 0x28486
movq %rbx, %rdi
movq %rax, %rsi
callq 0x2db20
jmp 0x264ce
leaq -0x3154(%rip), %rdx     # 0x22f60
cmpq %rdx, 0x8(%rax)
je 0x26051
movq %rbx, %rdi
callq 0x4be2c
movq 0xa4807(%rip), %rsi     # 0xca8d0
jmp 0x264c6
movq 0x38(%rbx), %rdx
leaq 0xc(%rsp), %r8
leaq 0x8(%rsp), %r9
movq %rbx, %rdi
movq %rax, %rsi
callq 0x55ce7
testq %rax, %rax
je 0x263c4
movq %rax, %r14
movq 0xa467e(%rip), %rax     # 0xca778
cmpq (%rax), %r14
je 0x2614e
movq %rbx, %rdi
callq 0x4be2c
callq 0x2845e
cmpl $0x3, 0x8(%rsp)
leaq 0x6f21e(%rip), %rcx     # 0x95336
leaq 0x6f21b(%rip), %rdx     # 0x9533a
cmoveq %rcx, %rdx
movl 0xc(%rsp), %ecx
movq %r14, %r8
addq $0x888, %r8             # imm = 0x888
movl 0x8b8(%r14), %r9d
leaq 0x6f1e8(%rip), %rsi     # 0x95327
movq %rax, %rdi
xorl %eax, %eax
callq 0x28a5e
jmp 0x2609d
cmpq $0x0, 0xa4572(%rip)     # 0xca6c8
je 0x26182
callq 0x264f7
cmpl $-0x1, %eax
jne 0x26182
movq 0x40(%rbx), %rax
movl 0x20(%rax), %eax
andl $0x4, %eax
je 0x26182
movq %rbx, %rdi
callq 0x4be2c
movq 0xa476b(%rip), %rsi     # 0xca8e8
jmp 0x264c6
cmpl $0x0, 0xa43db(%rip)     # 0xca564
setne %al
cmpl $0x0, 0xa4395(%rip)     # 0xca528
setg %cl
andb %al, %cl
cmpl $-0x1, 0xa43c1(%rip)    # 0xca560
sete %al
andb %cl, %al
cmpl $-0x1, 0xa4351(%rip)    # 0xca4fc
sete %cl
orb %al, %cl
movq 0xa4441(%rip), %rax     # 0xca5f8
testq %rax, %rax
sete %dl
andb %cl, %dl
cmpb $0x1, %dl
jne 0x261e3
movq 0x40(%rbx), %rcx
testb $0x1, 0x20(%rcx)
jne 0x262a4
leaq -0x3217(%rip), %rdx     # 0x22fc2
cmpq %rdx, 0x8(%rcx)
je 0x262a4
movl 0xa43f7(%rip), %ecx     # 0xca5e0
testl %ecx, %ecx
je 0x2621c
cmpl $0x0, 0xa43f0(%rip)     # 0xca5e4
je 0x2621c
movq 0x40(%rbx), %rdx
testb $0x1, 0x20(%rdx)
je 0x2621c
cmpl %ecx, 0xa43e2(%rip)     # 0xca5e8
jge 0x2621c
movq %rbx, %rdi
callq 0x4be2c
movq 0xa46c1(%rip), %rsi     # 0xca8d8
jmp 0x264c6
testq %rax, %rax
je 0x26241
cmpl $0x0, 0xa442c(%rip)     # 0xca654
je 0x26241
testb $0x2, 0x90(%rbx)
jne 0x26241
movq 0x40(%rbx), %rcx
testb $0x1, 0x20(%rcx)
jne 0x263ee
movq 0x158(%rbx), %rcx
movq 0x28(%rcx), %rdx
addq 0x48(%rcx), %rdx
jne 0x26264
movq 0x160(%rbx), %rcx
cmpq $0x0, 0x28(%rcx)
je 0x262f2
movq 0x40(%rbx), %rcx
movq 0x8(%rcx), %rcx
cmpq 0x9fccd(%rip), %rcx     # 0xc5f40
je 0x262f2
cmpq 0x9fd24(%rip), %rcx     # 0xc5fa0
je 0x262f2
cmpq 0x9fcdb(%rip), %rcx     # 0xc5f60
je 0x262f2
cmpq 0x9fd02(%rip), %rcx     # 0xc5f90
je 0x262f2
leaq 0x6f0d7(%rip), %rsi     # 0x9536e
movq %rbx, %rdi
callq 0x2dc63
jmp 0x264ce
movq %rbx, %rdi
callq 0x4be2c
cmpl $0x0, 0xa4249(%rip)     # 0xca4fc
je 0x263b8
callq 0x2845e
movq %rax, %r14
movl 0xa4239(%rip), %edi     # 0xca500
callq 0x1f630
leaq 0x6f06d(%rip), %rsi     # 0x95340
xorl %ebp, %ebp
movq %r14, %rdi
movq %rax, %rdx
xorl %eax, %eax
callq 0x28a5e
movq %rbx, %rdi
movq %rax, %rsi
callq 0x2db20
jmp 0x26033
testq %rax, %rax
je 0x26327
cmpl $0x5, 0xa431e(%rip)     # 0xca61c
je 0x26327
cmpl $0x0, 0xa4349(%rip)     # 0xca650
jne 0x26327
movq 0x40(%rbx), %rax
testb $0x4, 0x21(%rax)
jne 0x26327
movq %rbx, %rdi
callq 0x4be2c
movq 0xa4596(%rip), %rsi     # 0xca8b8
jmp 0x264c6
cmpl $0x0, 0xa3f6a(%rip)     # 0xca298
je 0x26346
movq 0x40(%rbx), %rax
testb $0x2, 0x21(%rax)
jne 0x26346
movq 0xa455f(%rip), %rsi     # 0xca8a0
jmp 0x264c6
cmpl $0x0, 0xa4473(%rip)     # 0xca7c0
je 0x26376
movq 0x40(%rbx), %rax
movq 0x8(%rax), %rax
leaq -0x33fe(%rip), %rcx     # 0x22f60
cmpq %rcx, %rax
sete %cl
cmpq 0x9fc2d(%rip), %rax     # 0xc5f98
sete %dl
orb %cl, %dl
je 0x26447
testb $0x8, 0x90(%rbx)
jne 0x263fa
movq %rbx, %rdi
movl $0x7, %esi
callq 0x25d39
movq 0xa4205(%rip), %rax     # 0xca598
movq %rax, 0x148(%rbx)
movq 0xa4347(%rip), %rax     # 0xca6e8
xorl %ebp, %ebp
cmpq $0x0, 0x28(%rax)
je 0x26033
callq 0x3b1e9
jmp 0x26033
movq 0xa44f1(%rip), %rsi     # 0xca8b0
jmp 0x264c6
movq %rbx, %rdi
callq 0x4be2c
movl 0x8(%rsp), %eax
cmpl $0x2, %eax
je 0x26483
cmpl $0x1, %eax
jne 0x264d5
leaq 0x6eea8(%rip), %rdi     # 0x95291
jmp 0x26098
movq 0xa44cb(%rip), %rsi     # 0xca8c0
jmp 0x264c6
movq 0x40(%rbx), %rax
movq 0x8(%rax), %rax
cmpq 0x9fb6f(%rip), %rax     # 0xc5f78
je 0x2637f
cmpq 0x9fb1a(%rip), %rax     # 0xc5f30
je 0x2637f
cmpq 0x9facd(%rip), %rax     # 0xc5ef0
je 0x2637f
cmpq 0x9fbc8(%rip), %rax     # 0xc5ff8
je 0x2637f
movq %rbx, %rdi
callq 0x4bc64
movq 0xa441b(%rip), %rsi     # 0xca860
jmp 0x264c6
cmpq 0x9fb5a(%rip), %rax     # 0xc5fa8
je 0x2648f
cmpq 0x9fab9(%rip), %rax     # 0xc5f10
jne 0x264b7
cmpl $0x2, 0x30(%rbx)
jne 0x264b7
callq 0x1f930
movq (%rax), %rax
movq 0x38(%rbx), %rcx
movq 0x8(%rcx), %rcx
movq 0x8(%rcx), %rcx
movsbq (%rcx), %rcx
cmpl $0x6b, (%rax,%rcx,4)
je 0x26376
jmp 0x264b7
leaq 0x6ee40(%rip), %rdi     # 0x952ca
jmp 0x26098
cmpl $0x2, 0x30(%rbx)
jne 0x264b7
callq 0x1f930
movq (%rax), %rax
movq 0x38(%rbx), %rcx
movq 0x8(%rcx), %rcx
movq 0x8(%rcx), %rcx
movsbq (%rcx), %rcx
cmpl $0x6e, (%rax,%rcx,4)
je 0x26376
movq %rbx, %rdi
callq 0x4be2c
movq 0xa43e2(%rip), %rsi     # 0xca8a8
movq %rbx, %rdi
callq 0x2d97c
xorl %ebp, %ebp
jmp 0x26033
leaq 0x6ee29(%rip), %rdi     # 0x95305
leaq 0x7a22d(%rip), %rsi     # 0xa0710
movl $0xa38, %edx            # imm = 0xA38
callq 0x4cae0
movl $0x1, %edi
callq 0x1f3b0