ziplist.c 28.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/* Memory layout of a ziplist, containing "foo", "bar", "quux":
 * <zlbytes><zllen><len>"foo"<len>"bar"<len>"quux"
 *
 * <zlbytes> is an unsigned integer to hold the number of bytes that
 * the ziplist occupies. This is stored to not have to traverse the ziplist
 * to know the new length when pushing.
 *
 * <zllen> is the number of items in the ziplist. When this value is
 * greater than 254, we need to traverse the entire list to know
 * how many items it holds.
 *
 * <len> is the number of bytes occupied by a single entry. When this
 * number is greater than 253, the length will occupy 5 bytes, where
 * the extra bytes contain an unsigned integer to hold the length.
 */

#include <stdio.h>
18
#include <stdlib.h>
19
#include <string.h>
20
#include <stdint.h>
21
#include <assert.h>
22
#include <limits.h>
23 24 25
#include "zmalloc.h"
#include "ziplist.h"

26 27 28 29 30
/* Important note: the ZIP_END value is used to depict the end of the
 * ziplist structure. When a pointer contains an entry, the first couple
 * of bytes contain the encoded length of the previous entry. This length
 * is encoded as ZIP_ENC_RAW length, so the first two bits will contain 00
 * and the byte will therefore never have a value of 255. */
31
#define ZIP_END 255
32
#define ZIP_BIGLEN 254
33 34 35

/* Entry encoding */
#define ZIP_ENC_RAW     0
36 37 38
#define ZIP_ENC_INT16   1
#define ZIP_ENC_INT32   2
#define ZIP_ENC_INT64   3
39 40 41 42 43 44 45 46
#define ZIP_ENCODING(p) ((p)[0] >> 6)

/* Length encoding for raw entries */
#define ZIP_LEN_INLINE  0
#define ZIP_LEN_UINT16  1
#define ZIP_LEN_UINT32  2

/* Utility macros */
47 48 49 50 51 52 53 54 55 56
#define ZIPLIST_BYTES(zl)       (*((uint32_t*)(zl)))
#define ZIPLIST_TAIL_OFFSET(zl) (*((uint32_t*)((zl)+sizeof(uint32_t))))
#define ZIPLIST_LENGTH(zl)      (*((uint16_t*)((zl)+sizeof(uint32_t)*2)))
#define ZIPLIST_HEADER_SIZE     (sizeof(uint32_t)*2+sizeof(uint16_t))
#define ZIPLIST_ENTRY_HEAD(zl)  ((zl)+ZIPLIST_HEADER_SIZE)
#define ZIPLIST_ENTRY_TAIL(zl)  ((zl)+ZIPLIST_TAIL_OFFSET(zl))
#define ZIPLIST_ENTRY_END(zl)   ((zl)+ZIPLIST_BYTES(zl)-1)

/* We know a positive increment can only be 1 because entries can only be
 * pushed one at a time. */
57
#define ZIPLIST_INCR_LENGTH(zl,incr) { \
58
    if (ZIPLIST_LENGTH(zl) < UINT16_MAX) ZIPLIST_LENGTH(zl)+=incr; }
59

60 61 62 63 64
typedef struct zlentry {
    unsigned int prevrawlensize, prevrawlen;
    unsigned int lensize, len;
    unsigned int headersize;
    unsigned char encoding;
65
    unsigned char *p;
66 67
} zlentry;

68
/* Return bytes needed to store integer encoded by 'encoding' */
69
static unsigned int zipEncodingSize(unsigned char encoding) {
70 71 72 73 74 75
    if (encoding == ZIP_ENC_INT16) {
        return sizeof(int16_t);
    } else if (encoding == ZIP_ENC_INT32) {
        return sizeof(int32_t);
    } else if (encoding == ZIP_ENC_INT64) {
        return sizeof(int64_t);
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
    }
    assert(NULL);
}

/* Decode the encoded length pointed by 'p'. If a pointer to 'lensize' is
 * provided, it is set to the number of bytes required to encode the length. */
static unsigned int zipDecodeLength(unsigned char *p, unsigned int *lensize) {
    unsigned char encoding = ZIP_ENCODING(p), lenenc;
    unsigned int len;

    if (encoding == ZIP_ENC_RAW) {
        lenenc = (p[0] >> 4) & 0x3;
        if (lenenc == ZIP_LEN_INLINE) {
            len = p[0] & 0xf;
            if (lensize) *lensize = 1;
        } else if (lenenc == ZIP_LEN_UINT16) {
            len = p[1] | (p[2] << 8);
            if (lensize) *lensize = 3;
        } else {
            len = p[1] | (p[2] << 8) | (p[3] << 16) | (p[4] << 24);
            if (lensize) *lensize = 5;
        }
    } else {
        len = zipEncodingSize(encoding);
        if (lensize) *lensize = 1;
    }
    return len;
}

