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 int quicklistCount(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 quicklistCompress(quicklist, entry.node); 675 return 1; 676 } else { 677 return 0; 678 } 679 } 680 681 /* Given two nodes, try to merge their ziplists. 682 * 683 * This helps us not have a quicklist with 3 element ziplists if 684 * our fill factor can handle much higher levels. 685 * 686 * Note: 'a' must be to the LEFT of 'b'. 687 * 688 * After calling this function, both 'a' and 'b' should be considered 689 * unusable. The return value from this function must be used 690 * instead of re-using any of the quicklistNode input arguments. 691 * 692 * Returns the input node picked to merge against or NULL if 693 * merging was not possible. */ 694 REDIS_STATIC quicklistNode *_quicklistZiplistMerge(quicklist *quicklist, 695 quicklistNode *a, 696 quicklistNode *b) { 697 D("Requested merge (a,b) (%u, %u)", a->count, b->count); 698 699 quicklistDecompressNode(a); 700 quicklistDecompressNode(b); 701 if ((ziplistMerge(&a->zl, &b->zl))) { 702 /* We merged ziplists! Now remove the unused quicklistNode. */ 703 quicklistNode *keep = NULL, *nokeep = NULL; 704 if (!a->zl) { 705 nokeep = a; 706 keep = b; 707 } else if (!b->zl) { 708 nokeep = b; 709 keep = a; 710 } 711 keep->count = ziplistLen(keep->zl); 712 quicklistNodeUpdateSz(keep); 713 714 nokeep->count = 0; 715 __quicklistDelNode(quicklist, nokeep); 716 quicklistCompress(quicklist, keep); 717 return keep; 718 } else { 719 /* else, the merge returned NULL and nothing changed. */ 720 return NULL; 721 } 722 } 723 724 /* Attempt to merge ziplists within two nodes on either side of 'center'. 725 * 726 * We attempt to merge: 727 * - (center->prev->prev, center->prev) 728 * - (center->next, center->next->next) 729 * - (center->prev, center) 730 * - (center, center->next) 731 */ 732 REDIS_STATIC void _quicklistMergeNodes(quicklist *quicklist, 733 quicklistNode *center) { 734 int fill = quicklist->fill; 735 quicklistNode *prev, *prev_prev, *next, *next_next, *target; 736 prev = prev_prev = next = next_next = target = NULL; 737 738 if (center->prev) { 739 prev = center->prev; 740 if (center->prev->prev) 741 prev_prev = center->prev->prev; 742 } 743 744 if (center->next) { 745 next = center->next; 746 if (center->next->next) 747 next_next = center->next->next; 748 } 749 750 /* Try to merge prev_prev and prev */ 751 if (_quicklistNodeAllowMerge(prev, prev_prev, fill)) { 752 _quicklistZiplistMerge(quicklist, prev_prev, prev); 753 prev_prev = prev = NULL; /* they could have moved, invalidate them. */ 754 } 755 756 /* Try to merge next and next_next */ 757 if (_quicklistNodeAllowMerge(next, next_next, fill)) { 758 _quicklistZiplistMerge(quicklist, next, next_next); 759 next = next_next = NULL; /* they could have moved, invalidate them. */ 760 } 761 762 /* Try to merge center node and previous node */ 763 if (_quicklistNodeAllowMerge(center, center->prev, fill)) { 764 target = _quicklistZiplistMerge(quicklist, center->prev, center); 765 center = NULL; /* center could have been deleted, invalidate it. */ 766 } else { 767 /* else, we didn't merge here, but target needs to be valid below. */ 768 target = center; 769 } 770 771 /* Use result of center merge (or original) to merge with next node. */ 772 if (_quicklistNodeAllowMerge(target, target->next, fill)) { 773 _quicklistZiplistMerge(quicklist, target, target->next); 774 } 775 } 776 777 /* Split 'node' into two parts, parameterized by 'offset' and 'after'. 778 * 779 * The 'after' argument controls which quicklistNode gets returned. 780 * If 'after'==1, returned node has elements after 'offset'. 781 * input node keeps elements up to 'offset', including 'offset'. 782 * If 'after'==0, returned node has elements up to 'offset', including 'offset'. 783 * input node keeps elements after 'offset'. 784 * 785 * If 'after'==1, returned node will have elements _after_ 'offset'. 786 * The returned node will have elements [OFFSET+1, END]. 787 * The input node keeps elements [0, OFFSET]. 788 * 789 * If 'after'==0, returned node will keep elements up to and including 'offset'. 790 * The returned node will have elements [0, OFFSET]. 791 * The input node keeps elements [OFFSET+1, END]. 792 * 793 * The input node keeps all elements not taken by the returned node. 794 * 795 * Returns newly created node or NULL if split not possible. */ 796 REDIS_STATIC quicklistNode *_quicklistSplitNode(quicklistNode *node, int offset, 797 int after) { 798 size_t zl_sz = node->sz; 799 800 quicklistNode *new_node = quicklistCreateNode(); 801 new_node->zl = zmalloc(zl_sz); 802 803 /* Copy original ziplist so we can split it */ 804 memcpy(new_node->zl, node->zl, zl_sz); 805 806 /* -1 here means "continue deleting until the list ends" */ 807 int orig_start = after ? offset + 1 : 0; 808 int orig_extent = after ? -1 : offset; 809 int new_start = after ? 0 : offset; 810 int new_extent = after ? offset + 1 : -1; 811 812 D("After %d (%d); ranges: [%d, %d], [%d, %d]", after, offset, orig_start, 813 orig_extent, new_start, new_extent); 814 815 node->zl = ziplistDeleteRange(node->zl, orig_start, orig_extent); 816 node->count = ziplistLen(node->zl); 817 quicklistNodeUpdateSz(node); 818 819 new_node->zl = ziplistDeleteRange(new_node->zl, new_start, new_extent); 820 new_node->count = ziplistLen(new_node->zl); 821 quicklistNodeUpdateSz(new_node); 822 823 D("After split lengths: orig (%d), new (%d)", node->count, new_node->count); 824 return new_node; 825 } 826 827 /* Insert a new entry before or after existing entry 'entry'. 828 * 829 * If after==1, the new value is inserted after 'entry', otherwise 830 * the new value is inserted before 'entry'. */ 831 REDIS_STATIC void _quicklistInsert(quicklist *quicklist, quicklistEntry *entry, 832 void *value, const size_t sz, int after) { 833 int full = 0, at_tail = 0, at_head = 0, full_next = 0, full_prev = 0; 834 int fill = quicklist->fill; 835 quicklistNode *node = entry->node; 836 quicklistNode *new_node = NULL; 837 838 if (!node) { 839 /* we have no reference node, so let's create only node in the list */ 840 D("No node given!"); 841 new_node = quicklistCreateNode(); 842 new_node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_HEAD); 843 __quicklistInsertNode(quicklist, NULL, new_node, after); 844 new_node->count++; 845 quicklist->count++; 846 return; 847 } 848 849 /* Populate accounting flags for easier boolean checks later */ 850 if (!_quicklistNodeAllowInsert(node, fill, sz)) { 851 D("Current node is full with count %d with requested fill %lu", 852 node->count, fill); 853 full = 1; 854 } 855 856 if (after && (entry->offset == node->count)) { 857 D("At Tail of current ziplist"); 858 at_tail = 1; 859 if (!_quicklistNodeAllowInsert(node->next, fill, sz)) { 860 D("Next node is full too."); 861 full_next = 1; 862 } 863 } 864 865 if (!after && (entry->offset == 0)) { 866 D("At Head"); 867 at_head = 1; 868 if (!_quicklistNodeAllowInsert(node->prev, fill, sz)) { 869 D("Prev node is full too."); 870 full_prev = 1; 871 } 872 } 873 874 /* Now determine where and how to insert the new element */ 875 if (!full && after) { 876 D("Not full, inserting after current position."); 877 quicklistDecompressNodeForUse(node); 878 unsigned char *next = ziplistNext(node->zl, entry->zi); 879 if (next == NULL) { 880 node->zl = ziplistPush(node->zl, value, sz, ZIPLIST_TAIL); 881 } else { 882 node->zl = ziplistInsert(node->zl, next, value, sz); 883 } 884 node->count++; 885 quicklistNodeUpdateSz(node); 886 quicklistRecompressOnly(quicklist, node); 887 } else if (!full && !after) { 888 D("Not full, inserting before current position."); 889 quicklistDecompressNodeForUse(node); 890 node->zl = ziplistInsert(node->zl, entry->zi, value, sz); 891 node->count++; 892 quicklistNodeUpdateSz(node); 893 quicklistRecompressOnly(quicklist, node); 894 } else if (full && at_tail && node->next && !full_next && after) { 895 /* If we are: at tail, next has free space, and inserting after: 896 * - insert entry at head of next node. */ 897 D("Full and tail, but next isn't full; inserting next node head"); 898 new_node = node->next; 899 quicklistDecompressNodeForUse(new_node); 900 new_node->zl = ziplistPush(new_node->zl, value, sz, ZIPLIST_HEAD); 901 new_node->count++; 902 quicklistNodeUpdateSz(new_node); 903 quicklistRecompressOnly(quicklist, new_node); 904 } else if (full && at_head && node->prev && !full_prev && !after) { 905 /* If we are: at head, previous has free space, and inserting before: 906 * - insert entry at tail of previous node. */ 907 D("Full and head, but prev isn't full, inserting prev node tail"); 908 new_node = node->prev; 909 quicklistDecompressNodeForUse(new_node); 910 new_node->zl = ziplistPush(new_node->zl, value, sz, ZIPLIST_TAIL); 911 new_node->count++; 912 quicklistNodeUpdateSz(new_node); 913 quicklistRecompressOnly(quicklist, new_node); 914 } else if (full && ((at_tail && node->next && full_next && after) || 915 (at_head && node->prev && full_prev && !after))) { 916 /* If we are: full, and our prev/next is full, then: 917 * - create new node and attach to quicklist */ 918 D("\tprovisioning new node..."); 919 new_node = quicklistCreateNode(); 920 new_node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_HEAD); 921 new_node->count++; 922 quicklistNodeUpdateSz(new_node); 923 __quicklistInsertNode(quicklist, node, new_node, after); 924 } else if (full) { 925 /* else, node is full we need to split it. */ 926 /* covers both after and !after cases */ 927 D("\tsplitting node..."); 928 quicklistDecompressNodeForUse(node); 929 new_node = _quicklistSplitNode(node, entry->offset, after); 930 new_node->zl = ziplistPush(new_node->zl, value, sz, 931 after ? ZIPLIST_HEAD : ZIPLIST_TAIL); 932 new_node->count++; 933 quicklistNodeUpdateSz(new_node); 934 __quicklistInsertNode(quicklist, node, new_node, after); 935 _quicklistMergeNodes(quicklist, node); 936 } 937 938 quicklist->count++; 939 } 940 941 void quicklistInsertBefore(quicklist *quicklist, quicklistEntry *entry, 942 void *value, const size_t sz) { 943 _quicklistInsert(quicklist, entry, value, sz, 0); 944 } 945 946 void quicklistInsertAfter(quicklist *quicklist, quicklistEntry *entry, 947 void *value, const size_t sz) { 948 _quicklistInsert(quicklist, entry, value, sz, 1); 949 } 950 951 /* Delete a range of elements from the quicklist. 952 * 953 * elements may span across multiple quicklistNodes, so we 954 * have to be careful about tracking where we start and end. 955 * 956 * Returns 1 if entries were deleted, 0 if nothing was deleted. */ 957 int quicklistDelRange(quicklist *quicklist, const long start, 958 const long count) { 959 if (count <= 0) 960 return 0; 961 962 unsigned long extent = count; /* range is inclusive of start position */ 963 964 if (start >= 0 && extent > (quicklist->count - start)) { 965 /* if requesting delete more elements than exist, limit to list size. */ 966 extent = quicklist->count - start; 967 } else if (start < 0 && extent > (unsigned long)(-start)) { 968 /* else, if at negative offset, limit max size to rest of list. */ 969 extent = -start; /* c.f. LREM -29 29; just delete until end. */ 970 } 971 972 quicklistEntry entry; 973 if (!quicklistIndex(quicklist, start, &entry)) 974 return 0; 975 976 D("Quicklist delete request for start %ld, count %ld, extent: %ld", start, 977 count, extent); 978 quicklistNode *node = entry.node; 979 980 /* iterate over next nodes until everything is deleted. */ 981 while (extent) { 982 quicklistNode *next = node->next; 983 984 unsigned long del; 985 int delete_entire_node = 0; 986 if (entry.offset == 0 && extent >= node->count) { 987 /* If we are deleting more than the count of this node, we 988 * can just delete the entire node without ziplist math. */ 989 delete_entire_node = 1; 990 del = node->count; 991 } else if (entry.offset >= 0 && extent >= node->count) { 992 /* If deleting more nodes after this one, calculate delete based 993 * on size of current node. */ 994 del = node->count - entry.offset; 995 } else if (entry.offset < 0) { 996 /* If offset is negative, we are in the first run of this loop 997 * and we are deleting the entire range 998 * from this start offset to end of list. Since the Negative 999 * offset is the number of elements until the tail of the list, 1000 * just use it directly as the deletion count. */ 1001 del = -entry.offset; 1002 1003 /* If the positive offset is greater than the remaining extent, 1004 * we only delete the remaining extent, not the entire offset. 1005 */ 1006 if (del > extent) 1007 del = extent; 1008 } else { 1009 /* else, we are deleting less than the extent of this node, so 1010 * use extent directly. */ 1011 del = extent; 1012 } 1013 1014 D("[%ld]: asking to del: %ld because offset: %d; (ENTIRE NODE: %d), " 1015 "node count: %u", 1016 extent, del, entry.offset, delete_entire_node, node->count); 1017 1018 if (delete_entire_node) { 1019 __quicklistDelNode(quicklist, node); 1020 } else { 1021 quicklistDecompressNodeForUse(node); 1022 node->zl = ziplistDeleteRange(node->zl, entry.offset, del); 1023 quicklistNodeUpdateSz(node); 1024 node->count -= del; 1025 quicklist->count -= del; 1026 quicklistDeleteIfEmpty(quicklist, node); 1027 if (node) 1028 quicklistRecompressOnly(quicklist, node); 1029 } 1030 1031 extent -= del; 1032 1033 node = next; 1034 1035 entry.offset = 0; 1036 } 1037 return 1; 1038 } 1039 1040 /* Passthrough to ziplistCompare() */ 1041 int quicklistCompare(unsigned char *p1, unsigned char *p2, int p2_len) { 1042 return ziplistCompare(p1, p2, p2_len); 1043 } 1044 1045 /* Returns a quicklist iterator 'iter'. After the initialization every 1046 * call to quicklistNext() will return the next element of the quicklist. */ 1047 quicklistIter *quicklistGetIterator(const quicklist *quicklist, int direction) { 1048 quicklistIter *iter; 1049 1050 iter = zmalloc(sizeof(*iter)); 1051 1052 if (direction == AL_START_HEAD) { 1053 iter->current = quicklist->head; 1054 iter->offset = 0; 1055 } else if (direction == AL_START_TAIL) { 1056 iter->current = quicklist->tail; 1057 iter->offset = -1; 1058 } 1059 1060 iter->direction = direction; 1061 iter->quicklist = quicklist; 1062 1063 iter->zi = NULL; 1064 1065 return iter; 1066 } 1067 1068 /* Initialize an iterator at a specific offset 'idx' and make the iterator 1069 * return nodes in 'direction' direction. */ 1070 quicklistIter *quicklistGetIteratorAtIdx(const quicklist *quicklist, 1071 const int direction, 1072 const long long idx) { 1073 quicklistEntry entry; 1074 1075 if (quicklistIndex(quicklist, idx, &entry)) { 1076 quicklistIter *base = quicklistGetIterator(quicklist, direction); 1077 base->zi = NULL; 1078 base->current = entry.node; 1079 base->offset = entry.offset; 1080 return base; 1081 } else { 1082 return NULL; 1083 } 1084 } 1085 1086 /* Release iterator. 1087 * If we still have a valid current node, then re-encode current node. */ 1088 void quicklistReleaseIterator(quicklistIter *iter) { 1089 if (iter->current) 1090 quicklistCompress(iter->quicklist, iter->current); 1091 1092 zfree(iter); 1093 } 1094 1095 /* Get next element in iterator. 1096 * 1097 * Note: You must NOT insert into the list while iterating over it. 1098 * You *may* delete from the list while iterating using the 1099 * quicklistDelEntry() function. 1100 * If you insert into the quicklist while iterating, you should 1101 * re-create the iterator after your addition. 1102 * 1103 * iter = quicklistGetIterator(quicklist,<direction>); 1104 * quicklistEntry entry; 1105 * while (quicklistNext(iter, &entry)) { 1106 * if (entry.value) 1107 * [[ use entry.value with entry.sz ]] 1108 * else 1109 * [[ use entry.longval ]] 1110 * } 1111 * 1112 * Populates 'entry' with values for this iteration. 1113 * Returns 0 when iteration is complete or if iteration not possible. 1114 * If return value is 0, the contents of 'entry' are not valid. 1115 */ 1116 int quicklistNext(quicklistIter *iter, quicklistEntry *entry) { 1117 initEntry(entry); 1118 1119 if (!iter) { 1120 D("Returning because no iter!"); 1121 return 0; 1122 } 1123 1124 entry->quicklist = iter->quicklist; 1125 entry->node = iter->current; 1126 1127 if (!iter->current) { 1128 D("Returning because current node is NULL") 1129 return 0; 1130 } 1131 1132 unsigned char *(*nextFn)(unsigned char *, unsigned char *) = NULL; 1133 int offset_update = 0; 1134 1135 if (!iter->zi) { 1136 /* If !zi, use current index. */ 1137 quicklistDecompressNodeForUse(iter->current); 1138 iter->zi = ziplistIndex(iter->current->zl, iter->offset); 1139 } else { 1140 /* else, use existing iterator offset and get prev/next as necessary. */ 1141 if (iter->direction == AL_START_HEAD) { 1142 nextFn = ziplistNext; 1143 offset_update = 1; 1144 } else if (iter->direction == AL_START_TAIL) { 1145 nextFn = ziplistPrev; 1146 offset_update = -1; 1147 } 1148 iter->zi = nextFn(iter->current->zl, iter->zi); 1149 iter->offset += offset_update; 1150 } 1151 1152 entry->zi = iter->zi; 1153 entry->offset = iter->offset; 1154 1155 if (iter->zi) { 1156 /* Populate value from existing ziplist position */ 1157 ziplistGet(entry->zi, &entry->value, &entry->sz, &entry->longval); 1158 return 1; 1159 } else { 1160 /* We ran out of ziplist entries. 1161 * Pick next node, update offset, then re-run retrieval. */ 1162 quicklistCompress(iter->quicklist, iter->current); 1163 if (iter->direction == AL_START_HEAD) { 1164 /* Forward traversal */ 1165 D("Jumping to start of next node"); 1166 iter->current = iter->current->next; 1167 iter->offset = 0; 1168 } else if (iter->direction == AL_START_TAIL) { 1169 /* Reverse traversal */ 1170 D("Jumping to end of previous node"); 1171 iter->current = iter->current->prev; 1172 iter->offset = -1; 1173 } 1174 iter->zi = NULL; 1175 return quicklistNext(iter, entry); 1176 } 1177 } 1178 1179 /* Duplicate the quicklist. 1180 * On success a copy of the original quicklist is returned. 1181 * 1182 * The original quicklist both on success or error is never modified. 1183 * 1184 * Returns newly allocated quicklist. */ 1185 quicklist *quicklistDup(quicklist *orig) { 1186 quicklist *copy; 1187 1188 copy = quicklistNew(orig->fill, orig->compress); 1189 1190 for (quicklistNode *current = orig->head; current; 1191 current = current->next) { 1192 quicklistNode *node = quicklistCreateNode(); 1193 1194 if (node->encoding == QUICKLIST_NODE_ENCODING_LZF) { 1195 quicklistLZF *lzf = (quicklistLZF *)node->zl; 1196 size_t lzf_sz = sizeof(*lzf) + lzf->sz; 1197 node->zl = zmalloc(lzf_sz); 1198 memcpy(node->zl, current->zl, lzf_sz); 1199 } else if (node->encoding == QUICKLIST_NODE_ENCODING_RAW) { 1200 node->zl = zmalloc(current->sz); 1201 memcpy(node->zl, current->zl, current->sz); 1202 } 1203 1204 node->count = current->count; 1205 copy->count += node->count; 1206 node->sz = current->sz; 1207 node->encoding = current->encoding; 1208 1209 _quicklistInsertNodeAfter(copy, copy->tail, node); 1210 } 1211 1212 /* copy->count must equal orig->count here */ 1213 return copy; 1214 } 1215 1216 /* Populate 'entry' with the element at the specified zero-based index 1217 * where 0 is the head, 1 is the element next to head 1218 * and so on. Negative integers are used in order to count 1219 * from the tail, -1 is the last element, -2 the penultimate 1220 * and so on. If the index is out of range 0 is returned. 1221 * 1222 * Returns 1 if element found 1223 * Returns 0 if element not found */ 1224 int quicklistIndex(const quicklist *quicklist, const long long idx, 1225 quicklistEntry *entry) { 1226 quicklistNode *n; 1227 unsigned long long accum = 0; 1228 unsigned long long index; 1229 int forward = idx < 0 ? 0 : 1; /* < 0 -> reverse, 0+ -> forward */ 1230 1231 initEntry(entry); 1232 entry->quicklist = quicklist; 1233 1234 if (!forward) { 1235 index = (-idx) - 1; 1236 n = quicklist->tail; 1237 } else { 1238 index = idx; 1239 n = quicklist->head; 1240 } 1241 1242 if (index >= quicklist->count) 1243 return 0; 1244 1245 while (likely(n)) { 1246 if ((accum + n->count) > index) { 1247 break; 1248 } else { 1249 D("Skipping over (%p) %u at accum %lld", (void *)n, n->count, 1250 accum); 1251 accum += n->count; 1252 n = forward ? n->next : n->prev; 1253 } 1254 } 1255 1256 if (!n) 1257 return 0; 1258 1259 D("Found node: %p at accum %llu, idx %llu, sub+ %llu, sub- %llu", (void *)n, 1260 accum, index, index - accum, (-index) - 1 + accum); 1261 1262 entry->node = n; 1263 if (forward) { 1264 /* forward = normal head-to-tail offset. */ 1265 entry->offset = index - accum; 1266 } else { 1267 /* reverse = need negative offset for tail-to-head, so undo 1268 * the result of the original if (index < 0) above. */ 1269 entry->offset = (-index) - 1 + accum; 1270 } 1271 1272 quicklistDecompressNodeForUse(entry->node); 1273 entry->zi = ziplistIndex(entry->node->zl, entry->offset); 1274 ziplistGet(entry->zi, &entry->value, &entry->sz, &entry->longval); 1275 /* The caller will use our result, so we don't re-compress here. 1276 * The caller can recompress or delete the node as needed. */ 1277 return 1; 1278 } 1279 1280 /* Rotate quicklist by moving the tail element to the head. */ 1281 void quicklistRotate(quicklist *quicklist) { 1282 if (quicklist->count <= 1) 1283 return; 1284 1285 /* First, get the tail entry */ 1286 unsigned char *p = ziplistIndex(quicklist->tail->zl, -1); 1287 unsigned char *value; 1288 long long longval; 1289 unsigned int sz; 1290 char longstr[32] = {0}; 1291 ziplistGet(p, &value, &sz, &longval); 1292 1293 /* If value found is NULL, then ziplistGet populated longval instead */ 1294 if (!value) { 1295 /* Write the longval as a string so we can re-add it */ 1296 sz = ll2string(longstr, sizeof(longstr), longval); 1297 value = (unsigned char *)longstr; 1298 } 1299 1300 /* Add tail entry to head (must happen before tail is deleted). */ 1301 quicklistPushHead(quicklist, value, sz); 1302 1303 /* If quicklist has only one node, the head ziplist is also the 1304 * tail ziplist and PushHead() could have reallocated our single ziplist, 1305 * which would make our pre-existing 'p' unusable. */ 1306 if (quicklist->len == 1) { 1307 p = ziplistIndex(quicklist->tail->zl, -1); 1308 } 1309 1310 /* Remove tail entry. */ 1311 quicklistDelIndex(quicklist, quicklist->tail, &p); 1312 } 1313 1314 /* pop from quicklist and return result in 'data' ptr. Value of 'data' 1315 * is the return value of 'saver' function pointer if the data is NOT a number. 1316 * 1317 * If the quicklist element is a long long, then the return value is returned in 1318 * 'sval'. 1319 * 1320 * Return value of 0 means no elements available. 1321 * Return value of 1 means check 'data' and 'sval' for values. 1322 * If 'data' is set, use 'data' and 'sz'. Otherwise, use 'sval'. */ 1323 int quicklistPopCustom(quicklist *quicklist, int where, unsigned char **data, 1324 unsigned int *sz, long long *sval, 1325 void *(*saver)(unsigned char *data, unsigned int sz)) { 1326 unsigned char *p; 1327 unsigned char *vstr; 1328 unsigned int vlen; 1329 long long vlong; 1330 int pos = (where == QUICKLIST_HEAD) ? 0 : -1; 1331 1332 if (quicklist->count == 0) 1333 return 0; 1334 1335 if (data) 1336 *data = NULL; 1337 if (sz) 1338 *sz = 0; 1339 if (sval) 1340 *sval = -123456789; 1341 1342 quicklistNode *node; 1343 if (where == QUICKLIST_HEAD && quicklist->head) { 1344 node = quicklist->head; 1345 } else if (where == QUICKLIST_TAIL && quicklist->tail) { 1346 node = quicklist->tail; 1347 } else { 1348 return 0; 1349 } 1350 1351 p = ziplistIndex(node->zl, pos); 1352 if (ziplistGet(p, &vstr, &vlen, &vlong)) { 1353 if (vstr) { 1354 if (data) 1355 *data = saver(vstr, vlen); 1356 if (sz) 1357 *sz = vlen; 1358 } else { 1359 if (data) 1360 *data = NULL; 1361 if (sval) 1362 *sval = vlong; 1363 } 1364 quicklistDelIndex(quicklist, node, &p); 1365 return 1; 1366 } 1367 return 0; 1368 } 1369 1370 /* Return a malloc'd copy of data passed in */ 1371 REDIS_STATIC void *_quicklistSaver(unsigned char *data, unsigned int sz) { 1372 unsigned char *vstr; 1373 if (data) { 1374 vstr = zmalloc(sz); 1375 memcpy(vstr, data, sz); 1376 return vstr; 1377 } 1378 return NULL; 1379 } 1380 1381 /* Default pop function 1382 * 1383 * Returns malloc'd value from quicklist */ 1384 int quicklistPop(quicklist *quicklist, int where, unsigned char **data, 1385 unsigned int *sz, long long *slong) { 1386 unsigned char *vstr; 1387 unsigned int vlen; 1388 long long vlong; 1389 if (quicklist->count == 0) 1390 return 0; 1391 int ret = quicklistPopCustom(quicklist, where, &vstr, &vlen, &vlong, 1392 _quicklistSaver); 1393 if (data) 1394 *data = vstr; 1395 if (slong) 1396 *slong = vlong; 1397 if (sz) 1398 *sz = vlen; 1399 return ret; 1400 } 1401 1402 /* Wrapper to allow argument-based switching between HEAD/TAIL pop */ 1403 void quicklistPush(quicklist *quicklist, void *value, const size_t sz, 1404 int where) { 1405 if (where == QUICKLIST_HEAD) { 1406 quicklistPushHead(quicklist, value, sz); 1407 } else if (where == QUICKLIST_TAIL) { 1408 quicklistPushTail(quicklist, value, sz); 1409 } 1410 } 1411 1412 /* The rest of this file is test cases and test helpers. */ 1413 #ifdef REDIS_TEST 1414 #include <stdint.h> 1415 #include <sys/time.h> 1416 1417 #define assert(_e) \ 1418 do { \ 1419 if (!(_e)) { \ 1420 printf("\n\n=== ASSERTION FAILED ===\n"); \ 1421 printf("==> %s:%d '%s' is not true\n", __FILE__, __LINE__, #_e); \ 1422 err++; \ 1423 } \ 1424 } while (0) 1425 1426 #define yell(str, ...) printf("ERROR! " str "\n\n", __VA_ARGS__) 1427 1428 #define OK printf("\tOK\n") 1429 1430 #define ERROR \ 1431 do { \ 1432 printf("\tERROR!\n"); \ 1433 err++; \ 1434 } while (0) 1435 1436 #define ERR(x, ...) \ 1437 do { \ 1438 printf("%s:%s:%d:\t", __FILE__, __FUNCTION__, __LINE__); \ 1439 printf("ERROR! " x "\n", __VA_ARGS__); \ 1440 err++; \ 1441 } while (0) 1442 1443 #define TEST(name) printf("test — %s\n", name); 1444 #define TEST_DESC(name, ...) printf("test — " name "\n", __VA_ARGS__); 1445 1446 #define QL_TEST_VERBOSE 0 1447 1448 #define UNUSED(x) (void)(x) 1449 static void ql_info(quicklist *ql) { 1450 #if QL_TEST_VERBOSE 1451 printf("Container length: %lu\n", ql->len); 1452 printf("Container size: %lu\n", ql->count); 1453 if (ql->head) 1454 printf("\t(zsize head: %d)\n", ziplistLen(ql->head->zl)); 1455 if (ql->tail) 1456 printf("\t(zsize tail: %d)\n", ziplistLen(ql->tail->zl)); 1457 printf("\n"); 1458 #else 1459 UNUSED(ql); 1460 #endif 1461 } 1462 1463 /* Return the UNIX time in microseconds */ 1464 static long long ustime(void) { 1465 struct timeval tv; 1466 long long ust; 1467 1468 gettimeofday(&tv, NULL); 1469 ust = ((long long)tv.tv_sec) * 1000000; 1470 ust += tv.tv_usec; 1471 return ust; 1472 } 1473 1474 /* Return the UNIX time in milliseconds */ 1475 static long long mstime(void) { return ustime() / 1000; } 1476 1477 /* Iterate over an entire quicklist. 1478 * Print the list if 'print' == 1. 1479 * 1480 * Returns physical count of elements found by iterating over the list. */ 1481 static int _itrprintr(quicklist *ql, int print, int forward) { 1482 quicklistIter *iter = 1483 quicklistGetIterator(ql, forward ? AL_START_HEAD : AL_START_TAIL); 1484 quicklistEntry entry; 1485 int i = 0; 1486 int p = 0; 1487 quicklistNode *prev = NULL; 1488 while (quicklistNext(iter, &entry)) { 1489 if (entry.node != prev) { 1490 /* Count the number of list nodes too */ 1491 p++; 1492 prev = entry.node; 1493 } 1494 if (print) { 1495 printf("[%3d (%2d)]: [%.*s] (%lld)\n", i, p, entry.sz, 1496 (char *)entry.value, entry.longval); 1497 } 1498 i++; 1499 } 1500 quicklistReleaseIterator(iter); 1501 return i; 1502 } 1503 static int itrprintr(quicklist *ql, int print) { 1504 return _itrprintr(ql, print, 1); 1505 } 1506 1507 static int itrprintr_rev(quicklist *ql, int print) { 1508 return _itrprintr(ql, print, 0); 1509 } 1510 1511 #define ql_verify(a, b, c, d, e) \ 1512 do { \ 1513 err += _ql_verify((a), (b), (c), (d), (e)); \ 1514 } while (0) 1515 1516 /* Verify list metadata matches physical list contents. */ 1517 static int _ql_verify(quicklist *ql, uint32_t len, uint32_t count, 1518 uint32_t head_count, uint32_t tail_count) { 1519 int errors = 0; 1520 1521 ql_info(ql); 1522 if (len != ql->len) { 1523 yell("quicklist length wrong: expected %d, got %u", len, ql->len); 1524 errors++; 1525 } 1526 1527 if (count != ql->count) { 1528 yell("quicklist count wrong: expected %d, got %lu", count, ql->count); 1529 errors++; 1530 } 1531 1532 int loopr = itrprintr(ql, 0); 1533 if (loopr != (int)ql->count) { 1534 yell("quicklist cached count not match actual count: expected %lu, got " 1535 "%d", 1536 ql->count, loopr); 1537 errors++; 1538 } 1539 1540 int rloopr = itrprintr_rev(ql, 0); 1541 if (loopr != rloopr) { 1542 yell("quicklist has different forward count than reverse count! " 1543 "Forward count is %d, reverse count is %d.", 1544 loopr, rloopr); 1545 errors++; 1546 } 1547 1548 if (ql->len == 0 && !errors) { 1549 OK; 1550 return errors; 1551 } 1552 1553 if (ql->head && head_count != ql->head->count && 1554 head_count != ziplistLen(ql->head->zl)) { 1555 yell("quicklist head count wrong: expected %d, " 1556 "got cached %d vs. actual %d", 1557 head_count, ql->head->count, ziplistLen(ql->head->zl)); 1558 errors++; 1559 } 1560 1561 if (ql->tail && tail_count != ql->tail->count && 1562 tail_count != ziplistLen(ql->tail->zl)) { 1563 yell("quicklist tail count wrong: expected %d, " 1564 "got cached %u vs. actual %d", 1565 tail_count, ql->tail->count, ziplistLen(ql->tail->zl)); 1566 errors++; 1567 } 1568 1569 if (quicklistAllowsCompression(ql)) { 1570 quicklistNode *node = ql->head; 1571 unsigned int low_raw = ql->compress; 1572 unsigned int high_raw = ql->len - ql->compress; 1573 1574 for (unsigned int at = 0; at < ql->len; at++, node = node->next) { 1575 if (node && (at < low_raw || at >= high_raw)) { 1576 if (node->encoding != QUICKLIST_NODE_ENCODING_RAW) { 1577 yell("Incorrect compression: node %d is " 1578 "compressed at depth %d ((%u, %u); total " 1579 "nodes: %u; size: %u; recompress: %d)", 1580 at, ql->compress, low_raw, high_raw, ql->len, node->sz, 1581 node->recompress); 1582 errors++; 1583 } 1584 } else { 1585 if (node->encoding != QUICKLIST_NODE_ENCODING_LZF && 1586 !node->attempted_compress) { 1587 yell("Incorrect non-compression: node %d is NOT " 1588 "compressed at depth %d ((%u, %u); total " 1589 "nodes: %u; size: %u; recompress: %d; attempted: %d)", 1590 at, ql->compress, low_raw, high_raw, ql->len, node->sz, 1591 node->recompress, node->attempted_compress); 1592 errors++; 1593 } 1594 } 1595 } 1596 } 1597 1598 if (!errors) 1599 OK; 1600 return errors; 1601 } 1602 1603 /* Generate new string concatenating integer i against string 'prefix' */ 1604 static char *genstr(char *prefix, int i) { 1605 static char result[64] = {0}; 1606 snprintf(result, sizeof(result), "%s%d", prefix, i); 1607 return result; 1608 } 1609 1610 /* main test, but callable from other files */ 1611 int quicklistTest(int argc, char *argv[]) { 1612 UNUSED(argc); 1613 UNUSED(argv); 1614 1615 unsigned int err = 0; 1616 int optimize_start = 1617 -(int)(sizeof(optimization_level) / sizeof(*optimization_level)); 1618 1619 printf("Starting optimization offset at: %d\n", optimize_start); 1620 1621 int options[] = {0, 1, 2, 3, 4, 5, 6, 10}; 1622 size_t option_count = sizeof(options) / sizeof(*options); 1623 long long runtime[option_count]; 1624 1625 for (int _i = 0; _i < (int)option_count; _i++) { 1626 printf("Testing Option %d\n", options[_i]); 1627 long long start = mstime(); 1628 1629 TEST("create list") { 1630 quicklist *ql = quicklistNew(-2, options[_i]); 1631 ql_verify(ql, 0, 0, 0, 0); 1632 quicklistRelease(ql); 1633 } 1634 1635 TEST("add to tail of empty list") { 1636 quicklist *ql = quicklistNew(-2, options[_i]); 1637 quicklistPushTail(ql, "hello", 6); 1638 /* 1 for head and 1 for tail beacuse 1 node = head = tail */ 1639 ql_verify(ql, 1, 1, 1, 1); 1640 quicklistRelease(ql); 1641 } 1642 1643 TEST("add to head of empty list") { 1644 quicklist *ql = quicklistNew(-2, options[_i]); 1645 quicklistPushHead(ql, "hello", 6); 1646 /* 1 for head and 1 for tail beacuse 1 node = head = tail */ 1647 ql_verify(ql, 1, 1, 1, 1); 1648 quicklistRelease(ql); 1649 } 1650 1651 for (int f = optimize_start; f < 32; f++) { 1652 TEST_DESC("add to tail 5x at fill %d at compress %d", f, 1653 options[_i]) { 1654 quicklist *ql = quicklistNew(f, options[_i]); 1655 for (int i = 0; i < 5; i++) 1656 quicklistPushTail(ql, genstr("hello", i), 32); 1657 if (ql->count != 5) 1658 ERROR; 1659 if (f == 32) 1660 ql_verify(ql, 1, 5, 5, 5); 1661 quicklistRelease(ql); 1662 } 1663 } 1664 1665 for (int f = optimize_start; f < 32; f++) { 1666 TEST_DESC("add to head 5x at fill %d at compress %d", f, 1667 options[_i]) { 1668 quicklist *ql = quicklistNew(f, options[_i]); 1669 for (int i = 0; i < 5; i++) 1670 quicklistPushHead(ql, genstr("hello", i), 32); 1671 if (ql->count != 5) 1672 ERROR; 1673 if (f == 32) 1674 ql_verify(ql, 1, 5, 5, 5); 1675 quicklistRelease(ql); 1676 } 1677 } 1678 1679 for (int f = optimize_start; f < 512; f++) { 1680 TEST_DESC("add to tail 500x at fill %d at compress %d", f, 1681 options[_i]) { 1682 quicklist *ql = quicklistNew(f, options[_i]); 1683 for (int i = 0; i < 500; i++) 1684 quicklistPushTail(ql, genstr("hello", i), 64); 1685 if (ql->count != 500) 1686 ERROR; 1687 if (f == 32) 1688 ql_verify(ql, 16, 500, 32, 20); 1689 quicklistRelease(ql); 1690 } 1691 } 1692 1693 for (int f = optimize_start; f < 512; f++) { 1694 TEST_DESC("add to head 500x at fill %d at compress %d", f, 1695 options[_i]) { 1696 quicklist *ql = quicklistNew(f, options[_i]); 1697 for (int i = 0; i < 500; i++) 1698 quicklistPushHead(ql, genstr("hello", i), 32); 1699 if (ql->count != 500) 1700 ERROR; 1701 if (f == 32) 1702 ql_verify(ql, 16, 500, 20, 32); 1703 quicklistRelease(ql); 1704 } 1705 } 1706 1707 TEST("rotate empty") { 1708 quicklist *ql = quicklistNew(-2, options[_i]); 1709 quicklistRotate(ql); 1710 ql_verify(ql, 0, 0, 0, 0); 1711 quicklistRelease(ql); 1712 } 1713 1714 for (int f = optimize_start; f < 32; f++) { 1715 TEST("rotate one val once") { 1716 quicklist *ql = quicklistNew(f, options[_i]); 1717 quicklistPushHead(ql, "hello", 6); 1718 quicklistRotate(ql); 1719 /* Ignore compression verify because ziplist is 1720 * too small to compress. */ 1721 ql_verify(ql, 1, 1, 1, 1); 1722 quicklistRelease(ql); 1723 } 1724 } 1725 1726 for (int f = optimize_start; f < 3; f++) { 1727 TEST_DESC("rotate 500 val 5000 times at fill %d at compress %d", f, 1728 options[_i]) { 1729 quicklist *ql = quicklistNew(f, options[_i]); 1730 quicklistPushHead(ql, "900", 3); 1731 quicklistPushHead(ql, "7000", 4); 1732 quicklistPushHead(ql, "-1200", 5); 1733 quicklistPushHead(ql, "42", 2); 1734 for (int i = 0; i < 500; i++) 1735 quicklistPushHead(ql, genstr("hello", i), 64); 1736 ql_info(ql); 1737 for (int i = 0; i < 5000; i++) { 1738 ql_info(ql); 1739 quicklistRotate(ql); 1740 } 1741 if (f == 1) 1742 ql_verify(ql, 504, 504, 1, 1); 1743 else if (f == 2) 1744 ql_verify(ql, 252, 504, 2, 2); 1745 else if (f == 32) 1746 ql_verify(ql, 16, 504, 32, 24); 1747 quicklistRelease(ql); 1748 } 1749 } 1750 1751 TEST("pop empty") { 1752 quicklist *ql = quicklistNew(-2, options[_i]); 1753 quicklistPop(ql, QUICKLIST_HEAD, NULL, NULL, NULL); 1754 ql_verify(ql, 0, 0, 0, 0); 1755 quicklistRelease(ql); 1756 } 1757 1758 TEST("pop 1 string from 1") { 1759 quicklist *ql = quicklistNew(-2, options[_i]); 1760 char *populate = genstr("hello", 331); 1761 quicklistPushHead(ql, populate, 32); 1762 unsigned char *data; 1763 unsigned int sz; 1764 long long lv; 1765 ql_info(ql); 1766 quicklistPop(ql, QUICKLIST_HEAD, &data, &sz, &lv); 1767 assert(data != NULL); 1768 assert(sz == 32); 1769 if (strcmp(populate, (char *)data)) 1770 ERR("Pop'd value (%.*s) didn't equal original value (%s)", sz, 1771 data, populate); 1772 zfree(data); 1773 ql_verify(ql, 0, 0, 0, 0); 1774 quicklistRelease(ql); 1775 } 1776 1777 TEST("pop head 1 number from 1") { 1778 quicklist *ql = quicklistNew(-2, options[_i]); 1779 quicklistPushHead(ql, "55513", 5); 1780 unsigned char *data; 1781 unsigned int sz; 1782 long long lv; 1783 ql_info(ql); 1784 quicklistPop(ql, QUICKLIST_HEAD, &data, &sz, &lv); 1785 assert(data == NULL); 1786 assert(lv == 55513); 1787 ql_verify(ql, 0, 0, 0, 0); 1788 quicklistRelease(ql); 1789 } 1790 1791 TEST("pop head 500 from 500") { 1792 quicklist *ql = quicklistNew(-2, options[_i]); 1793 for (int i = 0; i < 500; i++) 1794 quicklistPushHead(ql, genstr("hello", i), 32); 1795 ql_info(ql); 1796 for (int i = 0; i < 500; i++) { 1797 unsigned char *data; 1798 unsigned int sz; 1799 long long lv; 1800 int ret = quicklistPop(ql, QUICKLIST_HEAD, &data, &sz, &lv); 1801 assert(ret == 1); 1802 assert(data != NULL); 1803 assert(sz == 32); 1804 if (strcmp(genstr("hello", 499 - i), (char *)data)) 1805 ERR("Pop'd value (%.*s) didn't equal original value (%s)", 1806 sz, data, genstr("hello", 499 - i)); 1807 zfree(data); 1808 } 1809 ql_verify(ql, 0, 0, 0, 0); 1810 quicklistRelease(ql); 1811 } 1812 1813 TEST("pop head 5000 from 500") { 1814 quicklist *ql = quicklistNew(-2, options[_i]); 1815 for (int i = 0; i < 500; i++) 1816 quicklistPushHead(ql, genstr("hello", i), 32); 1817 for (int i = 0; i < 5000; i++) { 1818 unsigned char *data; 1819 unsigned int sz; 1820 long long lv; 1821 int ret = quicklistPop(ql, QUICKLIST_HEAD, &data, &sz, &lv); 1822 if (i < 500) { 1823 assert(ret == 1); 1824 assert(data != NULL); 1825 assert(sz == 32); 1826 if (strcmp(genstr("hello", 499 - i), (char *)data)) 1827 ERR("Pop'd value (%.*s) didn't equal original value " 1828 "(%s)", 1829 sz, data, genstr("hello", 499 - i)); 1830 zfree(data); 1831 } else { 1832 assert(ret == 0); 1833 } 1834 } 1835 ql_verify(ql, 0, 0, 0, 0); 1836 quicklistRelease(ql); 1837 } 1838 1839 TEST("iterate forward over 500 list") { 1840 quicklist *ql = quicklistNew(-2, options[_i]); 1841 quicklistSetFill(ql, 32); 1842 for (int i = 0; i < 500; i++) 1843 quicklistPushHead(ql, genstr("hello", i), 32); 1844 quicklistIter *iter = quicklistGetIterator(ql, AL_START_HEAD); 1845 quicklistEntry entry; 1846 int i = 499, count = 0; 1847 while (quicklistNext(iter, &entry)) { 1848 char *h = genstr("hello", i); 1849 if (strcmp((char *)entry.value, h)) 1850 ERR("value [%s] didn't match [%s] at position %d", 1851 entry.value, h, i); 1852 i--; 1853 count++; 1854 } 1855 if (count != 500) 1856 ERR("Didn't iterate over exactly 500 elements (%d)", i); 1857 ql_verify(ql, 16, 500, 20, 32); 1858 quicklistReleaseIterator(iter); 1859 quicklistRelease(ql); 1860 } 1861 1862 TEST("iterate reverse over 500 list") { 1863 quicklist *ql = quicklistNew(-2, options[_i]); 1864 quicklistSetFill(ql, 32); 1865 for (int i = 0; i < 500; i++) 1866 quicklistPushHead(ql, genstr("hello", i), 32); 1867 quicklistIter *iter = quicklistGetIterator(ql, AL_START_TAIL); 1868 quicklistEntry entry; 1869 int i = 0; 1870 while (quicklistNext(iter, &entry)) { 1871 char *h = genstr("hello", i); 1872 if (strcmp((char *)entry.value, h)) 1873 ERR("value [%s] didn't match [%s] at position %d", 1874 entry.value, h, i); 1875 i++; 1876 } 1877 if (i != 500) 1878 ERR("Didn't iterate over exactly 500 elements (%d)", i); 1879 ql_verify(ql, 16, 500, 20, 32); 1880 quicklistReleaseIterator(iter); 1881 quicklistRelease(ql); 1882 } 1883 1884 TEST("insert before with 0 elements") { 1885 quicklist *ql = quicklistNew(-2, options[_i]); 1886 quicklistEntry entry; 1887 quicklistIndex(ql, 0, &entry); 1888 quicklistInsertBefore(ql, &entry, "abc", 4); 1889 ql_verify(ql, 1, 1, 1, 1); 1890 quicklistRelease(ql); 1891 } 1892 1893 TEST("insert after with 0 elements") { 1894 quicklist *ql = quicklistNew(-2, options[_i]); 1895 quicklistEntry entry; 1896 quicklistIndex(ql, 0, &entry); 1897 quicklistInsertAfter(ql, &entry, "abc", 4); 1898 ql_verify(ql, 1, 1, 1, 1); 1899 quicklistRelease(ql); 1900 } 1901 1902 TEST("insert after 1 element") { 1903 quicklist *ql = quicklistNew(-2, options[_i]); 1904 quicklistPushHead(ql, "hello", 6); 1905 quicklistEntry entry; 1906 quicklistIndex(ql, 0, &entry); 1907 quicklistInsertAfter(ql, &entry, "abc", 4); 1908 ql_verify(ql, 1, 2, 2, 2); 1909 quicklistRelease(ql); 1910 } 1911 1912 TEST("insert before 1 element") { 1913 quicklist *ql = quicklistNew(-2, options[_i]); 1914 quicklistPushHead(ql, "hello", 6); 1915 quicklistEntry entry; 1916 quicklistIndex(ql, 0, &entry); 1917 quicklistInsertAfter(ql, &entry, "abc", 4); 1918 ql_verify(ql, 1, 2, 2, 2); 1919 quicklistRelease(ql); 1920 } 1921 1922 for (int f = optimize_start; f < 12; f++) { 1923 TEST_DESC("insert once in elements while iterating at fill %d at " 1924 "compress %d\n", 1925 f, options[_i]) { 1926 quicklist *ql = quicklistNew(f, options[_i]); 1927 quicklistPushTail(ql, "abc", 3); 1928 quicklistSetFill(ql, 1); 1929 quicklistPushTail(ql, "def", 3); /* force to unique node */ 1930 quicklistSetFill(ql, f); 1931 quicklistPushTail(ql, "bob", 3); /* force to reset for +3 */ 1932 quicklistPushTail(ql, "foo", 3); 1933 quicklistPushTail(ql, "zoo", 3); 1934 1935 itrprintr(ql, 0); 1936 /* insert "bar" before "bob" while iterating over list. */ 1937 quicklistIter *iter = quicklistGetIterator(ql, AL_START_HEAD); 1938 quicklistEntry entry; 1939 while (quicklistNext(iter, &entry)) { 1940 if (!strncmp((char *)entry.value, "bob", 3)) { 1941 /* Insert as fill = 1 so it spills into new node. */ 1942 quicklistInsertBefore(ql, &entry, "bar", 3); 1943 break; /* didn't we fix insert-while-iterating? */ 1944 } 1945 } 1946 itrprintr(ql, 0); 1947 1948 /* verify results */ 1949 quicklistIndex(ql, 0, &entry); 1950 if (strncmp((char *)entry.value, "abc", 3)) 1951 ERR("Value 0 didn't match, instead got: %.*s", entry.sz, 1952 entry.value); 1953 quicklistIndex(ql, 1, &entry); 1954 if (strncmp((char *)entry.value, "def", 3)) 1955 ERR("Value 1 didn't match, instead got: %.*s", entry.sz, 1956 entry.value); 1957 quicklistIndex(ql, 2, &entry); 1958 if (strncmp((char *)entry.value, "bar", 3)) 1959 ERR("Value 2 didn't match, instead got: %.*s", entry.sz, 1960 entry.value); 1961 quicklistIndex(ql, 3, &entry); 1962 if (strncmp((char *)entry.value, "bob", 3)) 1963 ERR("Value 3 didn't match, instead got: %.*s", entry.sz, 1964 entry.value); 1965 quicklistIndex(ql, 4, &entry); 1966 if (strncmp((char *)entry.value, "foo", 3)) 1967 ERR("Value 4 didn't match, instead got: %.*s", entry.sz, 1968 entry.value); 1969 quicklistIndex(ql, 5, &entry); 1970 if (strncmp((char *)entry.value, "zoo", 3)) 1971 ERR("Value 5 didn't match, instead got: %.*s", entry.sz, 1972 entry.value); 1973 quicklistReleaseIterator(iter); 1974 quicklistRelease(ql); 1975 } 1976 } 1977 1978 for (int f = optimize_start; f < 1024; f++) { 1979 TEST_DESC( 1980 "insert [before] 250 new in middle of 500 elements at fill" 1981 " %d at compress %d", 1982 f, options[_i]) { 1983 quicklist *ql = quicklistNew(f, options[_i]); 1984 for (int i = 0; i < 500; i++) 1985 quicklistPushTail(ql, genstr("hello", i), 32); 1986 for (int i = 0; i < 250; i++) { 1987 quicklistEntry entry; 1988 quicklistIndex(ql, 250, &entry); 1989 quicklistInsertBefore(ql, &entry, genstr("abc", i), 32); 1990 } 1991 if (f == 32) 1992 ql_verify(ql, 25, 750, 32, 20); 1993 quicklistRelease(ql); 1994 } 1995 } 1996 1997 for (int f = optimize_start; f < 1024; f++) { 1998 TEST_DESC("insert [after] 250 new in middle of 500 elements at " 1999 "fill %d at compress %d", 2000 f, options[_i]) { 2001 quicklist *ql = quicklistNew(f, options[_i]); 2002 for (int i = 0; i < 500; i++) 2003 quicklistPushHead(ql, genstr("hello", i), 32); 2004 for (int i = 0; i < 250; i++) { 2005 quicklistEntry entry; 2006 quicklistIndex(ql, 250, &entry); 2007 quicklistInsertAfter(ql, &entry, genstr("abc", i), 32); 2008 } 2009 2010 if (ql->count != 750) 2011 ERR("List size not 750, but rather %ld", ql->count); 2012 2013 if (f == 32) 2014 ql_verify(ql, 26, 750, 20, 32); 2015 quicklistRelease(ql); 2016 } 2017 } 2018 2019 TEST("duplicate empty list") { 2020 quicklist *ql = quicklistNew(-2, options[_i]); 2021 ql_verify(ql, 0, 0, 0, 0); 2022 quicklist *copy = quicklistDup(ql); 2023 ql_verify(copy, 0, 0, 0, 0); 2024 quicklistRelease(ql); 2025 quicklistRelease(copy); 2026 } 2027 2028 TEST("duplicate list of 1 element") { 2029 quicklist *ql = quicklistNew(-2, options[_i]); 2030 quicklistPushHead(ql, genstr("hello", 3), 32); 2031 ql_verify(ql, 1, 1, 1, 1); 2032 quicklist *copy = quicklistDup(ql); 2033 ql_verify(copy, 1, 1, 1, 1); 2034 quicklistRelease(ql); 2035 quicklistRelease(copy); 2036 } 2037 2038 TEST("duplicate list of 500") { 2039 quicklist *ql = quicklistNew(-2, options[_i]); 2040 quicklistSetFill(ql, 32); 2041 for (int i = 0; i < 500; i++) 2042 quicklistPushHead(ql, genstr("hello", i), 32); 2043 ql_verify(ql, 16, 500, 20, 32); 2044 2045 quicklist *copy = quicklistDup(ql); 2046 ql_verify(copy, 16, 500, 20, 32); 2047 quicklistRelease(ql); 2048 quicklistRelease(copy); 2049 } 2050 2051 for (int f = optimize_start; f < 512; f++) { 2052 TEST_DESC("index 1,200 from 500 list at fill %d at compress %d", f, 2053 options[_i]) { 2054 quicklist *ql = quicklistNew(f, options[_i]); 2055 for (int i = 0; i < 500; i++) 2056 quicklistPushTail(ql, genstr("hello", i + 1), 32); 2057 quicklistEntry entry; 2058 quicklistIndex(ql, 1, &entry); 2059 if (!strcmp((char *)entry.value, "hello2")) 2060 OK; 2061 else 2062 ERR("Value: %s", entry.value); 2063 quicklistIndex(ql, 200, &entry); 2064 if (!strcmp((char *)entry.value, "hello201")) 2065 OK; 2066 else 2067 ERR("Value: %s", entry.value); 2068 quicklistRelease(ql); 2069 } 2070 2071 TEST_DESC("index -1,-2 from 500 list at fill %d at compress %d", f, 2072 options[_i]) { 2073 quicklist *ql = quicklistNew(f, options[_i]); 2074 for (int i = 0; i < 500; i++) 2075 quicklistPushTail(ql, genstr("hello", i + 1), 32); 2076 quicklistEntry entry; 2077 quicklistIndex(ql, -1, &entry); 2078 if (!strcmp((char *)entry.value, "hello500")) 2079 OK; 2080 else 2081 ERR("Value: %s", entry.value); 2082 quicklistIndex(ql, -2, &entry); 2083 if (!strcmp((char *)entry.value, "hello499")) 2084 OK; 2085 else 2086 ERR("Value: %s", entry.value); 2087 quicklistRelease(ql); 2088 } 2089 2090 TEST_DESC("index -100 from 500 list at fill %d at compress %d", f, 2091 options[_i]) { 2092 quicklist *ql = quicklistNew(f, options[_i]); 2093 for (int i = 0; i < 500; i++) 2094 quicklistPushTail(ql, genstr("hello", i + 1), 32); 2095 quicklistEntry entry; 2096 quicklistIndex(ql, -100, &entry); 2097 if (!strcmp((char *)entry.value, "hello401")) 2098 OK; 2099 else 2100 ERR("Value: %s", entry.value); 2101 quicklistRelease(ql); 2102 } 2103 2104 TEST_DESC("index too big +1 from 50 list at fill %d at compress %d", 2105 f, options[_i]) { 2106 quicklist *ql = quicklistNew(f, options[_i]); 2107 for (int i = 0; i < 50; i++) 2108 quicklistPushTail(ql, genstr("hello", i + 1), 32); 2109 quicklistEntry entry; 2110 if (quicklistIndex(ql, 50, &entry)) 2111 ERR("Index found at 50 with 50 list: %.*s", entry.sz, 2112 entry.value); 2113 else 2114 OK; 2115 quicklistRelease(ql); 2116 } 2117 } 2118 2119 TEST("delete range empty list") { 2120 quicklist *ql = quicklistNew(-2, options[_i]); 2121 quicklistDelRange(ql, 5, 20); 2122 ql_verify(ql, 0, 0, 0, 0); 2123 quicklistRelease(ql); 2124 } 2125 2126 TEST("delete range of entire node in list of one node") { 2127 quicklist *ql = quicklistNew(-2, options[_i]); 2128 for (int i = 0; i < 32; i++) 2129 quicklistPushHead(ql, genstr("hello", i), 32); 2130 ql_verify(ql, 1, 32, 32, 32); 2131 quicklistDelRange(ql, 0, 32); 2132 ql_verify(ql, 0, 0, 0, 0); 2133 quicklistRelease(ql); 2134 } 2135 2136 TEST("delete range of entire node with overflow counts") { 2137 quicklist *ql = quicklistNew(-2, options[_i]); 2138 for (int i = 0; i < 32; i++) 2139 quicklistPushHead(ql, genstr("hello", i), 32); 2140 ql_verify(ql, 1, 32, 32, 32); 2141 quicklistDelRange(ql, 0, 128); 2142 ql_verify(ql, 0, 0, 0, 0); 2143 quicklistRelease(ql); 2144 } 2145 2146 TEST("delete middle 100 of 500 list") { 2147 quicklist *ql = quicklistNew(-2, options[_i]); 2148 quicklistSetFill(ql, 32); 2149 for (int i = 0; i < 500; i++) 2150 quicklistPushTail(ql, genstr("hello", i + 1), 32); 2151 ql_verify(ql, 16, 500, 32, 20); 2152 quicklistDelRange(ql, 200, 100); 2153 ql_verify(ql, 14, 400, 32, 20); 2154 quicklistRelease(ql); 2155 } 2156 2157 TEST("delete negative 1 from 500 list") { 2158 quicklist *ql = quicklistNew(-2, options[_i]); 2159 quicklistSetFill(ql, 32); 2160 for (int i = 0; i < 500; i++) 2161 quicklistPushTail(ql, genstr("hello", i + 1), 32); 2162 ql_verify(ql, 16, 500, 32, 20); 2163 quicklistDelRange(ql, -1, 1); 2164 ql_verify(ql, 16, 499, 32, 19); 2165 quicklistRelease(ql); 2166 } 2167 2168 TEST("delete negative 1 from 500 list with overflow counts") { 2169 quicklist *ql = quicklistNew(-2, options[_i]); 2170 quicklistSetFill(ql, 32); 2171 for (int i = 0; i < 500; i++) 2172 quicklistPushTail(ql, genstr("hello", i + 1), 32); 2173 ql_verify(ql, 16, 500, 32, 20); 2174 quicklistDelRange(ql, -1, 128); 2175 ql_verify(ql, 16, 499, 32, 19); 2176 quicklistRelease(ql); 2177 } 2178 2179 TEST("delete negative 100 from 500 list") { 2180 quicklist *ql = quicklistNew(-2, options[_i]); 2181 quicklistSetFill(ql, 32); 2182 for (int i = 0; i < 500; i++) 2183 quicklistPushTail(ql, genstr("hello", i + 1), 32); 2184 quicklistDelRange(ql, -100, 100); 2185 ql_verify(ql, 13, 400, 32, 16); 2186 quicklistRelease(ql); 2187 } 2188 2189 TEST("delete -10 count 5 from 50 list") { 2190 quicklist *ql = quicklistNew(-2, options[_i]); 2191 quicklistSetFill(ql, 32); 2192 for (int i = 0; i < 50; i++) 2193 quicklistPushTail(ql, genstr("hello", i + 1), 32); 2194 ql_verify(ql, 2, 50, 32, 18); 2195 quicklistDelRange(ql, -10, 5); 2196 ql_verify(ql, 2, 45, 32, 13); 2197 quicklistRelease(ql); 2198 } 2199 2200 TEST("numbers only list read") { 2201 quicklist *ql = quicklistNew(-2, options[_i]); 2202 quicklistPushTail(ql, "1111", 4); 2203 quicklistPushTail(ql, "2222", 4); 2204 quicklistPushTail(ql, "3333", 4); 2205 quicklistPushTail(ql, "4444", 4); 2206 ql_verify(ql, 1, 4, 4, 4); 2207 quicklistEntry entry; 2208 quicklistIndex(ql, 0, &entry); 2209 if (entry.longval != 1111) 2210 ERR("Not 1111, %lld", entry.longval); 2211 quicklistIndex(ql, 1, &entry); 2212 if (entry.longval != 2222) 2213 ERR("Not 2222, %lld", entry.longval); 2214 quicklistIndex(ql, 2, &entry); 2215 if (entry.longval != 3333) 2216 ERR("Not 3333, %lld", entry.longval); 2217 quicklistIndex(ql, 3, &entry); 2218 if (entry.longval != 4444) 2219 ERR("Not 4444, %lld", entry.longval); 2220 if (quicklistIndex(ql, 4, &entry)) 2221 ERR("Index past elements: %lld", entry.longval); 2222 quicklistIndex(ql, -1, &entry); 2223 if (entry.longval != 4444) 2224 ERR("Not 4444 (reverse), %lld", entry.longval); 2225 quicklistIndex(ql, -2, &entry); 2226 if (entry.longval != 3333) 2227 ERR("Not 3333 (reverse), %lld", entry.longval); 2228 quicklistIndex(ql, -3, &entry); 2229 if (entry.longval != 2222) 2230 ERR("Not 2222 (reverse), %lld", entry.longval); 2231 quicklistIndex(ql, -4, &entry); 2232 if (entry.longval != 1111) 2233 ERR("Not 1111 (reverse), %lld", entry.longval); 2234 if (quicklistIndex(ql, -5, &entry)) 2235 ERR("Index past elements (reverse), %lld", entry.longval); 2236 quicklistRelease(ql); 2237 } 2238 2239 TEST("numbers larger list read") { 2240 quicklist *ql = quicklistNew(-2, options[_i]); 2241 quicklistSetFill(ql, 32); 2242 char num[32]; 2243 long long nums[5000]; 2244 for (int i = 0; i < 5000; i++) { 2245 nums[i] = -5157318210846258176 + i; 2246 int sz = ll2string(num, sizeof(num), nums[i]); 2247 quicklistPushTail(ql, num, sz); 2248 } 2249 quicklistPushTail(ql, "xxxxxxxxxxxxxxxxxxxx", 20); 2250 quicklistEntry entry; 2251 for (int i = 0; i < 5000; i++) { 2252 quicklistIndex(ql, i, &entry); 2253 if (entry.longval != nums[i]) 2254 ERR("[%d] Not longval %lld but rather %lld", i, nums[i], 2255 entry.longval); 2256 entry.longval = 0xdeadbeef; 2257 } 2258 quicklistIndex(ql, 5000, &entry); 2259 if (strncmp((char *)entry.value, "xxxxxxxxxxxxxxxxxxxx", 20)) 2260 ERR("String val not match: %s", entry.value); 2261 ql_verify(ql, 157, 5001, 32, 9); 2262 quicklistRelease(ql); 2263 } 2264 2265 TEST("numbers larger list read B") { 2266 quicklist *ql = quicklistNew(-2, options[_i]); 2267 quicklistPushTail(ql, "99", 2); 2268 quicklistPushTail(ql, "98", 2); 2269 quicklistPushTail(ql, "xxxxxxxxxxxxxxxxxxxx", 20); 2270 quicklistPushTail(ql, "96", 2); 2271 quicklistPushTail(ql, "95", 2); 2272 quicklistReplaceAtIndex(ql, 1, "foo", 3); 2273 quicklistReplaceAtIndex(ql, -1, "bar", 3); 2274 quicklistRelease(ql); 2275 OK; 2276 } 2277 2278 for (int f = optimize_start; f < 16; f++) { 2279 TEST_DESC("lrem test at fill %d at compress %d", f, options[_i]) { 2280 quicklist *ql = quicklistNew(f, options[_i]); 2281 char *words[] = {"abc", "foo", "bar", "foobar", "foobared", 2282 "zap", "bar", "test", "foo"}; 2283 char *result[] = {"abc", "foo", "foobar", "foobared", 2284 "zap", "test", "foo"}; 2285 char *resultB[] = {"abc", "foo", "foobar", 2286 "foobared", "zap", "test"}; 2287 for (int i = 0; i < 9; i++) 2288 quicklistPushTail(ql, words[i], strlen(words[i])); 2289 2290 /* lrem 0 bar */ 2291 quicklistIter *iter = quicklistGetIterator(ql, AL_START_HEAD); 2292 quicklistEntry entry; 2293 int i = 0; 2294 while (quicklistNext(iter, &entry)) { 2295 if (quicklistCompare(entry.zi, (unsigned char *)"bar", 3)) { 2296 quicklistDelEntry(iter, &entry); 2297 } 2298 i++; 2299 } 2300 quicklistReleaseIterator(iter); 2301 2302 /* check result of lrem 0 bar */ 2303 iter = quicklistGetIterator(ql, AL_START_HEAD); 2304 i = 0; 2305 int ok = 1; 2306 while (quicklistNext(iter, &entry)) { 2307 /* Result must be: abc, foo, foobar, foobared, zap, test, 2308 * foo */ 2309 if (strncmp((char *)entry.value, result[i], entry.sz)) { 2310 ERR("No match at position %d, got %.*s instead of %s", 2311 i, entry.sz, entry.value, result[i]); 2312 ok = 0; 2313 } 2314 i++; 2315 } 2316 quicklistReleaseIterator(iter); 2317 2318 quicklistPushTail(ql, "foo", 3); 2319 2320 /* lrem -2 foo */ 2321 iter = quicklistGetIterator(ql, AL_START_TAIL); 2322 i = 0; 2323 int del = 2; 2324 while (quicklistNext(iter, &entry)) { 2325 if (quicklistCompare(entry.zi, (unsigned char *)"foo", 3)) { 2326 quicklistDelEntry(iter, &entry); 2327 del--; 2328 } 2329 if (!del) 2330 break; 2331 i++; 2332 } 2333 quicklistReleaseIterator(iter); 2334 2335 /* check result of lrem -2 foo */ 2336 /* (we're ignoring the '2' part and still deleting all foo 2337 * because 2338 * we only have two foo) */ 2339 iter = quicklistGetIterator(ql, AL_START_TAIL); 2340 i = 0; 2341 size_t resB = sizeof(resultB) / sizeof(*resultB); 2342 while (quicklistNext(iter, &entry)) { 2343 /* Result must be: abc, foo, foobar, foobared, zap, test, 2344 * foo */ 2345 if (strncmp((char *)entry.value, resultB[resB - 1 - i], 2346 entry.sz)) { 2347 ERR("No match at position %d, got %.*s instead of %s", 2348 i, entry.sz, entry.value, resultB[resB - 1 - i]); 2349 ok = 0; 2350 } 2351 i++; 2352 } 2353 2354 quicklistReleaseIterator(iter); 2355 /* final result of all tests */ 2356 if (ok) 2357 OK; 2358 quicklistRelease(ql); 2359 } 2360 } 2361 2362 for (int f = optimize_start; f < 16; f++) { 2363 TEST_DESC("iterate reverse + delete at fill %d at compress %d", f, 2364 options[_i]) { 2365 quicklist *ql = quicklistNew(f, options[_i]); 2366 quicklistPushTail(ql, "abc", 3); 2367 quicklistPushTail(ql, "def", 3); 2368 quicklistPushTail(ql, "hij", 3); 2369 quicklistPushTail(ql, "jkl", 3); 2370 quicklistPushTail(ql, "oop", 3); 2371 2372 quicklistEntry entry; 2373 quicklistIter *iter = quicklistGetIterator(ql, AL_START_TAIL); 2374 int i = 0; 2375 while (quicklistNext(iter, &entry)) { 2376 if (quicklistCompare(entry.zi, (unsigned char *)"hij", 3)) { 2377 quicklistDelEntry(iter, &entry); 2378 } 2379 i++; 2380 } 2381 quicklistReleaseIterator(iter); 2382 2383 if (i != 5) 2384 ERR("Didn't iterate 5 times, iterated %d times.", i); 2385 2386 /* Check results after deletion of "hij" */ 2387 iter = quicklistGetIterator(ql, AL_START_HEAD); 2388 i = 0; 2389 char *vals[] = {"abc", "def", "jkl", "oop"}; 2390 while (quicklistNext(iter, &entry)) { 2391 if (!quicklistCompare(entry.zi, (unsigned char *)vals[i], 2392 3)) { 2393 ERR("Value at %d didn't match %s\n", i, vals[i]); 2394 } 2395 i++; 2396 } 2397 quicklistReleaseIterator(iter); 2398 quicklistRelease(ql); 2399 } 2400 } 2401 2402 for (int f = optimize_start; f < 800; f++) { 2403 TEST_DESC("iterator at index test at fill %d at compress %d", f, 2404 options[_i]) { 2405 quicklist *ql = quicklistNew(f, options[_i]); 2406 char num[32]; 2407 long long nums[5000]; 2408 for (int i = 0; i < 760; i++) { 2409 nums[i] = -5157318210846258176 + i; 2410 int sz = ll2string(num, sizeof(num), nums[i]); 2411 quicklistPushTail(ql, num, sz); 2412 } 2413 2414 quicklistEntry entry; 2415 quicklistIter *iter = 2416 quicklistGetIteratorAtIdx(ql, AL_START_HEAD, 437); 2417 int i = 437; 2418 while (quicklistNext(iter, &entry)) { 2419 if (entry.longval != nums[i]) 2420 ERR("Expected %lld, but got %lld", entry.longval, 2421 nums[i]); 2422 i++; 2423 } 2424 quicklistReleaseIterator(iter); 2425 quicklistRelease(ql); 2426 } 2427 } 2428 2429 for (int f = optimize_start; f < 40; f++) { 2430 TEST_DESC("ltrim test A at fill %d at compress %d", f, 2431 options[_i]) { 2432 quicklist *ql = quicklistNew(f, options[_i]); 2433 char num[32]; 2434 long long nums[5000]; 2435 for (int i = 0; i < 32; i++) { 2436 nums[i] = -5157318210846258176 + i; 2437 int sz = ll2string(num, sizeof(num), nums[i]); 2438 quicklistPushTail(ql, num, sz); 2439 } 2440 if (f == 32) 2441 ql_verify(ql, 1, 32, 32, 32); 2442 /* ltrim 25 53 (keep [25,32] inclusive = 7 remaining) */ 2443 quicklistDelRange(ql, 0, 25); 2444 quicklistDelRange(ql, 0, 0); 2445 quicklistEntry entry; 2446 for (int i = 0; i < 7; i++) { 2447 quicklistIndex(ql, i, &entry); 2448 if (entry.longval != nums[25 + i]) 2449 ERR("Deleted invalid range! Expected %lld but got " 2450 "%lld", 2451 entry.longval, nums[25 + i]); 2452 } 2453 if (f == 32) 2454 ql_verify(ql, 1, 7, 7, 7); 2455 quicklistRelease(ql); 2456 } 2457 } 2458 2459 for (int f = optimize_start; f < 40; f++) { 2460 TEST_DESC("ltrim test B at fill %d at compress %d", f, 2461 options[_i]) { 2462 /* Force-disable compression because our 33 sequential 2463 * integers don't compress and the check always fails. */ 2464 quicklist *ql = quicklistNew(f, QUICKLIST_NOCOMPRESS); 2465 char num[32]; 2466 long long nums[5000]; 2467 for (int i = 0; i < 33; i++) { 2468 nums[i] = i; 2469 int sz = ll2string(num, sizeof(num), nums[i]); 2470 quicklistPushTail(ql, num, sz); 2471 } 2472 if (f == 32) 2473 ql_verify(ql, 2, 33, 32, 1); 2474 /* ltrim 5 16 (keep [5,16] inclusive = 12 remaining) */ 2475 quicklistDelRange(ql, 0, 5); 2476 quicklistDelRange(ql, -16, 16); 2477 if (f == 32) 2478 ql_verify(ql, 1, 12, 12, 12); 2479 quicklistEntry entry; 2480 quicklistIndex(ql, 0, &entry); 2481 if (entry.longval != 5) 2482 ERR("A: longval not 5, but %lld", entry.longval); 2483 else 2484 OK; 2485 quicklistIndex(ql, -1, &entry); 2486 if (entry.longval != 16) 2487 ERR("B! got instead: %lld", entry.longval); 2488 else 2489 OK; 2490 quicklistPushTail(ql, "bobobob", 7); 2491 quicklistIndex(ql, -1, &entry); 2492 if (strncmp((char *)entry.value, "bobobob", 7)) 2493 ERR("Tail doesn't match bobobob, it's %.*s instead", 2494 entry.sz, entry.value); 2495 for (int i = 0; i < 12; i++) { 2496 quicklistIndex(ql, i, &entry); 2497 if (entry.longval != nums[5 + i]) 2498 ERR("Deleted invalid range! Expected %lld but got " 2499 "%lld", 2500 entry.longval, nums[5 + i]); 2501 } 2502 quicklistRelease(ql); 2503 } 2504 } 2505 2506 for (int f = optimize_start; f < 40; f++) { 2507 TEST_DESC("ltrim test C at fill %d at compress %d", f, 2508 options[_i]) { 2509 quicklist *ql = quicklistNew(f, options[_i]); 2510 char num[32]; 2511 long long nums[5000]; 2512 for (int i = 0; i < 33; i++) { 2513 nums[i] = -5157318210846258176 + i; 2514 int sz = ll2string(num, sizeof(num), nums[i]); 2515 quicklistPushTail(ql, num, sz); 2516 } 2517 if (f == 32) 2518 ql_verify(ql, 2, 33, 32, 1); 2519 /* ltrim 3 3 (keep [3,3] inclusive = 1 remaining) */ 2520 quicklistDelRange(ql, 0, 3); 2521 quicklistDelRange(ql, -29, 2522 4000); /* make sure not loop forever */ 2523 if (f == 32) 2524 ql_verify(ql, 1, 1, 1, 1); 2525 quicklistEntry entry; 2526 quicklistIndex(ql, 0, &entry); 2527 if (entry.longval != -5157318210846258173) 2528 ERROR; 2529 else 2530 OK; 2531 quicklistRelease(ql); 2532 } 2533 } 2534 2535 for (int f = optimize_start; f < 40; f++) { 2536 TEST_DESC("ltrim test D at fill %d at compress %d", f, 2537 options[_i]) { 2538 quicklist *ql = quicklistNew(f, options[_i]); 2539 char num[32]; 2540 long long nums[5000]; 2541 for (int i = 0; i < 33; i++) { 2542 nums[i] = -5157318210846258176 + i; 2543 int sz = ll2string(num, sizeof(num), nums[i]); 2544 quicklistPushTail(ql, num, sz); 2545 } 2546 if (f == 32) 2547 ql_verify(ql, 2, 33, 32, 1); 2548 quicklistDelRange(ql, -12, 3); 2549 if (ql->count != 30) 2550 ERR("Didn't delete exactly three elements! Count is: %lu", 2551 ql->count); 2552 quicklistRelease(ql); 2553 } 2554 } 2555 2556 for (int f = optimize_start; f < 72; f++) { 2557 TEST_DESC("create quicklist from ziplist at fill %d at compress %d", 2558 f, options[_i]) { 2559 unsigned char *zl = ziplistNew(); 2560 long long nums[64]; 2561 char num[64]; 2562 for (int i = 0; i < 33; i++) { 2563 nums[i] = -5157318210846258176 + i; 2564 int sz = ll2string(num, sizeof(num), nums[i]); 2565 zl = 2566 ziplistPush(zl, (unsigned char *)num, sz, ZIPLIST_TAIL); 2567 } 2568 for (int i = 0; i < 33; i++) { 2569 zl = ziplistPush(zl, (unsigned char *)genstr("hello", i), 2570 32, ZIPLIST_TAIL); 2571 } 2572 quicklist *ql = quicklistCreateFromZiplist(f, options[_i], zl); 2573 if (f == 1) 2574 ql_verify(ql, 66, 66, 1, 1); 2575 else if (f == 32) 2576 ql_verify(ql, 3, 66, 32, 2); 2577 else if (f == 66) 2578 ql_verify(ql, 1, 66, 66, 66); 2579 quicklistRelease(ql); 2580 } 2581 } 2582 2583 long long stop = mstime(); 2584 runtime[_i] = stop - start; 2585 } 2586 2587 /* Run a longer test of compression depth outside of primary test loop. */ 2588 int list_sizes[] = {250, 251, 500, 999, 1000}; 2589 long long start = mstime(); 2590 for (int list = 0; list < (int)(sizeof(list_sizes) / sizeof(*list_sizes)); 2591 list++) { 2592 for (int f = optimize_start; f < 128; f++) { 2593 for (int depth = 1; depth < 40; depth++) { 2594 /* skip over many redundant test cases */ 2595 TEST_DESC("verify specific compression of interior nodes with " 2596 "%d list " 2597 "at fill %d at compress %d", 2598 list_sizes[list], f, depth) { 2599 quicklist *ql = quicklistNew(f, depth); 2600 for (int i = 0; i < list_sizes[list]; i++) { 2601 quicklistPushTail(ql, genstr("hello TAIL", i + 1), 64); 2602 quicklistPushHead(ql, genstr("hello HEAD", i + 1), 64); 2603 } 2604 2605 quicklistNode *node = ql->head; 2606 unsigned int low_raw = ql->compress; 2607 unsigned int high_raw = ql->len - ql->compress; 2608 2609 for (unsigned int at = 0; at < ql->len; 2610 at++, node = node->next) { 2611 if (at < low_raw || at >= high_raw) { 2612 if (node->encoding != QUICKLIST_NODE_ENCODING_RAW) { 2613 ERR("Incorrect compression: node %d is " 2614 "compressed at depth %d ((%u, %u); total " 2615 "nodes: %u; size: %u)", 2616 at, depth, low_raw, high_raw, ql->len, 2617 node->sz); 2618 } 2619 } else { 2620 if (node->encoding != QUICKLIST_NODE_ENCODING_LZF) { 2621 ERR("Incorrect non-compression: node %d is NOT " 2622 "compressed at depth %d ((%u, %u); total " 2623 "nodes: %u; size: %u; attempted: %d)", 2624 at, depth, low_raw, high_raw, ql->len, 2625 node->sz, node->attempted_compress); 2626 } 2627 } 2628 } 2629 quicklistRelease(ql); 2630 } 2631 } 2632 } 2633 } 2634 long long stop = mstime(); 2635 2636 printf("\n"); 2637 for (size_t i = 0; i < option_count; i++) 2638 printf("Test Loop %02d: %0.2f seconds.\n", options[i], 2639 (float)runtime[i] / 1000); 2640 printf("Compressions: %0.2f seconds.\n", (float)(stop - start) / 1000); 2641 printf("\n"); 2642 2643 if (!err) 2644 printf("ALL TESTS PASSED!\n"); 2645 else 2646 ERR("Sorry, not all tests passed! In fact, %d tests failed.", err); 2647 2648 return err; 2649 } 2650 #endif 2651