// File: /workspace/github_c_makefile_star2_O0/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
movq %rsp, %rbp
subq $0x80, %rsp
movq %rdi, -0x8(%rbp)
movq -0x8(%rbp), %rax
movq 0x38(%rax), %rax
movq 0x8(%rax), %rax
movq 0x8(%rax), %rdi
leaq 0x82c47(%rip), %rsi     # 0xe50dd
callq 0x1f2f0
cmpl $0x0, %eax
jne 0x625c3
movq -0x8(%rbp), %rax
cmpl $0x2, 0x30(%rax)
je 0x624bc
movq -0x8(%rbp), %rax
cmpl $0x3, 0x30(%rax)
jne 0x625c3
movq -0x8(%rbp), %rax
cmpl $0x2, 0x30(%rax)
jne 0x624ce
xorl %eax, %eax
movq %rax, -0x58(%rbp)
jmp 0x624e2
movq -0x8(%rbp), %rax
movq 0x38(%rax), %rax
movq 0x10(%rax), %rax
movq 0x8(%rax), %rax
movq %rax, -0x58(%rbp)
movq -0x58(%rbp), %rax
movq %rax, -0x10(%rbp)
leaq 0xb1407(%rip), %rax     # 0x1138f8
movq 0x7a8(%rax), %rdi
callq 0x241d0
movq %rax, -0x18(%rbp)
movq $0x0, -0x28(%rbp)
movq -0x8(%rbp), %rdi
callq 0x36050
movq %rax, -0x30(%rbp)
movq -0x18(%rbp), %rdi
callq 0x24270
movq %rax, -0x20(%rbp)
cmpq $0x0, %rax
je 0x625a4
movq -0x20(%rbp), %rax
movq (%rax), %rax
movq %rax, -0x38(%rbp)
movq -0x38(%rbp), %rax
movq 0x8(%rax), %rax
movq %rax, -0x40(%rbp)
cmpq $0x0, -0x10(%rbp)
je 0x62586
movq -0x10(%rbp), %rax
movq %rax, -0x70(%rbp)
movq -0x10(%rbp), %rdi
callq 0x61860
movl %eax, -0x64(%rbp)
movq -0x40(%rbp), %rax
movq %rax, -0x60(%rbp)
movq -0x40(%rbp), %rdi
callq 0x61860
movq -0x70(%rbp), %rdi
movl -0x64(%rbp), %esi
movq -0x60(%rbp), %rdx
movl %eax, %ecx
xorl %r8d, %r8d
callq 0x38f60
cmpl $0x0, %eax
je 0x6259f
movq -0x8(%rbp), %rdi
movq -0x38(%rbp), %rsi
callq 0x36620
movq -0x28(%rbp), %rax
addq $0x1, %rax
movq %rax, -0x28(%rbp)
jmp 0x62516
movq -0x18(%rbp), %rdi
callq 0x243d0
movq -0x8(%rbp), %rdi
movq -0x30(%rbp), %rsi
movq -0x28(%rbp), %rdx
callq 0x360c0
jmp 0x62715
movq -0x8(%rbp), %rax
movq 0x38(%rax), %rax
movq 0x8(%rax), %rax
movq 0x8(%rax), %rdi
leaq 0x82b0c(%rip), %rsi     # 0xe50e6
callq 0x1f2f0
cmpl $0x0, %eax
jne 0x626a7
movq -0x8(%rbp), %rax
cmpl $0x2, 0x30(%rax)
jl 0x626a7
movq -0x8(%rbp), %rdi
movq -0x8(%rbp), %rax
movl 0x30(%rax), %eax
subl $0x2, %eax
shll %eax
movslq %eax, %rsi
callq 0x364f0
movl $0x2, -0x44(%rbp)
movl -0x44(%rbp), %eax
movq -0x8(%rbp), %rcx
cmpl 0x30(%rcx), %eax
jge 0x626a5
leaq 0xb12cc(%rip), %rax     # 0x1138f8
movq 0x7a8(%rax), %rdi
movq -0x8(%rbp), %rax
movq 0x38(%rax), %rax
movslq -0x44(%rbp), %rcx
movq (%rax,%rcx,8), %rsi
callq 0x24050
movq %rax, -0x50(%rbp)
movq -0x8(%rbp), %rdi
movq -0x8(%rbp), %rax
movq 0x38(%rax), %rax
movslq -0x44(%rbp), %rcx
movq (%rax,%rcx,8), %rsi
callq 0x36620
movq -0x8(%rbp), %rax
movq %rax, -0x78(%rbp)
cmpq $0x0, -0x50(%rbp)
je 0x62682
movq -0x50(%rbp), %rax
movq 0x28(%rax), %rax
movq %rax, -0x80(%rbp)
jmp 0x6268a
xorl %eax, %eax
movq %rax, -0x80(%rbp)
jmp 0x6268a
movq -0x78(%rbp), %rdi
movq -0x80(%rbp), %rsi
callq 0x366c0
movl -0x44(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x44(%rbp)
jmp 0x62615
jmp 0x62713
movq -0x8(%rbp), %rax
movq 0x38(%rax), %rax
movq 0x8(%rax), %rax
movq 0x8(%rax), %rdi
leaq 0x82a2f(%rip), %rsi     # 0xe50ed
callq 0x1f2f0
cmpl $0x0, %eax
jne 0x626ef
movq -0x8(%rbp), %rax
cmpl $0x2, 0x30(%rax)
jne 0x626ef
movq -0x8(%rbp), %rdi
leaq 0xb121b(%rip), %rax     # 0x1138f8
movq 0x7b0(%rax), %rax
movq 0x28(%rax), %rsi
callq 0x36480
jmp 0x62711
movq -0x8(%rbp), %rdi
movq -0x8(%rbp), %rax
movq 0x38(%rax), %rax
movq 0x8(%rax), %rax
movq 0x8(%rax), %rdx
leaq 0x829ea(%rip), %rsi     # 0xe50f4
movb $0x0, %al
callq 0x35d80
jmp 0x62713
jmp 0x62715
addq $0x80, %rsp
popq %rbp
retq
nop