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. 8 * 9 * ---------------------------------------------------------------------------- 10 * 11 * ZIPLIST OVERALL LAYOUT: 12 * The general layout of the ziplist is as follows: 13 * <zlbytes><zltail><zllen><entry><entry><zlend> 14 * 15 * <zlbytes> is an unsigned integer to hold the number of bytes that the 16 * ziplist occupies. This value needs to be stored to be able to resize the 17 * entire structure without the need to traverse it first. 18 * 19 * <zltail> is the offset to the last entry in the list. This allows a pop 20 * operation on the far side of the list without the need for full traversal. 21 * 22 * <zllen> is the number of entries.When this value is larger than 2**16-2, 23 * we need to traverse the entire list to know how many items it holds. 24 * 25 * <zlend> is a single byte special value, equal to 255, which indicates the 26 * end of the list. 27 * 28 * ZIPLIST ENTRIES: 29 * Every entry in the ziplist is prefixed by a header that contains two pieces 30 * of information. First, the length of the previous entry is stored to be 31 * able to traverse the list from back to front. Second, the encoding with an 32 * optional string length of the entry itself is stored. 33 * 34 * The length of the previous entry is encoded in the following way: 35 * If this length is smaller than 254 bytes, it will only consume a single 36 * byte that takes the length as value. When the length is greater than or 37 * equal to 254, it will consume 5 bytes. The first byte is set to 254 to 38 * indicate a larger value is following. The remaining 4 bytes take the 39 * length of the previous entry as value. 40 * 41 * The other header field of the entry itself depends on the contents of the 42 * entry. When the entry is a string, the first 2 bits of this header will hold 43 * the type of encoding used to store the length of the string, followed by the 44 * actual length of the string. When the entry is an integer the first 2 bits 45 * are both set to 1. The following 2 bits are used to specify what kind of 46 * integer will be stored after this header. An overview of the different 47 * types and encodings is as follows: 48 * 49 * |00pppppp| - 1 byte 50 * String value with length less than or equal to 63 bytes (6 bits). 51 * |01pppppp|qqqqqqqq| - 2 bytes 52 * String value with length less than or equal to 16383 bytes (14 bits). 53 * |10______|qqqqqqqq|rrrrrrrr|ssssssss|tttttttt| - 5 bytes 54 * String value with length greater than or equal to 16384 bytes. 55 * |1100____| - 1 byte 56 * Integer encoded as int16_t (2 bytes). 57 * |1101____| - 1 byte 58 * Integer encoded as int32_t (4 bytes). 59 * |1110____| - 1 byte 60 * Integer encoded as int64_t (8 bytes). 61 */ 62 63 #include <stdio.h> 64 #include <stdlib.h> 65 #include <string.h> 66 #include <stdint.h> 67 #include <assert.h> 68 #include <limits.h> 69 #include "zmalloc.h" 70 #include "ziplist.h" 71 72 int ll2string(char *s, size_t len, long long value); 73 74 #define ZIP_END 255 75 #define ZIP_BIGLEN 254 76 77 /* Different encoding/length possibilities */ 78 #define ZIP_STR_06B (0 << 6) 79 #define ZIP_STR_14B (1 << 6) 80 #define ZIP_STR_32B (2 << 6) 81 #define ZIP_INT_16B (0xc0 | 0<<4) 82 #define ZIP_INT_32B (0xc0 | 1<<4) 83 #define ZIP_INT_64B (0xc0 | 2<<4) 84 85 /* Macro's to determine type */ 86 #define ZIP_IS_STR(enc) (((enc) & 0xc0) < 0xc0) 87 #define ZIP_IS_INT(enc) (!ZIP_IS_STR(enc) && ((enc) & 0x30) < 0x30) 88 89 /* Utility macros */ 90 #define ZIPLIST_BYTES(zl) (*((uint32_t*)(zl))) 91 #define ZIPLIST_TAIL_OFFSET(zl) (*((uint32_t*)((zl)+sizeof(uint32_t)))) 92 #define ZIPLIST_LENGTH(zl) (*((uint16_t*)((zl)+sizeof(uint32_t)*2))) 93 #define ZIPLIST_HEADER_SIZE (sizeof(uint32_t)*2+sizeof(uint16_t)) 94 #define ZIPLIST_ENTRY_HEAD(zl) ((zl)+ZIPLIST_HEADER_SIZE) 95 #define ZIPLIST_ENTRY_TAIL(zl) ((zl)+ZIPLIST_TAIL_OFFSET(zl)) 96 #define ZIPLIST_ENTRY_END(zl) ((zl)+ZIPLIST_BYTES(zl)-1) 97 98 /* We know a positive increment can only be 1 because entries can only be 99 * pushed one at a time. */ 100 #define ZIPLIST_INCR_LENGTH(zl,incr) { \ 101 if (ZIPLIST_LENGTH(zl) < UINT16_MAX) ZIPLIST_LENGTH(zl)+=incr; } 102 103 typedef struct zlentry { 104 unsigned int prevrawlensize, prevrawlen; 105 unsigned int lensize, len; 106 unsigned int headersize; 107 unsigned char encoding; 108 unsigned char *p; 109 } zlentry; 110 111 /* Return the encoding pointer to by 'p'. */ 112 static unsigned int zipEntryEncoding(unsigned char *p) { 113 /* String encoding: 2 MSBs */ 114 unsigned char b = p[0] & 0xc0; 115 if (b < 0xc0) { 116 return b; 117 } else { 118 /* Integer encoding: 4 MSBs */ 119 return p[0] & 0xf0; 120 } 121 assert(NULL); 122 return 0; 123 } 124 125 /* Return bytes needed to store integer encoded by 'encoding' */ 126 static unsigned int zipIntSize(unsigned char encoding) { 127 switch(encoding) { 128 case ZIP_INT_16B: return sizeof(int16_t); 129 case ZIP_INT_32B: return sizeof(int32_t); 130 case ZIP_INT_64B: return sizeof(int64_t); 131 } 132 assert(NULL); 133 return 0; 134 } 135 136 /* Decode the encoded length pointed by 'p'. If a pointer to 'lensize' is 137 * provided, it is set to the number of bytes required to encode the length. */ 138 static unsigned int zipDecodeLength(unsigned char *p, unsigned int *lensize) { 139 unsigned char encoding = zipEntryEncoding(p); 140 unsigned int len = 0; 141 142 if (ZIP_IS_STR(encoding)) { 143 switch(encoding) { 144 case ZIP_STR_06B: 145 len = p[0] & 0x3f; 146 if (lensize) *lensize = 1; 147 break; 148 case ZIP_STR_14B: 149 len = ((p[0] & 0x3f) << 8) | p[1]; 150 if (lensize) *lensize = 2; 151 break; 152 case ZIP_STR_32B: 153 len = (p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4]; 154 if (lensize) *lensize = 5; 155 break; 156 default: 157 assert(NULL); 158 } 159 } else { 160 len = zipIntSize(encoding); 161 if (lensize) *lensize = 1; 162 } 163 return len; 164 } 165 166 /* Encode the length 'l' writing it in 'p'. If p is NULL it just returns 167 * the amount of bytes required to encode such a length. */ 168 static unsigned int zipEncodeLength(unsigned char *p, unsigned char encoding, unsigned int rawlen) { 169 unsigned char len = 1, buf[5]; 170 171 if (ZIP_IS_STR(encoding)) { 172 /* Although encoding is given it may not be set for strings, 173 * so we determine it here using the raw length. */ 174 if (rawlen <= 0x3f) { 175 if (!p) return len; 176 buf[0] = ZIP_STR_06B | rawlen; 177 } else if (rawlen <= 0x3fff) { 178 len += 1; 179 if (!p) return len; 180 buf[0] = ZIP_STR_14B | ((rawlen >> 8) & 0x3f); 181 buf[1] = rawlen & 0xff; 182 } else { 183 len += 4; 184 if (!p) return len; 185 buf[0] = ZIP_STR_32B; 186 buf[1] = (rawlen >> 24) & 0xff; 187 buf[2] = (rawlen >> 16) & 0xff; 188 buf[3] = (rawlen >> 8) & 0xff; 189 buf[4] = rawlen & 0xff; 190 } 191 } else { 192 /* Implies integer encoding, so length is always 1. */ 193 if (!p) return len; 194 buf[0] = encoding; 195 } 196 197 /* Store this length at p */ 198 memcpy(p,buf,len); 199 return len; 200 } 201 202 /* Decode the length of the previous element stored at "p". */ 203 static unsigned int zipPrevDecodeLength(unsigned char *p, unsigned int *lensize) { 204 unsigned int len = *p; 205 if (len < ZIP_BIGLEN) { 206 if (lensize) *lensize = 1; 207 } else { 208 if (lensize) *lensize = 1+sizeof(len); 209 memcpy(&len,p+1,sizeof(len)); 210 } 211 return len; 212 } 213 214 /* Encode the length of the previous entry and write it to "p". Return the 215 * number of bytes needed to encode this length if "p" is NULL. */ 216 static unsigned int zipPrevEncodeLength(unsigned char *p, unsigned int len) { 217 if (p == NULL) { 218 return (len < ZIP_BIGLEN) ? 1 : sizeof(len)+1; 219 } else { 220 if (len < ZIP_BIGLEN) { 221 p[0] = len; 222 return 1; 223 } else { 224 p[0] = ZIP_BIGLEN; 225 memcpy(p+1,&len,sizeof(len)); 226 return 1+sizeof(len); 227 } 228 } 229 } 230 231 /* Encode the length of the previous entry and write it to "p". This only 232 * uses the larger encoding (required in __ziplistCascadeUpdate). */ 233 static void zipPrevEncodeLengthForceLarge(unsigned char *p, unsigned int len) { 234 if (p == NULL) return; 235 p[0] = ZIP_BIGLEN; 236 memcpy(p+1,&len,sizeof(len)); 237 } 238 239 /* Return the difference in number of bytes needed to store the new length 240 * "len" on the entry pointed to by "p". */ 241 static int zipPrevLenByteDiff(unsigned char *p, unsigned int len) { 242 unsigned int prevlensize; 243 zipPrevDecodeLength(p,&prevlensize); 244 return zipPrevEncodeLength(NULL,len)-prevlensize; 245 } 246 247 /* Check if string pointed to by 'entry' can be encoded as an integer. 248 * Stores the integer value in 'v' and its encoding in 'encoding'. */ 249 static int zipTryEncoding(unsigned char *entry, unsigned int entrylen, long long *v, unsigned char *encoding) { 250 long long value; 251 char *eptr; 252 char buf[32]; 253 254 if (entrylen >= 32 || entrylen == 0) return 0; 255 if (entry[0] == '-' || (entry[0] >= '0' && entry[0] <= '9')) { 256 int slen; 257 258 /* Perform a back-and-forth conversion to make sure that 259 * the string turned into an integer is not losing any info. */ 260 memcpy(buf,entry,entrylen); 261 buf[entrylen] = '\0'; 262 value = strtoll(buf,&eptr,10); 263 if (eptr[0] != '\0') return 0; 264 slen = ll2string(buf,32,value); 265 if (entrylen != (unsigned)slen || memcmp(buf,entry,slen)) return 0; 266 267 /* Great, the string can be encoded. Check what's the smallest 268 * of our encoding types that can hold this value. */ 269 if (value >= INT16_MIN && value <= INT16_MAX) { 270 *encoding = ZIP_INT_16B; 271 } else if (value >= INT32_MIN && value <= INT32_MAX) { 272 *encoding = ZIP_INT_32B; 273 } else { 274 *encoding = ZIP_INT_64B; 275 } 276 *v = value; 277 return 1; 278 } 279 return 0; 280 } 281 282 /* Store integer 'value' at 'p', encoded as 'encoding' */ 283 static void zipSaveInteger(unsigned char *p, int64_t value, unsigned char encoding) { 284 int16_t i16; 285 int32_t i32; 286 int64_t i64; 287 if (encoding == ZIP_INT_16B) { 288 i16 = value; 289 memcpy(p,&i16,sizeof(i16)); 290 } else if (encoding == ZIP_INT_32B) { 291 i32 = value; 292 memcpy(p,&i32,sizeof(i32)); 293 } else if (encoding == ZIP_INT_64B) { 294 i64 = value; 295 memcpy(p,&i64,sizeof(i64)); 296 } else { 297 assert(NULL); 298 } 299 } 300 301 /* Read integer encoded as 'encoding' from 'p' */ 302 static int64_t zipLoadInteger(unsigned char *p, unsigned char encoding) { 303 int16_t i16; 304 int32_t i32; 305 int64_t i64, ret = 0; 306 if (encoding == ZIP_INT_16B) { 307 memcpy(&i16,p,sizeof(i16)); 308 ret = i16; 309 } else if (encoding == ZIP_INT_32B) { 310 memcpy(&i32,p,sizeof(i32)); 311 ret = i32; 312 } else if (encoding == ZIP_INT_64B) { 313 memcpy(&i64,p,sizeof(i64)); 314 ret = i64; 315 } else { 316 assert(NULL); 317 } 318 return ret; 319 } 320 321 /* Return a struct with all information about an entry. */ 322 static zlentry zipEntry(unsigned char *p) { 323 zlentry e; 324 e.prevrawlen = zipPrevDecodeLength(p,&e.prevrawlensize); 325 e.len = zipDecodeLength(p+e.prevrawlensize,&e.lensize); 326 e.headersize = e.prevrawlensize+e.lensize; 327 e.encoding = zipEntryEncoding(p+e.prevrawlensize); 328 e.p = p; 329 return e; 330 } 331 332 /* Return the total number of bytes used by the entry at "p". */ 333 static unsigned int zipRawEntryLength(unsigned char *p) { 334 zlentry e = zipEntry(p); 335 return e.headersize + e.len; 336 } 337 338 /* Create a new empty ziplist. */ 339 unsigned char *ziplistNew(void) { 340 unsigned int bytes = ZIPLIST_HEADER_SIZE+1; 341 unsigned char *zl = zmalloc(bytes); 342 ZIPLIST_BYTES(zl) = bytes; 343 ZIPLIST_TAIL_OFFSET(zl) = ZIPLIST_HEADER_SIZE; 344 ZIPLIST_LENGTH(zl) = 0; 345 zl[bytes-1] = ZIP_END; 346 return zl; 347 } 348 349 /* Resize the ziplist. */ 350 static unsigned char *ziplistResize(unsigned char *zl, unsigned int len) { 351 zl = zrealloc(zl,len); 352 ZIPLIST_BYTES(zl) = len; 353 zl[len-1] = ZIP_END; 354 return zl; 355 } 356 357 /* When an entry is inserted, we need to set the prevlen field of the next 358 * entry to equal the length of the inserted entry. It can occur that this 359 * length cannot be encoded in 1 byte and the next entry needs to be grow 360 * a bit larger to hold the 5-byte encoded prevlen. This can be done for free, 361 * because this only happens when an entry is already being inserted (which 362 * causes a realloc and memmove). However, encoding the prevlen may require 363 * that this entry is grown as well. This effect may cascade throughout 364 * the ziplist when there are consecutive entries with a size close to 365 * ZIP_BIGLEN, so we need to check that the prevlen can be encoded in every 366 * consecutive entry. 367 * 368 * Note that this effect can also happen in reverse, where the bytes required 369 * to encode the prevlen field can shrink. This effect is deliberately ignored, 370 * because it can cause a "flapping" effect where a chain prevlen fields is 371 * first grown and then shrunk again after consecutive inserts. Rather, the 372 * field is allowed to stay larger than necessary, because a large prevlen 373 * field implies the ziplist is holding large entries anyway. 374 * 375 * The pointer "p" points to the first entry that does NOT need to be 376 * updated, i.e. consecutive fields MAY need an update. */ 377 static unsigned char *__ziplistCascadeUpdate(unsigned char *zl, unsigned char *p) { 378 unsigned int curlen = ZIPLIST_BYTES(zl), rawlen, rawlensize; 379 unsigned int offset, noffset, extra; 380 unsigned char *np; 381 zlentry cur, next; 382 383 while (p[0] != ZIP_END) { 384 cur = zipEntry(p); 385 rawlen = cur.headersize + cur.len; 386 rawlensize = zipPrevEncodeLength(NULL,rawlen); 387 388 /* Abort if there is no next entry. */ 389 if (p[rawlen] == ZIP_END) break; 390 next = zipEntry(p+rawlen); 391 392 /* Abort when "prevlen" has not changed. */ 393 if (next.prevrawlen == rawlen) break; 394 395 if (next.prevrawlensize < rawlensize) { 396 /* The "prevlen" field of "next" needs more bytes to hold 397 * the raw length of "cur". */ 398 offset = p-zl; 399 extra = rawlensize-next.prevrawlensize; 400 zl = ziplistResize(zl,curlen+extra); 401 ZIPLIST_TAIL_OFFSET(zl) += extra; 402 p = zl+offset; 403 404 /* Move the tail to the back. */ 405 np = p+rawlen; 406 noffset = np-zl; 407 memmove(np+rawlensize, 408 np+next.prevrawlensize, 409 curlen-noffset-next.prevrawlensize-1); 410 zipPrevEncodeLength(np,rawlen); 411 412 /* Advance the cursor */ 413 p += rawlen; 414 curlen += extra; 415 } else { 416 if (next.prevrawlensize > rawlensize) { 417 /* This would result in shrinking, which we want to avoid. 418 * So, set "rawlen" in the available bytes. */ 419 zipPrevEncodeLengthForceLarge(p+rawlen,rawlen); 420 } else { 421 zipPrevEncodeLength(p+rawlen,rawlen); 422 } 423 424 /* Stop here, as the raw length of "next" has not changed. */ 425 break; 426 } 427 } 428 return zl; 429 } 430 431 /* Delete "num" entries, starting at "p". Returns pointer to the ziplist. */ 432 static unsigned char *__ziplistDelete(unsigned char *zl, unsigned char *p, unsigned int num) { 433 unsigned int i, totlen, deleted = 0; 434 int offset, nextdiff = 0; 435 zlentry first, tail; 436 437 first = zipEntry(p); 438 for (i = 0; p[0] != ZIP_END && i < num; i++) { 439 p += zipRawEntryLength(p); 440 deleted++; 441 } 442 443 totlen = p-first.p; 444 if (totlen > 0) { 445 if (p[0] != ZIP_END) { 446 /* Tricky: storing the prevlen in this entry might reduce or 447 * increase the number of bytes needed, compared to the current 448 * prevlen. Note that we can always store this length because 449 * it was previously stored by an entry that is being deleted. */ 450 nextdiff = zipPrevLenByteDiff(p,first.prevrawlen); 451 zipPrevEncodeLength(p-nextdiff,first.prevrawlen); 452 453 /* Update offset for tail */ 454 ZIPLIST_TAIL_OFFSET(zl) -= totlen; 455 456 /* When the tail contains more than one entry, we need to take 457 * "nextdiff" in account as well. Otherwise, a change in the 458 * size of prevlen doesn't have an effect on the *tail* offset. */ 459 tail = zipEntry(p); 460 if (p[tail.headersize+tail.len] != ZIP_END) 461 ZIPLIST_TAIL_OFFSET(zl) += nextdiff; 462 463 /* Move tail to the front of the ziplist */ 464 memmove(first.p,p-nextdiff,ZIPLIST_BYTES(zl)-(p-zl)-1+nextdiff); 465 } else { 466 /* The entire tail was deleted. No need to move memory. */ 467 ZIPLIST_TAIL_OFFSET(zl) = (first.p-zl)-first.prevrawlen; 468 } 469 470 /* Resize and update length */ 471 offset = first.p-zl; 472 zl = ziplistResize(zl, ZIPLIST_BYTES(zl)-totlen+nextdiff); 473 ZIPLIST_INCR_LENGTH(zl,-deleted); 474 p = zl+offset; 475 476 /* When nextdiff != 0, the raw length of the next entry has changed, so 477 * we need to cascade the update throughout the ziplist */ 478 if (nextdiff != 0) 479 zl = __ziplistCascadeUpdate(zl,p); 480 } 481 return zl; 482 } 483 484 /* Insert item at "p". */ 485 static unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) { 486 unsigned int curlen = ZIPLIST_BYTES(zl), reqlen, prevlen = 0; 487 unsigned int offset, nextdiff = 0; 488 unsigned char encoding = 0; 489 long long value; 490 zlentry entry, tail; 491 492 /* Find out prevlen for the entry that is inserted. */ 493 if (p[0] != ZIP_END) { 494 entry = zipEntry(p); 495 prevlen = entry.prevrawlen; 496 } else { 497 unsigned char *ptail = ZIPLIST_ENTRY_TAIL(zl); 498 if (ptail[0] != ZIP_END) { 499 prevlen = zipRawEntryLength(ptail); 500 } 501 } 502 503 /* See if the entry can be encoded */ 504 if (zipTryEncoding(s,slen,&value,&encoding)) { 505 /* 'encoding' is set to the appropriate integer encoding */ 506 reqlen = zipIntSize(encoding); 507 } else { 508 /* 'encoding' is untouched, however zipEncodeLength will use the 509 * string length to figure out how to encode it. */ 510 reqlen = slen; 511 } 512 /* We need space for both the length of the previous entry and 513 * the length of the payload. */ 514 reqlen += zipPrevEncodeLength(NULL,prevlen); 515 reqlen += zipEncodeLength(NULL,encoding,slen); 516 517 /* When the insert position is not equal to the tail, we need to 518 * make sure that the next entry can hold this entry's length in 519 * its prevlen field. */ 520 nextdiff = (p[0] != ZIP_END) ? zipPrevLenByteDiff(p,reqlen) : 0; 521 522 /* Store offset because a realloc may change the address of zl. */ 523 offset = p-zl; 524 zl = ziplistResize(zl,curlen+reqlen+nextdiff); 525 p = zl+offset; 526 527 /* Apply memory move when necessary and update tail offset. */ 528 if (p[0] != ZIP_END) { 529 /* Subtract one because of the ZIP_END bytes */ 530 memmove(p+reqlen,p-nextdiff,curlen-offset-1+nextdiff); 531 532 /* Encode this entry's raw length in the next entry. */ 533 zipPrevEncodeLength(p+reqlen,reqlen); 534 535 /* Update offset for tail */ 536 ZIPLIST_TAIL_OFFSET(zl) += reqlen; 537 538 /* When the tail contains more than one entry, we need to take 539 * "nextdiff" in account as well. Otherwise, a change in the 540 * size of prevlen doesn't have an effect on the *tail* offset. */ 541 tail = zipEntry(p+reqlen); 542 if (p[reqlen+tail.headersize+tail.len] != ZIP_END) 543 ZIPLIST_TAIL_OFFSET(zl) += nextdiff; 544 } else { 545 /* This element will be the new tail. */ 546 ZIPLIST_TAIL_OFFSET(zl) = p-zl; 547 } 548 549 /* When nextdiff != 0, the raw length of the next entry has changed, so 550 * we need to cascade the update throughout the ziplist */ 551 if (nextdiff != 0) { 552 offset = p-zl; 553 zl = __ziplistCascadeUpdate(zl,p+reqlen); 554 p = zl+offset; 555 } 556 557 /* Write the entry */ 558 p += zipPrevEncodeLength(p,prevlen); 559 p += zipEncodeLength(p,encoding,slen); 560 if (ZIP_IS_STR(encoding)) { 561 memcpy(p,s,slen); 562 } else { 563 zipSaveInteger(p,value,encoding); 564 } 565 ZIPLIST_INCR_LENGTH(zl,1); 566 return zl; 567 } 568 569 unsigned char *ziplistPush(unsigned char *zl, unsigned char *s, unsigned int slen, int where) { 570 unsigned char *p; 571 p = (where == ZIPLIST_HEAD) ? ZIPLIST_ENTRY_HEAD(zl) : ZIPLIST_ENTRY_END(zl); 572 return __ziplistInsert(zl,p,s,slen); 573 } 574 575 /* Returns an offset to use for iterating with ziplistNext. When the given 576 * index is negative, the list is traversed back to front. When the list 577 * doesn't contain an element at the provided index, NULL is returned. */ 578 unsigned char *ziplistIndex(unsigned char *zl, int index) { 579 unsigned char *p; 580 zlentry entry; 581 if (index < 0) { 582 index = (-index)-1; 583 p = ZIPLIST_ENTRY_TAIL(zl); 584 if (p[0] != ZIP_END) { 585 entry = zipEntry(p); 586 while (entry.prevrawlen > 0 && index--) { 587 p -= entry.prevrawlen; 588 entry = zipEntry(p); 589 } 590 } 591 } else { 592 p = ZIPLIST_ENTRY_HEAD(zl); 593 while (p[0] != ZIP_END && index--) { 594 p += zipRawEntryLength(p); 595 } 596 } 597 return (p[0] == ZIP_END || index > 0) ? NULL : p; 598 } 599 600 /* Return pointer to next entry in ziplist. 601 * 602 * zl is the pointer to the ziplist 603 * p is the pointer to the current element 604 * 605 * The element after 'p' is returned, otherwise NULL if we are at the end. */ 606 unsigned char *ziplistNext(unsigned char *zl, unsigned char *p) { 607 ((void) zl); 608 609 /* "p" could be equal to ZIP_END, caused by ziplistDelete, 610 * and we should return NULL. Otherwise, we should return NULL 611 * when the *next* element is ZIP_END (there is no next entry). */ 612 if (p[0] == ZIP_END) { 613 return NULL; 614 } else { 615 p = p+zipRawEntryLength(p); 616 return (p[0] == ZIP_END) ? NULL : p; 617 } 618 } 619 620 /* Return pointer to previous entry in ziplist. */ 621 unsigned char *ziplistPrev(unsigned char *zl, unsigned char *p) { 622 zlentry entry; 623 624 /* Iterating backwards from ZIP_END should return the tail. When "p" is 625 * equal to the first element of the list, we're already at the head, 626 * and should return NULL. */ 627 if (p[0] == ZIP_END) { 628 p = ZIPLIST_ENTRY_TAIL(zl); 629 return (p[0] == ZIP_END) ? NULL : p; 630 } else if (p == ZIPLIST_ENTRY_HEAD(zl)) { 631 return NULL; 632 } else { 633 entry = zipEntry(p); 634 assert(entry.prevrawlen > 0); 635 return p-entry.prevrawlen; 636 } 637 } 638 639 /* Get entry pointer to by 'p' and store in either 'e' or 'v' depending 640 * on the encoding of the entry. 'e' is always set to NULL to be able 641 * to find out whether the string pointer or the integer value was set. 642 * Return 0 if 'p' points to the end of the zipmap, 1 otherwise. */ 643 unsigned int ziplistGet(unsigned char *p, unsigned char **sstr, unsigned int *slen, long long *sval) { 644 zlentry entry; 645 if (p == NULL || p[0] == ZIP_END) return 0; 646 if (sstr) *sstr = NULL; 647 648 entry = zipEntry(p); 649 if (ZIP_IS_STR(entry.encoding)) { 650 if (sstr) { 651 *slen = entry.len; 652 *sstr = p+entry.headersize; 653 } 654 } else { 655 if (sval) { 656 *sval = zipLoadInteger(p+entry.headersize,entry.encoding); 657 } 658 } 659 return 1; 660 } 661 662 /* Insert an entry at "p". */ 663 unsigned char *ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) { 664 return __ziplistInsert(zl,p,s,slen); 665 } 666 667 /* Delete a single entry from the ziplist, pointed to by *p. 668 * Also update *p in place, to be able to iterate over the 669 * ziplist, while deleting entries. */ 670 unsigned char *ziplistDelete(unsigned char *zl, unsigned char **p) { 671 unsigned int offset = *p-zl; 672 zl = __ziplistDelete(zl,*p,1); 673 674 /* Store pointer to current element in p, because ziplistDelete will 675 * do a realloc which might result in a different "zl"-pointer. 676 * When the delete direction is back to front, we might delete the last 677 * entry and end up with "p" pointing to ZIP_END, so check this. */ 678 *p = zl+offset; 679 return zl; 680 } 681 682 /* Delete a range of entries from the ziplist. */ 683 unsigned char *ziplistDeleteRange(unsigned char *zl, unsigned int index, unsigned int num) { 684 unsigned char *p = ziplistIndex(zl,index); 685 return (p == NULL) ? zl : __ziplistDelete(zl,p,num); 686 } 687 688 /* Compare entry pointer to by 'p' with 'entry'. Return 1 if equal. */ 689 unsigned int ziplistCompare(unsigned char *p, unsigned char *sstr, unsigned int slen) { 690 zlentry entry; 691 unsigned char sencoding; 692 long long zval, sval; 693 if (p[0] == ZIP_END) return 0; 694 695 entry = zipEntry(p); 696 if (ZIP_IS_STR(entry.encoding)) { 697 /* Raw compare */ 698 if (entry.len == slen) { 699 return memcmp(p+entry.headersize,sstr,slen) == 0; 700 } else { 701 return 0; 702 } 703 } else { 704 /* Try to compare encoded values */ 705 if (zipTryEncoding(sstr,slen,&sval,&sencoding)) { 706 if (entry.encoding == sencoding) { 707 zval = zipLoadInteger(p+entry.headersize,entry.encoding); 708 return zval == sval; 709 } 710 } 711 } 712 return 0; 713 } 714 715 /* Return length of ziplist. */ 716 unsigned int ziplistLen(unsigned char *zl) { 717 unsigned int len = 0; 718 if (ZIPLIST_LENGTH(zl) < UINT16_MAX) { 719 len = ZIPLIST_LENGTH(zl); 720 } else { 721 unsigned char *p = zl+ZIPLIST_HEADER_SIZE; 722 while (*p != ZIP_END) { 723 p += zipRawEntryLength(p); 724 len++; 725 } 726 727 /* Re-store length if small enough */ 728 if (len < UINT16_MAX) ZIPLIST_LENGTH(zl) = len; 729 } 730 return len; 731 } 732 733 /* Return size in bytes of ziplist. */ 734 unsigned int ziplistSize(unsigned char *zl) { 735 return ZIPLIST_BYTES(zl); 736 } 737 738 void ziplistRepr(unsigned char *zl) { 739 unsigned char *p; 740 int index = 0; 741 zlentry entry; 742 743 printf( 744 "{total bytes %d} " 745 "{length %u}\n" 746 "{tail offset %u}\n", 747 ZIPLIST_BYTES(zl), 748 ZIPLIST_LENGTH(zl), 749 ZIPLIST_TAIL_OFFSET(zl)); 750 p = ZIPLIST_ENTRY_HEAD(zl); 751 while(*p != ZIP_END) { 752 entry = zipEntry(p); 753 printf( 754 "{" 755 "addr 0x%08lx, " 756 "index %2d, " 757 "offset %5ld, " 758 "rl: %5u, " 759 "hs %2u, " 760 "pl: %5u, " 761 "pls: %2u, " 762 "payload %5u" 763 "} ", 764 (long unsigned)p, 765 index, 766 (unsigned long) (p-zl), 767 entry.headersize+entry.len, 768 entry.headersize, 769 entry.prevrawlen, 770 entry.prevrawlensize, 771 entry.len); 772 p += entry.headersize; 773 if (ZIP_IS_STR(entry.encoding)) { 774 if (entry.len > 40) { 775 if (fwrite(p,40,1,stdout) == 0) perror("fwrite"); 776 printf("..."); 777 } else { 778 if (entry.len && 779 fwrite(p,entry.len,1,stdout) == 0) perror("fwrite"); 780 } 781 } else { 782 printf("%lld", (long long) zipLoadInteger(p,entry.encoding)); 783 } 784 printf("\n"); 785 p += entry.len; 786 index++; 787 } 788 printf("{end}\n\n"); 789 } 790 791 #ifdef ZIPLIST_TEST_MAIN 792 #include <sys/time.h> 793 #include "adlist.h" 794 #include "sds.h" 795 796 #define debug(f, ...) { if (DEBUG) printf(f, __VA_ARGS__); } 797 798 unsigned char *createList() { 799 unsigned char *zl = ziplistNew(); 800 zl = ziplistPush(zl, (unsigned char*)"foo", 3, ZIPLIST_TAIL); 801 zl = ziplistPush(zl, (unsigned char*)"quux", 4, ZIPLIST_TAIL); 802 zl = ziplistPush(zl, (unsigned char*)"hello", 5, ZIPLIST_HEAD); 803 zl = ziplistPush(zl, (unsigned char*)"1024", 4, ZIPLIST_TAIL); 804 return zl; 805 } 806 807 unsigned char *createIntList() { 808 unsigned char *zl = ziplistNew(); 809 char buf[32]; 810 811 sprintf(buf, "100"); 812 zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL); 813 sprintf(buf, "128000"); 814 zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL); 815 sprintf(buf, "-100"); 816 zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_HEAD); 817 sprintf(buf, "4294967296"); 818 zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_HEAD); 819 sprintf(buf, "non integer"); 820 zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL); 821 sprintf(buf, "much much longer non integer"); 822 zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL); 823 return zl; 824 } 825 826 long long usec(void) { 827 struct timeval tv; 828 gettimeofday(&tv,NULL); 829 return (((long long)tv.tv_sec)*1000000)+tv.tv_usec; 830 } 831 832 void stress(int pos, int num, int maxsize, int dnum) { 833 int i,j,k; 834 unsigned char *zl; 835 char posstr[2][5] = { "HEAD", "TAIL" }; 836 long long start; 837 for (i = 0; i < maxsize; i+=dnum) { 838 zl = ziplistNew(); 839 for (j = 0; j < i; j++) { 840 zl = ziplistPush(zl,(unsigned char*)"quux",4,ZIPLIST_TAIL); 841 } 842 843 /* Do num times a push+pop from pos */ 844 start = usec(); 845 for (k = 0; k < num; k++) { 846 zl = ziplistPush(zl,(unsigned char*)"quux",4,pos); 847 zl = ziplistDeleteRange(zl,0,1); 848 } 849 printf("List size: %8d, bytes: %8d, %dx push+pop (%s): %6lld usec\n", 850 i,ZIPLIST_BYTES(zl),num,posstr[pos],usec()-start); 851 zfree(zl); 852 } 853 } 854 855 void pop(unsigned char *zl, int where) { 856 unsigned char *p, *vstr; 857 unsigned int vlen; 858 long long vlong; 859 860 p = ziplistIndex(zl,where == ZIPLIST_HEAD ? 0 : -1); 861 if (ziplistGet(p,&vstr,&vlen,&vlong)) { 862 if (where == ZIPLIST_HEAD) 863 printf("Pop head: "); 864 else 865 printf("Pop tail: "); 866 867 if (vstr) 868 if (vlen && fwrite(vstr,vlen,1,stdout) == 0) perror("fwrite"); 869 else 870 printf("%lld", vlong); 871 872 printf("\n"); 873 ziplistDeleteRange(zl,-1,1); 874 } else { 875 printf("ERROR: Could not pop\n"); 876 exit(1); 877 } 878 } 879 880 void randstring(char *target, unsigned int min, unsigned int max) { 881 int p, len = min+rand()%(max-min+1); 882 int minval, maxval; 883 switch(rand() % 3) { 884 case 0: 885 minval = 0; 886 maxval = 255; 887 break; 888 case 1: 889 minval = 48; 890 maxval = 122; 891 break; 892 case 2: 893 minval = 48; 894 maxval = 52; 895 break; 896 default: 897 assert(NULL); 898 } 899 900 while(p < len) 901 target[p++] = minval+rand()%(maxval-minval+1); 902 return; 903 } 904 905 906 int main(int argc, char **argv) { 907 unsigned char *zl, *p; 908 unsigned char *entry; 909 unsigned int elen; 910 long long value; 911 912 /* If an argument is given, use it as the random seed. */ 913 if (argc == 2) 914 srand(atoi(argv[1])); 915 916 zl = createIntList(); 917 ziplistRepr(zl); 918 919 zl = createList(); 920 ziplistRepr(zl); 921 922 pop(zl,ZIPLIST_TAIL); 923 ziplistRepr(zl); 924 925 pop(zl,ZIPLIST_HEAD); 926 ziplistRepr(zl); 927 928 pop(zl,ZIPLIST_TAIL); 929 ziplistRepr(zl); 930 931 pop(zl,ZIPLIST_TAIL); 932 ziplistRepr(zl); 933 934 printf("Get element at index 3:\n"); 935 { 936 zl = createList(); 937 p = ziplistIndex(zl, 3); 938 if (!ziplistGet(p, &entry, &elen, &value)) { 939 printf("ERROR: Could not access index 3\n"); 940 return 1; 941 } 942 if (entry) { 943 if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); 944 printf("\n"); 945 } else { 946 printf("%lld\n", value); 947 } 948 printf("\n"); 949 } 950 951 printf("Get element at index 4 (out of range):\n"); 952 { 953 zl = createList(); 954 p = ziplistIndex(zl, 4); 955 if (p == NULL) { 956 printf("No entry\n"); 957 } else { 958 printf("ERROR: Out of range index should return NULL, returned offset: %ld\n", p-zl); 959 return 1; 960 } 961 printf("\n"); 962 } 963 964 printf("Get element at index -1 (last element):\n"); 965 { 966 zl = createList(); 967 p = ziplistIndex(zl, -1); 968 if (!ziplistGet(p, &entry, &elen, &value)) { 969 printf("ERROR: Could not access index -1\n"); 970 return 1; 971 } 972 if (entry) { 973 if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); 974 printf("\n"); 975 } else { 976 printf("%lld\n", value); 977 } 978 printf("\n"); 979 } 980 981 printf("Get element at index -4 (first element):\n"); 982 { 983 zl = createList(); 984 p = ziplistIndex(zl, -4); 985 if (!ziplistGet(p, &entry, &elen, &value)) { 986 printf("ERROR: Could not access index -4\n"); 987 return 1; 988 } 989 if (entry) { 990 if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); 991 printf("\n"); 992 } else { 993 printf("%lld\n", value); 994 } 995 printf("\n"); 996 } 997 998 printf("Get element at index -5 (reverse out of range):\n"); 999 { 1000 zl = createList(); 1001 p = ziplistIndex(zl, -5); 1002 if (p == NULL) { 1003 printf("No entry\n"); 1004 } else { 1005 printf("ERROR: Out of range index should return NULL, returned offset: %ld\n", p-zl); 1006 return 1; 1007 } 1008 printf("\n"); 1009 } 1010 1011 printf("Iterate list from 0 to end:\n"); 1012 { 1013 zl = createList(); 1014 p = ziplistIndex(zl, 0); 1015 while (ziplistGet(p, &entry, &elen, &value)) { 1016 printf("Entry: "); 1017 if (entry) { 1018 if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); 1019 } else { 1020 printf("%lld", value); 1021 } 1022 p = ziplistNext(zl,p); 1023 printf("\n"); 1024 } 1025 printf("\n"); 1026 } 1027 1028 printf("Iterate list from 1 to end:\n"); 1029 { 1030 zl = createList(); 1031 p = ziplistIndex(zl, 1); 1032 while (ziplistGet(p, &entry, &elen, &value)) { 1033 printf("Entry: "); 1034 if (entry) { 1035 if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); 1036 } else { 1037 printf("%lld", value); 1038 } 1039 p = ziplistNext(zl,p); 1040 printf("\n"); 1041 } 1042 printf("\n"); 1043 } 1044 1045 printf("Iterate list from 2 to end:\n"); 1046 { 1047 zl = createList(); 1048 p = ziplistIndex(zl, 2); 1049 while (ziplistGet(p, &entry, &elen, &value)) { 1050 printf("Entry: "); 1051 if (entry) { 1052 if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); 1053 } else { 1054 printf("%lld", value); 1055 } 1056 p = ziplistNext(zl,p); 1057 printf("\n"); 1058 } 1059 printf("\n"); 1060 } 1061 1062 printf("Iterate starting out of range:\n"); 1063 { 1064 zl = createList(); 1065 p = ziplistIndex(zl, 4); 1066 if (!ziplistGet(p, &entry, &elen, &value)) { 1067 printf("No entry\n"); 1068 } else { 1069 printf("ERROR\n"); 1070 } 1071 printf("\n"); 1072 } 1073 1074 printf("Iterate from back to front:\n"); 1075 { 1076 zl = createList(); 1077 p = ziplistIndex(zl, -1); 1078 while (ziplistGet(p, &entry, &elen, &value)) { 1079 printf("Entry: "); 1080 if (entry) { 1081 if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); 1082 } else { 1083 printf("%lld", value); 1084 } 1085 p = ziplistPrev(zl,p); 1086 printf("\n"); 1087 } 1088 printf("\n"); 1089 } 1090 1091 printf("Iterate from back to front, deleting all items:\n"); 1092 { 1093 zl = createList(); 1094 p = ziplistIndex(zl, -1); 1095 while (ziplistGet(p, &entry, &elen, &value)) { 1096 printf("Entry: "); 1097 if (entry) { 1098 if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); 1099 } else { 1100 printf("%lld", value); 1101 } 1102 zl = ziplistDelete(zl,&p); 1103 p = ziplistPrev(zl,p); 1104 printf("\n"); 1105 } 1106 printf("\n"); 1107 } 1108 1109 printf("Delete inclusive range 0,0:\n"); 1110 { 1111 zl = createList(); 1112 zl = ziplistDeleteRange(zl, 0, 1); 1113 ziplistRepr(zl); 1114 } 1115 1116 printf("Delete inclusive range 0,1:\n"); 1117 { 1118 zl = createList(); 1119 zl = ziplistDeleteRange(zl, 0, 2); 1120 ziplistRepr(zl); 1121 } 1122 1123 printf("Delete inclusive range 1,2:\n"); 1124 { 1125 zl = createList(); 1126 zl = ziplistDeleteRange(zl, 1, 2); 1127 ziplistRepr(zl); 1128 } 1129 1130 printf("Delete with start index out of range:\n"); 1131 { 1132 zl = createList(); 1133 zl = ziplistDeleteRange(zl, 5, 1); 1134 ziplistRepr(zl); 1135 } 1136 1137 printf("Delete with num overflow:\n"); 1138 { 1139 zl = createList(); 1140 zl = ziplistDeleteRange(zl, 1, 5); 1141 ziplistRepr(zl); 1142 } 1143 1144 printf("Delete foo while iterating:\n"); 1145 { 1146 zl = createList(); 1147 p = ziplistIndex(zl,0); 1148 while (ziplistGet(p,&entry,&elen,&value)) { 1149 if (entry && strncmp("foo",(char*)entry,elen) == 0) { 1150 printf("Delete foo\n"); 1151 zl = ziplistDelete(zl,&p); 1152 } else { 1153 printf("Entry: "); 1154 if (entry) { 1155 if (elen && fwrite(entry,elen,1,stdout) == 0) 1156 perror("fwrite"); 1157 } else { 1158 printf("%lld",value); 1159 } 1160 p = ziplistNext(zl,p); 1161 printf("\n"); 1162 } 1163 } 1164 printf("\n"); 1165 ziplistRepr(zl); 1166 } 1167 1168 printf("Regression test for >255 byte strings:\n"); 1169 { 1170 char v1[257],v2[257]; 1171 memset(v1,'x',256); 1172 memset(v2,'y',256); 1173 zl = ziplistNew(); 1174 zl = ziplistPush(zl,(unsigned char*)v1,strlen(v1),ZIPLIST_TAIL); 1175 zl = ziplistPush(zl,(unsigned char*)v2,strlen(v2),ZIPLIST_TAIL); 1176 1177 /* Pop values again and compare their value. */ 1178 p = ziplistIndex(zl,0); 1179 assert(ziplistGet(p,&entry,&elen,&value)); 1180 assert(strncmp(v1,(char*)entry,elen) == 0); 1181 p = ziplistIndex(zl,1); 1182 assert(ziplistGet(p,&entry,&elen,&value)); 1183 assert(strncmp(v2,(char*)entry,elen) == 0); 1184 printf("SUCCESS\n\n"); 1185 } 1186 1187 printf("Create long list and check indices:\n"); 1188 { 1189 zl = ziplistNew(); 1190 char buf[32]; 1191 int i,len; 1192 for (i = 0; i < 1000; i++) { 1193 len = sprintf(buf,"%d",i); 1194 zl = ziplistPush(zl,(unsigned char*)buf,len,ZIPLIST_TAIL); 1195 } 1196 for (i = 0; i < 1000; i++) { 1197 p = ziplistIndex(zl,i); 1198 assert(ziplistGet(p,NULL,NULL,&value)); 1199 assert(i == value); 1200 1201 p = ziplistIndex(zl,-i-1); 1202 assert(ziplistGet(p,NULL,NULL,&value)); 1203 assert(999-i == value); 1204 } 1205 printf("SUCCESS\n\n"); 1206 } 1207 1208 printf("Compare strings with ziplist entries:\n"); 1209 { 1210 zl = createList(); 1211 p = ziplistIndex(zl,0); 1212 if (!ziplistCompare(p,(unsigned char*)"hello",5)) { 1213 printf("ERROR: not \"hello\"\n"); 1214 return 1; 1215 } 1216 if (ziplistCompare(p,(unsigned char*)"hella",5)) { 1217 printf("ERROR: \"hella\"\n"); 1218 return 1; 1219 } 1220 1221 p = ziplistIndex(zl,3); 1222 if (!ziplistCompare(p,(unsigned char*)"1024",4)) { 1223 printf("ERROR: not \"1024\"\n"); 1224 return 1; 1225 } 1226 if (ziplistCompare(p,(unsigned char*)"1025",4)) { 1227 printf("ERROR: \"1025\"\n"); 1228 return 1; 1229 } 1230 printf("SUCCESS\n\n"); 1231 } 1232 1233 printf("Stress with random payloads of different encoding:\n"); 1234 { 1235 int i,j,len,where; 1236 unsigned char *p; 1237 char buf[1024]; 1238 list *ref; 1239 listNode *refnode; 1240 1241 /* Hold temp vars from ziplist */ 1242 unsigned char *sstr; 1243 unsigned int slen; 1244 long long sval; 1245 1246 /* In the regression for the cascade bug, it was triggered 1247 * with a random seed of 2. */ 1248 srand(2); 1249 1250 for (i = 0; i < 20000; i++) { 1251 zl = ziplistNew(); 1252 ref = listCreate(); 1253 listSetFreeMethod(ref,sdsfree); 1254 len = rand() % 256; 1255 1256 /* Create lists */ 1257 for (j = 0; j < len; j++) { 1258 where = (rand() & 1) ? ZIPLIST_HEAD : ZIPLIST_TAIL; 1259 switch(rand() % 4) { 1260 case 0: 1261 sprintf(buf,"%lld",(0LL + rand()) >> 20); 1262 break; 1263 case 1: 1264 sprintf(buf,"%lld",(0LL + rand())); 1265 break; 1266 case 2: 1267 sprintf(buf,"%lld",(0LL + rand()) << 20); 1268 break; 1269 case 3: 1270 randstring(buf,0,256); 1271 break; 1272 default: 1273 assert(NULL); 1274 } 1275 1276 /* Add to ziplist */ 1277 zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), where); 1278 1279 /* Add to reference list */ 1280 if (where == ZIPLIST_HEAD) { 1281 listAddNodeHead(ref,sdsnew(buf)); 1282 } else if (where == ZIPLIST_TAIL) { 1283 listAddNodeTail(ref,sdsnew(buf)); 1284 } else { 1285 assert(NULL); 1286 } 1287 } 1288 1289 assert(listLength(ref) == ziplistLen(zl)); 1290 for (j = 0; j < len; j++) { 1291 /* Naive way to get elements, but similar to the stresser 1292 * executed from the Tcl test suite. */ 1293 p = ziplistIndex(zl,j); 1294 refnode = listIndex(ref,j); 1295 1296 assert(ziplistGet(p,&sstr,&slen,&sval)); 1297 if (sstr == NULL) { 1298 sprintf(buf,"%lld",sval); 1299 } else { 1300 memcpy(buf,sstr,slen); 1301 buf[slen] = '\0'; 1302 } 1303 assert(strcmp(buf,listNodeValue(refnode)) == 0); 1304 } 1305 zfree(zl); 1306 listRelease(ref); 1307 } 1308 printf("SUCCESS\n\n"); 1309 } 1310 1311 printf("Stress with variable ziplist size:\n"); 1312 { 1313 stress(ZIPLIST_HEAD,100000,16384,256); 1314 stress(ZIPLIST_TAIL,100000,16384,256); 1315 } 1316 1317 return 0; 1318 } 1319 1320 #endif 1321