]> git.saurik.com Git - redis.git/blame - src/ziplist.c
Fix integer overflow in zunionInterGenericCommand().
[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.
dcd4efe9 55 * |11000000| - 1 byte
c4705381 56 * Integer encoded as int16_t (2 bytes).
dcd4efe9 57 * |11010000| - 1 byte
c4705381 58 * Integer encoded as int32_t (4 bytes).
dcd4efe9 59 * |11100000| - 1 byte
c4705381 60 * Integer encoded as int64_t (8 bytes).
dcd4efe9 61 * |11110000| - 1 byte
dd51571c 62 * Integer encoded as 24 bit signed (3 bytes).
dcd4efe9 63 * |11111110| - 1 byte
64 * Integer encoded as 8 bit signed (1 byte).
65 * |1111xxxx| - (with xxxx between 0000 and 1101) immediate 4 bit integer.
66 * Unsigned integer from 0 to 12. The encoded value is actually from
67 * 1 to 13 because 0000 and 1111 can not be used, so 1 should be
68 * subtracted from the encoded 4 bit value to obtain the right value.
69 * |11111111| - End of ziplist.
dd51571c 70 *
71 * All the integers are represented in little endian byte order.
d288ee65 72 *
73 * ----------------------------------------------------------------------------
74 *
75 * Copyright (c) 2009-2012, Pieter Noordhuis <pcnoordhuis at gmail dot com>
76 * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
77 * All rights reserved.
78 *
79 * Redistribution and use in source and binary forms, with or without
80 * modification, are permitted provided that the following conditions are met:
81 *
82 * * Redistributions of source code must retain the above copyright notice,
83 * this list of conditions and the following disclaimer.
84 * * Redistributions in binary form must reproduce the above copyright
85 * notice, this list of conditions and the following disclaimer in the
86 * documentation and/or other materials provided with the distribution.
87 * * Neither the name of Redis nor the names of its contributors may be used
88 * to endorse or promote products derived from this software without
89 * specific prior written permission.
90 *
91 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
92 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
93 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
94 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
95 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
96 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
97 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
98 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
99 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
100 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
101 * POSSIBILITY OF SUCH DAMAGE.
11ac6ff6
PN
102 */
103
104#include <stdio.h>
29b14d5f 105#include <stdlib.h>
11ac6ff6 106#include <string.h>
e1f93d4b 107#include <stdint.h>
11ac6ff6 108#include <assert.h>
29b14d5f 109#include <limits.h>
11ac6ff6 110#include "zmalloc.h"
edf23aff 111#include "util.h"
11ac6ff6 112#include "ziplist.h"
7a3e3720 113#include "endianconv.h"
11ac6ff6 114
37fff074 115#define ZIP_END 255
aa549962 116#define ZIP_BIGLEN 254
37fff074 117
c4705381 118/* Different encoding/length possibilities */
dcd4efe9 119#define ZIP_STR_MASK 0xc0
120#define ZIP_INT_MASK 0x30
c4705381
PN
121#define ZIP_STR_06B (0 << 6)
122#define ZIP_STR_14B (1 << 6)
123#define ZIP_STR_32B (2 << 6)
124#define ZIP_INT_16B (0xc0 | 0<<4)
125#define ZIP_INT_32B (0xc0 | 1<<4)
126#define ZIP_INT_64B (0xc0 | 2<<4)
ad91404a 127#define ZIP_INT_24B (0xc0 | 3<<4)
dcd4efe9 128#define ZIP_INT_8B 0xfe
129/* 4 bit integer immediate encoding */
130#define ZIP_INT_IMM_MASK 0x0f
131#define ZIP_INT_IMM_MIN 0xf1 /* 11110001 */
132#define ZIP_INT_IMM_MAX 0xfd /* 11111101 */
133#define ZIP_INT_IMM_VAL(v) (v & ZIP_INT_IMM_MASK)
ad91404a
GT
134
135#define INT24_MAX 0x7fffff
136#define INT24_MIN (-INT24_MAX - 1)
37fff074 137
fe458402
PN
138/* Macro to determine type */
139#define ZIP_IS_STR(enc) (((enc) & ZIP_STR_MASK) < ZIP_STR_MASK)
37fff074
PN
140
141/* Utility macros */
e1f93d4b
PN
142#define ZIPLIST_BYTES(zl) (*((uint32_t*)(zl)))
143#define ZIPLIST_TAIL_OFFSET(zl) (*((uint32_t*)((zl)+sizeof(uint32_t))))
144#define ZIPLIST_LENGTH(zl) (*((uint16_t*)((zl)+sizeof(uint32_t)*2)))
145#define ZIPLIST_HEADER_SIZE (sizeof(uint32_t)*2+sizeof(uint16_t))
146#define ZIPLIST_ENTRY_HEAD(zl) ((zl)+ZIPLIST_HEADER_SIZE)
8e0ef249 147#define ZIPLIST_ENTRY_TAIL(zl) ((zl)+intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl)))
148#define ZIPLIST_ENTRY_END(zl) ((zl)+intrev32ifbe(ZIPLIST_BYTES(zl))-1)
e1f93d4b
PN
149
150/* We know a positive increment can only be 1 because entries can only be
151 * pushed one at a time. */
f6eb1747 152#define ZIPLIST_INCR_LENGTH(zl,incr) { \
56538477 153 if (ZIPLIST_LENGTH(zl) < UINT16_MAX) \
3fa19b7d 154 ZIPLIST_LENGTH(zl) = intrev16ifbe(intrev16ifbe(ZIPLIST_LENGTH(zl))+incr); \
56538477 155}
11ac6ff6 156
a5456b2c
PN
157typedef struct zlentry {
158 unsigned int prevrawlensize, prevrawlen;
159 unsigned int lensize, len;
160 unsigned int headersize;
161 unsigned char encoding;
0c0d0564 162 unsigned char *p;
a5456b2c
PN
163} zlentry;
164
dcd4efe9 165/* Extract the encoding from the byte pointed by 'ptr' and set it into
166 * 'encoding'. */
167#define ZIP_ENTRY_ENCODING(ptr, encoding) do { \
168 (encoding) = (ptr[0]); \
169 if ((encoding) < ZIP_STR_MASK) (encoding) &= ZIP_STR_MASK; \
fe458402 170} while(0)
c4705381 171
37fff074 172/* Return bytes needed to store integer encoded by 'encoding' */
c4705381
PN
173static unsigned int zipIntSize(unsigned char encoding) {
174 switch(encoding) {
dcd4efe9 175 case ZIP_INT_8B: return 1;
176 case ZIP_INT_16B: return 2;
177 case ZIP_INT_24B: return 3;
178 case ZIP_INT_32B: return 4;
179 case ZIP_INT_64B: return 8;
180 default: return 0; /* 4 bit immediate */
37fff074
PN
181 }
182 assert(NULL);
8ce39260 183 return 0;
37fff074
PN
184}
185
37fff074
PN
186/* Encode the length 'l' writing it in 'p'. If p is NULL it just returns
187 * the amount of bytes required to encode such a length. */
c4705381
PN
188static unsigned int zipEncodeLength(unsigned char *p, unsigned char encoding, unsigned int rawlen) {
189 unsigned char len = 1, buf[5];
190
191 if (ZIP_IS_STR(encoding)) {
192 /* Although encoding is given it may not be set for strings,
193 * so we determine it here using the raw length. */
194 if (rawlen <= 0x3f) {
37fff074 195 if (!p) return len;
c4705381
PN
196 buf[0] = ZIP_STR_06B | rawlen;
197 } else if (rawlen <= 0x3fff) {
198 len += 1;
37fff074 199 if (!p) return len;
c4705381
PN
200 buf[0] = ZIP_STR_14B | ((rawlen >> 8) & 0x3f);
201 buf[1] = rawlen & 0xff;
37fff074
PN
202 } else {
203 len += 4;
204 if (!p) return len;
c4705381
PN
205 buf[0] = ZIP_STR_32B;
206 buf[1] = (rawlen >> 24) & 0xff;
207 buf[2] = (rawlen >> 16) & 0xff;
208 buf[3] = (rawlen >> 8) & 0xff;
209 buf[4] = rawlen & 0xff;
37fff074 210 }
c4705381
PN
211 } else {
212 /* Implies integer encoding, so length is always 1. */
213 if (!p) return len;
214 buf[0] = encoding;
37fff074 215 }
37fff074 216
c4705381 217 /* Store this length at p */
37fff074
PN
218 memcpy(p,buf,len);
219 return len;
220}
221
fe458402
PN
222/* Decode the length encoded in 'ptr'. The 'encoding' variable will hold the
223 * entries encoding, the 'lensize' variable will hold the number of bytes
224 * required to encode the entries length, and the 'len' variable will hold the
225 * entries length. */
226#define ZIP_DECODE_LENGTH(ptr, encoding, lensize, len) do { \
227 ZIP_ENTRY_ENCODING((ptr), (encoding)); \
228 if ((encoding) < ZIP_STR_MASK) { \
229 if ((encoding) == ZIP_STR_06B) { \
230 (lensize) = 1; \
231 (len) = (ptr)[0] & 0x3f; \
232 } else if ((encoding) == ZIP_STR_14B) { \
233 (lensize) = 2; \
234 (len) = (((ptr)[0] & 0x3f) << 8) | (ptr)[1]; \
235 } else if (encoding == ZIP_STR_32B) { \
236 (lensize) = 5; \
237 (len) = ((ptr)[1] << 24) | \
238 ((ptr)[2] << 16) | \
239 ((ptr)[3] << 8) | \
240 ((ptr)[4]); \
241 } else { \
242 assert(NULL); \
243 } \
244 } else { \
245 (lensize) = 1; \
246 (len) = zipIntSize(encoding); \
247 } \
248} while(0);
7b1f85c0
PN
249
250/* Encode the length of the previous entry and write it to "p". Return the
251 * number of bytes needed to encode this length if "p" is NULL. */
252static unsigned int zipPrevEncodeLength(unsigned char *p, unsigned int len) {
253 if (p == NULL) {
254 return (len < ZIP_BIGLEN) ? 1 : sizeof(len)+1;
255 } else {
256 if (len < ZIP_BIGLEN) {
257 p[0] = len;
258 return 1;
259 } else {
260 p[0] = ZIP_BIGLEN;
261 memcpy(p+1,&len,sizeof(len));
f2204374 262 memrev32ifbe(p+1);
7b1f85c0
PN
263 return 1+sizeof(len);
264 }
265 }
266}
267
169d2ef1
PN
268/* Encode the length of the previous entry and write it to "p". This only
269 * uses the larger encoding (required in __ziplistCascadeUpdate). */
270static void zipPrevEncodeLengthForceLarge(unsigned char *p, unsigned int len) {
271 if (p == NULL) return;
272 p[0] = ZIP_BIGLEN;
273 memcpy(p+1,&len,sizeof(len));
f2204374 274 memrev32ifbe(p+1);
169d2ef1
PN
275}
276
fe458402
PN
277/* Decode the number of bytes required to store the length of the previous
278 * element, from the perspective of the entry pointed to by 'ptr'. */
279#define ZIP_DECODE_PREVLENSIZE(ptr, prevlensize) do { \
280 if ((ptr)[0] < ZIP_BIGLEN) { \
281 (prevlensize) = 1; \
282 } else { \
283 (prevlensize) = 5; \
284 } \
285} while(0);
286
287/* Decode the length of the previous element, from the perspective of the entry
288 * pointed to by 'ptr'. */
289#define ZIP_DECODE_PREVLEN(ptr, prevlensize, prevlen) do { \
290 ZIP_DECODE_PREVLENSIZE(ptr, prevlensize); \
291 if ((prevlensize) == 1) { \
292 (prevlen) = (ptr)[0]; \
293 } else if ((prevlensize) == 5) { \
294 assert(sizeof((prevlensize)) == 4); \
295 memcpy(&(prevlen), ((char*)(ptr)) + 1, 4); \
b64281cc 296 memrev32ifbe(&prevlen); \
fe458402
PN
297 } \
298} while(0);
299
300/* Return the difference in number of bytes needed to store the length of the
301 * previous element 'len', in the entry pointed to by 'p'. */
dcb9cf4e
PN
302static int zipPrevLenByteDiff(unsigned char *p, unsigned int len) {
303 unsigned int prevlensize;
fe458402
PN
304 ZIP_DECODE_PREVLENSIZE(p, prevlensize);
305 return zipPrevEncodeLength(NULL, len) - prevlensize;
306}
307
308/* Return the total number of bytes used by the entry pointed to by 'p'. */
309static unsigned int zipRawEntryLength(unsigned char *p) {
310 unsigned int prevlensize, encoding, lensize, len;
311 ZIP_DECODE_PREVLENSIZE(p, prevlensize);
312 ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len);
313 return prevlensize + lensize + len;
dcb9cf4e
PN
314}
315
37fff074 316/* Check if string pointed to by 'entry' can be encoded as an integer.
61712508 317 * Stores the integer value in 'v' and its encoding in 'encoding'. */
318static int zipTryEncoding(unsigned char *entry, unsigned int entrylen, long long *v, unsigned char *encoding) {
37fff074 319 long long value;
37fff074 320
61712508 321 if (entrylen >= 32 || entrylen == 0) return 0;
edf23aff 322 if (string2ll((char*)entry,entrylen,&value)) {
61712508 323 /* Great, the string can be encoded. Check what's the smallest
324 * of our encoding types that can hold this value. */
dcd4efe9 325 if (value >= 0 && value <= 12) {
326 *encoding = ZIP_INT_IMM_MIN+value;
327 } else if (value >= INT8_MIN && value <= INT8_MAX) {
328 *encoding = ZIP_INT_8B;
329 } else if (value >= INT16_MIN && value <= INT16_MAX) {
c4705381 330 *encoding = ZIP_INT_16B;
ad91404a
GT
331 } else if (value >= INT24_MIN && value <= INT24_MAX) {
332 *encoding = ZIP_INT_24B;
e1f93d4b 333 } else if (value >= INT32_MIN && value <= INT32_MAX) {
c4705381 334 *encoding = ZIP_INT_32B;
37fff074 335 } else {
c4705381 336 *encoding = ZIP_INT_64B;
37fff074
PN
337 }
338 *v = value;
339 return 1;
340 }
341 return 0;
342}
343
344/* Store integer 'value' at 'p', encoded as 'encoding' */
e1f93d4b
PN
345static void zipSaveInteger(unsigned char *p, int64_t value, unsigned char encoding) {
346 int16_t i16;
347 int32_t i32;
348 int64_t i64;
dcd4efe9 349 if (encoding == ZIP_INT_8B) {
82675c86 350 ((int8_t*)p)[0] = (int8_t)value;
dcd4efe9 351 } else if (encoding == ZIP_INT_16B) {
e1f93d4b
PN
352 i16 = value;
353 memcpy(p,&i16,sizeof(i16));
f2204374 354 memrev16ifbe(p);
ad91404a
GT
355 } else if (encoding == ZIP_INT_24B) {
356 i32 = value<<8;
357 memrev32ifbe(&i32);
82675c86 358 memcpy(p,((uint8_t*)&i32)+1,sizeof(i32)-sizeof(uint8_t));
c4705381 359 } else if (encoding == ZIP_INT_32B) {
e1f93d4b
PN
360 i32 = value;
361 memcpy(p,&i32,sizeof(i32));
f2204374 362 memrev32ifbe(p);
c4705381 363 } else if (encoding == ZIP_INT_64B) {
e1f93d4b
PN
364 i64 = value;
365 memcpy(p,&i64,sizeof(i64));
f2204374 366 memrev64ifbe(p);
dcd4efe9 367 } else if (encoding >= ZIP_INT_IMM_MIN && encoding <= ZIP_INT_IMM_MAX) {
368 /* Nothing to do, the value is stored in the encoding itself. */
37fff074
PN
369 } else {
370 assert(NULL);
371 }
372}
373
374/* Read integer encoded as 'encoding' from 'p' */
e1f93d4b
PN
375static int64_t zipLoadInteger(unsigned char *p, unsigned char encoding) {
376 int16_t i16;
377 int32_t i32;
8ce39260 378 int64_t i64, ret = 0;
dcd4efe9 379 if (encoding == ZIP_INT_8B) {
82675c86 380 ret = ((int8_t*)p)[0];
dcd4efe9 381 } else if (encoding == ZIP_INT_16B) {
e1f93d4b 382 memcpy(&i16,p,sizeof(i16));
f2204374 383 memrev16ifbe(&i16);
e1f93d4b 384 ret = i16;
c4705381 385 } else if (encoding == ZIP_INT_32B) {
e1f93d4b 386 memcpy(&i32,p,sizeof(i32));
66d1b021 387 memrev32ifbe(&i32);
e1f93d4b 388 ret = i32;
ad91404a
GT
389 } else if (encoding == ZIP_INT_24B) {
390 i32 = 0;
82675c86 391 memcpy(((uint8_t*)&i32)+1,p,sizeof(i32)-sizeof(uint8_t));
ad91404a
GT
392 memrev32ifbe(&i32);
393 ret = i32>>8;
c4705381 394 } else if (encoding == ZIP_INT_64B) {
e1f93d4b 395 memcpy(&i64,p,sizeof(i64));
3fa19b7d 396 memrev64ifbe(&i64);
e1f93d4b 397 ret = i64;
dcd4efe9 398 } else if (encoding >= ZIP_INT_IMM_MIN && encoding <= ZIP_INT_IMM_MAX) {
399 ret = (encoding & ZIP_INT_IMM_MASK)-1;
37fff074
PN
400 } else {
401 assert(NULL);
402 }
403 return ret;
404}
405
a5456b2c
PN
406/* Return a struct with all information about an entry. */
407static zlentry zipEntry(unsigned char *p) {
408 zlentry e;
fe458402
PN
409
410 ZIP_DECODE_PREVLEN(p, e.prevrawlensize, e.prevrawlen);
411 ZIP_DECODE_LENGTH(p + e.prevrawlensize, e.encoding, e.lensize, e.len);
412 e.headersize = e.prevrawlensize + e.lensize;
0c0d0564 413 e.p = p;
a5456b2c
PN
414 return e;
415}
416
11ac6ff6
PN
417/* Create a new empty ziplist. */
418unsigned char *ziplistNew(void) {
419 unsigned int bytes = ZIPLIST_HEADER_SIZE+1;
420 unsigned char *zl = zmalloc(bytes);
56538477 421 ZIPLIST_BYTES(zl) = intrev32ifbe(bytes);
422 ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(ZIPLIST_HEADER_SIZE);
11ac6ff6
PN
423 ZIPLIST_LENGTH(zl) = 0;
424 zl[bytes-1] = ZIP_END;
425 return zl;
426}
427
37fff074 428/* Resize the ziplist. */
11ac6ff6 429static unsigned char *ziplistResize(unsigned char *zl, unsigned int len) {
37fff074 430 zl = zrealloc(zl,len);
56538477 431 ZIPLIST_BYTES(zl) = intrev32ifbe(len);
11ac6ff6
PN
432 zl[len-1] = ZIP_END;
433 return zl;
434}
435
169d2ef1
PN
436/* When an entry is inserted, we need to set the prevlen field of the next
437 * entry to equal the length of the inserted entry. It can occur that this
438 * length cannot be encoded in 1 byte and the next entry needs to be grow
439 * a bit larger to hold the 5-byte encoded prevlen. This can be done for free,
440 * because this only happens when an entry is already being inserted (which
441 * causes a realloc and memmove). However, encoding the prevlen may require
442 * that this entry is grown as well. This effect may cascade throughout
443 * the ziplist when there are consecutive entries with a size close to
444 * ZIP_BIGLEN, so we need to check that the prevlen can be encoded in every
445 * consecutive entry.
446 *
447 * Note that this effect can also happen in reverse, where the bytes required
448 * to encode the prevlen field can shrink. This effect is deliberately ignored,
449 * because it can cause a "flapping" effect where a chain prevlen fields is
450 * first grown and then shrunk again after consecutive inserts. Rather, the
451 * field is allowed to stay larger than necessary, because a large prevlen
452 * field implies the ziplist is holding large entries anyway.
453 *
454 * The pointer "p" points to the first entry that does NOT need to be
455 * updated, i.e. consecutive fields MAY need an update. */
456static unsigned char *__ziplistCascadeUpdate(unsigned char *zl, unsigned char *p) {
56538477 457 size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), rawlen, rawlensize;
69298a05 458 size_t offset, noffset, extra;
169d2ef1
PN
459 unsigned char *np;
460 zlentry cur, next;
461
462 while (p[0] != ZIP_END) {
463 cur = zipEntry(p);
464 rawlen = cur.headersize + cur.len;
465 rawlensize = zipPrevEncodeLength(NULL,rawlen);
466
467 /* Abort if there is no next entry. */
468 if (p[rawlen] == ZIP_END) break;
469 next = zipEntry(p+rawlen);
470
471 /* Abort when "prevlen" has not changed. */
472 if (next.prevrawlen == rawlen) break;
473
474 if (next.prevrawlensize < rawlensize) {
475 /* The "prevlen" field of "next" needs more bytes to hold
476 * the raw length of "cur". */
477 offset = p-zl;
478 extra = rawlensize-next.prevrawlensize;
479 zl = ziplistResize(zl,curlen+extra);
169d2ef1
PN
480 p = zl+offset;
481
b7d3bf51 482 /* Current pointer and offset for next element. */
169d2ef1
PN
483 np = p+rawlen;
484 noffset = np-zl;
b7d3bf51
PN
485
486 /* Update tail offset when next element is not the tail element. */
3fa19b7d 487 if ((zl+intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))) != np) {
488 ZIPLIST_TAIL_OFFSET(zl) =
489 intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+extra);
490 }
b7d3bf51
PN
491
492 /* Move the tail to the back. */
169d2ef1
PN
493 memmove(np+rawlensize,
494 np+next.prevrawlensize,
495 curlen-noffset-next.prevrawlensize-1);
496 zipPrevEncodeLength(np,rawlen);
497
498 /* Advance the cursor */
499 p += rawlen;
306c6a02 500 curlen += extra;
169d2ef1
PN
501 } else {
502 if (next.prevrawlensize > rawlensize) {
503 /* This would result in shrinking, which we want to avoid.
504 * So, set "rawlen" in the available bytes. */
505 zipPrevEncodeLengthForceLarge(p+rawlen,rawlen);
506 } else {
507 zipPrevEncodeLength(p+rawlen,rawlen);
508 }
509
510 /* Stop here, as the raw length of "next" has not changed. */
511 break;
512 }
513 }
514 return zl;
515}
516
0c0d0564 517/* Delete "num" entries, starting at "p". Returns pointer to the ziplist. */
b6eb9703 518static unsigned char *__ziplistDelete(unsigned char *zl, unsigned char *p, unsigned int num) {
0c0d0564 519 unsigned int i, totlen, deleted = 0;
69298a05
PN
520 size_t offset;
521 int nextdiff = 0;
169d2ef1
PN
522 zlentry first, tail;
523
524 first = zipEntry(p);
0c0d0564
PN
525 for (i = 0; p[0] != ZIP_END && i < num; i++) {
526 p += zipRawEntryLength(p);
527 deleted++;
528 }
529
530 totlen = p-first.p;
531 if (totlen > 0) {
532 if (p[0] != ZIP_END) {
2f444526
PN
533 /* Storing `prevrawlen` in this entry may increase or decrease the
534 * number of bytes required compare to the current `prevrawlen`.
535 * There always is room to store this, because it was previously
536 * stored by an entry that is now being deleted. */
0c0d0564 537 nextdiff = zipPrevLenByteDiff(p,first.prevrawlen);
2f444526
PN
538 p -= nextdiff;
539 zipPrevEncodeLength(p,first.prevrawlen);
0c0d0564
PN
540
541 /* Update offset for tail */
3fa19b7d 542 ZIPLIST_TAIL_OFFSET(zl) =
543 intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))-totlen);
169d2ef1
PN
544
545 /* When the tail contains more than one entry, we need to take
546 * "nextdiff" in account as well. Otherwise, a change in the
547 * size of prevlen doesn't have an effect on the *tail* offset. */
548 tail = zipEntry(p);
3fa19b7d 549 if (p[tail.headersize+tail.len] != ZIP_END) {
cab1105c 550 ZIPLIST_TAIL_OFFSET(zl) =
3fa19b7d 551 intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+nextdiff);
552 }
0c0d0564
PN
553
554 /* Move tail to the front of the ziplist */
2f444526
PN
555 memmove(first.p,p,
556 intrev32ifbe(ZIPLIST_BYTES(zl))-(p-zl)-1);
0c0d0564
PN
557 } else {
558 /* The entire tail was deleted. No need to move memory. */
56538477 559 ZIPLIST_TAIL_OFFSET(zl) =
560 intrev32ifbe((first.p-zl)-first.prevrawlen);
0c0d0564
PN
561 }
562
563 /* Resize and update length */
169d2ef1 564 offset = first.p-zl;
56538477 565 zl = ziplistResize(zl, intrev32ifbe(ZIPLIST_BYTES(zl))-totlen+nextdiff);
0c0d0564 566 ZIPLIST_INCR_LENGTH(zl,-deleted);
169d2ef1
PN
567 p = zl+offset;
568
569 /* When nextdiff != 0, the raw length of the next entry has changed, so
570 * we need to cascade the update throughout the ziplist */
571 if (nextdiff != 0)
572 zl = __ziplistCascadeUpdate(zl,p);
0c0d0564
PN
573 }
574 return zl;
11ac6ff6
PN
575}
576
6435c767 577/* Insert item at "p". */
b6eb9703 578static unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {
56538477 579 size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), reqlen, prevlen = 0;
69298a05
PN
580 size_t offset;
581 int nextdiff = 0;
c4705381 582 unsigned char encoding = 0;
f013f400 583 long long value = 123456789; /* initialized to avoid warning. Using a value
584 that is easy to see if for some reason
585 we use it uninitialized. */
169d2ef1 586 zlentry entry, tail;
11ac6ff6 587
6435c767
PN
588 /* Find out prevlen for the entry that is inserted. */
589 if (p[0] != ZIP_END) {
590 entry = zipEntry(p);
591 prevlen = entry.prevrawlen;
dcb9cf4e 592 } else {
169d2ef1
PN
593 unsigned char *ptail = ZIPLIST_ENTRY_TAIL(zl);
594 if (ptail[0] != ZIP_END) {
595 prevlen = zipRawEntryLength(ptail);
6435c767 596 }
dcb9cf4e
PN
597 }
598
29b14d5f 599 /* See if the entry can be encoded */
61712508 600 if (zipTryEncoding(s,slen,&value,&encoding)) {
c4705381
PN
601 /* 'encoding' is set to the appropriate integer encoding */
602 reqlen = zipIntSize(encoding);
29b14d5f 603 } else {
c4705381
PN
604 /* 'encoding' is untouched, however zipEncodeLength will use the
605 * string length to figure out how to encode it. */
6435c767 606 reqlen = slen;
29b14d5f 607 }
dcb9cf4e
PN
608 /* We need space for both the length of the previous entry and
609 * the length of the payload. */
7b1f85c0 610 reqlen += zipPrevEncodeLength(NULL,prevlen);
6435c767
PN
611 reqlen += zipEncodeLength(NULL,encoding,slen);
612
613 /* When the insert position is not equal to the tail, we need to
614 * make sure that the next entry can hold this entry's length in
615 * its prevlen field. */
177a0a0b 616 nextdiff = (p[0] != ZIP_END) ? zipPrevLenByteDiff(p,reqlen) : 0;
6435c767
PN
617
618 /* Store offset because a realloc may change the address of zl. */
619 offset = p-zl;
620 zl = ziplistResize(zl,curlen+reqlen+nextdiff);
621 p = zl+offset;
622
623 /* Apply memory move when necessary and update tail offset. */
624 if (p[0] != ZIP_END) {
625 /* Subtract one because of the ZIP_END bytes */
626 memmove(p+reqlen,p-nextdiff,curlen-offset-1+nextdiff);
169d2ef1 627
6435c767 628 /* Encode this entry's raw length in the next entry. */
7b1f85c0 629 zipPrevEncodeLength(p+reqlen,reqlen);
169d2ef1 630
6435c767 631 /* Update offset for tail */
3fa19b7d 632 ZIPLIST_TAIL_OFFSET(zl) =
633 intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+reqlen);
169d2ef1
PN
634
635 /* When the tail contains more than one entry, we need to take
636 * "nextdiff" in account as well. Otherwise, a change in the
637 * size of prevlen doesn't have an effect on the *tail* offset. */
638 tail = zipEntry(p+reqlen);
3fa19b7d 639 if (p[reqlen+tail.headersize+tail.len] != ZIP_END) {
640 ZIPLIST_TAIL_OFFSET(zl) =
641 intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+nextdiff);
642 }
11ac6ff6 643 } else {
6435c767 644 /* This element will be the new tail. */
56538477 645 ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(p-zl);
dcb9cf4e
PN
646 }
647
169d2ef1
PN
648 /* When nextdiff != 0, the raw length of the next entry has changed, so
649 * we need to cascade the update throughout the ziplist */
650 if (nextdiff != 0) {
651 offset = p-zl;
652 zl = __ziplistCascadeUpdate(zl,p+reqlen);
653 p = zl+offset;
654 }
655
11ac6ff6 656 /* Write the entry */
7b1f85c0 657 p += zipPrevEncodeLength(p,prevlen);
6435c767 658 p += zipEncodeLength(p,encoding,slen);
c4705381 659 if (ZIP_IS_STR(encoding)) {
6435c767 660 memcpy(p,s,slen);
c4705381
PN
661 } else {
662 zipSaveInteger(p,value,encoding);
29b14d5f 663 }
f6eb1747 664 ZIPLIST_INCR_LENGTH(zl,1);
11ac6ff6
PN
665 return zl;
666}
667
b6eb9703 668unsigned char *ziplistPush(unsigned char *zl, unsigned char *s, unsigned int slen, int where) {
6435c767 669 unsigned char *p;
1ce81fa5 670 p = (where == ZIPLIST_HEAD) ? ZIPLIST_ENTRY_HEAD(zl) : ZIPLIST_ENTRY_END(zl);
6435c767
PN
671 return __ziplistInsert(zl,p,s,slen);
672}
673
c03206fd
PN
674/* Returns an offset to use for iterating with ziplistNext. When the given
675 * index is negative, the list is traversed back to front. When the list
676 * doesn't contain an element at the provided index, NULL is returned. */
677unsigned char *ziplistIndex(unsigned char *zl, int index) {
678 unsigned char *p;
679 zlentry entry;
680 if (index < 0) {
681 index = (-index)-1;
682 p = ZIPLIST_ENTRY_TAIL(zl);
683 if (p[0] != ZIP_END) {
684 entry = zipEntry(p);
685 while (entry.prevrawlen > 0 && index--) {
686 p -= entry.prevrawlen;
687 entry = zipEntry(p);
688 }
689 }
690 } else {
691 p = ZIPLIST_ENTRY_HEAD(zl);
692 while (p[0] != ZIP_END && index--) {
693 p += zipRawEntryLength(p);
694 }
08253bf4 695 }
177a0a0b 696 return (p[0] == ZIP_END || index > 0) ? NULL : p;
08253bf4
PN
697}
698
d51ebef5 699/* Return pointer to next entry in ziplist.
700 *
701 * zl is the pointer to the ziplist
702 * p is the pointer to the current element
703 *
704 * The element after 'p' is returned, otherwise NULL if we are at the end. */
8632fb30
PN
705unsigned char *ziplistNext(unsigned char *zl, unsigned char *p) {
706 ((void) zl);
d71b9865
PN
707
708 /* "p" could be equal to ZIP_END, caused by ziplistDelete,
709 * and we should return NULL. Otherwise, we should return NULL
710 * when the *next* element is ZIP_END (there is no next entry). */
711 if (p[0] == ZIP_END) {
712 return NULL;
d71b9865 713 }
fe458402
PN
714
715 p += zipRawEntryLength(p);
716 if (p[0] == ZIP_END) {
717 return NULL;
718 }
719
720 return p;
75d8978e
PN
721}
722
033fb554 723/* Return pointer to previous entry in ziplist. */
8632fb30
PN
724unsigned char *ziplistPrev(unsigned char *zl, unsigned char *p) {
725 zlentry entry;
726
727 /* Iterating backwards from ZIP_END should return the tail. When "p" is
728 * equal to the first element of the list, we're already at the head,
729 * and should return NULL. */
730 if (p[0] == ZIP_END) {
731 p = ZIPLIST_ENTRY_TAIL(zl);
732 return (p[0] == ZIP_END) ? NULL : p;
733 } else if (p == ZIPLIST_ENTRY_HEAD(zl)) {
734 return NULL;
735 } else {
736 entry = zipEntry(p);
169d2ef1 737 assert(entry.prevrawlen > 0);
8632fb30
PN
738 return p-entry.prevrawlen;
739 }
033fb554
PN
740}
741
75d8978e
PN
742/* Get entry pointer to by 'p' and store in either 'e' or 'v' depending
743 * on the encoding of the entry. 'e' is always set to NULL to be able
744 * to find out whether the string pointer or the integer value was set.
745 * Return 0 if 'p' points to the end of the zipmap, 1 otherwise. */
b6eb9703 746unsigned int ziplistGet(unsigned char *p, unsigned char **sstr, unsigned int *slen, long long *sval) {
a5456b2c 747 zlentry entry;
c03206fd 748 if (p == NULL || p[0] == ZIP_END) return 0;
03e52931 749 if (sstr) *sstr = NULL;
dcb9cf4e 750
a5456b2c 751 entry = zipEntry(p);
c4705381 752 if (ZIP_IS_STR(entry.encoding)) {
03e52931
PN
753 if (sstr) {
754 *slen = entry.len;
b6eb9703 755 *sstr = p+entry.headersize;
75d8978e
PN
756 }
757 } else {
03e52931
PN
758 if (sval) {
759 *sval = zipLoadInteger(p+entry.headersize,entry.encoding);
75d8978e 760 }
08253bf4 761 }
75d8978e 762 return 1;
08253bf4
PN
763}
764
033fb554 765/* Insert an entry at "p". */
b6eb9703 766unsigned char *ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {
033fb554 767 return __ziplistInsert(zl,p,s,slen);
779deb60
PN
768}
769
0f10458c
PN
770/* Delete a single entry from the ziplist, pointed to by *p.
771 * Also update *p in place, to be able to iterate over the
772 * ziplist, while deleting entries. */
6a8e35ad 773unsigned char *ziplistDelete(unsigned char *zl, unsigned char **p) {
69298a05 774 size_t offset = *p-zl;
0c0d0564 775 zl = __ziplistDelete(zl,*p,1);
0f10458c 776
0c0d0564 777 /* Store pointer to current element in p, because ziplistDelete will
0f3dfa87
PN
778 * do a realloc which might result in a different "zl"-pointer.
779 * When the delete direction is back to front, we might delete the last
780 * entry and end up with "p" pointing to ZIP_END, so check this. */
6a8e35ad 781 *p = zl+offset;
0f10458c
PN
782 return zl;
783}
784
033fb554
PN
785/* Delete a range of entries from the ziplist. */
786unsigned char *ziplistDeleteRange(unsigned char *zl, unsigned int index, unsigned int num) {
787 unsigned char *p = ziplistIndex(zl,index);
788 return (p == NULL) ? zl : __ziplistDelete(zl,p,num);
789}
790
c09c2c3b 791/* Compare entry pointer to by 'p' with 'entry'. Return 1 if equal. */
b6eb9703 792unsigned int ziplistCompare(unsigned char *p, unsigned char *sstr, unsigned int slen) {
a5456b2c 793 zlentry entry;
b6eb9703
PN
794 unsigned char sencoding;
795 long long zval, sval;
177a0a0b 796 if (p[0] == ZIP_END) return 0;
c09c2c3b 797
a5456b2c 798 entry = zipEntry(p);
c4705381 799 if (ZIP_IS_STR(entry.encoding)) {
c09c2c3b 800 /* Raw compare */
a5456b2c 801 if (entry.len == slen) {
03e52931 802 return memcmp(p+entry.headersize,sstr,slen) == 0;
c09c2c3b
PN
803 } else {
804 return 0;
805 }
c4aace90 806 } else {
0ef88927
PN
807 /* Try to compare encoded values. Don't compare encoding because
808 * different implementations may encoded integers differently. */
61712508 809 if (zipTryEncoding(sstr,slen,&sval,&sencoding)) {
0ef88927
PN
810 zval = zipLoadInteger(p+entry.headersize,entry.encoding);
811 return zval == sval;
c4aace90 812 }
c09c2c3b 813 }
c4aace90 814 return 0;
c09c2c3b
PN
815}
816
fe458402
PN
817/* Find pointer to the entry equal to the specified entry. Skip 'skip' entries
818 * between every comparison. Returns NULL when the field could not be found. */
819unsigned char *ziplistFind(unsigned char *p, unsigned char *vstr, unsigned int vlen, unsigned int skip) {
820 int skipcnt = 0;
821 unsigned char vencoding = 0;
822 long long vll = 0;
823
824 while (p[0] != ZIP_END) {
825 unsigned int prevlensize, encoding, lensize, len;
826 unsigned char *q;
827
828 ZIP_DECODE_PREVLENSIZE(p, prevlensize);
829 ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len);
830 q = p + prevlensize + lensize;
831
832 if (skipcnt == 0) {
833 /* Compare current entry with specified entry */
834 if (ZIP_IS_STR(encoding)) {
835 if (len == vlen && memcmp(q, vstr, vlen) == 0) {
836 return p;
837 }
838 } else {
8361d6c4 839 /* Find out if the searched field can be encoded. Note that
840 * we do it only the first time, once done vencoding is set
841 * to non-zero and vll is set to the integer value. */
fe458402 842 if (vencoding == 0) {
fe458402 843 if (!zipTryEncoding(vstr, vlen, &vll, &vencoding)) {
8361d6c4 844 /* If the entry can't be encoded we set it to
845 * UCHAR_MAX so that we don't retry again the next
846 * time. */
fe458402
PN
847 vencoding = UCHAR_MAX;
848 }
fe458402
PN
849 /* Must be non-zero by now */
850 assert(vencoding);
851 }
852
8361d6c4 853 /* Compare current entry with specified entry, do it only
854 * if vencoding != UCHAR_MAX because if there is no encoding
855 * possible for the field it can't be a valid integer. */
856 if (vencoding != UCHAR_MAX) {
fe458402
PN
857 long long ll = zipLoadInteger(q, encoding);
858 if (ll == vll) {
859 return p;
860 }
861 }
862 }
863
864 /* Reset skip count */
865 skipcnt = skip;
866 } else {
867 /* Skip entry */
868 skipcnt--;
869 }
870
871 /* Move to next entry */
872 p = q + len;
873 }
874
875 return NULL;
876}
877
6205b463
PN
878/* Return length of ziplist. */
879unsigned int ziplistLen(unsigned char *zl) {
880 unsigned int len = 0;
56538477 881 if (intrev16ifbe(ZIPLIST_LENGTH(zl)) < UINT16_MAX) {
882 len = intrev16ifbe(ZIPLIST_LENGTH(zl));
6205b463
PN
883 } else {
884 unsigned char *p = zl+ZIPLIST_HEADER_SIZE;
885 while (*p != ZIP_END) {
886 p += zipRawEntryLength(p);
887 len++;
888 }
889
890 /* Re-store length if small enough */
56538477 891 if (len < UINT16_MAX) ZIPLIST_LENGTH(zl) = intrev16ifbe(len);
6205b463
PN
892 }
893 return len;
894}
895
d4fb9f41 896/* Return ziplist blob size in bytes. */
897size_t ziplistBlobLen(unsigned char *zl) {
56538477 898 return intrev32ifbe(ZIPLIST_BYTES(zl));
4812cf28
PN
899}
900
11ac6ff6 901void ziplistRepr(unsigned char *zl) {
c8d9e7f4 902 unsigned char *p;
169d2ef1 903 int index = 0;
c8d9e7f4 904 zlentry entry;
11ac6ff6 905
169d2ef1
PN
906 printf(
907 "{total bytes %d} "
908 "{length %u}\n"
909 "{tail offset %u}\n",
56538477 910 intrev32ifbe(ZIPLIST_BYTES(zl)),
911 intrev16ifbe(ZIPLIST_LENGTH(zl)),
912 intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl)));
1ce81fa5 913 p = ZIPLIST_ENTRY_HEAD(zl);
11ac6ff6 914 while(*p != ZIP_END) {
c8d9e7f4 915 entry = zipEntry(p);
169d2ef1
PN
916 printf(
917 "{"
918 "addr 0x%08lx, "
919 "index %2d, "
920 "offset %5ld, "
921 "rl: %5u, "
922 "hs %2u, "
923 "pl: %5u, "
924 "pls: %2u, "
925 "payload %5u"
926 "} ",
10c12171 927 (long unsigned)p,
169d2ef1 928 index,
10c12171 929 (unsigned long) (p-zl),
169d2ef1
PN
930 entry.headersize+entry.len,
931 entry.headersize,
932 entry.prevrawlen,
933 entry.prevrawlensize,
934 entry.len);
c8d9e7f4 935 p += entry.headersize;
c4705381 936 if (ZIP_IS_STR(entry.encoding)) {
169d2ef1 937 if (entry.len > 40) {
10c12171 938 if (fwrite(p,40,1,stdout) == 0) perror("fwrite");
169d2ef1
PN
939 printf("...");
940 } else {
10c12171 941 if (entry.len &&
942 fwrite(p,entry.len,1,stdout) == 0) perror("fwrite");
169d2ef1 943 }
29b14d5f 944 } else {
3688d7f3 945 printf("%lld", (long long) zipLoadInteger(p,entry.encoding));
29b14d5f 946 }
11ac6ff6 947 printf("\n");
c8d9e7f4 948 p += entry.len;
169d2ef1 949 index++;
11ac6ff6
PN
950 }
951 printf("{end}\n\n");
952}
953
954#ifdef ZIPLIST_TEST_MAIN
ffc15852 955#include <sys/time.h>
306c6a02
PN
956#include "adlist.h"
957#include "sds.h"
958
959#define debug(f, ...) { if (DEBUG) printf(f, __VA_ARGS__); }
11ac6ff6 960
08253bf4
PN
961unsigned char *createList() {
962 unsigned char *zl = ziplistNew();
b84186ff
PN
963 zl = ziplistPush(zl, (unsigned char*)"foo", 3, ZIPLIST_TAIL);
964 zl = ziplistPush(zl, (unsigned char*)"quux", 4, ZIPLIST_TAIL);
965 zl = ziplistPush(zl, (unsigned char*)"hello", 5, ZIPLIST_HEAD);
966 zl = ziplistPush(zl, (unsigned char*)"1024", 4, ZIPLIST_TAIL);
08253bf4
PN
967 return zl;
968}
969
29b14d5f
PN
970unsigned char *createIntList() {
971 unsigned char *zl = ziplistNew();
972 char buf[32];
973
974 sprintf(buf, "100");
b84186ff 975 zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);
29b14d5f 976 sprintf(buf, "128000");
b84186ff 977 zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);
29b14d5f 978 sprintf(buf, "-100");
b84186ff 979 zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_HEAD);
29b14d5f 980 sprintf(buf, "4294967296");
b84186ff 981 zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_HEAD);
29b14d5f 982 sprintf(buf, "non integer");
b84186ff 983 zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);
29b14d5f 984 sprintf(buf, "much much longer non integer");
b84186ff 985 zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);
29b14d5f
PN
986 return zl;
987}
988
ffc15852
PN
989long long usec(void) {
990 struct timeval tv;
991 gettimeofday(&tv,NULL);
992 return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;
993}
994
995void stress(int pos, int num, int maxsize, int dnum) {
996 int i,j,k;
997 unsigned char *zl;
998 char posstr[2][5] = { "HEAD", "TAIL" };
999 long long start;
1000 for (i = 0; i < maxsize; i+=dnum) {
1001 zl = ziplistNew();
1002 for (j = 0; j < i; j++) {
1003 zl = ziplistPush(zl,(unsigned char*)"quux",4,ZIPLIST_TAIL);
1004 }
1005
1006 /* Do num times a push+pop from pos */
1007 start = usec();
1008 for (k = 0; k < num; k++) {
1009 zl = ziplistPush(zl,(unsigned char*)"quux",4,pos);
1010 zl = ziplistDeleteRange(zl,0,1);
1011 }
1012 printf("List size: %8d, bytes: %8d, %dx push+pop (%s): %6lld usec\n",
56538477 1013 i,intrev32ifbe(ZIPLIST_BYTES(zl)),num,posstr[pos],usec()-start);
ffc15852
PN
1014 zfree(zl);
1015 }
1016}
1017
306974f5
PN
1018void pop(unsigned char *zl, int where) {
1019 unsigned char *p, *vstr;
1020 unsigned int vlen;
1021 long long vlong;
1022
1023 p = ziplistIndex(zl,where == ZIPLIST_HEAD ? 0 : -1);
1024 if (ziplistGet(p,&vstr,&vlen,&vlong)) {
1025 if (where == ZIPLIST_HEAD)
1026 printf("Pop head: ");
1027 else
1028 printf("Pop tail: ");
1029
1030 if (vstr)
10c12171 1031 if (vlen && fwrite(vstr,vlen,1,stdout) == 0) perror("fwrite");
306974f5
PN
1032 else
1033 printf("%lld", vlong);
1034
1035 printf("\n");
1036 ziplistDeleteRange(zl,-1,1);
1037 } else {
1038 printf("ERROR: Could not pop\n");
1039 exit(1);
1040 }
1041}
1042
b7d3bf51 1043int randstring(char *target, unsigned int min, unsigned int max) {
306c6a02
PN
1044 int p, len = min+rand()%(max-min+1);
1045 int minval, maxval;
1046 switch(rand() % 3) {
1047 case 0:
1048 minval = 0;
1049 maxval = 255;
1050 break;
1051 case 1:
1052 minval = 48;
1053 maxval = 122;
1054 break;
1055 case 2:
1056 minval = 48;
1057 maxval = 52;
1058 break;
1059 default:
1060 assert(NULL);
1061 }
1062
1063 while(p < len)
1064 target[p++] = minval+rand()%(maxval-minval+1);
b7d3bf51 1065 return len;
306c6a02
PN
1066}
1067
89bf6f58
PN
1068void verify(unsigned char *zl, zlentry *e) {
1069 int i;
1070 int len = ziplistLen(zl);
1071 zlentry _e;
1072
1073 for (i = 0; i < len; i++) {
1074 memset(&e[i], 0, sizeof(zlentry));
1075 e[i] = zipEntry(ziplistIndex(zl, i));
1076
1077 memset(&_e, 0, sizeof(zlentry));
1078 _e = zipEntry(ziplistIndex(zl, -len+i));
1079
1080 assert(memcmp(&e[i], &_e, sizeof(zlentry)) == 0);
1081 }
1082}
1083
08253bf4 1084int main(int argc, char **argv) {
a24ba809 1085 unsigned char *zl, *p;
b84186ff 1086 unsigned char *entry;
335d16bc 1087 unsigned int elen;
75d8978e 1088 long long value;
08253bf4 1089
84403fe7
PN
1090 /* If an argument is given, use it as the random seed. */
1091 if (argc == 2)
1092 srand(atoi(argv[1]));
1093
29b14d5f
PN
1094 zl = createIntList();
1095 ziplistRepr(zl);
1096
08253bf4 1097 zl = createList();
11ac6ff6
PN
1098 ziplistRepr(zl);
1099
306974f5 1100 pop(zl,ZIPLIST_TAIL);
11ac6ff6
PN
1101 ziplistRepr(zl);
1102
306974f5 1103 pop(zl,ZIPLIST_HEAD);
11ac6ff6
PN
1104 ziplistRepr(zl);
1105
306974f5 1106 pop(zl,ZIPLIST_TAIL);
dcb9cf4e
PN
1107 ziplistRepr(zl);
1108
306974f5 1109 pop(zl,ZIPLIST_TAIL);
dcb9cf4e
PN
1110 ziplistRepr(zl);
1111
c03206fd
PN
1112 printf("Get element at index 3:\n");
1113 {
1114 zl = createList();
1115 p = ziplistIndex(zl, 3);
1116 if (!ziplistGet(p, &entry, &elen, &value)) {
1117 printf("ERROR: Could not access index 3\n");
1118 return 1;
1119 }
1120 if (entry) {
10c12171 1121 if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite");
c03206fd
PN
1122 printf("\n");
1123 } else {
1124 printf("%lld\n", value);
1125 }
1126 printf("\n");
1127 }
1128
1129 printf("Get element at index 4 (out of range):\n");
1130 {
1131 zl = createList();
1132 p = ziplistIndex(zl, 4);
1133 if (p == NULL) {
1134 printf("No entry\n");
1135 } else {
1136 printf("ERROR: Out of range index should return NULL, returned offset: %ld\n", p-zl);
1137 return 1;
1138 }
1139 printf("\n");
1140 }
1141
1142 printf("Get element at index -1 (last element):\n");
1143 {
1144 zl = createList();
1145 p = ziplistIndex(zl, -1);
1146 if (!ziplistGet(p, &entry, &elen, &value)) {
1147 printf("ERROR: Could not access index -1\n");
1148 return 1;
1149 }
1150 if (entry) {
10c12171 1151 if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite");
c03206fd
PN
1152 printf("\n");
1153 } else {
1154 printf("%lld\n", value);
1155 }
1156 printf("\n");
1157 }
1158
1159 printf("Get element at index -4 (first element):\n");
1160 {
1161 zl = createList();
1162 p = ziplistIndex(zl, -4);
1163 if (!ziplistGet(p, &entry, &elen, &value)) {
1164 printf("ERROR: Could not access index -4\n");
1165 return 1;
1166 }
1167 if (entry) {
10c12171 1168 if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite");
c03206fd
PN
1169 printf("\n");
1170 } else {
1171 printf("%lld\n", value);
1172 }
1173 printf("\n");
1174 }
1175
1176 printf("Get element at index -5 (reverse out of range):\n");
1177 {
1178 zl = createList();
1179 p = ziplistIndex(zl, -5);
1180 if (p == NULL) {
1181 printf("No entry\n");
1182 } else {
1183 printf("ERROR: Out of range index should return NULL, returned offset: %ld\n", p-zl);
1184 return 1;
1185 }
1186 printf("\n");
1187 }
1188
08253bf4
PN
1189 printf("Iterate list from 0 to end:\n");
1190 {
1191 zl = createList();
1192 p = ziplistIndex(zl, 0);
75d8978e 1193 while (ziplistGet(p, &entry, &elen, &value)) {
335d16bc 1194 printf("Entry: ");
75d8978e 1195 if (entry) {
10c12171 1196 if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite");
75d8978e
PN
1197 } else {
1198 printf("%lld", value);
1199 }
8632fb30 1200 p = ziplistNext(zl,p);
75d8978e 1201 printf("\n");
08253bf4
PN
1202 }
1203 printf("\n");
1204 }
1205
1206 printf("Iterate list from 1 to end:\n");
1207 {
1208 zl = createList();
1209 p = ziplistIndex(zl, 1);
75d8978e 1210 while (ziplistGet(p, &entry, &elen, &value)) {
335d16bc 1211 printf("Entry: ");
75d8978e 1212 if (entry) {
10c12171 1213 if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite");
75d8978e
PN
1214 } else {
1215 printf("%lld", value);
1216 }
8632fb30 1217 p = ziplistNext(zl,p);
75d8978e 1218 printf("\n");
08253bf4
PN
1219 }
1220 printf("\n");
1221 }
1222
1223 printf("Iterate list from 2 to end:\n");
1224 {
1225 zl = createList();
1226 p = ziplistIndex(zl, 2);
75d8978e 1227 while (ziplistGet(p, &entry, &elen, &value)) {
335d16bc 1228 printf("Entry: ");
75d8978e 1229 if (entry) {
10c12171 1230 if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite");
75d8978e
PN
1231 } else {
1232 printf("%lld", value);
1233 }
8632fb30 1234 p = ziplistNext(zl,p);
75d8978e 1235 printf("\n");
08253bf4
PN
1236 }
1237 printf("\n");
1238 }
1239
1240 printf("Iterate starting out of range:\n");
1241 {
1242 zl = createList();
75d8978e
PN
1243 p = ziplistIndex(zl, 4);
1244 if (!ziplistGet(p, &entry, &elen, &value)) {
08253bf4
PN
1245 printf("No entry\n");
1246 } else {
1247 printf("ERROR\n");
1248 }
779deb60
PN
1249 printf("\n");
1250 }
1251
0f3dfa87
PN
1252 printf("Iterate from back to front:\n");
1253 {
1254 zl = createList();
1255 p = ziplistIndex(zl, -1);
1256 while (ziplistGet(p, &entry, &elen, &value)) {
1257 printf("Entry: ");
1258 if (entry) {
10c12171 1259 if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite");
0f3dfa87
PN
1260 } else {
1261 printf("%lld", value);
1262 }
8632fb30 1263 p = ziplistPrev(zl,p);
0f3dfa87
PN
1264 printf("\n");
1265 }
1266 printf("\n");
1267 }
1268
1269 printf("Iterate from back to front, deleting all items:\n");
1270 {
1271 zl = createList();
1272 p = ziplistIndex(zl, -1);
1273 while (ziplistGet(p, &entry, &elen, &value)) {
1274 printf("Entry: ");
1275 if (entry) {
10c12171 1276 if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite");
0f3dfa87
PN
1277 } else {
1278 printf("%lld", value);
1279 }
8632fb30
PN
1280 zl = ziplistDelete(zl,&p);
1281 p = ziplistPrev(zl,p);
0f3dfa87
PN
1282 printf("\n");
1283 }
1284 printf("\n");
1285 }
1286
779deb60
PN
1287 printf("Delete inclusive range 0,0:\n");
1288 {
1289 zl = createList();
ba5b4bde 1290 zl = ziplistDeleteRange(zl, 0, 1);
779deb60
PN
1291 ziplistRepr(zl);
1292 }
1293
1294 printf("Delete inclusive range 0,1:\n");
1295 {
1296 zl = createList();
ba5b4bde 1297 zl = ziplistDeleteRange(zl, 0, 2);
779deb60
PN
1298 ziplistRepr(zl);
1299 }
1300
1301 printf("Delete inclusive range 1,2:\n");
1302 {
1303 zl = createList();
ba5b4bde 1304 zl = ziplistDeleteRange(zl, 1, 2);
779deb60
PN
1305 ziplistRepr(zl);
1306 }
1307
1308 printf("Delete with start index out of range:\n");
1309 {
1310 zl = createList();
ba5b4bde 1311 zl = ziplistDeleteRange(zl, 5, 1);
779deb60
PN
1312 ziplistRepr(zl);
1313 }
1314
1315 printf("Delete with num overflow:\n");
1316 {
1317 zl = createList();
ba5b4bde 1318 zl = ziplistDeleteRange(zl, 1, 5);
779deb60 1319 ziplistRepr(zl);
08253bf4
PN
1320 }
1321
0f10458c
PN
1322 printf("Delete foo while iterating:\n");
1323 {
1324 zl = createList();
b84186ff
PN
1325 p = ziplistIndex(zl,0);
1326 while (ziplistGet(p,&entry,&elen,&value)) {
1327 if (entry && strncmp("foo",(char*)entry,elen) == 0) {
0f10458c 1328 printf("Delete foo\n");
b84186ff 1329 zl = ziplistDelete(zl,&p);
0f10458c
PN
1330 } else {
1331 printf("Entry: ");
75d8978e 1332 if (entry) {
10c12171 1333 if (elen && fwrite(entry,elen,1,stdout) == 0)
1334 perror("fwrite");
75d8978e 1335 } else {
b84186ff 1336 printf("%lld",value);
75d8978e 1337 }
b84186ff 1338 p = ziplistNext(zl,p);
75d8978e 1339 printf("\n");
0f10458c
PN
1340 }
1341 }
1342 printf("\n");
1343 ziplistRepr(zl);
c09c2c3b
PN
1344 }
1345
b0d605c1
PN
1346 printf("Regression test for >255 byte strings:\n");
1347 {
1348 char v1[257],v2[257];
1349 memset(v1,'x',256);
1350 memset(v2,'y',256);
1351 zl = ziplistNew();
1352 zl = ziplistPush(zl,(unsigned char*)v1,strlen(v1),ZIPLIST_TAIL);
1353 zl = ziplistPush(zl,(unsigned char*)v2,strlen(v2),ZIPLIST_TAIL);
1354
1355 /* Pop values again and compare their value. */
1356 p = ziplistIndex(zl,0);
1357 assert(ziplistGet(p,&entry,&elen,&value));
306c6a02 1358 assert(strncmp(v1,(char*)entry,elen) == 0);
b0d605c1
PN
1359 p = ziplistIndex(zl,1);
1360 assert(ziplistGet(p,&entry,&elen,&value));
306c6a02 1361 assert(strncmp(v2,(char*)entry,elen) == 0);
b0d605c1
PN
1362 printf("SUCCESS\n\n");
1363 }
1364
89bf6f58
PN
1365 printf("Regression test deleting next to last entries:\n");
1366 {
1367 char v[3][257];
1368 zlentry e[3];
1369 int i;
1370
1371 for (i = 0; i < (sizeof(v)/sizeof(v[0])); i++) {
1372 memset(v[i], 'a' + i, sizeof(v[0]));
1373 }
1374
1375 v[0][256] = '\0';
1376 v[1][ 1] = '\0';
1377 v[2][256] = '\0';
1378
1379 zl = ziplistNew();
1380 for (i = 0; i < (sizeof(v)/sizeof(v[0])); i++) {
1381 zl = ziplistPush(zl, (unsigned char *) v[i], strlen(v[i]), ZIPLIST_TAIL);
1382 }
1383
1384 verify(zl, e);
1385
1386 assert(e[0].prevrawlensize == 1);
1387 assert(e[1].prevrawlensize == 5);
1388 assert(e[2].prevrawlensize == 1);
1389
1390 /* Deleting entry 1 will increase `prevrawlensize` for entry 2 */
1391 unsigned char *p = e[1].p;
1392 zl = ziplistDelete(zl, &p);
1393
1394 verify(zl, e);
1395
1396 assert(e[0].prevrawlensize == 1);
1397 assert(e[1].prevrawlensize == 5);
1398
1399 printf("SUCCESS\n\n");
1400 }
1401
dbaa41c6
PN
1402 printf("Create long list and check indices:\n");
1403 {
1404 zl = ziplistNew();
1405 char buf[32];
1406 int i,len;
1407 for (i = 0; i < 1000; i++) {
1408 len = sprintf(buf,"%d",i);
b84186ff 1409 zl = ziplistPush(zl,(unsigned char*)buf,len,ZIPLIST_TAIL);
dbaa41c6
PN
1410 }
1411 for (i = 0; i < 1000; i++) {
1412 p = ziplistIndex(zl,i);
1413 assert(ziplistGet(p,NULL,NULL,&value));
1414 assert(i == value);
1415
1416 p = ziplistIndex(zl,-i-1);
1417 assert(ziplistGet(p,NULL,NULL,&value));
1418 assert(999-i == value);
1419 }
1420 printf("SUCCESS\n\n");
1421 }
1422
c09c2c3b
PN
1423 printf("Compare strings with ziplist entries:\n");
1424 {
1425 zl = createList();
b84186ff
PN
1426 p = ziplistIndex(zl,0);
1427 if (!ziplistCompare(p,(unsigned char*)"hello",5)) {
dcb9cf4e 1428 printf("ERROR: not \"hello\"\n");
a24ba809 1429 return 1;
c09c2c3b 1430 }
b84186ff 1431 if (ziplistCompare(p,(unsigned char*)"hella",5)) {
dcb9cf4e 1432 printf("ERROR: \"hella\"\n");
a24ba809 1433 return 1;
c09c2c3b
PN
1434 }
1435
b84186ff
PN
1436 p = ziplistIndex(zl,3);
1437 if (!ziplistCompare(p,(unsigned char*)"1024",4)) {
dcb9cf4e 1438 printf("ERROR: not \"1024\"\n");
a24ba809 1439 return 1;
c09c2c3b 1440 }
b84186ff 1441 if (ziplistCompare(p,(unsigned char*)"1025",4)) {
dcb9cf4e 1442 printf("ERROR: \"1025\"\n");
a24ba809 1443 return 1;
c09c2c3b 1444 }
169d2ef1
PN
1445 printf("SUCCESS\n\n");
1446 }
1447
1448 printf("Stress with random payloads of different encoding:\n");
1449 {
306c6a02 1450 int i,j,len,where;
169d2ef1 1451 unsigned char *p;
306c6a02 1452 char buf[1024];
b7d3bf51 1453 int buflen;
306c6a02
PN
1454 list *ref;
1455 listNode *refnode;
1456
1457 /* Hold temp vars from ziplist */
1458 unsigned char *sstr;
1459 unsigned int slen;
1460 long long sval;
1461
306c6a02
PN
1462 for (i = 0; i < 20000; i++) {
1463 zl = ziplistNew();
1464 ref = listCreate();
1465 listSetFreeMethod(ref,sdsfree);
1466 len = rand() % 256;
1467
1468 /* Create lists */
1469 for (j = 0; j < len; j++) {
1470 where = (rand() & 1) ? ZIPLIST_HEAD : ZIPLIST_TAIL;
b7d3bf51
PN
1471 if (rand() % 2) {
1472 buflen = randstring(buf,1,sizeof(buf)-1);
1473 } else {
1474 switch(rand() % 3) {
1475 case 0:
1476 buflen = sprintf(buf,"%lld",(0LL + rand()) >> 20);
1477 break;
1478 case 1:
1479 buflen = sprintf(buf,"%lld",(0LL + rand()));
1480 break;
1481 case 2:
1482 buflen = sprintf(buf,"%lld",(0LL + rand()) << 20);
1483 break;
1484 default:
1485 assert(NULL);
1486 }
306c6a02
PN
1487 }
1488
1489 /* Add to ziplist */
b7d3bf51 1490 zl = ziplistPush(zl, (unsigned char*)buf, buflen, where);
169d2ef1 1491
306c6a02
PN
1492 /* Add to reference list */
1493 if (where == ZIPLIST_HEAD) {
b7d3bf51 1494 listAddNodeHead(ref,sdsnewlen(buf, buflen));
306c6a02 1495 } else if (where == ZIPLIST_TAIL) {
b7d3bf51 1496 listAddNodeTail(ref,sdsnewlen(buf, buflen));
306c6a02
PN
1497 } else {
1498 assert(NULL);
1499 }
169d2ef1
PN
1500 }
1501
306c6a02
PN
1502 assert(listLength(ref) == ziplistLen(zl));
1503 for (j = 0; j < len; j++) {
1504 /* Naive way to get elements, but similar to the stresser
1505 * executed from the Tcl test suite. */
1506 p = ziplistIndex(zl,j);
1507 refnode = listIndex(ref,j);
1508
1509 assert(ziplistGet(p,&sstr,&slen,&sval));
1510 if (sstr == NULL) {
b7d3bf51 1511 buflen = sprintf(buf,"%lld",sval);
306c6a02 1512 } else {
b7d3bf51
PN
1513 buflen = slen;
1514 memcpy(buf,sstr,buflen);
1515 buf[buflen] = '\0';
306c6a02 1516 }
b7d3bf51 1517 assert(memcmp(buf,listNodeValue(refnode),buflen) == 0);
306c6a02
PN
1518 }
1519 zfree(zl);
1520 listRelease(ref);
169d2ef1
PN
1521 }
1522 printf("SUCCESS\n\n");
0f10458c
PN
1523 }
1524
ffc15852
PN
1525 printf("Stress with variable ziplist size:\n");
1526 {
1527 stress(ZIPLIST_HEAD,100000,16384,256);
1528 stress(ZIPLIST_TAIL,100000,16384,256);
1529 }
1530
11ac6ff6
PN
1531 return 0;
1532}
ffc15852 1533
11ac6ff6 1534#endif