// File: /workspace/github_c_makefile_star2_O1/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 0x6f441(%rip), %rsi     # 0x951b2
movq %r14, %rdi
callq 0x1f2e0
testl %eax, %eax
je 0x25dc6
movq 0xa423c(%rip), %rdi     # 0xc9fc0
movq %r14, %rsi
callq 0x22813
movq %rax, 0x48(%rbx)
movq %rax, 0x40(%rbx)
testq %rax, %rax
je 0x25de3
movl 0x10(%rax), %edx
testl %edx, %edx
jle 0x25da5
cmpl 0x30(%rbx), %edx
jne 0x25dae
movl 0x30(%rbx), %ecx
negl %edx
cmpl %edx, %ecx
jge 0x25e14
movq %rbx, %rdi
callq 0x4c440
movq 0x40(%rbx), %rax
movq (%rax), %rdx
leaq 0x6f408(%rip), %rsi     # 0x951cc
jmp 0x25dfd
movq 0xa4a3b(%rip), %rsi     # 0xca808
movq %rbx, %rdi
callq 0x2d6f0
orb $0x40, 0x90(%rbx)
movl $0xffffffff, %ebp       # imm = 0xFFFFFFFF
jmp 0x25e09
movq %rbx, %rdi
callq 0x4c440
movq 0x38(%rbx), %rax
movq (%rax), %rax
movq 0x8(%rax), %rdx
leaq 0x6f3ba(%rip), %rsi     # 0x951b7
xorl %ebp, %ebp
movq %rbx, %rdi
xorl %eax, %eax
callq 0x2d9fe
movl %ebp, %eax
addq $0x10, %rsp
popq %rbx
popq %r14
popq %rbp
retq
cmpq $0x0, 0xa41cc(%rip)     # 0xc9fe8
je 0x25e27
cmpl $0x0, 0x94(%rbx)
je 0x25e83
cmpl $0x0, 0xa4936(%rip)     # 0xca764
je 0x25f24
testb $0x2, 0x90(%rbx)
jne 0x25f24
cmpq $0x0, 0x28(%rax)
jne 0x25e52
cmpl $0x0, 0x30(%rax)
je 0x25f24
movq 0xa491f(%rip), %rdx     # 0xca778
cmpl $0x0, 0x10(%rdx)
je 0x25ea4
movq %rbx, %rdi
callq 0x4c440
leaq 0x6f389(%rip), %rdi     # 0x951f7
callq 0x281da
movq %rbx, %rdi
movq %rax, %rsi
callq 0x2d894
jmp 0x26296
leaq -0x30ee(%rip), %rdx     # 0x22d9c
cmpq %rdx, 0x8(%rax)
je 0x25e27
movq %rbx, %rdi
callq 0x4c440
movq 0xa4a31(%rip), %rsi     # 0xca8d0
jmp 0x2628e
movq 0x38(%rbx), %rdx
leaq 0xc(%rsp), %r8
leaq 0x8(%rsp), %r9
movq %rbx, %rdi
movq %rax, %rsi
callq 0x562df
testq %rax, %rax
je 0x2619a
movq %rax, %r14
movq 0xa48a8(%rip), %rax     # 0xca778
cmpq (%rax), %r14
je 0x25f24
movq %rbx, %rdi
callq 0x4c440
callq 0x281b2
cmpl $0x3, 0x8(%rsp)
leaq 0x6f3f8(%rip), %rcx     # 0x952e6
leaq 0x6f3f5(%rip), %rdx     # 0x952ea
cmoveq %rcx, %rdx
movl 0xc(%rsp), %ecx
movq %r14, %r8
addq $0x888, %r8             # imm = 0x888
movl 0x8b8(%r14), %r9d
leaq 0x6f3c2(%rip), %rsi     # 0x952d7
movq %rax, %rdi
xorl %eax, %eax
callq 0x28760
jmp 0x25e73
cmpq $0x0, 0xa479c(%rip)     # 0xca6c8
je 0x25f58
callq 0x262cb
cmpl $-0x1, %eax
jne 0x25f58
movq 0x40(%rbx), %rax
movl 0x20(%rax), %eax
andl $0x4, %eax
je 0x25f58
movq %rbx, %rdi
callq 0x4c440
movq 0xa4995(%rip), %rsi     # 0xca8e8
jmp 0x2628e
cmpl $0x0, 0xa4605(%rip)     # 0xca564
setne %al
cmpl $0x0, 0xa45bf(%rip)     # 0xca528
setg %cl
andb %al, %cl
cmpl $-0x1, 0xa45eb(%rip)    # 0xca560
sete %al
andb %cl, %al
cmpl $-0x1, 0xa457b(%rip)    # 0xca4fc
sete %cl
orb %al, %cl
movq 0xa466b(%rip), %rax     # 0xca5f8
testq %rax, %rax
sete %dl
andb %cl, %dl
cmpb $0x1, %dl
jne 0x25fb9
movq 0x40(%rbx), %rcx
testb $0x1, 0x20(%rcx)
jne 0x2607a
leaq -0x31b1(%rip), %rdx     # 0x22dfe
cmpq %rdx, 0x8(%rcx)
je 0x2607a
movl 0xa4621(%rip), %ecx     # 0xca5e0
testl %ecx, %ecx
je 0x25ff2
cmpl $0x0, 0xa461a(%rip)     # 0xca5e4
je 0x25ff2
movq 0x40(%rbx), %rdx
testb $0x1, 0x20(%rdx)
je 0x25ff2
cmpl %ecx, 0xa460c(%rip)     # 0xca5e8
jge 0x25ff2
movq %rbx, %rdi
callq 0x4c440
movq 0xa48eb(%rip), %rsi     # 0xca8d8
jmp 0x2628e
testq %rax, %rax
je 0x26017
cmpl $0x0, 0xa4656(%rip)     # 0xca654
je 0x26017
testb $0x2, 0x90(%rbx)
jne 0x26017
movq 0x40(%rbx), %rcx
testb $0x1, 0x20(%rcx)
jne 0x261c4
movq 0x158(%rbx), %rcx
movq 0x28(%rcx), %rdx
addq 0x48(%rcx), %rdx
jne 0x2603a
movq 0x160(%rbx), %rcx
cmpq $0x0, 0x28(%rcx)
je 0x260c8
movq 0x40(%rbx), %rcx
movq 0x8(%rcx), %rcx
cmpq 0x9fef7(%rip), %rcx     # 0xc5f40
je 0x260c8
cmpq 0x9ff4e(%rip), %rcx     # 0xc5fa0
je 0x260c8
cmpq 0x9ff05(%rip), %rcx     # 0xc5f60
je 0x260c8
cmpq 0x9ff2c(%rip), %rcx     # 0xc5f90
je 0x260c8
leaq 0x6f2b1(%rip), %rsi     # 0x9531e
movq %rbx, %rdi
callq 0x2d9d7
jmp 0x26296
movq %rbx, %rdi
callq 0x4c440
cmpl $0x0, 0xa4473(%rip)     # 0xca4fc
je 0x2618e
callq 0x281b2
movq %rax, %r14
movl 0xa4463(%rip), %edi     # 0xca500
callq 0x1f630
leaq 0x6f247(%rip), %rsi     # 0x952f0
xorl %ebp, %ebp
movq %r14, %rdi
movq %rax, %rdx
xorl %eax, %eax
callq 0x28760
movq %rbx, %rdi
movq %rax, %rsi
callq 0x2d894
jmp 0x25e09
testq %rax, %rax
je 0x260fd
cmpl $0x5, 0xa4548(%rip)     # 0xca61c
je 0x260fd
cmpl $0x0, 0xa4573(%rip)     # 0xca650
jne 0x260fd
movq 0x40(%rbx), %rax
testb $0x4, 0x21(%rax)
jne 0x260fd
movq %rbx, %rdi
callq 0x4c440
movq 0xa47c0(%rip), %rsi     # 0xca8b8
jmp 0x2628e
cmpl $0x0, 0xa4194(%rip)     # 0xca298
je 0x2611c
movq 0x40(%rbx), %rax
testb $0x2, 0x21(%rax)
jne 0x2611c
movq 0xa4789(%rip), %rsi     # 0xca8a0
jmp 0x2628e
cmpl $0x0, 0xa469d(%rip)     # 0xca7c0
je 0x2614c
movq 0x40(%rbx), %rax
movq 0x8(%rax), %r14
leaq -0x3398(%rip), %rax     # 0x22d9c
cmpq %rax, %r14
sete %al
cmpq 0x9fe57(%rip), %r14     # 0xc5f98
sete %cl
orb %al, %cl
je 0x2621d
testb $0x8, 0x90(%rbx)
jne 0x261d0
movq %rbx, %rdi
movl $0x7, %esi
callq 0x25b37
movq 0xa442f(%rip), %rax     # 0xca598
movq %rax, 0x148(%rbx)
movq 0xa4571(%rip), %rax     # 0xca6e8
xorl %ebp, %ebp
cmpq $0x0, 0x28(%rax)
je 0x25e09
callq 0x3b18d
jmp 0x25e09
movq 0xa471b(%rip), %rsi     # 0xca8b0
jmp 0x2628e
movq %rbx, %rdi
callq 0x4c440
movl 0x8(%rsp), %eax
cmpl $0x2, %eax
je 0x2629d
cmpl $0x1, %eax
jne 0x262a9
leaq 0x6f082(%rip), %rdi     # 0x95241
jmp 0x25e6e
movq 0xa46f5(%rip), %rsi     # 0xca8c0
jmp 0x2628e
movq 0x40(%rbx), %rax
movq 0x8(%rax), %rax
cmpq 0x9fd99(%rip), %rax     # 0xc5f78
je 0x26155
cmpq 0x9fd44(%rip), %rax     # 0xc5f30
je 0x26155
cmpq 0x9fcf7(%rip), %rax     # 0xc5ef0
je 0x26155
cmpq 0x9fdf2(%rip), %rax     # 0xc5ff8
je 0x26155
movq %rbx, %rdi
callq 0x4c278
movq 0xa4645(%rip), %rsi     # 0xca860
jmp 0x2628e
cmpq 0x9fd84(%rip), %r14     # 0xc5fa8
jne 0x2624e
cmpl $0x2, 0x30(%rbx)
jne 0x2624e
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 0x2614c
cmpq 0x9fcbb(%rip), %r14     # 0xc5f10
jne 0x2627f
cmpl $0x2, 0x30(%rbx)
jne 0x2627f
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 0x2614c
movq %rbx, %rdi
callq 0x4c440
movq 0xa461a(%rip), %rsi     # 0xca8a8
movq %rbx, %rdi
callq 0x2d6f0
xorl %ebp, %ebp
jmp 0x25e09
leaq 0x6efd6(%rip), %rdi     # 0x9527a
jmp 0x25e6e
leaq 0x6f005(%rip), %rdi     # 0x952b5
leaq 0x7a3d9(%rip), %rsi     # 0xa0690
movl $0xa38, %edx            # imm = 0xA38
callq 0x4d0e5
movl $0x1, %edi
callq 0x1f3b0