ziplist.c 21.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 20
#include <string.h>
#include <assert.h>
21
#include <limits.h>
22 23 24 25
#include "zmalloc.h"
#include "sds.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 36 37 38 39 40 41 42 43 44 45 46

/* Entry encoding */
#define ZIP_ENC_RAW     0
#define ZIP_ENC_SHORT   1
#define ZIP_ENC_INT     2
#define ZIP_ENC_LLONG   3
#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
#define ZIPLIST_BYTES(zl) (*((unsigned int*)(zl)))
48 49 50
#define ZIPLIST_TAIL_OFFSET(zl) (*((zl)+sizeof(unsigned int)))
#define ZIPLIST_LENGTH(zl) (*((zl)+2*sizeof(unsigned int)))
#define ZIPLIST_HEADER_SIZE (2*sizeof(unsigned int)+1)
51
#define ZIPLIST_INCR_LENGTH(zl,incr) { \
52
    if (ZIPLIST_LENGTH(zl) < ZIP_BIGLEN) ZIPLIST_LENGTH(zl)+=incr; }
53

54 55 56 57 58 59 60
typedef struct zlentry {
    unsigned int prevrawlensize, prevrawlen;
    unsigned int lensize, len;
    unsigned int headersize;
    unsigned char encoding;
} zlentry;

61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 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
/* Return bytes needed to store integer encoded by 'encoding' */
static unsigned int zipEncodingSize(char encoding) {
    if (encoding == ZIP_ENC_SHORT) {
        return sizeof(short int);
    } else if (encoding == ZIP_ENC_INT) {
        return sizeof(int);
    } else if (encoding == ZIP_ENC_LLONG) {
        return sizeof(long long);
    }
    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;
}

132 133 134 135 136 137 138 139
/* 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;
    zipDecodeLength(p,&prevlensize);
    return zipEncodeLength(NULL,ZIP_ENC_RAW,len)-prevlensize;
}

140 141 142 143 144 145 146 147
/* 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! */
static int zipTryEncoding(unsigned char *entry, long long *v, char *encoding) {
    long long value;
    char *eptr;

    if (entry[0] == '-' || (entry[0] >= '0' && entry[0] <= '9')) {
P
Pieter Noordhuis 已提交
148
        value = strtoll((char*)entry,&eptr,10);
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
        if (eptr[0] != '\0') return 0;
        if (value >= SHRT_MIN && value <= SHRT_MAX) {
            *encoding = ZIP_ENC_SHORT;
        } else if (value >= INT_MIN && value <= INT_MAX) {
            *encoding = ZIP_ENC_INT;
        } else {
            *encoding = ZIP_ENC_LLONG;
        }
        *v = value;
        return 1;
    }
    return 0;
}

/* Store integer 'value' at 'p', encoded as 'encoding' */
static void zipSaveInteger(unsigned char *p, long long value, char encoding) {
    short int s;
    int i;
    long long l;
    if (encoding == ZIP_ENC_SHORT) {
        s = value;
        memcpy(p,&s,sizeof(s));
    } else if (encoding == ZIP_ENC_INT) {
        i = value;
        memcpy(p,&i,sizeof(i));
    } else if (encoding == ZIP_ENC_LLONG) {
        l = value;
        memcpy(p,&l,sizeof(l));
    } else {
        assert(NULL);
    }
}

/* Read integer encoded as 'encoding' from 'p' */
static long long zipLoadInteger(unsigned char *p, char encoding) {
    short int s;
    int i;
    long long l, ret;
    if (encoding == ZIP_ENC_SHORT) {
        memcpy(&s,p,sizeof(s));
        ret = s;
    } else if (encoding == ZIP_ENC_INT) {
        memcpy(&i,p,sizeof(i));
        ret = i;
    } else if (encoding == ZIP_ENC_LLONG) {
        memcpy(&l,p,sizeof(l));
        ret = l;
    } else {
        assert(NULL);
    }
    return ret;
}

202 203 204 205 206 207 208 209 210 211
/* Return a struct with all information about an entry. */
static zlentry zipEntry(unsigned char *p) {
    zlentry e;
    e.prevrawlen = zipDecodeLength(p,&e.prevrawlensize);
    e.len = zipDecodeLength(p+e.prevrawlensize,&e.lensize);
    e.headersize = e.prevrawlensize+e.lensize;
    e.encoding = ZIP_ENCODING(p+e.prevrawlensize);
    return e;
}

212 213
/* Return the total amount used by an entry (encoded length + payload). */
static unsigned int zipRawEntryLength(unsigned char *p) {
214 215 216 217 218 219
    unsigned int prevlensize, lensize, len;
    /* Byte-size of encoded length of previous entry */
    zipDecodeLength(p,&prevlensize);
    /* Encoded length of this entry's payload */
    len = zipDecodeLength(p+prevlensize, &lensize);
    return prevlensize+lensize+len;
220 221
}

222 223 224 225 226
/* 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;
227
    ZIPLIST_TAIL_OFFSET(zl) = ZIPLIST_HEADER_SIZE;
228 229 230 231 232
    ZIPLIST_LENGTH(zl) = 0;
    zl[bytes-1] = ZIP_END;
    return zl;
}

233
/* Resize the ziplist. */
234
static unsigned char *ziplistResize(unsigned char *zl, unsigned int len) {
235
    zl = zrealloc(zl,len);
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
    ZIPLIST_BYTES(zl) = len;
    zl[len-1] = ZIP_END;
    return zl;
}

static unsigned char *ziplistHead(unsigned char *zl) {
    return zl+ZIPLIST_HEADER_SIZE;
}

static unsigned char *ziplistTail(unsigned char *zl) {
    unsigned char *p, *q;
    p = q = ziplistHead(zl);
    while (*p != ZIP_END) {
        q = p;
        p += zipRawEntryLength(p);
    }
    return q;
}

unsigned char *ziplistPush(unsigned char *zl, unsigned char *entry, unsigned int elen, int where) {
256 257
    unsigned int curlen = ZIPLIST_BYTES(zl), reqlen, prevlen;
    unsigned char *p, *curtail;
258 259
    char encoding = ZIP_ENC_RAW;
    long long value;
260

261 262 263 264 265 266 267 268 269
    /* We need to store the length of the current tail when the list
     * is non-empty and we push at the tail. */
    curtail = zl+ZIPLIST_TAIL_OFFSET(zl);
    if (where == ZIPLIST_TAIL && curtail[0] != ZIP_END) {
        prevlen = zipRawEntryLength(curtail);
    } else {
        prevlen = 0;
    }

270 271 272 273 274 275
    /* See if the entry can be encoded */
    if (zipTryEncoding(entry,&value,&encoding)) {
        reqlen = zipEncodingSize(encoding);
    } else {
        reqlen = elen;
    }
276 277 278 279

    /* We need space for both the length of the previous entry and
     * the length of the payload. */
    reqlen += zipEncodeLength(NULL,ZIP_ENC_RAW,prevlen);
280
    reqlen += zipEncodeLength(NULL,encoding,elen);
281

282 283
    /* Resize the ziplist and move if needed */
    zl = ziplistResize(zl,curlen+reqlen);
284 285 286 287 288 289 290 291 292 293
    if (where == ZIPLIST_HEAD) {
        p = zl+ZIPLIST_HEADER_SIZE;
        if (*p != ZIP_END) {
            /* Subtract one because of the ZIP_END bytes */
            memmove(p+reqlen,p,curlen-ZIPLIST_HEADER_SIZE-1);
        }
    } else {
        p = zl+curlen-1;
    }

294 295 296 297 298 299 300 301 302
    /* Update tail offset if this is not the first element */
    if (curtail[0] != ZIP_END) {
        if (where == ZIPLIST_HEAD) {
            ZIPLIST_TAIL_OFFSET(zl) += reqlen;
        } else {
            ZIPLIST_TAIL_OFFSET(zl) += prevlen;
        }
    }

303
    /* Write the entry */
304
    p += zipEncodeLength(p,ZIP_ENC_RAW,prevlen);
305 306 307 308 309 310
    p += zipEncodeLength(p,encoding,elen);
    if (encoding != ZIP_ENC_RAW) {
        zipSaveInteger(p,value,encoding);
    } else {
        memcpy(p,entry,elen);
    }
311
    ZIPLIST_INCR_LENGTH(zl,1);
312 313 314
    return zl;
}

