Redis-壓縮表-zipTryEncoding詳解

/* 判斷長度爲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;
}

 

發佈了104 篇原創文章 · 獲贊 13 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章