redis6.0.5之ziplist阅读笔记3--压缩列表(ziplist)之插入和删除

***********************************************************************
/* Given a pointer 'p' to the prevlen info that prefixes an entry, this
 * function returns the difference in number of bytes needed to encode
 * the prevlen if the previous entry changes of size.
指针P指向实体前缀的前一个实体的长度字段,这个函数返回前一个实体改变长度后的编码与之前长度编码的差值。
 * So if A is the number of bytes used right now to encode the 'prevlen' field.
假设A是字段prevlen现在编码所需的字节数
 * And B is the number of bytes that are needed in order to encode the
 * 'prevlen' if the previous element will be updated to one of size 'len'.
当前面的实体长度变成len后,假设B就是是prevlen字段现在所需的编码字节数。
 * Then the function returns B - A
这个函数返回B-A
 * So the function returns a positive number if more space is needed,
 * a negative number if less space is needed, or zero if the same space
 * is needed. */
如果需要更多空间,就返回一个正数,如果更少的空间需要就返回负数,
返回0值表示需要同样大小的空间。
int zipPrevLenByteDiff(unsigned char *p, unsigned int len) {
    unsigned int prevlensize;
    ZIP_DECODE_PREVLENSIZE(p, prevlensize);  获取之前的前一个实体的长度编码所需空间大小
    return zipStorePrevEntryLength(NULL, len) - prevlensize;  
    当前长度编码所需空间大小 - 之前的前一个实体的长度编码所需空间大小
}
***********************************************************************
/* Return the total number of bytes used by the entry pointed to by 'p'. */
返回指针p指向的实体使用的总的字节数
unsigned int zipRawEntryLength(unsigned char *p) {
    unsigned int prevlensize, encoding, lensize, len;
    ZIP_DECODE_PREVLENSIZE(p, prevlensize); //prevlensize 前一个字节的长度编码长度
    ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len);
p + prevlensize 当前实体编码字段起始地址,     lensize返回编码长度, len返回内容长度
    return prevlensize + lensize + len; 
    所以 prevlensize + lensize + len = <prevlen> 的编码长度  +  <encoding>的长度 + <entry-data>的长度
    就是这个实体的总长度
}
***********************************************************************
/* Check if string pointed to by 'entry' can be encoded as an integer.
 * Stores the integer value in 'v' and its encoding in 'encoding'. */
