// File: /workspace/github_c_makefile_star2_O1/nilyang[P]redis-tdd-annotation/src/pubsub.c
// Function: pubsubCommand
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);
    }
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
pushq %rax
movq %rdi, %rbx
movq 0x38(%rdi), %r15
movq 0x8(%r15), %rax
movq 0x8(%rax), %r14
leaq 0x4efe1(%rip), %rsi     # 0x9b00d
movq %r14, %rdi
callq 0x1f2e0
testl %eax, %eax
jne 0x4c056
movl 0x30(%rbx), %eax
movl %eax, %ecx
andl $-0x2, %ecx
cmpl $0x2, %ecx
jne 0x4c056
cmpl $0x2, %eax
jne 0x4c152
xorl %r14d, %r14d
jmp 0x4c15a
leaq 0x4efb9(%rip), %rsi     # 0x9b016
movq %r14, %rdi
callq 0x1f2e0
testl %eax, %eax
jne 0x4c0ef
movl 0x30(%rbx), %eax
cmpl $0x2, %eax
jl 0x4c0ef
leal -0x4(,%rax,2), %esi
movq %rbx, %rdi
callq 0x2ded8
cmpl $0x3, 0x30(%rbx)
jl 0x4c0e0
movl $0x2, %r15d
leaq 0x7df11(%rip), %r12     # 0xc9fa8
movq 0x7a8(%r12), %rdi
movq 0x38(%rbx), %rax
movq (%rax,%r15,8), %rsi
callq 0x22813
movq %rax, %r14
movq 0x38(%rbx), %rax
movq (%rax,%r15,8), %rsi
movq %rbx, %rdi
callq 0x2df74
testq %r14, %r14
je 0x4c0ca
movq 0x28(%r14), %rsi
jmp 0x4c0cc
xorl %esi, %esi
movq %rbx, %rdi
callq 0x2dfe7
incq %r15
movslq 0x30(%rbx), %rax
cmpq %rax, %r15
jl 0x4c097
addq $0x8, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
leaq 0x4ef27(%rip), %rsi     # 0x9b01d
movq %r14, %rdi
callq 0x1f2e0
testl %eax, %eax
jne 0x4c130
cmpl $0x2, 0x30(%rbx)
jne 0x4c130
leaq 0x7de99(%rip), %rax     # 0xc9fa8
movq 0x7b0(%rax), %rax
movq 0x28(%rax), %rsi
movq %rbx, %rdi
addq $0x8, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
jmp 0x2dea3
leaq 0x4eeed(%rip), %rsi     # 0x9b024
movq %rbx, %rdi
movq %r14, %rdx
xorl %eax, %eax
addq $0x8, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
jmp 0x2d9fe
movq 0x10(%r15), %rax
movq 0x8(%rax), %r14
leaq 0x7de47(%rip), %rax     # 0xc9fa8
movq 0x7a8(%rax), %rdi
callq 0x228b5
movq %rax, %r15
movq %rbx, %rdi
callq 0x2dc0a
movq %rax, %r12
movq %r15, %rdi
callq 0x22913
xorl %r13d, %r13d
testq %rax, %rax
je 0x4c1c5
movq (%rax), %rbp
testq %r14, %r14
je 0x4c1ad
movq 0x8(%rbp), %rdx
movl -0x8(%r14), %esi
movl -0x8(%rdx), %ecx
movq %r14, %rdi
xorl %r8d, %r8d
callq 0x2f97c
testl %eax, %eax
je 0x4c1bb
movq %rbx, %rdi
movq %rbp, %rsi
callq 0x2df74
incq %r13
movq %r15, %rdi
callq 0x22913
jmp 0x4c186
movq %r15, %rdi
callq 0x229b9
movq %rbx, %rdi
movq %r12, %rsi
movq %r13, %rdx
addq $0x8, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
jmp 0x2dc46
nopl (%rax)