315 316
unsigned char *ziplistPop(unsigned char *zl, sds *target, int where) {
    unsigned int curlen = ZIPLIST_BYTES(zl), rawlen;
317
    zlentry entry;
318
    int nextdiff = 0;
319
    unsigned char *p;
320 321
    long long value;
    if (target) *target = NULL;
322 323 324 325

    /* Get pointer to element to remove */
    p = (where == ZIPLIST_HEAD) ? ziplistHead(zl) : ziplistTail(zl);
    if (*p == ZIP_END) return zl;
326

327 328
    entry = zipEntry(p);
    rawlen = entry.headersize+entry.len;
329
    if (target) {
330 331
        if (entry.encoding == ZIP_ENC_RAW) {
            *target = sdsnewlen(p+entry.headersize,entry.len);
332
        } else {
333
            value = zipLoadInteger(p+entry.headersize,entry.encoding);
334 335 336
            *target = sdscatprintf(sdsempty(), "%lld", value);
        }
    }
337 338

    if (where == ZIPLIST_HEAD) {
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
        /* The next entry will now be the head of the list */
        if (p[rawlen] != ZIP_END) {
            /* Tricky: storing the length of the previous entry in the next
             * entry (this previous length is always 0 when popping from the
             * head), might reduce the number of bytes needed.
             *
             * In this special case (new length is 0), we know that the
             * byte difference to store is always <= 0, which means that
             * we always have space to store it. */
            nextdiff = zipPrevLenByteDiff(p+rawlen,0);
            zipEncodeLength(p+rawlen-nextdiff,ZIP_ENC_RAW,0);
        }
        /* Move list to the front */
        memmove(p,p+rawlen-nextdiff,curlen-ZIPLIST_HEADER_SIZE-rawlen+nextdiff);

        /* Subtract the gained space from the tail offset */
        ZIPLIST_TAIL_OFFSET(zl) -= rawlen+nextdiff;
    } else {
        /* Subtract the length of the previous element from the tail offset. */
358
        ZIPLIST_TAIL_OFFSET(zl) -= entry.prevrawlen;
359 360 361
    }

    /* Resize and update length */
362
    zl = ziplistResize(zl,curlen-rawlen+nextdiff);
363
    ZIPLIST_INCR_LENGTH(zl,-1);
364 365 366
    return zl;
}

367 368 369 370 371 372 373 374 375 376 377
/* Returns an offset to use for iterating with ziplistNext. */
unsigned char *ziplistIndex(unsigned char *zl, unsigned int index) {
    unsigned char *p = zl+ZIPLIST_HEADER_SIZE;
    unsigned int i = 0;
    for (; i < index; i++) {
        if (*p == ZIP_END) break;
        p += zipRawEntryLength(p);
    }
    return p;
}

378 379 380 381 382 383 384 385 386 387
/* Return pointer to next entry in ziplist. */
unsigned char *ziplistNext(unsigned char *p) {
    return *p == ZIP_END ? p : p+zipRawEntryLength(p);
}

/* 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. */
unsigned int ziplistGet(unsigned char *p, unsigned char **e, unsigned int *elen, long long *v) {
388
    zlentry entry;
389 390
    if (*p == ZIP_END) return 0;
    if (e) *e = NULL;
391

392 393
    entry = zipEntry(p);
    if (entry.encoding == ZIP_ENC_RAW) {
394
        if (e) {
395 396
            *elen = entry.len;
            *e = p+entry.headersize;
397 398 399
        }
    } else {
        if (v) {
400
            *v = zipLoadInteger(p+entry.headersize,entry.encoding);
401
        }
402
    }
403
    return 1;
404 405
}

406 407
/* Delete a range of entries from the ziplist. */
unsigned char *ziplistDeleteRange(unsigned char *zl, unsigned int index, unsigned int num) {
408
    unsigned char *p, *first = ziplistIndex(zl, index);
P
Pieter Noordhuis 已提交
409
    unsigned int i, totlen, deleted = 0;
410 411 412 413 414 415 416 417 418 419 420 421
    for (p = first, i = 0; *p != ZIP_END && i < num; i++) {
        p += zipRawEntryLength(p);
        deleted++;
    }

    totlen = p-first;
    if (totlen > 0) {
        /* Move current tail to the new tail when there *is* a tail */
        if (*p != ZIP_END) memmove(first,p,ZIPLIST_BYTES(zl)-(p-zl)-1);

        /* Resize and update length */
        zl = ziplistResize(zl, ZIPLIST_BYTES(zl)-totlen);
422
        ZIPLIST_INCR_LENGTH(zl,-deleted);
423 424 425 426
    }
    return zl;
}

427 428 429 430 431 432 433 434 435 436 437 438 439
/* 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. */
unsigned char *ziplistDelete(unsigned char *zl, unsigned char **p) {
    unsigned int offset = *p-zl, tail, len;
    len = zipRawEntryLength(*p);
    tail = ZIPLIST_BYTES(zl)-offset-len-1;

    /* Move current tail to the new tail when there *is* a tail */
    if (tail > 0) memmove(*p,*p+len,tail);

    /* Resize and update length */
    zl = ziplistResize(zl, ZIPLIST_BYTES(zl)-len);
440
    ZIPLIST_INCR_LENGTH(zl,-1);
441 442 443 444 445 446 447

    /* Store new pointer to current element in p.
     * This needs to be done because zl can change on realloc. */
    *p = zl+offset;
    return zl;
}

