【redis源码】(三)Zipmap

更科学的分析见这篇文章,小弟看了之后才明白一点点的

http://www.open-open.com/bbs/view/1321697543077

 

下面几句是我自己对zipmap的理解

1. zipmap是一个简单的使用字符串实现的hash表,按照作者的本意,只会包含254以内的的key-value对

2. 整个redis来看,redis是一个巨大地hash表【key-value对】,只不过这个大hash表的value不只支持字符串,而是redisObject,redisObject中的void * ptr指针可以指向任何数据结构~~~【强悍的扩展性,咱可以自己添加】

3. dict乃是redis实现hash表的数据结构,这是个复杂的东西【如果entry量很小,用起来效率很低】,作者为了节省内存,当redisobject为一个hashmap并且entry数量很小【<254】,value会使用轻量级的zipmap作为map的承载体. zip本身的操作复杂度是O(n)【n为key-value对的数量】

 

 

哦了~ 贴代码,只是加了一些本人的注释,没啥特别的,供自己做个记录用~

Ziphash.h

 1 #ifndef _ZIMMAP_H
 2 #define _ZIPMAP_H
 3 
 4 unsigned char *zipmapNew(void);
 5 unsigned char *zipmapSet(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned char *val, unsigned int vlen, int *update);
 6 unsigned char *zipmapDel(unsigned char *zm, unsigned char *key, unsigned int klen, int *deleted);
 7 unsigned char *zipmapRewind(unsigned char *zm);
 8 unsigned char *zipmapNext(unsigned char *zm, unsigned char **key, unsigned int *klen, unsigned char **value, unsigned int *vlen);
 9 int zipmapGet(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned char **value, unsigned int *vlen);
10 int zipmapExists(unsigned char *zm, unsigned char *key, unsigned int klen);
11 unsigned int zipmapLen(unsigned char *zm);
12 void zipmapRepr(unsigned char *p);
13 
14 #endif

Ziphash.c

  1 #include <stdio.h>
  2 #include <string.h>
  3 #include <assert.h>
  4 #include "zmalloc.h"
  5 
  6 //如果len字段为254,则后4个字节表示真正长度
  7 #define ZIPMAP_BIGLEN 254
  8 //zipmap串最后一个byte的值
  9 #define ZIPMAP_END 255
 10 
 11 /* The following defines the max value for the <free> field described in the
 12  * comments above, that is, the max number of trailing bytes in a value. */
 13 //如果free字段大于此值,则进行压缩操作,节省空间
 14 #define ZIPMAP_VALUE_MAX_FREE 4
 15 
 16 /* The following macro returns the number of bytes needed to encode the length
 17  * for the integer value _l, that is, 1 byte for lengths < ZIPMAP_BIGLEN and
 18  * 5 bytes for all the other lengths. */
 19 //这个宏用来计算表达len的真实长度,如果小于254,则len占用1个字节,否则占用4+1个字节
 20 #define ZIPMAP_LEN_BYTES(_l) (((_l) < ZIPMAP_BIGLEN) ? 1 : sizeof(unsigned int)+1)
 21 
 22 //创建一个新的zipmap,这个空zipmap占用2个byte的内存空间
 23 /* Create a new empty zipmap. */
 24 unsigned char *zipmapNew(void) {
 25     unsigned char *zm = zmalloc(2);
 26 
 27     zm[0] = 0; /* Length */
 28     zm[1] = ZIPMAP_END;
 29     return zm;
 30 }
 31 
 32 
 33 //如果长度p所在的unsigned char的小于254,即使后边跟随的string的长度,如果大于等于254,即后边的4个bytes为真实长度
 34 /* Decode the encoded length pointed by 'p' */
 35 static unsigned int zipmapDecodeLength(unsigned char *p) {
 36     unsigned int len = *p;
 37 
 38     if (len < ZIPMAP_BIGLEN) return len;
 39     memcpy(&len,p+1,sizeof(unsigned int));
 40     return len;
 41 }
 42 
 43 
 44 //输入value或者key的长度len,将zipmap自有的长度表示方法的字符串放到p开头的字符串中,如果p为NULL直接返回占用的byte字节数
 45 /* Encode the length 'l' writing it in 'p'. If p is NULL it just returns
 46  * the amount of bytes required to encode such a length. */
 47 static unsigned int zipmapEncodeLength(unsigned char *p, unsigned int len) {
 48     if (p == NULL) {
 49         return ZIPMAP_LEN_BYTES(len);
 50     } else {
 51         if (len < ZIPMAP_BIGLEN) {
 52             p[0] = len;
 53             return 1;
 54         } else {
 55             p[0] = ZIPMAP_BIGLEN;
 56             memcpy(p+1,&len,sizeof(len));
 57             return 1+sizeof(len);
 58         }
 59     }
 60 }
 61 
 62 //在zm中寻找长度为klen的key,如果找到,返回其所为key的<len>字段的指针,如果没找到返回NULL.
 63 //如果totlen字段不为NULL,则使其为整个zm所占的bytes数
 64 /* Search for a matching key, returning a pointer to the entry inside the
 65  * zipmap. Returns NULL if the key is not found.
 66  *
 67  * If NULL is returned, and totlen is not NULL, it is set to the entire
 68  * size of the zimap, so that the calling function will be able to
 69  * reallocate the original zipmap to make room for more entries. */
 70 static unsigned char *zipmapLookupRaw(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned int *totlen) {
 71     unsigned char *p = zm+1, *k = NULL;
 72     unsigned int l,llen;
 73 
 74     while(*p != ZIPMAP_END) { //如果未到结尾
 75         unsigned char free;
 76 
 77         /* Match or skip the key */
 78         l = zipmapDecodeLength(p); //得到value或者key长度大小
 79         llen = zipmapEncodeLength(NULL,l); // 得到len本身占的byte数
 80         //如果长度和cmp结果为0(相同)
 81         if (k == NULL && l == klen && !memcmp(p+llen,key,l)) {
 82             /* Only return when the user doesn't care
 83              * for the total length of the zipmap. */
 84             if (totlen != NULL) {
 85                 k = p;
 86             } else { //如果totlen为NULL直接返回指针p
 87                 return p;
 88             }
 89         }
 90         p += llen+l;
 91         /* Skip the value as well */
 92         l = zipmapDecodeLength(p);
 93         p += zipmapEncodeLength(NULL,l);
 94         free = p[0];
 95         p += l+1+free; /* +1 to skip the free byte */
 96     }
 97     if (totlen != NULL) *totlen = (unsigned int)(p-zm)+1;
 98     return k;
 99 }
