// File: /workspace/github_c_makefile_star2_O2/nilyang[P]redis-tdd-annotation/src/rdb.c
// Function: rdbLoad
int rdbLoad(char *filename) {
    uint32_t dbid;
    int type, rdbver;
    redisDb *db = server.db+0;
    char buf[1024];
    long long expiretime, now = mstime();
    FILE *fp;
    rio rdb;

    // 打开 rdb 文件
    if ((fp = fopen(filename,"r")) == NULL) return REDIS_ERR;

    // 初始化写入流
    rioInitWithFile(&rdb,fp);
    rdb.update_cksum = rdbLoadProgressCallback;
    rdb.max_processing_chunk = server.loading_process_events_interval_bytes;
    if (rioRead(&rdb,buf,9) == 0) goto eoferr;
    buf[9] = '\0';

    // 检查版本号
    if (memcmp(buf,"REDIS",5) != 0) {
        fclose(fp);
        redisLog(REDIS_WARNING,"Wrong signature trying to load DB from file");
        errno = EINVAL;
        return REDIS_ERR;
    }
    rdbver = atoi(buf+5);
    if (rdbver < 1 || rdbver > REDIS_RDB_VERSION) {
        fclose(fp);
        redisLog(REDIS_WARNING,"Can't handle RDB format version %d",rdbver);
        errno = EINVAL;
        return REDIS_ERR;
    }

    // 将服务器状态调整到开始载入状态
    startLoading(fp);
    while(1) {
        robj *key, *val;
        expiretime = -1;

        /* Read type. 
         *
         * 读入类型指示，决定该如何读入之后跟着的数据。
         *
         * 这个指示可以是 rdb.h 中定义的所有以
         * REDIS_RDB_TYPE_* 为前缀的常量的其中一个
         * 或者所有以 REDIS_RDB_OPCODE_* 为前缀的常量的其中一个
         */
        if ((type = rdbLoadType(&rdb)) == -1) goto eoferr;

        // 读入过期时间值
        if (type == REDIS_RDB_OPCODE_EXPIRETIME) {

            // 以秒计算的过期时间

            if ((expiretime = rdbLoadTime(&rdb)) == -1) goto eoferr;

            /* We read the time so we need to read the object type again. 
             *
             * 在过期时间之后会跟着一个键值对，我们要读入这个键值对的类型
             */
            if ((type = rdbLoadType(&rdb)) == -1) goto eoferr;

            /* the EXPIRETIME opcode specifies time in seconds, so convert
             * into milliseconds. 
             *
             * 将格式转换为毫秒*/
            expiretime *= 1000;
        } else if (type == REDIS_RDB_OPCODE_EXPIRETIME_MS) {

            // 以毫秒计算的过期时间

            /* Milliseconds precision expire times introduced with RDB
             * version 3. */
            if ((expiretime = rdbLoadMillisecondTime(&rdb)) == -1) goto eoferr;

            /* We read the time so we need to read the object type again.
             *
             * 在过期时间之后会跟着一个键值对，我们要读入这个键值对的类型
             */
            if ((type = rdbLoadType(&rdb)) == -1) goto eoferr;
        }
            
        // 读入数据 EOF （不是 rdb 文件的 EOF）
        if (type == REDIS_RDB_OPCODE_EOF)
            break;

        /* Handle SELECT DB opcode as a special case 
         *
         * 读入切换数据库指示
         */
        if (type == REDIS_RDB_OPCODE_SELECTDB) {

            // 读入数据库号码
            if ((dbid = rdbLoadLen(&rdb,NULL)) == REDIS_RDB_LENERR)
                goto eoferr;

            // 检查数据库号码的正确性
            if (dbid >= (unsigned)server.dbnum) {
                redisLog(REDIS_WARNING,"FATAL: Data file was created with a Redis server configured to handle more than %d databases. Exiting\n", server.dbnum);
                exit(1);
            }

            // 在程序内容切换数据库
            db = server.db+dbid;

            // 跳过
            continue;
        }

        /* Read key 
         *
         * 读入键
         */
        if ((key = rdbLoadStringObject(&rdb)) == NULL) goto eoferr;

        /* Read value 
         *
         * 读入值
         */
        if ((val = rdbLoadObject(type,&rdb)) == NULL) goto eoferr;

        /* Check if the key already expired. This function is used when loading
         * an RDB file from disk, either at startup, or when an RDB was
         * received from the master. In the latter case, the master is
         * responsible for key expiry. If we would expire keys here, the
         * snapshot taken by the master may not be reflected on the slave. 
         *
         * 如果服务器为主节点的话，
         * 那么在键已经过期的时候，不再将它们关联到数据库中去
         */
        if (server.masterhost == NULL && expiretime != -1 && expiretime < now) {
            decrRefCount(key);
            decrRefCount(val);
            // 跳过
            continue;
        }

        /* Add the new object in the hash table 
         *
         * 将键值对关联到数据库中
         */
        dbAdd(db,key,val);

        /* Set the expire time if needed 
         *
         * 设置过期时间
         */
        if (expiretime != -1) setExpire(db,key,expiretime);

        decrRefCount(key);
    }

    /* Verify the checksum if RDB version is >= 5 
     *
     * 如果 RDB 版本 >= 5 ，那么比对校验和
     */
    if (rdbver >= 5 && server.rdb_checksum) {
        uint64_t cksum, expected = rdb.cksum;

        // 读入文件的校验和
        if (rioRead(&rdb,&cksum,8) == 0) goto eoferr;
        memrev64ifbe(&cksum);

        // 比对校验和
        if (cksum == 0) {
            redisLog(REDIS_WARNING,"RDB file was saved with checksum disabled: no check performed.");
        } else if (cksum != expected) {
            redisLog(REDIS_WARNING,"Wrong RDB checksum. Aborting now.");
            exit(1);
        }
    }

    // 关闭 RDB 
    fclose(fp);

    // 服务器从载入状态中退出
    stopLoading();

    return REDIS_OK;

eoferr: /* unexpected end of file is handled here with a fatal exit */
    redisLog(REDIS_WARNING,"Short read or OOM loading DB. Unrecoverable error, aborting now.");
    exit(1);
    return REDIS_ERR; /* Just to avoid warning */
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x478, %rsp            # imm = 0x478
movq %rdi, %rbx
leaq 0x91502(%rip), %r12     # 0xc6fa8
movq 0x10(%r12), %r15
callq 0x22843
movq %rax, %r14
leaq 0x57119(%rip), %rsi     # 0x8cbd3
movq %rbx, %rdi
callq 0x1fa60
pushq $-0x1
popq %rbp
testq %rax, %rax
je 0x35b88
leaq 0x20(%rsp), %rbx
movq %rbx, %rdi
movq %rax, 0x8(%rsp)
movq %rax, %rsi
callq 0x541e8
leaq -0xe8(%rip), %rax       # 0x35a02
movq %rax, 0x18(%rbx)
movq 0x310(%r12), %rax
movq %rax, 0x30(%rbx)
leaq 0x70(%rsp), %rsi
pushq $0x9
popq %rdx
movq %rbx, %rdi
callq 0x33f11
testq %rax, %rax
je 0x35d8c
leaq 0x70(%rsp), %rdi
movb $0x0, 0x9(%rdi)
leaq 0x59142(%rip), %rsi     # 0x8ec65
pushq $0x5
popq %rdx
callq 0x1f6a0
testl %eax, %eax
je 0x35b4c
movq 0x8(%rsp), %rdi
callq 0x1f9a0
leaq 0x5912b(%rip), %rsi     # 0x8ec6b
pushq $0x3
popq %rdi
xorl %eax, %eax
callq 0x22613
jmp 0x35b7d
leaq 0x75(%rsp), %rdi
callq 0x1f710
movl %eax, %ebx
leal -0x7(%rbx), %eax
cmpl $-0x7, %eax
movq 0x8(%rsp), %rdi
ja 0x35b9c
callq 0x1f9a0
leaq 0x59126(%rip), %rsi     # 0x8ec97
pushq $0x3
popq %rdi
movl %ebx, %edx
xorl %eax, %eax
callq 0x22613
callq 0x1f810
movl $0x16, (%rax)
movl %ebp, %eax
addq $0x478, %rsp            # imm = 0x478
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq %rbx, 0x10(%rsp)
callq 0x3595a
leaq 0x20(%rsp), %r13
movq %r13, %rdi
callq 0x33ee7
cmpl $0xfc, %eax
je 0x35c05
movl %eax, %r12d
cmpl $0xfd, %eax
je 0x35bd3
cmpl $-0x1, %r12d
je 0x35d8c
pushq $-0x1
popq %rbp
jmp 0x35c2e
movq %r13, %rdi
callq 0x33f84
cmpq $-0x1, %rax
je 0x35d8c
movq %rax, %rbx
movq %r13, %rdi
callq 0x33ee7
cmpl $-0x1, %eax
je 0x35d8c
movl %eax, %r12d
imulq $0x3e8, %rbx, %rbp      # imm = 0x3E8
jmp 0x35c2e
movq %r13, %rdi
callq 0x33fc5
cmpq $-0x1, %rax
je 0x35d8c
movq %rax, %rbp
movq %r13, %rdi
callq 0x33ee7
cmpl $-0x1, %eax
je 0x35d8c
movl %eax, %r12d
cmpl $0xff, %r12d
je 0x35d14
movq %r13, %rdi
cmpl $0xfe, %r12d
je 0x35cdf
callq 0x346cf
testq %rax, %rax
je 0x35d8c
movq %rax, %rbx
movl %r12d, %edi
movq %r13, %rsi
callq 0x3522d
testq %rax, %rax
je 0x35d8c
movq %rax, %r12
leaq 0x9132e(%rip), %rax     # 0xc6fa8
cmpq $0x0, 0x650(%rax)
sete %al
cmpq $-0x1, %rbp
setne %cl
cmpq %r14, %rbp
setl %dl
andb %cl, %dl
andb %al, %dl
cmpb $0x1, %dl
jne 0x35cb0
movq %rbx, %rdi
callq 0x2ee1f
movq %r12, %rdi
callq 0x2ee1f
jmp 0x35bab
movq %r15, %rdi
movq %rbx, %rsi
movq %r12, %rdx
callq 0x2fa09
cmpq $-0x1, %rbp
je 0x35cd2
movq %r15, %rdi
movq %rbx, %rsi
movq %rbp, %rdx
callq 0x30b2d
movq %rbx, %rdi
callq 0x2ee1f
jmp 0x35bab
xorl %esi, %esi
callq 0x34096
cmpl $-0x1, %eax
je 0x35d8c
leaq 0x912b2(%rip), %rcx     # 0xc6fa8
movl 0x480(%rcx), %edx
cmpl %edx, %eax
jae 0x35da5
movl %eax, %r15d
shlq $0x6, %r15
addq 0x10(%rcx), %r15
jmp 0x35bab
cmpl $0x5, 0x10(%rsp)
leaq 0x91288(%rip), %r14     # 0xc6fa8
movq 0x8(%rsp), %r15
jl 0x35d75
cmpl $0x0, 0x594(%r14)
je 0x35d75
leaq 0x20(%rsp), %rdi
movq 0x20(%rdi), %rbx
leaq 0x18(%rsp), %rsi
pushq $0x8
popq %rdx
callq 0x33f11
testq %rax, %rax
je 0x35d8c
movq 0x18(%rsp), %rax
testq %rax, %rax
je 0x35d64
cmpq %rbx, %rax
je 0x35d75
leaq 0x58ffe(%rip), %rsi     # 0x8ed60
jmp 0x35d93
leaq 0x58fb6(%rip), %rsi     # 0x8ed21
pushq $0x3
popq %rdi
xorl %eax, %eax
callq 0x22613
movq %r15, %rdi
callq 0x1f9a0
andl $0x0, 0x2f0(%r14)
xorl %ebp, %ebp
jmp 0x35b88
leaq 0x58fef(%rip), %rsi     # 0x8ed82
pushq $0x3
popq %rdi
xorl %eax, %eax
callq 0x22613
pushq $0x1
popq %rdi
callq 0x1f2b0
leaq 0x58f0e(%rip), %rsi     # 0x8ecba
pushq $0x3
popq %rdi
xorl %eax, %eax
callq 0x22613
jmp 0x35d9d