448
/* Compare entry pointer to by 'p' with 'entry'. Return 1 if equal. */
449 450 451 452
unsigned int ziplistCompare(unsigned char *p, unsigned char *s, unsigned int slen) {
    zlentry entry;
    unsigned char sencoding;
    long long val, sval;
453 454
    if (*p == ZIP_END) return 0;

455 456
    entry = zipEntry(p);
    if (entry.encoding == ZIP_ENC_RAW) {
457
        /* Raw compare */
458 459
        if (entry.len == slen) {
            return memcmp(p+entry.headersize,s,slen) == 0;
460 461 462
        } else {
            return 0;
        }
463
    } else {
464
        /* Try to compare encoded values */
465
        if (zipTryEncoding(s,&sval,&sencoding)) {
466 467 468 469
            if (entry.encoding == sencoding) {
                val = zipLoadInteger(p+entry.headersize,entry.encoding);
                return val == sval;
            }
470
        }
471
    }
472
    return 0;
473 474
}

475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492
/* Return length of ziplist. */
unsigned int ziplistLen(unsigned char *zl) {
    unsigned int len = 0;
    if (ZIPLIST_LENGTH(zl) < ZIP_BIGLEN) {
        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 */
        if (len < ZIP_BIGLEN) ZIPLIST_LENGTH(zl) = len;
    }
    return len;
}

493 494 495 496 497
/* Return size in bytes of ziplist. */
unsigned int ziplistSize(unsigned char *zl) {
    return ZIPLIST_BYTES(zl);
}

498
void ziplistRepr(unsigned char *zl) {
499 500
    unsigned char *p;
    zlentry entry;
501

502
    printf("{total bytes %d} {length %u}\n",ZIPLIST_BYTES(zl), ZIPLIST_LENGTH(zl));
503 504
    p = ziplistHead(zl);
    while(*p != ZIP_END) {
505 506 507 508 509
        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);
510
        } else {
511
            printf("%lld", zipLoadInteger(p,entry.encoding));
512
        }
513
        printf("\n");
514
        p += entry.len;
515 516 517 518 519 520
    }
    printf("{end}\n\n");
}

#ifdef ZIPLIST_TEST_MAIN

521 522
unsigned char *createList() {
    unsigned char *zl = ziplistNew();
523 524 525
    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);
526
    zl = ziplistPush(zl, (unsigned char*)"1024", 4, ZIPLIST_TAIL);
527 528 529
    return zl;
}

530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
unsigned char *createIntList() {
    unsigned char *zl = ziplistNew();
    char buf[32];

    sprintf(buf, "100");
    zl = ziplistPush(zl, buf, strlen(buf), ZIPLIST_TAIL);
    sprintf(buf, "128000");
    zl = ziplistPush(zl, buf, strlen(buf), ZIPLIST_TAIL);
    sprintf(buf, "-100");
    zl = ziplistPush(zl, buf, strlen(buf), ZIPLIST_HEAD);
    sprintf(buf, "4294967296");
    zl = ziplistPush(zl, buf, strlen(buf), ZIPLIST_HEAD);
    sprintf(buf, "non integer");
    zl = ziplistPush(zl, buf, strlen(buf), ZIPLIST_TAIL);
    sprintf(buf, "much much longer non integer");
    zl = ziplistPush(zl, buf, strlen(buf), ZIPLIST_TAIL);
    return zl;
}

