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(data, vstr, 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 quicklistPushHead(ql, genstr("hello", 331), 32); 1761 unsigned char *data; 1762 unsigned int sz; 1763 long long lv; 1764 ql_info(ql); 1765 quicklistPop(ql, QUICKLIST_HEAD, &data, &sz, &lv); 1766 assert(data != NULL); 1767 assert(sz == 32); 1768 zfree(data); 1769 ql_verify(ql, 0, 0, 0, 0); 1770 quicklistRelease(ql); 1771 } 1772 1773 TEST("pop head 1 number from 1") { 1774 quicklist *ql = quicklistNew(-2, options[_i]); 1775 quicklistPushHead(ql, "55513", 5); 1776 unsigned char *data; 1777 unsigned int sz; 1778 long long lv; 1779 ql_info(ql); 1780 quicklistPop(ql, QUICKLIST_HEAD, &data, &sz, &lv); 1781 assert(data == NULL); 1782 assert(lv == 55513); 1783 ql_verify(ql, 0, 0, 0, 0); 1784 quicklistRelease(ql); 1785 } 1786 1787 TEST("pop head 500 from 500") { 1788 quicklist *ql = quicklistNew(-2, options[_i]); 1789 for (int i = 0; i < 500; i++) 1790 quicklistPushHead(ql, genstr("hello", i), 32); 1791 ql_info(ql); 1792 for (int i = 0; i < 500; i++) { 1793 unsigned char *data; 1794 unsigned int sz; 1795 long long lv; 1796 int ret = quicklistPop(ql, QUICKLIST_HEAD, &data, &sz, &lv); 1797 assert(ret == 1); 1798 assert(data != NULL); 1799 assert(sz == 32); 1800 zfree(data); 1801 } 1802 ql_verify(ql, 0, 0, 0, 0); 1803 quicklistRelease(ql); 1804 } 1805 1806 TEST("pop head 5000 from 500") { 1807 quicklist *ql = quicklistNew(-2, options[_i]); 1808 for (int i = 0; i < 500; i++) 1809 quicklistPushHead(ql, genstr("hello", i), 32); 1810 for (int i = 0; i < 5000; i++) { 1811 unsigned char *data; 1812 unsigned int sz; 1813 long long lv; 1814 int ret = quicklistPop(ql, QUICKLIST_HEAD, &data, &sz, &lv); 1815 if (i < 500) { 1816 assert(ret == 1); 1817 assert(data != NULL); 1818 assert(sz == 32); 1819 zfree(data); 1820 } else { 1821 assert(ret == 0); 1822 } 1823 } 1824 ql_verify(ql, 0, 0, 0, 0); 1825 quicklistRelease(ql); 1826 } 1827 1828 TEST("iterate forward over 500 list") { 1829 quicklist *ql = quicklistNew(-2, options[_i]); 1830 quicklistSetFill(ql, 32); 1831 for (int i = 0; i < 500; i++) 1832 quicklistPushHead(ql, genstr("hello", i), 32); 1833 quicklistIter *iter = quicklistGetIterator(ql, AL_START_HEAD); 1834 quicklistEntry entry; 1835 int i = 499, count = 0; 1836 while (quicklistNext(iter, &entry)) { 1837 char *h = genstr("hello", i); 1838 if (strcmp((char *)entry.value, h)) 1839 ERR("value [%s] didn't match [%s] at position %d", 1840 entry.value, h, i); 1841 i--; 1842 count++; 1843 } 1844 if (count != 500) 1845 ERR("Didn't iterate over exactly 500 elements (%d)", i); 1846 ql_verify(ql, 16, 500, 20, 32); 1847 quicklistReleaseIterator(iter); 1848 quicklistRelease(ql); 1849 } 1850 1851 TEST("iterate reverse over 500 list") { 1852 quicklist *ql = quicklistNew(-2, options[_i]); 1853 quicklistSetFill(ql, 32); 1854 for (int i = 0; i < 500; i++) 1855 quicklistPushHead(ql, genstr("hello", i), 32); 1856 quicklistIter *iter = quicklistGetIterator(ql, AL_START_TAIL); 1857 quicklistEntry entry; 1858 int i = 0; 1859 while (quicklistNext(iter, &entry)) { 1860 char *h = genstr("hello", i); 1861 if (strcmp((char *)entry.value, h)) 1862 ERR("value [%s] didn't match [%s] at position %d", 1863 entry.value, h, i); 1864 i++; 1865 } 1866 if (i != 500) 1867 ERR("Didn't iterate over exactly 500 elements (%d)", i); 1868 ql_verify(ql, 16, 500, 20, 32); 1869 quicklistReleaseIterator(iter); 1870 quicklistRelease(ql); 1871 } 1872 1873 TEST("insert before with 0 elements") { 1874 quicklist *ql = quicklistNew(-2, options[_i]); 1875 quicklistEntry entry; 1876 quicklistIndex(ql, 0, &entry); 1877 quicklistInsertBefore(ql, &entry, "abc", 4); 1878 ql_verify(ql, 1, 1, 1, 1); 1879 quicklistRelease(ql); 1880 } 1881 1882 TEST("insert after with 0 elements") { 1883 quicklist *ql = quicklistNew(-2, options[_i]); 1884 quicklistEntry entry; 1885 quicklistIndex(ql, 0, &entry); 1886 quicklistInsertAfter(ql, &entry, "abc", 4); 1887 ql_verify(ql, 1, 1, 1, 1); 1888 quicklistRelease(ql); 1889 } 1890 1891 TEST("insert after 1 element") { 1892 quicklist *ql = quicklistNew(-2, options[_i]); 1893 quicklistPushHead(ql, "hello", 6); 1894 quicklistEntry entry; 1895 quicklistIndex(ql, 0, &entry); 1896 quicklistInsertAfter(ql, &entry, "abc", 4); 1897 ql_verify(ql, 1, 2, 2, 2); 1898 quicklistRelease(ql); 1899 } 1900 1901 TEST("insert before 1 element") { 1902 quicklist *ql = quicklistNew(-2, options[_i]); 1903 quicklistPushHead(ql, "hello", 6); 1904 quicklistEntry entry; 1905 quicklistIndex(ql, 0, &entry); 1906 quicklistInsertAfter(ql, &entry, "abc", 4); 1907 ql_verify(ql, 1, 2, 2, 2); 1908 quicklistRelease(ql); 1909 } 1910 1911 for (int f = optimize_start; f < 12; f++) { 1912 TEST_DESC("insert once in elements while iterating at fill %d at " 1913 "compress %d\n", 1914 f, options[_i]) { 1915 quicklist *ql = quicklistNew(f, options[_i]); 1916 quicklistPushTail(ql, "abc", 3); 1917 quicklistSetFill(ql, 1); 1918 quicklistPushTail(ql, "def", 3); /* force to unique node */ 1919 quicklistSetFill(ql, f); 1920 quicklistPushTail(ql, "bob", 3); /* force to reset for +3 */ 1921 quicklistPushTail(ql, "foo", 3); 1922 quicklistPushTail(ql, "zoo", 3); 1923 1924 itrprintr(ql, 0); 1925 /* insert "bar" before "bob" while iterating over list. */ 1926 quicklistIter *iter = quicklistGetIterator(ql, AL_START_HEAD); 1927 quicklistEntry entry; 1928 while (quicklistNext(iter, &entry)) { 1929 if (!strncmp((char *)entry.value, "bob", 3)) { 1930 /* Insert as fill = 1 so it spills into new node. */ 1931 quicklistInsertBefore(ql, &entry, "bar", 3); 1932 break; /* didn't we fix insert-while-iterating? */ 1933 } 1934 } 1935 itrprintr(ql, 0); 1936 1937 /* verify results */ 1938 quicklistIndex(ql, 0, &entry); 1939 if (strncmp((char *)entry.value, "abc", 3)) 1940 ERR("Value 0 didn't match, instead got: %.*s", entry.sz, 1941 entry.value); 1942 quicklistIndex(ql, 1, &entry); 1943 if (strncmp((char *)entry.value, "def", 3)) 1944 ERR("Value 1 didn't match, instead got: %.*s", entry.sz, 1945 entry.value); 1946 quicklistIndex(ql, 2, &entry); 1947 if (strncmp((char *)entry.value, "bar", 3)) 1948 ERR("Value 2 didn't match, instead got: %.*s", entry.sz, 1949 entry.value); 1950 quicklistIndex(ql, 3, &entry); 1951 if (strncmp((char *)entry.value, "bob", 3)) 1952 ERR("Value 3 didn't match, instead got: %.*s", entry.sz, 1953 entry.value); 1954 quicklistIndex(ql, 4, &entry); 1955 if (strncmp((char *)entry.value, "foo", 3)) 1956 ERR("Value 4 didn't match, instead got: %.*s", entry.sz, 1957 entry.value); 1958 quicklistIndex(ql, 5, &entry); 1959 if (strncmp((char *)entry.value, "zoo", 3)) 1960 ERR("Value 5 didn't match, instead got: %.*s", entry.sz, 1961 entry.value); 1962 quicklistReleaseIterator(iter); 1963 quicklistRelease(ql); 1964 } 1965 } 1966 1967 for (int f = optimize_start; f < 1024; f++) { 1968 TEST_DESC( 1969 "insert [before] 250 new in middle of 500 elements at fill" 1970 " %d at compress %d", 1971 f, options[_i]) { 1972 quicklist *ql = quicklistNew(f, options[_i]); 1973 for (int i = 0; i < 500; i++) 1974 quicklistPushTail(ql, genstr("hello", i), 32); 1975 for (int i = 0; i < 250; i++) { 1976 quicklistEntry entry; 1977 quicklistIndex(ql, 250, &entry); 1978 quicklistInsertBefore(ql, &entry, genstr("abc", i), 32); 1979 } 1980 if (f == 32) 1981 ql_verify(ql, 25, 750, 32, 20); 1982 quicklistRelease(ql); 1983 } 1984 } 1985 1986 for (int f = optimize_start; f < 1024; f++) { 1987 TEST_DESC("insert [after] 250 new in middle of 500 elements at " 1988 "fill %d at compress %d", 1989 f, options[_i]) { 1990 quicklist *ql = quicklistNew(f, options[_i]); 1991 for (int i = 0; i < 500; i++) 1992 quicklistPushHead(ql, genstr("hello", i), 32); 1993 for (int i = 0; i < 250; i++) { 1994 quicklistEntry entry; 1995 quicklistIndex(ql, 250, &entry); 1996 quicklistInsertAfter(ql, &entry, genstr("abc", i), 32); 1997 } 1998 1999 if (ql->count != 750) 2000 ERR("List size not 750, but rather %ld", ql->count); 2001 2002 if (f == 32) 2003 ql_verify(ql, 26, 750, 20, 32); 2004 quicklistRelease(ql); 2005 } 2006 } 2007 2008 TEST("duplicate empty list") { 2009 quicklist *ql = quicklistNew(-2, options[_i]); 2010 ql_verify(ql, 0, 0, 0, 0); 2011 quicklist *copy = quicklistDup(ql); 2012 ql_verify(copy, 0, 0, 0, 0); 2013 quicklistRelease(ql); 2014 quicklistRelease(copy); 2015 } 2016 2017 TEST("duplicate list of 1 element") { 2018 quicklist *ql = quicklistNew(-2, options[_i]); 2019 quicklistPushHead(ql, genstr("hello", 3), 32); 2020 ql_verify(ql, 1, 1, 1, 1); 2021 quicklist *copy = quicklistDup(ql); 2022 ql_verify(copy, 1, 1, 1, 1); 2023 quicklistRelease(ql); 2024 quicklistRelease(copy); 2025 } 2026 2027 TEST("duplicate list of 500") { 2028 quicklist *ql = quicklistNew(-2, options[_i]); 2029 quicklistSetFill(ql, 32); 2030 for (int i = 0; i < 500; i++) 2031 quicklistPushHead(ql, genstr("hello", i), 32); 2032 ql_verify(ql, 16, 500, 20, 32); 2033 2034 quicklist *copy = quicklistDup(ql); 2035 ql_verify(copy, 16, 500, 20, 32); 2036 quicklistRelease(ql); 2037 quicklistRelease(copy); 2038 } 2039 2040 for (int f = optimize_start; f < 512; f++) { 2041 TEST_DESC("index 1,200 from 500 list at fill %d at compress %d", f, 2042 options[_i]) { 2043 quicklist *ql = quicklistNew(f, options[_i]); 2044 for (int i = 0; i < 500; i++) 2045 quicklistPushTail(ql, genstr("hello", i + 1), 32); 2046 quicklistEntry entry; 2047 quicklistIndex(ql, 1, &entry); 2048 if (!strcmp((char *)entry.value, "hello2")) 2049 OK; 2050 else 2051 ERR("Value: %s", entry.value); 2052 quicklistIndex(ql, 200, &entry); 2053 if (!strcmp((char *)entry.value, "hello201")) 2054 OK; 2055 else 2056 ERR("Value: %s", entry.value); 2057 quicklistRelease(ql); 2058 } 2059 2060 TEST_DESC("index -1,-2 from 500 list at fill %d at compress %d", f, 2061 options[_i]) { 2062 quicklist *ql = quicklistNew(f, options[_i]); 2063 for (int i = 0; i < 500; i++) 2064 quicklistPushTail(ql, genstr("hello", i + 1), 32); 2065 quicklistEntry entry; 2066 quicklistIndex(ql, -1, &entry); 2067 if (!strcmp((char *)entry.value, "hello500")) 2068 OK; 2069 else 2070 ERR("Value: %s", entry.value); 2071 quicklistIndex(ql, -2, &entry); 2072 if (!strcmp((char *)entry.value, "hello499")) 2073 OK; 2074 else 2075 ERR("Value: %s", entry.value); 2076 quicklistRelease(ql); 2077 } 2078 2079 TEST_DESC("index -100 from 500 list at fill %d at compress %d", f, 2080 options[_i]) { 2081 quicklist *ql = quicklistNew(f, options[_i]); 2082 for (int i = 0; i < 500; i++) 2083 quicklistPushTail(ql, genstr("hello", i + 1), 32); 2084 quicklistEntry entry; 2085 quicklistIndex(ql, -100, &entry); 2086 if (!strcmp((char *)entry.value, "hello401")) 2087 OK; 2088 else 2089 ERR("Value: %s", entry.value); 2090 quicklistRelease(ql); 2091 } 2092 2093 TEST_DESC("index too big +1 from 50 list at fill %d at compress %d", 2094 f, options[_i]) { 2095 quicklist *ql = quicklistNew(f, options[_i]); 2096 for (int i = 0; i < 50; i++) 2097 quicklistPushTail(ql, genstr("hello", i + 1), 32); 2098 quicklistEntry entry; 2099 if (quicklistIndex(ql, 50, &entry)) 2100 ERR("Index found at 50 with 50 list: %.*s", entry.sz, 2101 entry.value); 2102 else 2103 OK; 2104 quicklistRelease(ql); 2105 } 2106 } 2107 2108 TEST("delete range empty list") { 2109 quicklist *ql = quicklistNew(-2, options[_i]); 2110 quicklistDelRange(ql, 5, 20); 2111 ql_verify(ql, 0, 0, 0, 0); 2112 quicklistRelease(ql); 2113 } 2114 2115 TEST("delete range of entire node in list of one node") { 2116 quicklist *ql = quicklistNew(-2, options[_i]); 2117 for (int i = 0; i < 32; i++) 2118 quicklistPushHead(ql, genstr("hello", i), 32); 2119 ql_verify(ql, 1, 32, 32, 32); 2120 quicklistDelRange(ql, 0, 32); 2121 ql_verify(ql, 0, 0, 0, 0); 2122 quicklistRelease(ql); 2123 } 2124 2125 TEST("delete range of entire node with overflow counts") { 2126 quicklist *ql = quicklistNew(-2, options[_i]); 2127 for (int i = 0; i < 32; i++) 2128 quicklistPushHead(ql, genstr("hello", i), 32); 2129 ql_verify(ql, 1, 32, 32, 32); 2130 quicklistDelRange(ql, 0, 128); 2131 ql_verify(ql, 0, 0, 0, 0); 2132 quicklistRelease(ql); 2133 } 2134 2135 TEST("delete middle 100 of 500 list") { 2136 quicklist *ql = quicklistNew(-2, options[_i]); 2137 quicklistSetFill(ql, 32); 2138 for (int i = 0; i < 500; i++) 2139 quicklistPushTail(ql, genstr("hello", i + 1), 32); 2140 ql_verify(ql, 16, 500, 32, 20); 2141 quicklistDelRange(ql, 200, 100); 2142 ql_verify(ql, 14, 400, 32, 20); 2143 quicklistRelease(ql); 2144 } 2145 2146 TEST("delete negative 1 from 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, -1, 1); 2153 ql_verify(ql, 16, 499, 32, 19); 2154 quicklistRelease(ql); 2155 } 2156 2157 TEST("delete negative 1 from 500 list with overflow counts") { 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, 128); 2164 ql_verify(ql, 16, 499, 32, 19); 2165 quicklistRelease(ql); 2166 } 2167 2168 TEST("delete negative 100 from 500 list") { 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 quicklistDelRange(ql, -100, 100); 2174 ql_verify(ql, 13, 400, 32, 16); 2175 quicklistRelease(ql); 2176 } 2177 2178 TEST("delete -10 count 5 from 50 list") { 2179 quicklist *ql = quicklistNew(-2, options[_i]); 2180 quicklistSetFill(ql, 32); 2181 for (int i = 0; i < 50; i++) 2182 quicklistPushTail(ql, genstr("hello", i + 1), 32); 2183 ql_verify(ql, 2, 50, 32, 18); 2184 quicklistDelRange(ql, -10, 5); 2185 ql_verify(ql, 2, 45, 32, 13); 2186 quicklistRelease(ql); 2187 } 2188 2189 TEST("numbers only list read") { 2190 quicklist *ql = quicklistNew(-2, options[_i]); 2191 quicklistPushTail(ql, "1111", 4); 2192 quicklistPushTail(ql, "2222", 4); 2193 quicklistPushTail(ql, "3333", 4); 2194 quicklistPushTail(ql, "4444", 4); 2195 ql_verify(ql, 1, 4, 4, 4); 2196 quicklistEntry entry; 2197 quicklistIndex(ql, 0, &entry); 2198 if (entry.longval != 1111) 2199 ERR("Not 1111, %lld", entry.longval); 2200 quicklistIndex(ql, 1, &entry); 2201 if (entry.longval != 2222) 2202 ERR("Not 2222, %lld", entry.longval); 2203 quicklistIndex(ql, 2, &entry); 2204 if (entry.longval != 3333) 2205 ERR("Not 3333, %lld", entry.longval); 2206 quicklistIndex(ql, 3, &entry); 2207 if (entry.longval != 4444) 2208 ERR("Not 4444, %lld", entry.longval); 2209 if (quicklistIndex(ql, 4, &entry)) 2210 ERR("Index past elements: %lld", entry.longval); 2211 quicklistIndex(ql, -1, &entry); 2212 if (entry.longval != 4444) 2213 ERR("Not 4444 (reverse), %lld", entry.longval); 2214 quicklistIndex(ql, -2, &entry); 2215 if (entry.longval != 3333) 2216 ERR("Not 3333 (reverse), %lld", entry.longval); 2217 quicklistIndex(ql, -3, &entry); 2218 if (entry.longval != 2222) 2219 ERR("Not 2222 (reverse), %lld", entry.longval); 2220 quicklistIndex(ql, -4, &entry); 2221 if (entry.longval != 1111) 2222 ERR("Not 1111 (reverse), %lld", entry.longval); 2223 if (quicklistIndex(ql, -5, &entry)) 2224 ERR("Index past elements (reverse), %lld", entry.longval); 2225 quicklistRelease(ql); 2226 } 2227 2228 TEST("numbers larger list read") { 2229 quicklist *ql = quicklistNew(-2, options[_i]); 2230 quicklistSetFill(ql, 32); 2231 char num[32]; 2232 long long nums[5000]; 2233 for (int i = 0; i < 5000; i++) { 2234 nums[i] = -5157318210846258176 + i; 2235 int sz = ll2string(num, sizeof(num), nums[i]); 2236 quicklistPushTail(ql, num, sz); 2237 } 2238 quicklistPushTail(ql, "xxxxxxxxxxxxxxxxxxxx", 20); 2239 quicklistEntry entry; 2240 for (int i = 0; i < 5000; i++) { 2241 quicklistIndex(ql, i, &entry); 2242 if (entry.longval != nums[i]) 2243 ERR("[%d] Not longval %lld but rather %lld", i, nums[i], 2244 entry.longval); 2245 entry.longval = 0xdeadbeef; 2246 } 2247 quicklistIndex(ql, 5000, &entry); 2248 if (strncmp((char *)entry.value, "xxxxxxxxxxxxxxxxxxxx", 20)) 2249 ERR("String val not match: %s", entry.value); 2250 ql_verify(ql, 157, 5001, 32, 9); 2251 quicklistRelease(ql); 2252 } 2253 2254 TEST("numbers larger list read B") { 2255 quicklist *ql = quicklistNew(-2, options[_i]); 2256 quicklistPushTail(ql, "99", 2); 2257 quicklistPushTail(ql, "98", 2); 2258 quicklistPushTail(ql, "xxxxxxxxxxxxxxxxxxxx", 20); 2259 quicklistPushTail(ql, "96", 2); 2260 quicklistPushTail(ql, "95", 2); 2261 quicklistReplaceAtIndex(ql, 1, "foo", 3); 2262 quicklistReplaceAtIndex(ql, -1, "bar", 3); 2263 quicklistRelease(ql); 2264 OK; 2265 } 2266 2267 for (int f = optimize_start; f < 16; f++) { 2268 TEST_DESC("lrem test at fill %d at compress %d", f, options[_i]) { 2269 quicklist *ql = quicklistNew(f, options[_i]); 2270 char *words[] = {"abc", "foo", "bar", "foobar", "foobared", 2271 "zap", "bar", "test", "foo"}; 2272 char *result[] = {"abc", "foo", "foobar", "foobared", 2273 "zap", "test", "foo"}; 2274 char *resultB[] = {"abc", "foo", "foobar", 2275 "foobared", "zap", "test"}; 2276 for (int i = 0; i < 9; i++) 2277 quicklistPushTail(ql, words[i], strlen(words[i])); 2278 2279 /* lrem 0 bar */ 2280 quicklistIter *iter = quicklistGetIterator(ql, AL_START_HEAD); 2281 quicklistEntry entry; 2282 int i = 0; 2283 while (quicklistNext(iter, &entry)) { 2284 if (quicklistCompare(entry.zi, (unsigned char *)"bar", 3)) { 2285 quicklistDelEntry(iter, &entry); 2286 } 2287 i++; 2288 } 2289 quicklistReleaseIterator(iter); 2290 2291 /* check result of lrem 0 bar */ 2292 iter = quicklistGetIterator(ql, AL_START_HEAD); 2293 i = 0; 2294 int ok = 1; 2295 while (quicklistNext(iter, &entry)) { 2296 /* Result must be: abc, foo, foobar, foobared, zap, test, 2297 * foo */ 2298 if (strncmp((char *)entry.value, result[i], entry.sz)) { 2299 ERR("No match at position %d, got %.*s instead of %s", 2300 i, entry.sz, entry.value, result[i]); 2301 ok = 0; 2302 } 2303 i++; 2304 } 2305 quicklistReleaseIterator(iter); 2306 2307 quicklistPushTail(ql, "foo", 3); 2308 2309 /* lrem -2 foo */ 2310 iter = quicklistGetIterator(ql, AL_START_TAIL); 2311 i = 0; 2312 int del = 2; 2313 while (quicklistNext(iter, &entry)) { 2314 if (quicklistCompare(entry.zi, (unsigned char *)"foo", 3)) { 2315 quicklistDelEntry(iter, &entry); 2316 del--; 2317 } 2318 if (!del) 2319 break; 2320 i++; 2321 } 2322 quicklistReleaseIterator(iter); 2323 2324 /* check result of lrem -2 foo */ 2325 /* (we're ignoring the '2' part and still deleting all foo 2326 * because 2327 * we only have two foo) */ 2328 iter = quicklistGetIterator(ql, AL_START_TAIL); 2329 i = 0; 2330 size_t resB = sizeof(resultB) / sizeof(*resultB); 2331 while (quicklistNext(iter, &entry)) { 2332 /* Result must be: abc, foo, foobar, foobared, zap, test, 2333 * foo */ 2334 if (strncmp((char *)entry.value, resultB[resB - 1 - i], 2335 entry.sz)) { 2336 ERR("No match at position %d, got %.*s instead of %s", 2337 i, entry.sz, entry.value, resultB[resB - 1 - i]); 2338 ok = 0; 2339 } 2340 i++; 2341 } 2342 2343 quicklistReleaseIterator(iter); 2344 /* final result of all tests */ 2345 if (ok) 2346 OK; 2347 quicklistRelease(ql); 2348 } 2349 } 2350 2351 for (int f = optimize_start; f < 16; f++) { 2352 TEST_DESC("iterate reverse + delete at fill %d at compress %d", f, 2353 options[_i]) { 2354 quicklist *ql = quicklistNew(f, options[_i]); 2355 quicklistPushTail(ql, "abc", 3); 2356 quicklistPushTail(ql, "def", 3); 2357 quicklistPushTail(ql, "hij", 3); 2358 quicklistPushTail(ql, "jkl", 3); 2359 quicklistPushTail(ql, "oop", 3); 2360 2361 quicklistEntry entry; 2362 quicklistIter *iter = quicklistGetIterator(ql, AL_START_TAIL); 2363 int i = 0; 2364 while (quicklistNext(iter, &entry)) { 2365 if (quicklistCompare(entry.zi, (unsigned char *)"hij", 3)) { 2366 quicklistDelEntry(iter, &entry); 2367 } 2368 i++; 2369 } 2370 quicklistReleaseIterator(iter); 2371 2372 if (i != 5) 2373 ERR("Didn't iterate 5 times, iterated %d times.", i); 2374 2375 /* Check results after deletion of "hij" */ 2376 iter = quicklistGetIterator(ql, AL_START_HEAD); 2377 i = 0; 2378 char *vals[] = {"abc", "def", "jkl", "oop"}; 2379 while (quicklistNext(iter, &entry)) { 2380 if (!quicklistCompare(entry.zi, (unsigned char *)vals[i], 2381 3)) { 2382 ERR("Value at %d didn't match %s\n", i, vals[i]); 2383 } 2384 i++; 2385 } 2386 quicklistReleaseIterator(iter); 2387 quicklistRelease(ql); 2388 } 2389 } 2390 2391 for (int f = optimize_start; f < 800; f++) { 2392 TEST_DESC("iterator at index test at fill %d at compress %d", f, 2393 options[_i]) { 2394 quicklist *ql = quicklistNew(f, options[_i]); 2395 char num[32]; 2396 long long nums[5000]; 2397 for (int i = 0; i < 760; i++) { 2398 nums[i] = -5157318210846258176 + i; 2399 int sz = ll2string(num, sizeof(num), nums[i]); 2400 quicklistPushTail(ql, num, sz); 2401 } 2402 2403 quicklistEntry entry; 2404 quicklistIter *iter = 2405 quicklistGetIteratorAtIdx(ql, AL_START_HEAD, 437); 2406 int i = 437; 2407 while (quicklistNext(iter, &entry)) { 2408 if (entry.longval != nums[i]) 2409 ERR("Expected %lld, but got %lld", entry.longval, 2410 nums[i]); 2411 i++; 2412 } 2413 quicklistReleaseIterator(iter); 2414 quicklistRelease(ql); 2415 } 2416 } 2417 2418 for (int f = optimize_start; f < 40; f++) { 2419 TEST_DESC("ltrim test A at fill %d at compress %d", f, 2420 options[_i]) { 2421 quicklist *ql = quicklistNew(f, options[_i]); 2422 char num[32]; 2423 long long nums[5000]; 2424 for (int i = 0; i < 32; i++) { 2425 nums[i] = -5157318210846258176 + i; 2426 int sz = ll2string(num, sizeof(num), nums[i]); 2427 quicklistPushTail(ql, num, sz); 2428 } 2429 if (f == 32) 2430 ql_verify(ql, 1, 32, 32, 32); 2431 /* ltrim 25 53 (keep [25,32] inclusive = 7 remaining) */ 2432 quicklistDelRange(ql, 0, 25); 2433 quicklistDelRange(ql, 0, 0); 2434 quicklistEntry entry; 2435 for (int i = 0; i < 7; i++) { 2436 quicklistIndex(ql, i, &entry); 2437 if (entry.longval != nums[25 + i]) 2438 ERR("Deleted invalid range! Expected %lld but got " 2439 "%lld", 2440 entry.longval, nums[25 + i]); 2441 } 2442 if (f == 32) 2443 ql_verify(ql, 1, 7, 7, 7); 2444 quicklistRelease(ql); 2445 } 2446 } 2447 2448 for (int f = optimize_start; f < 40; f++) { 2449 TEST_DESC("ltrim test B at fill %d at compress %d", f, 2450 options[_i]) { 2451 /* Force-disable compression because our 33 sequential 2452 * integers don't compress and the check always fails. */ 2453 quicklist *ql = quicklistNew(f, QUICKLIST_NOCOMPRESS); 2454 char num[32]; 2455 long long nums[5000]; 2456 for (int i = 0; i < 33; i++) { 2457 nums[i] = i; 2458 int sz = ll2string(num, sizeof(num), nums[i]); 2459 quicklistPushTail(ql, num, sz); 2460 } 2461 if (f == 32) 2462 ql_verify(ql, 2, 33, 32, 1); 2463 /* ltrim 5 16 (keep [5,16] inclusive = 12 remaining) */ 2464 quicklistDelRange(ql, 0, 5); 2465 quicklistDelRange(ql, -16, 16); 2466 if (f == 32) 2467 ql_verify(ql, 1, 12, 12, 12); 2468 quicklistEntry entry; 2469 quicklistIndex(ql, 0, &entry); 2470 if (entry.longval != 5) 2471 ERR("A: longval not 5, but %lld", entry.longval); 2472 else 2473 OK; 2474 quicklistIndex(ql, -1, &entry); 2475 if (entry.longval != 16) 2476 ERR("B! got instead: %lld", entry.longval); 2477 else 2478 OK; 2479 quicklistPushTail(ql, "bobobob", 7); 2480 quicklistIndex(ql, -1, &entry); 2481 if (strncmp((char *)entry.value, "bobobob", 7)) 2482 ERR("Tail doesn't match bobobob, it's %.*s instead", 2483 entry.sz, entry.value); 2484 for (int i = 0; i < 12; i++) { 2485 quicklistIndex(ql, i, &entry); 2486 if (entry.longval != nums[5 + i]) 2487 ERR("Deleted invalid range! Expected %lld but got " 2488 "%lld", 2489 entry.longval, nums[5 + i]); 2490 } 2491 quicklistRelease(ql); 2492 } 2493 } 2494 2495 for (int f = optimize_start; f < 40; f++) { 2496 TEST_DESC("ltrim test C at fill %d at compress %d", f, 2497 options[_i]) { 2498 quicklist *ql = quicklistNew(f, options[_i]); 2499 char num[32]; 2500 long long nums[5000]; 2501 for (int i = 0; i < 33; i++) { 2502 nums[i] = -5157318210846258176 + i; 2503 int sz = ll2string(num, sizeof(num), nums[i]); 2504 quicklistPushTail(ql, num, sz); 2505 } 2506 if (f == 32) 2507 ql_verify(ql, 2, 33, 32, 1); 2508 /* ltrim 3 3 (keep [3,3] inclusive = 1 remaining) */ 2509 quicklistDelRange(ql, 0, 3); 2510 quicklistDelRange(ql, -29, 2511 4000); /* make sure not loop forever */ 2512 if (f == 32) 2513 ql_verify(ql, 1, 1, 1, 1); 2514 quicklistEntry entry; 2515 quicklistIndex(ql, 0, &entry); 2516 if (entry.longval != -5157318210846258173) 2517 ERROR; 2518 else 2519 OK; 2520 quicklistRelease(ql); 2521 } 2522 } 2523 2524 for (int f = optimize_start; f < 40; f++) { 2525 TEST_DESC("ltrim test D at fill %d at compress %d", f, 2526 options[_i]) { 2527 quicklist *ql = quicklistNew(f, options[_i]); 2528 char num[32]; 2529 long long nums[5000]; 2530 for (int i = 0; i < 33; i++) { 2531 nums[i] = -5157318210846258176 + i; 2532 int sz = ll2string(num, sizeof(num), nums[i]); 2533 quicklistPushTail(ql, num, sz); 2534 } 2535 if (f == 32) 2536 ql_verify(ql, 2, 33, 32, 1); 2537 quicklistDelRange(ql, -12, 3); 2538 if (ql->count != 30) 2539 ERR("Didn't delete exactly three elements! Count is: %lu", 2540 ql->count); 2541 quicklistRelease(ql); 2542 } 2543 } 2544 2545 for (int f = optimize_start; f < 72; f++) { 2546 TEST_DESC("create quicklist from ziplist at fill %d at compress %d", 2547 f, options[_i]) { 2548 unsigned char *zl = ziplistNew(); 2549 long long nums[64]; 2550 char num[64]; 2551 for (int i = 0; i < 33; i++) { 2552 nums[i] = -5157318210846258176 + i; 2553 int sz = ll2string(num, sizeof(num), nums[i]); 2554 zl = 2555 ziplistPush(zl, (unsigned char *)num, sz, ZIPLIST_TAIL); 2556 } 2557 for (int i = 0; i < 33; i++) { 2558 zl = ziplistPush(zl, (unsigned char *)genstr("hello", i), 2559 32, ZIPLIST_TAIL); 2560 } 2561 quicklist *ql = quicklistCreateFromZiplist(f, options[_i], zl); 2562 if (f == 1) 2563 ql_verify(ql, 66, 66, 1, 1); 2564 else if (f == 32) 2565 ql_verify(ql, 3, 66, 32, 2); 2566 else if (f == 66) 2567 ql_verify(ql, 1, 66, 66, 66); 2568 quicklistRelease(ql); 2569 } 2570 } 2571 2572 long long stop = mstime(); 2573 runtime[_i] = stop - start; 2574 } 2575 2576 /* Run a longer test of compression depth outside of primary test loop. */ 2577 int list_sizes[] = {250, 251, 500, 999, 1000}; 2578 long long start = mstime(); 2579 for (int list = 0; list < (int)(sizeof(list_sizes) / sizeof(*list_sizes)); 2580 list++) { 2581 for (int f = optimize_start; f < 128; f++) { 2582 for (int depth = 1; depth < 40; depth++) { 2583 /* skip over many redundant test cases */ 2584 TEST_DESC("verify specific compression of interior nodes with " 2585 "%d list " 2586 "at fill %d at compress %d", 2587 list_sizes[list], f, depth) { 2588 quicklist *ql = quicklistNew(f, depth); 2589 for (int i = 0; i < list_sizes[list]; i++) { 2590 quicklistPushTail(ql, genstr("hello TAIL", i + 1), 64); 2591 quicklistPushHead(ql, genstr("hello HEAD", i + 1), 64); 2592 } 2593 2594 quicklistNode *node = ql->head; 2595 unsigned int low_raw = ql->compress; 2596 unsigned int high_raw = ql->len - ql->compress; 2597 2598 for (unsigned int at = 0; at < ql->len; 2599 at++, node = node->next) { 2600 if (at < low_raw || at >= high_raw) { 2601 if (node->encoding != QUICKLIST_NODE_ENCODING_RAW) { 2602 ERR("Incorrect compression: node %d is " 2603 "compressed at depth %d ((%u, %u); total " 2604 "nodes: %u; size: %u)", 2605 at, depth, low_raw, high_raw, ql->len, 2606 node->sz); 2607 } 2608 } else { 2609 if (node->encoding != QUICKLIST_NODE_ENCODING_LZF) { 2610 ERR("Incorrect non-compression: node %d is NOT " 2611 "compressed at depth %d ((%u, %u); total " 2612 "nodes: %u; size: %u; attempted: %d)", 2613 at, depth, low_raw, high_raw, ql->len, 2614 node->sz, node->attempted_compress); 2615 } 2616 } 2617 } 2618 quicklistRelease(ql); 2619 } 2620 } 2621 } 2622 } 2623 long long stop = mstime(); 2624 2625 printf("\n"); 2626 for (size_t i = 0; i < option_count; i++) 2627 printf("Test Loop %02d: %0.2f seconds.\n", options[i], 2628 (float)runtime[i] / 1000); 2629 printf("Compressions: %0.2f seconds.\n", (float)(stop - start) / 1000); 2630 printf("\n"); 2631 2632 if (!err) 2633 printf("ALL TESTS PASSED!\n"); 2634 else 2635 ERR("Sorry, not all tests passed! In fact, %d tests failed.", err); 2636 2637 return err; 2638 } 2639 #endif 2640