previous_entry_length
节点的 previous_entry_length
属性以字节为单位,
记录了压缩列表中前一个节点的长度。
previous_entry_length
属性的长度可以是 1
字节或者 5
字节:
图 7-5 展示了一个包含一字节长 previous_entry_length
属性的压缩列表节点,
属性的值为 0x05
,
表示前一节点的长度为 5
字节。
图 7-6 展示了一个包含五字节长 previous_entry_length
属性的压缩节点,
属性的值为 0xFE00002766
,
其中值的最高位字节 0xFE
表示这是一个五字节长的 previous_entry_length
属性,
而之后的四字节 0x00002766
(十进制值 10086
)才是前一节点的实际长度。
因为节点的 previous_entry_length
属性记录了前一个节点的长度,
所以程序可以通过指针运算,
根据当前节点的起始地址来计算出前一个节点的起始地址。
举个例子,
如果我们有一个指向当前节点起始地址的指针 c
,
那么我们只要用指针 c
减去当前节点 previous_entry_length
属性的值,
就可以得出一个指向前一个节点起始地址的指针 p
,
如图 7-7 所示。
压缩列表的从表尾向表头遍历操作就是使用这一原理实现的:
只要我们拥有了一个指向某个节点起始地址的指针,
那么通过这个指针以及这个节点的 previous_entry_length
属性,
程序就可以一直向前一个节点回溯,
最终到达压缩列表的表头节点。
图 7-8 展示了一个从表尾节点向表头节点进行遍历的完整过程:
首先,我们拥有指向压缩列表表尾节点 entry4
起始地址的指针 p1
(指向表尾节点的指针可以通过指向压缩列表起始地址的指针加上 zltail
属性的值得出);
通过用 p1
减去 entry4
节点 previous_entry_length
属性的值,
我们得到一个指向 entry4
前一节点 entry3
起始地址的指针 p2
;
通过用 p2
减去 entry3
节点 previous_entry_length
属性的值,
我们得到一个指向 entry3
前一节点 entry2
起始地址的指针 p3
;
通过用 p3
减去 entry2
节点 previous_entry_length
属性的值,
我们得到一个指向 entry2
前一节点 entry1
起始地址的指针 p4
,
entry1
为压缩列表的表头节点;
最终,
我们从表尾节点向表头节点遍历了整个列表。
content
节点的 content
属性负责保存节点的值,
节点值可以是一个字节数组或者整数,
值的类型和长度由节点的 encoding
属性决定。
图 7-9 展示了一个保存字节数组的节点示例:
编码的最高两位 00
表示节点保存的是一个字节数组;
编码的后六位 001011
记录了字节数组的长度 11
;
content
属性保存着节点的值 "hello world"
。
图 7-10 展示了一个保存整数值的节点示例: