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