/* 返回压缩表zl的第index个结点,
index为非负:从表头开始查找,0表示返回第一个结点
index为负:从表尾开始查找,-1表示返回最后一个结点
*/
unsigned char *ziplistIndex(unsigned char *zl, int index) {
unsigned char *p; /* 保存目标结点地址 */
zlentry entry; /* 未对改变量进行初始化,考虑到该变量会被函数zipEntry返回值直接赋值,可以原谅未初始化 */
if (index < 0) {
/* 将索引转换为正数,因为负数索引从-1开始,所以转换为正数之后索引值-1,
保证索引为-1,通过计算获得索引是0,指向的是从表尾开始查找时的最后一个结点 */
index = (-index) - 1;
/* p指向表尾结点,即最后一个结点 */
p = ZIPLIST_ENTRY_TAIL(zl);
/* 如果压缩表不为空 */
if (p[0] != ZIP_END) {
entry = zipEntry(p); /* 只有列表不为空时,p才指向一个结点,才能获得p指向的结点信息entry */
/* 通过遍历index次获得指定索引的结点 */
/* prevrawlen:前置结点的长度 */
while (entry.prevrawlen > 0 && index--) {
p -= entry.prevrawlen;
entry = zipEntry(p); /* 必须确保前置结点长度大于0(entry.prevrawlen > 0),该操作才有意义 */
}
}
} else {
/* 获得压缩表的第一个结点指针 */
p = ZIPLIST_ENTRY_HEAD(zl);
/* 通过遍历index次获得指定索引的结点 */
/* p[0] != ZIP_END:保证p执行的结点是有效结点 */
while (p[0] != ZIP_END && index--) {
p += zipRawEntryLength(p); /* 函数zipRawEntryLength功能:返回p指向的结点占用的字节长度 */
}
}
/* p[0] == ZIP_END:指定的索引值大于0时查找,查找到表尾,还没有查找到指定索引的结点,
或者p[0] == ZIP_END的同时,index=0,此时索引值大于压缩表的结点数,仍然返回NULL;
index > 0:指定的索引值小于0时查找,已经到表头,index仍然大于0,说明指定的索引值超出压缩表的索引范围*/
return (p[0] == ZIP_END || index > 0) ? NULL : p;
}