]> git.saurik.com Git - redis.git/blame - src/ziplist.c
Make ziplist schema more efficient for strings with length > 15
[redis.git] / src / ziplist.c
CommitLineData
c4705381
PN
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.
11ac6ff6 8 *
c4705381 9 * ----------------------------------------------------------------------------
11ac6ff6 10 *
c4705381
PN
11 * ZIPLIST OVERALL LAYOUT:
12 * The general layout of the ziplist is as follows:
13 * <zlbytes><zltail><zllen><entry><entry><zlend>
11ac6ff6 14 *
c4705381
PN
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).
11ac6ff6
PN
61 */
62
63#include <stdio.h>
29b14d5f 64#include <stdlib.h>
11ac6ff6 65#include <string.h>
e1f93d4b 66#include <stdint.h>
11ac6ff6 67#include <assert.h>
29b14d5f 68#include <limits.h>
11ac6ff6 69#include "zmalloc.h"
11ac6ff6 70#include "ziplist.h"
11ac6ff6 71
61712508 72int ll2string(char *s, size_t len, long long value);
73
37fff074 74#define ZIP_END 255
aa549962 75#define ZIP_BIGLEN 254
37fff074 76
c4705381
PN
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)
37fff074 84
c4705381
PN
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)
37fff074
PN
88
89/* Utility macros */
e1f93d4b
PN
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. */
f6eb1747 100#define ZIPLIST_INCR_LENGTH(zl,incr) { \
e1f93d4b 101 if (ZIPLIST_LENGTH(zl) < UINT16_MAX) ZIPLIST_LENGTH(zl)+=incr; }
11ac6ff6 102
a5456b2c
PN
103typedef struct zlentry {
104 unsigned int prevrawlensize, prevrawlen;
105 unsigned int lensize, len;
106 unsigned int headersize;
107 unsigned char encoding;
0c0d0564 108 unsigned char *p;
a5456b2c
PN
109} zlentry;
110
c4705381
PN
111/* Return the encoding pointer to by 'p'. */
112static 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
37fff074 124/* Return bytes needed to store integer encoded by 'encoding' */
c4705381
PN
125static 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);
37fff074
PN
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. */
136static unsigned int zipDecodeLength(unsigned char *p, unsigned int *lensize) {
c4705381 137 unsigned char encoding = zipEntryEncoding(p);
37fff074
PN
138 unsigned int len;
139
c4705381
PN
140 if (ZIP_IS_STR(encoding)) {
141 switch(encoding) {
142 case ZIP_STR_06B:
143 len = p[0] & 0x3f;
37fff074 144 if (lensize) *lensize = 1;
c4705381
PN
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];
37fff074 152 if (lensize) *lensize = 5;
c4705381
PN
153 break;
154 default:
155 assert(NULL);
37fff074
PN
156 }
157 } else {
c4705381 158 len = zipIntSize(encoding);
37fff074
PN
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. */
c4705381
PN
166static 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) {
37fff074 173 if (!p) return len;
c4705381
PN
174 buf[0] = ZIP_STR_06B | rawlen;
175 } else if (rawlen <= 0x3fff) {
176 len += 1;
37fff074 177 if (!p) return len;
c4705381
PN
178 buf[0] = ZIP_STR_14B | ((rawlen >> 8) & 0x3f);
179 buf[1] = rawlen & 0xff;
37fff074
PN
180 } else {
181 len += 4;
182 if (!p) return len;
c4705381
PN
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;
37fff074 188 }
c4705381
PN
189 } else {
190 /* Implies integer encoding, so length is always 1. */
191 if (!p) return len;
192 buf[0] = encoding;
37fff074 193 }
37fff074 194
c4705381 195 /* Store this length at p */
37fff074
PN
196 memcpy(p,buf,len);
197 return len;
198}
199
7b1f85c0
PN
200/* Decode the length of the previous element stored at "p". */
201static 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. */
214static 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
dcb9cf4e
PN
229/* Return the difference in number of bytes needed to store the new length
230 * "len" on the entry pointed to by "p". */
231static int zipPrevLenByteDiff(unsigned char *p, unsigned int len) {
232 unsigned int prevlensize;
7b1f85c0
PN
233 zipPrevDecodeLength(p,&prevlensize);
234 return zipPrevEncodeLength(NULL,len)-prevlensize;
dcb9cf4e
PN
235}
236
37fff074 237/* Check if string pointed to by 'entry' can be encoded as an integer.
61712508 238 * Stores the integer value in 'v' and its encoding in 'encoding'. */
239static int zipTryEncoding(unsigned char *entry, unsigned int entrylen, long long *v, unsigned char *encoding) {
37fff074
PN
240 long long value;
241 char *eptr;
61712508 242 char buf[32];
37fff074 243
61712508 244 if (entrylen >= 32 || entrylen == 0) return 0;
37fff074 245 if (entry[0] == '-' || (entry[0] >= '0' && entry[0] <= '9')) {
61712508 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);
37fff074 253 if (eptr[0] != '\0') return 0;
61712508 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. */
e1f93d4b 259 if (value >= INT16_MIN && value <= INT16_MAX) {
c4705381 260 *encoding = ZIP_INT_16B;
e1f93d4b 261 } else if (value >= INT32_MIN && value <= INT32_MAX) {
c4705381 262 *encoding = ZIP_INT_32B;
37fff074 263 } else {
c4705381 264 *encoding = ZIP_INT_64B;
37fff074
PN
265 }
266 *v = value;
267 return 1;
268 }
269 return 0;
270}
271
272/* Store integer 'value' at 'p', encoded as 'encoding' */
e1f93d4b
PN
273static void zipSaveInteger(unsigned char *p, int64_t value, unsigned char encoding) {
274 int16_t i16;
275 int32_t i32;
276 int64_t i64;
c4705381 277 if (encoding == ZIP_INT_16B) {
e1f93d4b
PN
278 i16 = value;
279 memcpy(p,&i16,sizeof(i16));
c4705381 280 } else if (encoding == ZIP_INT_32B) {
e1f93d4b
PN
281 i32 = value;
282 memcpy(p,&i32,sizeof(i32));
c4705381 283 } else if (encoding == ZIP_INT_64B) {
e1f93d4b
PN
284 i64 = value;
285 memcpy(p,&i64,sizeof(i64));
37fff074
PN
286 } else {
287 assert(NULL);
288 }
289}
290
291/* Read integer encoded as 'encoding' from 'p' */
e1f93d4b
PN
292static int64_t zipLoadInteger(unsigned char *p, unsigned char encoding) {
293 int16_t i16;
294 int32_t i32;
295 int64_t i64, ret;
c4705381 296 if (encoding == ZIP_INT_16B) {
e1f93d4b
PN
297 memcpy(&i16,p,sizeof(i16));
298 ret = i16;
c4705381 299 } else if (encoding == ZIP_INT_32B) {
e1f93d4b
PN
300 memcpy(&i32,p,sizeof(i32));
301 ret = i32;
c4705381 302 } else if (encoding == ZIP_INT_64B) {
e1f93d4b
PN
303 memcpy(&i64,p,sizeof(i64));
304 ret = i64;
37fff074
PN
305 } else {
306 assert(NULL);
307 }
308 return ret;
309}
310
a5456b2c
PN
311/* Return a struct with all information about an entry. */
312static zlentry zipEntry(unsigned char *p) {
313 zlentry e;
7b1f85c0 314 e.prevrawlen = zipPrevDecodeLength(p,&e.prevrawlensize);
a5456b2c
PN
315 e.len = zipDecodeLength(p+e.prevrawlensize,&e.lensize);
316 e.headersize = e.prevrawlensize+e.lensize;
c4705381 317 e.encoding = zipEntryEncoding(p+e.prevrawlensize);
0c0d0564 318 e.p = p;
a5456b2c
PN
319 return e;
320}
321
bb57b965 322/* Return the total number of bytes used by the entry at "p". */
37fff074 323static unsigned int zipRawEntryLength(unsigned char *p) {
bb57b965
PN
324 zlentry e = zipEntry(p);
325 return e.headersize + e.len;
37fff074
PN
326}
327
11ac6ff6
PN
328/* Create a new empty ziplist. */
329unsigned char *ziplistNew(void) {
330 unsigned int bytes = ZIPLIST_HEADER_SIZE+1;
331 unsigned char *zl = zmalloc(bytes);
332 ZIPLIST_BYTES(zl) = bytes;
dcb9cf4e 333 ZIPLIST_TAIL_OFFSET(zl) = ZIPLIST_HEADER_SIZE;
11ac6ff6
PN
334 ZIPLIST_LENGTH(zl) = 0;
335 zl[bytes-1] = ZIP_END;
336 return zl;
337}
338
37fff074 339/* Resize the ziplist. */
11ac6ff6 340static unsigned char *ziplistResize(unsigned char *zl, unsigned int len) {
37fff074 341 zl = zrealloc(zl,len);
11ac6ff6
PN
342 ZIPLIST_BYTES(zl) = len;
343 zl[len-1] = ZIP_END;
344 return zl;
345}
346
0c0d0564 347/* Delete "num" entries, starting at "p". Returns pointer to the ziplist. */
b6eb9703 348static unsigned char *__ziplistDelete(unsigned char *zl, unsigned char *p, unsigned int num) {
0c0d0564
PN
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);
7b1f85c0 365 zipPrevEncodeLength(p-nextdiff,first.prevrawlen);
0c0d0564
PN
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;
11ac6ff6
PN
382}
383
6435c767 384/* Insert item at "p". */
b6eb9703 385static unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {
6435c767
PN
386 unsigned int curlen = ZIPLIST_BYTES(zl), reqlen, prevlen = 0;
387 unsigned int offset, nextdiff = 0;
388 unsigned char *tail;
c4705381 389 unsigned char encoding = 0;
29b14d5f 390 long long value;
6435c767 391 zlentry entry;
11ac6ff6 392
6435c767
PN
393 /* Find out prevlen for the entry that is inserted. */
394 if (p[0] != ZIP_END) {
395 entry = zipEntry(p);
396 prevlen = entry.prevrawlen;
dcb9cf4e 397 } else {
1ce81fa5 398 tail = ZIPLIST_ENTRY_TAIL(zl);
6435c767
PN
399 if (tail[0] != ZIP_END) {
400 prevlen = zipRawEntryLength(tail);
401 }
dcb9cf4e
PN
402 }
403
29b14d5f 404 /* See if the entry can be encoded */
61712508 405 if (zipTryEncoding(s,slen,&value,&encoding)) {
c4705381
PN
406 /* 'encoding' is set to the appropriate integer encoding */
407 reqlen = zipIntSize(encoding);
29b14d5f 408 } else {
c4705381
PN
409 /* 'encoding' is untouched, however zipEncodeLength will use the
410 * string length to figure out how to encode it. */
6435c767 411 reqlen = slen;
29b14d5f 412 }
dcb9cf4e
PN
413 /* We need space for both the length of the previous entry and
414 * the length of the payload. */
7b1f85c0 415 reqlen += zipPrevEncodeLength(NULL,prevlen);
6435c767
PN
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. */
177a0a0b 421 nextdiff = (p[0] != ZIP_END) ? zipPrevLenByteDiff(p,reqlen) : 0;
6435c767
PN
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. */
7b1f85c0 433 zipPrevEncodeLength(p+reqlen,reqlen);
6435c767
PN
434 /* Update offset for tail */
435 ZIPLIST_TAIL_OFFSET(zl) += reqlen+nextdiff;
11ac6ff6 436 } else {
6435c767
PN
437 /* This element will be the new tail. */
438 ZIPLIST_TAIL_OFFSET(zl) = p-zl;
dcb9cf4e
PN
439 }
440
11ac6ff6 441 /* Write the entry */
7b1f85c0 442 p += zipPrevEncodeLength(p,prevlen);
6435c767 443 p += zipEncodeLength(p,encoding,slen);
c4705381 444 if (ZIP_IS_STR(encoding)) {
6435c767 445 memcpy(p,s,slen);
c4705381
PN
446 } else {
447 zipSaveInteger(p,value,encoding);
29b14d5f 448 }
f6eb1747 449 ZIPLIST_INCR_LENGTH(zl,1);
11ac6ff6
PN
450 return zl;
451}
452
b6eb9703 453unsigned char *ziplistPush(unsigned char *zl, unsigned char *s, unsigned int slen, int where) {
6435c767 454 unsigned char *p;
1ce81fa5 455 p = (where == ZIPLIST_HEAD) ? ZIPLIST_ENTRY_HEAD(zl) : ZIPLIST_ENTRY_END(zl);
6435c767
PN
456 return __ziplistInsert(zl,p,s,slen);
457}
458
c03206fd
PN
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. */
462unsigned 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 }
08253bf4 480 }
177a0a0b 481 return (p[0] == ZIP_END || index > 0) ? NULL : p;
08253bf4
PN
482}
483
75d8978e 484/* Return pointer to next entry in ziplist. */
8632fb30
PN
485unsigned char *ziplistNext(unsigned char *zl, unsigned char *p) {
486 ((void) zl);
d71b9865
PN
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 }
75d8978e
PN
497}
498
033fb554 499/* Return pointer to previous entry in ziplist. */
8632fb30
PN
500unsigned 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 }
033fb554
PN
515}
516
75d8978e
PN
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. */
b6eb9703 521unsigned int ziplistGet(unsigned char *p, unsigned char **sstr, unsigned int *slen, long long *sval) {
a5456b2c 522 zlentry entry;
c03206fd 523 if (p == NULL || p[0] == ZIP_END) return 0;
03e52931 524 if (sstr) *sstr = NULL;
dcb9cf4e 525
a5456b2c 526 entry = zipEntry(p);
c4705381 527 if (ZIP_IS_STR(entry.encoding)) {
03e52931
PN
528 if (sstr) {
529 *slen = entry.len;
b6eb9703 530 *sstr = p+entry.headersize;
75d8978e
PN
531 }
532 } else {
03e52931
PN
533 if (sval) {
534 *sval = zipLoadInteger(p+entry.headersize,entry.encoding);
75d8978e 535 }
08253bf4 536 }
75d8978e 537 return 1;
08253bf4
PN
538}
539
033fb554 540/* Insert an entry at "p". */
b6eb9703 541unsigned char *ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {
033fb554 542 return __ziplistInsert(zl,p,s,slen);
779deb60
PN
543}
544
0f10458c
PN
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. */
6a8e35ad 548unsigned char *ziplistDelete(unsigned char *zl, unsigned char **p) {
0c0d0564
PN
549 unsigned int offset = *p-zl;
550 zl = __ziplistDelete(zl,*p,1);
0f10458c 551
0c0d0564 552 /* Store pointer to current element in p, because ziplistDelete will
0f3dfa87
PN
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. */
6a8e35ad 556 *p = zl+offset;
0f10458c
PN
557 return zl;
558}
559
033fb554
PN
560/* Delete a range of entries from the ziplist. */
561unsigned 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
c09c2c3b 566/* Compare entry pointer to by 'p' with 'entry'. Return 1 if equal. */
b6eb9703 567unsigned int ziplistCompare(unsigned char *p, unsigned char *sstr, unsigned int slen) {
a5456b2c 568 zlentry entry;
b6eb9703
PN
569 unsigned char sencoding;
570 long long zval, sval;
177a0a0b 571 if (p[0] == ZIP_END) return 0;
c09c2c3b 572
a5456b2c 573 entry = zipEntry(p);
c4705381 574 if (ZIP_IS_STR(entry.encoding)) {
c09c2c3b 575 /* Raw compare */
a5456b2c 576 if (entry.len == slen) {
03e52931 577 return memcmp(p+entry.headersize,sstr,slen) == 0;
c09c2c3b
PN
578 } else {
579 return 0;
580 }
c4aace90 581 } else {
d593c488 582 /* Try to compare encoded values */
61712508 583 if (zipTryEncoding(sstr,slen,&sval,&sencoding)) {
d593c488 584 if (entry.encoding == sencoding) {
b6eb9703
PN
585 zval = zipLoadInteger(p+entry.headersize,entry.encoding);
586 return zval == sval;
d593c488 587 }
c4aace90 588 }
c09c2c3b 589 }
c4aace90 590 return 0;
c09c2c3b
PN
591}
592
6205b463
PN
593/* Return length of ziplist. */
594unsigned int ziplistLen(unsigned char *zl) {
595 unsigned int len = 0;
e1f93d4b 596 if (ZIPLIST_LENGTH(zl) < UINT16_MAX) {
6205b463
PN
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 */
e1f93d4b 606 if (len < UINT16_MAX) ZIPLIST_LENGTH(zl) = len;
6205b463
PN
607 }
608 return len;
609}
610
4812cf28
PN
611/* Return size in bytes of ziplist. */
612unsigned int ziplistSize(unsigned char *zl) {
613 return ZIPLIST_BYTES(zl);
614}
615
11ac6ff6 616void ziplistRepr(unsigned char *zl) {
c8d9e7f4
PN
617 unsigned char *p;
618 zlentry entry;
11ac6ff6 619
29b14d5f 620 printf("{total bytes %d} {length %u}\n",ZIPLIST_BYTES(zl), ZIPLIST_LENGTH(zl));
1ce81fa5 621 p = ZIPLIST_ENTRY_HEAD(zl);
11ac6ff6 622 while(*p != ZIP_END) {
c8d9e7f4
PN
623 entry = zipEntry(p);
624 printf("{offset %ld, header %u, payload %u} ",p-zl,entry.headersize,entry.len);
625 p += entry.headersize;
c4705381 626 if (ZIP_IS_STR(entry.encoding)) {
c8d9e7f4 627 fwrite(p,entry.len,1,stdout);
29b14d5f 628 } else {
3688d7f3 629 printf("%lld", (long long) zipLoadInteger(p,entry.encoding));
29b14d5f 630 }
11ac6ff6 631 printf("\n");
c8d9e7f4 632 p += entry.len;
11ac6ff6
PN
633 }
634 printf("{end}\n\n");
635}
636
637#ifdef ZIPLIST_TEST_MAIN
ffc15852 638#include <sys/time.h>
11ac6ff6 639
08253bf4
PN
640unsigned char *createList() {
641 unsigned char *zl = ziplistNew();
b84186ff
PN
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);
08253bf4
PN
646 return zl;
647}
648
29b14d5f
PN
649unsigned char *createIntList() {
650 unsigned char *zl = ziplistNew();
651 char buf[32];
652
653 sprintf(buf, "100");
b84186ff 654 zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);
29b14d5f 655 sprintf(buf, "128000");
b84186ff 656 zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);
29b14d5f 657 sprintf(buf, "-100");
b84186ff 658 zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_HEAD);
29b14d5f 659 sprintf(buf, "4294967296");
b84186ff 660 zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_HEAD);
29b14d5f 661 sprintf(buf, "non integer");
b84186ff 662 zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);
29b14d5f 663 sprintf(buf, "much much longer non integer");
b84186ff 664 zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);
29b14d5f
PN
665 return zl;
666}
667
ffc15852
PN
668long long usec(void) {
669 struct timeval tv;
670 gettimeofday(&tv,NULL);
671 return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;
672}
673
674void 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
306974f5
PN
697void 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
08253bf4 722int main(int argc, char **argv) {
a24ba809 723 unsigned char *zl, *p;
b84186ff 724 unsigned char *entry;
335d16bc 725 unsigned int elen;
75d8978e 726 long long value;
08253bf4 727
29b14d5f
PN
728 zl = createIntList();
729 ziplistRepr(zl);
730
08253bf4 731 zl = createList();
11ac6ff6
PN
732 ziplistRepr(zl);
733
306974f5 734 pop(zl,ZIPLIST_TAIL);
11ac6ff6
PN
735 ziplistRepr(zl);
736
306974f5 737 pop(zl,ZIPLIST_HEAD);
11ac6ff6
PN
738 ziplistRepr(zl);
739
306974f5 740 pop(zl,ZIPLIST_TAIL);
dcb9cf4e
PN
741 ziplistRepr(zl);
742
306974f5 743 pop(zl,ZIPLIST_TAIL);
dcb9cf4e
PN
744 ziplistRepr(zl);
745
c03206fd
PN
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
08253bf4
PN
823 printf("Iterate list from 0 to end:\n");
824 {
825 zl = createList();
826 p = ziplistIndex(zl, 0);
75d8978e 827 while (ziplistGet(p, &entry, &elen, &value)) {
335d16bc 828 printf("Entry: ");
75d8978e
PN
829 if (entry) {
830 fwrite(entry,elen,1,stdout);
831 } else {
832 printf("%lld", value);
833 }
8632fb30 834 p = ziplistNext(zl,p);
75d8978e 835 printf("\n");
08253bf4
PN
836 }
837 printf("\n");
838 }
839
840 printf("Iterate list from 1 to end:\n");
841 {
842 zl = createList();
843 p = ziplistIndex(zl, 1);
75d8978e 844 while (ziplistGet(p, &entry, &elen, &value)) {
335d16bc 845 printf("Entry: ");
75d8978e
PN
846 if (entry) {
847 fwrite(entry,elen,1,stdout);
848 } else {
849 printf("%lld", value);
850 }
8632fb30 851 p = ziplistNext(zl,p);
75d8978e 852 printf("\n");
08253bf4
PN
853 }
854 printf("\n");
855 }
856
857 printf("Iterate list from 2 to end:\n");
858 {
859 zl = createList();
860 p = ziplistIndex(zl, 2);
75d8978e 861 while (ziplistGet(p, &entry, &elen, &value)) {
335d16bc 862 printf("Entry: ");
75d8978e
PN
863 if (entry) {
864 fwrite(entry,elen,1,stdout);
865 } else {
866 printf("%lld", value);
867 }
8632fb30 868 p = ziplistNext(zl,p);
75d8978e 869 printf("\n");
08253bf4
PN
870 }
871 printf("\n");
872 }
873
874 printf("Iterate starting out of range:\n");
875 {
876 zl = createList();
75d8978e
PN
877 p = ziplistIndex(zl, 4);
878 if (!ziplistGet(p, &entry, &elen, &value)) {
08253bf4
PN
879 printf("No entry\n");
880 } else {
881 printf("ERROR\n");
882 }
779deb60
PN
883 printf("\n");
884 }
885
0f3dfa87
PN
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 }
8632fb30 897 p = ziplistPrev(zl,p);
0f3dfa87
PN
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 }
8632fb30
PN
914 zl = ziplistDelete(zl,&p);
915 p = ziplistPrev(zl,p);
0f3dfa87
PN
916 printf("\n");
917 }
918 printf("\n");
919 }
920
779deb60
PN
921 printf("Delete inclusive range 0,0:\n");
922 {
923 zl = createList();
ba5b4bde 924 zl = ziplistDeleteRange(zl, 0, 1);
779deb60
PN
925 ziplistRepr(zl);
926 }
927
928 printf("Delete inclusive range 0,1:\n");
929 {
930 zl = createList();
ba5b4bde 931 zl = ziplistDeleteRange(zl, 0, 2);
779deb60
PN
932 ziplistRepr(zl);
933 }
934
935 printf("Delete inclusive range 1,2:\n");
936 {
937 zl = createList();
ba5b4bde 938 zl = ziplistDeleteRange(zl, 1, 2);
779deb60
PN
939 ziplistRepr(zl);
940 }
941
942 printf("Delete with start index out of range:\n");
943 {
944 zl = createList();
ba5b4bde 945 zl = ziplistDeleteRange(zl, 5, 1);
779deb60
PN
946 ziplistRepr(zl);
947 }
948
949 printf("Delete with num overflow:\n");
950 {
951 zl = createList();
ba5b4bde 952 zl = ziplistDeleteRange(zl, 1, 5);
779deb60 953 ziplistRepr(zl);
08253bf4
PN
954 }
955
0f10458c
PN
956 printf("Delete foo while iterating:\n");
957 {
958 zl = createList();
b84186ff
PN
959 p = ziplistIndex(zl,0);
960 while (ziplistGet(p,&entry,&elen,&value)) {
961 if (entry && strncmp("foo",(char*)entry,elen) == 0) {
0f10458c 962 printf("Delete foo\n");
b84186ff 963 zl = ziplistDelete(zl,&p);
0f10458c
PN
964 } else {
965 printf("Entry: ");
75d8978e
PN
966 if (entry) {
967 fwrite(entry,elen,1,stdout);
968 } else {
b84186ff 969 printf("%lld",value);
75d8978e 970 }
b84186ff 971 p = ziplistNext(zl,p);
75d8978e 972 printf("\n");
0f10458c
PN
973 }
974 }
975 printf("\n");
976 ziplistRepr(zl);
c09c2c3b
PN
977 }
978
dbaa41c6
PN
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);
b84186ff 986 zl = ziplistPush(zl,(unsigned char*)buf,len,ZIPLIST_TAIL);
dbaa41c6
PN
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
c09c2c3b
PN
1000 printf("Compare strings with ziplist entries:\n");
1001 {
1002 zl = createList();
b84186ff
PN
1003 p = ziplistIndex(zl,0);
1004 if (!ziplistCompare(p,(unsigned char*)"hello",5)) {
dcb9cf4e 1005 printf("ERROR: not \"hello\"\n");
a24ba809 1006 return 1;
c09c2c3b 1007 }
b84186ff 1008 if (ziplistCompare(p,(unsigned char*)"hella",5)) {
dcb9cf4e 1009 printf("ERROR: \"hella\"\n");
a24ba809 1010 return 1;
c09c2c3b
PN
1011 }
1012
b84186ff
PN
1013 p = ziplistIndex(zl,3);
1014 if (!ziplistCompare(p,(unsigned char*)"1024",4)) {
dcb9cf4e 1015 printf("ERROR: not \"1024\"\n");
a24ba809 1016 return 1;
c09c2c3b 1017 }
b84186ff 1018 if (ziplistCompare(p,(unsigned char*)"1025",4)) {
dcb9cf4e 1019 printf("ERROR: \"1025\"\n");
a24ba809 1020 return 1;
c09c2c3b
PN
1021 }
1022 printf("SUCCESS\n");
0f10458c
PN
1023 }
1024
ffc15852
PN
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
11ac6ff6
PN
1031 return 0;
1032}
ffc15852 1033
11ac6ff6 1034#endif