redis6.0.5之ziplist阅读笔记4--压缩列表(ziplist)之剩余函数
***********************************************************************
/* Merge ziplists 'first' and 'second' by appending 'second' to 'first'.
合并两个压缩列表,第二个压缩列表拼接在第一个后面
*
* NOTE: The larger ziplist is reallocated to contain the new merged ziplist.
* Either 'first' or 'second' can be used for the result. The parameter not
* used will be free'd and set to NULL.
注意: 新合并的大压缩列表需要重新分配空间来存储。
无论第一个压缩列表还是第二个压缩列表都可以用来表示新的合并列表。
不使用的压缩列表参数(一个被用来存储合并后的压缩列表)需要释放和设置为空
* After calling this function, the input parameters are no longer valid since
* they are changed and free'd in-place.
经过这个函数的调用,输入的压缩列表参数不再有效,因为它们不是被改变就是被释放
* The result ziplist is the contents of 'first' followed by 'second'.
最终的压缩列表的内容包含了第一个压缩列表紧随第二个压缩列表的内容。
* On failure: returns NULL if the merge is impossible.
* On success: returns the merged ziplist (which is expanded version of either
* 'first' or 'second', also frees the other unused input ziplist, and sets the
* input ziplist argument equal to newly reallocated ziplist return value. */
失败情况:如果合并是不可能的,返回NULL
成功情况:返回合并的压缩列表(可能是第一个或者第二个压缩列表的扩张版本,剩余的一个输入压缩列表将被释放,
设置输入的压缩列表参数等同于返回的新重新分配的压缩列表)
unsigned char *ziplistMerge(unsigned char **first, unsigned char **second) {
/* If any params are null, we can't merge, so NULL. */
输入为空,不能合并
if (first == NULL || *first == NULL || second == NULL || *second == NULL)
return NULL;
/* Can't merge same list into itself. */ 对同一列表不合并
if (*first == *second)
return NULL;
size_t first_bytes = intrev32ifbe(ZIPLIST_BYTES(*first)); 第一个列表总字节长度
size_t first_len = intrev16ifbe(ZIPLIST_LENGTH(*first)); 第一个列表实体个数
size_t second_bytes = intrev32ifbe(ZIPLIST_BYTES(*second)); 第二个列表总字节长度
size_t second_len = intrev16ifbe(ZIPLIST_LENGTH(*second)); 第二个列表实体个数
int append;
unsigned char *source, *target;
size_t target_bytes, source_bytes;
/* Pick the largest ziplist so we can resize easily in-place.
* We must also track if we are now appending or prepending to
* the target ziplist. */
挑选大的压缩列表作为第一个,这样我们重新分配空间更加容易。
我们还必须追踪 我们是正在后面拼接 还是前面添加 (源压缩列表)到目标压缩列表。
if (first_len >= second_len) {
/* retain first, append second to first. */
1的元素个数大于2, 保持1在前面,拼接2到1的后面
target = *first;
target_bytes = first_bytes;
source = *second;
source_bytes = second_bytes;
append = 1; 后面拼接
} else {
/* else, retain second, prepend first to second. */
2的元素个数多余1,保持2在前面,1添加到2前面
target = *second;
target_bytes = second_bytes;
source = *first;
source_bytes = first_bytes;
append = 0; 前面添加
}
/* Calculate final bytes (subtract one pair of metadata) */
计算新的(合并后的)总字节数(减去其中一对元数据,就是头尾非实体部分)
size_t zlbytes = first_bytes + second_bytes -
ZIPLIST_HEADER_SIZE - ZIPLIST_END_SIZE;
size_t zllength = first_len + second_len; 合并后的实体(元素)个数
/* Combined zl length should be limited within UINT16_MAX */
合并后的总长度不能大于UINT16_MAX,大于就设置成最大值UINT16_MAX
zllength = zllength < UINT16_MAX ? zllength : UINT16_MAX;
/* Save offset positions before we start ripping memory apart. */
在我们操作内存之前先确定尾部偏移量
size_t first_offset = intrev32ifbe(ZIPLIST_TAIL_OFFSET(*first)); 第一个压缩列表的偏移量
size_t second_offset = intrev32ifbe(ZIPLIST_TAIL_OFFSET(*second));第二个压缩列表的偏移量
/* Extend target to new zlbytes then append or prepend source. */
扩展目标列表到新的总字节大小,然后添加或者前面拼接源列表
target = zrealloc(target, zlbytes);
if (append) { 后面拼接的情况
/* append == appending to target */ append为1 表示 拼接到目标后面
/* Copy source after target (copying over original [END]):
* [TARGET - END, SOURCE - HEADER] */
拷贝源压缩列表到目标压缩列表(拷贝过程覆盖原始的结尾标志)
memcpy(target + target_bytes - ZIPLIST_END_SIZE, 目标压缩列表的结尾标志位置
source + ZIPLIST_HEADER_SIZE, 源压缩列表第一个实体开始的位置
source_bytes - ZIPLIST_HEADER_SIZE); 源压缩列表 所有实体 + 结尾标志 的 长度
} else { 前面添加的情况
/* !append == prepending to target */ append为0 表示 添加到目标前面
/* Move target *contents* exactly size of (source - [END]),
* then copy source into vacataed space (source - [END]):
* [SOURCE - END, TARGET - HEADER] */
移动目标的内容向后source - [END]个字节(即腾出源目标实体所需的空间)
然后拷贝源压缩列表的实体到腾出的空间(source - [END]).
memmove(target + source_bytes - ZIPLIST_END_SIZE, 目标压缩列表第一个实体新的开始位置
target + ZIPLIST_HEADER_SIZE, 目标压缩列表第一个实体原来的开始位置
target_bytes - ZIPLIST_HEADER_SIZE); 目标压缩列表的总长度 - 头部长度= 所有实体的长度 + 结尾标志
将目标压缩列表的实体向后移动source_bytes - ZIPLIST_END_SIZE个字节
腾出source_bytes - ZIPLIST_END_SIZE字节大小的空间
memcpy(target, source, source_bytes - ZIPLIST_END_SIZE); 只拷贝源压缩列表的实体
}
/* Update header metadata. */ 更新头部元数据
ZIPLIST_BYTES(target) = intrev32ifbe(zlbytes); 新则总字节长度
ZIPLIST_LENGTH(target) = intrev16ifbe(zllength); 新的总实体(元素)个数
/* New tail offset is:
* + N bytes of first ziplist
* - 1 byte for [END] of first ziplist
* + M bytes for the offset of the original tail of the second ziplist
* - J bytes for HEADER because second_offset keeps no header. */
新的尾部偏移量:
+ 第一个压缩列表的N个字节
- 第一个压缩列表的结尾标志
+ 第二个压缩列表原来的尾部偏移量M个字节
- 第二个列表被除去的头部字节数J
ZIPLIST_TAIL_OFFSET(target) = intrev32ifbe(
(first_bytes - ZIPLIST_END_SIZE) +
(second_offset - ZIPLIST_HEADER_SIZE));
/* __ziplistCascadeUpdate just fixes the prev length values until it finds a
* correct prev length value (then it assumes the rest of the list is okay).
* We tell CascadeUpdate to start at the first ziplist's tail element to fix
* the merge seam. */
函数__ziplistCascadeUpdate修复前一个实体的长度直到找个一个正确的前一个实体的长度值
(然后假设列表剩余部分也是正确的)。我们调用级联更新从第一个压缩列表的尾部元素开始,进行无缝衔接。
(原先第二个列表的第一个实体的前置实体长度为0,现在是第一个列表的最后一个实体长度,所以会引起级联更新)
target = __ziplistCascadeUpdate(target, target+first_offset);
/* Now free and NULL out what we didn't realloc */
if (append) { 如果是后面拼接,就是用的第一个列表扩展,释放掉第二个列表
zfree(*second);
*second = NULL;
*first = target; 返回值指向新的合并列表
} else {如果是前置添加,就是用的第二个列表扩展,释放掉第一个列表
zfree(*first);
*first = NULL;
*second = target;
}
return target;
}
***********************************************************************
根据参数在头部或者尾部插入一个实体
unsigned char *ziplistPush(unsigned char *zl, unsigned char *s, unsigned int slen, int where) {
unsigned char *p;
p = (where == ZIPLIST_HEAD) ? ZIPLIST_ENTRY_HEAD(zl) : ZIPLIST_ENTRY_END(zl);
参数where决定是在头部还是尾部插入一个实体
#define ZIPLIST_HEAD 0
#define ZIPLIST_TAIL 1
return __ziplistInsert(zl,p,s,slen);
}
****************************************************************************
/* Returns an offset to use for iterating with ziplistNext. When the given
* index is negative, the list is traversed back to front. When the list
* doesn't contain an element at the provided index, NULL is returned. */
返回一个偏移量用来在函数ziplistNext中进行迭代。当给出的索引是负数,
就从列表的后面向前面遍历.如果列表在指定位置没有元素(实体),返回NULL
unsigned char *ziplistIndex(unsigned char *zl, int index) {
unsigned char *p;
unsigned int prevlensize, prevlen = 0;
if (index < 0) { 索引为负数,从后往前遍历列表
index = (-index)-1;
p = ZIPLIST_ENTRY_TAIL(zl); 指向最后一个实体开始位置
if (p[0] != ZIP_END) {
ZIP_DECODE_PREVLEN(p, prevlensize, prevlen); 获取前一个实体的编码长度 和 实体长度
while (prevlen > 0 && index--) { 这里表示还没有到第一个实体并且找到了索引所在位置 才结束
prevlen=0表示是第一个实体(这里的条件就是没有到第一个实体)
p -= prevlen; 指向前一个实体
ZIP_DECODE_PREVLEN(p, prevlensize, prevlen);
}
}
} else {
p = ZIPLIST_ENTRY_HEAD(zl); 指向第一个实体开始位置
while (p[0] != ZIP_END && index--) {
p += zipRawEntryLength(p);
}
}
return (p[0] == ZIP_END || index > 0) ? NULL : p;
释放返回空,成功返回指向索引所在实体的指针
p[0] == ZIP_END 表示失败,已经到结尾还没有找到
index > 0 也表示失败,实体不够遍历,提前退出
}
***********************************************************************
/* Return pointer to next entry in ziplist.
返回指向压缩列表中下个实体的指针
* zl is the pointer to the ziplist
zl是指向压缩列表的指针
* p is the pointer to the current element
p是指向当前元素(实体)的指针
* The element after 'p' is returned, otherwise NULL if we are at the end. */
p后面的元素被返回,或者返回空如果我们已经到了尾巴(即当前指针指向的是最后一个实体)
unsigned char *ziplistNext(unsigned char *zl, unsigned char *p) {
((void) zl); 防止编译报警,因为zl在本函数中没有使用
/* "p" could be equal to ZIP_END, caused by ziplistDelete,
* and we should return NULL. Otherwise, we should return NULL
* when the *next* element is ZIP_END (there is no next entry). */
p可能因为函数ziplistDelete指向结尾标志,所以我们返回NULL。
否则如果我们返回NULL意味着 下一个元素是结尾标志(不存在下一个实体)
if (p[0] == ZIP_END) {
return NULL;
}
p += zipRawEntryLength(p); 指向下一个实体的开始位置
if (p[0] == ZIP_END) { 如果是结尾标志
return NULL;
}
return p;
}
***********************************************************************
/* Return pointer to previous entry in ziplist. */
返回指向压缩列表中前一个实体的指针
unsigned char *ziplistPrev(unsigned char *zl, unsigned char *p) {
unsigned int prevlensize, prevlen = 0;
/* Iterating backwards from ZIP_END should return the tail. When "p" is
* equal to the first element of the list, we're already at the head,
* and should return NULL. */
从结尾标志反向迭代应该返回最后一个实体。
当p指向列表的第一个元素时候,我们已经达到了头部,所以返回NULL
if (p[0] == ZIP_END) { 如果是结尾标志
p = ZIPLIST_ENTRY_TAIL(zl); 获取最后一个实体的开始位置
return (p[0] == ZIP_END) ? NULL : p;
发现还是指向结尾标志,说明这个列表没有实体,返回空,否则返回最后一个实体
} else if (p == ZIPLIST_ENTRY_HEAD(zl)) { 已经指向了第一个实体开始位置,返回NULL
return NULL;
} else { 其余情况,还存在其它实体
ZIP_DECODE_PREVLEN(p, prevlensize, prevlen); 获取前一个实体的相关信息
assert(prevlen > 0); 确认前一个实体长度大于0,正常肯定要大于0
return p-prevlen; 返回前一个实体的开始位置
}
}
***********************************************************************
/* Get entry pointed to by 'p' and store in either '*sstr' or 'sval' depending
* on the encoding of the entry. '*sstr' is always set to NULL to be able
* to find out whether the string pointer or the integer value was set.
* Return 0 if 'p' points to the end of the ziplist, 1 otherwise. */
获取p指向的实体,根据实体的编码存储在 '*sstr' 或者 'sval' 中。
'*sstr'总是设置为NULL,可以用来发现是否是字符串指针或者整数值被设置。
返回0如果p指向了压缩列表的结尾标志,否则返回1
unsigned int ziplistGet(unsigned char *p, unsigned char **sstr, unsigned int *slen, long long *sval) {
zlentry entry;
if (p == NULL || p[0] == ZIP_END) return 0; 列表不存在或者指向了结尾标志,返回0
if (sstr) *sstr = NULL; 传入了指针sstr,初始化为NULL
zipEntry(p, &entry); 获取p指向的实体信息
if (ZIP_IS_STR(entry.encoding)) { 如果是字符串
if (sstr) {
*slen = entry.len; 字符串长度
*sstr = p+entry.headersize; 指向字符串开始的位置
}
} else { 如果是整数
if (sval) {
*sval = zipLoadInteger(p+entry.headersize,entry.encoding); 获取整数的值
}
}
return 1;
}
***********************************************************************
/* Insert an entry at "p". */
在p处插入一个实体
unsigned char *ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {
return __ziplistInsert(zl,p,s,slen);
}
***********************************************************************
/* Delete a single entry from the ziplist, pointed to by *p.
* Also update *p in place, to be able to iterate over the
* ziplist, while deleting entries. */
从压缩列表删除*p指向的一个实体,
同时更新p的指向,以便于删除实体是偶可以迭代遍历压缩列表
unsigned char *ziplistDelete(unsigned char *zl, unsigned char **p) {
size_t offset = *p-zl;
zl = __ziplistDelete(zl,*p,1); 只删除*p指向的实体
/* Store pointer to current element in p, because ziplistDelete will
* do a realloc which might result in a different "zl"-pointer.
* When the delete direction is back to front, we might delete the last
* entry and end up with "p" pointing to ZIP_END, so check this. */
存储p指向当前的元素,因为函数ziplistDelete会导致重新分配内存空间,可能会导致指针zl出现变化。
当删除的方向是从后向前,我们会删除最后的实体,从而p指向结尾标志,所以我们做了这个操作。
(就是当存在实体时候,保存了p始终指向下一个实体,因为*p-zl的举例不会改变,前面实体没有变动过)
*p = zl+offset; 所以这里就指向了原来的下一个实体初始位置
return zl;
}
***********************************************************************
/* Delete a range of entries from the ziplist. */
删除压缩列表一个范围内的所有实体
unsigned char *ziplistDeleteRange(unsigned char *zl, int index, unsigned int num) {
unsigned char *p = ziplistIndex(zl,index); 找到索引所在的实体
return (p == NULL) ? zl : __ziplistDelete(zl,p,num);
索引位置所在的实体不为空,就删除连续的num个实体
}
***********************************************************************
/* Compare entry pointer to by 'p' with 'sstr' of length 'slen'. */
/* Return 1 if equal. */
比较指针p指向的实体和长度为'slen'的字符串'sstr', 相等返回1
unsigned int ziplistCompare(unsigned char *p, unsigned char *sstr, unsigned int slen) {
zlentry entry;
unsigned char sencoding;
long long zval, sval;
if (p[0] == ZIP_END) return 0; 如果p指向了结束标志,返回0
zipEntry(p, &entry); 获取实体信息
if (ZIP_IS_STR(entry.encoding)) { 是字符串编码
/* Raw compare */
if (entry.len == slen) { 长度相等
return memcmp(p+entry.headersize,sstr,slen) == 0; 返回比较结果
} else {
return 0; 长度不等明显就不相等
}
} else { 是整数编码
/* Try to compare encoded values. Don't compare encoding because
* different implementations may encoded integers differently. */
比较编码的值,不要比较编码,因为不同的实现会导致整数编码不同(难道指的是不同版本的编码方式可能不一样?)
if (zipTryEncoding(sstr,slen,&sval,&sencoding)) {
函数zipTryEncoding获取传入字符串的 值 sval 和 编码方式sencoding
如果能够成功转化数字,才有比较的可能性
zval = zipLoadInteger(p+entry.headersize,entry.encoding);
获取指针p指向实体的值
return zval == sval;
}
}
return 0;
}
***********************************************************************
/* Find pointer to the entry equal to the specified entry. Skip 'skip' entries
* between every comparison. Returns NULL when the field could not be found. */
查找指向 等同于特定实体的 指针。每次比较跳过'skip'个实体(即步长为skip)
返回NULL如果值没有被找到相应实体
unsigned char *ziplistFind(unsigned char *p, unsigned char *vstr, unsigned int vlen, unsigned int skip) {
int skipcnt = 0;
unsigned char vencoding = 0;
long long vll = 0;
while (p[0] != ZIP_END) {
unsigned int prevlensize, encoding, lensize, len;
unsigned char *q;
ZIP_DECODE_PREVLENSIZE(p, prevlensize);
获取前一个实体长度的编码长度prevlensize
ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len);
获取当前实体的内容长度编码lensize,内容的长度值len
q = p + prevlensize + lensize; q指向当前实体具体的内容
if (skipcnt == 0) {
/* Compare current entry with specified entry */ 比较当前实体和特定实体
if (ZIP_IS_STR(encoding)) { 如果是字符串,则进行字符串比较
if (len == vlen && memcmp(q, vstr, vlen) == 0) { 相等找到的情况
return p;
}
} else {
/* Find out if the searched field can be encoded. Note that
* we do it only the first time, once done vencoding is set
* to non-zero and vll is set to the integer value. */
查找的字段是否可以编码。注意我们只在第一次做这个事情,
一旦做了,vencoding就被设置为非0,vll就被设置为整数值
if (vencoding == 0) { 只做一次,即第一次
if (!zipTryEncoding(vstr, vlen, &vll, &vencoding)) {
/* If the entry can't be encoded we set it to
* UCHAR_MAX so that we don't retry again the next
* time. */
如果实体不能被整数编码,我们设置vencoding为UCHAR_MAX,
这样我们就下次就不会在尝试编码vstr这个值
vencoding = UCHAR_MAX;
}
/* Must be non-zero by now */
assert(vencoding); 确定现在不等于0了,编码至少有一个值
}
/* Compare current entry with specified entry, do it only
* if vencoding != UCHAR_MAX because if there is no encoding
* possible for the field it can't be a valid integer. */
只有当vencoding != UCHAR_MAX时比较当前实体和特定实体,
因为如果字段没有编码那么它不会是一个有效的整数(所以这种情况不用比较)
if (vencoding != UCHAR_MAX) { 有效的情况才比较
long long ll = zipLoadInteger(q, encoding);获取p指向实体中的值
if (ll == vll) {
return p;
}
}
}
/* Reset skip count */ 改变步长,skip设置为0就是一个也不跳
skipcnt = skip;
} else {
/* Skip entry */ 忽略的实体
skipcnt--;
}
/* Move to next entry */指向下个实体
p = q + len; q 已经指向了内容开始位置 + 内容本身的长度 = 下个实体开始的位置
}
return NULL;
}
***********************************************************************
/* Return length of ziplist. */
返回列表的长度
unsigned int ziplistLen(unsigned char *zl) {
unsigned int len = 0;
if (intrev16ifbe(ZIPLIST_LENGTH(zl)) < UINT16_MAX) {
如果没有超出范围,直接取即可
len = intrev16ifbe(ZIPLIST_LENGTH(zl));
} else {
超出范围了,老老实实挨个挨个计数
unsigned char *p = zl+ZIPLIST_HEADER_SIZE; 指向第一个实体开始位置
while (*p != ZIP_END) {
p += zipRawEntryLength(p); 指向下一个实体开始位置
len++; 计数++
}
/* Re-store length if small enough */如果数量变少了,重置列表实体个数
if (len < UINT16_MAX) ZIPLIST_LENGTH(zl) = intrev16ifbe(len);
}
return len;
}
***********************************************************************
/* Return ziplist blob size in bytes. */
返回列表的总字节数
size_t ziplistBlobLen(unsigned char *zl) {
return intrev32ifbe(ZIPLIST_BYTES(zl));
}
***********************************************************************