检查entry指向的字符串是否能够编码为整数,在v中保存整数并且在encoding中保存编码。
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)) { 通过函数string2ll 成功转化字符串为数值
        /* Great, the string can be encoded. Check what's the smallest
         * of our encoding types that can hold this value. */
太好了,能够被编码,检查可以容纳这个值的最小编码
        if (value >= 0 && value <= 12) {  当在0到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;
}
***********************************************************************
/* Store integer 'value' at 'p', encoded as 'encoding' */
在p中存储整数值,在字段encoding中存储编码
void zipSaveInteger(unsigned char *p, int64_t value, unsigned char encoding) {
    int16_t i16;
    int32_t i32;
    int64_t i64;
    if (encoding == ZIP_INT_8B) {
        ((int8_t*)p)[0] = (int8_t)value;
    } else if (encoding == ZIP_INT_16B) {
        i16 = value;
        memcpy(p,&i16,sizeof(i16));
        memrev16ifbe(p);
    } else if (encoding == ZIP_INT_24B) {
        i32 = value<<8;
        memrev32ifbe(&i32);
        memcpy(p,((uint8_t*)&i32)+1,sizeof(i32)-sizeof(uint8_t));拷贝24个字节
    } else if (encoding == ZIP_INT_32B) {
        i32 = value;
        memcpy(p,&i32,sizeof(i32));
        memrev32ifbe(p);
    } else if (encoding == ZIP_INT_64B) {
        i64 = value;
        memcpy(p,&i64,sizeof(i64));
        memrev64ifbe(p);
    } else if (encoding >= ZIP_INT_IMM_MIN && encoding <= ZIP_INT_IMM_MAX) {
        /* Nothing to do, the value is stored in the encoding itself. 
        这种情况下值存储在编码字段本身,就不在单独保存值
    } else {
        assert(NULL);
    }
}
***********************************************************************
/* Read integer encoded as 'encoding' from 'p' */
从指针p中读取整数编码当做encoding的值
int64_t zipLoadInteger(unsigned char *p, unsigned char encoding) {
    int16_t i16;
    int32_t i32;
    int64_t i64, ret = 0;
    if (encoding == ZIP_INT_8B) {
        ret = ((int8_t*)p)[0];
    } else if (encoding == ZIP_INT_16B) {
        memcpy(&i16,p,sizeof(i16));
        memrev16ifbe(&i16);
        ret = i16;
    } else if (encoding == ZIP_INT_32B) {
        memcpy(&i32,p,sizeof(i32));
        memrev32ifbe(&i32);
        ret = i32;
    } else if (encoding == ZIP_INT_24B) {
        i32 = 0;
        memcpy(((uint8_t*)&i32)+1,p,sizeof(i32)-sizeof(uint8_t));
        前面1个字节留空,拷贝后面的3个字节
        memrev32ifbe(&i32);
        ret = i32>>8; 获取后面的24位,即3个字节
    } else if (encoding == ZIP_INT_64B) {
        memcpy(&i64,p,sizeof(i64));
        memrev64ifbe(&i64);
        ret = i64;
    } else if (encoding >= ZIP_INT_IMM_MIN && encoding <= ZIP_INT_IMM_MAX) {
        ret = (encoding & ZIP_INT_IMM_MASK)-1; 
        从编码中获取值,值获取低4位,并且要减去1(用1-13表示0-12)
        #define ZIP_INT_IMM_MASK 0x0f
    } else {
        assert(NULL);
    }
    return ret;
}
***********************************************************************
/* Return a struct with all information about an entry. */
返回一个关于实体所有信息的结构
void zipEntry(unsigned char *p, zlentry *e) {

    ZIP_DECODE_PREVLEN(p, e->prevrawlensize, e->prevrawlen);
    ZIP_DECODE_LENGTH(p + e->prevrawlensize, e->encoding, e->lensize, e->len);
    e->headersize = e->prevrawlensize + e->lensize;
当前实体的头部长度 = 前一个实体的长度 + 实体长度
    e->p = p;
}
***********************************************************************
/* Create a new empty ziplist. */
创建一个新的空ziplist
unsigned char *ziplistNew(void) {
    unsigned int bytes = ZIPLIST_HEADER_SIZE+ZIPLIST_END_SIZE;
    没有任何实体情况下的ziplist的长度
#define ZIPLIST_HEADER_SIZE     (sizeof(uint32_t)*2+sizeof(uint16_t))
#define ZIPLIST_END_SIZE        (sizeof(uint8_t))

    unsigned char *zl = zmalloc(bytes); 分配空间
    ZIPLIST_BYTES(zl) = intrev32ifbe(bytes); 赋值ziplist总的长度
    #define ZIPLIST_BYTES(zl)       (*((uint32_t*)(zl)))
    
    ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(ZIPLIST_HEADER_SIZE);
    赋值偏移量,当前没有实体,头部长度就是偏移量
    #define ZIPLIST_TAIL_OFFSET(zl) (*((uint32_t*)((zl)+sizeof(uint32_t))))
    
    ZIPLIST_LENGTH(zl) = 0; 实体个数为0(即无实体)
    zl[bytes-1] = ZIP_END; 结尾字节
    return zl;
}
***********************************************************************
/* Resize the ziplist. */
对ziplist重新分配空间
unsigned char *ziplistResize(unsigned char *zl, unsigned int len) {
    zl = zrealloc(zl,len); 在原来地址上分配len字节的空间
    ZIPLIST_BYTES(zl) = intrev32ifbe(len); 总长度变了
    zl[len-1] = ZIP_END; 重置结尾符号(这里有个疑问,原来的结尾符号还在,不知怎么处理)
    return zl;
}
***********************************************************************
/* When an entry is inserted, we need to set the prevlen field of the next
 * entry to equal the length of the inserted entry. It can occur that this
 * length cannot be encoded in 1 byte and the next entry needs to be grow
 * a bit larger to hold the 5-byte encoded prevlen. This can be done for free,
 * because this only happens when an entry is already being inserted (which
 * causes a realloc and memmove). However, encoding the prevlen may require
 * that this entry is grown as well. This effect may cascade throughout
 * the ziplist when there are consecutive entries with a size close to
 * ZIP_BIG_PREVLEN, so we need to check that the prevlen can be encoded in
 * every consecutive entry.
当插入一个新的实体,我们需要去设置下一个实体中的前一个实体长度的值,
使得新的长度等于新插入实体的长度。这个情况发生在长度不能用1个字节表示,
那么接下来的实体需要扩大到5个字节保存前一个实体的长度。不过有时候我们可以不必这么做,
因为这种情况只是发生在一个实体已经在列表中(引发重分配和迁移的情况)。
然而编码前一个实体的长度可能需要当前实体扩容。这个效应导致级联传播整个压缩列表,
范围是接近ZIP_BIG_PREVLEN的所有后续的实体,
所以我们需要检查前一个实体的长度是否能够被后续每一个实体保存(即是否能容纳)。
(这个是因为一个实体长度的变化会导致下一个的实体长度也可能变回,从而导致连续的实体长度都可能变化)
 * Note that this effect can also happen in reverse, where the bytes required
 * to encode the prevlen field can shrink. This effect is deliberately ignored,
 * because it can cause a "flapping" effect where a chain prevlen fields is
 * first grown and then shrunk again after consecutive inserts. Rather, the
 * field is allowed to stay larger than necessary, because a large prevlen
 * field implies the ziplist is holding large entries anyway.
注意到这个影响同样发生在相反的情况,即用于编码前一个实体长度的需求字节减少的时候。
这个影响被我们故意忽略,因为这种情况可能造成反复影响,在连续插入新实体的情况下,
一个链的前一个实体长度字段第一次增长,然后收缩。因此继续保留之前大的空间,即使实际上用不到。
因为一个大的长度字段暗示压缩列表保持大实体的编码。
(个人理解是大的空间可以包容小的空间,保持大的空间是为了防止反复操作,一会儿增一会儿减,这样提高效率)
 * The pointer "p" points to the first entry that does NOT need to be
 * updated, i.e. consecutive fields MAY need an update. */
指针p指向第一个不需要修改的实体,即后续的字段可能需要更新
unsigned char *__ziplistCascadeUpdate(unsigned char *zl, unsigned char *p) {
    获取当前长度curlen
    size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), rawlen, rawlensize;
    size_t offset, noffset, extra;
    unsigned char *np;
    zlentry cur, next;

    while (p[0] != ZIP_END) { p是否指向ziplist结尾
        zipEntry(p, &cur); 获取实体的相关信息 如 头部长度 ,具体指向的指针
        rawlen = cur.headersize + cur.len;该实体所需全部空间大小
        rawlensize = zipStorePrevEntryLength(NULL,rawlen); 获取编码长度的大小

        /* Abort if there is no next entry. */如果没有下一个实体就终止
        if (p[rawlen] == ZIP_END) break; p[0]到p[rawlen]刚好rawlen个字节
        zipEntry(p+rawlen, &next); 获取下一个实体的信息

        /* Abort when "prevlen" has not changed. */
        如果前面的实体长度没有变化就终止,即无需重新分配空间
        if (next.prevrawlen == rawlen) break;  
        后一个实体原来的长度 = 当前的长度

        if (next.prevrawlensize < rawlensize) {  
        前一个实体原来的编码长度 小于 前一个实体当前长度编码
        后面的实体就需要更多的字节来保存前一个实体的编码长度
            /* The "prevlen" field of "next" needs more bytes to hold
             * the raw length of "cur". */
            offset = p-zl;
            extra = rawlensize-next.prevrawlensize; 
            前一个实体 新的编码长度 减去 旧的编码长度,这个长度就是需要新增的空间
            zl = ziplistResize(zl,curlen+extra);
            p = zl+offset;

            /* Current pointer and offset for next element. */
            当前指针和下一个元素的偏移量
            np = p+rawlen; 指向下一个实体
            noffset = np-zl;

            /* Update tail offset when next element is not the tail element. */
            当下一个元素不是结尾元素的时候更新结尾的偏移量
            if ((zl+intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))) != np) {
              判断下一个元素是否是结尾,不是的情况下,更新原来的偏移量
                ZIPLIST_TAIL_OFFSET(zl) =
                    intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+extra);
            }

            /* Move the tail to the back. */
            memmove(np+rawlensize,
                np+next.prevrawlensize, 从表示前一个实体长度编码字段后面开始,全部迁移
                curlen-noffset-next.prevrawlensize-1); 减去1的原因就是 减去上面的前一个实体长度编码的一个字节
            zipStorePrevEntryLength(np,rawlen);

            /* Advance the cursor */
            p += rawlen;  指向下一个实体开始位置,继续循环,可能存在级联效应
            curlen += extra;  新的总长度
        } else {
            if (next.prevrawlensize > rawlensize) {
            对于前一个实体 原先编码字段的长度 大于 现在编码字段的长度
                /* This would result in shrinking, which we want to avoid.
                 * So, set "rawlen" in the available bytes. */
                 这种情况本应该收缩的,但是我们为了避免不必要的操作,
                 只是在可用字节中设置了rawlen的值(弱水三千,只取一瓢饮)
                zipStorePrevEntryLengthLarge(p+rawlen,rawlen);
            } else {
            前一个实体 长度 编码一样的情况下,重新设置长度值就可以了
                zipStorePrevEntryLength(p+rawlen,rawlen);
            }

            /* Stop here, as the raw length of "next" has not changed. */
            这种情况下,后面实体就无需改变,不用迁移末尾,就结束了循环
            break;
        }
    }
    return zl;
}
***********************************************************************
/* Delete "num" entries, starting at "p". Returns pointer to the ziplist. */
删除实体,从指针p开始,返回指向的压缩列表
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;

    zipEntry(p, &first); 初始化辅助结构体
    for (i = 0; p[0] != ZIP_END && i < num; i++) {
    从p指向的实体开始 删除
        p += zipRawEntryLength(p); 每删除一个实体,向后移动的字节
        deleted++; 删除个数++
    }

    totlen = p-first.p; /* Bytes taken by the element(s) to delete. */ 删除的总字节数
    if (totlen > 0) { 如果删除的字节数大于0
        if (p[0] != ZIP_END) { 当前指向的实体不是结尾
            /* Storing `prevrawlen` in this entry may increase or decrease the
             * number of bytes required compare to the current `prevrawlen`.
             * There always is room to store this, because it was previously
             * stored by an entry that is now being deleted. */
保存前一个实体的长度肯能会增加或者缩小,因为当前的前一个实体长度需要和之前的进行比较。
总是有空间来保存这个长度,因为之前存储的空间已经不再需要了(即有实体被删除了,多出了空间),可以再次使用
            nextdiff = zipPrevLenByteDiff(p,first.prevrawlen); 
            比较现在的前一个实体的长度 和之前的长度 

            /* Note that there is always space when p jumps backward: if
             * the new previous entry is large, one of the deleted elements
             * had a 5 bytes prevlen header, so there is for sure at least
             * 5 bytes free and we need just 4. */
             注意到如果p往后移动了,那么总是有空间的:
            如果新的前一个实体需要大的5个字节存储,
            那么必定有一个删除的实体拥有一个5个字节长度的空间(因为需要存储前一个实体的长度),
            所以至少有5个字节的空间可以利用,而我们只需要4个(原来至少存在一个字节)
            p -= nextdiff; p往后面退差值,可能是0,也是可能是4,还有可能是-4
            zipStorePrevEntryLength(p,first.prevrawlen); 设置新的前一个实体的长度

            /* Update offset for tail */ 更新相对于尾部的偏移量
            ZIPLIST_TAIL_OFFSET(zl) =
                intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))-totlen); 
                原先的偏移量  减去 删除实体的总长度

            /* When the tail contains more than one entry, we need to take
             * "nextdiff" in account as well. Otherwise, a change in the
             * size of prevlen doesn't have an effect on the *tail* offset. */
            如果尾巴上含有多于一个的实体,我们还需要考虑nextdiff的影响。
            否则如果只有一个实体,那么前一个实体长度的变化不会影响尾巴的偏移量
            zipEntry(p, &tail);
            if (p[tail.headersize+tail.len] != ZIP_END) {  尾巴中包含多于一个实体
                 实体头部长度 + 数据长度  刚好指向 下个实体的第一个字节, 如果不是结尾ZIP_END
                ZIPLIST_TAIL_OFFSET(zl) =
                   intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+nextdiff); 
                   需要考虑nextdiff的影响,如果大于0,表示需要加上去这多的4个字节的空间
                   如果小于0,表示可以再减去4个字节
            }

            /* Move tail to the front of the ziplist */移动尾部到压缩列表的前面
            memmove(first.p,p, intrev32ifbe(ZIPLIST_BYTES(zl))-(p-zl)-1);  除去结尾符号,全部拷贝移动到前面
        } else {
            /* The entire tail was deleted. No need to move memory. */
            ZIPLIST_TAIL_OFFSET(zl) =
                intrev32ifbe((first.p-zl)-first.prevrawlen);
first.p-zl 为第一删除实体到头部的长度  
再减去 first.prevrawlen即第一个删除节点前面一个实体的长度,
就是指向前面实体的第一个字节,就是定义的对尾部的偏移量
        }

        /* Resize and update length */
        offset = first.p-zl;
        zl = ziplistResize(zl, intrev32ifbe(ZIPLIST_BYTES(zl))-totlen+nextdiff); 重新分配空间
        ZIPLIST_INCR_LENGTH(zl,-deleted);  修改总实体个数
        p = zl+offset;指向第一个被删除实体的初始位置

        /* When nextdiff != 0, the raw length of the next entry has changed, so
         * we need to cascade the update throughout the ziplist */
        如果nextdiff不为0,那么下一个实体的长度也会受到影响,
        所以我们需要级联去更新接下来的压缩列表
        if (nextdiff != 0)
            zl = __ziplistCascadeUpdate(zl,p);
    }
    return zl;
}
***********************************************************************
/* Insert item at "p". */
在p处插入一个实体
unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {
    当前长度,即总的字节数
    size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), reqlen;
    unsigned int prevlensize, prevlen = 0;
    size_t offset;
    int nextdiff = 0;
    unsigned char encoding = 0;
    long long value = 123456789; /* initialized to avoid warning. Using a value
                                    that is easy to see if for some reason
                                    we use it uninitialized. */
    zlentry tail;

    /* Find out prevlen for the entry that is inserted. */
    if (p[0] != ZIP_END) { 非结尾
        ZIP_DECODE_PREVLEN(p, prevlensize, prevlen);
        获取 编码长度prevlensize  和  实体总长度prevlen
    } else {
        unsigned char *ptail = ZIPLIST_ENTRY_TAIL(zl); 指向最后一个实体的开始位置
        if (ptail[0] != ZIP_END) {  存在实体(没有实体,就是空的压缩列表)
            prevlen = zipRawEntryLength(ptail); 返回实体的长度
        }
    }

    /* See if the entry can be encoded */
    if (zipTryEncoding(s,slen,&value,&encoding)) { 是否可以用整数编码
        /* 'encoding' is set to the appropriate integer encoding */
        可以用整数编码的情况下,encoding被设置成合适的整数编码值
        reqlen = zipIntSize(encoding);
    } else { 不能用整数编码
        /* 'encoding' is untouched, however zipStoreEntryEncoding will use the
         * string length to figure out how to encode it. */
         不能用整数编码的情况下,可以用函数zipStoreEntryEncoding通过字符串长度进行编码
        reqlen = slen;  所以这里我们值需要设置长度即可
    }
    /* We need space for both the length of the previous entry and
     * the length of the payload. */
    我们需要空间来保存前一个实体的长度以及新实体的内容
    reqlen += zipStorePrevEntryLength(NULL,prevlen);  加 前一个实体的长度
    reqlen += zipStoreEntryEncoding(NULL,encoding,slen); 根据编码和长度确定 新实体内容长度的编码长度
    zipIntSize(encoding)或者slen 即内容长度 
    zipStorePrevEntryLength(NULL,prevlen)  前一个实体长度的编码长度
    zipStoreEntryEncoding(NULL,encoding,slen);  新实体内容长度的编码长度
    三者加在一起就是新实体的总长度

    /* When the insert position is not equal to the tail, we need to
     * make sure that the next entry can hold this entry's length in
     * its prevlen field. */
如果插入的位置不是在尾部,那么我们需要确保下一个实体的prevlen字段有足够的空间保存这个实体的长度
    int forcelarge = 0;
    nextdiff = (p[0] != ZIP_END) ? zipPrevLenByteDiff(p,reqlen) : 0;
    如果等于结尾标志,显然不会有任何影响,因为后面没有实体了,不用计算前一个实体的长度
    如果不等于结尾标志,就会对后续的实体 产生影响
    if (nextdiff == -4 && reqlen < 4) {   明天把这个bug想办法重现
    如果新的实体的长度编码长度 小于 原来前实体的长度编码长度 ,就是值为-4的情况
    (长度编码 的编码不是1  就是 5  )
    并且新实体总的长度小于4,这样会导致新增一个实体总长度反而小的问题,这种情况会出现结尾符号提前bug
详细见原文
https://github.com/redis/redis/commit/c495d095ae495ea5253443ee4562aaa30681a854#diff-3cee7e9c2084403adfa8843617f87099444c7ef8aa2ad69f397013b11794589c
        nextdiff = 0;
        forcelarge = 1; 所以需要强制使用大编码空间,保证新的列表总体长度不缩小
    }

    /* Store offset because a realloc may change the address of zl. */
    offset = p-zl;
    zl = ziplistResize(zl,curlen+reqlen+nextdiff); 
    reqlen 新实体的总长度  nextdiff 新实体长度和原前实体 对  后面实体 前一个实体的长度编码的影响字节数
    p = zl+offset;

    /* Apply memory move when necessary and update tail offset. */
    必要的时候进行内存迁移 并且 更新尾部偏移量
    if (p[0] != ZIP_END) {
        /* Subtract one because of the ZIP_END bytes */
        memmove(p+reqlen,p-nextdiff,curlen-offset-1+nextdiff);
        reqlen 是给新的实体留出的空间
        p-nextdiff 下一个实体开始的位置
        curlen-offset-1+nextdiff  需要搬迁的内存字节数

        /* Encode this entry's raw length in the next entry. */
        在下一个实体编码新实体的长度
        if (forcelarge) 强制使用大空间编码长度
            zipStorePrevEntryLengthLarge(p+reqlen,reqlen);
        else  根据实际长度进行编码
            zipStorePrevEntryLength(p+reqlen,reqlen);

        /* Update offset for tail */
        ZIPLIST_TAIL_OFFSET(zl) =
            intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+reqlen);
            reqlen 新增的实体长度

        /* When the tail contains more than one entry, we need to take
         * "nextdiff" in account as well. Otherwise, a change in the
         * size of prevlen doesn't have an effect on the *tail* offset. */