/* Encode the length 'l' writing it in 'p'. If p is NULL it just returns
 * the amount of bytes required to encode such a length. */
static unsigned int zipEncodeLength(unsigned char *p, char encoding, unsigned int rawlen) {
    unsigned char len = 1, lenenc, buf[5];
    if (encoding == ZIP_ENC_RAW) {
        if (rawlen <= 0xf) {
            if (!p) return len;
            lenenc = ZIP_LEN_INLINE;
            buf[0] = rawlen;
        } else if (rawlen <= 0xffff) {
            len += 2;
            if (!p) return len;
            lenenc = ZIP_LEN_UINT16;
            buf[1] = (rawlen     ) & 0xff;
            buf[2] = (rawlen >> 8) & 0xff;
        } else {
            len += 4;
            if (!p) return len;
            lenenc = ZIP_LEN_UINT32;
            buf[1] = (rawlen      ) & 0xff;
            buf[2] = (rawlen >>  8) & 0xff;
            buf[3] = (rawlen >> 16) & 0xff;
            buf[4] = (rawlen >> 24) & 0xff;
        }
        buf[0] = (lenenc << 4) | (buf[0] & 0xf);
    }
    if (!p) return len;

    /* Apparently we need to store the length in 'p' */
    buf[0] = (encoding << 6) | (buf[0] & 0x3f);
    memcpy(p,buf,len);
    return len;
}

139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
/* Decode the length of the previous element stored at "p". */
static unsigned int zipPrevDecodeLength(unsigned char *p, unsigned int *lensize) {
    unsigned int len = *p;
    if (len < ZIP_BIGLEN) {
        if (lensize) *lensize = 1;
    } else {
        if (lensize) *lensize = 1+sizeof(len);
        memcpy(&len,p+1,sizeof(len));
    }
    return len;
}

/* Encode the length of the previous entry and write it to "p". Return the
 * number of bytes needed to encode this length if "p" is NULL. */
static unsigned int zipPrevEncodeLength(unsigned char *p, unsigned int len) {
    if (p == NULL) {
        return (len < ZIP_BIGLEN) ? 1 : sizeof(len)+1;
    } else {
        if (len < ZIP_BIGLEN) {
            p[0] = len;
            return 1;
        } else {
            p[0] = ZIP_BIGLEN;
            memcpy(p+1,&len,sizeof(len));
            return 1+sizeof(len);
        }
    }
}

168 169 170 171
/* Return the difference in number of bytes needed to store the new length
 * "len" on the entry pointed to by "p". */
static int zipPrevLenByteDiff(unsigned char *p, unsigned int len) {
    unsigned int prevlensize;
172 173
    zipPrevDecodeLength(p,&prevlensize);
    return zipPrevEncodeLength(NULL,len)-prevlensize;
174 175
}

176 177 178
/* Check if string pointed to by 'entry' can be encoded as an integer.
 * Stores the integer value in 'v' and its encoding in 'encoding'.
 * Warning: this function requires a NULL-terminated string! */
179
static int zipTryEncoding(unsigned char *entry, long long *v, unsigned char *encoding) {
180 181 182 183
    long long value;
    char *eptr;

    if (entry[0] == '-' || (entry[0] >= '0' && entry[0] <= '9')) {
184
        value = strtoll((char*)entry,&eptr,10);
185
        if (eptr[0] != '\0') return 0;
186 187 188 189
        if (value >= INT16_MIN && value <= INT16_MAX) {
            *encoding = ZIP_ENC_INT16;
        } else if (value >= INT32_MIN && value <= INT32_MAX) {
            *encoding = ZIP_ENC_INT32;
190
        } else {
191
            *encoding = ZIP_ENC_INT64;
192 193 194 195 196 197 198 199
        }
        *v = value;
        return 1;
    }
    return 0;
}

/* Store integer 'value' at 'p', encoded as 'encoding' */
200 201 202 203 204 205 206 207 208 209 210 211 212
static void zipSaveInteger(unsigned char *p, int64_t value, unsigned char encoding) {
    int16_t i16;
    int32_t i32;
    int64_t i64;
    if (encoding == ZIP_ENC_INT16) {
        i16 = value;
        memcpy(p,&i16,sizeof(i16));
    } else if (encoding == ZIP_ENC_INT32) {
        i32 = value;
        memcpy(p,&i32,sizeof(i32));
    } else if (encoding == ZIP_ENC_INT64) {
        i64 = value;
        memcpy(p,&i64,sizeof(i64));
213 214 215 216 217 218
    } else {
        assert(NULL);
    }
}

