]>
git.saurik.com Git - redis.git/blob - src/ziplist.c
1 /* Memory layout of a ziplist, containing "foo", "bar", "quux":
2 * <zlbytes><zllen><len>"foo"<len>"bar"<len>"quux"
4 * <zlbytes> is an unsigned integer to hold the number of bytes that
5 * the ziplist occupies. This is stored to not have to traverse the ziplist
6 * to know the new length when pushing.
8 * <zllen> is the number of items in the ziplist. When this value is
9 * greater than 254, we need to traverse the entire list to know
10 * how many items it holds.
12 * <len> is the number of bytes occupied by a single entry. When this
13 * number is greater than 253, the length will occupy 5 bytes, where
14 * the extra bytes contain an unsigned integer to hold the length.
26 int ll2string(char *s
, size_t len
, long long value
);
28 /* Important note: the ZIP_END value is used to depict the end of the
29 * ziplist structure. When a pointer contains an entry, the first couple
30 * of bytes contain the encoded length of the previous entry. This length
31 * is encoded as ZIP_ENC_RAW length, so the first two bits will contain 00
32 * and the byte will therefore never have a value of 255. */
34 #define ZIP_BIGLEN 254
38 #define ZIP_ENC_INT16 1
39 #define ZIP_ENC_INT32 2
40 #define ZIP_ENC_INT64 3
41 #define ZIP_ENCODING(p) ((p)[0] >> 6)
43 /* Length encoding for raw entries */
44 #define ZIP_LEN_INLINE 0
45 #define ZIP_LEN_UINT16 1
46 #define ZIP_LEN_UINT32 2
49 #define ZIPLIST_BYTES(zl) (*((uint32_t*)(zl)))
50 #define ZIPLIST_TAIL_OFFSET(zl) (*((uint32_t*)((zl)+sizeof(uint32_t))))
51 #define ZIPLIST_LENGTH(zl) (*((uint16_t*)((zl)+sizeof(uint32_t)*2)))
52 #define ZIPLIST_HEADER_SIZE (sizeof(uint32_t)*2+sizeof(uint16_t))
53 #define ZIPLIST_ENTRY_HEAD(zl) ((zl)+ZIPLIST_HEADER_SIZE)
54 #define ZIPLIST_ENTRY_TAIL(zl) ((zl)+ZIPLIST_TAIL_OFFSET(zl))
55 #define ZIPLIST_ENTRY_END(zl) ((zl)+ZIPLIST_BYTES(zl)-1)
57 /* We know a positive increment can only be 1 because entries can only be
58 * pushed one at a time. */
59 #define ZIPLIST_INCR_LENGTH(zl,incr) { \
60 if (ZIPLIST_LENGTH(zl) < UINT16_MAX) ZIPLIST_LENGTH(zl)+=incr; }
62 typedef struct zlentry
{
63 unsigned int prevrawlensize
, prevrawlen
;
64 unsigned int lensize
, len
;
65 unsigned int headersize
;
66 unsigned char encoding
;
70 /* Return bytes needed to store integer encoded by 'encoding' */
71 static unsigned int zipEncodingSize(unsigned char encoding
) {
72 if (encoding
== ZIP_ENC_INT16
) {
73 return sizeof(int16_t);
74 } else if (encoding
== ZIP_ENC_INT32
) {
75 return sizeof(int32_t);
76 } else if (encoding
== ZIP_ENC_INT64
) {
77 return sizeof(int64_t);
82 /* Decode the encoded length pointed by 'p'. If a pointer to 'lensize' is
83 * provided, it is set to the number of bytes required to encode the length. */
84 static unsigned int zipDecodeLength(unsigned char *p
, unsigned int *lensize
) {
85 unsigned char encoding
= ZIP_ENCODING(p
), lenenc
;
88 if (encoding
== ZIP_ENC_RAW
) {
89 lenenc
= (p
[0] >> 4) & 0x3;
90 if (lenenc
== ZIP_LEN_INLINE
) {
92 if (lensize
) *lensize
= 1;
93 } else if (lenenc
== ZIP_LEN_UINT16
) {
94 len
= p
[1] | (p
[2] << 8);
95 if (lensize
) *lensize
= 3;
97 len
= p
[1] | (p
[2] << 8) | (p
[3] << 16) | (p
[4] << 24);
98 if (lensize
) *lensize
= 5;
101 len
= zipEncodingSize(encoding
);
102 if (lensize
) *lensize
= 1;
107 /* Encode the length 'l' writing it in 'p'. If p is NULL it just returns
108 * the amount of bytes required to encode such a length. */
109 static unsigned int zipEncodeLength(unsigned char *p
, char encoding
, unsigned int rawlen
) {
110 unsigned char len
= 1, lenenc
, buf
[5];
111 if (encoding
== ZIP_ENC_RAW
) {
114 lenenc
= ZIP_LEN_INLINE
;
116 } else if (rawlen
<= 0xffff) {
119 lenenc
= ZIP_LEN_UINT16
;
120 buf
[1] = (rawlen
) & 0xff;
121 buf
[2] = (rawlen
>> 8) & 0xff;
125 lenenc
= ZIP_LEN_UINT32
;
126 buf
[1] = (rawlen
) & 0xff;
127 buf
[2] = (rawlen
>> 8) & 0xff;
128 buf
[3] = (rawlen
>> 16) & 0xff;
129 buf
[4] = (rawlen
>> 24) & 0xff;
131 buf
[0] = (lenenc
<< 4) | (buf
[0] & 0xf);
135 /* Apparently we need to store the length in 'p' */
136 buf
[0] = (encoding
<< 6) | (buf
[0] & 0x3f);
141 /* Decode the length of the previous element stored at "p". */
142 static unsigned int zipPrevDecodeLength(unsigned char *p
, unsigned int *lensize
) {
143 unsigned int len
= *p
;
144 if (len
< ZIP_BIGLEN
) {
145 if (lensize
) *lensize
= 1;
147 if (lensize
) *lensize
= 1+sizeof(len
);
148 memcpy(&len
,p
+1,sizeof(len
));
153 /* Encode the length of the previous entry and write it to "p". Return the
154 * number of bytes needed to encode this length if "p" is NULL. */
155 static unsigned int zipPrevEncodeLength(unsigned char *p
, unsigned int len
) {
157 return (len
< ZIP_BIGLEN
) ? 1 : sizeof(len
)+1;
159 if (len
< ZIP_BIGLEN
) {
164 memcpy(p
+1,&len
,sizeof(len
));
165 return 1+sizeof(len
);
170 /* Return the difference in number of bytes needed to store the new length
171 * "len" on the entry pointed to by "p". */
172 static int zipPrevLenByteDiff(unsigned char *p
, unsigned int len
) {
173 unsigned int prevlensize
;
174 zipPrevDecodeLength(p
,&prevlensize
);
175 return zipPrevEncodeLength(NULL
,len
)-prevlensize
;
178 /* Check if string pointed to by 'entry' can be encoded as an integer.
179 * Stores the integer value in 'v' and its encoding in 'encoding'. */
180 static int zipTryEncoding(unsigned char *entry
, unsigned int entrylen
, long long *v
, unsigned char *encoding
) {
185 if (entrylen
>= 32 || entrylen
== 0) return 0;
186 if (entry
[0] == '-' || (entry
[0] >= '0' && entry
[0] <= '9')) {
189 /* Perform a back-and-forth conversion to make sure that
190 * the string turned into an integer is not losing any info. */
191 memcpy(buf
,entry
,entrylen
);
192 buf
[entrylen
] = '\0';
193 value
= strtoll(buf
,&eptr
,10);
194 if (eptr
[0] != '\0') return 0;
195 slen
= ll2string(buf
,32,value
);
196 if (entrylen
!= (unsigned)slen
|| memcmp(buf
,entry
,slen
)) return 0;
198 /* Great, the string can be encoded. Check what's the smallest
199 * of our encoding types that can hold this value. */
200 if (value
>= INT16_MIN
&& value
<= INT16_MAX
) {
201 *encoding
= ZIP_ENC_INT16
;
202 } else if (value
>= INT32_MIN
&& value
<= INT32_MAX
) {
203 *encoding
= ZIP_ENC_INT32
;
205 *encoding
= ZIP_ENC_INT64
;
213 /* Store integer 'value' at 'p', encoded as 'encoding' */
214 static void zipSaveInteger(unsigned char *p
, int64_t value
, unsigned char encoding
) {
218 if (encoding
== ZIP_ENC_INT16
) {
220 memcpy(p
,&i16
,sizeof(i16
));
221 } else if (encoding
== ZIP_ENC_INT32
) {
223 memcpy(p
,&i32
,sizeof(i32
));
224 } else if (encoding
== ZIP_ENC_INT64
) {
226 memcpy(p
,&i64
,sizeof(i64
));
232 /* Read integer encoded as 'encoding' from 'p' */
233 static int64_t zipLoadInteger(unsigned char *p
, unsigned char encoding
) {
237 if (encoding
== ZIP_ENC_INT16
) {
238 memcpy(&i16
,p
,sizeof(i16
));
240 } else if (encoding
== ZIP_ENC_INT32
) {
241 memcpy(&i32
,p
,sizeof(i32
));
243 } else if (encoding
== ZIP_ENC_INT64
) {
244 memcpy(&i64
,p
,sizeof(i64
));
252 /* Return a struct with all information about an entry. */
253 static zlentry
zipEntry(unsigned char *p
) {
255 e
.prevrawlen
= zipPrevDecodeLength(p
,&e
.prevrawlensize
);
256 e
.len
= zipDecodeLength(p
+e
.prevrawlensize
,&e
.lensize
);
257 e
.headersize
= e
.prevrawlensize
+e
.lensize
;
258 e
.encoding
= ZIP_ENCODING(p
+e
.prevrawlensize
);
263 /* Return the total number of bytes used by the entry at "p". */
264 static unsigned int zipRawEntryLength(unsigned char *p
) {
265 zlentry e
= zipEntry(p
);
266 return e
.headersize
+ e
.len
;
269 /* Create a new empty ziplist. */
270 unsigned char *ziplistNew(void) {
271 unsigned int bytes
= ZIPLIST_HEADER_SIZE
+1;
272 unsigned char *zl
= zmalloc(bytes
);
273 ZIPLIST_BYTES(zl
) = bytes
;
274 ZIPLIST_TAIL_OFFSET(zl
) = ZIPLIST_HEADER_SIZE
;
275 ZIPLIST_LENGTH(zl
) = 0;
276 zl
[bytes
-1] = ZIP_END
;
280 /* Resize the ziplist. */
281 static unsigned char *ziplistResize(unsigned char *zl
, unsigned int len
) {
282 zl
= zrealloc(zl
,len
);
283 ZIPLIST_BYTES(zl
) = len
;
288 /* Delete "num" entries, starting at "p". Returns pointer to the ziplist. */
289 static unsigned char *__ziplistDelete(unsigned char *zl
, unsigned char *p
, unsigned int num
) {
290 unsigned int i
, totlen
, deleted
= 0;
292 zlentry first
= zipEntry(p
);
293 for (i
= 0; p
[0] != ZIP_END
&& i
< num
; i
++) {
294 p
+= zipRawEntryLength(p
);
300 if (p
[0] != ZIP_END
) {
301 /* Tricky: storing the prevlen in this entry might reduce or
302 * increase the number of bytes needed, compared to the current
303 * prevlen. Note that we can always store this length because
304 * it was previously stored by an entry that is being deleted. */
305 nextdiff
= zipPrevLenByteDiff(p
,first
.prevrawlen
);
306 zipPrevEncodeLength(p
-nextdiff
,first
.prevrawlen
);
308 /* Update offset for tail */
309 ZIPLIST_TAIL_OFFSET(zl
) -= totlen
+nextdiff
;
311 /* Move tail to the front of the ziplist */
312 memmove(first
.p
,p
-nextdiff
,ZIPLIST_BYTES(zl
)-(p
-zl
)-1+nextdiff
);
314 /* The entire tail was deleted. No need to move memory. */
315 ZIPLIST_TAIL_OFFSET(zl
) = (first
.p
-zl
)-first
.prevrawlen
;
318 /* Resize and update length */
319 zl
= ziplistResize(zl
, ZIPLIST_BYTES(zl
)-totlen
+nextdiff
);
320 ZIPLIST_INCR_LENGTH(zl
,-deleted
);
325 /* Insert item at "p". */
326 static unsigned char *__ziplistInsert(unsigned char *zl
, unsigned char *p
, unsigned char *s
, unsigned int slen
) {
327 unsigned int curlen
= ZIPLIST_BYTES(zl
), reqlen
, prevlen
= 0;
328 unsigned int offset
, nextdiff
= 0;
330 unsigned char encoding
= ZIP_ENC_RAW
;
334 /* Find out prevlen for the entry that is inserted. */
335 if (p
[0] != ZIP_END
) {
337 prevlen
= entry
.prevrawlen
;
339 tail
= ZIPLIST_ENTRY_TAIL(zl
);
340 if (tail
[0] != ZIP_END
) {
341 prevlen
= zipRawEntryLength(tail
);
345 /* See if the entry can be encoded */
346 if (zipTryEncoding(s
,slen
,&value
,&encoding
)) {
347 reqlen
= zipEncodingSize(encoding
);
352 /* We need space for both the length of the previous entry and
353 * the length of the payload. */
354 reqlen
+= zipPrevEncodeLength(NULL
,prevlen
);
355 reqlen
+= zipEncodeLength(NULL
,encoding
,slen
);
357 /* When the insert position is not equal to the tail, we need to
358 * make sure that the next entry can hold this entry's length in
359 * its prevlen field. */
360 nextdiff
= (p
[0] != ZIP_END
) ? zipPrevLenByteDiff(p
,reqlen
) : 0;
362 /* Store offset because a realloc may change the address of zl. */
364 zl
= ziplistResize(zl
,curlen
+reqlen
+nextdiff
);
367 /* Apply memory move when necessary and update tail offset. */
368 if (p
[0] != ZIP_END
) {
369 /* Subtract one because of the ZIP_END bytes */
370 memmove(p
+reqlen
,p
-nextdiff
,curlen
-offset
-1+nextdiff
);
371 /* Encode this entry's raw length in the next entry. */
372 zipPrevEncodeLength(p
+reqlen
,reqlen
);
373 /* Update offset for tail */
374 ZIPLIST_TAIL_OFFSET(zl
) += reqlen
+nextdiff
;
376 /* This element will be the new tail. */
377 ZIPLIST_TAIL_OFFSET(zl
) = p
-zl
;
380 /* Write the entry */
381 p
+= zipPrevEncodeLength(p
,prevlen
);
382 p
+= zipEncodeLength(p
,encoding
,slen
);
383 if (encoding
!= ZIP_ENC_RAW
) {
384 zipSaveInteger(p
,value
,encoding
);
388 ZIPLIST_INCR_LENGTH(zl
,1);
392 unsigned char *ziplistPush(unsigned char *zl
, unsigned char *s
, unsigned int slen
, int where
) {
394 p
= (where
== ZIPLIST_HEAD
) ? ZIPLIST_ENTRY_HEAD(zl
) : ZIPLIST_ENTRY_END(zl
);
395 return __ziplistInsert(zl
,p
,s
,slen
);
398 /* Returns an offset to use for iterating with ziplistNext. When the given
399 * index is negative, the list is traversed back to front. When the list
400 * doesn't contain an element at the provided index, NULL is returned. */
401 unsigned char *ziplistIndex(unsigned char *zl
, int index
) {
406 p
= ZIPLIST_ENTRY_TAIL(zl
);
407 if (p
[0] != ZIP_END
) {
409 while (entry
.prevrawlen
> 0 && index
--) {
410 p
-= entry
.prevrawlen
;
415 p
= ZIPLIST_ENTRY_HEAD(zl
);
416 while (p
[0] != ZIP_END
&& index
--) {
417 p
+= zipRawEntryLength(p
);
420 return (p
[0] == ZIP_END
|| index
> 0) ? NULL
: p
;
423 /* Return pointer to next entry in ziplist. */
424 unsigned char *ziplistNext(unsigned char *zl
, unsigned char *p
) {
427 /* "p" could be equal to ZIP_END, caused by ziplistDelete,
428 * and we should return NULL. Otherwise, we should return NULL
429 * when the *next* element is ZIP_END (there is no next entry). */
430 if (p
[0] == ZIP_END
) {
433 p
= p
+zipRawEntryLength(p
);
434 return (p
[0] == ZIP_END
) ? NULL
: p
;
438 /* Return pointer to previous entry in ziplist. */
439 unsigned char *ziplistPrev(unsigned char *zl
, unsigned char *p
) {
442 /* Iterating backwards from ZIP_END should return the tail. When "p" is
443 * equal to the first element of the list, we're already at the head,
444 * and should return NULL. */
445 if (p
[0] == ZIP_END
) {
446 p
= ZIPLIST_ENTRY_TAIL(zl
);
447 return (p
[0] == ZIP_END
) ? NULL
: p
;
448 } else if (p
== ZIPLIST_ENTRY_HEAD(zl
)) {
452 return p
-entry
.prevrawlen
;
456 /* Get entry pointer to by 'p' and store in either 'e' or 'v' depending
457 * on the encoding of the entry. 'e' is always set to NULL to be able
458 * to find out whether the string pointer or the integer value was set.
459 * Return 0 if 'p' points to the end of the zipmap, 1 otherwise. */
460 unsigned int ziplistGet(unsigned char *p
, unsigned char **sstr
, unsigned int *slen
, long long *sval
) {
462 if (p
== NULL
|| p
[0] == ZIP_END
) return 0;
463 if (sstr
) *sstr
= NULL
;
466 if (entry
.encoding
== ZIP_ENC_RAW
) {
469 *sstr
= p
+entry
.headersize
;
473 *sval
= zipLoadInteger(p
+entry
.headersize
,entry
.encoding
);
479 /* Insert an entry at "p". */
480 unsigned char *ziplistInsert(unsigned char *zl
, unsigned char *p
, unsigned char *s
, unsigned int slen
) {
481 return __ziplistInsert(zl
,p
,s
,slen
);
484 /* Delete a single entry from the ziplist, pointed to by *p.
485 * Also update *p in place, to be able to iterate over the
486 * ziplist, while deleting entries. */
487 unsigned char *ziplistDelete(unsigned char *zl
, unsigned char **p
) {
488 unsigned int offset
= *p
-zl
;
489 zl
= __ziplistDelete(zl
,*p
,1);
491 /* Store pointer to current element in p, because ziplistDelete will
492 * do a realloc which might result in a different "zl"-pointer.
493 * When the delete direction is back to front, we might delete the last
494 * entry and end up with "p" pointing to ZIP_END, so check this. */
499 /* Delete a range of entries from the ziplist. */
500 unsigned char *ziplistDeleteRange(unsigned char *zl
, unsigned int index
, unsigned int num
) {
501 unsigned char *p
= ziplistIndex(zl
,index
);
502 return (p
== NULL
) ? zl
: __ziplistDelete(zl
,p
,num
);
505 /* Compare entry pointer to by 'p' with 'entry'. Return 1 if equal. */
506 unsigned int ziplistCompare(unsigned char *p
, unsigned char *sstr
, unsigned int slen
) {
508 unsigned char sencoding
;
509 long long zval
, sval
;
510 if (p
[0] == ZIP_END
) return 0;
513 if (entry
.encoding
== ZIP_ENC_RAW
) {
515 if (entry
.len
== slen
) {
516 return memcmp(p
+entry
.headersize
,sstr
,slen
) == 0;
521 /* Try to compare encoded values */
522 if (zipTryEncoding(sstr
,slen
,&sval
,&sencoding
)) {
523 if (entry
.encoding
== sencoding
) {
524 zval
= zipLoadInteger(p
+entry
.headersize
,entry
.encoding
);
532 /* Return length of ziplist. */
533 unsigned int ziplistLen(unsigned char *zl
) {
534 unsigned int len
= 0;
535 if (ZIPLIST_LENGTH(zl
) < UINT16_MAX
) {
536 len
= ZIPLIST_LENGTH(zl
);
538 unsigned char *p
= zl
+ZIPLIST_HEADER_SIZE
;
539 while (*p
!= ZIP_END
) {
540 p
+= zipRawEntryLength(p
);
544 /* Re-store length if small enough */
545 if (len
< UINT16_MAX
) ZIPLIST_LENGTH(zl
) = len
;
550 /* Return size in bytes of ziplist. */
551 unsigned int ziplistSize(unsigned char *zl
) {
552 return ZIPLIST_BYTES(zl
);
555 void ziplistRepr(unsigned char *zl
) {
559 printf("{total bytes %d} {length %u}\n",ZIPLIST_BYTES(zl
), ZIPLIST_LENGTH(zl
));
560 p
= ZIPLIST_ENTRY_HEAD(zl
);
561 while(*p
!= ZIP_END
) {
563 printf("{offset %ld, header %u, payload %u} ",p
-zl
,entry
.headersize
,entry
.len
);
564 p
+= entry
.headersize
;
565 if (entry
.encoding
== ZIP_ENC_RAW
) {
566 fwrite(p
,entry
.len
,1,stdout
);
568 printf("%lld", (long long) zipLoadInteger(p
,entry
.encoding
));
576 #ifdef ZIPLIST_TEST_MAIN
577 #include <sys/time.h>
579 unsigned char *createList() {
580 unsigned char *zl
= ziplistNew();
581 zl
= ziplistPush(zl
, (unsigned char*)"foo", 3, ZIPLIST_TAIL
);
582 zl
= ziplistPush(zl
, (unsigned char*)"quux", 4, ZIPLIST_TAIL
);
583 zl
= ziplistPush(zl
, (unsigned char*)"hello", 5, ZIPLIST_HEAD
);
584 zl
= ziplistPush(zl
, (unsigned char*)"1024", 4, ZIPLIST_TAIL
);
588 unsigned char *createIntList() {
589 unsigned char *zl
= ziplistNew();
593 zl
= ziplistPush(zl
, (unsigned char*)buf
, strlen(buf
), ZIPLIST_TAIL
);
594 sprintf(buf
, "128000");
595 zl
= ziplistPush(zl
, (unsigned char*)buf
, strlen(buf
), ZIPLIST_TAIL
);
596 sprintf(buf
, "-100");
597 zl
= ziplistPush(zl
, (unsigned char*)buf
, strlen(buf
), ZIPLIST_HEAD
);
598 sprintf(buf
, "4294967296");
599 zl
= ziplistPush(zl
, (unsigned char*)buf
, strlen(buf
), ZIPLIST_HEAD
);
600 sprintf(buf
, "non integer");
601 zl
= ziplistPush(zl
, (unsigned char*)buf
, strlen(buf
), ZIPLIST_TAIL
);
602 sprintf(buf
, "much much longer non integer");
603 zl
= ziplistPush(zl
, (unsigned char*)buf
, strlen(buf
), ZIPLIST_TAIL
);
607 long long usec(void) {
609 gettimeofday(&tv
,NULL
);
610 return (((long long)tv
.tv_sec
)*1000000)+tv
.tv_usec
;
613 void stress(int pos
, int num
, int maxsize
, int dnum
) {
616 char posstr
[2][5] = { "HEAD", "TAIL" };
618 for (i
= 0; i
< maxsize
; i
+=dnum
) {
620 for (j
= 0; j
< i
; j
++) {
621 zl
= ziplistPush(zl
,(unsigned char*)"quux",4,ZIPLIST_TAIL
);
624 /* Do num times a push+pop from pos */
626 for (k
= 0; k
< num
; k
++) {
627 zl
= ziplistPush(zl
,(unsigned char*)"quux",4,pos
);
628 zl
= ziplistDeleteRange(zl
,0,1);
630 printf("List size: %8d, bytes: %8d, %dx push+pop (%s): %6lld usec\n",
631 i
,ZIPLIST_BYTES(zl
),num
,posstr
[pos
],usec()-start
);
636 void pop(unsigned char *zl
, int where
) {
637 unsigned char *p
, *vstr
;
641 p
= ziplistIndex(zl
,where
== ZIPLIST_HEAD
? 0 : -1);
642 if (ziplistGet(p
,&vstr
,&vlen
,&vlong
)) {
643 if (where
== ZIPLIST_HEAD
)
644 printf("Pop head: ");
646 printf("Pop tail: ");
649 fwrite(vstr
,vlen
,1,stdout
);
651 printf("%lld", vlong
);
654 ziplistDeleteRange(zl
,-1,1);
656 printf("ERROR: Could not pop\n");
661 int main(int argc
, char **argv
) {
662 unsigned char *zl
, *p
;
663 unsigned char *entry
;
667 zl
= createIntList();
673 pop(zl
,ZIPLIST_TAIL
);
676 pop(zl
,ZIPLIST_HEAD
);
679 pop(zl
,ZIPLIST_TAIL
);
682 pop(zl
,ZIPLIST_TAIL
);
685 printf("Get element at index 3:\n");
688 p
= ziplistIndex(zl
, 3);
689 if (!ziplistGet(p
, &entry
, &elen
, &value
)) {
690 printf("ERROR: Could not access index 3\n");
694 fwrite(entry
,elen
,1,stdout
);
697 printf("%lld\n", value
);
702 printf("Get element at index 4 (out of range):\n");
705 p
= ziplistIndex(zl
, 4);
707 printf("No entry\n");
709 printf("ERROR: Out of range index should return NULL, returned offset: %ld\n", p
-zl
);
715 printf("Get element at index -1 (last element):\n");
718 p
= ziplistIndex(zl
, -1);
719 if (!ziplistGet(p
, &entry
, &elen
, &value
)) {
720 printf("ERROR: Could not access index -1\n");
724 fwrite(entry
,elen
,1,stdout
);
727 printf("%lld\n", value
);
732 printf("Get element at index -4 (first element):\n");
735 p
= ziplistIndex(zl
, -4);
736 if (!ziplistGet(p
, &entry
, &elen
, &value
)) {
737 printf("ERROR: Could not access index -4\n");
741 fwrite(entry
,elen
,1,stdout
);
744 printf("%lld\n", value
);
749 printf("Get element at index -5 (reverse out of range):\n");
752 p
= ziplistIndex(zl
, -5);
754 printf("No entry\n");
756 printf("ERROR: Out of range index should return NULL, returned offset: %ld\n", p
-zl
);
762 printf("Iterate list from 0 to end:\n");
765 p
= ziplistIndex(zl
, 0);
766 while (ziplistGet(p
, &entry
, &elen
, &value
)) {
769 fwrite(entry
,elen
,1,stdout
);
771 printf("%lld", value
);
773 p
= ziplistNext(zl
,p
);
779 printf("Iterate list from 1 to end:\n");
782 p
= ziplistIndex(zl
, 1);
783 while (ziplistGet(p
, &entry
, &elen
, &value
)) {
786 fwrite(entry
,elen
,1,stdout
);
788 printf("%lld", value
);
790 p
= ziplistNext(zl
,p
);
796 printf("Iterate list from 2 to end:\n");
799 p
= ziplistIndex(zl
, 2);
800 while (ziplistGet(p
, &entry
, &elen
, &value
)) {
803 fwrite(entry
,elen
,1,stdout
);
805 printf("%lld", value
);
807 p
= ziplistNext(zl
,p
);
813 printf("Iterate starting out of range:\n");
816 p
= ziplistIndex(zl
, 4);
817 if (!ziplistGet(p
, &entry
, &elen
, &value
)) {
818 printf("No entry\n");
825 printf("Iterate from back to front:\n");
828 p
= ziplistIndex(zl
, -1);
829 while (ziplistGet(p
, &entry
, &elen
, &value
)) {
832 fwrite(entry
,elen
,1,stdout
);
834 printf("%lld", value
);
836 p
= ziplistPrev(zl
,p
);
842 printf("Iterate from back to front, deleting all items:\n");
845 p
= ziplistIndex(zl
, -1);
846 while (ziplistGet(p
, &entry
, &elen
, &value
)) {
849 fwrite(entry
,elen
,1,stdout
);
851 printf("%lld", value
);
853 zl
= ziplistDelete(zl
,&p
);
854 p
= ziplistPrev(zl
,p
);
860 printf("Delete inclusive range 0,0:\n");
863 zl
= ziplistDeleteRange(zl
, 0, 1);
867 printf("Delete inclusive range 0,1:\n");
870 zl
= ziplistDeleteRange(zl
, 0, 2);
874 printf("Delete inclusive range 1,2:\n");
877 zl
= ziplistDeleteRange(zl
, 1, 2);
881 printf("Delete with start index out of range:\n");
884 zl
= ziplistDeleteRange(zl
, 5, 1);
888 printf("Delete with num overflow:\n");
891 zl
= ziplistDeleteRange(zl
, 1, 5);
895 printf("Delete foo while iterating:\n");
898 p
= ziplistIndex(zl
,0);
899 while (ziplistGet(p
,&entry
,&elen
,&value
)) {
900 if (entry
&& strncmp("foo",(char*)entry
,elen
) == 0) {
901 printf("Delete foo\n");
902 zl
= ziplistDelete(zl
,&p
);
906 fwrite(entry
,elen
,1,stdout
);
908 printf("%lld",value
);
910 p
= ziplistNext(zl
,p
);
918 printf("Create long list and check indices:\n");
923 for (i
= 0; i
< 1000; i
++) {
924 len
= sprintf(buf
,"%d",i
);
925 zl
= ziplistPush(zl
,(unsigned char*)buf
,len
,ZIPLIST_TAIL
);
927 for (i
= 0; i
< 1000; i
++) {
928 p
= ziplistIndex(zl
,i
);
929 assert(ziplistGet(p
,NULL
,NULL
,&value
));
932 p
= ziplistIndex(zl
,-i
-1);
933 assert(ziplistGet(p
,NULL
,NULL
,&value
));
934 assert(999-i
== value
);
936 printf("SUCCESS\n\n");
939 printf("Compare strings with ziplist entries:\n");
942 p
= ziplistIndex(zl
,0);
943 if (!ziplistCompare(p
,(unsigned char*)"hello",5)) {
944 printf("ERROR: not \"hello\"\n");
947 if (ziplistCompare(p
,(unsigned char*)"hella",5)) {
948 printf("ERROR: \"hella\"\n");
952 p
= ziplistIndex(zl
,3);
953 if (!ziplistCompare(p
,(unsigned char*)"1024",4)) {
954 printf("ERROR: not \"1024\"\n");
957 if (ziplistCompare(p
,(unsigned char*)"1025",4)) {
958 printf("ERROR: \"1025\"\n");
964 printf("Stress with variable ziplist size:\n");
966 stress(ZIPLIST_HEAD
,100000,16384,256);
967 stress(ZIPLIST_TAIL
,100000,16384,256);