结合redis设计与实现的redis源码学习-7-ziplist(压缩列表)
来源:互联网 发布:js数组包含另一个数组 编辑:程序博客网 时间:2024/05/20 11:32
压缩列表是列表件和哈希键的底层实现之一,当一个列表件只包含少量列表项,并且每个列表项要么就是小整数值,要么就是长度比较短的字符串,那么redis就会使用压缩列表来做列表键的底层实现。
压缩列表的特点:
1、压缩列表是由一系列特殊编码的连续内存块组成的顺序型数据结构。
2、一个压缩列表可以包含任意多个节点,每个节点可以保存一个字节数组或者一个整数值。
3、添加新节点到压缩列表,或者从压缩列表中删除节点,可能会引发连锁更新操邹,但是出现的几率不高。
redis设计与实现中介绍了压缩列表的各个组成部分的详细说明:
zlbytes:uint32_t,4字节长度,记录整个压缩列表占用的内存字节数:在对压缩列表进行内存重分配或者计算zlend的位置时使用。
zltail:uint32_t,4字节长度,记录压缩列表表尾节点距离压缩列表的起始地址有多少字节:通过这个值可以直接确定表尾节点的地址。
zllen:uint16_t,2字节长度,记录了压缩列表的节点数量:当值小于65535时,这个值就是节点数;当这个值大于65535时,真实数量需要遍历整个压缩列表才能得出。
entryX:列表节点:长度不定:由节点保存的内容决定。
zlend:uint8_t:1字节长度:为0xFF,表示压缩列表的末端。
ziplist.h中定义了压缩列表的方法,ziplist.c中定义了压缩列表的结构及方法实现。
#ifndef _ZIPLIST_H#define _ZIPLIST_H#define ZIPLIST_HEAD 0#define ZIPLIST_TAIL 1unsigned 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);#ifdef REDIS_TESTint ziplistTest(int argc, char *argv[]);#endif#endif /* _ZIPLIST_H */
ziplist.c
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <stdint.h>#include <limits.h>#include "zmalloc.h"#include "util.h"#include "ziplist.h"#include "endianconv.h"#include "redisassert.h"#define ZIP_END 255#define ZIP_BIGLEN 254/* 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 4位长的整形编码*/#define ZIP_INT_IMM_MASK 0x0f#define ZIP_INT_IMM_MIN 0xf1 /* 11110001 */#define ZIP_INT_IMM_MAX 0xfd /* 11111101 */#define ZIP_INT_IMM_VAL(v) (v & ZIP_INT_IMM_MASK)//定义int24的最大最小值#define INT24_MAX 0x7fffff#define INT24_MIN (-INT24_MAX - 1)/* Macro to determine type 返回类型定义*/#define ZIP_IS_STR(enc) (((enc) & ZIP_STR_MASK) < ZIP_STR_MASK)/* Utility macros 一些工具定义*/#define ZIPLIST_BYTES(zl) (*((uint32_t*)(zl)))#define ZIPLIST_TAIL_OFFSET(zl) (*((uint32_t*)((zl)+sizeof(uint32_t))))#define ZIPLIST_LENGTH(zl) (*((uint16_t*)((zl)+sizeof(uint32_t)*2)))#define ZIPLIST_HEADER_SIZE (sizeof(uint32_t)*2+sizeof(uint16_t))#define ZIPLIST_END_SIZE (sizeof(uint8_t))#define ZIPLIST_ENTRY_HEAD(zl) ((zl)+ZIPLIST_HEADER_SIZE)#define ZIPLIST_ENTRY_TAIL(zl) ((zl)+intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl)))#define ZIPLIST_ENTRY_END(zl) ((zl)+intrev32ifbe(ZIPLIST_BYTES(zl))-1)/* We know a positive increment can only be 1 because entries can only be * pushed one at a time. ziplist的增加每次只会是1*/#define ZIPLIST_INCR_LENGTH(zl,incr) { \ if (ZIPLIST_LENGTH(zl) < UINT16_MAX) \ ZIPLIST_LENGTH(zl) = intrev16ifbe(intrev16ifbe(ZIPLIST_LENGTH(zl))+incr); \}//存放数据的节点定义typedef struct zlentry { unsigned int prevrawlensize/*记录上一个节点长度的长度*/, prevrawlen;/*上一个节点长度*/ unsigned int lensize/*当前节点长度*/, len/*保存当前长度所需要的长度*/; unsigned int headersize;//数据节点头部信息长度的字节数 unsigned char encoding;//编码方式 unsigned char *p;//数据} zlentry;//初始化一个节点#define ZIPLIST_ENTRY_ZERO(zle) { \ (zle)->prevrawlensize = (zle)->prevrawlen = 0; \ (zle)->lensize = (zle)->len = (zle)->headersize = 0; \ (zle)->encoding = 0; \ (zle)->p = NULL; \}/* Extract the encoding from the byte pointed by 'ptr' and set it into 返回编码方式 * 'encoding'. */#define ZIP_ENTRY_ENCODING(ptr, encoding) do { \ (encoding) = (ptr[0]); \ if ((encoding) < ZIP_STR_MASK) (encoding) &= ZIP_STR_MASK; \} while(0)/* Return bytes needed to store integer encoded by 'encoding' 返回不同编码的存储长度*/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; default: return 0; /* 4 bit immediate */ } assert(NULL); return 0;}//编码rawlen到p中,如果p是空的,那么就返回所需要的长度unsigned int zipEncodeLength(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;}/* 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 zipPrevEncodeLength(unsigned char *p, unsigned int len) { if (p == NULL) { return (len < ZIP_BIGLEN) ? 1 : sizeof(len)+1; } else { if (len < ZIP_BIGLEN) { p[0] = len; return 1; } else { p[0] = ZIP_BIGLEN; memcpy(p+1,&len,sizeof(len)); memrev32ifbe(p+1); return 1+sizeof(len); } }}/* Encode the length of the previous entry and write it to "p". This only * uses the larger encoding (required in __ziplistCascadeUpdate). 只处理大编码*/void zipPrevEncodeLengthForceLarge(unsigned char *p, unsigned int len) { if (p == NULL) return; p[0] = ZIP_BIGLEN; memcpy(p+1,&len,sizeof(len)); memrev32ifbe(p+1);}/* Decode the number of bytes required to store the length of the previous * element, from the perspective of the entry pointed to by 'ptr'. 返回保存前一节点长度的值的长度*/#define ZIP_DECODE_PREVLENSIZE(ptr, prevlensize) do { \ if ((ptr)[0] < ZIP_BIGLEN) { \ (prevlensize) = 1; \ } else { \ (prevlensize) = 5; \ } \} while(0);/* Decode the length of the previous element, from the perspective of the entry * pointed to by 'ptr'. 解码前一节点的长度*/#define ZIP_DECODE_PREVLEN(ptr, prevlensize, prevlen) do { \ ZIP_DECODE_PREVLENSIZE(ptr, prevlensize); \ if ((prevlensize) == 1) { \ (prevlen) = (ptr)[0]; \ } else if ((prevlensize) == 5) { \ assert(sizeof((prevlensize)) == 4); \ memcpy(&(prevlen), ((char*)(ptr)) + 1, 4); \ memrev32ifbe(&prevlen); \ } \} while(0);/* Return the difference in number of bytes needed to store the length of the * previous element 'len', in the entry pointed to by 'p'. 返回当前节点到前一节点的p的差值*/int zipPrevLenByteDiff(unsigned char *p, unsigned int len) { unsigned int prevlensize; ZIP_DECODE_PREVLENSIZE(p, prevlensize); return zipPrevEncodeLength(NULL, len) - prevlensize;}/* Return the total number of bytes used by the entry pointed to by 'p'. 返回总计使用的长度*/unsigned int zipRawEntryLength(unsigned char *p) { unsigned int prevlensize, encoding, lensize, len; ZIP_DECODE_PREVLENSIZE(p, prevlensize); ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len); return prevlensize + lensize + len;}/* Check if string pointed to by 'entry' can be encoded as an integer. * Stores the integer value in 'v' and its encoding in 'encoding'. */int zipTryEncoding(unsigned char *entry, unsigned int entrylen, long long *v, unsigned char *encoding) { long long value; //判断节点长度是否符合规则 if (entrylen >= 32 || entrylen == 0) return 0; if (string2ll((char*)entry,entrylen,&value)) { /* Great, the string can be encoded. Check what's the smallest * of our encoding types that can hold this value. 返回编码类型*/ if (value >= 0 && value <= 12) { *encoding = ZIP_INT_IMM_MIN+value; } else if (value >= INT8_MIN && value <= INT8_MAX) { *encoding = ZIP_INT_8B; } else if (value >= INT16_MIN && value <= INT16_MAX) { *encoding = ZIP_INT_16B; } else if (value >= INT24_MIN && value <= INT24_MAX) { *encoding = ZIP_INT_24B; } else if (value >= INT32_MIN && value <= INT32_MAX) { *encoding = ZIP_INT_32B; } else { *encoding = ZIP_INT_64B; } *v = value; return 1; } return 0;}/* Store integer 'value' at 'p', encoded as 'encoding' 按编码方式保存整数到p中*/void zipSaveInteger(unsigned char *p, int64_t value, unsigned char encoding) { int16_t i16; int32_t i32; int64_t i64; if (encoding == ZIP_INT_8B) { ((int8_t*)p)[0] = (int8_t)value; } else if (encoding == ZIP_INT_16B) { i16 = value; memcpy(p,&i16,sizeof(i16)); memrev16ifbe(p); } else if (encoding == ZIP_INT_24B) { i32 = value<<8; memrev32ifbe(&i32); memcpy(p,((uint8_t*)&i32)+1,sizeof(i32)-sizeof(uint8_t)); } else if (encoding == ZIP_INT_32B) { i32 = value; memcpy(p,&i32,sizeof(i32)); memrev32ifbe(p); } else if (encoding == ZIP_INT_64B) { i64 = value; memcpy(p,&i64,sizeof(i64)); memrev64ifbe(p); } else if (encoding >= ZIP_INT_IMM_MIN && encoding <= ZIP_INT_IMM_MAX) { /* Nothing to do, the value is stored in the encoding itself. */ } else { assert(NULL); }}/* Read integer encoded as 'encoding' from 'p' 按编码方式在p中读取整数*/int64_t zipLoadInteger(unsigned char *p, unsigned char encoding) { int16_t i16; int32_t i32; int64_t i64, ret = 0; if (encoding == ZIP_INT_8B) { ret = ((int8_t*)p)[0]; } else if (encoding == ZIP_INT_16B) { memcpy(&i16,p,sizeof(i16)); memrev16ifbe(&i16); ret = i16; } else if (encoding == ZIP_INT_32B) { memcpy(&i32,p,sizeof(i32)); memrev32ifbe(&i32); ret = i32; } else if (encoding == ZIP_INT_24B) { i32 = 0; memcpy(((uint8_t*)&i32)+1,p,sizeof(i32)-sizeof(uint8_t)); memrev32ifbe(&i32); ret = i32>>8; } else if (encoding == ZIP_INT_64B) { memcpy(&i64,p,sizeof(i64)); memrev64ifbe(&i64); ret = i64; } else if (encoding >= ZIP_INT_IMM_MIN && encoding <= ZIP_INT_IMM_MAX) { ret = (encoding & ZIP_INT_IMM_MASK)-1; } else { assert(NULL); } return ret;}/* Return a struct with all information about an entry.返回一个条目的所有信息*/void zipEntry(unsigned char *p, zlentry *e) { ZIP_DECODE_PREVLEN(p, e->prevrawlensize, e->prevrawlen); ZIP_DECODE_LENGTH(p + e->prevrawlensize, e->encoding, e->lensize, e->len); e->headersize = e->prevrawlensize + e->lensize; e->p = p;}/* Create a new empty ziplist. 创建一个空的压缩列表*/unsigned char *ziplistNew(void) { unsigned int bytes = ZIPLIST_HEADER_SIZE+1;//创始化长度为头长度 unsigned char *zl = zmalloc(bytes); ZIPLIST_BYTES(zl) = intrev32ifbe(bytes); ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(ZIPLIST_HEADER_SIZE);//初始化偏移量为头长度 ZIPLIST_LENGTH(zl) = 0;//长度置为0 zl[bytes-1] = ZIP_END;//压缩列表结束符 return zl;}/* Resize the ziplist. 重新分配压缩列表的大小*/unsigned char *ziplistResize(unsigned char *zl, unsigned int len) { zl = zrealloc(zl,len); ZIPLIST_BYTES(zl) = intrev32ifbe(len); zl[len-1] = ZIP_END; return zl;}/* Insert item at "p". 在p里插入新条目*/unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) { size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), reqlen; 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; /* 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 = zipRawEntryLength(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 zipEncodeLength 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 += zipPrevEncodeLength(NULL,prevlen); reqlen += zipEncodeLength(NULL,encoding,slen); /* 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; } /* Store offset because a realloc may change the address of zl. */ offset = p-zl; zl = ziplistResize(zl,curlen+reqlen+nextdiff); 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) zipPrevEncodeLengthForceLarge(p+reqlen,reqlen); else zipPrevEncodeLength(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. */ zipEntry(p+reqlen, &tail); 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 += zipPrevEncodeLength(p,prevlen); p += zipEncodeLength(p,encoding,slen); if (ZIP_IS_STR(encoding)) { memcpy(p,s,slen); } else { zipSaveInteger(p,value,encoding); } ZIPLIST_INCR_LENGTH(zl,1); return zl;}//创建一个包含给定值的新节点,并将这个新节点添加到压缩列表的表头或者表尾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); 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. 返回压缩列表给定索引上的节点*/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--) { 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;}/* 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. 在压缩列表中查找并返回包含了给定值得节点*/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); ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len); q = p + prevlensize + lensize; 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. */ 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; } /* Must be non-zero by now */ assert(vencoding); } /* 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. */ if (vencoding != UCHAR_MAX) { long long ll = zipLoadInteger(q, encoding); if (ll == vll) { return p; } } } /* Reset skip count */ skipcnt = skip; } else { /* Skip entry */ skipcnt--; } /* Move to next entry */ p = q + len; } return NULL;}/* Return pointer to next entry in ziplist. * zl is the pointer to the ziplist * p is the pointer to the current element * The element after 'p' is returned, otherwise NULL if we are at the end. 返回给定节点得下一个节点*/unsigned char *ziplistNext(unsigned char *zl, unsigned char *p) { ((void) 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). */ 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. */ if (p[0] == ZIP_END) { p = ZIPLIST_ENTRY_TAIL(zl); return (p[0] == ZIP_END) ? NULL : p; } else if (p == ZIPLIST_ENTRY_HEAD(zl)) { return NULL; } else { ZIP_DECODE_PREVLEN(p, prevlensize, prevlen); assert(prevlen > 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. 获取给定节点所保存的值*/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; if (sstr) *sstr = NULL; zipEntry(p, &entry); 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;}/* Delete "num" entries, starting at "p". Returns pointer to the ziplist. 删除指定索引的节点*/unsigned char *__ziplistDelete(unsigned char *zl, unsigned char *p, unsigned int num) { unsigned int i, totlen, deleted = 0; size_t offset; int nextdiff = 0; zlentry first, tail; zipEntry(p, &first); for (i = 0; p[0] != ZIP_END && i < num; i++) { p += zipRawEntryLength(p); deleted++; } totlen = p-first.p; if (totlen > 0) { if (p[0] != ZIP_END) { /* Storing `prevrawlen` in this entry may increase or decrease the * number of bytes required compare to the current `prevrawlen`. * There always is room to store this, because it was previously * stored by an entry that is now being deleted. */ nextdiff = zipPrevLenByteDiff(p,first.prevrawlen); p -= nextdiff; zipPrevEncodeLength(p,first.prevrawlen); /* Update offset for tail */ ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))-totlen); /* 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. */ zipEntry(p, &tail); if (p[tail.headersize+tail.len] != ZIP_END) { ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+nextdiff); } /* Move tail to the front of the ziplist */ memmove(first.p,p, intrev32ifbe(ZIPLIST_BYTES(zl))-(p-zl)-1); } else { /* The entire tail was deleted. No need to move memory. */ ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe((first.p-zl)-first.prevrawlen); } /* Resize and update length */ offset = first.p-zl; zl = ziplistResize(zl, intrev32ifbe(ZIPLIST_BYTES(zl))-totlen+nextdiff); ZIPLIST_INCR_LENGTH(zl,-deleted); p = zl+offset; /* 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) zl = __ziplistCascadeUpdate(zl,p); } return zl;}/* 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. 从压缩列表中删除给定的节点*/unsigned char *ziplistDelete(unsigned char *zl, unsigned char **p) { size_t offset = *p-zl; zl = __ziplistDelete(zl,*p,1); /* 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 = 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);}/* Return ziplist blob size in bytes. 返回压缩列表占用的内存字节数*/size_t ziplistBlobLen(unsigned char *zl) { return intrev32ifbe(ZIPLIST_BYTES(zl));}/* Return length of ziplist. 返回压缩列表的节点数量,节点数小于65535时为O(1),大于时时O(N)*/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;}
阅读全文
0 0
- 结合redis设计与实现的redis源码学习-7-ziplist(压缩列表)
- redis ziplist压缩列表的源码分析
- 结合redis设计与实现的redis源码学习-8.3-t_list.c(列表键)
- redis源码分析(八)、redis数据结构之压缩ziplist--------ziplist.c ziplist.h学习笔记
- 【redis源码分析】压缩列表---ziplist
- 结合redis设计与实现的redis源码学习-9-zipmap(压缩图)
- Redis源码分析(六)--- ziplist压缩列表
- Redis源码分析(七)——压缩列表Ziplist
- Redis源码剖析和注释(六)--- 压缩列表(ziplist)
- redis学习笔记(6)---压缩列表ziplist
- 【Redis源码剖析】 - Redis内置数据结构之压缩列表ziplist
- Redis-数据结构-压缩列表-ziplist
- 结合redis设计与实现的redis源码学习-8.1-object.c(对象实现)
- 结合redis设计与实现的redis源码学习-17-发布与订阅(pubsub.c)
- 结合redis设计与实现的redis源码学习-1-内存分配(zmalloc)
- 结合redis设计与实现的redis源码学习-2-SDS(简单动态字符串)
- 结合redis设计与实现的redis源码学习-4-dict(字典)
- 结合redis设计与实现的redis源码学习-5-skiplist(跳跃表)
- POJ1275 Cashier Employment (差分约束系统 + 二分答案)
- 车站分级 (线段树优化建边 拓扑序最长路)
- drawlayout侧拉
- 直播 | 脑血管斑块磁共振成像:技术研发、临床转化和数据挑战
- DUBBO+SPRINGMVC+MYBATIS+EHCACHE+REDIS }企业大型互联网分布式架构{JAVA分布式架构}
- 结合redis设计与实现的redis源码学习-7-ziplist(压缩列表)
- Frament
- android String.charAt的使用
- Activity启动模式与任务栈(Task)全面深入记录(上)
- 快速集成视频直播功能
- 如何一次性添加整个文件夹到vs中
- 1
- 作业三 单链表
- Spring Cloud应用程序上下文服务