// File: /workspace/github_c_makefile_star2_O0/nilyang[P]redis-tdd-annotation/src/networking.c
// Function: sendReplyToClient
void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask) {
    redisClient *c = privdata;
    int nwritten = 0, totwritten = 0, objlen;
    size_t objmem;
    robj *o;
    REDIS_NOTUSED(el);
    REDIS_NOTUSED(mask);

    // 一直循环，直到回复缓冲区为空
    // 或者指定条件满足为止
    while(c->bufpos > 0 || listLength(c->reply)) {

        if (c->bufpos > 0) {

            // c->bufpos > 0

            // 写入内容到套接字
            // c->sentlen 是用来处理 short write 的
            // 当出现 short write ，导致写入未能一次完成时，
            // c->buf+c->sentlen 就会偏移到正确（未写入）内容的位置上。
            nwritten = write(fd,c->buf+c->sentlen,c->bufpos-c->sentlen);
            // 出错则跳出
            if (nwritten <= 0) break;
            // 成功写入则更新写入计数器变量
            c->sentlen += nwritten;
            totwritten += nwritten;

            /* If the buffer was sent, set bufpos to zero to continue with
             * the remainder of the reply. */
            // 如果缓冲区中的内容已经全部写入完毕
            // 那么清空客户端的两个计数器变量
            if (c->sentlen == c->bufpos) {
                c->bufpos = 0;
                c->sentlen = 0;
            }
        } else {

            // listLength(c->reply) != 0

            // 取出位于链表最前面的对象
            o = listNodeValue(listFirst(c->reply));
            objlen = sdslen(o->ptr);
            objmem = getStringObjectSdsUsedMemory(o);

            // 略过空对象
            if (objlen == 0) {
                listDelNode(c->reply,listFirst(c->reply));
                c->reply_bytes -= objmem;
                continue;
            }

            // 写入内容到套接字
            // c->sentlen 是用来处理 short write 的
            // 当出现 short write ，导致写入未能一次完成时，
            // c->buf+c->sentlen 就会偏移到正确（未写入）内容的位置上。
            nwritten = write(fd, ((char*)o->ptr)+c->sentlen,objlen-c->sentlen);
            // 写入出错则跳出
            if (nwritten <= 0) break;
            // 成功写入则更新写入计数器变量
            c->sentlen += nwritten;
            totwritten += nwritten;

            /* If we fully sent the object on head go to the next one */
            // 如果缓冲区内容全部写入完毕，那么删除已写入完毕的节点
            if (c->sentlen == objlen) {
                listDelNode(c->reply,listFirst(c->reply));
                c->sentlen = 0;
                c->reply_bytes -= objmem;
            }
        }
        /* Note that we avoid to send more than REDIS_MAX_WRITE_PER_EVENT
         * bytes, in a single threaded server it's a good idea to serve
         * other clients as well, even if a very large request comes from
         * super fast link that is always able to accept data (in real world
         * scenario think about 'KEYS *' against the loopback interface).
         *
         * 为了避免一个非常大的回复独占服务器，
         * 当写入的总数量大于 REDIS_MAX_WRITE_PER_EVENT ，
         * 临时中断写入，将处理时间让给其他客户端，
         * 剩余的内容等下次写入就绪再继续写入
         *
         * However if we are over the maxmemory limit we ignore that and
         * just deliver as much data as it is possible to deliver. 
         *
         * 不过，如果服务器的内存占用已经超过了限制，
         * 那么为了将回复缓冲区中的内容尽快写入给客户端，
         * 然后释放回复缓冲区的空间来回收内存，
         * 这时即使写入量超过了 REDIS_MAX_WRITE_PER_EVENT ，
         * 程序也继续进行写入
         */
        if (totwritten > REDIS_MAX_WRITE_PER_EVENT &&
            (server.maxmemory == 0 ||
             zmalloc_used_memory() < server.maxmemory)) break;
    }

    // 写入出错检查
    if (nwritten == -1) {
        if (errno == EAGAIN) {
            nwritten = 0;
        } else {
            redisLog(REDIS_VERBOSE,
                "Error writing to client: %s", strerror(errno));
            freeClient(c);
            return;
        }
    }

    if (totwritten > 0) {
        /* For clients representing masters we don't count sending data
         * as an interaction, since we always send REPLCONF ACK commands
         * that take some time to just fill the socket output buffer.
         * We just rely on data / pings received for timeout detection. */
        if (!(c->flags & REDIS_MASTER)) c->lastinteraction = server.unixtime;
    }
    if (c->bufpos == 0 && listLength(c->reply) == 0) {
        c->sentlen = 0;

        // 删除 write handler
        aeDeleteFileEvent(server.el,c->fd,AE_WRITABLE);

        /* Close connection after entire reply has been sent. */
        // 如果指定了写入之后关闭客户端 FLAG ，那么关闭客户端
        if (c->flags & REDIS_CLOSE_AFTER_REPLY) freeClient(c);
    }
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x50, %rsp
movq %rdi, -0x8(%rbp)
movl %esi, -0xc(%rbp)
movq %rdx, -0x18(%rbp)
movl %ecx, -0x1c(%rbp)
movq -0x18(%rbp), %rax
movq %rax, -0x28(%rbp)
movl $0x0, -0x2c(%rbp)
movl $0x0, -0x30(%rbp)
movq -0x28(%rbp), %rcx
movb $0x1, %al
cmpl $0x0, 0x170(%rcx)
movb %al, -0x49(%rbp)
jg 0x35091
movq -0x28(%rbp), %rax
movq 0x60(%rax), %rax
cmpq $0x0, 0x28(%rax)
setne %al
movb %al, -0x49(%rbp)
movb -0x49(%rbp), %al
testb $0x1, %al
jne 0x3509d
jmp 0x35261
movq -0x28(%rbp), %rax
cmpl $0x0, 0x170(%rax)
jle 0x35135
movl -0xc(%rbp), %edi
movq -0x28(%rbp), %rsi
addq $0x174, %rsi            # imm = 0x174
movq -0x28(%rbp), %rax
movslq 0x70(%rax), %rax
addq %rax, %rsi
movq -0x28(%rbp), %rax
movl 0x170(%rax), %eax
movq -0x28(%rbp), %rcx
subl 0x70(%rcx), %eax
movslq %eax, %rdx
callq 0x1fb50
movl %eax, -0x2c(%rbp)
cmpl $0x0, -0x2c(%rbp)
jg 0x350ee
jmp 0x35261
movl -0x2c(%rbp), %ecx
movq -0x28(%rbp), %rax
addl 0x70(%rax), %ecx
movl %ecx, 0x70(%rax)
movl -0x2c(%rbp), %eax
addl -0x30(%rbp), %eax
movl %eax, -0x30(%rbp)
movq -0x28(%rbp), %rax
movl 0x70(%rax), %eax
movq -0x28(%rbp), %rcx
cmpl 0x170(%rcx), %eax
jne 0x35130
movq -0x28(%rbp), %rax
movl $0x0, 0x170(%rax)
movq -0x28(%rbp), %rax
movl $0x0, 0x70(%rax)
jmp 0x3522b
movq -0x28(%rbp), %rax
movq 0x60(%rax), %rax
movq (%rax), %rax
movq 0x10(%rax), %rax
movq %rax, -0x48(%rbp)
movq -0x48(%rbp), %rax
movq 0x8(%rax), %rdi
callq 0x34800
movl %eax, -0x34(%rbp)
movq -0x48(%rbp), %rdi
callq 0x34860
movq %rax, -0x40(%rbp)
cmpl $0x0, -0x34(%rbp)
jne 0x3519b
movq -0x28(%rbp), %rax
movq 0x60(%rax), %rdi
movq -0x28(%rbp), %rax
movq 0x60(%rax), %rax
movq (%rax), %rsi
callq 0x20170
movq -0x40(%rbp), %rdx
movq -0x28(%rbp), %rax
movq 0x68(%rax), %rcx
subq %rdx, %rcx
movq %rcx, 0x68(%rax)
jmp 0x3506c
movl -0xc(%rbp), %edi
movq -0x48(%rbp), %rax
movq 0x8(%rax), %rsi
movq -0x28(%rbp), %rax
movslq 0x70(%rax), %rax
addq %rax, %rsi
movl -0x34(%rbp), %eax
movq -0x28(%rbp), %rcx
subl 0x70(%rcx), %eax
movslq %eax, %rdx
callq 0x1fb50
movl %eax, -0x2c(%rbp)
cmpl $0x0, -0x2c(%rbp)
jg 0x351d1
jmp 0x35261
movl -0x2c(%rbp), %ecx
movq -0x28(%rbp), %rax
addl 0x70(%rax), %ecx
movl %ecx, 0x70(%rax)
movl -0x2c(%rbp), %eax
addl -0x30(%rbp), %eax
movl %eax, -0x30(%rbp)
movq -0x28(%rbp), %rax
movl 0x70(%rax), %eax
cmpl -0x34(%rbp), %eax
jne 0x35229
movq -0x28(%rbp), %rax
movq 0x60(%rax), %rdi
movq -0x28(%rbp), %rax
movq 0x60(%rax), %rax
movq (%rax), %rsi
callq 0x20170
movq -0x28(%rbp), %rax
movl $0x0, 0x70(%rax)
movq -0x40(%rbp), %rdx
movq -0x28(%rbp), %rax
movq 0x68(%rax), %rcx
subq %rdx, %rcx
movq %rcx, 0x68(%rax)
jmp 0x3522b
cmpl $0x10000, -0x30(%rbp)   # imm = 0x10000
jle 0x3525c
leaq 0xde6bd(%rip), %rax     # 0x1138f8
cmpq $0x0, 0x720(%rax)
je 0x3525a
callq 0x2e480
leaq 0xde6a7(%rip), %rcx     # 0x1138f8
cmpq 0x720(%rcx), %rax
jae 0x3525c
jmp 0x35261
jmp 0x3506c
cmpl $-0x1, -0x2c(%rbp)
jne 0x352ac
callq 0x1f820
cmpl $0xb, (%rax)
jne 0x3527a
movl $0x0, -0x2c(%rbp)
jmp 0x352aa
callq 0x1f820
movl (%rax), %edi
callq 0x1f630
movq %rax, %rdx
movl $0x1, %edi
leaq 0xabcba(%rip), %rsi     # 0xe0f4f
movb $0x0, %al
callq 0x24fb0
movq -0x28(%rbp), %rdi
callq 0x36a80
jmp 0x3533e
jmp 0x352ac
cmpl $0x0, -0x30(%rbp)
jle 0x352df
movq -0x28(%rbp), %rax
movl 0x90(%rax), %eax
andl $0x2, %eax
cmpl $0x0, %eax
jne 0x352dd
leaq 0xde62d(%rip), %rax     # 0x1138f8
movq 0x798(%rax), %rcx
movq -0x28(%rbp), %rax
movq %rcx, 0x80(%rax)
jmp 0x352df
movq -0x28(%rbp), %rax
cmpl $0x0, 0x170(%rax)
jne 0x3533e
movq -0x28(%rbp), %rax
movq 0x60(%rax), %rax
cmpq $0x0, 0x28(%rax)
jne 0x3533e
movq -0x28(%rbp), %rax
movl $0x0, 0x70(%rax)
leaq 0xde5eb(%rip), %rax     # 0x1138f8
movq 0x28(%rax), %rdi
movq -0x28(%rbp), %rax
movl (%rax), %esi
movl $0x2, %edx
callq 0x20c60
movq -0x28(%rbp), %rax
movl 0x90(%rax), %eax
andl $0x40, %eax
cmpl $0x0, %eax
je 0x3533c
movq -0x28(%rbp), %rdi
callq 0x36a80
jmp 0x3533e
addq $0x50, %rsp
popq %rbp
retq
nopw %cs:(%rax,%rax)