Ziplist
简介
Redis为了节约空间,在zset和hash的元素个数较少的时候使用ziplist进行存储。zip+list,是一系列的zip结构的数据链在一起。压缩链表是一块连续的内存空间,元素之间紧挨着存储,没有冗余空隙。
3.0之后list键已经不直接用ziplist和linkedlist作为底层实现了,取而代之的是quicklist
这些键的常规底层实现如下:
list键:双向链表
hash键:字典dict
zset键:跳跃表zskiplist
结构体
ziplist.h的结构体
压缩的元素,要么是string要么是整型
- 如果是string,提供slen的方法
- 如果是整型,sval字段为空,lval字段存储值。
/* Each entry in the ziplist is either a string or an integer. */
typedef struct {
/* When string is used, it is provided with the length (slen). */
unsigned char *sval;
unsigned int slen;
/* When integer is used, 'sval' is NULL, and lval holds the value. */
long long lval;
} ziplistEntry;
ziplist.c中的结构体
##我们使用这个函数来接收关于ziplist条目的信息。
注意,这并不是数据的实际编码方式,只是为了更容易地操作,我们用函数填充数据。
typedef struct zlentry {
unsigned int prevrawlensize; /* 用于编码前一个条目len的字节*/
unsigned int prevrawlen; /* 前一节点长度. */
unsigned int lensize; /* 用于编码此条目type/len的字节。例如,字符串有1、2或5个字节的头。整数总是使用单个字节。*/
unsigned int len; /* 用于表示实际条目长度的字节。对于字符串,这只是字符串长度
unsigned int headersize; /* prevrawlensize + lensize. */
unsigned char encoding; /* 根据条目编码设置为ZIP_STR_*或ZIP_INT_*
* 然而,对于4位直接整数,这可以假设一个值的范围,并且必须进行范围检查。 */
unsigned char *p; /* 指向条目开始位置的指针,也就是说,它指向previ-entry-len字段。 */
} zlentry;
宏函数
将所有的值赋为0
#define ZIPLIST_ENTRY_ZERO(zle) { \
(zle)->prevrawlensize = (zle)->prevrawlen = 0; \
(zle)->lensize = (zle)->len = (zle)->headersize = 0; \
(zle)->encoding = 0; \
(zle)->p = NULL; \
}
从’ptr’指向的字节中提取编码,并将其设置为zlentry结构的’encoding’字段
/* Extract the encoding from the byte pointed by 'ptr' and set it into
* 'encoding' field of the zlentry structure. */
#define ZIP_ENTRY_ENCODING(ptr, encoding) do { \
(encoding) = ((ptr)[0]); \
if ((encoding) < ZIP_STR_MASK) (encoding) &= ZIP_STR_MASK; \
} while(0)
获取encoding字段,判断需要的字节数,来解析数据的类型和程序
#define ZIP_ENCODING_SIZE_INVALID 0xff
/* Return the number of bytes required to encode the entry type + length.
* On error, return ZIP_ENCODING_SIZE_INVALID */
static inline unsigned int zipEncodingLenSize(unsigned char encoding) {
if (encoding == ZIP_INT_16B || encoding == ZIP_INT_32B ||
encoding == ZIP_INT_24B || encoding == ZIP_INT_64B ||
encoding == ZIP_INT_8B)
return 1;
if (encoding >= ZIP_INT_IMM_MIN && encoding <= ZIP_INT_IMM_MAX)
return 1;
if (encoding == ZIP_STR_06B)
return 1;
if (encoding == ZIP_STR_14B)
return 2;
if (encoding == ZIP_STR_32B)
return 5;
return ZIP_ENCODING_SIZE_INVALID;
}
判断解析出来的长度字段是不是invalid 是的话则堵塞 一直判断
#define ZIP_ASSERT_ENCODING(encoding) do { \
assert(zipEncodingLenSize(encoding) != ZIP_ENCODING_SIZE_INVALID); \
} while (0)
解析encoding字段 判断需要用来存储整型需要的空间大小
/* Return bytes needed to store integer encoded by 'encoding' */
static inline unsigned int zipIntSize(unsigned char encoding) {
switch(encoding) {
case ZIP_INT_8B: return 1;
case ZIP_INT_16B: return 2;
case ZIP_INT_24B: return 3;
case ZIP_INT_32B: return 4;
case ZIP_INT_64B: return 8;
}
if (encoding >= ZIP_INT_IMM_MIN && encoding <= ZIP_INT_IMM_MAX)
return 0; /* 4 bit immediate */
/* bad encoding, covered by a previous call to ZIP_ASSERT_ENCODING */
redis_unreachable();
return 0;
}
ziplist.c中的宏
宏定义
/* Different encoding/length possibilities */
#define ZIP_STR_MASK 0xc0
#define ZIP_INT_MASK 0x30
#define ZIP_STR_06B (0 << 6)
#define ZIP_STR_14B (1 << 6)
#define ZIP_STR_32B (2 << 6)
#define ZIP_INT_16B (0xc0 | 0<<4)
#define ZIP_INT_32B (0xc0 | 1<<4)
#define ZIP_INT_64B (0xc0 | 2<<4)
#define ZIP_INT_24B (0xc0 | 3<<4)
#define ZIP_INT_8B 0xfe
/* 4 bit integer immediate encoding |1111xxxx| with xxxx between
* 0001 and 1101. */
#define ZIP_INT_IMM_MASK 0x0f /* Mask to extract the 4 bits value. To add
one is needed to reconstruct the value. */
#define ZIP_INT_IMM_MIN 0xf1 /* 11110001 */
#define ZIP_INT_IMM_MAX 0xfd /* 11111101 */
#define INT24_MAX 0x7fffff
#define INT24_MIN (-INT24_MAX - 1)
因为string类型从来都不“11”开头,所以只需要位运算获取前面两位并对比就可以知道是不是string
/* Macro to determine if the entry is a string. String entries never start
* with "11" as most significant bits of the first byte. */
#define ZIP_IS_STR(enc) (((enc) & ZIP_STR_MASK) < ZIP_STR_MASK)
返回一个ziplist所组成的总字节数,相当于4字节的头存储的值
/* Utility macros.*/
/* Return total bytes a ziplist is composed of. */
#define ZIPLIST_BYTES(zl) (*((uint32_t*)(zl)))
返回ziplist的尾部的offset的大小,用于快速定位到最后一个节点,存储在一个四字节的数据中
/* Return the offset of the last item inside the ziplist. */
#define ZIPLIST_TAIL_OFFSET(zl) (*((uint32_t*)((zl)+sizeof(uint32_t))))
两字节的数据大小,存储的内容是元素个数
/* Return the length of a ziplist, or UINT16_MAX if the length cannot be
* determined without scanning the whole ziplist. */
#define ZIPLIST_LENGTH(zl) (*((uint16_t*)((zl)+sizeof(uint32_t)*2)))
10字节存储ziplist的头部的大小
/* The size of a ziplist header: two 32 bit integers for the total
* bytes count and last item offset. One 16 bit integer for the number
* of items field. */
#define ZIPLIST_HEADER_SIZE (sizeof(uint32_t)*2+sizeof(uint16_t))
一字节存储结束标志
/* Size of the "end of ziplist" entry. Just one byte. */
#define ZIPLIST_END_SIZE (sizeof(uint8_t))
可以获得第一个元素的结点的地址
/* Return the pointer to the first entry of a ziplist. */
#define ZIPLIST_ENTRY_HEAD(zl) ((zl)+ZIPLIST_HEADER_SIZE)
获取节点的最后一个节点
intrev32ifbe只有在目标主机是大端序的情况下,才执行实际转换的函数变体
/* Return the pointer to the last entry of a ziplist, using the
* last entry offset inside the ziplist header. */
#define ZIPLIST_ENTRY_TAIL(zl) ((zl)+intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl)))
返回指向ziplist的最后一个字节的指针,也就是ziplist FF条目的结束。
/* Return the pointer to the last byte of a ziplist, which is, the
* end of ziplist FF entry. */
#define ZIPLIST_ENTRY_END(zl) ((zl)+intrev32ifbe(ZIPLIST_BYTES(zl))-ZIPLIST_END_SIZE)
增加ziplist报头中的项字段的数量。注意这个宏不应该溢出无符号的16位整数,因为条目总是一次推入一个。当UINT16_MAX达到时,我们希望计数停留在那里,以表明需要一个完整的扫描来获得拉链内的项目的数量
/* Increment the number of items field in the ziplist header. Note that this
* macro should never overflow the unsigned 16 bit integer, since entries are
* always pushed one at a time. When UINT16_MAX is reached we want the count
* to stay there to signal that a full scan is needed to get the number of
* items inside the ziplist. */
#define ZIPLIST_INCR_LENGTH(zl,incr) { \
if (intrev16ifbe(ZIPLIST_LENGTH(zl)) < UINT16_MAX) \
ZIPLIST_LENGTH(zl) = intrev16ifbe(intrev16ifbe(ZIPLIST_LENGTH(zl))+incr); \
}
在任何情况下都不要让拉链增长超过1GB,不冒溢出风险
/* Don't let ziplists grow over 1GB in any case, don't wanna risk overflow in
* zlbytes */
#define ZIPLIST_MAX_SAFETY_SIZE (1<<30)
int ziplistSafeToAdd(unsigned char* zl, size_t add) {
size_t len = zl? ziplistBlobLen(zl): 0;
if (len + add > ZIPLIST_MAX_SAFETY_SIZE)
return 0;
return 1;
}
ziplist.h中声明的函数
函数名 | 备注 |
---|---|
unsigned char *ziplistNew(void); | |
unsigned char *ziplistMerge(unsigned char **first, unsigned char **second); | |
unsigned char *ziplistPush(unsigned char *zl, unsigned char *s, unsigned int slen, int where); | |
unsigned char *ziplistIndex(unsigned char *zl, int index); | |
unsigned char *ziplistNext(unsigned char *zl, unsigned char *p); | |
unsigned char *ziplistPrev(unsigned char *zl, unsigned char *p); | |
unsigned int ziplistGet(unsigned char *p, unsigned char **sval, unsigned int *slen, long long *lval); | |
unsigned char *ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen); | |
unsigned char *ziplistDelete(unsigned char *zl, unsigned char **p); | |
unsigned char *ziplistDeleteRange(unsigned char *zl, int index, unsigned int num); | |
unsigned char *ziplistReplace(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen); | |
unsigned int ziplistCompare(unsigned char *p, unsigned char *s, unsigned int slen); | |
unsigned char *ziplistFind(unsigned char *zl, unsigned char *p, unsigned char *vstr, unsigned int vlen, unsigned int skip); | |
unsigned int ziplistLen(unsigned char *zl); | |
size_t ziplistBlobLen(unsigned char *zl); | |
void ziplistRepr(unsigned char *zl); | |
typedef int (ziplistValidateEntryCB)(unsigned char p, unsigned int head_count, void* userdata); | |
int ziplistValidateIntegrity(unsigned char *zl, size_t size, int deep,iplistValidateEntryCB entry_cb, void *cb_userdata); | |
void ziplistRandomPair(unsigned char *zl, unsigned long total_count, ziplistEntry *key, ziplistEntry *val); | |
void ziplistRandomPairs(unsigned char *zl, unsigned int count, ziplistEntry *keys, ziplistEntry *vals); | |
unsigned int ziplistRandomPairsUnique(unsigned char *zl, unsigned int count, ziplistEntry *keys, ziplistEntry *vals); | |
int ziplistSafeToAdd(unsigned char* zl, size_t add); |
implements
属性 prevrawlen、prevrawlensize
属性 prevrawlen 为前置节点的长度,而 prevrawlensize 是代表存储 prevrawlen 这个属性所需的字节大小
属性 len、lensize
属性 len 为当前节点值的长度,而 lensize 是代表存储 len 属性所需的字节大小。(注意:当节点保存的是字符串的时候,len 为字符串长度,如果保存的是整数值,len 为整数值的字节长度)
属性 headersize
属性 headersize 为当前节点 header 的长度,等于 prevrawlensize + lensize
属性 encoding
属性 encoding 为当前节点值所使用的编码类型
属性 p
属性 p 为一个指针,指向了当前节点的内存地址
previous_entry_length
属性 previous_entry_length 保存了前置节点的长度。
当前置节点的长度小于 254 字节的时候,那么 previous_entry_length 的长度将为1个字节,前置节点的长度就保存在这个字节里面。
当前置节点的长度大于等于 254 字节的时候,那么 previous_entry_length 的长度将为5个字节,这5个字节的第一个字节将被设置为254,然后剩下的四个字节保存前置节点的长度。
程序主要可以通过这个前置节点的长度,以及根据当前节点的起始地址来计算出前置节点的起始地址。压缩列表的从表头到表尾的遍历操作就是通过这样的原理来实现的。
encoding
属性 encoding 记录了对应节点的 content 属性所保存数据的类型以及长度。
encoding 的长度可能是一个字节、两个字节或者五个字节。它具体多少个字节,取决于 encoding 值的最高两位。当最高两位为00、01、10开头的时候,代表 content 存储的是字符串且 encoding 的长度分别为一个字节、两个字节或者五个字节。而为11开头的时候,即 content 存储的是整数且 encoding 的长度为一个字节。
00开头的时候:
encoding 占用了一个字节的空间,表示 content 能存储长度小于等于 2^6-1 字节的字符串
01开头的时候:
encoding 占用了两个字节的空间,表示 content 能存储长度小于等于 2^14-1 字节的字符串
10开头的时候:
encoding 占用了五个字节的空间,第一字节的后六位由0填充,剩下的表示 content 能存储长度小于等于 2^32-1 字节的字符串
11开头的时候:
encoding 占用了一个字节的空间,且由于编码的不同,content 存储的整数值范围都是不同的
encoding 值: 11000000 ,content 能存储 int16_t 类型的整数
encoding 值: 11010000 ,content 能存储 int32_t 类型的整数
encoding 值: 11100000 ,content 能存储 int64_t 类型的整数
encoding 值: 11110000 ,content 能存储 24 位有符号整数
encoding 值: 11111110 ,content 能存储 8 位有符号整数
encoding 值: 1111xxxx ,content 能存储 0-12 之间的值,没有明确的类型
content
压缩列表的连锁更新
假如在一个压缩列表中,有多个连续节点 e1 至 eN 且他们各自的长度都在250字节到253字节之间,加上上面提到的 previous_entry_length 的相关内容,这个属性记录每个前置节点的长度的时候,值将都是一个字节长度的。
此时我们把一个长度大于等于254字节的新节点 new 设置到压缩列表的表头的时候,那么 new 节点将成为 e1 节点的前置节点,而 e1 节点的 previous_entry_length 值只有1字节的长度,没有存储表示前置节点长度的5个字节长度,所以程序分配空间给 e1 多四个字节存储,接着 e2 也需要增加空间存储 e1 的长度,然后以此类推,引发了后面所有的节点需要更新空间,这就是连锁更新。
连锁更新会导致在最坏的情况下要对压缩列表 N 次空间重新分配,而每次空间重新分配的复杂度为 O(N),所以连锁更新的最坏时间复杂度为 O(N^2)
不过这种是需要恰好多个连续节点都是在 250-253字节之间,连锁更新才可能发生,这种情况概率很低。又假如是几个连续这样的 250-253 字节之间,对性能是不会造成影响的。
/* Create a new empty ziplist. */
unsigned char *ziplistNew(void) {
unsigned int bytes = ZIPLIST_HEADER_SIZE+ZIPLIST_END_SIZE;
unsigned char *zl = zmalloc(bytes);
ZIPLIST_BYTES(zl) = intrev32ifbe(bytes);
ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(ZIPLIST_HEADER_SIZE);
ZIPLIST_LENGTH(zl) = 0;
zl[bytes-1] = ZIP_END;
return zl;
}
/* Insert item at "p". */
unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {
size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), reqlen, newlen;
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;
#获取prevlen
/* Find out prevlen for the entry that is inserted. */
if (p[0] != ZIP_END) {
ZIP_DECODE_PREVLEN(p, prevlensize, prevlen);
} else {
unsigned char *ptail = ZIPLIST_ENTRY_TAIL(zl);
if (ptail[0] != ZIP_END) {
prevlen = zipRawEntryLengthSafe(zl, curlen, ptail);
}
}
##看能否解析
/* See if the entry can be encoded */
if (zipTryEncoding(s,slen,&value,&encoding)) {
/* 'encoding' is set to the appropriate integer encoding */
reqlen = zipIntSize(encoding);
} else {
/* 'encoding' is untouched, however zipStoreEntryEncoding will use the
* string length to figure out how to encode it. */
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);
##至此,reqlen 保存了存储当前节点数据占用字节数和 encoding 编码占用的字节数总和
/* 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. */
##插入的地方不是尾部的时候,需要确保下一条目的前一条目域
int forcelarge = 0;
//当前节点占用的总字节减去存储前一个节点字段占用的字节
//当前节点占用的总字节减去存储前一个节点字段占用的字节
nextdiff = (p[0] != ZIP_END) ? zipPrevLenByteDiff(p,reqlen) : 0;
if (nextdiff == -4 && reqlen < 4) {
nextdiff = 0;
forcelarge = 1;
}
##保存offset 并空间重铸
/* Store offset because a realloc may change the address of zl. */
//当前节点占用的总字节减去存储前一个节点字段占用的字节
offset = p-zl;
newlen = curlen+reqlen+nextdiff;
zl = ziplistResize(zl,newlen);
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);
//把当前节点占用的字节数存储到下一个节点的头部字段
/* 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);
/* 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. */
assert(zipEntrySafe(zl, newlen, p+reqlen, &tail, 1));
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 */
//是否触发连锁更新
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);
return zl;
}
/* Resize the ziplist. */
unsigned char *ziplistResize(unsigned char *zl, size_t len) {
assert(len < UINT32_MAX);
zl = zrealloc(zl,len);
ZIPLIST_BYTES(zl) = intrev32ifbe(len);
zl[len-1] = ZIP_END;
return zl;
}
在’p’中写入条目的编码头。如果为NULL,则返回编码该长度所需的字节数。
参数:'encoding’是我们用于条目的编码。它可以是ZIP_INT_或ZIP_STR_,或者介于ZIP_INT_IMM MIN和zip_int_im_max之间对于单字节的小直接整数。
*'rawlen’仅用于ZIP_STR_*编码,是长度此条目表示的字符串。函数返回存储在’p’中的encoding/length *头所使用的字节数。
根据encoding类型和rawlen长度获取字段,然后存储到buf后,赋值给p,返回len的长度。
unsigned int zipStoreEntryEncoding(unsigned char *p, unsigned char encoding, unsigned int rawlen) {
unsigned char len = 1, buf[5];
if (ZIP_IS_STR(encoding)) {
/* Although encoding is given it may not be set for strings,
* so we determine it here using the raw length. */
if (rawlen <= 0x3f) {
if (!p) return len;
buf[0] = ZIP_STR_06B | rawlen;
} else if (rawlen <= 0x3fff) {
len += 1;
if (!p) return len;
buf[0] = ZIP_STR_14B | ((rawlen >> 8) & 0x3f);
buf[1] = rawlen & 0xff;
} else {
len += 4;
if (!p) return len;
buf[0] = ZIP_STR_32B;
buf[1] = (rawlen >> 24) & 0xff;
buf[2] = (rawlen >> 16) & 0xff;
buf[3] = (rawlen >> 8) & 0xff;
buf[4] = rawlen & 0xff;
}
} else {
/* Implies integer encoding, so length is always 1. */
if (!p) return len;
buf[0] = encoding;
}
/* Store this length at p. */
memcpy(p,buf,len);
return len;
}
解码编码在’ptr’中的条目编码类型和数据长度(字符串的字符串长度,整数条目的整数字节数)。'encoding’变量是输入,由调用者提取,'lensize’变量将保存编码条目长度所需的字节数,'len’变量将保存条目长度。在无效编码时,ennon lensize设置为0
zip解析长度的函数
/* Decode the entry encoding type and data length (string length for strings,
* number of bytes used for the integer for integer entries) encoded in 'ptr'.
* The 'encoding' variable is input, extracted by the caller, the 'lensize'
* variable will hold the number of bytes required to encode the entry
* length, and the 'len' variable will hold the entry length.
* On invalid encoding error, lensize is set to 0. */
#define ZIP_DECODE_LENGTH(ptr, encoding, lensize, len) do { \
if ((encoding) < ZIP_STR_MASK) { \
if ((encoding) == ZIP_STR_06B) { \
(lensize) = 1; \
(len) = (ptr)[0] & 0x3f; \
} else if ((encoding) == ZIP_STR_14B) { \
(lensize) = 2; \
(len) = (((ptr)[0] & 0x3f) << 8) | (ptr)[1]; \
} else if ((encoding) == ZIP_STR_32B) { \
(lensize) = 5; \
(len) = ((uint32_t)(ptr)[1] << 24) | \
((uint32_t)(ptr)[2] << 16) | \
((uint32_t)(ptr)[3] << 8) | \
((uint32_t)(ptr)[4]); \
} else { \
(lensize) = 0; /* bad encoding, should be covered by a previous */ \
(len) = 0; /* ZIP_ASSERT_ENCODING / zipEncodingLenSize, or */ \
/* match the lensize after this macro with 0. */ \
} \
} else { \
(lensize) = 1; \
if ((encoding) == ZIP_INT_8B) (len) = 1; \
else if ((encoding) == ZIP_INT_16B) (len) = 2; \
else if ((encoding) == ZIP_INT_24B) (len) = 3; \
else if ((encoding) == ZIP_INT_32B) (len) = 4; \
else if ((encoding) == ZIP_INT_64B) (len) = 8; \
else if (encoding >= ZIP_INT_IMM_MIN && encoding <= ZIP_INT_IMM_MAX) \
(len) = 0; /* 4 bit immediate */ \
else \
(lensize) = (len) = 0; /* bad encoding */ \
} \
} while(0)
对前面条目的长度进行解码,并将其写入“p”。只在较大的编码(required-in-ziplistCascadeUpdate)时使用
/* Encode the length of the previous entry and write it to "p". This only
* uses the larger encoding (required in __ziplistCascadeUpdate). */
int zipStorePrevEntryLengthLarge(unsigned char *p, unsigned int len) {
uint32_t u32;
if (p != NULL) {
p[0] = ZIP_BIG_PREVLEN;
u32 = len;
memcpy(p+1,&u32,sizeof(u32));
memrev32ifbe(p+1);
}
return 1 + sizeof(uint32_t);
}
对前一项的长度进行编码,并将其写入“p”。如果"p"为NULL,则返回编码此长度所需的字节数,当p使用较大前缀长度,就使用上述的解析较大条目长度函数zipStorePrevEntryLengthLarge。
/* Encode the length of the previous entry and write it to "p". Return the
* number of bytes needed to encode this length if "p" is NULL. */
unsigned int zipStorePrevEntryLength(unsigned char *p, unsigned int len) {
if (p == NULL) {
return (len < ZIP_BIG_PREVLEN) ? 1 : sizeof(uint32_t) + 1;
} else {
if (len < ZIP_BIG_PREVLEN) {
p[0] = len;
return 1;
} else {
return zipStorePrevEntryLengthLarge(p,len);
}
}
}
根据ptr[0]返回需要解析前一个条目的字节数
/* Return the number of bytes used to encode the length of the previous
* entry. The length is returned by setting the var 'prevlensize'. */
#define ZIP_DECODE_PREVLENSIZE(ptr, prevlensize) do { \
if ((ptr)[0] < ZIP_BIG_PREVLEN) { \
(prevlensize) = 1; \
} else { \
(prevlensize) = 5; \
} \
} while(0)
返回前一个元素的长度存储于prevlen,以及用于编码前一个元素长度的字节数存储在prevlensize。
'ptr’必须指向一个条目的前缀(编码前一个条目的长度,以便向后导航元素)。
/* Return the length of the previous element, and the number of bytes that
* are used in order to encode the previous element length.
* 'ptr' must point to the prevlen prefix of an entry (that encodes the
* length of the previous entry in order to navigate the elements backward).
* The length of the previous entry is stored in 'prevlen', the number of
* bytes needed to encode the previous entry length are stored nin
* 'prevlensize'. */
#define ZIP_DECODE_PREVLEN(ptr, prevlensize, prevlen) do { \
ZIP_DECODE_PREVLENSIZE(ptr, prevlensize); \
if ((prevlensize) == 1) { \
(prevlen) = (ptr)[0]; \
} else { /* prevlensize == 5 */ \
(prevlen) = ((ptr)[4] << 24) | \
((ptr)[3] << 16) | \
((ptr)[2] << 8) | \
((ptr)[1]); \
} \
} while(0)
/* 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.
*
* So if A is the number of bytes used right now to encode the 'prevlen'
* field.
*
* 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'.
*
* Then the function returns 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. */
int zipPrevLenByteDiff(unsigned char *p, unsigned int len) {
unsigned int prevlensize;
ZIP_DECODE_PREVLENSIZE(p, prevlensize);
return zipStorePrevEntryLength(NULL, len) - prevlensize;
}