天天看點

Redis-壓縮表-__ziplistDelete詳解

/* 從位置p開始,連續删除num個結點
傳回删除結點之後的壓縮表 */
static unsigned char *__ziplistDelete(unsigned char *zl, unsigned char *p, unsigned int num) {
    unsigned int i, totlen, deleted = 0;
    size_t offset;
    int nextdiff = 0;
    zlentry first, tail;

    /* 計算被删除結點占用記憶體數 */
    first = zipEntry(p); /* 利用first.p儲存p結點的位置,而沒有額外設定一個變量儲存p位置 */
    for (i = 0; p[0] != ZIP_END && i < num; i++) {
        p += zipRawEntryLength(p); /* 函數zipRawEntryLength功能:計算p位置的結點占用的記憶體數 */
        deleted++; /* 統計可删除的結點數,可能在删除num結點之前,p位置已經到達壓縮表表尾,此時deleted < num */
    } /* 循環結束之後,p指向需要删除的所有結點的下一個結點的位置 */

    totlen = p - first.p; /* totlen表示需要删除的記憶體位元組數 */
    if (totlen > 0) { /* 需要删除的記憶體位元組數必須大于0才會進行處理 */
        if (p[0] != ZIP_END) { /* p指向一個有效結點,即需要删除的結點之後還有結點 */

            /* 需要判斷p儲存前一個結點的記憶體長度是否适合儲存需要删除的第一個結點的前一個結點的長度,是以下面計算記憶體長度內插補點 */
            nextdiff = zipPrevLenByteDiff(p, first.prevrawlen);/* nextdiff=仍需記憶體位元組數=實際所需記憶體位元組數-已有記憶體位元組數
            編碼前置結點長度所需記憶體位元組數為1或者5,nextdiff=0/-4/4*/

            /* 既然需要删除一些記憶體空間,那被删除的記憶體空間和随意使用,到時候已經使用的記憶體空間不釋放掉即可 */
            p -= nextdiff; /* p需要增加記憶體位元組數為nextdiff,是以這裡p-nextdiff,使得指針前移為p指向的結點獲得更大的記憶體空間 */

            zipPrevEncodeLength(p, first.prevrawlen); /* 将前置結點的長度編碼進p中 */

            /* 更新到達壓縮表中最後一個結點的偏移量,此時的計算可能是不準确的,因為nextdiff可能增加或者減少了totlen的長度 */
            ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl) - totlen));

            /* 以下代碼塊的邏輯不明白:如果删除後的第一個結點的記憶體有變化,此時不管該結點是不是最後一個結點,
            指向壓縮表最後一個結點的偏移都不是-totlen不是嗎?*/
            tail = zipEntry(p); /* 獲得目前p指向的結點資訊 */
            if (p[tail.headersize + tail.len] != ZIP_END) { /* 判斷p結點之後還有沒有結點 */
                /* 如果p結點之後還有結點,此時更新到達壓縮表的最後一個結點的偏移?WHY?*/
                ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl)) + nextdiff);
            }

            /* 偏移量p-zl+1,表示要删除記憶體位元組數,zl-(p-zl-1)=zl-(p-zl)+1 */
            memmove(first.p, p, intrev32ifbe(ZIPLIST_BYTES(zl) - (p - zl) -1));
        } else {
            /* 被删除結點之後沒有任何其他結點,此時first結點的前一個結點是最後一個結點 */
            ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe((first.p - zl) - first.prevrawlen);
        }

        offset = first.p - zl; /* 儲存從壓縮表起點到被删除結點位置的偏移量
        因為下面對壓縮表大小的更新可能導緻first.p指向的記憶體位址發生變化,故保留相對位移 */

        /* 壓縮表的長度是zl - totlen + nextdiff */
        zl = ziplistResize(zl, intrev32ifbe(ZIPLIST_BYTES(zl)) - totlen + nextdiff); 
        ZIPLIST_INCR_LENGTH(zl, -deleted); /* 更新壓縮表的結點數 */
        p = zl + offset; /* 此時p指向壓縮表num(函數指定的删除的結點數)結點後面的第一個結點的位置 */
        /* 因為删除結點之後,p指向的結點的長度可能發生了變更,是以可能導緻後面的結點長度級聯變更 */
        if (nextdiff != 0) {
            zl = __ziplistCascadeUpdate(zl, p);
        }
    }

    return zl;
}
           

繼續閱讀