redis-ziplist-源码解读

325 阅读2分钟

1、关于ziplist压缩列表的源码文件有两个,分别为ziplist.h和ziplist.c,好了直接上源码。

我先申明一点,这里并不会贴出完整的源码。

2、先看下ziplist.h这个头文件里有啥呢?

两个宏定义,用0表示ziplist的头部,1表示ziplist的尾部
#define ZIPLIST_HEAD 0
#define ZIPLIST_TAIL 1
一堆函数定义,先大概混个眼熟吧,好了头文件就是一些定义
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 int ziplistCompare(unsigned char *p, unsigned char *s, unsigned int slen);
unsigned char *ziplistFind(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);

3、那么主要的实现都在ziplist.c中,接下来让我们详细看看吧

#define ZIP_END 255
前一个元素最大的长度是254,通过后面的源码知道,他是不包括254#define ZIP_BIG_PREVLEN 254
计算出该ziplist的占用字节数,就是有多大(zlbytes)
#define ZIPLIST_BYTES(zl)       (*((uint32_t*)(zl)))
计算ziplist表尾节点距离ziplist的起始地址的偏移量,也就是zltail的大小
#define ZIPLIST_TAIL_OFFSET(zl) (*((uint32_t*)((zl)+sizeof(uint32_t))))
计算ziplist包含的节点数量,也就是zllen指针存储的值
#define ZIPLIST_LENGTH(zl)  (*((uint16_t*)((zl)+sizeof(uint32_t)*2)))
计算ziplist的头部大小,也就是zlbytes+zltail+zllen的和为10字节
#define ZIPLIST_HEADER_SIZE     (sizeof(uint32_t)*2+sizeof(uint16_t))
计算zlend的大小为一字节
#define ZIPLIST_END_SIZE        (sizeof(uint8_t))
计算出ziplist第一个元素的指针地址
#define ZIPLIST_ENTRY_HEAD(zl)  ((zl)+ZIPLIST_HEADER_SIZE)
计算出ziplist最后一个元素的指针地址
#define ZIPLIST_ENTRY_TAIL(zl) ((zl)+intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl)))
计算出ziplist指向zlend的指针地址
#define ZIPLIST_ENTRY_END(zl)   ((zl)+intrev32ifbe(ZIPLIST_BYTES(zl))-1)
增加ziplist元素的数量,前提是小于65535,因为zllen只有2字节
#define ZIPLIST_INCR_LENGTH(zl,incr) { \
if (ZIPLIST_LENGTH(zl) < UINT16_MAX) \
    ZIPLIST_LENGTH(zl) = intrev16ifbe(intrev16ifbe(ZIPLIST_LENGTH(zl))+incr); \
}
下面这些是关于encoding的一些定义,首先压缩列表节点可 以保存字节数组和整数值两种
#define ZIP_STR_MASK 0xc0
#define ZIP_INT_MASK 0x30
判断编码是不是字节数组
#define ZIP_IS_STR(enc) (((enc) & ZIP_STR_MASK) < ZIP_STR_MASK)
三种字节数组
#define ZIP_STR_06B (0 << 6)最高位是00,剩下6位表示长度,1字节
#define ZIP_STR_14B (1 << 6)最高位是01,剩下14位表示长度,2字节
#define ZIP_STR_32B (2 << 6)最高位是10,后四字节表示长度,5字节
六种整数值,都是1字节,最高位是11
#define ZIP_INT_16B (0xc0 | 0<<4)剩下6位是000000
#define ZIP_INT_32B (0xc0 | 1<<4)剩下6位是010000
#define ZIP_INT_64B (0xc0 | 2<<4)剩下6位是100000
#define ZIP_INT_24B (0xc0 | 3<<4)剩下6位是110000
#define ZIP_INT_8B 0xfe剩下6位是111110表示8位有符号整数,如果11开头,最后四位,表示0~12的无符号整数
下面这几个定义就是区分是不是以1111开头的,并且它的范围
#define ZIP_INT_IMM_MASK 0x0f   
#define ZIP_INT_IMM_MIN 0xf1    /* 11110001 */
#define ZIP_INT_IMM_MAX 0xfd    /* 11111101 */
24位有符号整数的范围
#define INT24_MAX 0x7fffff
#define INT24_MIN (-INT24_MAX - 1)
ziplist节点的结构体
typedef struct zlentry {
    unsigned int prevrawlensize; 前一个元素长度的字节数(15)
    unsigned int prevrawlen; 实际的长度值
    unsigned int lensize;encoding的长度(125)
    unsigned int len;表示实际内容的字节
    unsigned int headersize; headersize=prevrawlensize+lensize
    unsigned char encoding;编码类型,如果是1111开头的四位直接整数,后面就是实际的长度 
    unsigned char *p; 指向实际存储节点的开始部分的指针地址,p[0]==255,表示ziplist中没有节点
} zlentry;
定义一个0的ziplist元素
#define ZIPLIST_ENTRY_ZERO(zle) { \
    (zle)->prevrawlensize = (zle)->prevrawlen = 0; \
    (zle)->lensize = (zle)->len = (zle)->headersize = 0; \
    (zle)->encoding = 0; \
    (zle)->p = NULL; \
}
从ptr中提取编码,并给encoding赋值
#define ZIP_ENTRY_ENCODING(ptr, encoding) do {  \
  (encoding) = (ptr[0]); \
  if ((encoding) < ZIP_STR_MASK) (encoding) &= ZIP_STR_MASK; \
} while(0)
返回需要存储'encoding'编码的整数的字节
unsigned int zipIntSize(unsigned char encoding){}