如果尾巴上含有多于一个的实体,我们还需要考虑nextdiff的影响。
否则如果只有一个实体,那么前一个实体长度的变化不会影响尾巴的偏移量
        zipEntry(p+reqlen, &tail); 获取指向最后一个实体的指针
        if (p[reqlen+tail.headersize+tail.len] != ZIP_END) {  如果指向下个实体的开始位置的内容不是结尾标志
            ZIPLIST_TAIL_OFFSET(zl) =
                intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+nextdiff); 
                需要考虑当前节点的改变对结尾偏移量的影响
        }
    } else {
        /* This element will be the new tail. */
        这个实体就是新的尾部实体
        ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(p-zl); 
    }

    /* When nextdiff != 0, the raw length of the next entry has changed, so
     * we need to cascade the update throughout the ziplist */
     如果nextdiff不为0,那么下一个实体的长度也会受到影响,
    所以我们需要级联去更新接下来的压缩列表中的实体
    if (nextdiff != 0) {
        offset = p-zl;
        zl = __ziplistCascadeUpdate(zl,p+reqlen);  对新节点后面的节点进行级联更新
        p = zl+offset;
    }

    /* Write the entry */ 写入新实体内容
    p += zipStorePrevEntryLength(p,prevlen);
    p += zipStoreEntryEncoding(p,encoding,slen);
    if (ZIP_IS_STR(encoding)) {
    字符串编码就拷贝字符
        memcpy(p,s,slen); 
    } else {
    保存整数编码的值
        zipSaveInteger(p,value,encoding);
    }
    ZIPLIST_INCR_LENGTH(zl,1); 总的实体个数加1
    return zl;
}
***********************************************************************
posted on 2021-01-20 21:37  子虚乌有  阅读(178)  评论(0)    收藏  举报