549
int main(int argc, char **argv) {
550
    unsigned char *zl, *p, *q, *entry;
551
    unsigned int elen;
552
    long long value;
553 554
    sds s;

555 556 557
    zl = createIntList();
    ziplistRepr(zl);

558
    zl = createList();
559 560 561 562 563 564 565 566 567 568
    ziplistRepr(zl);

    zl = ziplistPop(zl, &s, ZIPLIST_TAIL);
    printf("Pop tail: %s (length %ld)\n", s, sdslen(s));
    ziplistRepr(zl);

    zl = ziplistPop(zl, &s, ZIPLIST_HEAD);
    printf("Pop head: %s (length %ld)\n", s, sdslen(s));
    ziplistRepr(zl);

569 570 571 572 573 574 575 576
    zl = ziplistPop(zl, &s, ZIPLIST_TAIL);
    printf("Pop tail: %s (length %ld)\n", s, sdslen(s));
    ziplistRepr(zl);

    zl = ziplistPop(zl, &s, ZIPLIST_TAIL);
    printf("Pop tail: %s (length %ld)\n", s, sdslen(s));
    ziplistRepr(zl);

577 578 579 580
    printf("Iterate list from 0 to end:\n");
    {
        zl = createList();
        p = ziplistIndex(zl, 0);
581
        while (ziplistGet(p, &entry, &elen, &value)) {
582
            printf("Entry: ");
583 584 585 586 587 588 589
            if (entry) {
                fwrite(entry,elen,1,stdout);
            } else {
                printf("%lld", value);
            }
            p = ziplistNext(p);
            printf("\n");
590 591 592 593 594 595 596 597
        }
        printf("\n");
    }

    printf("Iterate list from 1 to end:\n");
    {
        zl = createList();
        p = ziplistIndex(zl, 1);
598
        while (ziplistGet(p, &entry, &elen, &value)) {
599
            printf("Entry: ");
600 601 602 603 604 605 606
            if (entry) {
                fwrite(entry,elen,1,stdout);
            } else {
                printf("%lld", value);
            }
            p = ziplistNext(p);
            printf("\n");
607 608 609 610 611 612 613 614
        }
        printf("\n");
    }

    printf("Iterate list from 2 to end:\n");
    {
        zl = createList();
        p = ziplistIndex(zl, 2);
615
        while (ziplistGet(p, &entry, &elen, &value)) {
616
            printf("Entry: ");
617 618 619 620 621 622 623
            if (entry) {
                fwrite(entry,elen,1,stdout);
            } else {
                printf("%lld", value);
            }
            p = ziplistNext(p);
            printf("\n");
624 625 626 627 628 629 630
        }
        printf("\n");
    }

    printf("Iterate starting out of range:\n");
    {
        zl = createList();
631 632
        p = ziplistIndex(zl, 4);
        if (!ziplistGet(p, &entry, &elen, &value)) {
633 634 635 636
            printf("No entry\n");
        } else {
            printf("ERROR\n");
        }
637 638 639 640 641 642
        printf("\n");
    }

    printf("Delete inclusive range 0,0:\n");
    {
        zl = createList();
643
        zl = ziplistDeleteRange(zl, 0, 1);
644 645 646 647 648 649
        ziplistRepr(zl);
    }

    printf("Delete inclusive range 0,1:\n");
    {
        zl = createList();
650
        zl = ziplistDeleteRange(zl, 0, 2);
651 652 653 654 655 656
        ziplistRepr(zl);
    }

    printf("Delete inclusive range 1,2:\n");
    {
        zl = createList();
657
        zl = ziplistDeleteRange(zl, 1, 2);
658 659 660 661 662 663
        ziplistRepr(zl);
    }

    printf("Delete with start index out of range:\n");
    {
        zl = createList();
664
        zl = ziplistDeleteRange(zl, 5, 1);
665 666 667 668 669 670
        ziplistRepr(zl);
    }

    printf("Delete with num overflow:\n");
    {
        zl = createList();
671
        zl = ziplistDeleteRange(zl, 1, 5);
672
        ziplistRepr(zl);
673 674
    }

675 676 677 678
    printf("Delete foo while iterating:\n");
    {
        zl = createList();
        p = ziplistIndex(zl, 0);
679 680
        while (ziplistGet(p, &entry, &elen, &value)) {
            if (entry && strncmp("foo", entry, elen) == 0) {
681
                printf("Delete foo\n");
682
                zl = ziplistDelete(zl, &p);
683 684
            } else {
                printf("Entry: ");
685 686 687 688 689 690 691
                if (entry) {
                    fwrite(entry,elen,1,stdout);
                } else {
                    printf("%lld", value);
                }
                p = ziplistNext(p);
                printf("\n");
692 693 694 695
            }
        }
        printf("\n");
        ziplistRepr(zl);
696 697 698 699 700 701 702
    }

    printf("Compare strings with ziplist entries:\n");
    {
        zl = createList();
        p = ziplistIndex(zl, 0);
        if (!ziplistCompare(p,"hello",5)) {
703
            printf("ERROR: not \"hello\"\n");
704 705 706
            return;
        }
        if (ziplistCompare(p,"hella",5)) {
707
            printf("ERROR: \"hella\"\n");
708 709 710 711 712
            return;
        }

        p = ziplistIndex(zl, 3);
        if (!ziplistCompare(p,"1024",4)) {
713
            printf("ERROR: not \"1024\"\n");
714 715 716
            return;
        }
        if (ziplistCompare(p,"1025",4)) {
717
            printf("ERROR: \"1025\"\n");
718 719 720
            return;
        }
        printf("SUCCESS\n");
721 722
    }

723 724 725
    return 0;
}
#endif