Redis-压缩表-zipTryEncoding详解

本文深入探讨了Redis中压缩表(ziplist)的数据结构,特别是zipTryEncoding函数,该函数用于尝试对值进行高效编码以节省内存。我们将分析其工作原理,以及在不同场景下如何优化内存使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/* 判断长度为entrylen的entry字符串能否转换为数值,转换结果保存在v中 编码方式保存在encoding中 */
static int zipTryEncoding(unsigned char *entry, unsigned int entrylen, long long *v, unsigned char *encoding) {
    long long value;

    if (entrylen >= 32 || entrylen == 0)
        return 0;

    if (string2ll((char *)entry, entrylen, &value)) {
        if (value >= 0 && value <= 12) {
            *encoding = ZIP_INT_IMM_MIN + value;
        } else if (value >= INT8_MIN && value <= INT8_MAX) {
            *encoding = ZIP_INT_8B;
        } else if (value >= INT16_MIN && value <= INT16_MAX) {
            *encoding = ZIP_INT_16B;
        } else if (value >= INT24_MIN && value <= INT24_MAX) {
            *encoding = ZIP_INT_24B;
        } else if (value >= INT32_MIN && value <= INT32_MAX) {
            *encoding = ZIP_INT_32B;
        } else {
            *encoding = ZIP_INT_64B;
        }

        *v = value;
        return 1;
    }

    return 0;
}

/* 将长度为slen的字符串s转换为long long类型的整数,
成功:返回1,
失败:返回0 */
int string2ll(const char *s, size_t slen, long long *value) {
    const char *p = s;
    size_t plen = 0;
    int negative = 0;
    unsigned long long v; /* 注意v的数据类型是无符号的,返回值value是有符号的,两者表示的数的范围是不一样的 */

    /* 字符串长度为0,无需进行转换 */
    if (plen == slen)
        return 0;

    /* 字符串长度为1,且第一个字符为0,直接转换为0 
    认为此处的判断可以省略,因为下文中有该条件的判断,且放在下面是合理的 */
    if (slen == 1 && p[0] == '0') {
        if (value != NULL)
            *value = 0;
        return 1;
    }

    /* 如果为负数,记录负数标识,
    如果只有一个符号,即字符串长度为1,函数返回 */
    if (p[0] == '-') {
        negative = 1;
        p++;
        plen++;
        if (plen == slen)
            return 1;
    }

    /* 判断字符串中字符是否是数字字符 */
    if (p[0] >= '1' && p[0] <= '9') { /* 第一个字符是数字字符'1'到'9',转换继续 */
        v = p[0] - '0';
        p++;
        plen++;
    } else if (p[0] == '0' && slen == 1) { /* 第一个字符是数字字符'0'且长度为1时,转换结果是0 */
        *value = 0;
        return 1;
    } else { /* 第一个字符不是数字字符,直接返回0 */
        return 0;
    }

    /* 循环进行字符串转换为数字 */
    while (plen < slen && p[0] >= '0' && p[0] <= '9') {
        /* 因为下一步操作是v *= 10,所以在执行下一步的操作之前,判断如果执行下一步操作是否会出现溢出*/
        if (v > (ULLONG_MAX / 10)) /* 如果可能溢出,则返回0 */
            return 0;
        v *= 10; /* 字符串中先出现的字符是高位,所以先把已经转换好的数值*10 */

        if (v > (ULLONG_MAX - (p[0] - '0'))) /* 进行下一步操作之前首先判断是否会溢出 */
            return 0;
        v += p[0] - '0';

        p++; /* 字符往下走一个,已经处理的字符长度也+1 */
        plen++;
    }

    if (plen < slen) /* 说明字符串还没有完全转换为整数的时候,出现了非数字字符,程序返回0 */
        return 0;

    /* 对函数的正负数进行处理 */
    if (negative) {
        /* 如果是负数,先判断v变成负数之前是否会溢出 
        这里需要理解的是,为什么判断条件不是v > (unsigned long long)(-LLONG_MIN)?
        理解:以一个有符号的1字节数为例,其取值范围是-128~127,如果是最小值直接取反,
        即-(-128)=128,因为最大值是127,此时就出现了溢出。
        这里最后+1的操作,使用了运算的方式,将数值转换为无符号数,就不会导致数值的溢出 */
        /* 一下是测试的程序:
        #include <stdio.h>
        #include <limits.h>
        
        int main()
        {
            printf("%lld\n", LLONG_MIN);
            printf("%lld", (unsigned long long)(-LLONG_MIN));
            printf("%lld\n", ((unsigned long long)(-(LLONG_MIN + 1)) + 1));
            return 0;
        }
        执行结果:
        limit.c: In function ‘main’:
        limit.c:7:41: warning: integer overflow in expression [-Woverflow]
        printf("%lld", (unsigned long long)(-LLONG_MIN));*/
        if (v > ((unsigned long long)(-(LLONG_MIN + 1)) + 1))
            return 0;
        if (value != NULL)
            *value = -v;
    } else {
        if (v > LLONG_MAX) /* 如果是整数,判断是否大于有符号数的最大值,大于的话,直接返回0 */
            return 0;
        if (value != NULL)
            *value = v;
    }

    return 1;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值