/* Read integer encoded as 'encoding' from 'p' */
219 220 221 222 223 224 225 226 227 228 229 230 231
static int64_t zipLoadInteger(unsigned char *p, unsigned char encoding) {
    int16_t i16;
    int32_t i32;
    int64_t i64, ret;
    if (encoding == ZIP_ENC_INT16) {
        memcpy(&i16,p,sizeof(i16));
        ret = i16;
    } else if (encoding == ZIP_ENC_INT32) {
        memcpy(&i32,p,sizeof(i32));
        ret = i32;
    } else if (encoding == ZIP_ENC_INT64) {
        memcpy(&i64,p,sizeof(i64));
        ret = i64;
232 233 234 235 236 237
    } else {
        assert(NULL);
    }
    return ret;
}

238 239 240
/* Return a struct with all information about an entry. */
static zlentry zipEntry(unsigned char *p) {
    zlentry e;
241
    e.prevrawlen = zipPrevDecodeLength(p,&e.prevrawlensize);
242 243 244
    e.len = zipDecodeLength(p+e.prevrawlensize,&e.lensize);
    e.headersize = e.prevrawlensize+e.lensize;
    e.encoding = ZIP_ENCODING(p+e.prevrawlensize);
245
    e.p = p;
246 247 248
    return e;
}

249
/* Return the total number of bytes used by the entry at "p". */
250
static unsigned int zipRawEntryLength(unsigned char *p) {
251 252
    zlentry e = zipEntry(p);
    return e.headersize + e.len;
253 254
}

255 256 257 258 259
/* Create a new empty ziplist. */
unsigned char *ziplistNew(void) {
    unsigned int bytes = ZIPLIST_HEADER_SIZE+1;
    unsigned char *zl = zmalloc(bytes);
    ZIPLIST_BYTES(zl) = bytes;
260
    ZIPLIST_TAIL_OFFSET(zl) = ZIPLIST_HEADER_SIZE;
261 262 263 264 265
    ZIPLIST_LENGTH(zl) = 0;
    zl[bytes-1] = ZIP_END;
    return zl;
}

266
/* Resize the ziplist. */
267
static unsigned char *ziplistResize(unsigned char *zl, unsigned int len) {
268
    zl = zrealloc(zl,len);
269 270 271 272 273
    ZIPLIST_BYTES(zl) = len;
    zl[len-1] = ZIP_END;
    return zl;
}

274
/* Delete "num" entries, starting at "p". Returns pointer to the ziplist. */
275
static unsigned char *__ziplistDelete(unsigned char *zl, unsigned char *p, unsigned int num) {
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
    unsigned int i, totlen, deleted = 0;
    int nextdiff = 0;
    zlentry first = zipEntry(p);
    for (i = 0; p[0] != ZIP_END && i < num; i++) {
        p += zipRawEntryLength(p);
        deleted++;
    }

    totlen = p-first.p;
    if (totlen > 0) {
        if (p[0] != ZIP_END) {
            /* Tricky: storing the prevlen in this entry might reduce or
             * increase the number of bytes needed, compared to the current
             * prevlen. Note that we can always store this length because
             * it was previously stored by an entry that is being deleted. */
            nextdiff = zipPrevLenByteDiff(p,first.prevrawlen);
292
            zipPrevEncodeLength(p-nextdiff,first.prevrawlen);
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308

            /* Update offset for tail */
            ZIPLIST_TAIL_OFFSET(zl) -= totlen+nextdiff;

            /* Move tail to the front of the ziplist */
            memmove(first.p,p-nextdiff,ZIPLIST_BYTES(zl)-(p-zl)-1+nextdiff);
        } else {
            /* The entire tail was deleted. No need to move memory. */
            ZIPLIST_TAIL_OFFSET(zl) = (first.p-zl)-first.prevrawlen;
        }

        /* Resize and update length */
        zl = ziplistResize(zl, ZIPLIST_BYTES(zl)-totlen+nextdiff);
        ZIPLIST_INCR_LENGTH(zl,-deleted);
    }
    return zl;
309 310
}

