1 /* quicklist.c - A doubly linked list of ziplists 2 * 3 * Copyright (c) 2014, Matt Stancliff <[email protected]> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * * Redistributions of source code must start the above copyright notice, 10 * this quicklist of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above copyright 12 * notice, this quicklist of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * * Neither the name of Redis nor the names of its contributors may be used 15 * to endorse or promote products derived from this software without 16 * specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include <string.h> /* for memcpy */ 32 #include "quicklist.h" 33 #include "zmalloc.h" 34 #include "ziplist.h" 35 #include "util.h" /* for ll2string */ 36 #include "lzf.h" 37 38 #if defined(REDIS_TEST) || defined(REDIS_TEST_VERBOSE) 39 #include <stdio.h> /* for printf (debug printing), snprintf (genstr) */ 40 #endif 41 42 #ifndef REDIS_STATIC 43 #define REDIS_STATIC static 44 #endif 45 46 /* Optimization levels for size-based filling */ 47 static const size_t optimization_level[] = {4096, 8192, 16384, 32768, 65536}; 48 49 /* Maximum size in bytes of any multi-element ziplist. 50 * Larger values will live in their own isolated ziplists. */ 51 #define SIZE_SAFETY_LIMIT 8192 52 53 /* Minimum ziplist size in bytes for attempting compression. */ 54 #define MIN_COMPRESS_BYTES 48 55 56 /* Minimum size reduction in bytes to store compressed quicklistNode data. 57 * This also prevents us from storing compression if the compression 58 * resulted in a larger size than the original data. */ 59 #define MIN_COMPRESS_IMPROVE 8 60 61 /* If not verbose testing, remove all debug printing. */ 62 #ifndef REDIS_TEST_VERBOSE 63 #define D(...) 64 #else 65 #define D(...) \ 66 do { \ 67 printf("%s:%s:%d:\t", __FILE__, __FUNCTION__, __LINE__); \ 68 printf(__VA_ARGS__); \ 69 printf("\n"); \ 70 } while (0); 71 #endif 72 73 /* Simple way to give quicklistEntry structs default values with one call. */ 74 #define initEntry(e) \ 75 do { \ 76 (e)->zi = (e)->value = NULL; \ 77 (e)->longval = -123456789; \ 78 (e)->quicklist = NULL; \ 79 (e)->node = NULL; \ 80 (e)->offset = 123456789; \ 81 (e)->sz = 0; \ 82 } while (0) 83 84 #if __GNUC__ >= 3 85 #define likely(x) __builtin_expect(!!(x), 1) 86 #define unlikely(x) __builtin_expect(!!(x), 0) 87 #else 88 #define likely(x) (x) 89 #define unlikely(x) (x) 90 #endif 91 92 /* Create a new quicklist. 93 * Free with quicklistRelease(). */ 94 quicklist *quicklistCreate(void) { 95 struct quicklist *quicklist; 96 97 quicklist = zmalloc(sizeof(*quicklist)); 98 quicklist->head = quicklist->tail = NULL; 99 quicklist->len = 0; 100 quicklist->count = 0; 101 quicklist->compress = 0; 102 quicklist->fill = -2; 103 return quicklist; 104 } 105 106 #define COMPRESS_MAX (1 << 16) 107 void quicklistSetCompressDepth(quicklist *quicklist, int compress) { 108 if (compress > COMPRESS_MAX) { 109 compress = COMPRESS_MAX; 110 } else if (compress < 0) { 111 compress = 0; 112 } 113 quicklist->compress = compress; 114 } 115 116 #define FILL_MAX (1 << 15) 117 void quicklistSetFill(quicklist *quicklist, int fill) { 118 if (fill > FILL_MAX) { 119 fill = FILL_MAX; 120 } else if (fill < -5) { 121 fill = -5; 122 } 123 quicklist->fill = fill; 124 } 125 126 void quicklistSetOptions(quicklist *quicklist, int fill, int depth) { 127 quicklistSetFill(quicklist, fill); 128 quicklistSetCompressDepth(quicklist, depth); 129 } 130 131 /* Create a new quicklist with some default parameters. */ 132 quicklist *quicklistNew(int fill, int compress) { 133 quicklist *quicklist = quicklistCreate(); 134 quicklistSetOptions(quicklist, fill, compress); 135 return quicklist; 136 } 137 138 REDIS_STATIC quicklistNode *quicklistCreateNode(void) { 139 quicklistNode *node; 140 node = zmalloc(sizeof(*node)); 141 node->zl = NULL; 142 node->count = 0; 143 node->sz = 0; 144 node->next = node->prev = NULL; 145 node->encoding = QUICKLIST_NODE_ENCODING_RAW; 146 node->container = QUICKLIST_NODE_CONTAINER_ZIPLIST; 147 node->recompress = 0; 148 return node; 149 } 150 151 /* Return cached quicklist count */ 152 unsigned long quicklistCount(const quicklist *ql) { return ql->count; } 153 154 /* Free entire quicklist. */ 155 void quicklistRelease(quicklist *quicklist) { 156 unsigned long len; 157 quicklistNode *current, *next; 158 159 current = quicklist->head; 160 len = quicklist->len; 161 while (len--) { 162 next = current->next; 163 164 zfree(current->zl); 165 quicklist->count -= current->count; 166 167 zfree(current); 168 169 quicklist->len--; 170 current = next; 171 } 172 zfree(quicklist); 173 } 174 175 /* Compress the ziplist in 'node' and update encoding details. 176 * Returns 1 if ziplist compressed successfully. 177 * Returns 0 if compression failed or if ziplist too small to compress. */ 178 REDIS_STATIC int __quicklistCompressNode(quicklistNode *node) { 179 #ifdef REDIS_TEST 180 node->attempted_compress = 1; 181 #endif 182 183 /* Don't bother compressing small values */ 184 if (node->sz < MIN_COMPRESS_BYTES) 185 return 0; 186 187 quicklistLZF *lzf = zmalloc(sizeof(*lzf) + node->sz); 188 189 /* Cancel if compression fails or doesn't compress small enough */ 190 if (((lzf->sz = lzf_compress(node->zl, node->sz, lzf->compressed, 191 node->sz)) == 0) || 192 lzf->sz + MIN_COMPRESS_IMPROVE >= node->sz) { 193 /* lzf_compress aborts/rejects compression if value not compressable. */ 194 zfree(lzf); 195 return 0; 196 } 197 lzf = zrealloc(lzf, sizeof(*lzf) + lzf->sz); 198 zfree(node->zl); 199 node->zl = (unsigned char *)lzf; 200 node->encoding = QUICKLIST_NODE_ENCODING_LZF; 201 node->recompress = 0; 202 return 1; 203 } 204 205 /* Compress only uncompressed nodes. */ 206 #define quicklistCompressNode(_node) \ 207 do { \ 208 if ((_node) && (_node)->encoding == QUICKLIST_NODE_ENCODING_RAW) { \ 209 __quicklistCompressNode((_node)); \ 210 } \ 211 } while (0) 212 213 /* Uncompress the ziplist in 'node' and update encoding details. 214 * Returns 1 on successful decode, 0 on failure to decode. */ 215 REDIS_STATIC int __quicklistDecompressNode(quicklistNode *node) { 216 #ifdef REDIS_TEST 217 node->attempted_compress = 0; 218 #endif 219 220 void *decompressed = zmalloc(node->sz); 221 quicklistLZF *lzf = (quicklistLZF *)node->zl; 222 if (lzf_decompress(lzf->compressed, lzf->sz, decompressed, node->sz) == 0) { 223 /* Someone requested decompress, but we can't decompress. Not good. */ 224 zfree(decompressed); 225 return 0; 226 } 227 zfree(lzf); 228 node->zl = decompressed; 229 node->encoding = QUICKLIST_NODE_ENCODING_RAW; 230 return 1; 231 } 232 233 /* Decompress only compressed nodes. */ 234 #define quicklistDecompressNode(_node) \ 235 do { \ 236 if ((_node) && (_node)->encoding == QUICKLIST_NODE_ENCODING_LZF) { \ 237 __quicklistDecompressNode((_node)); \ 238 } \ 239 } while (0) 240 241 /* Force node to not be immediately re-compresable */ 242 #define quicklistDecompressNodeForUse(_node) \ 243 do { \ 244 if ((_node) && (_node)->encoding == QUICKLIST_NODE_ENCODING_LZF) { \ 245 __quicklistDecompressNode((_node)); \ 246 (_node)->recompress = 1; \ 247 } \ 248 } while (0) 249 250 /* Extract the raw LZF data from this quicklistNode. 251 * Pointer to LZF data is assigned to '*data'. 252 * Return value is the length of compressed LZF data. */ 253 size_t quicklistGetLzf(const quicklistNode *node, void **data) { 254 quicklistLZF *lzf = (quicklistLZF *)node->zl; 255 *data = lzf->compressed; 256 return lzf->sz; 257 } 258 259 #define quicklistAllowsCompression(_ql) ((_ql)->compress != 0) 260 261 /* Force 'quicklist' to meet compression guidelines set by compress depth. 262 * The only way to guarantee interior nodes get compressed is to iterate 263 * to our "interior" compress depth then compress the next node we find. 264 * If compress depth is larger than the entire list, we return immediately. */ 265 REDIS_STATIC void __quicklistCompress(const quicklist *quicklist, 266 quicklistNode *node) { 267 /* If length is less than our compress depth (from both sides), 268 * we can't compress anything. */ 269 if (!quicklistAllowsCompression(quicklist) || 270 quicklist->len < (unsigned int)(quicklist->compress * 2)) 271 return; 272 273 #if 0 274 /* Optimized cases for small depth counts */ 275 if (quicklist->compress == 1) { 276 quicklistNode *h = quicklist->head, *t = quicklist->tail; 277 quicklistDecompressNode(h); 278 quicklistDecompressNode(t); 279 if (h != node && t != node) 280 quicklistCompressNode(node); 281 return; 282 } else if (quicklist->compress == 2) { 283 quicklistNode *h = quicklist->head, *hn = h->next, *hnn = hn->next; 284 quicklistNode *t = quicklist->tail, *tp = t->prev, *tpp = tp->prev; 285 quicklistDecompressNode(h); 286 quicklistDecompressNode(hn); 287 quicklistDecompressNode(t); 288 quicklistDecompressNode(tp); 289 if (h != node && hn != node && t != node && tp != node) { 290 quicklistCompressNode(node); 291 } 292 if (hnn != t) { 293 quicklistCompressNode(hnn); 294 } 295 if (tpp != h) { 296 quicklistCompressNode(tpp); 297 } 298 return; 299 } 300 #endif 301 302 /* Iterate until we reach compress depth for both sides of the list.a 303 * Note: because we do length checks at the *top* of this function, 304 * we can skip explicit null checks below. Everything exists. */ 305 quicklistNode *forward = quicklist->head; 306 quicklistNode *reverse = quicklist->tail; 307 int depth = 0; 308 int in_depth = 0; 309 while (depth++ < quicklist->compress) { 310 quicklistDecompressNode(forward); 311 quicklistDecompressNode(reverse); 312 313 if (forward == node || reverse == node) 314 in_depth = 1; 315 316 if (forward == reverse) 317 return; 318 319 forward = forward->next; 320 reverse = reverse->prev; 321 } 322 323 if (!in_depth) 324 quicklistCompressNode(node); 325 326 if (depth > 2) { 327 /* At this point, forward and reverse are one node beyond depth */ 328 quicklistCompressNode(forward); 329 quicklistCompressNode(reverse); 330 } 331 } 332 333 #define quicklistCompress(_ql, _node) \ 334 do { \ 335 if ((_node)->recompress) \ 336 quicklistCompressNode((_node)); \ 337 else \ 338 __quicklistCompress((_ql), (_node)); \ 339 } while (0) 340 341 /* If we previously used quicklistDecompressNodeForUse(), just recompress. */ 342 #define quicklistRecompressOnly(_ql, _node) \ 343 do { \ 344 if ((_node)->recompress) \ 345 quicklistCompressNode((_node)); \ 346 } while (0) 347 348 /* Insert 'new_node' after 'old_node' if 'after' is 1. 349 * Insert 'new_node' before 'old_node' if 'after' is 0. 350 * Note: 'new_node' is *always* uncompressed, so if we assign it to 351 * head or tail, we do not need to uncompress it. */ 352 REDIS_STATIC void __quicklistInsertNode(quicklist *quicklist, 353 quicklistNode *old_node, 354 quicklistNode *new_node, int after) { 355 if (after) { 356 new_node->prev = old_node; 357 if (old_node) { 358 new_node->next = old_node->next; 359 if (old_node->next) 360 old_node->next->prev = new_node; 361 old_node->next = new_node; 362 } 363 if (quicklist->tail == old_node) 364 quicklist->tail = new_node; 365 } else { 366 new_node->next = old_node; 367 if (old_node) { 368 new_node->prev = old_node->prev; 369 if (old_node->prev) 370 old_node->prev->next = new_node; 371 old_node->prev = new_node; 372 } 373 if (quicklist->head == old_node) 374 quicklist->head = new_node; 375 } 376 /* If this insert creates the only element so far, initialize head/tail. */ 377 if (quicklist->len == 0) { 378 quicklist->head = quicklist->tail = new_node; 379 } 380 381 if (old_node) 382 quicklistCompress(quicklist, old_node); 383 384 quicklist->len++; 385 } 386 387 /* Wrappers for node inserting around existing node. */ 388 REDIS_STATIC void _quicklistInsertNodeBefore(quicklist *quicklist, 389 quicklistNode *old_node, 390 quicklistNode *new_node) { 391 __quicklistInsertNode(quicklist, old_node, new_node, 0); 392 } 393 394 REDIS_STATIC void _quicklistInsertNodeAfter(quicklist *quicklist, 395 quicklistNode *old_node, 396 quicklistNode *new_node) { 397 __quicklistInsertNode(quicklist, old_node, new_node, 1); 398 } 399 400 REDIS_STATIC int 401 _quicklistNodeSizeMeetsOptimizationRequirement(const size_t sz, 402 const int fill) { 403 if (fill >= 0) 404 return 0; 405 406 size_t offset = (-fill) - 1; 407 if (offset < (sizeof(optimization_level) / sizeof(*optimization_level))) { 408 if (sz <= optimization_level[offset]) { 409 return 1; 410 } else { 411 return 0; 412 } 413 } else { 414 return 0; 415 } 416 } 417 418 #define sizeMeetsSafetyLimit(sz) ((sz) <= SIZE_SAFETY_LIMIT) 419 420 REDIS_STATIC int _quicklistNodeAllowInsert(const quicklistNode *node, 421 const int fill, const size_t sz) { 422 if (unlikely(!node)) 423 return 0; 424 425 int ziplist_overhead; 426 /* size of previous offset */ 427 if (sz < 254) 428 ziplist_overhead = 1; 429 else 430 ziplist_overhead = 5; 431 432 /* size of forward offset */ 433 if (sz < 64) 434 ziplist_overhead += 1; 435 else if (likely(sz < 16384)) 436 ziplist_overhead += 2; 437 else 438 ziplist_overhead += 5; 439 440 /* new_sz overestimates if 'sz' encodes to an integer type */ 441 unsigned int new_sz = node->sz + sz + ziplist_overhead; 442 if (likely(_quicklistNodeSizeMeetsOptimizationRequirement(new_sz, fill))) 443 return 1; 444 else if (!sizeMeetsSafetyLimit(new_sz)) 445 return 0; 446 else if ((int)node->count < fill) 447 return 1; 448 else 449 return 0; 450 } 451 452 REDIS_STATIC int _quicklistNodeAllowMerge(const quicklistNode *a, 453 const quicklistNode *b, 454 const int fill) { 455 if (!a || !b) 456 return 0; 457 458 /* approximate merged ziplist size (- 11 to remove one ziplist 459 * header/trailer) */ 460 unsigned int merge_sz = a->sz + b->sz - 11; 461 if (likely(_quicklistNodeSizeMeetsOptimizationRequirement(merge_sz, fill))) 462 return 1; 463 else if (!sizeMeetsSafetyLimit(merge_sz)) 464 return 0; 465 else if ((int)(a->count + b->count) <= fill) 466 return 1; 467 else 468 return 0; 469 } 470 471 #define quicklistNodeUpdateSz(node) \ 472 do { \ 473 (node)->sz = ziplistBlobLen((node)->zl); \ 474 } while (0) 475 476 /* Add new entry to head node of quicklist. 477 * 478 * Returns 0 if used existing head. 479 * Returns 1 if new head created. */ 480 int quicklistPushHead(quicklist *quicklist, void *value, size_t sz) { 481 quicklistNode *orig_head = quicklist->head; 482 if (likely( 483 _quicklistNodeAllowInsert(quicklist->head, quicklist->fill, sz))) { 484 quicklist->head->zl = 485 ziplistPush(quicklist->head->zl, value, sz, ZIPLIST_HEAD); 486 quicklistNodeUpdateSz(quicklist->head); 487 } else { 488 quicklistNode *node = quicklistCreateNode(); 489 node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_HEAD); 490 491 quicklistNodeUpdateSz(node); 492 _quicklistInsertNodeBefore(quicklist, quicklist->head, node); 493 } 494 quicklist->count++; 495 quicklist->head->count++; 496 return (orig_head != quicklist->head); 497 } 498 499 /* Add new entry to tail node of quicklist. 500 * 501 * Returns 0 if used existing tail. 502 * Returns 1 if new tail created. */ 503 int quicklistPushTail(quicklist *quicklist, void *value, size_t sz) { 504 quicklistNode *orig_tail = quicklist->tail; 505 if (likely( 506 _quicklistNodeAllowInsert(quicklist->tail, quicklist->fill, sz))) { 507 quicklist->tail->zl = 508 ziplistPush(quicklist->tail->zl, value, sz, ZIPLIST_TAIL); 509 quicklistNodeUpdateSz(quicklist->tail); 510 } else { 511 quicklistNode *node = quicklistCreateNode(); 512 node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_TAIL); 513 514 quicklistNodeUpdateSz(node); 515 _quicklistInsertNodeAfter(quicklist, quicklist->tail, node); 516 } 517 quicklist->count++; 518 quicklist->tail->count++; 519 return (orig_tail != quicklist->tail); 520 } 521 522 /* Create new node consisting of a pre-formed ziplist. 523 * Used for loading RDBs where entire ziplists have been stored 524 * to be retrieved later. */ 525 void quicklistAppendZiplist(quicklist *quicklist, unsigned char *zl) { 526 quicklistNode *node = quicklistCreateNode(); 527 528 node->zl = zl; 529 node->count = ziplistLen(node->zl); 530 node->sz = ziplistBlobLen(zl); 531 532 _quicklistInsertNodeAfter(quicklist, quicklist->tail, node); 533 quicklist->count += node->count; 534 } 535 536 /* Append all values of ziplist 'zl' individually into 'quicklist'. 537 * 538 * This allows us to restore old RDB ziplists into new quicklists 539 * with smaller ziplist sizes than the saved RDB ziplist. 540 * 541 * Returns 'quicklist' argument. Frees passed-in ziplist 'zl' */ 542 quicklist *quicklistAppendValuesFromZiplist(quicklist *quicklist, 543 unsigned char *zl) { 544 unsigned char *value; 545 unsigned int sz; 546 long long longval; 547 char longstr[32] = {0}; 548 549 unsigned char *p = ziplistIndex(zl, 0); 550 while (ziplistGet(p, &value, &sz, &longval)) { 551 if (!value) { 552 /* Write the longval as a string so we can re-add it */ 553 sz = ll2string(longstr, sizeof(longstr), longval); 554 value = (unsigned char *)longstr; 555 } 556 quicklistPushTail(quicklist, value, sz); 557 p = ziplistNext(zl, p); 558 } 559 zfree(zl); 560 return quicklist; 561 } 562 563 /* Create new (potentially multi-node) quicklist from a single existing ziplist. 564 * 565 * Returns new quicklist. Frees passed-in ziplist 'zl'. */ 566 quicklist *quicklistCreateFromZiplist(int fill, int compress, 567 unsigned char *zl) { 568 return quicklistAppendValuesFromZiplist(quicklistNew(fill, compress), zl); 569 } 570 571 #define quicklistDeleteIfEmpty(ql, n) \ 572 do { \ 573 if ((n)->count == 0) { \ 574 __quicklistDelNode((ql), (n)); \ 575 (n) = NULL; \ 576 } \ 577 } while (0) 578 579 REDIS_STATIC void __quicklistDelNode(quicklist *quicklist, 580 quicklistNode *node) { 581 if (node->next) 582 node->next->prev = node->prev; 583 if (node->prev) 584 node->prev->next = node->next; 585 586 if (node == quicklist->tail) { 587 quicklist->tail = node->prev; 588 } 589 590 if (node == quicklist->head) { 591 quicklist->head = node->next; 592 } 593 594 /* If we deleted a node within our compress depth, we 595 * now have compressed nodes needing to be decompressed. */ 596 __quicklistCompress(quicklist, NULL); 597 598 quicklist->count -= node->count; 599 600 zfree(node->zl); 601 zfree(node); 602 quicklist->len--; 603 } 604 605 /* Delete one entry from list given the node for the entry and a pointer 606 * to the entry in the node. 607 * 608 * Note: quicklistDelIndex() *requires* uncompressed nodes because you 609 * already had to get *p from an uncompressed node somewhere. 610 * 611 * Returns 1 if the entire node was deleted, 0 if node still exists. 612 * Also updates in/out param 'p' with the next offset in the ziplist. */ 613 REDIS_STATIC int quicklistDelIndex(quicklist *quicklist, quicklistNode *node, 614 unsigned char **p) { 615 int gone = 0; 616 617 node->zl = ziplistDelete(node->zl, p); 618 node->count--; 619 if (node->count == 0) { 620 gone = 1; 621 __quicklistDelNode(quicklist, node); 622 } else { 623 quicklistNodeUpdateSz(node); 624 } 625 quicklist->count--; 626 /* If we deleted the node, the original node is no longer valid */ 627 return gone ? 1 : 0; 628 } 629 630 /* Delete one element represented by 'entry' 631 * 632 * 'entry' stores enough metadata to delete the proper position in 633 * the correct ziplist in the correct quicklist node. */ 634 void quicklistDelEntry(quicklistIter *iter, quicklistEntry *entry) { 635 quicklistNode *prev = entry->node->prev; 636 quicklistNode *next = entry->node->next; 637 int deleted_node = quicklistDelIndex((quicklist *)entry->quicklist, 638 entry->node, &entry->zi); 639 640 /* after delete, the zi is now invalid for any future usage. */ 641 iter->zi = NULL; 642 643 /* If current node is deleted, we must update iterator node and offset. */ 644 if (deleted_node) { 645 if (iter->direction == AL_START_HEAD) { 646 iter->current = next; 647 iter->offset = 0; 648 } else if (iter->direction == AL_START_TAIL) { 649 iter->current = prev; 650 iter->offset = -1; 651 } 652 } 653 /* else if (!deleted_node), no changes needed. 654 * we already reset iter->zi above, and the existing iter->offset 655 * doesn't move again because: 656 * - [1, 2, 3] => delete offset 1 => [1, 3]: next element still offset 1 657 * - [1, 2, 3] => delete offset 0 => [2, 3]: next element still offset 0 658 * if we deleted the last element at offet N and now 659 * length of this ziplist is N-1, the next call into 660 * quicklistNext() will jump to the next node. */ 661 } 662 663 /* Replace quicklist entry at offset 'index' by 'data' with length 'sz'. 664 * 665 * Returns 1 if replace happened. 666 * Returns 0 if replace failed and no changes happened. */ 667 int quicklistReplaceAtIndex(quicklist *quicklist, long index, void *data, 668 int sz) { 669 quicklistEntry entry; 670 if (likely(quicklistIndex(quicklist, index, &entry))) { 671 /* quicklistIndex provides an uncompressed node */ 672 entry.node->zl = ziplistDelete(entry.node->zl, &entry.zi); 673 entry.node->zl = ziplistInsert(entry.node->zl, entry.zi, data, sz); 674 quicklistNodeUpdateSz(entry.node); 675 quicklistCompress(quicklist, entry.node); 676 return 1; 677 } else { 678 return 0; 679 } 680 } 681 682 /* Given two nodes, try to merge their ziplists. 683 * 684 * This helps us not have a quicklist with 3 element ziplists if 685 * our fill factor can handle much higher levels. 686 * 687 * Note: 'a' must be to the LEFT of 'b'. 688 * 689 * After calling this function, both 'a' and 'b' should be considered 690 * unusable. The return value from this function must be used 691 * instead of re-using any of the quicklistNode input arguments. 692 * 693 * Returns the input node picked to merge against or NULL if 694 * merging was not possible. */ 695 REDIS_STATIC quicklistNode *_quicklistZiplistMerge(quicklist *quicklist, 696 quicklistNode *a, 697 quicklistNode *b) { 698 D("Requested merge (a,b) (%u, %u)", a->count, b->count); 699 700 quicklistDecompressNode(a); 701 quicklistDecompressNode(b); 702 if ((ziplistMerge(&a->zl, &b->zl))) { 703 /* We merged ziplists! Now remove the unused quicklistNode. */ 704 quicklistNode *keep = NULL, *nokeep = NULL; 705 if (!a->zl) { 706 nokeep = a; 707 keep = b; 708 } else if (!b->zl) { 709 nokeep = b; 710 keep = a; 711 } 712 keep->count = ziplistLen(keep->zl); 713 quicklistNodeUpdateSz(keep); 714 715 nokeep->count = 0; 716 __quicklistDelNode(quicklist, nokeep); 717 quicklistCompress(quicklist, keep); 718 return keep; 719 } else { 720 /* else, the merge returned NULL and nothing changed. */ 721 return NULL; 722 } 723 } 724 725 /* Attempt to merge ziplists within two nodes on either side of 'center'. 726 * 727 * We attempt to merge: 728 * - (center->prev->prev, center->prev) 729 * - (center->next, center->next->next) 730 * - (center->prev, center) 731 * - (center, center->next) 732 */ 733 REDIS_STATIC void _quicklistMergeNodes(quicklist *quicklist, 734 quicklistNode *center) { 735 int fill = quicklist->fill; 736 quicklistNode *prev, *prev_prev, *next, *next_next, *target; 737 prev = prev_prev = next = next_next = target = NULL; 738 739 if (center->prev) { 740 prev = center->prev; 741 if (center->prev->prev) 742 prev_prev = center->prev->prev; 743 } 744 745 if (center->next) { 746 next = center->next; 747 if (center->next->next) 748 next_next = center->next->next; 749 } 750 751 /* Try to merge prev_prev and prev */ 752 if (_quicklistNodeAllowMerge(prev, prev_prev, fill)) { 753 _quicklistZiplistMerge(quicklist, prev_prev, prev); 754 prev_prev = prev = NULL; /* they could have moved, invalidate them. */ 755 } 756 757 /* Try to merge next and next_next */ 758 if (_quicklistNodeAllowMerge(next, next_next, fill)) { 759 _quicklistZiplistMerge(quicklist, next, next_next); 760 next = next_next = NULL; /* they could have moved, invalidate them. */ 761 } 762 763 /* Try to merge center node and previous node */ 764 if (_quicklistNodeAllowMerge(center, center->prev, fill)) { 765 target = _quicklistZiplistMerge(quicklist, center->prev, center); 766 center = NULL; /* center could have been deleted, invalidate it. */ 767 } else { 768 /* else, we didn't merge here, but target needs to be valid below. */ 769 target = center; 770 } 771 772 /* Use result of center merge (or original) to merge with next node. */ 773 if (_quicklistNodeAllowMerge(target, target->next, fill)) { 774 _quicklistZiplistMerge(quicklist, target, target->next); 775 } 776 } 777 778 /* Split 'node' into two parts, parameterized by 'offset' and 'after'. 779 * 780 * The 'after' argument controls which quicklistNode gets returned. 781 * If 'after'==1, returned node has elements after 'offset'. 782 * input node keeps elements up to 'offset', including 'offset'. 783 * If 'after'==0, returned node has elements up to 'offset', including 'offset'. 784 * input node keeps elements after 'offset'. 785 * 786 * If 'after'==1, returned node will have elements _after_ 'offset'. 787 * The returned node will have elements [OFFSET+1, END]. 788 * The input node keeps elements [0, OFFSET]. 789 * 790 * If 'after'==0, returned node will keep elements up to and including 'offset'. 791 * The returned node will have elements [0, OFFSET]. 792 * The input node keeps elements [OFFSET+1, END]. 793 * 794 * The input node keeps all elements not taken by the returned node. 795 * 796 * Returns newly created node or NULL if split not possible. */ 797 REDIS_STATIC quicklistNode *_quicklistSplitNode(quicklistNode *node, int offset, 798 int after) { 799 size_t zl_sz = node->sz; 800 801 quicklistNode *new_node = quicklistCreateNode(); 802 new_node->zl = zmalloc(zl_sz); 803 804 /* Copy original ziplist so we can split it */ 805 memcpy(new_node->zl, node->zl, zl_sz); 806 807 /* -1 here means "continue deleting until the list ends" */ 808 int orig_start = after ? offset + 1 : 0; 809 int orig_extent = after ? -1 : offset; 810 int new_start = after ? 0 : offset; 811 int new_extent = after ? offset + 1 : -1; 812 813 D("After %d (%d); ranges: [%d, %d], [%d, %d]", after, offset, orig_start, 814 orig_extent, new_start, new_extent); 815 816 node->zl = ziplistDeleteRange(node->zl, orig_start, orig_extent); 817 node->count = ziplistLen(node->zl); 818 quicklistNodeUpdateSz(node); 819 820 new_node->zl = ziplistDeleteRange(new_node->zl, new_start, new_extent); 821 new_node->count = ziplistLen(new_node->zl); 822 quicklistNodeUpdateSz(new_node); 823 824 D("After split lengths: orig (%d), new (%d)", node->count, new_node->count); 825 return new_node; 826 } 827 828 /* Insert a new entry before or after existing entry 'entry'. 829 * 830 * If after==1, the new value is inserted after 'entry', otherwise 831 * the new value is inserted before 'entry'. */ 832 REDIS_STATIC void _quicklistInsert(quicklist *quicklist, quicklistEntry *entry, 833 void *value, const size_t sz, int after) { 834 int full = 0, at_tail = 0, at_head = 0, full_next = 0, full_prev = 0; 835 int fill = quicklist->fill; 836 quicklistNode *node = entry->node; 837 quicklistNode *new_node = NULL; 838 839 if (!node) { 840 /* we have no reference node, so let's create only node in the list */ 841 D("No node given!"); 842 new_node = quicklistCreateNode(); 843 new_node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_HEAD); 844 __quicklistInsertNode(quicklist, NULL, new_node, after); 845 new_node->count++; 846 quicklist->count++; 847 return; 848 } 849 850 /* Populate accounting flags for easier boolean checks later */ 851 if (!_quicklistNodeAllowInsert(node, fill, sz)) { 852 D("Current node is full with count %d with requested fill %lu", 853 node->count, fill); 854 full = 1; 855 } 856 857 if (after && (entry->offset == node->count)) { 858 D("At Tail of current ziplist"); 859 at_tail = 1; 860 if (!_quicklistNodeAllowInsert(node->next, fill, sz)) { 861 D("Next node is full too."); 862 full_next = 1; 863 } 864 } 865 866 if (!after && (entry->offset == 0)) { 867 D("At Head"); 868 at_head = 1; 869 if (!_quicklistNodeAllowInsert(node->prev, fill, sz)) { 870 D("Prev node is full too."); 871 full_prev = 1; 872 } 873 } 874 875 /* Now determine where and how to insert the new element */ 876 if (!full && after) { 877 D("Not full, inserting after current position."); 878 quicklistDecompressNodeForUse(node); 879 unsigned char *next = ziplistNext(node->zl, entry->zi); 880 if (next == NULL) { 881 node->zl = ziplistPush(node->zl, value, sz, ZIPLIST_TAIL); 882 } else { 883 node->zl = ziplistInsert(node->zl, next, value, sz); 884 } 885 node->count++; 886 quicklistNodeUpdateSz(node); 887 quicklistRecompressOnly(quicklist, node); 888 } else if (!full && !after) { 889 D("Not full, inserting before current position."); 890 quicklistDecompressNodeForUse(node); 891 node->zl = ziplistInsert(node->zl, entry->zi, value, sz); 892 node->count++; 893 quicklistNodeUpdateSz(node); 894 quicklistRecompressOnly(quicklist, node); 895 } else if (full && at_tail && node->next && !full_next && after) { 896 /* If we are: at tail, next has free space, and inserting after: 897 * - insert entry at head of next node. */ 898 D("Full and tail, but next isn't full; inserting next node head"); 899 new_node = node->next; 900 quicklistDecompressNodeForUse(new_node); 901 new_node->zl = ziplistPush(new_node->zl, value, sz, ZIPLIST_HEAD); 902 new_node->count++; 903 quicklistNodeUpdateSz(new_node); 904 quicklistRecompressOnly(quicklist, new_node); 905 } else if (full && at_head && node->prev && !full_prev && !after) { 906 /* If we are: at head, previous has free space, and inserting before: 907 * - insert entry at tail of previous node. */ 908 D("Full and head, but prev isn't full, inserting prev node tail"); 909 new_node = node->prev; 910 quicklistDecompressNodeForUse(new_node); 911 new_node->zl = ziplistPush(new_node->zl, value, sz, ZIPLIST_TAIL); 912 new_node->count++; 913 quicklistNodeUpdateSz(new_node); 914 quicklistRecompressOnly(quicklist, new_node); 915 } else if (full && ((at_tail && node->next && full_next && after) || 916 (at_head && node->prev && full_prev && !after))) { 917 /* If we are: full, and our prev/next is full, then: 918 * - create new node and attach to quicklist */ 919 D("\tprovisioning new node..."); 920 new_node = quicklistCreateNode(); 921 new_node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_HEAD); 922 new_node->count++; 923 quicklistNodeUpdateSz(new_node); 924 __quicklistInsertNode(quicklist, node, new_node, after); 925 } else if (full) { 926 /* else, node is full we need to split it. */ 927 /* covers both after and !after cases */ 928 D("\tsplitting node..."); 929 quicklistDecompressNodeForUse(node); 930 new_node = _quicklistSplitNode(node, entry->offset, after); 931 new_node->zl = ziplistPush(new_node->zl, value, sz, 932 after ? ZIPLIST_HEAD : ZIPLIST_TAIL); 933 new_node->count++; 934 quicklistNodeUpdateSz(new_node); 935 __quicklistInsertNode(quicklist, node, new_node, after); 936 _quicklistMergeNodes(quicklist, node); 937 } 938 939 quicklist->count++; 940 } 941 942 void quicklistInsertBefore(quicklist *quicklist, quicklistEntry *entry, 943 void *value, const size_t sz) { 944 _quicklistInsert(quicklist, entry, value, sz, 0); 945 } 946 947 void quicklistInsertAfter(quicklist *quicklist, quicklistEntry *entry, 948 void *value, const size_t sz) { 949 _quicklistInsert(quicklist, entry, value, sz, 1); 950 } 951 952 /* Delete a range of elements from the quicklist. 953 * 954 * elements may span across multiple quicklistNodes, so we 955 * have to be careful about tracking where we start and end. 956 * 957 * Returns 1 if entries were deleted, 0 if nothing was deleted. */ 958 int quicklistDelRange(quicklist *quicklist, const long start, 959 const long count) { 960 if (count <= 0) 961 return 0; 962 963 unsigned long extent = count; /* range is inclusive of start position */ 964 965 if (start >= 0 && extent > (quicklist->count - start)) { 966 /* if requesting delete more elements than exist, limit to list size. */ 967 extent = quicklist->count - start; 968 } else if (start < 0 && extent > (unsigned long)(-start)) { 969 /* else, if at negative offset, limit max size to rest of list. */ 970 extent = -start; /* c.f. LREM -29 29; just delete until end. */ 971 } 972 973 quicklistEntry entry; 974 if (!quicklistIndex(quicklist, start, &entry)) 975 return 0; 976 977 D("Quicklist delete request for start %ld, count %ld, extent: %ld", start, 978 count, extent); 979 quicklistNode *node = entry.node; 980 981 /* iterate over next nodes until everything is deleted. */ 982 while (extent) { 983 quicklistNode *next = node->next; 984 985 unsigned long del; 986 int delete_entire_node = 0; 987 if (entry.offset == 0 && extent >= node->count) { 988 /* If we are deleting more than the count of this node, we 989 * can just delete the entire node without ziplist math. */ 990 delete_entire_node = 1; 991 del = node->count; 992 } else if (entry.offset >= 0 && extent >= node->count) { 993 /* If deleting more nodes after this one, calculate delete based 994 * on size of current node. */ 995 del = node->count - entry.offset; 996 } else if (entry.offset < 0) { 997 /* If offset is negative, we are in the first run of this loop 998 * and we are deleting the entire range 999 * from this start offset to end of list. Since the Negative 1000 * offset is the number of elements until the tail of the list, 1001 * just use it directly as the deletion count. */ 1002 del = -entry.offset; 1003 1004 /* If the positive offset is greater than the remaining extent, 1005 * we only delete the remaining extent, not the entire offset. 1006 */ 1007 if (del > extent) 1008 del = extent; 1009 } else { 1010 /* else, we are deleting less than the extent of this node, so 1011 * use extent directly. */ 1012 del = extent; 1013 } 1014 1015 D("[%ld]: asking to del: %ld because offset: %d; (ENTIRE NODE: %d), " 1016 "node count: %u", 1017 extent, del, entry.offset, delete_entire_node, node->count); 1018 1019 if (delete_entire_node) { 1020 __quicklistDelNode(quicklist, node); 1021 } else { 1022 quicklistDecompressNodeForUse(node); 1023 node->zl = ziplistDeleteRange(node->zl, entry.offset, del); 1024 quicklistNodeUpdateSz(node); 1025 node->count -= del; 1026 quicklist->count -= del; 1027 quicklistDeleteIfEmpty(quicklist, node); 1028 if (node) 1029 quicklistRecompressOnly(quicklist, node); 1030 } 1031 1032 extent -= del; 1033 1034 node = next; 1035 1036 entry.offset = 0; 1037 } 1038 return 1; 1039 } 1040 1041 /* Passthrough to ziplistCompare() */ 1042 int quicklistCompare(unsigned char *p1, unsigned char *p2, int p2_len) { 1043 return ziplistCompare(p1, p2, p2_len); 1044 } 1045 1046 /* Returns a quicklist iterator 'iter'. After the initialization every 1047 * call to quicklistNext() will return the next element of the quicklist. */ 1048 quicklistIter *quicklistGetIterator(const quicklist *quicklist, int direction) { 1049 quicklistIter *iter; 1050 1051 iter = zmalloc(sizeof(*iter)); 1052 1053 if (direction == AL_START_HEAD) { 1054 iter->current = quicklist->head; 1055 iter->offset = 0; 1056 } else if (direction == AL_START_TAIL) { 1057 iter->current = quicklist->tail; 1058 iter->offset = -1; 1059 } 1060 1061 iter->direction = direction; 1062 iter->quicklist = quicklist; 1063 1064 iter->zi = NULL; 1065 1066 return iter; 1067 } 1068 1069 /* Initialize an iterator at a specific offset 'idx' and make the iterator 1070 * return nodes in 'direction' direction. */ 1071 quicklistIter *quicklistGetIteratorAtIdx(const quicklist *quicklist, 1072 const int direction, 1073 const long long idx) { 1074 quicklistEntry entry; 1075 1076 if (quicklistIndex(quicklist, idx, &entry)) { 1077 quicklistIter *base = quicklistGetIterator(quicklist, direction); 1078 base->zi = NULL; 1079 base->current = entry.node; 1080 base->offset = entry.offset; 1081 return base; 1082 } else { 1083 return NULL; 1084 } 1085 } 1086 1087 /* Release iterator. 1088 * If we still have a valid current node, then re-encode current node. */ 1089 void quicklistReleaseIterator(quicklistIter *iter) { 1090 if (iter->current) 1091 quicklistCompress(iter->quicklist, iter->current); 1092 1093 zfree(iter); 1094 } 1095 1096 /* Get next element in iterator. 1097 * 1098 * Note: You must NOT insert into the list while iterating over it. 1099 * You *may* delete from the list while iterating using the 1100 * quicklistDelEntry() function. 1101 * If you insert into the quicklist while iterating, you should 1102 * re-create the iterator after your addition. 1103 * 1104 * iter = quicklistGetIterator(quicklist,<direction>); 1105 * quicklistEntry entry; 1106 * while (quicklistNext(iter, &entry)) { 1107 * if (entry.value) 1108 * [[ use entry.value with entry.sz ]] 1109 * else 1110 * [[ use entry.longval ]] 1111 * } 1112 * 1113 * Populates 'entry' with values for this iteration. 1114 * Returns 0 when iteration is complete or if iteration not possible. 1115 * If return value is 0, the contents of 'entry' are not valid. 1116 */ 1117 int quicklistNext(quicklistIter *iter, quicklistEntry *entry) { 1118 initEntry(entry); 1119 1120 if (!iter) { 1121 D("Returning because no iter!"); 1122 return 0; 1123 } 1124 1125 entry->quicklist = iter->quicklist; 1126 entry->node = iter->current; 1127 1128 if (!iter->current) { 1129 D("Returning because current node is NULL") 1130 return 0; 1131 } 1132 1133 unsigned char *(*nextFn)(unsigned char *, unsigned char *) = NULL; 1134 int offset_update = 0; 1135 1136 if (!iter->zi) { 1137 /* If !zi, use current index. */ 1138 quicklistDecompressNodeForUse(iter->current); 1139 iter->zi = ziplistIndex(iter->current->zl, iter->offset); 1140 } else { 1141 /* else, use existing iterator offset and get prev/next as necessary. */ 1142 if (iter->direction == AL_START_HEAD) { 1143 nextFn = ziplistNext; 1144 offset_update = 1; 1145 } else if (iter->direction == AL_START_TAIL) { 1146 nextFn = ziplistPrev; 1147 offset_update = -1; 1148 } 1149 iter->zi = nextFn(iter->current->zl, iter->zi); 1150 iter->offset += offset_update; 1151 } 1152 1153 entry->zi = iter->zi; 1154 entry->offset = iter->offset; 1155 1156 if (iter->zi) { 1157 /* Populate value from existing ziplist position */ 1158 ziplistGet(entry->zi, &entry->value, &entry->sz, &entry->longval); 1159 return 1; 1160 } else { 1161 /* We ran out of ziplist entries. 1162 * Pick next node, update offset, then re-run retrieval. */ 1163 quicklistCompress(iter->quicklist, iter->current); 1164 if (iter->direction == AL_START_HEAD) { 1165 /* Forward traversal */ 1166 D("Jumping to start of next node"); 1167 iter->current = iter->current->next; 1168 iter->offset = 0; 1169 } else if (iter->direction == AL_START_TAIL) { 1170 /* Reverse traversal */ 1171 D("Jumping to end of previous node"); 1172 iter->current = iter->current->prev; 1173 iter->offset = -1; 1174 } 1175 iter->zi = NULL; 1176 return quicklistNext(iter, entry); 1177 } 1178 } 1179 1180 /* Duplicate the quicklist. 1181 * On success a copy of the original quicklist is returned. 1182 * 1183 * The original quicklist both on success or error is never modified. 1184 * 1185 * Returns newly allocated quicklist. */ 1186 quicklist *quicklistDup(quicklist *orig) { 1187 quicklist *copy; 1188 1189 copy = quicklistNew(orig->fill, orig->compress); 1190 1191 for (quicklistNode *current = orig->head; current; 1192 current = current->next) { 1193 quicklistNode *node = quicklistCreateNode(); 1194 1195 if (current->encoding == QUICKLIST_NODE_ENCODING_LZF) { 1196 quicklistLZF *lzf = (quicklistLZF *)current->zl; 1197 size_t lzf_sz = sizeof(*lzf) + lzf->sz; 1198 node->zl = zmalloc(lzf_sz); 1199 memcpy(node->zl, current->zl, lzf_sz); 1200 } else if (current->encoding == QUICKLIST_NODE_ENCODING_RAW) { 1201 node->zl = zmalloc(current->sz); 1202 memcpy(node->zl, current->zl, current->sz); 1203 } 1204 1205 node->count = current->count; 1206 copy->count += node->count; 1207 node->sz = current->sz; 1208 node->encoding = current->encoding; 1209 1210 _quicklistInsertNodeAfter(copy, copy->tail, node); 1211 } 1212 1213 /* copy->count must equal orig->count here */ 1214 return copy; 1215 } 1216 1217 /* Populate 'entry' with the element at the specified zero-based index 1218 * where 0 is the head, 1 is the element next to head 1219 * and so on. Negative integers are used in order to count 1220 * from the tail, -1 is the last element, -2 the penultimate 1221 * and so on. If the index is out of range 0 is returned. 1222 * 1223 * Returns 1 if element found 1224 * Returns 0 if element not found */ 1225 int quicklistIndex(const quicklist *quicklist, const long long idx, 1226 quicklistEntry *entry) { 1227 quicklistNode *n; 1228 unsigned long long accum = 0; 1229 unsigned long long index; 1230 int forward = idx < 0 ? 0 : 1; /* < 0 -> reverse, 0+ -> forward */ 1231 1232 initEntry(entry); 1233 entry->quicklist = quicklist; 1234 1235 if (!forward) { 1236 index = (-idx) - 1; 1237 n = quicklist->tail; 1238 } else { 1239 index = idx; 1240 n = quicklist->head; 1241 } 1242 1243 if (index >= quicklist->count) 1244 return 0; 1245 1246 while (likely(n)) { 1247 if ((accum + n->count) > index) { 1248 break; 1249 } else { 1250 D("Skipping over (%p) %u at accum %lld", (void *)n, n->count, 1251 accum); 1252 accum += n->count; 1253 n = forward ? n->next : n->prev; 1254 } 1255 } 1256 1257 if (!n) 1258 return 0; 1259 1260 D("Found node: %p at accum %llu, idx %llu, sub+ %llu, sub- %llu", (void *)n, 1261 accum, index, index - accum, (-index) - 1 + accum); 1262 1263 entry->node = n; 1264 if (forward) { 1265 /* forward = normal head-to-tail offset. */ 1266 entry->offset = index - accum; 1267 } else { 1268 /* reverse = need negative offset for tail-to-head, so undo 1269 * the result of the original if (index < 0) above. */ 1270 entry->offset = (-index) - 1 + accum; 1271 } 1272 1273 quicklistDecompressNodeForUse(entry->node); 1274 entry->zi = ziplistIndex(entry->node->zl, entry->offset); 1275 ziplistGet(entry->zi, &entry->value, &entry->sz, &entry->longval); 1276 /* The caller will use our result, so we don't re-compress here. 1277 * The caller can recompress or delete the node as needed. */ 1278 return 1; 1279 } 1280 1281 /* Rotate quicklist by moving the tail element to the head. */ 1282 void quicklistRotate(quicklist *quicklist) { 1283 if (quicklist->count <= 1) 1284 return; 1285 1286 /* First, get the tail entry */ 1287 unsigned char *p = ziplistIndex(quicklist->tail->zl, -1); 1288 unsigned char *value; 1289 long long longval; 1290 unsigned int sz; 1291 char longstr[32] = {0}; 1292 ziplistGet(p, &value, &sz, &longval); 1293 1294 /* If value found is NULL, then ziplistGet populated longval instead */ 1295 if (!value) { 1296 /* Write the longval as a string so we can re-add it */ 1297 sz = ll2string(longstr, sizeof(longstr), longval); 1298 value = (unsigned char *)longstr; 1299 } 1300 1301 /* Add tail entry to head (must happen before tail is deleted). */ 1302 quicklistPushHead(quicklist, value, sz); 1303 1304 /* If quicklist has only one node, the head ziplist is also the 1305 * tail ziplist and PushHead() could have reallocated our single ziplist, 1306 * which would make our pre-existing 'p' unusable. */ 1307 if (quicklist->len == 1) { 1308 p = ziplistIndex(quicklist->tail->zl, -1); 1309 } 1310 1311 /* Remove tail entry. */ 1312 quicklistDelIndex(quicklist, quicklist->tail, &p); 1313 } 1314 1315 /* pop from quicklist and return result in 'data' ptr. Value of 'data' 1316 * is the return value of 'saver' function pointer if the data is NOT a number. 1317 * 1318 * If the quicklist element is a long long, then the return value is returned in 1319 * 'sval'. 1320 * 1321 * Return value of 0 means no elements available. 1322 * Return value of 1 means check 'data' and 'sval' for values. 1323 * If 'data' is set, use 'data' and 'sz'. Otherwise, use 'sval'. */ 1324 int quicklistPopCustom(quicklist *quicklist, int where, unsigned char **data, 1325 unsigned int *sz, long long *sval, 1326 void *(*saver)(unsigned char *data, unsigned int sz)) { 1327 unsigned char *p; 1328 unsigned char *vstr; 1329 unsigned int vlen; 1330 long long vlong; 1331 int pos = (where == QUICKLIST_HEAD) ? 0 : -1; 1332 1333 if (quicklist->count == 0) 1334 return 0; 1335 1336 if (data) 1337 *data = NULL; 1338 if (sz) 1339 *sz = 0; 1340 if (sval) 1341 *sval = -123456789; 1342 1343 quicklistNode *node; 1344 if (where == QUICKLIST_HEAD && quicklist->head) { 1345 node = quicklist->head; 1346 } else if (where == QUICKLIST_TAIL && quicklist->tail) { 1347 node = quicklist->tail; 1348 } else { 1349 return 0; 1350 } 1351 1352 p = ziplistIndex(node->zl, pos); 1353 if (ziplistGet(p, &vstr, &vlen, &vlong)) { 1354 if (vstr) { 1355 if (data) 1356 *data = saver(vstr, vlen); 1357 if (sz) 1358 *sz = vlen; 1359 } else { 1360 if (data) 1361 *data = NULL; 1362 if (sval) 1363 *sval = vlong; 1364 } 1365 quicklistDelIndex(quicklist, node, &p); 1366 return 1; 1367 } 1368 return 0; 1369 } 1370 1371 /* Return a malloc'd copy of data passed in */ 1372 REDIS_STATIC void *_quicklistSaver(unsigned char *data, unsigned int sz) { 1373 unsigned char *vstr; 1374 if (data) { 1375 vstr = zmalloc(sz); 1376 memcpy(vstr, data, sz); 1377 return vstr; 1378 } 1379 return NULL; 1380 } 1381 1382 /* Default pop function 1383 * 1384 * Returns malloc'd value from quicklist */ 1385 int quicklistPop(quicklist *quicklist, int where, unsigned char **data, 1386 unsigned int *sz, long long *slong) { 1387 unsigned char *vstr; 1388 unsigned int vlen; 1389 long long vlong; 1390 if (quicklist->count == 0) 1391 return 0; 1392 int ret = quicklistPopCustom(quicklist, where, &vstr, &vlen, &vlong, 1393 _quicklistSaver); 1394 if (data) 1395 *data = vstr; 1396 if (slong) 1397 *slong = vlong; 1398 if (sz) 1399 *sz = vlen; 1400 return ret; 1401 } 1402 1403 /* Wrapper to allow argument-based switching between HEAD/TAIL pop */ 1404 void quicklistPush(quicklist *quicklist, void *value, const size_t sz, 1405 int where) { 1406 if (where == QUICKLIST_HEAD) { 1407 quicklistPushHead(quicklist, value, sz); 1408 } else if (where == QUICKLIST_TAIL) { 1409 quicklistPushTail(quicklist, value, sz); 1410 } 1411 } 1412 1413 /* The rest of this file is test cases and test helpers. */ 1414 #ifdef REDIS_TEST 1415 #include <stdint.h> 1416 #include <sys/time.h> 1417 1418 #define assert(_e) \ 1419 do { \ 1420 if (!(_e)) { \ 1421 printf("\n\n=== ASSERTION FAILED ===\n"); \ 1422 printf("==> %s:%d '%s' is not true\n", __FILE__, __LINE__, #_e); \ 1423 err++; \ 1424 } \ 1425 } while (0) 1426 1427 #define yell(str, ...) printf("ERROR! " str "\n\n", __VA_ARGS__) 1428 1429 #define OK printf("\tOK\n") 1430 1431 #define ERROR \ 1432 do { \ 1433 printf("\tERROR!\n"); \ 1434 err++; \ 1435 } while (0) 1436 1437 #define ERR(x, ...) \ 1438 do { \ 1439 printf("%s:%s:%d:\t", __FILE__, __FUNCTION__, __LINE__); \ 1440 printf("ERROR! " x "\n", __VA_ARGS__); \ 1441 err++; \ 1442 } while (0) 1443 1444 #define TEST(name) printf("test — %s\n", name); 1445 #define TEST_DESC(name, ...) printf("test — " name "\n", __VA_ARGS__); 1446 1447 #define QL_TEST_VERBOSE 0 1448 1449 #define UNUSED(x) (void)(x) 1450 static void ql_info(quicklist *ql) { 1451 #if QL_TEST_VERBOSE 1452 printf("Container length: %lu\n", ql->len); 1453 printf("Container size: %lu\n", ql->count); 1454 if (ql->head) 1455 printf("\t(zsize head: %d)\n", ziplistLen(ql->head->zl)); 1456 if (ql->tail) 1457 printf("\t(zsize tail: %d)\n", ziplistLen(ql->tail->zl)); 1458 printf("\n"); 1459 #else 1460 UNUSED(ql); 1461 #endif 1462 } 1463 1464 /* Return the UNIX time in microseconds */ 1465 static long long ustime(void) { 1466 struct timeval tv; 1467 long long ust; 1468 1469 gettimeofday(&tv, NULL); 1470 ust = ((long long)tv.tv_sec) * 1000000; 1471 ust += tv.tv_usec; 1472 return ust; 1473 } 1474 1475 /* Return the UNIX time in milliseconds */ 1476 static long long mstime(void) { return ustime() / 1000; } 1477 1478 /* Iterate over an entire quicklist. 1479 * Print the list if 'print' == 1. 1480 * 1481 * Returns physical count of elements found by iterating over the list. */ 1482 static int _itrprintr(quicklist *ql, int print, int forward) { 1483 quicklistIter *iter = 1484 quicklistGetIterator(ql, forward ? AL_START_HEAD : AL_START_TAIL); 1485 quicklistEntry entry; 1486 int i = 0; 1487 int p = 0; 1488 quicklistNode *prev = NULL; 1489 while (quicklistNext(iter, &entry)) { 1490 if (entry.node != prev) { 1491 /* Count the number of list nodes too */ 1492 p++; 1493 prev = entry.node; 1494 } 1495 if (print) { 1496 printf("[%3d (%2d)]: [%.*s] (%lld)\n", i, p, entry.sz, 1497 (char *)entry.value, entry.longval); 1498 } 1499 i++; 1500 } 1501 quicklistReleaseIterator(iter); 1502 return i; 1503 } 1504 static int itrprintr(quicklist *ql, int print) { 1505 return _itrprintr(ql, print, 1); 1506 } 1507 1508 static int itrprintr_rev(quicklist *ql, int print) { 1509 return _itrprintr(ql, print, 0); 1510 } 1511 1512 #define ql_verify(a, b, c, d, e) \ 1513 do { \ 1514 err += _ql_verify((a), (b), (c), (d), (e)); \ 1515 } while (0) 1516 1517 /* Verify list metadata matches physical list contents. */ 1518 static int _ql_verify(quicklist *ql, uint32_t len, uint32_t count, 1519 uint32_t head_count, uint32_t tail_count) { 1520 int errors = 0; 1521 1522 ql_info(ql); 1523 if (len != ql->len) { 1524 yell("quicklist length wrong: expected %d, got %u", len, ql->len); 1525 errors++; 1526 } 1527 1528 if (count != ql->count) { 1529 yell("quicklist count wrong: expected %d, got %lu", count, ql->count); 1530 errors++; 1531 } 1532 1533 int loopr = itrprintr(ql, 0); 1534 if (loopr != (int)ql->count) { 1535 yell("quicklist cached count not match actual count: expected %lu, got " 1536 "%d", 1537 ql->count, loopr); 1538 errors++; 1539 } 1540 1541 int rloopr = itrprintr_rev(ql, 0); 1542 if (loopr != rloopr) { 1543 yell("quicklist has different forward count than reverse count! " 1544 "Forward count is %d, reverse count is %d.", 1545 loopr, rloopr); 1546 errors++; 1547 } 1548 1549 if (ql->len == 0 && !errors) { 1550 OK; 1551 return errors; 1552 } 1553 1554 if (ql->head && head_count != ql->head->count && 1555 head_count != ziplistLen(ql->head->zl)) { 1556 yell("quicklist head count wrong: expected %d, " 1557 "got cached %d vs. actual %d", 1558 head_count, ql->head->count, ziplistLen(ql->head->zl)); 1559 errors++; 1560 } 1561 1562 if (ql->tail && tail_count != ql->tail->count && 1563 tail_count != ziplistLen(ql->tail->zl)) { 1564 yell("quicklist tail count wrong: expected %d, " 1565 "got cached %u vs. actual %d", 1566 tail_count, ql->tail->count, ziplistLen(ql->tail->zl)); 1567 errors++; 1568 } 1569 1570 if (quicklistAllowsCompression(ql)) { 1571 quicklistNode *node = ql->head; 1572 unsigned int low_raw = ql->compress; 1573 unsigned int high_raw = ql->len - ql->compress; 1574 1575 for (unsigned int at = 0; at < ql->len; at++, node = node->next) { 1576 if (node && (at < low_raw || at >= high_raw)) { 1577 if (node->encoding != QUICKLIST_NODE_ENCODING_RAW) { 1578 yell("Incorrect compression: node %d is " 1579 "compressed at depth %d ((%u, %u); total " 1580 "nodes: %u; size: %u; recompress: %d)", 1581 at, ql->compress, low_raw, high_raw, ql->len, node->sz, 1582 node->recompress); 1583 errors++; 1584 } 1585 } else { 1586 if (node->encoding != QUICKLIST_NODE_ENCODING_LZF && 1587 !node->attempted_compress) { 1588 yell("Incorrect non-compression: node %d is NOT " 1589 "compressed at depth %d ((%u, %u); total " 1590 "nodes: %u; size: %u; recompress: %d; attempted: %d)", 1591 at, ql->compress, low_raw, high_raw, ql->len, node->sz, 1592 node->recompress, node->attempted_compress); 1593 errors++; 1594 } 1595 } 1596 } 1597 } 1598 1599 if (!errors) 1600 OK; 1601 return errors; 1602 } 1603 1604 /* Generate new string concatenating integer i against string 'prefix' */ 1605 static char *genstr(char *prefix, int i) { 1606 static char result[64] = {0}; 1607 snprintf(result, sizeof(result), "%s%d", prefix, i); 1608 return result; 1609 } 1610 1611 /* main test, but callable from other files */ 1612 int quicklistTest(int argc, char *argv[]) { 1613 UNUSED(argc); 1614 UNUSED(argv); 1615 1616 unsigned int err = 0; 1617 int optimize_start = 1618 -(int)(sizeof(optimization_level) / sizeof(*optimization_level)); 1619 1620 printf("Starting optimization offset at: %d\n", optimize_start); 1621 1622 int options[] = {0, 1, 2, 3, 4, 5, 6, 10}; 1623 size_t option_count = sizeof(options) / sizeof(*options); 1624 long long runtime[option_count]; 1625 1626 for (int _i = 0; _i < (int)option_count; _i++) { 1627 printf("Testing Option %d\n", options[_i]); 1628 long long start = mstime(); 1629 1630 TEST("create list") { 1631 quicklist *ql = quicklistNew(-2, options[_i]); 1632 ql_verify(ql, 0, 0, 0, 0); 1633 quicklistRelease(ql); 1634 } 1635 1636 TEST("add to tail of empty list") { 1637 quicklist *ql = quicklistNew(-2, options[_i]); 1638 quicklistPushTail(ql, "hello", 6); 1639 /* 1 for head and 1 for tail because 1 node = head = tail */ 1640 ql_verify(ql, 1, 1, 1, 1); 1641 quicklistRelease(ql); 1642 } 1643 1644 TEST("add to head of empty list") { 1645 quicklist *ql = quicklistNew(-2, options[_i]); 1646 quicklistPushHead(ql, "hello", 6); 1647 /* 1 for head and 1 for tail because 1 node = head = tail */ 1648 ql_verify(ql, 1, 1, 1, 1); 1649 quicklistRelease(ql); 1650 } 1651 1652 for (int f = optimize_start; f < 32; f++) { 1653 TEST_DESC("add to tail 5x at fill %d at compress %d", f, 1654 options[_i]) { 1655 quicklist *ql = quicklistNew(f, options[_i]); 1656 for (int i = 0; i < 5; i++) 1657 quicklistPushTail(ql, genstr("hello", i), 32); 1658 if (ql->count != 5) 1659 ERROR; 1660 if (f == 32) 1661 ql_verify(ql, 1, 5, 5, 5); 1662 quicklistRelease(ql); 1663 } 1664 } 1665 1666 for (int f = optimize_start; f < 32; f++) { 1667 TEST_DESC("add to head 5x at fill %d at compress %d", f, 1668 options[_i]) { 1669 quicklist *ql = quicklistNew(f, options[_i]); 1670 for (int i = 0; i < 5; i++) 1671 quicklistPushHead(ql, genstr("hello", i), 32); 1672 if (ql->count != 5) 1673 ERROR; 1674 if (f == 32) 1675 ql_verify(ql, 1, 5, 5, 5); 1676 quicklistRelease(ql); 1677 } 1678 } 1679 1680 for (int f = optimize_start; f < 512; f++) { 1681 TEST_DESC("add to tail 500x at fill %d at compress %d", f, 1682 options[_i]) { 1683 quicklist *ql = quicklistNew(f, options[_i]); 1684 for (int i = 0; i < 500; i++) 1685 quicklistPushTail(ql, genstr("hello", i), 64); 1686 if (ql->count != 500) 1687 ERROR; 1688 if (f == 32) 1689 ql_verify(ql, 16, 500, 32, 20); 1690 quicklistRelease(ql); 1691 } 1692 } 1693 1694 for (int f = optimize_start; f < 512; f++) { 1695 TEST_DESC("add to head 500x at fill %d at compress %d", f, 1696 options[_i]) { 1697 quicklist *ql = quicklistNew(f, options[_i]); 1698 for (int i = 0; i < 500; i++) 1699 quicklistPushHead(ql, genstr("hello", i), 32); 1700 if (ql->count != 500) 1701 ERROR; 1702 if (f == 32) 1703 ql_verify(ql, 16, 500, 20, 32); 1704 quicklistRelease(ql); 1705 } 1706 } 1707 1708 TEST("rotate empty") { 1709 quicklist *ql = quicklistNew(-2, options[_i]); 1710 quicklistRotate(ql); 1711 ql_verify(ql, 0, 0, 0, 0); 1712 quicklistRelease(ql); 1713 } 1714 1715 for (int f = optimize_start; f < 32; f++) { 1716 TEST("rotate one val once") { 1717 quicklist *ql = quicklistNew(f, options[_i]); 1718 quicklistPushHead(ql, "hello", 6); 1719 quicklistRotate(ql); 1720 /* Ignore compression verify because ziplist is 1721 * too small to compress. */ 1722 ql_verify(ql, 1, 1, 1, 1); 1723 quicklistRelease(ql); 1724 } 1725 } 1726 1727 for (int f = optimize_start; f < 3; f++) { 1728 TEST_DESC("rotate 500 val 5000 times at fill %d at compress %d", f, 1729 options[_i]) { 1730 quicklist *ql = quicklistNew(f, options[_i]); 1731 quicklistPushHead(ql, "900", 3); 1732 quicklistPushHead(ql, "7000", 4); 1733 quicklistPushHead(ql, "-1200", 5); 1734 quicklistPushHead(ql, "42", 2); 1735 for (int i = 0; i < 500; i++) 1736 quicklistPushHead(ql, genstr("hello", i), 64); 1737 ql_info(ql); 1738 for (int i = 0; i < 5000; i++) { 1739 ql_info(ql); 1740 quicklistRotate(ql); 1741 } 1742 if (f == 1) 1743 ql_verify(ql, 504, 504, 1, 1); 1744 else if (f == 2) 1745 ql_verify(ql, 252, 504, 2, 2); 1746 else if (f == 32) 1747 ql_verify(ql, 16, 504, 32, 24); 1748 quicklistRelease(ql); 1749 } 1750 } 1751 1752 TEST("pop empty") { 1753 quicklist *ql = quicklistNew(-2, options[_i]); 1754 quicklistPop(ql, QUICKLIST_HEAD, NULL, NULL, NULL); 1755 ql_verify(ql, 0, 0, 0, 0); 1756 quicklistRelease(ql); 1757 } 1758 1759 TEST("pop 1 string from 1") { 1760 quicklist *ql = quicklistNew(-2, options[_i]); 1761 char *populate = genstr("hello", 331); 1762 quicklistPushHead(ql, populate, 32); 1763 unsigned char *data; 1764 unsigned int sz; 1765 long long lv; 1766 ql_info(ql); 1767 quicklistPop(ql, QUICKLIST_HEAD, &data, &sz, &lv); 1768 assert(data != NULL); 1769 assert(sz == 32); 1770 if (strcmp(populate, (char *)data)) 1771 ERR("Pop'd value (%.*s) didn't equal original value (%s)", sz, 1772 data, populate); 1773 zfree(data); 1774 ql_verify(ql, 0, 0, 0, 0); 1775 quicklistRelease(ql); 1776 } 1777 1778 TEST("pop head 1 number from 1") { 1779 quicklist *ql = quicklistNew(-2, options[_i]); 1780 quicklistPushHead(ql, "55513", 5); 1781 unsigned char *data; 1782 unsigned int sz; 1783 long long lv; 1784 ql_info(ql); 1785 quicklistPop(ql, QUICKLIST_HEAD, &data, &sz, &lv); 1786 assert(data == NULL); 1787 assert(lv == 55513); 1788 ql_verify(ql, 0, 0, 0, 0); 1789 quicklistRelease(ql); 1790 } 1791 1792 TEST("pop head 500 from 500") { 1793 quicklist *ql = quicklistNew(-2, options[_i]); 1794 for (int i = 0; i < 500; i++) 1795 quicklistPushHead(ql, genstr("hello", i), 32); 1796 ql_info(ql); 1797 for (int i = 0; i < 500; i++) { 1798 unsigned char *data; 1799 unsigned int sz; 1800 long long lv; 1801 int ret = quicklistPop(ql, QUICKLIST_HEAD, &data, &sz, &lv); 1802 assert(ret == 1); 1803 assert(data != NULL); 1804 assert(sz == 32); 1805 if (strcmp(genstr("hello", 499 - i), (char *)data)) 1806 ERR("Pop'd value (%.*s) didn't equal original value (%s)", 1807 sz, data, genstr("hello", 499 - i)); 1808 zfree(data); 1809 } 1810 ql_verify(ql, 0, 0, 0, 0); 1811 quicklistRelease(ql); 1812 } 1813 1814 TEST("pop head 5000 from 500") { 1815 quicklist *ql = quicklistNew(-2, options[_i]); 1816 for (int i = 0; i < 500; i++) 1817 quicklistPushHead(ql, genstr("hello", i), 32); 1818 for (int i = 0; i < 5000; i++) { 1819 unsigned char *data; 1820 unsigned int sz; 1821 long long lv; 1822 int ret = quicklistPop(ql, QUICKLIST_HEAD, &data, &sz, &lv); 1823 if (i < 500) { 1824 assert(ret == 1); 1825 assert(data != NULL); 1826 assert(sz == 32); 1827 if (strcmp(genstr("hello", 499 - i), (char *)data)) 1828 ERR("Pop'd value (%.*s) didn't equal original value " 1829 "(%s)", 1830 sz, data, genstr("hello", 499 - i)); 1831 zfree(data); 1832 } else { 1833 assert(ret == 0); 1834 } 1835 } 1836 ql_verify(ql, 0, 0, 0, 0); 1837 quicklistRelease(ql); 1838 } 1839 1840 TEST("iterate forward over 500 list") { 1841 quicklist *ql = quicklistNew(-2, options[_i]); 1842 quicklistSetFill(ql, 32); 1843 for (int i = 0; i < 500; i++) 1844 quicklistPushHead(ql, genstr("hello", i), 32); 1845 quicklistIter *iter = quicklistGetIterator(ql, AL_START_HEAD); 1846 quicklistEntry entry; 1847 int i = 499, count = 0; 1848 while (quicklistNext(iter, &entry)) { 1849 char *h = genstr("hello", i); 1850 if (strcmp((char *)entry.value, h)) 1851 ERR("value [%s] didn't match [%s] at position %d", 1852 entry.value, h, i); 1853 i--; 1854 count++; 1855 } 1856 if (count != 500) 1857 ERR("Didn't iterate over exactly 500 elements (%d)", i); 1858 ql_verify(ql, 16, 500, 20, 32); 1859 quicklistReleaseIterator(iter); 1860 quicklistRelease(ql); 1861 } 1862 1863 TEST("iterate reverse over 500 list") { 1864 quicklist *ql = quicklistNew(-2, options[_i]); 1865 quicklistSetFill(ql, 32); 1866 for (int i = 0; i < 500; i++) 1867 quicklistPushHead(ql, genstr("hello", i), 32); 1868 quicklistIter *iter = quicklistGetIterator(ql, AL_START_TAIL); 1869 quicklistEntry entry; 1870 int i = 0; 1871 while (quicklistNext(iter, &entry)) { 1872 char *h = genstr("hello", i); 1873 if (strcmp((char *)entry.value, h)) 1874 ERR("value [%s] didn't match [%s] at position %d", 1875 entry.value, h, i); 1876 i++; 1877 } 1878 if (i != 500) 1879 ERR("Didn't iterate over exactly 500 elements (%d)", i); 1880 ql_verify(ql, 16, 500, 20, 32); 1881 quicklistReleaseIterator(iter); 1882 quicklistRelease(ql); 1883 } 1884 1885 TEST("insert before with 0 elements") { 1886 quicklist *ql = quicklistNew(-2, options[_i]); 1887 quicklistEntry entry; 1888 quicklistIndex(ql, 0, &entry); 1889 quicklistInsertBefore(ql, &entry, "abc", 4); 1890 ql_verify(ql, 1, 1, 1, 1); 1891 quicklistRelease(ql); 1892 } 1893 1894 TEST("insert after with 0 elements") { 1895 quicklist *ql = quicklistNew(-2, options[_i]); 1896 quicklistEntry entry; 1897 quicklistIndex(ql, 0, &entry); 1898 quicklistInsertAfter(ql, &entry, "abc", 4); 1899 ql_verify(ql, 1, 1, 1, 1); 1900 quicklistRelease(ql); 1901 } 1902 1903 TEST("insert after 1 element") { 1904 quicklist *ql = quicklistNew(-2, options[_i]); 1905 quicklistPushHead(ql, "hello", 6); 1906 quicklistEntry entry; 1907 quicklistIndex(ql, 0, &entry); 1908 quicklistInsertAfter(ql, &entry, "abc", 4); 1909 ql_verify(ql, 1, 2, 2, 2); 1910 quicklistRelease(ql); 1911 } 1912 1913 TEST("insert before 1 element") { 1914 quicklist *ql = quicklistNew(-2, options[_i]); 1915 quicklistPushHead(ql, "hello", 6); 1916 quicklistEntry entry; 1917 quicklistIndex(ql, 0, &entry); 1918 quicklistInsertAfter(ql, &entry, "abc", 4); 1919 ql_verify(ql, 1, 2, 2, 2); 1920 quicklistRelease(ql); 1921 } 1922 1923 for (int f = optimize_start; f < 12; f++) { 1924 TEST_DESC("insert once in elements while iterating at fill %d at " 1925 "compress %d\n", 1926 f, options[_i]) { 1927 quicklist *ql = quicklistNew(f, options[_i]); 1928 quicklistPushTail(ql, "abc", 3); 1929 quicklistSetFill(ql, 1); 1930 quicklistPushTail(ql, "def", 3); /* force to unique node */ 1931 quicklistSetFill(ql, f); 1932 quicklistPushTail(ql, "bob", 3); /* force to reset for +3 */ 1933 quicklistPushTail(ql, "foo", 3); 1934 quicklistPushTail(ql, "zoo", 3); 1935 1936 itrprintr(ql, 0); 1937 /* insert "bar" before "bob" while iterating over list. */ 1938 quicklistIter *iter = quicklistGetIterator(ql, AL_START_HEAD); 1939 quicklistEntry entry; 1940 while (quicklistNext(iter, &entry)) { 1941 if (!strncmp((char *)entry.value, "bob", 3)) { 1942 /* Insert as fill = 1 so it spills into new node. */ 1943 quicklistInsertBefore(ql, &entry, "bar", 3); 1944 break; /* didn't we fix insert-while-iterating? */ 1945 } 1946 } 1947 itrprintr(ql, 0); 1948 1949 /* verify results */ 1950 quicklistIndex(ql, 0, &entry); 1951 if (strncmp((char *)entry.value, "abc", 3)) 1952 ERR("Value 0 didn't match, instead got: %.*s", entry.sz, 1953 entry.value); 1954 quicklistIndex(ql, 1, &entry); 1955 if (strncmp((char *)entry.value, "def", 3)) 1956 ERR("Value 1 didn't match, instead got: %.*s", entry.sz, 1957 entry.value); 1958 quicklistIndex(ql, 2, &entry); 1959 if (strncmp((char *)entry.value, "bar", 3)) 1960 ERR("Value 2 didn't match, instead got: %.*s", entry.sz, 1961 entry.value); 1962 quicklistIndex(ql, 3, &entry); 1963 if (strncmp((char *)entry.value, "bob", 3)) 1964 ERR("Value 3 didn't match, instead got: %.*s", entry.sz, 1965 entry.value); 1966 quicklistIndex(ql, 4, &entry); 1967 if (strncmp((char *)entry.value, "foo", 3)) 1968 ERR("Value 4 didn't match, instead got: %.*s", entry.sz, 1969 entry.value); 1970 quicklistIndex(ql, 5, &entry); 1971 if (strncmp((char *)entry.value, "zoo", 3)) 1972 ERR("Value 5 didn't match, instead got: %.*s", entry.sz, 1973 entry.value); 1974 quicklistReleaseIterator(iter); 1975 quicklistRelease(ql); 1976 } 1977 } 1978 1979 for (int f = optimize_start; f < 1024; f++) { 1980 TEST_DESC( 1981 "insert [before] 250 new in middle of 500 elements at fill" 1982 " %d at compress %d", 1983 f, options[_i]) { 1984 quicklist *ql = quicklistNew(f, options[_i]); 1985 for (int i = 0; i < 500; i++) 1986 quicklistPushTail(ql, genstr("hello", i), 32); 1987 for (int i = 0; i < 250; i++) { 1988 quicklistEntry entry; 1989 quicklistIndex(ql, 250, &entry); 1990 quicklistInsertBefore(ql, &entry, genstr("abc", i), 32); 1991 } 1992 if (f == 32) 1993 ql_verify(ql, 25, 750, 32, 20); 1994 quicklistRelease(ql); 1995 } 1996 } 1997 1998 for (int f = optimize_start; f < 1024; f++) { 1999 TEST_DESC("insert [after] 250 new in middle of 500 elements at " 2000 "fill %d at compress %d", 2001 f, options[_i]) { 2002 quicklist *ql = quicklistNew(f, options[_i]); 2003 for (int i = 0; i < 500; i++) 2004 quicklistPushHead(ql, genstr("hello", i), 32); 2005 for (int i = 0; i < 250; i++) { 2006 quicklistEntry entry; 2007 quicklistIndex(ql, 250, &entry); 2008 quicklistInsertAfter(ql, &entry, genstr("abc", i), 32); 2009 } 2010 2011 if (ql->count != 750) 2012 ERR("List size not 750, but rather %ld", ql->count); 2013 2014 if (f == 32) 2015 ql_verify(ql, 26, 750, 20, 32); 2016 quicklistRelease(ql); 2017 } 2018 } 2019 2020 TEST("duplicate empty list") { 2021 quicklist *ql = quicklistNew(-2, options[_i]); 2022 ql_verify(ql, 0, 0, 0, 0); 2023 quicklist *copy = quicklistDup(ql); 2024 ql_verify(copy, 0, 0, 0, 0); 2025 quicklistRelease(ql); 2026 quicklistRelease(copy); 2027 } 2028 2029 TEST("duplicate list of 1 element") { 2030 quicklist *ql = quicklistNew(-2, options[_i]); 2031 quicklistPushHead(ql, genstr("hello", 3), 32); 2032 ql_verify(ql, 1, 1, 1, 1); 2033 quicklist *copy = quicklistDup(ql); 2034 ql_verify(copy, 1, 1, 1, 1); 2035 quicklistRelease(ql); 2036 quicklistRelease(copy); 2037 } 2038 2039 TEST("duplicate list of 500") { 2040 quicklist *ql = quicklistNew(-2, options[_i]); 2041 quicklistSetFill(ql, 32); 2042 for (int i = 0; i < 500; i++) 2043 quicklistPushHead(ql, genstr("hello", i), 32); 2044 ql_verify(ql, 16, 500, 20, 32); 2045 2046 quicklist *copy = quicklistDup(ql); 2047 ql_verify(copy, 16, 500, 20, 32); 2048 quicklistRelease(ql); 2049 quicklistRelease(copy); 2050 } 2051 2052 for (int f = optimize_start; f < 512; f++) { 2053 TEST_DESC("index 1,200 from 500 list at fill %d at compress %d", f, 2054 options[_i]) { 2055 quicklist *ql = quicklistNew(f, options[_i]); 2056 for (int i = 0; i < 500; i++) 2057 quicklistPushTail(ql, genstr("hello", i + 1), 32); 2058 quicklistEntry entry; 2059 quicklistIndex(ql, 1, &entry); 2060 if (!strcmp((char *)entry.value, "hello2")) 2061 OK; 2062 else 2063 ERR("Value: %s", entry.value); 2064 quicklistIndex(ql, 200, &entry); 2065 if (!strcmp((char *)entry.value, "hello201")) 2066 OK; 2067 else 2068 ERR("Value: %s", entry.value); 2069 quicklistRelease(ql); 2070 } 2071 2072 TEST_DESC("index -1,-2 from 500 list at fill %d at compress %d", f, 2073 options[_i]) { 2074 quicklist *ql = quicklistNew(f, options[_i]); 2075 for (int i = 0; i < 500; i++) 2076 quicklistPushTail(ql, genstr("hello", i + 1), 32); 2077 quicklistEntry entry; 2078 quicklistIndex(ql, -1, &entry); 2079 if (!strcmp((char *)entry.value, "hello500")) 2080 OK; 2081 else 2082 ERR("Value: %s", entry.value); 2083 quicklistIndex(ql, -2, &entry); 2084 if (!strcmp((char *)entry.value, "hello499")) 2085 OK; 2086 else 2087 ERR("Value: %s", entry.value); 2088 quicklistRelease(ql); 2089 } 2090 2091 TEST_DESC("index -100 from 500 list at fill %d at compress %d", f, 2092 options[_i]) { 2093 quicklist *ql = quicklistNew(f, options[_i]); 2094 for (int i = 0; i < 500; i++) 2095 quicklistPushTail(ql, genstr("hello", i + 1), 32); 2096 quicklistEntry entry; 2097 quicklistIndex(ql, -100, &entry); 2098 if (!strcmp((char *)entry.value, "hello401")) 2099 OK; 2100 else 2101 ERR("Value: %s", entry.value); 2102 quicklistRelease(ql); 2103 } 2104 2105 TEST_DESC("index too big +1 from 50 list at fill %d at compress %d", 2106 f, options[_i]) { 2107 quicklist *ql = quicklistNew(f, options[_i]); 2108 for (int i = 0; i < 50; i++) 2109 quicklistPushTail(ql, genstr("hello", i + 1), 32); 2110 quicklistEntry entry; 2111 if (quicklistIndex(ql, 50, &entry)) 2112 ERR("Index found at 50 with 50 list: %.*s", entry.sz, 2113 entry.value); 2114 else 2115 OK; 2116 quicklistRelease(ql); 2117 } 2118 } 2119 2120 TEST("delete range empty list") { 2121 quicklist *ql = quicklistNew(-2, options[_i]); 2122 quicklistDelRange(ql, 5, 20); 2123 ql_verify(ql, 0, 0, 0, 0); 2124 quicklistRelease(ql); 2125 } 2126 2127 TEST("delete range of entire node in list of one node") { 2128 quicklist *ql = quicklistNew(-2, options[_i]); 2129 for (int i = 0; i < 32; i++) 2130 quicklistPushHead(ql, genstr("hello", i), 32); 2131 ql_verify(ql, 1, 32, 32, 32); 2132 quicklistDelRange(ql, 0, 32); 2133 ql_verify(ql, 0, 0, 0, 0); 2134 quicklistRelease(ql); 2135 } 2136 2137 TEST("delete range of entire node with overflow counts") { 2138 quicklist *ql = quicklistNew(-2, options[_i]); 2139 for (int i = 0; i < 32; i++) 2140 quicklistPushHead(ql, genstr("hello", i), 32); 2141 ql_verify(ql, 1, 32, 32, 32); 2142 quicklistDelRange(ql, 0, 128); 2143 ql_verify(ql, 0, 0, 0, 0); 2144 quicklistRelease(ql); 2145 } 2146 2147 TEST("delete middle 100 of 500 list") { 2148 quicklist *ql = quicklistNew(-2, options[_i]); 2149 quicklistSetFill(ql, 32); 2150 for (int i = 0; i < 500; i++) 2151 quicklistPushTail(ql, genstr("hello", i + 1), 32); 2152 ql_verify(ql, 16, 500, 32, 20); 2153 quicklistDelRange(ql, 200, 100); 2154 ql_verify(ql, 14, 400, 32, 20); 2155 quicklistRelease(ql); 2156 } 2157 2158 TEST("delete negative 1 from 500 list") { 2159 quicklist *ql = quicklistNew(-2, options[_i]); 2160 quicklistSetFill(ql, 32); 2161 for (int i = 0; i < 500; i++) 2162 quicklistPushTail(ql, genstr("hello", i + 1), 32); 2163 ql_verify(ql, 16, 500, 32, 20); 2164 quicklistDelRange(ql, -1, 1); 2165 ql_verify(ql, 16, 499, 32, 19); 2166 quicklistRelease(ql); 2167 } 2168 2169 TEST("delete negative 1 from 500 list with overflow counts") { 2170 quicklist *ql = quicklistNew(-2, options[_i]); 2171 quicklistSetFill(ql, 32); 2172 for (int i = 0; i < 500; i++) 2173 quicklistPushTail(ql, genstr("hello", i + 1), 32); 2174 ql_verify(ql, 16, 500, 32, 20); 2175 quicklistDelRange(ql, -1, 128); 2176 ql_verify(ql, 16, 499, 32, 19); 2177 quicklistRelease(ql); 2178 } 2179 2180 TEST("delete negative 100 from 500 list") { 2181 quicklist *ql = quicklistNew(-2, options[_i]); 2182 quicklistSetFill(ql, 32); 2183 for (int i = 0; i < 500; i++) 2184 quicklistPushTail(ql, genstr("hello", i + 1), 32); 2185 quicklistDelRange(ql, -100, 100); 2186 ql_verify(ql, 13, 400, 32, 16); 2187 quicklistRelease(ql); 2188 } 2189 2190 TEST("delete -10 count 5 from 50 list") { 2191 quicklist *ql = quicklistNew(-2, options[_i]); 2192 quicklistSetFill(ql, 32); 2193 for (int i = 0; i < 50; i++) 2194 quicklistPushTail(ql, genstr("hello", i + 1), 32); 2195 ql_verify(ql, 2, 50, 32, 18); 2196 quicklistDelRange(ql, -10, 5); 2197 ql_verify(ql, 2, 45, 32, 13); 2198 quicklistRelease(ql); 2199 } 2200 2201 TEST("numbers only list read") { 2202 quicklist *ql = quicklistNew(-2, options[_i]); 2203 quicklistPushTail(ql, "1111", 4); 2204 quicklistPushTail(ql, "2222", 4); 2205 quicklistPushTail(ql, "3333", 4); 2206 quicklistPushTail(ql, "4444", 4); 2207 ql_verify(ql, 1, 4, 4, 4); 2208 quicklistEntry entry; 2209 quicklistIndex(ql, 0, &entry); 2210 if (entry.longval != 1111) 2211 ERR("Not 1111, %lld", entry.longval); 2212 quicklistIndex(ql, 1, &entry); 2213 if (entry.longval != 2222) 2214 ERR("Not 2222, %lld", entry.longval); 2215 quicklistIndex(ql, 2, &entry); 2216 if (entry.longval != 3333) 2217 ERR("Not 3333, %lld", entry.longval); 2218 quicklistIndex(ql, 3, &entry); 2219 if (entry.longval != 4444) 2220 ERR("Not 4444, %lld", entry.longval); 2221 if (quicklistIndex(ql, 4, &entry)) 2222 ERR("Index past elements: %lld", entry.longval); 2223 quicklistIndex(ql, -1, &entry); 2224 if (entry.longval != 4444) 2225 ERR("Not 4444 (reverse), %lld", entry.longval); 2226 quicklistIndex(ql, -2, &entry); 2227 if (entry.longval != 3333) 2228 ERR("Not 3333 (reverse), %lld", entry.longval); 2229 quicklistIndex(ql, -3, &entry); 2230 if (entry.longval != 2222) 2231 ERR("Not 2222 (reverse), %lld", entry.longval); 2232 quicklistIndex(ql, -4, &entry); 2233 if (entry.longval != 1111) 2234 ERR("Not 1111 (reverse), %lld", entry.longval); 2235 if (quicklistIndex(ql, -5, &entry)) 2236 ERR("Index past elements (reverse), %lld", entry.longval); 2237 quicklistRelease(ql); 2238 } 2239 2240 TEST("numbers larger list read") { 2241 quicklist *ql = quicklistNew(-2, options[_i]); 2242 quicklistSetFill(ql, 32); 2243 char num[32]; 2244 long long nums[5000]; 2245 for (int i = 0; i < 5000; i++) { 2246 nums[i] = -5157318210846258176 + i; 2247 int sz = ll2string(num, sizeof(num), nums[i]); 2248 quicklistPushTail(ql, num, sz); 2249 } 2250 quicklistPushTail(ql, "xxxxxxxxxxxxxxxxxxxx", 20); 2251 quicklistEntry entry; 2252 for (int i = 0; i < 5000; i++) { 2253 quicklistIndex(ql, i, &entry); 2254 if (entry.longval != nums[i]) 2255 ERR("[%d] Not longval %lld but rather %lld", i, nums[i], 2256 entry.longval); 2257 entry.longval = 0xdeadbeef; 2258 } 2259 quicklistIndex(ql, 5000, &entry); 2260 if (strncmp((char *)entry.value, "xxxxxxxxxxxxxxxxxxxx", 20)) 2261 ERR("String val not match: %s", entry.value); 2262 ql_verify(ql, 157, 5001, 32, 9); 2263 quicklistRelease(ql); 2264 } 2265 2266 TEST("numbers larger list read B") { 2267 quicklist *ql = quicklistNew(-2, options[_i]); 2268 quicklistPushTail(ql, "99", 2); 2269 quicklistPushTail(ql, "98", 2); 2270 quicklistPushTail(ql, "xxxxxxxxxxxxxxxxxxxx", 20); 2271 quicklistPushTail(ql, "96", 2); 2272 quicklistPushTail(ql, "95", 2); 2273 quicklistReplaceAtIndex(ql, 1, "foo", 3); 2274 quicklistReplaceAtIndex(ql, -1, "bar", 3); 2275 quicklistRelease(ql); 2276 OK; 2277 } 2278 2279 for (int f = optimize_start; f < 16; f++) { 2280 TEST_DESC("lrem test at fill %d at compress %d", f, options[_i]) { 2281 quicklist *ql = quicklistNew(f, options[_i]); 2282 char *words[] = {"abc", "foo", "bar", "foobar", "foobared", 2283 "zap", "bar", "test", "foo"}; 2284 char *result[] = {"abc", "foo", "foobar", "foobared", 2285 "zap", "test", "foo"}; 2286 char *resultB[] = {"abc", "foo", "foobar", 2287 "foobared", "zap", "test"}; 2288 for (int i = 0; i < 9; i++) 2289 quicklistPushTail(ql, words[i], strlen(words[i])); 2290 2291 /* lrem 0 bar */ 2292 quicklistIter *iter = quicklistGetIterator(ql, AL_START_HEAD); 2293 quicklistEntry entry; 2294 int i = 0; 2295 while (quicklistNext(iter, &entry)) { 2296 if (quicklistCompare(entry.zi, (unsigned char *)"bar", 3)) { 2297 quicklistDelEntry(iter, &entry); 2298 } 2299 i++; 2300 } 2301 quicklistReleaseIterator(iter); 2302 2303 /* check result of lrem 0 bar */ 2304 iter = quicklistGetIterator(ql, AL_START_HEAD); 2305 i = 0; 2306 int ok = 1; 2307 while (quicklistNext(iter, &entry)) { 2308 /* Result must be: abc, foo, foobar, foobared, zap, test, 2309 * foo */ 2310 if (strncmp((char *)entry.value, result[i], entry.sz)) { 2311 ERR("No match at position %d, got %.*s instead of %s", 2312 i, entry.sz, entry.value, result[i]); 2313 ok = 0; 2314 } 2315 i++; 2316 } 2317 quicklistReleaseIterator(iter); 2318 2319 quicklistPushTail(ql, "foo", 3); 2320 2321 /* lrem -2 foo */ 2322 iter = quicklistGetIterator(ql, AL_START_TAIL); 2323 i = 0; 2324 int del = 2; 2325 while (quicklistNext(iter, &entry)) { 2326 if (quicklistCompare(entry.zi, (unsigned char *)"foo", 3)) { 2327 quicklistDelEntry(iter, &entry); 2328 del--; 2329 } 2330 if (!del) 2331 break; 2332 i++; 2333 } 2334 quicklistReleaseIterator(iter); 2335 2336 /* check result of lrem -2 foo */ 2337 /* (we're ignoring the '2' part and still deleting all foo 2338 * because 2339 * we only have two foo) */ 2340 iter = quicklistGetIterator(ql, AL_START_TAIL); 2341 i = 0; 2342 size_t resB = sizeof(resultB) / sizeof(*resultB); 2343 while (quicklistNext(iter, &entry)) { 2344 /* Result must be: abc, foo, foobar, foobared, zap, test, 2345 * foo */ 2346 if (strncmp((char *)entry.value, resultB[resB - 1 - i], 2347 entry.sz)) { 2348 ERR("No match at position %d, got %.*s instead of %s", 2349 i, entry.sz, entry.value, resultB[resB - 1 - i]); 2350 ok = 0; 2351 } 2352 i++; 2353 } 2354 2355 quicklistReleaseIterator(iter); 2356 /* final result of all tests */ 2357 if (ok) 2358 OK; 2359 quicklistRelease(ql); 2360 } 2361 } 2362 2363 for (int f = optimize_start; f < 16; f++) { 2364 TEST_DESC("iterate reverse + delete at fill %d at compress %d", f, 2365 options[_i]) { 2366 quicklist *ql = quicklistNew(f, options[_i]); 2367 quicklistPushTail(ql, "abc", 3); 2368 quicklistPushTail(ql, "def", 3); 2369 quicklistPushTail(ql, "hij", 3); 2370 quicklistPushTail(ql, "jkl", 3); 2371 quicklistPushTail(ql, "oop", 3); 2372 2373 quicklistEntry entry; 2374 quicklistIter *iter = quicklistGetIterator(ql, AL_START_TAIL); 2375 int i = 0; 2376 while (quicklistNext(iter, &entry)) { 2377 if (quicklistCompare(entry.zi, (unsigned char *)"hij", 3)) { 2378 quicklistDelEntry(iter, &entry); 2379 } 2380 i++; 2381 } 2382 quicklistReleaseIterator(iter); 2383 2384 if (i != 5) 2385 ERR("Didn't iterate 5 times, iterated %d times.", i); 2386 2387 /* Check results after deletion of "hij" */ 2388 iter = quicklistGetIterator(ql, AL_START_HEAD); 2389 i = 0; 2390 char *vals[] = {"abc", "def", "jkl", "oop"}; 2391 while (quicklistNext(iter, &entry)) { 2392 if (!quicklistCompare(entry.zi, (unsigned char *)vals[i], 2393 3)) { 2394 ERR("Value at %d didn't match %s\n", i, vals[i]); 2395 } 2396 i++; 2397 } 2398 quicklistReleaseIterator(iter); 2399 quicklistRelease(ql); 2400 } 2401 } 2402 2403 for (int f = optimize_start; f < 800; f++) { 2404 TEST_DESC("iterator at index test at fill %d at compress %d", f, 2405 options[_i]) { 2406 quicklist *ql = quicklistNew(f, options[_i]); 2407 char num[32]; 2408 long long nums[5000]; 2409 for (int i = 0; i < 760; i++) { 2410 nums[i] = -5157318210846258176 + i; 2411 int sz = ll2string(num, sizeof(num), nums[i]); 2412 quicklistPushTail(ql, num, sz); 2413 } 2414 2415 quicklistEntry entry; 2416 quicklistIter *iter = 2417 quicklistGetIteratorAtIdx(ql, AL_START_HEAD, 437); 2418 int i = 437; 2419 while (quicklistNext(iter, &entry)) { 2420 if (entry.longval != nums[i]) 2421 ERR("Expected %lld, but got %lld", entry.longval, 2422 nums[i]); 2423 i++; 2424 } 2425 quicklistReleaseIterator(iter); 2426 quicklistRelease(ql); 2427 } 2428 } 2429 2430 for (int f = optimize_start; f < 40; f++) { 2431 TEST_DESC("ltrim test A at fill %d at compress %d", f, 2432 options[_i]) { 2433 quicklist *ql = quicklistNew(f, options[_i]); 2434 char num[32]; 2435 long long nums[5000]; 2436 for (int i = 0; i < 32; i++) { 2437 nums[i] = -5157318210846258176 + i; 2438 int sz = ll2string(num, sizeof(num), nums[i]); 2439 quicklistPushTail(ql, num, sz); 2440 } 2441 if (f == 32) 2442 ql_verify(ql, 1, 32, 32, 32); 2443 /* ltrim 25 53 (keep [25,32] inclusive = 7 remaining) */ 2444 quicklistDelRange(ql, 0, 25); 2445 quicklistDelRange(ql, 0, 0); 2446 quicklistEntry entry; 2447 for (int i = 0; i < 7; i++) { 2448 quicklistIndex(ql, i, &entry); 2449 if (entry.longval != nums[25 + i]) 2450 ERR("Deleted invalid range! Expected %lld but got " 2451 "%lld", 2452 entry.longval, nums[25 + i]); 2453 } 2454 if (f == 32) 2455 ql_verify(ql, 1, 7, 7, 7); 2456 quicklistRelease(ql); 2457 } 2458 } 2459 2460 for (int f = optimize_start; f < 40; f++) { 2461 TEST_DESC("ltrim test B at fill %d at compress %d", f, 2462 options[_i]) { 2463 /* Force-disable compression because our 33 sequential 2464 * integers don't compress and the check always fails. */ 2465 quicklist *ql = quicklistNew(f, QUICKLIST_NOCOMPRESS); 2466 char num[32]; 2467 long long nums[5000]; 2468 for (int i = 0; i < 33; i++) { 2469 nums[i] = i; 2470 int sz = ll2string(num, sizeof(num), nums[i]); 2471 quicklistPushTail(ql, num, sz); 2472 } 2473 if (f == 32) 2474 ql_verify(ql, 2, 33, 32, 1); 2475 /* ltrim 5 16 (keep [5,16] inclusive = 12 remaining) */ 2476 quicklistDelRange(ql, 0, 5); 2477 quicklistDelRange(ql, -16, 16); 2478 if (f == 32) 2479 ql_verify(ql, 1, 12, 12, 12); 2480 quicklistEntry entry; 2481 quicklistIndex(ql, 0, &entry); 2482 if (entry.longval != 5) 2483 ERR("A: longval not 5, but %lld", entry.longval); 2484 else 2485 OK; 2486 quicklistIndex(ql, -1, &entry); 2487 if (entry.longval != 16) 2488 ERR("B! got instead: %lld", entry.longval); 2489 else 2490 OK; 2491 quicklistPushTail(ql, "bobobob", 7); 2492 quicklistIndex(ql, -1, &entry); 2493 if (strncmp((char *)entry.value, "bobobob", 7)) 2494 ERR("Tail doesn't match bobobob, it's %.*s instead", 2495 entry.sz, entry.value); 2496 for (int i = 0; i < 12; i++) { 2497 quicklistIndex(ql, i, &entry); 2498 if (entry.longval != nums[5 + i]) 2499 ERR("Deleted invalid range! Expected %lld but got " 2500 "%lld", 2501 entry.longval, nums[5 + i]); 2502 } 2503 quicklistRelease(ql); 2504 } 2505 } 2506 2507 for (int f = optimize_start; f < 40; f++) { 2508 TEST_DESC("ltrim test C at fill %d at compress %d", f, 2509 options[_i]) { 2510 quicklist *ql = quicklistNew(f, options[_i]); 2511 char num[32]; 2512 long long nums[5000]; 2513 for (int i = 0; i < 33; i++) { 2514 nums[i] = -5157318210846258176 + i; 2515 int sz = ll2string(num, sizeof(num), nums[i]); 2516 quicklistPushTail(ql, num, sz); 2517 } 2518 if (f == 32) 2519 ql_verify(ql, 2, 33, 32, 1); 2520 /* ltrim 3 3 (keep [3,3] inclusive = 1 remaining) */ 2521 quicklistDelRange(ql, 0, 3); 2522 quicklistDelRange(ql, -29, 2523 4000); /* make sure not loop forever */ 2524 if (f == 32) 2525 ql_verify(ql, 1, 1, 1, 1); 2526 quicklistEntry entry; 2527 quicklistIndex(ql, 0, &entry); 2528 if (entry.longval != -5157318210846258173) 2529 ERROR; 2530 else 2531 OK; 2532 quicklistRelease(ql); 2533 } 2534 } 2535 2536 for (int f = optimize_start; f < 40; f++) { 2537 TEST_DESC("ltrim test D at fill %d at compress %d", f, 2538 options[_i]) { 2539 quicklist *ql = quicklistNew(f, options[_i]); 2540 char num[32]; 2541 long long nums[5000]; 2542 for (int i = 0; i < 33; i++) { 2543 nums[i] = -5157318210846258176 + i; 2544 int sz = ll2string(num, sizeof(num), nums[i]); 2545 quicklistPushTail(ql, num, sz); 2546 } 2547 if (f == 32) 2548 ql_verify(ql, 2, 33, 32, 1); 2549 quicklistDelRange(ql, -12, 3); 2550 if (ql->count != 30) 2551 ERR("Didn't delete exactly three elements! Count is: %lu", 2552 ql->count); 2553 quicklistRelease(ql); 2554 } 2555 } 2556 2557 for (int f = optimize_start; f < 72; f++) { 2558 TEST_DESC("create quicklist from ziplist at fill %d at compress %d", 2559 f, options[_i]) { 2560 unsigned char *zl = ziplistNew(); 2561 long long nums[64]; 2562 char num[64]; 2563 for (int i = 0; i < 33; i++) { 2564 nums[i] = -5157318210846258176 + i; 2565 int sz = ll2string(num, sizeof(num), nums[i]); 2566 zl = 2567 ziplistPush(zl, (unsigned char *)num, sz, ZIPLIST_TAIL); 2568 } 2569 for (int i = 0; i < 33; i++) { 2570 zl = ziplistPush(zl, (unsigned char *)genstr("hello", i), 2571 32, ZIPLIST_TAIL); 2572 } 2573 quicklist *ql = quicklistCreateFromZiplist(f, options[_i], zl); 2574 if (f == 1) 2575 ql_verify(ql, 66, 66, 1, 1); 2576 else if (f == 32) 2577 ql_verify(ql, 3, 66, 32, 2); 2578 else if (f == 66) 2579 ql_verify(ql, 1, 66, 66, 66); 2580 quicklistRelease(ql); 2581 } 2582 } 2583 2584 long long stop = mstime(); 2585 runtime[_i] = stop - start; 2586 } 2587 2588 /* Run a longer test of compression depth outside of primary test loop. */ 2589 int list_sizes[] = {250, 251, 500, 999, 1000}; 2590 long long start = mstime(); 2591 for (int list = 0; list < (int)(sizeof(list_sizes) / sizeof(*list_sizes)); 2592 list++) { 2593 for (int f = optimize_start; f < 128; f++) { 2594 for (int depth = 1; depth < 40; depth++) { 2595 /* skip over many redundant test cases */ 2596 TEST_DESC("verify specific compression of interior nodes with " 2597 "%d list " 2598 "at fill %d at compress %d", 2599 list_sizes[list], f, depth) { 2600 quicklist *ql = quicklistNew(f, depth); 2601 for (int i = 0; i < list_sizes[list]; i++) { 2602 quicklistPushTail(ql, genstr("hello TAIL", i + 1), 64); 2603 quicklistPushHead(ql, genstr("hello HEAD", i + 1), 64); 2604 } 2605 2606 quicklistNode *node = ql->head; 2607 unsigned int low_raw = ql->compress; 2608 unsigned int high_raw = ql->len - ql->compress; 2609 2610 for (unsigned int at = 0; at < ql->len; 2611 at++, node = node->next) { 2612 if (at < low_raw || at >= high_raw) { 2613 if (node->encoding != QUICKLIST_NODE_ENCODING_RAW) { 2614 ERR("Incorrect compression: node %d is " 2615 "compressed at depth %d ((%u, %u); total " 2616 "nodes: %u; size: %u)", 2617 at, depth, low_raw, high_raw, ql->len, 2618 node->sz); 2619 } 2620 } else { 2621 if (node->encoding != QUICKLIST_NODE_ENCODING_LZF) { 2622 ERR("Incorrect non-compression: node %d is NOT " 2623 "compressed at depth %d ((%u, %u); total " 2624 "nodes: %u; size: %u; attempted: %d)", 2625 at, depth, low_raw, high_raw, ql->len, 2626 node->sz, node->attempted_compress); 2627 } 2628 } 2629 } 2630 quicklistRelease(ql); 2631 } 2632 } 2633 } 2634 } 2635 long long stop = mstime(); 2636 2637 printf("\n"); 2638 for (size_t i = 0; i < option_count; i++) 2639 printf("Test Loop %02d: %0.2f seconds.\n", options[i], 2640 (float)runtime[i] / 1000); 2641 printf("Compressions: %0.2f seconds.\n", (float)(stop - start) / 1000); 2642 printf("\n"); 2643 2644 if (!err) 2645 printf("ALL TESTS PASSED!\n"); 2646 else 2647 ERR("Sorry, not all tests passed! In fact, %d tests failed.", err); 2648 2649 return err; 2650 } 2651 #endif 2652