// File: /workspace/github_c_makefile_star2_O2/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 0x66301(%rip), %rsi     # 0x8b1c2
movq %r14, %rdi
callq 0x1f2e0
testl %eax, %eax
je 0x24f10
movq %r14, %rdi
callq 0x24bcc
movq %rax, 0x48(%rbx)
movq %rax, 0x40(%rbx)
testq %rax, %rax
je 0x24f2b
movl 0x10(%rax), %edx
movl 0x30(%rbx), %ecx
testl %edx, %edx
jle 0x24ef2
cmpl %ecx, %edx
movl %edx, %ecx
jne 0x24ef8
negl %edx
cmpl %edx, %ecx
jge 0x24f5c
movq %rbx, %rdi
callq 0x485a1
movq 0x40(%rbx), %rax
movq (%rax), %rdx
leaq 0x662ce(%rip), %rsi     # 0x8b1dc
jmp 0x24f45
movq 0xa28f1(%rip), %rsi     # 0xc7808
movq %rbx, %rdi
callq 0x2c10d
orb $0x40, 0x90(%rbx)
pushq $-0x1
popq %rbp
jmp 0x24f51
movq %rbx, %rdi
callq 0x485a1
movq 0x38(%rbx), %rax
movq (%rax), %rax
movq 0x8(%rax), %rdx
leaq 0x66282(%rip), %rsi     # 0x8b1c7
xorl %ebp, %ebp
movq %rbx, %rdi
xorl %eax, %eax
callq 0x2c301
movl %ebp, %eax
addq $0x10, %rsp
popq %rbx
popq %r14
popq %rbp
retq
cmpq $0x0, 0xa2084(%rip)     # 0xc6fe8
je 0x24f6f
cmpl $0x0, 0x94(%rbx)
je 0x24fcb
cmpl $0x0, 0xa27ee(%rip)     # 0xc7764
je 0x2506c
testb $0x2, 0x90(%rbx)
jne 0x2506c
cmpq $0x0, 0x28(%rax)
jne 0x24f9a
cmpl $0x0, 0x30(%rax)
je 0x2506c
movq 0xa27d7(%rip), %rdx     # 0xc7778
cmpl $0x0, 0x10(%rdx)
je 0x24fec
movq %rbx, %rdi
callq 0x485a1
leaq 0x66251(%rip), %rdi     # 0x8b207
callq 0x271cd
movq %rbx, %rdi
movq %rax, %rsi
callq 0x2c20f
jmp 0x253ea
leaq -0x2c6e(%rip), %rdx     # 0x22364
cmpq %rdx, 0x8(%rax)
je 0x24f6f
movq %rbx, %rdi
callq 0x485a1
movq 0xa28e9(%rip), %rsi     # 0xc78d0
jmp 0x253e2
movq 0x38(%rbx), %rdx
leaq 0xc(%rsp), %r8
leaq 0x8(%rsp), %r9
movq %rbx, %rdi
movq %rax, %rsi
callq 0x518e7
testq %rax, %rax
je 0x252e0
movq %rax, %r14
movq 0xa2760(%rip), %rax     # 0xc7778
cmpq (%rax), %r14
je 0x2506c
movq %rbx, %rdi
callq 0x485a1
callq 0x271bf
cmpl $0x3, 0x8(%rsp)
leaq 0x662c0(%rip), %rcx     # 0x8b2f6
leaq 0x662bd(%rip), %rdx     # 0x8b2fa
cmoveq %rcx, %rdx
movl 0xc(%rsp), %ecx
movq %r14, %r8
addq $0x888, %r8             # imm = 0x888
movl 0x8b8(%r14), %r9d
leaq 0x6628a(%rip), %rsi     # 0x8b2e7
movq %rax, %rdi
xorl %eax, %eax
callq 0x2763d
jmp 0x24fbb
cmpq $0x0, 0xa2654(%rip)     # 0xc76c8
je 0x250a0
callq 0x25411
cmpl $-0x1, %eax
jne 0x250a0
movq 0x40(%rbx), %rax
movl 0x20(%rax), %eax
andl $0x4, %eax
je 0x250a0
movq %rbx, %rdi
callq 0x485a1
movq 0xa284d(%rip), %rsi     # 0xc78e8
jmp 0x253e2
cmpl $0x0, 0xa24bd(%rip)     # 0xc7564
setne %al
cmpl $0x0, 0xa2477(%rip)     # 0xc7528
setg %cl
andb %al, %cl
cmpl $-0x1, 0xa24a3(%rip)    # 0xc7560
sete %al
andb %cl, %al
cmpl $-0x1, 0xa2433(%rip)    # 0xc74fc
sete %cl
orb %al, %cl
movq 0xa2523(%rip), %rax     # 0xc75f8
testq %rax, %rax
sete %dl
andb %cl, %dl
cmpb $0x1, %dl
jne 0x25101
movq 0x40(%rbx), %rcx
testb $0x1, 0x20(%rcx)
jne 0x251c2
leaq -0x2d34(%rip), %rdx     # 0x223c3
cmpq %rdx, 0x8(%rcx)
je 0x251c2
movl 0xa24d9(%rip), %ecx     # 0xc75e0
testl %ecx, %ecx
je 0x2513a
cmpl $0x0, 0xa24d2(%rip)     # 0xc75e4
je 0x2513a
movq 0x40(%rbx), %rdx
testb $0x1, 0x20(%rdx)
je 0x2513a
cmpl %ecx, 0xa24c4(%rip)     # 0xc75e8
jge 0x2513a
movq %rbx, %rdi
callq 0x485a1
movq 0xa27a3(%rip), %rsi     # 0xc78d8
jmp 0x253e2
testq %rax, %rax
je 0x2515f
cmpl $0x0, 0xa250e(%rip)     # 0xc7654
je 0x2515f
testb $0x2, 0x90(%rbx)
jne 0x2515f
movq 0x40(%rbx), %rcx
testb $0x1, 0x20(%rcx)
jne 0x2530a
movq 0x158(%rbx), %rcx
movq 0x28(%rcx), %rdx
addq 0x48(%rcx), %rdx
jne 0x25182
movq 0x160(%rbx), %rcx
cmpq $0x0, 0x28(%rcx)
je 0x25210
movq 0x40(%rbx), %rcx
movq 0x8(%rcx), %rcx
cmpq 0x9ddaf(%rip), %rcx     # 0xc2f40
je 0x25210
cmpq 0x9de06(%rip), %rcx     # 0xc2fa0
je 0x25210
cmpq 0x9ddbd(%rip), %rcx     # 0xc2f60
je 0x25210
cmpq 0x9dde4(%rip), %rcx     # 0xc2f90
je 0x25210
leaq 0x66179(%rip), %rsi     # 0x8b32e
movq %rbx, %rdi
callq 0x2c2da
jmp 0x253ea
movq %rbx, %rdi
callq 0x485a1
cmpl $0x0, 0xa232b(%rip)     # 0xc74fc
je 0x252d4
callq 0x271bf
movq %rax, %r14
movl 0xa231b(%rip), %edi     # 0xc7500
callq 0x1f630
leaq 0x6610f(%rip), %rsi     # 0x8b300
xorl %ebp, %ebp
movq %r14, %rdi
movq %rax, %rdx
xorl %eax, %eax
callq 0x2763d
movq %rbx, %rdi
movq %rax, %rsi
callq 0x2c20f
jmp 0x24f51
testq %rax, %rax
je 0x25245
cmpl $0x5, 0xa2400(%rip)     # 0xc761c
je 0x25245
cmpl $0x0, 0xa242b(%rip)     # 0xc7650
jne 0x25245
movq 0x40(%rbx), %rax
testb $0x4, 0x21(%rax)
jne 0x25245
movq %rbx, %rdi
callq 0x485a1
movq 0xa2678(%rip), %rsi     # 0xc78b8
jmp 0x253e2
cmpl $0x0, 0xa204c(%rip)     # 0xc7298
je 0x25264
movq 0x40(%rbx), %rax
testb $0x2, 0x21(%rax)
jne 0x25264
movq 0xa2641(%rip), %rsi     # 0xc78a0
jmp 0x253e2
cmpl $0x0, 0xa2555(%rip)     # 0xc77c0
je 0x25294
movq 0x40(%rbx), %rax
movq 0x8(%rax), %rax
leaq -0x2f18(%rip), %rcx     # 0x22364
cmpq %rcx, %rax
sete %cl
cmpq 0x9dd0f(%rip), %rax     # 0xc2f98
sete %dl
orb %cl, %dl
je 0x25363
testb $0x8, 0x90(%rbx)
jne 0x25316
pushq $0x7
popq %rsi
movq %rbx, %rdi
callq 0x24c94
movq 0xa22e9(%rip), %rax     # 0xc7598
movq %rax, 0x148(%rbx)
movq 0xa242b(%rip), %rax     # 0xc76e8
xorl %ebp, %ebp
cmpq $0x0, 0x28(%rax)
je 0x24f51
callq 0x387e8
jmp 0x24f51
movq 0xa25d5(%rip), %rsi     # 0xc78b0
jmp 0x253e2
movq %rbx, %rdi
callq 0x485a1
movl 0x8(%rsp), %eax
cmpl $0x2, %eax
je 0x2539f
cmpl $0x1, %eax
jne 0x253f1
leaq 0x65f4c(%rip), %rdi     # 0x8b251
jmp 0x24fb6
movq 0xa25af(%rip), %rsi     # 0xc78c0
jmp 0x253e2
movq 0x40(%rbx), %rax
movq 0x8(%rax), %rax
cmpq 0x9dc53(%rip), %rax     # 0xc2f78
je 0x2529d
cmpq 0x9dbfe(%rip), %rax     # 0xc2f30
je 0x2529d
cmpq 0x9dbb1(%rip), %rax     # 0xc2ef0
je 0x2529d
cmpq 0x9dcac(%rip), %rax     # 0xc2ff8
je 0x2529d
movq %rbx, %rdi
callq 0x483fa
movq 0xa24ff(%rip), %rsi     # 0xc7860
jmp 0x253e2
cmpq 0x9dc3e(%rip), %rax     # 0xc2fa8
je 0x253ab
cmpq 0x9db9d(%rip), %rax     # 0xc2f10
jne 0x253d3
cmpl $0x2, 0x30(%rbx)
jne 0x253d3
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 0x25294
jmp 0x253d3
leaq 0x65ee4(%rip), %rdi     # 0x8b28a
jmp 0x24fb6
cmpl $0x2, 0x30(%rbx)
jne 0x253d3
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 0x25294
movq %rbx, %rdi
callq 0x485a1
movq 0xa24c6(%rip), %rsi     # 0xc78a8
movq %rbx, %rdi
callq 0x2c10d
xorl %ebp, %ebp
jmp 0x24f51
leaq 0x65ecd(%rip), %rdi     # 0x8b2c5
leaq 0x7128d(%rip), %rsi     # 0x9668c
movl $0xa38, %edx            # imm = 0xA38
callq 0x490e8
pushq $0x1
popq %rdi
callq 0x1f3b0