311
/* Insert item at "p". */
312
static unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {
313 314 315
    unsigned int curlen = ZIPLIST_BYTES(zl), reqlen, prevlen = 0;
    unsigned int offset, nextdiff = 0;
    unsigned char *tail;
316
    unsigned char encoding = ZIP_ENC_RAW;
317
    long long value;
318
    zlentry entry;
319

320 321 322 323
    /* Find out prevlen for the entry that is inserted. */
    if (p[0] != ZIP_END) {
        entry = zipEntry(p);
        prevlen = entry.prevrawlen;
324
    } else {
325
        tail = ZIPLIST_ENTRY_TAIL(zl);
326 327 328
        if (tail[0] != ZIP_END) {
            prevlen = zipRawEntryLength(tail);
        }
329 330
    }

331
    /* See if the entry can be encoded */
332
    if (zipTryEncoding(s,&value,&encoding)) {
333 334
        reqlen = zipEncodingSize(encoding);
    } else {
335
        reqlen = slen;
336
    }
337 338 339

    /* We need space for both the length of the previous entry and
     * the length of the payload. */
340
    reqlen += zipPrevEncodeLength(NULL,prevlen);
341 342 343 344 345
    reqlen += zipEncodeLength(NULL,encoding,slen);

    /* When the insert position is not equal to the tail, we need to
     * make sure that the next entry can hold this entry's length in
     * its prevlen field. */
P
Pieter Noordhuis 已提交
346
    nextdiff = (p[0] != ZIP_END) ? zipPrevLenByteDiff(p,reqlen) : 0;
347 348 349 350 351 352 353 354 355 356 357

    /* Store offset because a realloc may change the address of zl. */
    offset = p-zl;
    zl = ziplistResize(zl,curlen+reqlen+nextdiff);
    p = zl+offset;

    /* Apply memory move when necessary and update tail offset. */
    if (p[0] != ZIP_END) {
        /* Subtract one because of the ZIP_END bytes */
        memmove(p+reqlen,p-nextdiff,curlen-offset-1+nextdiff);
        /* Encode this entry's raw length in the next entry. */
358
        zipPrevEncodeLength(p+reqlen,reqlen);
359 360
        /* Update offset for tail */
        ZIPLIST_TAIL_OFFSET(zl) += reqlen+nextdiff;
361
    } else {
362 363
        /* This element will be the new tail. */
        ZIPLIST_TAIL_OFFSET(zl) = p-zl;
364 365
    }

366
    /* Write the entry */
367
    p += zipPrevEncodeLength(p,prevlen);
368
    p += zipEncodeLength(p,encoding,slen);
369 370 371
    if (encoding != ZIP_ENC_RAW) {
        zipSaveInteger(p,value,encoding);
    } else {
372
        memcpy(p,s,slen);
373
    }
374
    ZIPLIST_INCR_LENGTH(zl,1);
375 376 377
    return zl;
}

378
unsigned char *ziplistPush(unsigned char *zl, unsigned char *s, unsigned int slen, int where) {
379
    unsigned char *p;
380
    p = (where == ZIPLIST_HEAD) ? ZIPLIST_ENTRY_HEAD(zl) : ZIPLIST_ENTRY_END(zl);
381 382 383
    return __ziplistInsert(zl,p,s,slen);
}

384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
/* Returns an offset to use for iterating with ziplistNext. When the given
 * index is negative, the list is traversed back to front. When the list
 * doesn't contain an element at the provided index, NULL is returned. */
unsigned char *ziplistIndex(unsigned char *zl, int index) {
    unsigned char *p;
    zlentry entry;
    if (index < 0) {
        index = (-index)-1;
        p = ZIPLIST_ENTRY_TAIL(zl);
        if (p[0] != ZIP_END) {
            entry = zipEntry(p);
            while (entry.prevrawlen > 0 && index--) {
                p -= entry.prevrawlen;
                entry = zipEntry(p);
            }
        }
    } else {
        p = ZIPLIST_ENTRY_HEAD(zl);
        while (p[0] != ZIP_END && index--) {
            p += zipRawEntryLength(p);
        }
405
    }
P
Pieter Noordhuis 已提交
406
    return (p[0] == ZIP_END || index > 0) ? NULL : p;
407 408
}

409
/* Return pointer to next entry in ziplist. */
410 411
unsigned char *ziplistNext(unsigned char *zl, unsigned char *p) {
    ((void) zl);
412 413 414 415 416 417 418 419 420 421

    /* "p" could be equal to ZIP_END, caused by ziplistDelete,
     * and we should return NULL. Otherwise, we should return NULL
     * when the *next* element is ZIP_END (there is no next entry). */
    if (p[0] == ZIP_END) {
        return NULL;
    } else {
        p = p+zipRawEntryLength(p);
        return (p[0] == ZIP_END) ? NULL : p;
    }
422 423
}