100 
101 
102 //得到一个只保存一个keyvalue对的zipmap所需要的bytes数
103 static unsigned long zipmapRequiredLength(unsigned int klen, unsigned int vlen) {
104     unsigned int l;
105 
106     l = klen+vlen+3; //key_len+value_len+value_free
107     if (klen >= ZIPMAP_BIGLEN) l += 4; 
108     if (vlen >= ZIPMAP_BIGLEN) l += 4;
109     return l;
110 }
111 
112 //得到一个key所需要的内存大小
113 /* Return the total amount used by a key (encoded length + payload) */
114 static unsigned int zipmapRawKeyLength(unsigned char *p) {
115     unsigned int l = zipmapDecodeLength(p);
116     return zipmapEncodeLength(NULL,l) + l;
117 }
118 
119 //p为value的首地址,计算value段占用的内存bytes数
120 /* Return the total amount used by a value
121  * (encoded length + single byte free count + payload) */
122 static unsigned int zipmapRawValueLength(unsigned char *p) {
123     unsigned int l = zipmapDecodeLength(p);
124     unsigned int used;
125     
126     used = zipmapEncodeLength(NULL,l);
127     used += p[used] + 1 + l; //p[used] free的字节数+ free本身一个字节+len长度(1 or 5)
128     return used;
129 }
130 
131 //返回整个p指向的key-value对所占用的内存空间大小
132 /* If 'p' points to a key, this function returns the total amount of
133  * bytes used to store this entry (entry = key + associated value + trailing
134  * free space if any). */
135 static unsigned int zipmapRawEntryLength(unsigned char *p) {
136     unsigned int l = zipmapRawKeyLength(p);
137     return l + zipmapRawValueLength(p+l);
138 }
139 
140 
141 //realloc 内存for zm
142 static inline unsigned char *zipmapResize(unsigned char *zm, unsigned int len) {
143     zm = zrealloc(zm, len);
144     zm[len-1] = ZIPMAP_END;
145     return zm;
146 }
147 
148 //插入一个key-value对,如果key不存在,则直接resize zm,加到最后边的位置
149 //如果key存在,并且空间够用,则直接在原位置对value进行替换,更新free,如果free小于ZIPMAP_VALUE_MAX_FREE,则保留free,如果大于等于ZIPMAP_VALUE_MAX_FREE,则需要尽心memmove
150 /* Set key to value, creating the key if it does not already exist.
151  * If 'update' is not NULL, *update is set to 1 if the key was
152  * already preset, otherwise to 0. */
153 unsigned char *zipmapSet(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned char *val, unsigned int vlen, int *update) {
154     unsigned int zmlen, offset;
155     unsigned int freelen, reqlen = zipmapRequiredLength(klen,vlen);
156     unsigned int empty, vempty;
157     unsigned char *p;
158    
159     freelen = reqlen;
160     if (update) *update = 0;
161     p = zipmapLookupRaw(zm,key,klen,&zmlen);
162     if (p == NULL) {
163         /* Key not found: enlarge */
164         zm = zipmapResize(zm, zmlen+reqlen);
165         p = zm+zmlen-1;
166         zmlen = zmlen+reqlen;
167 
168         /* Increase zipmap length (this is an insert) */
169         if (zm[0] < ZIPMAP_BIGLEN) zm[0]++;
170     } else {
171         /* Key found. Is there enough space for the new value? */
172         /* Compute the total length: */
173         if (update) *update = 1;
174         freelen = zipmapRawEntryLength(p);
175         if (freelen < reqlen) {
176             /* Store the offset of this key within the current zipmap, so
177              * it can be resized. Then, move the tail backwards so this
178              * pair fits at the current position. */
179             offset = p-zm;
180             zm = zipmapResize(zm, zmlen-freelen+reqlen);
181             p = zm+offset;
182 
183             /* The +1 in the number of bytes to be moved is caused by the
184              * end-of-zipmap byte. Note: the *original* zmlen is used. */
185             memmove(p+reqlen, p+freelen, zmlen-(offset+freelen+1));
186             zmlen = zmlen-freelen+reqlen;
187             freelen = reqlen;
188         }
189     }
190 
191     /* We now have a suitable block where the key/value entry can
192      * be written. If there is too much free space, move the tail
193      * of the zipmap a few bytes to the front and shrink the zipmap,
194      * as we want zipmaps to be very space efficient. */
195     empty = freelen-reqlen;
196     if (empty >= ZIPMAP_VALUE_MAX_FREE) {
197         /* First, move the tail <empty> bytes to the front, then resize
198          * the zipmap to be <empty> bytes smaller. */
199         offset = p-zm;
200         memmove(p+reqlen, p+freelen, zmlen-(offset+freelen+1));
201         zmlen -= empty;
202         zm = zipmapResize(zm, zmlen);
203         p = zm+offset;
204         vempty = 0;
205     } else {
206         vempty = empty;
207     }
208 
209     /* Just write the key + value and we are done. */
210     /* Key: */
211     p += zipmapEncodeLength(p,klen);
212     memcpy(p,key,klen);
213     p += klen;
214     /* Value: */
215     p += zipmapEncodeLength(p,vlen);
216     *p++ = vempty;
217     memcpy(p,val,vlen);
218     return zm;
219 }
220 
221 
222 //删除指定key,并用deleted来indicated执行结果
223 /* Remove the specified key. If 'deleted' is not NULL the pointed integer is
224  * set to 0 if the key was not found, to 1 if it was found and deleted. */
225 unsigned char *zipmapDel(unsigned char *zm, unsigned char *key, unsigned int klen, int *deleted) {
226     unsigned int zmlen, freelen;
227     unsigned char *p = zipmapLookupRaw(zm,key,klen,&zmlen);
228     if (p) {
229         freelen = zipmapRawEntryLength(p);
230         memmove(p, p+freelen, zmlen-((p-zm)+freelen+1));
231         zm = zipmapResize(zm, zmlen-freelen);
232 
233         /* Decrease zipmap length */
234         if (zm[0] < ZIPMAP_BIGLEN) zm[0]--;
235 
236         if (deleted) *deleted = 1;
237     } else {
238         if (deleted) *deleted = 0;
239     }
240     return zm;
241 }
242 
243 /* Call it before to iterate trought elements via zipmapNext() */
244 unsigned char *zipmapRewind(unsigned char *zm) {
245     return zm+1;
246 }
247 
248 /* This function is used to iterate through all the zipmap elements.
249  * In the first call the first argument is the pointer to the zipmap + 1.
250  * In the next calls what zipmapNext returns is used as first argument.
251  * Example:
252  *
253  * unsigned char *i = zipmapRewind(my_zipmap);
254  * while((i = zipmapNext(i,&key,&klen,&value,&vlen)) != NULL) {
255  *     printf("%d bytes key at $p\n", klen, key);
256  *     printf("%d bytes value at $p\n", vlen, value);
257  * }
258  */
259 unsigned char *zipmapNext(unsigned char *zm, unsigned char **key, unsigned int *klen, unsigned char **value, unsigned int *vlen) {
260     if (zm[0] == ZIPMAP_END) return NULL;
261     if (key) {
262         *key = zm;
263         *klen = zipmapDecodeLength(zm);
264         *key += ZIPMAP_LEN_BYTES(*klen);
265     }
266     zm += zipmapRawKeyLength(zm);
267     if (value) {
268         *value = zm+1;
269         *vlen = zipmapDecodeLength(zm);
270         *value += ZIPMAP_LEN_BYTES(*vlen);
271     }
272     zm += zipmapRawValueLength(zm);
273     return zm;
274 }
275 
276 //取得一个key对应的value,如果找到key,返回1,否则返回0
277 /* Search a key and retrieve the pointer and len of the associated value.
278  * If the key is found the function returns 1, otherwise 0. */
279 int zipmapGet(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned char **value, unsigned int *vlen) {
280     unsigned char *p;
281 
282     if ((p = zipmapLookupRaw(zm,key,klen,NULL)) == NULL) return 0;
283     p += zipmapRawKeyLength(p);
284     *vlen = zipmapDecodeLength(p);
285     *value = p + ZIPMAP_LEN_BYTES(*vlen) + 1;
286     return 1;
287 }
288 
289 //判断一个key是否存在于zm中
290 /* Return 1 if the key exists, otherwise 0 is returned. */
291 int zipmapExists(unsigned char *zm, unsigned char *key, unsigned int klen) {
292     return zipmapLookupRaw(zm,key,klen,NULL) != NULL;
293 }
294 
295 /* Return the number of entries inside a zipmap */
296 unsigned int zipmapLen(unsigned char *zm) {
297     unsigned int len = 0;
298     if (zm[0] < ZIPMAP_BIGLEN) {
299         len = zm[0];
300     } else {
301         unsigned char *p = zipmapRewind(zm);
302         while((p = zipmapNext(p,NULL,NULL,NULL,NULL)) != NULL) len++;
303 
304         /* Re-store length if small enough */
305         if (len < ZIPMAP_BIGLEN) zm[0] = len;
306     }
307     return len;
308 }
309 
310 //打印p开头的kye-value对
311 void zipmapRepr(unsigned char *p) {
312     unsigned int l;
313 
314     printf("{status %u}",*p++);
315     while(1) {
316         if (p[0] == ZIPMAP_END) {
317             printf("{end}");
318             break;
319         } else {
320             unsigned char e;
321 
322             l = zipmapDecodeLength(p);
323             printf("{key %u}",l);
324             p += zipmapEncodeLength(NULL,l);
325             if (l != 0 && fwrite(p,l,1,stdout) == 0) perror("fwrite");
326             p += l;
327 
328             l = zipmapDecodeLength(p);
329             printf("{value %u}",l);
330             p += zipmapEncodeLength(NULL,l);
331             e = *p++;
332             if (l != 0 && fwrite(p,l,1,stdout) == 0) perror("fwrite");
333             p += l+e;
334             if (e) {
335                 printf("[");
336                 while(e--) printf(".");
337                 printf("]");
338             }
339         }
340     }
341     printf("\n");
342 }
343 
344 #ifdef ZIPMAP_TEST_MAIN
345 int main(void) {
346     unsigned char *zm;
347 
348     zm = zipmapNew();
349 
350     zm = zipmapSet(zm,(unsigned char*) "name",4, (unsigned char*) "foo",3,NULL);
351     zm = zipmapSet(zm,(unsigned char*) "surname",7, (unsigned char*) "foo",3,NULL);
352     zm = zipmapSet(zm,(unsigned char*) "age",3, (unsigned char*) "foo",3,NULL);
353     zipmapRepr(zm);
354 
355     zm = zipmapSet(zm,(unsigned char*) "hello",5, (unsigned char*) "world!",6,NULL);
356     zm = zipmapSet(zm,(unsigned char*) "foo",3, (unsigned char*) "bar",3,NULL);
357     zm = zipmapSet(zm,(unsigned char*) "foo",3, (unsigned char*) "!",1,NULL);
358     zipmapRepr(zm);
359     zm = zipmapSet(zm,(unsigned char*) "foo",3, (unsigned char*) "12345",5,NULL);
360     zipmapRepr(zm);
361     zm = zipmapSet(zm,(unsigned char*) "new",3, (unsigned char*) "xx",2,NULL);
362     zm = zipmapSet(zm,(unsigned char*) "noval",5, (unsigned char*) "",0,NULL);
363     zipmapRepr(zm);
364     zm = zipmapDel(zm,(unsigned char*) "new",3,NULL);
365     zipmapRepr(zm);
366 
367     printf("\nLook up large key:\n");
368     {
369         unsigned char buf[512];
370         unsigned char *value;
371         unsigned int vlen, i;
372         for (i = 0; i < 512; i++) buf[i] = 'a';
373 
374         zm = zipmapSet(zm,buf,512,(unsigned char*) "long",4,NULL);
375         if (zipmapGet(zm,buf,512,&value,&vlen)) {
376             printf("  <long key> is associated to the %d bytes value: %.*s\n",
377                 vlen, vlen, value);
378         }
379     }
380 
381     printf("\nPerform a direct lookup:\n");
382     {
383         unsigned char *value;
384         unsigned int vlen;
385 
386         if (zipmapGet(zm,(unsigned char*) "foo",3,&value,&vlen)) {
387             printf("  foo is associated to the %d bytes value: %.*s\n",
388                 vlen, vlen, value);
389         }
390     }
391     printf("\nIterate trought elements:\n");
392     {
393         unsigned char *i = zipmapRewind(zm);
394         unsigned char *key, *value;
395         unsigned int klen, vlen;
396 
397         while((i = zipmapNext(i,&key,&klen,&value,&vlen)) != NULL) {
398             printf("  %d:%.*s => %d:%.*s\n", klen, klen, key, vlen, vlen, value);
399         }
400     }
401     return 0;
402 }
403 #endif

下一篇将介绍ziplist~~

posted @ 2012-08-27 16:06  ~嘉言懿行~~我是煲仔饭~~  阅读(893)  评论(0编辑  收藏  举报