]> git.saurik.com Git - redis.git/blob - src/ziplist.c
Make ziplist schema more efficient for strings with length > 15
[redis.git] / src / ziplist.c
1 /* The ziplist is a specially encoded dually linked list that is designed
2 * to be very memory efficient. It stores both strings and integer values,
3 * where integers are encoded as actual integers instead of a series of
4 * characters. It allows push and pop operations on either side of the list
5 * in O(1) time. However, because every operation requires a reallocation of
6 * the memory used by the ziplist, the actual complexity is related to the
7 * amount of memory used by the ziplist.
8 *
9 * ----------------------------------------------------------------------------
10 *
11 * ZIPLIST OVERALL LAYOUT:
12 * The general layout of the ziplist is as follows:
13 * <zlbytes><zltail><zllen><entry><entry><zlend>
14 *
15 * <zlbytes> is an unsigned integer to hold the number of bytes that the
16 * ziplist occupies. This value needs to be stored to be able to resize the
17 * entire structure without the need to traverse it first.
18 *
19 * <zltail> is the offset to the last entry in the list. This allows a pop
20 * operation on the far side of the list without the need for full traversal.
21 *
22 * <zllen> is the number of entries.When this value is larger than 2**16-2,
23 * we need to traverse the entire list to know how many items it holds.
24 *
25 * <zlend> is a single byte special value, equal to 255, which indicates the
26 * end of the list.
27 *
28 * ZIPLIST ENTRIES:
29 * Every entry in the ziplist is prefixed by a header that contains two pieces
30 * of information. First, the length of the previous entry is stored to be
31 * able to traverse the list from back to front. Second, the encoding with an
32 * optional string length of the entry itself is stored.
33 *
34 * The length of the previous entry is encoded in the following way:
35 * If this length is smaller than 254 bytes, it will only consume a single
36 * byte that takes the length as value. When the length is greater than or
37 * equal to 254, it will consume 5 bytes. The first byte is set to 254 to
38 * indicate a larger value is following. The remaining 4 bytes take the
39 * length of the previous entry as value.
40 *
41 * The other header field of the entry itself depends on the contents of the
42 * entry. When the entry is a string, the first 2 bits of this header will hold
43 * the type of encoding used to store the length of the string, followed by the
44 * actual length of the string. When the entry is an integer the first 2 bits
45 * are both set to 1. The following 2 bits are used to specify what kind of
46 * integer will be stored after this header. An overview of the different
47 * types and encodings is as follows:
48 *
49 * |00pppppp| - 1 byte
50 * String value with length less than or equal to 63 bytes (6 bits).
51 * |01pppppp|qqqqqqqq| - 2 bytes
52 * String value with length less than or equal to 16383 bytes (14 bits).
53 * |10______|qqqqqqqq|rrrrrrrr|ssssssss|tttttttt| - 5 bytes
54 * String value with length greater than or equal to 16384 bytes.
55 * |1100____| - 1 byte
56 * Integer encoded as int16_t (2 bytes).
57 * |1101____| - 1 byte
58 * Integer encoded as int32_t (4 bytes).
59 * |1110____| - 1 byte
60 * Integer encoded as int64_t (8 bytes).
61 */
62
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <stdint.h>
67 #include <assert.h>
68 #include <limits.h>
69 #include "zmalloc.h"
70 #include "ziplist.h"
71
72 int ll2string(char *s, size_t len, long long value);
73
74 #define ZIP_END 255
75 #define ZIP_BIGLEN 254
76
77 /* Different encoding/length possibilities */
78 #define ZIP_STR_06B (0 << 6)
79 #define ZIP_STR_14B (1 << 6)
80 #define ZIP_STR_32B (2 << 6)
81 #define ZIP_INT_16B (0xc0 | 0<<4)
82 #define ZIP_INT_32B (0xc0 | 1<<4)
83 #define ZIP_INT_64B (0xc0 | 2<<4)
84
85 /* Macro's to determine type */
86 #define ZIP_IS_STR(enc) (((enc) & 0xc0) < 0xc0)
87 #define ZIP_IS_INT(enc) (!ZIP_IS_STR(enc) && ((enc) & 0x30) < 0x30)
88
89 /* Utility macros */
90 #define ZIPLIST_BYTES(zl) (*((uint32_t*)(zl)))
91 #define ZIPLIST_TAIL_OFFSET(zl) (*((uint32_t*)((zl)+sizeof(uint32_t))))
92 #define ZIPLIST_LENGTH(zl) (*((uint16_t*)((zl)+sizeof(uint32_t)*2)))
93 #define ZIPLIST_HEADER_SIZE (sizeof(uint32_t)*2+sizeof(uint16_t))
94 #define ZIPLIST_ENTRY_HEAD(zl) ((zl)+ZIPLIST_HEADER_SIZE)
95 #define ZIPLIST_ENTRY_TAIL(zl) ((zl)+ZIPLIST_TAIL_OFFSET(zl))
96 #define ZIPLIST_ENTRY_END(zl) ((zl)+ZIPLIST_BYTES(zl)-1)
97
98 /* We know a positive increment can only be 1 because entries can only be
99 * pushed one at a time. */
100 #define ZIPLIST_INCR_LENGTH(zl,incr) { \
101 if (ZIPLIST_LENGTH(zl) < UINT16_MAX) ZIPLIST_LENGTH(zl)+=incr; }
102
103 typedef struct zlentry {
104 unsigned int prevrawlensize, prevrawlen;
105 unsigned int lensize, len;
106 unsigned int headersize;
107 unsigned char encoding;
108 unsigned char *p;
109 } zlentry;
110
111 /* Return the encoding pointer to by 'p'. */
112 static unsigned int zipEntryEncoding(unsigned char *p) {
113 /* String encoding: 2 MSBs */
114 unsigned char b = p[0] & 0xc0;
115 if (b < 0xc0) {
116 return b;
117 } else {
118 /* Integer encoding: 4 MSBs */
119 return p[0] & 0xf0;
120 }
121 assert(NULL);
122 }
123
124 /* Return bytes needed to store integer encoded by 'encoding' */
125 static unsigned int zipIntSize(unsigned char encoding) {
126 switch(encoding) {
127 case ZIP_INT_16B: return sizeof(int16_t);
128 case ZIP_INT_32B: return sizeof(int32_t);
129 case ZIP_INT_64B: return sizeof(int64_t);
130 }
131 assert(NULL);
132 }
133
134 /* Decode the encoded length pointed by 'p'. If a pointer to 'lensize' is
135 * provided, it is set to the number of bytes required to encode the length. */
136 static unsigned int zipDecodeLength(unsigned char *p, unsigned int *lensize) {
137 unsigned char encoding = zipEntryEncoding(p);
138 unsigned int len;
139
140 if (ZIP_IS_STR(encoding)) {
141 switch(encoding) {
142 case ZIP_STR_06B:
143 len = p[0] & 0x3f;
144 if (lensize) *lensize = 1;
145 break;
146 case ZIP_STR_14B:
147 len = ((p[0] & 0x3f) << 6) | p[1];
148 if (lensize) *lensize = 2;
149 break;
150 case ZIP_STR_32B:
151 len = (p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4];
152 if (lensize) *lensize = 5;
153 break;
154 default:
155 assert(NULL);
156 }
157 } else {
158 len = zipIntSize(encoding);
159 if (lensize) *lensize = 1;
160 }
161 return len;
162 }
163
164 /* Encode the length 'l' writing it in 'p'. If p is NULL it just returns
165 * the amount of bytes required to encode such a length. */
166 static unsigned int zipEncodeLength(unsigned char *p, unsigned char encoding, unsigned int rawlen) {
167 unsigned char len = 1, buf[5];
168
169 if (ZIP_IS_STR(encoding)) {
170 /* Although encoding is given it may not be set for strings,
171 * so we determine it here using the raw length. */
172 if (rawlen <= 0x3f) {
173 if (!p) return len;
174 buf[0] = ZIP_STR_06B | rawlen;
175 } else if (rawlen <= 0x3fff) {
176 len += 1;
177 if (!p) return len;
178 buf[0] = ZIP_STR_14B | ((rawlen >> 8) & 0x3f);
179 buf[1] = rawlen & 0xff;
180 } else {
181 len += 4;
182 if (!p) return len;
183 buf[0] = ZIP_STR_32B;
184 buf[1] = (rawlen >> 24) & 0xff;
185 buf[2] = (rawlen >> 16) & 0xff;
186 buf[3] = (rawlen >> 8) & 0xff;
187 buf[4] = rawlen & 0xff;
188 }
189 } else {
190 /* Implies integer encoding, so length is always 1. */
191 if (!p) return len;
192 buf[0] = encoding;
193 }
194
195 /* Store this length at p */
196 memcpy(p,buf,len);
197 return len;
198 }
199
200 /* Decode the length of the previous element stored at "p". */
201 static unsigned int zipPrevDecodeLength(unsigned char *p, unsigned int *lensize) {
202 unsigned int len = *p;
203 if (len < ZIP_BIGLEN) {
204 if (lensize) *lensize = 1;
205 } else {
206 if (lensize) *lensize = 1+sizeof(len);
207 memcpy(&len,p+1,sizeof(len));
208 }
209 return len;
210 }
211
212 /* Encode the length of the previous entry and write it to "p". Return the
213 * number of bytes needed to encode this length if "p" is NULL. */
214 static unsigned int zipPrevEncodeLength(unsigned char *p, unsigned int len) {
215 if (p == NULL) {
216 return (len < ZIP_BIGLEN) ? 1 : sizeof(len)+1;
217 } else {
218 if (len < ZIP_BIGLEN) {
219 p[0] = len;
220 return 1;
221 } else {
222 p[0] = ZIP_BIGLEN;
223 memcpy(p+1,&len,sizeof(len));
224 return 1+sizeof(len);
225 }
226 }
227 }
228
229 /* Return the difference in number of bytes needed to store the new length
230 * "len" on the entry pointed to by "p". */
231 static int zipPrevLenByteDiff(unsigned char *p, unsigned int len) {
232 unsigned int prevlensize;
233 zipPrevDecodeLength(p,&prevlensize);
234 return zipPrevEncodeLength(NULL,len)-prevlensize;
235 }
236
237 /* Check if string pointed to by 'entry' can be encoded as an integer.
238 * Stores the integer value in 'v' and its encoding in 'encoding'. */
239 static int zipTryEncoding(unsigned char *entry, unsigned int entrylen, long long *v, unsigned char *encoding) {
240 long long value;
241 char *eptr;
242 char buf[32];
243
244 if (entrylen >= 32 || entrylen == 0) return 0;
245 if (entry[0] == '-' || (entry[0] >= '0' && entry[0] <= '9')) {
246 int slen;
247
248 /* Perform a back-and-forth conversion to make sure that
249 * the string turned into an integer is not losing any info. */
250 memcpy(buf,entry,entrylen);
251 buf[entrylen] = '\0';
252 value = strtoll(buf,&eptr,10);
253 if (eptr[0] != '\0') return 0;
254 slen = ll2string(buf,32,value);
255 if (entrylen != (unsigned)slen || memcmp(buf,entry,slen)) return 0;
256
257 /* Great, the string can be encoded. Check what's the smallest
258 * of our encoding types that can hold this value. */
259 if (value >= INT16_MIN && value <= INT16_MAX) {
260 *encoding = ZIP_INT_16B;
261 } else if (value >= INT32_MIN && value <= INT32_MAX) {
262 *encoding = ZIP_INT_32B;
263 } else {
264 *encoding = ZIP_INT_64B;
265 }
266 *v = value;
267 return 1;
268 }
269 return 0;
270 }
271
272 /* Store integer 'value' at 'p', encoded as 'encoding' */
273 static void zipSaveInteger(unsigned char *p, int64_t value, unsigned char encoding) {
274 int16_t i16;
275 int32_t i32;
276 int64_t i64;
277 if (encoding == ZIP_INT_16B) {
278 i16 = value;
279 memcpy(p,&i16,sizeof(i16));
280 } else if (encoding == ZIP_INT_32B) {
281 i32 = value;
282 memcpy(p,&i32,sizeof(i32));
283 } else if (encoding == ZIP_INT_64B) {
284 i64 = value;
285 memcpy(p,&i64,sizeof(i64));
286 } else {
287 assert(NULL);
288 }
289 }
290
291 /* Read integer encoded as 'encoding' from 'p' */
292 static int64_t zipLoadInteger(unsigned char *p, unsigned char encoding) {
293 int16_t i16;
294 int32_t i32;
295 int64_t i64, ret;
296 if (encoding == ZIP_INT_16B) {
297 memcpy(&i16,p,sizeof(i16));
298 ret = i16;
299 } else if (encoding == ZIP_INT_32B) {
300 memcpy(&i32,p,sizeof(i32));
301 ret = i32;
302 } else if (encoding == ZIP_INT_64B) {
303 memcpy(&i64,p,sizeof(i64));
304 ret = i64;
305 } else {
306 assert(NULL);
307 }
308 return ret;
309 }
310
311 /* Return a struct with all information about an entry. */
312 static zlentry zipEntry(unsigned char *p) {
313 zlentry e;
314 e.prevrawlen = zipPrevDecodeLength(p,&e.prevrawlensize);
315 e.len = zipDecodeLength(p+e.prevrawlensize,&e.lensize);
316 e.headersize = e.prevrawlensize+e.lensize;
317 e.encoding = zipEntryEncoding(p+e.prevrawlensize);
318 e.p = p;
319 return e;
320 }
321
322 /* Return the total number of bytes used by the entry at "p". */
323 static unsigned int zipRawEntryLength(unsigned char *p) {
324 zlentry e = zipEntry(p);
325 return e.headersize + e.len;
326 }
327
328 /* Create a new empty ziplist. */
329 unsigned char *ziplistNew(void) {
330 unsigned int bytes = ZIPLIST_HEADER_SIZE+1;
331 unsigned char *zl = zmalloc(bytes);
332 ZIPLIST_BYTES(zl) = bytes;
333 ZIPLIST_TAIL_OFFSET(zl) = ZIPLIST_HEADER_SIZE;
334 ZIPLIST_LENGTH(zl) = 0;
335 zl[bytes-1] = ZIP_END;
336 return zl;
337 }
338
339 /* Resize the ziplist. */
340 static unsigned char *ziplistResize(unsigned char *zl, unsigned int len) {
341 zl = zrealloc(zl,len);
342 ZIPLIST_BYTES(zl) = len;
343 zl[len-1] = ZIP_END;
344 return zl;
345 }
346
347 /* Delete "num" entries, starting at "p". Returns pointer to the ziplist. */
348 static unsigned char *__ziplistDelete(unsigned char *zl, unsigned char *p, unsigned int num) {
349 unsigned int i, totlen, deleted = 0;
350 int nextdiff = 0;
351 zlentry first = zipEntry(p);
352 for (i = 0; p[0] != ZIP_END && i < num; i++) {
353 p += zipRawEntryLength(p);
354 deleted++;
355 }
356
357 totlen = p-first.p;
358 if (totlen > 0) {
359 if (p[0] != ZIP_END) {
360 /* Tricky: storing the prevlen in this entry might reduce or
361 * increase the number of bytes needed, compared to the current
362 * prevlen. Note that we can always store this length because
363 * it was previously stored by an entry that is being deleted. */
364 nextdiff = zipPrevLenByteDiff(p,first.prevrawlen);
365 zipPrevEncodeLength(p-nextdiff,first.prevrawlen);
366
367 /* Update offset for tail */
368 ZIPLIST_TAIL_OFFSET(zl) -= totlen+nextdiff;
369
370 /* Move tail to the front of the ziplist */
371 memmove(first.p,p-nextdiff,ZIPLIST_BYTES(zl)-(p-zl)-1+nextdiff);
372 } else {
373 /* The entire tail was deleted. No need to move memory. */
374 ZIPLIST_TAIL_OFFSET(zl) = (first.p-zl)-first.prevrawlen;
375 }
376
377 /* Resize and update length */
378 zl = ziplistResize(zl, ZIPLIST_BYTES(zl)-totlen+nextdiff);
379 ZIPLIST_INCR_LENGTH(zl,-deleted);
380 }
381 return zl;
382 }
383
384 /* Insert item at "p". */
385 static unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {
386 unsigned int curlen = ZIPLIST_BYTES(zl), reqlen, prevlen = 0;
387 unsigned int offset, nextdiff = 0;
388 unsigned char *tail;
389 unsigned char encoding = 0;
390 long long value;
391 zlentry entry;
392
393 /* Find out prevlen for the entry that is inserted. */
394 if (p[0] != ZIP_END) {
395 entry = zipEntry(p);
396 prevlen = entry.prevrawlen;
397 } else {
398 tail = ZIPLIST_ENTRY_TAIL(zl);
399 if (tail[0] != ZIP_END) {
400 prevlen = zipRawEntryLength(tail);
401 }
402 }
403
404 /* See if the entry can be encoded */
405 if (zipTryEncoding(s,slen,&value,&encoding)) {
406 /* 'encoding' is set to the appropriate integer encoding */
407 reqlen = zipIntSize(encoding);
408 } else {
409 /* 'encoding' is untouched, however zipEncodeLength will use the
410 * string length to figure out how to encode it. */
411 reqlen = slen;
412 }
413 /* We need space for both the length of the previous entry and
414 * the length of the payload. */
415 reqlen += zipPrevEncodeLength(NULL,prevlen);
416 reqlen += zipEncodeLength(NULL,encoding,slen);
417
418 /* When the insert position is not equal to the tail, we need to
419 * make sure that the next entry can hold this entry's length in
420 * its prevlen field. */
421 nextdiff = (p[0] != ZIP_END) ? zipPrevLenByteDiff(p,reqlen) : 0;
422
423 /* Store offset because a realloc may change the address of zl. */
424 offset = p-zl;
425 zl = ziplistResize(zl,curlen+reqlen+nextdiff);
426 p = zl+offset;
427
428 /* Apply memory move when necessary and update tail offset. */
429 if (p[0] != ZIP_END) {
430 /* Subtract one because of the ZIP_END bytes */
431 memmove(p+reqlen,p-nextdiff,curlen-offset-1+nextdiff);
432 /* Encode this entry's raw length in the next entry. */
433 zipPrevEncodeLength(p+reqlen,reqlen);
434 /* Update offset for tail */
435 ZIPLIST_TAIL_OFFSET(zl) += reqlen+nextdiff;
436 } else {
437 /* This element will be the new tail. */
438 ZIPLIST_TAIL_OFFSET(zl) = p-zl;
439 }
440
441 /* Write the entry */
442 p += zipPrevEncodeLength(p,prevlen);
443 p += zipEncodeLength(p,encoding,slen);
444 if (ZIP_IS_STR(encoding)) {
445 memcpy(p,s,slen);
446 } else {
447 zipSaveInteger(p,value,encoding);
448 }
449 ZIPLIST_INCR_LENGTH(zl,1);
450 return zl;
451 }
452
453 unsigned char *ziplistPush(unsigned char *zl, unsigned char *s, unsigned int slen, int where) {
454 unsigned char *p;
455 p = (where == ZIPLIST_HEAD) ? ZIPLIST_ENTRY_HEAD(zl) : ZIPLIST_ENTRY_END(zl);
456 return __ziplistInsert(zl,p,s,slen);
457 }
458
459 /* Returns an offset to use for iterating with ziplistNext. When the given
460 * index is negative, the list is traversed back to front. When the list
461 * doesn't contain an element at the provided index, NULL is returned. */
462 unsigned char *ziplistIndex(unsigned char *zl, int index) {
463 unsigned char *p;
464 zlentry entry;
465 if (index < 0) {
466 index = (-index)-1;
467 p = ZIPLIST_ENTRY_TAIL(zl);
468 if (p[0] != ZIP_END) {
469 entry = zipEntry(p);
470 while (entry.prevrawlen > 0 && index--) {
471 p -= entry.prevrawlen;
472 entry = zipEntry(p);
473 }
474 }
475 } else {
476 p = ZIPLIST_ENTRY_HEAD(zl);
477 while (p[0] != ZIP_END && index--) {
478 p += zipRawEntryLength(p);
479 }
480 }
481 return (p[0] == ZIP_END || index > 0) ? NULL : p;
482 }
483
484 /* Return pointer to next entry in ziplist. */
485 unsigned char *ziplistNext(unsigned char *zl, unsigned char *p) {
486 ((void) zl);
487
488 /* "p" could be equal to ZIP_END, caused by ziplistDelete,
489 * and we should return NULL. Otherwise, we should return NULL
490 * when the *next* element is ZIP_END (there is no next entry). */
491 if (p[0] == ZIP_END) {
492 return NULL;
493 } else {
494 p = p+zipRawEntryLength(p);
495 return (p[0] == ZIP_END) ? NULL : p;
496 }
497 }
498
499 /* Return pointer to previous entry in ziplist. */
500 unsigned char *ziplistPrev(unsigned char *zl, unsigned char *p) {
501 zlentry entry;
502
503 /* Iterating backwards from ZIP_END should return the tail. When "p" is
504 * equal to the first element of the list, we're already at the head,
505 * and should return NULL. */
506 if (p[0] == ZIP_END) {
507 p = ZIPLIST_ENTRY_TAIL(zl);
508 return (p[0] == ZIP_END) ? NULL : p;
509 } else if (p == ZIPLIST_ENTRY_HEAD(zl)) {
510 return NULL;
511 } else {
512 entry = zipEntry(p);
513 return p-entry.prevrawlen;
514 }
515 }
516
517 /* Get entry pointer to by 'p' and store in either 'e' or 'v' depending
518 * on the encoding of the entry. 'e' is always set to NULL to be able
519 * to find out whether the string pointer or the integer value was set.
520 * Return 0 if 'p' points to the end of the zipmap, 1 otherwise. */
521 unsigned int ziplistGet(unsigned char *p, unsigned char **sstr, unsigned int *slen, long long *sval) {
522 zlentry entry;
523 if (p == NULL || p[0] == ZIP_END) return 0;
524 if (sstr) *sstr = NULL;
525
526 entry = zipEntry(p);
527 if (ZIP_IS_STR(entry.encoding)) {
528 if (sstr) {
529 *slen = entry.len;
530 *sstr = p+entry.headersize;
531 }
532 } else {
533 if (sval) {
534 *sval = zipLoadInteger(p+entry.headersize,entry.encoding);
535 }
536 }
537 return 1;
538 }
539
540 /* Insert an entry at "p". */
541 unsigned char *ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {
542 return __ziplistInsert(zl,p,s,slen);
543 }
544
545 /* Delete a single entry from the ziplist, pointed to by *p.
546 * Also update *p in place, to be able to iterate over the
547 * ziplist, while deleting entries. */
548 unsigned char *ziplistDelete(unsigned char *zl, unsigned char **p) {
549 unsigned int offset = *p-zl;
550 zl = __ziplistDelete(zl,*p,1);
551
552 /* Store pointer to current element in p, because ziplistDelete will
553 * do a realloc which might result in a different "zl"-pointer.
554 * When the delete direction is back to front, we might delete the last
555 * entry and end up with "p" pointing to ZIP_END, so check this. */
556 *p = zl+offset;
557 return zl;
558 }
559
560 /* Delete a range of entries from the ziplist. */
561 unsigned char *ziplistDeleteRange(unsigned char *zl, unsigned int index, unsigned int num) {
562 unsigned char *p = ziplistIndex(zl,index);
563 return (p == NULL) ? zl : __ziplistDelete(zl,p,num);
564 }
565
566 /* Compare entry pointer to by 'p' with 'entry'. Return 1 if equal. */
567 unsigned int ziplistCompare(unsigned char *p, unsigned char *sstr, unsigned int slen) {
568 zlentry entry;
569 unsigned char sencoding;
570 long long zval, sval;
571 if (p[0] == ZIP_END) return 0;
572
573 entry = zipEntry(p);
574 if (ZIP_IS_STR(entry.encoding)) {
575 /* Raw compare */
576 if (entry.len == slen) {
577 return memcmp(p+entry.headersize,sstr,slen) == 0;
578 } else {
579 return 0;
580 }
581 } else {
582 /* Try to compare encoded values */
583 if (zipTryEncoding(sstr,slen,&sval,&sencoding)) {
584 if (entry.encoding == sencoding) {
585 zval = zipLoadInteger(p+entry.headersize,entry.encoding);
586 return zval == sval;
587 }
588 }
589 }
590 return 0;
591 }
592
593 /* Return length of ziplist. */
594 unsigned int ziplistLen(unsigned char *zl) {
595 unsigned int len = 0;
596 if (ZIPLIST_LENGTH(zl) < UINT16_MAX) {
597 len = ZIPLIST_LENGTH(zl);
598 } else {
599 unsigned char *p = zl+ZIPLIST_HEADER_SIZE;
600 while (*p != ZIP_END) {
601 p += zipRawEntryLength(p);
602 len++;
603 }
604
605 /* Re-store length if small enough */
606 if (len < UINT16_MAX) ZIPLIST_LENGTH(zl) = len;
607 }
608 return len;
609 }
610
611 /* Return size in bytes of ziplist. */
612 unsigned int ziplistSize(unsigned char *zl) {
613 return ZIPLIST_BYTES(zl);
614 }
615
616 void ziplistRepr(unsigned char *zl) {
617 unsigned char *p;
618 zlentry entry;
619
620 printf("{total bytes %d} {length %u}\n",ZIPLIST_BYTES(zl), ZIPLIST_LENGTH(zl));
621 p = ZIPLIST_ENTRY_HEAD(zl);
622 while(*p != ZIP_END) {
623 entry = zipEntry(p);
624 printf("{offset %ld, header %u, payload %u} ",p-zl,entry.headersize,entry.len);
625 p += entry.headersize;
626 if (ZIP_IS_STR(entry.encoding)) {
627 fwrite(p,entry.len,1,stdout);
628 } else {
629 printf("%lld", (long long) zipLoadInteger(p,entry.encoding));
630 }
631 printf("\n");
632 p += entry.len;
633 }
634 printf("{end}\n\n");
635 }
636
637 #ifdef ZIPLIST_TEST_MAIN
638 #include <sys/time.h>
639
640 unsigned char *createList() {
641 unsigned char *zl = ziplistNew();
642 zl = ziplistPush(zl, (unsigned char*)"foo", 3, ZIPLIST_TAIL);
643 zl = ziplistPush(zl, (unsigned char*)"quux", 4, ZIPLIST_TAIL);
644 zl = ziplistPush(zl, (unsigned char*)"hello", 5, ZIPLIST_HEAD);
645 zl = ziplistPush(zl, (unsigned char*)"1024", 4, ZIPLIST_TAIL);
646 return zl;
647 }
648
649 unsigned char *createIntList() {
650 unsigned char *zl = ziplistNew();
651 char buf[32];
652
653 sprintf(buf, "100");
654 zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);
655 sprintf(buf, "128000");
656 zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);
657 sprintf(buf, "-100");
658 zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_HEAD);
659 sprintf(buf, "4294967296");
660 zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_HEAD);
661 sprintf(buf, "non integer");
662 zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);
663 sprintf(buf, "much much longer non integer");
664 zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);
665 return zl;
666 }
667
668 long long usec(void) {
669 struct timeval tv;
670 gettimeofday(&tv,NULL);
671 return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;
672 }
673
674 void stress(int pos, int num, int maxsize, int dnum) {
675 int i,j,k;
676 unsigned char *zl;
677 char posstr[2][5] = { "HEAD", "TAIL" };
678 long long start;
679 for (i = 0; i < maxsize; i+=dnum) {
680 zl = ziplistNew();
681 for (j = 0; j < i; j++) {
682 zl = ziplistPush(zl,(unsigned char*)"quux",4,ZIPLIST_TAIL);
683 }
684
685 /* Do num times a push+pop from pos */
686 start = usec();
687 for (k = 0; k < num; k++) {
688 zl = ziplistPush(zl,(unsigned char*)"quux",4,pos);
689 zl = ziplistDeleteRange(zl,0,1);
690 }
691 printf("List size: %8d, bytes: %8d, %dx push+pop (%s): %6lld usec\n",
692 i,ZIPLIST_BYTES(zl),num,posstr[pos],usec()-start);
693 zfree(zl);
694 }
695 }
696
697 void pop(unsigned char *zl, int where) {
698 unsigned char *p, *vstr;
699 unsigned int vlen;
700 long long vlong;
701
702 p = ziplistIndex(zl,where == ZIPLIST_HEAD ? 0 : -1);
703 if (ziplistGet(p,&vstr,&vlen,&vlong)) {
704 if (where == ZIPLIST_HEAD)
705 printf("Pop head: ");
706 else
707 printf("Pop tail: ");
708
709 if (vstr)
710 fwrite(vstr,vlen,1,stdout);
711 else
712 printf("%lld", vlong);
713
714 printf("\n");
715 ziplistDeleteRange(zl,-1,1);
716 } else {
717 printf("ERROR: Could not pop\n");
718 exit(1);
719 }
720 }
721
722 int main(int argc, char **argv) {
723 unsigned char *zl, *p;
724 unsigned char *entry;
725 unsigned int elen;
726 long long value;
727
728 zl = createIntList();
729 ziplistRepr(zl);
730
731 zl = createList();
732 ziplistRepr(zl);
733
734 pop(zl,ZIPLIST_TAIL);
735 ziplistRepr(zl);
736
737 pop(zl,ZIPLIST_HEAD);
738 ziplistRepr(zl);
739
740 pop(zl,ZIPLIST_TAIL);
741 ziplistRepr(zl);
742
743 pop(zl,ZIPLIST_TAIL);
744 ziplistRepr(zl);
745
746 printf("Get element at index 3:\n");
747 {
748 zl = createList();
749 p = ziplistIndex(zl, 3);
750 if (!ziplistGet(p, &entry, &elen, &value)) {
751 printf("ERROR: Could not access index 3\n");
752 return 1;
753 }
754 if (entry) {
755 fwrite(entry,elen,1,stdout);
756 printf("\n");
757 } else {
758 printf("%lld\n", value);
759 }
760 printf("\n");
761 }
762
763 printf("Get element at index 4 (out of range):\n");
764 {
765 zl = createList();
766 p = ziplistIndex(zl, 4);
767 if (p == NULL) {
768 printf("No entry\n");
769 } else {
770 printf("ERROR: Out of range index should return NULL, returned offset: %ld\n", p-zl);
771 return 1;
772 }
773 printf("\n");
774 }
775
776 printf("Get element at index -1 (last element):\n");
777 {
778 zl = createList();
779 p = ziplistIndex(zl, -1);
780 if (!ziplistGet(p, &entry, &elen, &value)) {
781 printf("ERROR: Could not access index -1\n");
782 return 1;
783 }
784 if (entry) {
785 fwrite(entry,elen,1,stdout);
786 printf("\n");
787 } else {
788 printf("%lld\n", value);
789 }
790 printf("\n");
791 }
792
793 printf("Get element at index -4 (first element):\n");
794 {
795 zl = createList();
796 p = ziplistIndex(zl, -4);
797 if (!ziplistGet(p, &entry, &elen, &value)) {
798 printf("ERROR: Could not access index -4\n");
799 return 1;
800 }
801 if (entry) {
802 fwrite(entry,elen,1,stdout);
803 printf("\n");
804 } else {
805 printf("%lld\n", value);
806 }
807 printf("\n");
808 }
809
810 printf("Get element at index -5 (reverse out of range):\n");
811 {
812 zl = createList();
813 p = ziplistIndex(zl, -5);
814 if (p == NULL) {
815 printf("No entry\n");
816 } else {
817 printf("ERROR: Out of range index should return NULL, returned offset: %ld\n", p-zl);
818 return 1;
819 }
820 printf("\n");
821 }
822
823 printf("Iterate list from 0 to end:\n");
824 {
825 zl = createList();
826 p = ziplistIndex(zl, 0);
827 while (ziplistGet(p, &entry, &elen, &value)) {
828 printf("Entry: ");
829 if (entry) {
830 fwrite(entry,elen,1,stdout);
831 } else {
832 printf("%lld", value);
833 }
834 p = ziplistNext(zl,p);
835 printf("\n");
836 }
837 printf("\n");
838 }
839
840 printf("Iterate list from 1 to end:\n");
841 {
842 zl = createList();
843 p = ziplistIndex(zl, 1);
844 while (ziplistGet(p, &entry, &elen, &value)) {
845 printf("Entry: ");
846 if (entry) {
847 fwrite(entry,elen,1,stdout);
848 } else {
849 printf("%lld", value);
850 }
851 p = ziplistNext(zl,p);
852 printf("\n");
853 }
854 printf("\n");
855 }
856
857 printf("Iterate list from 2 to end:\n");
858 {
859 zl = createList();
860 p = ziplistIndex(zl, 2);
861 while (ziplistGet(p, &entry, &elen, &value)) {
862 printf("Entry: ");
863 if (entry) {
864 fwrite(entry,elen,1,stdout);
865 } else {
866 printf("%lld", value);
867 }
868 p = ziplistNext(zl,p);
869 printf("\n");
870 }
871 printf("\n");
872 }
873
874 printf("Iterate starting out of range:\n");
875 {
876 zl = createList();
877 p = ziplistIndex(zl, 4);
878 if (!ziplistGet(p, &entry, &elen, &value)) {
879 printf("No entry\n");
880 } else {
881 printf("ERROR\n");
882 }
883 printf("\n");
884 }
885
886 printf("Iterate from back to front:\n");
887 {
888 zl = createList();
889 p = ziplistIndex(zl, -1);
890 while (ziplistGet(p, &entry, &elen, &value)) {
891 printf("Entry: ");
892 if (entry) {
893 fwrite(entry,elen,1,stdout);
894 } else {
895 printf("%lld", value);
896 }
897 p = ziplistPrev(zl,p);
898 printf("\n");
899 }
900 printf("\n");
901 }
902
903 printf("Iterate from back to front, deleting all items:\n");
904 {
905 zl = createList();
906 p = ziplistIndex(zl, -1);
907 while (ziplistGet(p, &entry, &elen, &value)) {
908 printf("Entry: ");
909 if (entry) {
910 fwrite(entry,elen,1,stdout);
911 } else {
912 printf("%lld", value);
913 }
914 zl = ziplistDelete(zl,&p);
915 p = ziplistPrev(zl,p);
916 printf("\n");
917 }
918 printf("\n");
919 }
920
921 printf("Delete inclusive range 0,0:\n");
922 {
923 zl = createList();
924 zl = ziplistDeleteRange(zl, 0, 1);
925 ziplistRepr(zl);
926 }
927
928 printf("Delete inclusive range 0,1:\n");
929 {
930 zl = createList();
931 zl = ziplistDeleteRange(zl, 0, 2);
932 ziplistRepr(zl);
933 }
934
935 printf("Delete inclusive range 1,2:\n");
936 {
937 zl = createList();
938 zl = ziplistDeleteRange(zl, 1, 2);
939 ziplistRepr(zl);
940 }
941
942 printf("Delete with start index out of range:\n");
943 {
944 zl = createList();
945 zl = ziplistDeleteRange(zl, 5, 1);
946 ziplistRepr(zl);
947 }
948
949 printf("Delete with num overflow:\n");
950 {
951 zl = createList();
952 zl = ziplistDeleteRange(zl, 1, 5);
953 ziplistRepr(zl);
954 }
955
956 printf("Delete foo while iterating:\n");
957 {
958 zl = createList();
959 p = ziplistIndex(zl,0);
960 while (ziplistGet(p,&entry,&elen,&value)) {
961 if (entry && strncmp("foo",(char*)entry,elen) == 0) {
962 printf("Delete foo\n");
963 zl = ziplistDelete(zl,&p);
964 } else {
965 printf("Entry: ");
966 if (entry) {
967 fwrite(entry,elen,1,stdout);
968 } else {
969 printf("%lld",value);
970 }
971 p = ziplistNext(zl,p);
972 printf("\n");
973 }
974 }
975 printf("\n");
976 ziplistRepr(zl);
977 }
978
979 printf("Create long list and check indices:\n");
980 {
981 zl = ziplistNew();
982 char buf[32];
983 int i,len;
984 for (i = 0; i < 1000; i++) {
985 len = sprintf(buf,"%d",i);
986 zl = ziplistPush(zl,(unsigned char*)buf,len,ZIPLIST_TAIL);
987 }
988 for (i = 0; i < 1000; i++) {
989 p = ziplistIndex(zl,i);
990 assert(ziplistGet(p,NULL,NULL,&value));
991 assert(i == value);
992
993 p = ziplistIndex(zl,-i-1);
994 assert(ziplistGet(p,NULL,NULL,&value));
995 assert(999-i == value);
996 }
997 printf("SUCCESS\n\n");
998 }
999
1000 printf("Compare strings with ziplist entries:\n");
1001 {
1002 zl = createList();
1003 p = ziplistIndex(zl,0);
1004 if (!ziplistCompare(p,(unsigned char*)"hello",5)) {
1005 printf("ERROR: not \"hello\"\n");
1006 return 1;
1007 }
1008 if (ziplistCompare(p,(unsigned char*)"hella",5)) {
1009 printf("ERROR: \"hella\"\n");
1010 return 1;
1011 }
1012
1013 p = ziplistIndex(zl,3);
1014 if (!ziplistCompare(p,(unsigned char*)"1024",4)) {
1015 printf("ERROR: not \"1024\"\n");
1016 return 1;
1017 }
1018 if (ziplistCompare(p,(unsigned char*)"1025",4)) {
1019 printf("ERROR: \"1025\"\n");
1020 return 1;
1021 }
1022 printf("SUCCESS\n");
1023 }
1024
1025 printf("Stress with variable ziplist size:\n");
1026 {
1027 stress(ZIPLIST_HEAD,100000,16384,256);
1028 stress(ZIPLIST_TAIL,100000,16384,256);
1029 }
1030
1031 return 0;
1032 }
1033
1034 #endif