424
/* Return pointer to previous entry in ziplist. */
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
unsigned char *ziplistPrev(unsigned char *zl, unsigned char *p) {
    zlentry entry;

    /* Iterating backwards from ZIP_END should return the tail. When "p" is
     * equal to the first element of the list, we're already at the head,
     * and should return NULL. */
    if (p[0] == ZIP_END) {
        p = ZIPLIST_ENTRY_TAIL(zl);
        return (p[0] == ZIP_END) ? NULL : p;
    } else if (p == ZIPLIST_ENTRY_HEAD(zl)) {
        return NULL;
    } else {
        entry = zipEntry(p);
        return p-entry.prevrawlen;
    }
440 441
}

442 443 444 445
/* Get entry pointer to by 'p' and store in either 'e' or 'v' depending
 * on the encoding of the entry. 'e' is always set to NULL to be able
 * to find out whether the string pointer or the integer value was set.
 * Return 0 if 'p' points to the end of the zipmap, 1 otherwise. */
446
unsigned int ziplistGet(unsigned char *p, unsigned char **sstr, unsigned int *slen, long long *sval) {
447
    zlentry entry;
448
    if (p == NULL || p[0] == ZIP_END) return 0;
449
    if (sstr) *sstr = NULL;
450

451 452
    entry = zipEntry(p);
    if (entry.encoding == ZIP_ENC_RAW) {
453 454
        if (sstr) {
            *slen = entry.len;
455
            *sstr = p+entry.headersize;
456 457
        }
    } else {
458 459
        if (sval) {
            *sval = zipLoadInteger(p+entry.headersize,entry.encoding);
460
        }
461
    }
462
    return 1;
463 464
}

465
/* Insert an entry at "p". */
466
unsigned char *ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {
467
    return __ziplistInsert(zl,p,s,slen);
468 469
}

470 471 472
/* Delete a single entry from the ziplist, pointed to by *p.
 * Also update *p in place, to be able to iterate over the
 * ziplist, while deleting entries. */
473
unsigned char *ziplistDelete(unsigned char *zl, unsigned char **p) {
474 475
    unsigned int offset = *p-zl;
    zl = __ziplistDelete(zl,*p,1);
476

477
    /* Store pointer to current element in p, because ziplistDelete will
478 479 480
     * do a realloc which might result in a different "zl"-pointer.
     * When the delete direction is back to front, we might delete the last
     * entry and end up with "p" pointing to ZIP_END, so check this. */
481
    *p = zl+offset;
482 483 484
    return zl;
}

485 486 487 488 489 490
/* Delete a range of entries from the ziplist. */
unsigned char *ziplistDeleteRange(unsigned char *zl, unsigned int index, unsigned int num) {
    unsigned char *p = ziplistIndex(zl,index);
    return (p == NULL) ? zl : __ziplistDelete(zl,p,num);
}

491
/* Compare entry pointer to by 'p' with 'entry'. Return 1 if equal. */
492
unsigned int ziplistCompare(unsigned char *p, unsigned char *sstr, unsigned int slen) {
493
    zlentry entry;
494 495
    unsigned char sencoding;
    long long zval, sval;
P
Pieter Noordhuis 已提交
496
    if (p[0] == ZIP_END) return 0;
497

498 499
    entry = zipEntry(p);
    if (entry.encoding == ZIP_ENC_RAW) {
500
        /* Raw compare */
501
        if (entry.len == slen) {
502
            return memcmp(p+entry.headersize,sstr,slen) == 0;
503 504 505
        } else {
            return 0;
        }
506
    } else {
507
        /* Try to compare encoded values */
508
        if (zipTryEncoding(sstr,&sval,&sencoding)) {
509
            if (entry.encoding == sencoding) {
510 511
                zval = zipLoadInteger(p+entry.headersize,entry.encoding);
                return zval == sval;
512
            }
513
        }
514
    }
515
    return 0;
516 517
}

518 519 520
/* Return length of ziplist. */
unsigned int ziplistLen(unsigned char *zl) {
    unsigned int len = 0;
521
    if (ZIPLIST_LENGTH(zl) < UINT16_MAX) {
522 523 524 525 526 527 528 529 530
        len = ZIPLIST_LENGTH(zl);
    } else {
        unsigned char *p = zl+ZIPLIST_HEADER_SIZE;
        while (*p != ZIP_END) {
            p += zipRawEntryLength(p);
            len++;
        }

        /* Re-store length if small enough */
531
        if (len < UINT16_MAX) ZIPLIST_LENGTH(zl) = len;
532 533 534 535
    }
    return len;
}

536 537 538 539 540
/* Return size in bytes of ziplist. */
unsigned int ziplistSize(unsigned char *zl) {
    return ZIPLIST_BYTES(zl);
}

541
void ziplistRepr(unsigned char *zl) {
542 543
    unsigned char *p;
    zlentry entry;
544

545
    printf("{total bytes %d} {length %u}\n",ZIPLIST_BYTES(zl), ZIPLIST_LENGTH(zl));
546
    p = ZIPLIST_ENTRY_HEAD(zl);
547
    while(*p != ZIP_END) {
548 549 550 551 552
        entry = zipEntry(p);
        printf("{offset %ld, header %u, payload %u} ",p-zl,entry.headersize,entry.len);
        p += entry.headersize;
        if (entry.encoding == ZIP_ENC_RAW) {
            fwrite(p,entry.len,1,stdout);
553
        } else {
554
            printf("%lld", zipLoadInteger(p,entry.encoding));
555
        }
556
        printf("\n");
557
        p += entry.len;
558 559 560 561 562
    }
    printf("{end}\n\n");
}

#ifdef ZIPLIST_TEST_MAIN
563
#include <sys/time.h>
564

565 566
unsigned char *createList() {
    unsigned char *zl = ziplistNew();
567 568 569 570
    zl = ziplistPush(zl, (unsigned char*)"foo", 3, ZIPLIST_TAIL);
    zl = ziplistPush(zl, (unsigned char*)"quux", 4, ZIPLIST_TAIL);
    zl = ziplistPush(zl, (unsigned char*)"hello", 5, ZIPLIST_HEAD);
    zl = ziplistPush(zl, (unsigned char*)"1024", 4, ZIPLIST_TAIL);
571 572 573
    return zl;
}

574 575 576 577 578
unsigned char *createIntList() {
    unsigned char *zl = ziplistNew();
    char buf[32];

    sprintf(buf, "100");
579
    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);
580
    sprintf(buf, "128000");
581
    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);
582
    sprintf(buf, "-100");
583
    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_HEAD);
584
    sprintf(buf, "4294967296");
585
    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_HEAD);
586
    sprintf(buf, "non integer");
587
    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);
588
    sprintf(buf, "much much longer non integer");
589
    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);
590 591 592
    return zl;
}

593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621
long long usec(void) {
    struct timeval tv;
    gettimeofday(&tv,NULL);
    return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;
}

void stress(int pos, int num, int maxsize, int dnum) {
    int i,j,k;
    unsigned char *zl;
    char posstr[2][5] = { "HEAD", "TAIL" };
    long long start;
    for (i = 0; i < maxsize; i+=dnum) {
        zl = ziplistNew();
        for (j = 0; j < i; j++) {
            zl = ziplistPush(zl,(unsigned char*)"quux",4,ZIPLIST_TAIL);
        }

        /* Do num times a push+pop from pos */
        start = usec();
        for (k = 0; k < num; k++) {
            zl = ziplistPush(zl,(unsigned char*)"quux",4,pos);
            zl = ziplistDeleteRange(zl,0,1);
        }
        printf("List size: %8d, bytes: %8d, %dx push+pop (%s): %6lld usec\n",
            i,ZIPLIST_BYTES(zl),num,posstr[pos],usec()-start);
        zfree(zl);
    }
}

622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646
void pop(unsigned char *zl, int where) {
    unsigned char *p, *vstr;
    unsigned int vlen;
    long long vlong;

    p = ziplistIndex(zl,where == ZIPLIST_HEAD ? 0 : -1);
    if (ziplistGet(p,&vstr,&vlen,&vlong)) {
        if (where == ZIPLIST_HEAD)
            printf("Pop head: ");
        else
            printf("Pop tail: ");

        if (vstr)
            fwrite(vstr,vlen,1,stdout);
        else
            printf("%lld", vlong);

        printf("\n");
        ziplistDeleteRange(zl,-1,1);
    } else {
        printf("ERROR: Could not pop\n");
        exit(1);
    }
}

647
int main(int argc, char **argv) {
P
Pieter Noordhuis 已提交
648
    unsigned char *zl, *p;
649
    unsigned char *entry;
650
    unsigned int elen;
651
    long long value;
652

653 654 655
    zl = createIntList();
    ziplistRepr(zl);

656
    zl = createList();
657 658
    ziplistRepr(zl);

659
    pop(zl,ZIPLIST_TAIL);
660 661
    ziplistRepr(zl);

662
    pop(zl,ZIPLIST_HEAD);
663 664
    ziplistRepr(zl);

665
    pop(zl,ZIPLIST_TAIL);
666 667
    ziplistRepr(zl);

668
    pop(zl,ZIPLIST_TAIL);
669 670
    ziplistRepr(zl);

671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747
    printf("Get element at index 3:\n");
    {
        zl = createList();
        p = ziplistIndex(zl, 3);
        if (!ziplistGet(p, &entry, &elen, &value)) {
            printf("ERROR: Could not access index 3\n");
            return 1;
        }
        if (entry) {
            fwrite(entry,elen,1,stdout);
            printf("\n");
        } else {
            printf("%lld\n", value);
        }
        printf("\n");
    }

    printf("Get element at index 4 (out of range):\n");
    {
        zl = createList();
        p = ziplistIndex(zl, 4);
        if (p == NULL) {
            printf("No entry\n");
        } else {
            printf("ERROR: Out of range index should return NULL, returned offset: %ld\n", p-zl);
            return 1;
        }
        printf("\n");
    }

    printf("Get element at index -1 (last element):\n");
    {
        zl = createList();
        p = ziplistIndex(zl, -1);
        if (!ziplistGet(p, &entry, &elen, &value)) {
            printf("ERROR: Could not access index -1\n");
            return 1;
        }
        if (entry) {
            fwrite(entry,elen,1,stdout);
            printf("\n");
        } else {
            printf("%lld\n", value);
        }
        printf("\n");
    }

    printf("Get element at index -4 (first element):\n");
    {
        zl = createList();
        p = ziplistIndex(zl, -4);
        if (!ziplistGet(p, &entry, &elen, &value)) {
            printf("ERROR: Could not access index -4\n");
            return 1;
        }
        if (entry) {
            fwrite(entry,elen,1,stdout);
            printf("\n");
        } else {
            printf("%lld\n", value);
        }
        printf("\n");
    }

    printf("Get element at index -5 (reverse out of range):\n");
    {
        zl = createList();
        p = ziplistIndex(zl, -5);
        if (p == NULL) {
            printf("No entry\n");
        } else {
            printf("ERROR: Out of range index should return NULL, returned offset: %ld\n", p-zl);
            return 1;
        }
        printf("\n");
    }

748 749 750 751
    printf("Iterate list from 0 to end:\n");
    {
        zl = createList();
        p = ziplistIndex(zl, 0);
752
        while (ziplistGet(p, &entry, &elen, &value)) {
753
            printf("Entry: ");
754 755 756 757 758
            if (entry) {
                fwrite(entry,elen,1,stdout);
            } else {
                printf("%lld", value);
            }
759
            p = ziplistNext(zl,p);
760
            printf("\n");
761 762 763 764 765 766 767 768
        }
        printf("\n");
    }

    printf("Iterate list from 1 to end:\n");
    {
        zl = createList();
        p = ziplistIndex(zl, 1);
769
        while (ziplistGet(p, &entry, &elen, &value)) {
770
            printf("Entry: ");
771 772 773 774 775
            if (entry) {
                fwrite(entry,elen,1,stdout);
            } else {
                printf("%lld", value);
            }
776
            p = ziplistNext(zl,p);
777
            printf("\n");
778 779 780 781 782 783 784 785
        }
        printf("\n");
    }

    printf("Iterate list from 2 to end:\n");
    {
        zl = createList();
        p = ziplistIndex(zl, 2);
786
        while (ziplistGet(p, &entry, &elen, &value)) {
787
            printf("Entry: ");
788 789 790 791 792
            if (entry) {
                fwrite(entry,elen,1,stdout);
            } else {
                printf("%lld", value);
            }
793
            p = ziplistNext(zl,p);
794
            printf("\n");
795 796 797 798 799 800 801
        }
        printf("\n");
    }

    printf("Iterate starting out of range:\n");
    {
        zl = createList();
802 803
        p = ziplistIndex(zl, 4);
        if (!ziplistGet(p, &entry, &elen, &value)) {
804 805 806 807
            printf("No entry\n");
        } else {
            printf("ERROR\n");
        }
808 809 810
        printf("\n");
    }

811 812 813 814 815 816 817 818 819 820 821
    printf("Iterate from back to front:\n");
    {
        zl = createList();
        p = ziplistIndex(zl, -1);
        while (ziplistGet(p, &entry, &elen, &value)) {
            printf("Entry: ");
            if (entry) {
                fwrite(entry,elen,1,stdout);
            } else {
                printf("%lld", value);
            }
822
            p = ziplistPrev(zl,p);
823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838
            printf("\n");
        }
        printf("\n");
    }

    printf("Iterate from back to front, deleting all items:\n");
    {
        zl = createList();
        p = ziplistIndex(zl, -1);
        while (ziplistGet(p, &entry, &elen, &value)) {
            printf("Entry: ");
            if (entry) {
                fwrite(entry,elen,1,stdout);
            } else {
                printf("%lld", value);
            }
839 840
            zl = ziplistDelete(zl,&p);
            p = ziplistPrev(zl,p);
841 842 843 844 845
            printf("\n");
        }
        printf("\n");
    }

846 847 848
    printf("Delete inclusive range 0,0:\n");
    {
        zl = createList();
849
        zl = ziplistDeleteRange(zl, 0, 1);
850 851 852 853 854 855
        ziplistRepr(zl);
    }

    printf("Delete inclusive range 0,1:\n");
    {
        zl = createList();
856
        zl = ziplistDeleteRange(zl, 0, 2);
857 858 859 860 861 862
        ziplistRepr(zl);
    }

    printf("Delete inclusive range 1,2:\n");
    {
        zl = createList();
863
        zl = ziplistDeleteRange(zl, 1, 2);
864 865 866 867 868 869
        ziplistRepr(zl);
    }

    printf("Delete with start index out of range:\n");
    {
        zl = createList();
870
        zl = ziplistDeleteRange(zl, 5, 1);
871 872 873 874 875 876
        ziplistRepr(zl);
    }

    printf("Delete with num overflow:\n");
    {
        zl = createList();
877
        zl = ziplistDeleteRange(zl, 1, 5);
878
        ziplistRepr(zl);
879 880
    }

881 882 883
    printf("Delete foo while iterating:\n");
    {
        zl = createList();
884 885 886
        p = ziplistIndex(zl,0);
        while (ziplistGet(p,&entry,&elen,&value)) {
            if (entry && strncmp("foo",(char*)entry,elen) == 0) {
887
                printf("Delete foo\n");
888
                zl = ziplistDelete(zl,&p);
889 890
            } else {
                printf("Entry: ");
891 892 893
                if (entry) {
                    fwrite(entry,elen,1,stdout);
                } else {
894
                    printf("%lld",value);
895
                }
896
                p = ziplistNext(zl,p);
897
                printf("\n");
898 899 900 901
            }
        }
        printf("\n");
        ziplistRepr(zl);
902 903
    }

904 905 906 907 908 909 910
    printf("Create long list and check indices:\n");
    {
        zl = ziplistNew();
        char buf[32];
        int i,len;
        for (i = 0; i < 1000; i++) {
            len = sprintf(buf,"%d",i);
911
            zl = ziplistPush(zl,(unsigned char*)buf,len,ZIPLIST_TAIL);
912 913 914 915 916 917 918 919 920 921 922 923 924
        }
        for (i = 0; i < 1000; i++) {
            p = ziplistIndex(zl,i);
            assert(ziplistGet(p,NULL,NULL,&value));
            assert(i == value);

            p = ziplistIndex(zl,-i-1);
            assert(ziplistGet(p,NULL,NULL,&value));
            assert(999-i == value);
        }
        printf("SUCCESS\n\n");
    }

925 926 927
    printf("Compare strings with ziplist entries:\n");
    {
        zl = createList();
928 929
        p = ziplistIndex(zl,0);
        if (!ziplistCompare(p,(unsigned char*)"hello",5)) {
930
            printf("ERROR: not \"hello\"\n");
P
Pieter Noordhuis 已提交
931
            return 1;
932
        }
933
        if (ziplistCompare(p,(unsigned char*)"hella",5)) {
934
            printf("ERROR: \"hella\"\n");
P
Pieter Noordhuis 已提交
935
            return 1;
936 937
        }

938 939
        p = ziplistIndex(zl,3);
        if (!ziplistCompare(p,(unsigned char*)"1024",4)) {
940
            printf("ERROR: not \"1024\"\n");
P
Pieter Noordhuis 已提交
941
            return 1;
942
        }
943
        if (ziplistCompare(p,(unsigned char*)"1025",4)) {
944
            printf("ERROR: \"1025\"\n");
P
Pieter Noordhuis 已提交
945
            return 1;
946 947
        }
        printf("SUCCESS\n");
948 949
    }

950 951 952 953 954 955
    printf("Stress with variable ziplist size:\n");
    {
        stress(ZIPLIST_HEAD,100000,16384,256);
        stress(ZIPLIST_TAIL,100000,16384,256);
    }

956 957
    return 0;
}
958

959
#endif