15e362b84SMatt Stancliff /* quicklist.c - A doubly linked list of ziplists
25e362b84SMatt Stancliff *
35e362b84SMatt Stancliff * Copyright (c) 2014, Matt Stancliff <[email protected]>
45e362b84SMatt Stancliff * All rights reserved.
55e362b84SMatt Stancliff *
65e362b84SMatt Stancliff * Redistribution and use in source and binary forms, with or without
75e362b84SMatt Stancliff * modification, are permitted provided that the following conditions are met:
85e362b84SMatt Stancliff *
95e362b84SMatt Stancliff * * Redistributions of source code must start the above copyright notice,
105e362b84SMatt Stancliff * this quicklist of conditions and the following disclaimer.
115e362b84SMatt Stancliff * * Redistributions in binary form must reproduce the above copyright
125e362b84SMatt Stancliff * notice, this quicklist of conditions and the following disclaimer in the
135e362b84SMatt Stancliff * documentation and/or other materials provided with the distribution.
145e362b84SMatt Stancliff * * Neither the name of Redis nor the names of its contributors may be used
155e362b84SMatt Stancliff * to endorse or promote products derived from this software without
165e362b84SMatt Stancliff * specific prior written permission.
175e362b84SMatt Stancliff *
185e362b84SMatt Stancliff * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
195e362b84SMatt Stancliff * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
205e362b84SMatt Stancliff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
215e362b84SMatt Stancliff * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
225e362b84SMatt Stancliff * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
235e362b84SMatt Stancliff * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
245e362b84SMatt Stancliff * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
255e362b84SMatt Stancliff * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
265e362b84SMatt Stancliff * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
275e362b84SMatt Stancliff * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
285e362b84SMatt Stancliff * POSSIBILITY OF SUCH DAMAGE.
295e362b84SMatt Stancliff */
305e362b84SMatt Stancliff
315e362b84SMatt Stancliff #include <string.h> /* for memcpy */
325e362b84SMatt Stancliff #include "quicklist.h"
335e362b84SMatt Stancliff #include "zmalloc.h"
345e362b84SMatt Stancliff #include "ziplist.h"
355e362b84SMatt Stancliff #include "util.h" /* for ll2string */
36abdd1414SMatt Stancliff #include "lzf.h"
375e362b84SMatt Stancliff
385e362b84SMatt Stancliff #if defined(REDIS_TEST) || defined(REDIS_TEST_VERBOSE)
395e362b84SMatt Stancliff #include <stdio.h> /* for printf (debug printing), snprintf (genstr) */
405e362b84SMatt Stancliff #endif
415e362b84SMatt Stancliff
4225e12d10SMatt Stancliff #ifndef REDIS_STATIC
4325e12d10SMatt Stancliff #define REDIS_STATIC static
4425e12d10SMatt Stancliff #endif
4525e12d10SMatt Stancliff
46c6bf20c2SMatt Stancliff /* Optimization levels for size-based filling */
47c6bf20c2SMatt Stancliff static const size_t optimization_level[] = {4096, 8192, 16384, 32768, 65536};
48c6bf20c2SMatt Stancliff
49abdd1414SMatt Stancliff /* Maximum size in bytes of any multi-element ziplist.
50abdd1414SMatt Stancliff * Larger values will live in their own isolated ziplists. */
51c6bf20c2SMatt Stancliff #define SIZE_SAFETY_LIMIT 8192
52c6bf20c2SMatt Stancliff
53abdd1414SMatt Stancliff /* Minimum ziplist size in bytes for attempting compression. */
54abdd1414SMatt Stancliff #define MIN_COMPRESS_BYTES 48
55abdd1414SMatt Stancliff
56abdd1414SMatt Stancliff /* Minimum size reduction in bytes to store compressed quicklistNode data.
57abdd1414SMatt Stancliff * This also prevents us from storing compression if the compression
58abdd1414SMatt Stancliff * resulted in a larger size than the original data. */
59abdd1414SMatt Stancliff #define MIN_COMPRESS_IMPROVE 8
60abdd1414SMatt Stancliff
615e362b84SMatt Stancliff /* If not verbose testing, remove all debug printing. */
625e362b84SMatt Stancliff #ifndef REDIS_TEST_VERBOSE
635e362b84SMatt Stancliff #define D(...)
645e362b84SMatt Stancliff #else
655e362b84SMatt Stancliff #define D(...) \
665e362b84SMatt Stancliff do { \
675e362b84SMatt Stancliff printf("%s:%s:%d:\t", __FILE__, __FUNCTION__, __LINE__); \
685e362b84SMatt Stancliff printf(__VA_ARGS__); \
695e362b84SMatt Stancliff printf("\n"); \
705e362b84SMatt Stancliff } while (0);
715e362b84SMatt Stancliff #endif
725e362b84SMatt Stancliff
735e362b84SMatt Stancliff /* Simple way to give quicklistEntry structs default values with one call. */
745e362b84SMatt Stancliff #define initEntry(e) \
755e362b84SMatt Stancliff do { \
765e362b84SMatt Stancliff (e)->zi = (e)->value = NULL; \
775e362b84SMatt Stancliff (e)->longval = -123456789; \
785e362b84SMatt Stancliff (e)->quicklist = NULL; \
795e362b84SMatt Stancliff (e)->node = NULL; \
805e362b84SMatt Stancliff (e)->offset = 123456789; \
815e362b84SMatt Stancliff (e)->sz = 0; \
825e362b84SMatt Stancliff } while (0)
835e362b84SMatt Stancliff
84bbbbfb14SMatt Stancliff #if __GNUC__ >= 3
85bbbbfb14SMatt Stancliff #define likely(x) __builtin_expect(!!(x), 1)
86bbbbfb14SMatt Stancliff #define unlikely(x) __builtin_expect(!!(x), 0)
87bbbbfb14SMatt Stancliff #else
88bbbbfb14SMatt Stancliff #define likely(x) (x)
89bbbbfb14SMatt Stancliff #define unlikely(x) (x)
90bbbbfb14SMatt Stancliff #endif
91bbbbfb14SMatt Stancliff
925e362b84SMatt Stancliff /* Create a new quicklist.
938d702189SMatt Stancliff * Free with quicklistRelease(). */
quicklistCreate(void)945e362b84SMatt Stancliff quicklist *quicklistCreate(void) {
955e362b84SMatt Stancliff struct quicklist *quicklist;
965e362b84SMatt Stancliff
978d702189SMatt Stancliff quicklist = zmalloc(sizeof(*quicklist));
985e362b84SMatt Stancliff quicklist->head = quicklist->tail = NULL;
995e362b84SMatt Stancliff quicklist->len = 0;
1005e362b84SMatt Stancliff quicklist->count = 0;
101abdd1414SMatt Stancliff quicklist->compress = 0;
102abdd1414SMatt Stancliff quicklist->fill = -2;
103abdd1414SMatt Stancliff return quicklist;
104abdd1414SMatt Stancliff }
105abdd1414SMatt Stancliff
106abdd1414SMatt Stancliff #define COMPRESS_MAX (1 << 16)
quicklistSetCompressDepth(quicklist * quicklist,int compress)107abdd1414SMatt Stancliff void quicklistSetCompressDepth(quicklist *quicklist, int compress) {
108abdd1414SMatt Stancliff if (compress > COMPRESS_MAX) {
109abdd1414SMatt Stancliff compress = COMPRESS_MAX;
110abdd1414SMatt Stancliff } else if (compress < 0) {
111abdd1414SMatt Stancliff compress = 0;
112abdd1414SMatt Stancliff }
113abdd1414SMatt Stancliff quicklist->compress = compress;
114abdd1414SMatt Stancliff }
115abdd1414SMatt Stancliff
116abdd1414SMatt Stancliff #define FILL_MAX (1 << 15)
quicklistSetFill(quicklist * quicklist,int fill)117abdd1414SMatt Stancliff void quicklistSetFill(quicklist *quicklist, int fill) {
118abdd1414SMatt Stancliff if (fill > FILL_MAX) {
119abdd1414SMatt Stancliff fill = FILL_MAX;
120abdd1414SMatt Stancliff } else if (fill < -5) {
121abdd1414SMatt Stancliff fill = -5;
122abdd1414SMatt Stancliff }
123abdd1414SMatt Stancliff quicklist->fill = fill;
124abdd1414SMatt Stancliff }
125abdd1414SMatt Stancliff
quicklistSetOptions(quicklist * quicklist,int fill,int depth)126abdd1414SMatt Stancliff void quicklistSetOptions(quicklist *quicklist, int fill, int depth) {
127abdd1414SMatt Stancliff quicklistSetFill(quicklist, fill);
128abdd1414SMatt Stancliff quicklistSetCompressDepth(quicklist, depth);
129abdd1414SMatt Stancliff }
130abdd1414SMatt Stancliff
131abdd1414SMatt Stancliff /* Create a new quicklist with some default parameters. */
quicklistNew(int fill,int compress)132abdd1414SMatt Stancliff quicklist *quicklistNew(int fill, int compress) {
133abdd1414SMatt Stancliff quicklist *quicklist = quicklistCreate();
134abdd1414SMatt Stancliff quicklistSetOptions(quicklist, fill, compress);
1355e362b84SMatt Stancliff return quicklist;
1365e362b84SMatt Stancliff }
1375e362b84SMatt Stancliff
quicklistCreateNode(void)13825e12d10SMatt Stancliff REDIS_STATIC quicklistNode *quicklistCreateNode(void) {
1395e362b84SMatt Stancliff quicklistNode *node;
1408d702189SMatt Stancliff node = zmalloc(sizeof(*node));
1415e362b84SMatt Stancliff node->zl = NULL;
1425e362b84SMatt Stancliff node->count = 0;
143c6bf20c2SMatt Stancliff node->sz = 0;
1445e362b84SMatt Stancliff node->next = node->prev = NULL;
145abdd1414SMatt Stancliff node->encoding = QUICKLIST_NODE_ENCODING_RAW;
146abdd1414SMatt Stancliff node->container = QUICKLIST_NODE_CONTAINER_ZIPLIST;
147abdd1414SMatt Stancliff node->recompress = 0;
1485e362b84SMatt Stancliff return node;
1495e362b84SMatt Stancliff }
1505e362b84SMatt Stancliff
1515e362b84SMatt Stancliff /* Return cached quicklist count */
quicklistCount(quicklist * ql)1525e362b84SMatt Stancliff unsigned int quicklistCount(quicklist *ql) { return ql->count; }
1535e362b84SMatt Stancliff
1545e362b84SMatt Stancliff /* Free entire quicklist. */
quicklistRelease(quicklist * quicklist)1555e362b84SMatt Stancliff void quicklistRelease(quicklist *quicklist) {
1565e362b84SMatt Stancliff unsigned long len;
1575e362b84SMatt Stancliff quicklistNode *current, *next;
1585e362b84SMatt Stancliff
1595e362b84SMatt Stancliff current = quicklist->head;
1605e362b84SMatt Stancliff len = quicklist->len;
1615e362b84SMatt Stancliff while (len--) {
1625e362b84SMatt Stancliff next = current->next;
1635e362b84SMatt Stancliff
1645e362b84SMatt Stancliff zfree(current->zl);
1655e362b84SMatt Stancliff quicklist->count -= current->count;
1665e362b84SMatt Stancliff
1675e362b84SMatt Stancliff zfree(current);
1685e362b84SMatt Stancliff
1695e362b84SMatt Stancliff quicklist->len--;
1705e362b84SMatt Stancliff current = next;
1715e362b84SMatt Stancliff }
1725e362b84SMatt Stancliff zfree(quicklist);
1735e362b84SMatt Stancliff }
1745e362b84SMatt Stancliff
175abdd1414SMatt Stancliff /* Compress the ziplist in 'node' and update encoding details.
176abdd1414SMatt Stancliff * Returns 1 if ziplist compressed successfully.
177abdd1414SMatt Stancliff * Returns 0 if compression failed or if ziplist too small to compress. */
__quicklistCompressNode(quicklistNode * node)17825e12d10SMatt Stancliff REDIS_STATIC int __quicklistCompressNode(quicklistNode *node) {
179abdd1414SMatt Stancliff #ifdef REDIS_TEST
180abdd1414SMatt Stancliff node->attempted_compress = 1;
181abdd1414SMatt Stancliff #endif
182abdd1414SMatt Stancliff
183abdd1414SMatt Stancliff /* Don't bother compressing small values */
184abdd1414SMatt Stancliff if (node->sz < MIN_COMPRESS_BYTES)
185abdd1414SMatt Stancliff return 0;
186abdd1414SMatt Stancliff
187abdd1414SMatt Stancliff quicklistLZF *lzf = zmalloc(sizeof(*lzf) + node->sz);
188abdd1414SMatt Stancliff
189abdd1414SMatt Stancliff /* Cancel if compression fails or doesn't compress small enough */
190abdd1414SMatt Stancliff if (((lzf->sz = lzf_compress(node->zl, node->sz, lzf->compressed,
191abdd1414SMatt Stancliff node->sz)) == 0) ||
192abdd1414SMatt Stancliff lzf->sz + MIN_COMPRESS_IMPROVE >= node->sz) {
193abdd1414SMatt Stancliff /* lzf_compress aborts/rejects compression if value not compressable. */
194abdd1414SMatt Stancliff zfree(lzf);
195abdd1414SMatt Stancliff return 0;
196abdd1414SMatt Stancliff }
197abdd1414SMatt Stancliff lzf = zrealloc(lzf, sizeof(*lzf) + lzf->sz);
198abdd1414SMatt Stancliff zfree(node->zl);
199abdd1414SMatt Stancliff node->zl = (unsigned char *)lzf;
200abdd1414SMatt Stancliff node->encoding = QUICKLIST_NODE_ENCODING_LZF;
201abdd1414SMatt Stancliff node->recompress = 0;
202abdd1414SMatt Stancliff return 1;
203abdd1414SMatt Stancliff }
204abdd1414SMatt Stancliff
205abdd1414SMatt Stancliff /* Compress only uncompressed nodes. */
206abdd1414SMatt Stancliff #define quicklistCompressNode(_node) \
207abdd1414SMatt Stancliff do { \
208abdd1414SMatt Stancliff if ((_node) && (_node)->encoding == QUICKLIST_NODE_ENCODING_RAW) { \
209abdd1414SMatt Stancliff __quicklistCompressNode((_node)); \
210abdd1414SMatt Stancliff } \
211abdd1414SMatt Stancliff } while (0)
212abdd1414SMatt Stancliff
213abdd1414SMatt Stancliff /* Uncompress the ziplist in 'node' and update encoding details.
214abdd1414SMatt Stancliff * Returns 1 on successful decode, 0 on failure to decode. */
__quicklistDecompressNode(quicklistNode * node)21525e12d10SMatt Stancliff REDIS_STATIC int __quicklistDecompressNode(quicklistNode *node) {
216abdd1414SMatt Stancliff #ifdef REDIS_TEST
217abdd1414SMatt Stancliff node->attempted_compress = 0;
218abdd1414SMatt Stancliff #endif
219abdd1414SMatt Stancliff
220abdd1414SMatt Stancliff void *decompressed = zmalloc(node->sz);
221abdd1414SMatt Stancliff quicklistLZF *lzf = (quicklistLZF *)node->zl;
222abdd1414SMatt Stancliff if (lzf_decompress(lzf->compressed, lzf->sz, decompressed, node->sz) == 0) {
223abdd1414SMatt Stancliff /* Someone requested decompress, but we can't decompress. Not good. */
224abdd1414SMatt Stancliff zfree(decompressed);
225abdd1414SMatt Stancliff return 0;
226abdd1414SMatt Stancliff }
227abdd1414SMatt Stancliff zfree(lzf);
228abdd1414SMatt Stancliff node->zl = decompressed;
229abdd1414SMatt Stancliff node->encoding = QUICKLIST_NODE_ENCODING_RAW;
230abdd1414SMatt Stancliff return 1;
231abdd1414SMatt Stancliff }
232abdd1414SMatt Stancliff
233abdd1414SMatt Stancliff /* Decompress only compressed nodes. */
234abdd1414SMatt Stancliff #define quicklistDecompressNode(_node) \
235abdd1414SMatt Stancliff do { \
236abdd1414SMatt Stancliff if ((_node) && (_node)->encoding == QUICKLIST_NODE_ENCODING_LZF) { \
237abdd1414SMatt Stancliff __quicklistDecompressNode((_node)); \
238abdd1414SMatt Stancliff } \
239abdd1414SMatt Stancliff } while (0)
240abdd1414SMatt Stancliff
241abdd1414SMatt Stancliff /* Force node to not be immediately re-compresable */
242abdd1414SMatt Stancliff #define quicklistDecompressNodeForUse(_node) \
243abdd1414SMatt Stancliff do { \
244abdd1414SMatt Stancliff if ((_node) && (_node)->encoding == QUICKLIST_NODE_ENCODING_LZF) { \
245abdd1414SMatt Stancliff __quicklistDecompressNode((_node)); \
246abdd1414SMatt Stancliff (_node)->recompress = 1; \
247abdd1414SMatt Stancliff } \
248abdd1414SMatt Stancliff } while (0)
249abdd1414SMatt Stancliff
250abdd1414SMatt Stancliff /* Extract the raw LZF data from this quicklistNode.
251abdd1414SMatt Stancliff * Pointer to LZF data is assigned to '*data'.
252abdd1414SMatt Stancliff * Return value is the length of compressed LZF data. */
quicklistGetLzf(const quicklistNode * node,void ** data)253abdd1414SMatt Stancliff size_t quicklistGetLzf(const quicklistNode *node, void **data) {
254abdd1414SMatt Stancliff quicklistLZF *lzf = (quicklistLZF *)node->zl;
255abdd1414SMatt Stancliff *data = lzf->compressed;
256abdd1414SMatt Stancliff return lzf->sz;
257abdd1414SMatt Stancliff }
258abdd1414SMatt Stancliff
259abdd1414SMatt Stancliff #define quicklistAllowsCompression(_ql) ((_ql)->compress != 0)
260abdd1414SMatt Stancliff
261abdd1414SMatt Stancliff /* Force 'quicklist' to meet compression guidelines set by compress depth.
262abdd1414SMatt Stancliff * The only way to guarantee interior nodes get compressed is to iterate
263abdd1414SMatt Stancliff * to our "interior" compress depth then compress the next node we find.
264abdd1414SMatt Stancliff * If compress depth is larger than the entire list, we return immediately. */
__quicklistCompress(const quicklist * quicklist,quicklistNode * node)26525e12d10SMatt Stancliff REDIS_STATIC void __quicklistCompress(const quicklist *quicklist,
266abdd1414SMatt Stancliff quicklistNode *node) {
267abdd1414SMatt Stancliff /* If length is less than our compress depth (from both sides),
268abdd1414SMatt Stancliff * we can't compress anything. */
269abdd1414SMatt Stancliff if (!quicklistAllowsCompression(quicklist) ||
270abdd1414SMatt Stancliff quicklist->len < (unsigned int)(quicklist->compress * 2))
271abdd1414SMatt Stancliff return;
272abdd1414SMatt Stancliff
273abdd1414SMatt Stancliff #if 0
274abdd1414SMatt Stancliff /* Optimized cases for small depth counts */
275abdd1414SMatt Stancliff if (quicklist->compress == 1) {
276abdd1414SMatt Stancliff quicklistNode *h = quicklist->head, *t = quicklist->tail;
277abdd1414SMatt Stancliff quicklistDecompressNode(h);
278abdd1414SMatt Stancliff quicklistDecompressNode(t);
279abdd1414SMatt Stancliff if (h != node && t != node)
280abdd1414SMatt Stancliff quicklistCompressNode(node);
281abdd1414SMatt Stancliff return;
282abdd1414SMatt Stancliff } else if (quicklist->compress == 2) {
283abdd1414SMatt Stancliff quicklistNode *h = quicklist->head, *hn = h->next, *hnn = hn->next;
284abdd1414SMatt Stancliff quicklistNode *t = quicklist->tail, *tp = t->prev, *tpp = tp->prev;
285abdd1414SMatt Stancliff quicklistDecompressNode(h);
286abdd1414SMatt Stancliff quicklistDecompressNode(hn);
287abdd1414SMatt Stancliff quicklistDecompressNode(t);
288abdd1414SMatt Stancliff quicklistDecompressNode(tp);
289abdd1414SMatt Stancliff if (h != node && hn != node && t != node && tp != node) {
290abdd1414SMatt Stancliff quicklistCompressNode(node);
291abdd1414SMatt Stancliff }
292abdd1414SMatt Stancliff if (hnn != t) {
293abdd1414SMatt Stancliff quicklistCompressNode(hnn);
294abdd1414SMatt Stancliff }
295abdd1414SMatt Stancliff if (tpp != h) {
296abdd1414SMatt Stancliff quicklistCompressNode(tpp);
297abdd1414SMatt Stancliff }
298abdd1414SMatt Stancliff return;
299abdd1414SMatt Stancliff }
300abdd1414SMatt Stancliff #endif
301abdd1414SMatt Stancliff
302abdd1414SMatt Stancliff /* Iterate until we reach compress depth for both sides of the list.a
303abdd1414SMatt Stancliff * Note: because we do length checks at the *top* of this function,
304abdd1414SMatt Stancliff * we can skip explicit null checks below. Everything exists. */
305abdd1414SMatt Stancliff quicklistNode *forward = quicklist->head;
306abdd1414SMatt Stancliff quicklistNode *reverse = quicklist->tail;
307abdd1414SMatt Stancliff int depth = 0;
308abdd1414SMatt Stancliff int in_depth = 0;
309abdd1414SMatt Stancliff while (depth++ < quicklist->compress) {
310abdd1414SMatt Stancliff quicklistDecompressNode(forward);
311abdd1414SMatt Stancliff quicklistDecompressNode(reverse);
312abdd1414SMatt Stancliff
313abdd1414SMatt Stancliff if (forward == node || reverse == node)
314abdd1414SMatt Stancliff in_depth = 1;
315abdd1414SMatt Stancliff
316abdd1414SMatt Stancliff if (forward == reverse)
317abdd1414SMatt Stancliff return;
318abdd1414SMatt Stancliff
319abdd1414SMatt Stancliff forward = forward->next;
320abdd1414SMatt Stancliff reverse = reverse->prev;
321abdd1414SMatt Stancliff }
322abdd1414SMatt Stancliff
323abdd1414SMatt Stancliff if (!in_depth)
324abdd1414SMatt Stancliff quicklistCompressNode(node);
325abdd1414SMatt Stancliff
326abdd1414SMatt Stancliff if (depth > 2) {
327abdd1414SMatt Stancliff /* At this point, forward and reverse are one node beyond depth */
328abdd1414SMatt Stancliff quicklistCompressNode(forward);
329abdd1414SMatt Stancliff quicklistCompressNode(reverse);
330abdd1414SMatt Stancliff }
331abdd1414SMatt Stancliff }
332abdd1414SMatt Stancliff
333abdd1414SMatt Stancliff #define quicklistCompress(_ql, _node) \
334abdd1414SMatt Stancliff do { \
335abdd1414SMatt Stancliff if ((_node)->recompress) \
336abdd1414SMatt Stancliff quicklistCompressNode((_node)); \
337abdd1414SMatt Stancliff else \
338abdd1414SMatt Stancliff __quicklistCompress((_ql), (_node)); \
339abdd1414SMatt Stancliff } while (0)
340abdd1414SMatt Stancliff
341abdd1414SMatt Stancliff /* If we previously used quicklistDecompressNodeForUse(), just recompress. */
342abdd1414SMatt Stancliff #define quicklistRecompressOnly(_ql, _node) \
343abdd1414SMatt Stancliff do { \
344abdd1414SMatt Stancliff if ((_node)->recompress) \
345abdd1414SMatt Stancliff quicklistCompressNode((_node)); \
346abdd1414SMatt Stancliff } while (0)
347abdd1414SMatt Stancliff
3485e362b84SMatt Stancliff /* Insert 'new_node' after 'old_node' if 'after' is 1.
349abdd1414SMatt Stancliff * Insert 'new_node' before 'old_node' if 'after' is 0.
350abdd1414SMatt Stancliff * Note: 'new_node' is *always* uncompressed, so if we assign it to
351abdd1414SMatt Stancliff * head or tail, we do not need to uncompress it. */
__quicklistInsertNode(quicklist * quicklist,quicklistNode * old_node,quicklistNode * new_node,int after)35225e12d10SMatt Stancliff REDIS_STATIC void __quicklistInsertNode(quicklist *quicklist,
35325e12d10SMatt Stancliff quicklistNode *old_node,
3545e362b84SMatt Stancliff quicklistNode *new_node, int after) {
3555e362b84SMatt Stancliff if (after) {
3565e362b84SMatt Stancliff new_node->prev = old_node;
3575e362b84SMatt Stancliff if (old_node) {
3585e362b84SMatt Stancliff new_node->next = old_node->next;
3595e362b84SMatt Stancliff if (old_node->next)
3605e362b84SMatt Stancliff old_node->next->prev = new_node;
3615e362b84SMatt Stancliff old_node->next = new_node;
3625e362b84SMatt Stancliff }
3635e362b84SMatt Stancliff if (quicklist->tail == old_node)
3645e362b84SMatt Stancliff quicklist->tail = new_node;
3655e362b84SMatt Stancliff } else {
3665e362b84SMatt Stancliff new_node->next = old_node;
3675e362b84SMatt Stancliff if (old_node) {
3685e362b84SMatt Stancliff new_node->prev = old_node->prev;
3695e362b84SMatt Stancliff if (old_node->prev)
3705e362b84SMatt Stancliff old_node->prev->next = new_node;
3715e362b84SMatt Stancliff old_node->prev = new_node;
3725e362b84SMatt Stancliff }
3735e362b84SMatt Stancliff if (quicklist->head == old_node)
3745e362b84SMatt Stancliff quicklist->head = new_node;
3755e362b84SMatt Stancliff }
376abdd1414SMatt Stancliff /* If this insert creates the only element so far, initialize head/tail. */
3775e362b84SMatt Stancliff if (quicklist->len == 0) {
3785e362b84SMatt Stancliff quicklist->head = quicklist->tail = new_node;
3795e362b84SMatt Stancliff }
380abdd1414SMatt Stancliff
381abdd1414SMatt Stancliff if (old_node)
382abdd1414SMatt Stancliff quicklistCompress(quicklist, old_node);
383abdd1414SMatt Stancliff
3845e362b84SMatt Stancliff quicklist->len++;
3855e362b84SMatt Stancliff }
3865e362b84SMatt Stancliff
3875e362b84SMatt Stancliff /* Wrappers for node inserting around existing node. */
_quicklistInsertNodeBefore(quicklist * quicklist,quicklistNode * old_node,quicklistNode * new_node)38825e12d10SMatt Stancliff REDIS_STATIC void _quicklistInsertNodeBefore(quicklist *quicklist,
3895e362b84SMatt Stancliff quicklistNode *old_node,
3905e362b84SMatt Stancliff quicklistNode *new_node) {
3915e362b84SMatt Stancliff __quicklistInsertNode(quicklist, old_node, new_node, 0);
3925e362b84SMatt Stancliff }
3935e362b84SMatt Stancliff
_quicklistInsertNodeAfter(quicklist * quicklist,quicklistNode * old_node,quicklistNode * new_node)39425e12d10SMatt Stancliff REDIS_STATIC void _quicklistInsertNodeAfter(quicklist *quicklist,
3955e362b84SMatt Stancliff quicklistNode *old_node,
3965e362b84SMatt Stancliff quicklistNode *new_node) {
3975e362b84SMatt Stancliff __quicklistInsertNode(quicklist, old_node, new_node, 1);
3985e362b84SMatt Stancliff }
3995e362b84SMatt Stancliff
40025e12d10SMatt Stancliff REDIS_STATIC int
_quicklistNodeSizeMeetsOptimizationRequirement(const size_t sz,const int fill)40125e12d10SMatt Stancliff _quicklistNodeSizeMeetsOptimizationRequirement(const size_t sz,
402c6bf20c2SMatt Stancliff const int fill) {
403c6bf20c2SMatt Stancliff if (fill >= 0)
404c6bf20c2SMatt Stancliff return 0;
405c6bf20c2SMatt Stancliff
406c6bf20c2SMatt Stancliff size_t offset = (-fill) - 1;
407c6bf20c2SMatt Stancliff if (offset < (sizeof(optimization_level) / sizeof(*optimization_level))) {
408c6bf20c2SMatt Stancliff if (sz <= optimization_level[offset]) {
409c6bf20c2SMatt Stancliff return 1;
410c6bf20c2SMatt Stancliff } else {
411c6bf20c2SMatt Stancliff return 0;
412c6bf20c2SMatt Stancliff }
413c6bf20c2SMatt Stancliff } else {
414c6bf20c2SMatt Stancliff return 0;
415c6bf20c2SMatt Stancliff }
416c6bf20c2SMatt Stancliff }
417c6bf20c2SMatt Stancliff
418c6bf20c2SMatt Stancliff #define sizeMeetsSafetyLimit(sz) ((sz) <= SIZE_SAFETY_LIMIT)
419c6bf20c2SMatt Stancliff
_quicklistNodeAllowInsert(const quicklistNode * node,const int fill,const size_t sz)42025e12d10SMatt Stancliff REDIS_STATIC int _quicklistNodeAllowInsert(const quicklistNode *node,
42125e12d10SMatt Stancliff const int fill, const size_t sz) {
422bbbbfb14SMatt Stancliff if (unlikely(!node))
423c6bf20c2SMatt Stancliff return 0;
424c6bf20c2SMatt Stancliff
425c6bf20c2SMatt Stancliff int ziplist_overhead;
426c6bf20c2SMatt Stancliff /* size of previous offset */
427c6bf20c2SMatt Stancliff if (sz < 254)
428c6bf20c2SMatt Stancliff ziplist_overhead = 1;
429c6bf20c2SMatt Stancliff else
430c6bf20c2SMatt Stancliff ziplist_overhead = 5;
431c6bf20c2SMatt Stancliff
432c6bf20c2SMatt Stancliff /* size of forward offset */
433c6bf20c2SMatt Stancliff if (sz < 64)
434c6bf20c2SMatt Stancliff ziplist_overhead += 1;
435bbbbfb14SMatt Stancliff else if (likely(sz < 16384))
436c6bf20c2SMatt Stancliff ziplist_overhead += 2;
437c6bf20c2SMatt Stancliff else
438c6bf20c2SMatt Stancliff ziplist_overhead += 5;
439c6bf20c2SMatt Stancliff
440c6bf20c2SMatt Stancliff /* new_sz overestimates if 'sz' encodes to an integer type */
441c6bf20c2SMatt Stancliff unsigned int new_sz = node->sz + sz + ziplist_overhead;
442bbbbfb14SMatt Stancliff if (likely(_quicklistNodeSizeMeetsOptimizationRequirement(new_sz, fill)))
443c6bf20c2SMatt Stancliff return 1;
444c6bf20c2SMatt Stancliff else if (!sizeMeetsSafetyLimit(new_sz))
445c6bf20c2SMatt Stancliff return 0;
446c6bf20c2SMatt Stancliff else if ((int)node->count < fill)
447c6bf20c2SMatt Stancliff return 1;
448c6bf20c2SMatt Stancliff else
449c6bf20c2SMatt Stancliff return 0;
450c6bf20c2SMatt Stancliff }
451c6bf20c2SMatt Stancliff
_quicklistNodeAllowMerge(const quicklistNode * a,const quicklistNode * b,const int fill)45225e12d10SMatt Stancliff REDIS_STATIC int _quicklistNodeAllowMerge(const quicklistNode *a,
45325e12d10SMatt Stancliff const quicklistNode *b,
45425e12d10SMatt Stancliff const int fill) {
455c6bf20c2SMatt Stancliff if (!a || !b)
456c6bf20c2SMatt Stancliff return 0;
457c6bf20c2SMatt Stancliff
458c6bf20c2SMatt Stancliff /* approximate merged ziplist size (- 11 to remove one ziplist
459c6bf20c2SMatt Stancliff * header/trailer) */
460c6bf20c2SMatt Stancliff unsigned int merge_sz = a->sz + b->sz - 11;
461bbbbfb14SMatt Stancliff if (likely(_quicklistNodeSizeMeetsOptimizationRequirement(merge_sz, fill)))
462c6bf20c2SMatt Stancliff return 1;
463c6bf20c2SMatt Stancliff else if (!sizeMeetsSafetyLimit(merge_sz))
464c6bf20c2SMatt Stancliff return 0;
465c6bf20c2SMatt Stancliff else if ((int)(a->count + b->count) <= fill)
466c6bf20c2SMatt Stancliff return 1;
467c6bf20c2SMatt Stancliff else
468c6bf20c2SMatt Stancliff return 0;
469c6bf20c2SMatt Stancliff }
470c6bf20c2SMatt Stancliff
471c6bf20c2SMatt Stancliff #define quicklistNodeUpdateSz(node) \
472c6bf20c2SMatt Stancliff do { \
473c6bf20c2SMatt Stancliff (node)->sz = ziplistBlobLen((node)->zl); \
474c6bf20c2SMatt Stancliff } while (0)
475c6bf20c2SMatt Stancliff
476abdd1414SMatt Stancliff /* Add new entry to head node of quicklist.
4775e362b84SMatt Stancliff *
478abdd1414SMatt Stancliff * Returns 0 if used existing head.
479abdd1414SMatt Stancliff * Returns 1 if new head created. */
quicklistPushHead(quicklist * quicklist,void * value,size_t sz)480abdd1414SMatt Stancliff int quicklistPushHead(quicklist *quicklist, void *value, size_t sz) {
481abdd1414SMatt Stancliff quicklistNode *orig_head = quicklist->head;
482bbbbfb14SMatt Stancliff if (likely(
483bbbbfb14SMatt Stancliff _quicklistNodeAllowInsert(quicklist->head, quicklist->fill, sz))) {
4845e362b84SMatt Stancliff quicklist->head->zl =
4855e362b84SMatt Stancliff ziplistPush(quicklist->head->zl, value, sz, ZIPLIST_HEAD);
486abdd1414SMatt Stancliff quicklistNodeUpdateSz(quicklist->head);
4875e362b84SMatt Stancliff } else {
4885e362b84SMatt Stancliff quicklistNode *node = quicklistCreateNode();
4895e362b84SMatt Stancliff node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_HEAD);
4905e362b84SMatt Stancliff
491abdd1414SMatt Stancliff quicklistNodeUpdateSz(node);
4925e362b84SMatt Stancliff _quicklistInsertNodeBefore(quicklist, quicklist->head, node);
4935e362b84SMatt Stancliff }
4945e362b84SMatt Stancliff quicklist->count++;
4955e362b84SMatt Stancliff quicklist->head->count++;
496abdd1414SMatt Stancliff return (orig_head != quicklist->head);
4975e362b84SMatt Stancliff }
4985e362b84SMatt Stancliff
499abdd1414SMatt Stancliff /* Add new entry to tail node of quicklist.
5005e362b84SMatt Stancliff *
501abdd1414SMatt Stancliff * Returns 0 if used existing tail.
502abdd1414SMatt Stancliff * Returns 1 if new tail created. */
quicklistPushTail(quicklist * quicklist,void * value,size_t sz)503abdd1414SMatt Stancliff int quicklistPushTail(quicklist *quicklist, void *value, size_t sz) {
504abdd1414SMatt Stancliff quicklistNode *orig_tail = quicklist->tail;
505bbbbfb14SMatt Stancliff if (likely(
506bbbbfb14SMatt Stancliff _quicklistNodeAllowInsert(quicklist->tail, quicklist->fill, sz))) {
5075e362b84SMatt Stancliff quicklist->tail->zl =
5085e362b84SMatt Stancliff ziplistPush(quicklist->tail->zl, value, sz, ZIPLIST_TAIL);
509abdd1414SMatt Stancliff quicklistNodeUpdateSz(quicklist->tail);
5105e362b84SMatt Stancliff } else {
5115e362b84SMatt Stancliff quicklistNode *node = quicklistCreateNode();
5125e362b84SMatt Stancliff node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_TAIL);
5135e362b84SMatt Stancliff
514abdd1414SMatt Stancliff quicklistNodeUpdateSz(node);
5155e362b84SMatt Stancliff _quicklistInsertNodeAfter(quicklist, quicklist->tail, node);
5165e362b84SMatt Stancliff }
5175e362b84SMatt Stancliff quicklist->count++;
5185e362b84SMatt Stancliff quicklist->tail->count++;
519abdd1414SMatt Stancliff return (orig_tail != quicklist->tail);
5205e362b84SMatt Stancliff }
5215e362b84SMatt Stancliff
5225e362b84SMatt Stancliff /* Create new node consisting of a pre-formed ziplist.
5235e362b84SMatt Stancliff * Used for loading RDBs where entire ziplists have been stored
5245e362b84SMatt Stancliff * to be retrieved later. */
quicklistAppendZiplist(quicklist * quicklist,unsigned char * zl)5255e362b84SMatt Stancliff void quicklistAppendZiplist(quicklist *quicklist, unsigned char *zl) {
5265e362b84SMatt Stancliff quicklistNode *node = quicklistCreateNode();
5275e362b84SMatt Stancliff
5285e362b84SMatt Stancliff node->zl = zl;
5295e362b84SMatt Stancliff node->count = ziplistLen(node->zl);
530c6bf20c2SMatt Stancliff node->sz = ziplistBlobLen(zl);
5315e362b84SMatt Stancliff
5325e362b84SMatt Stancliff _quicklistInsertNodeAfter(quicklist, quicklist->tail, node);
5335e362b84SMatt Stancliff quicklist->count += node->count;
5345e362b84SMatt Stancliff }
5355e362b84SMatt Stancliff
5365e362b84SMatt Stancliff /* Append all values of ziplist 'zl' individually into 'quicklist'.
5375e362b84SMatt Stancliff *
5385e362b84SMatt Stancliff * This allows us to restore old RDB ziplists into new quicklists
5395e362b84SMatt Stancliff * with smaller ziplist sizes than the saved RDB ziplist.
5405e362b84SMatt Stancliff *
5415e362b84SMatt Stancliff * Returns 'quicklist' argument. Frees passed-in ziplist 'zl' */
quicklistAppendValuesFromZiplist(quicklist * quicklist,unsigned char * zl)5425e362b84SMatt Stancliff quicklist *quicklistAppendValuesFromZiplist(quicklist *quicklist,
543abdd1414SMatt Stancliff unsigned char *zl) {
5445e362b84SMatt Stancliff unsigned char *value;
5455e362b84SMatt Stancliff unsigned int sz;
5465e362b84SMatt Stancliff long long longval;
5475e362b84SMatt Stancliff char longstr[32] = {0};
5485e362b84SMatt Stancliff
5495e362b84SMatt Stancliff unsigned char *p = ziplistIndex(zl, 0);
5505e362b84SMatt Stancliff while (ziplistGet(p, &value, &sz, &longval)) {
5515e362b84SMatt Stancliff if (!value) {
5525e362b84SMatt Stancliff /* Write the longval as a string so we can re-add it */
5535e362b84SMatt Stancliff sz = ll2string(longstr, sizeof(longstr), longval);
5545e362b84SMatt Stancliff value = (unsigned char *)longstr;
5555e362b84SMatt Stancliff }
556abdd1414SMatt Stancliff quicklistPushTail(quicklist, value, sz);
5575e362b84SMatt Stancliff p = ziplistNext(zl, p);
5585e362b84SMatt Stancliff }
5595e362b84SMatt Stancliff zfree(zl);
5605e362b84SMatt Stancliff return quicklist;
5615e362b84SMatt Stancliff }
5625e362b84SMatt Stancliff
5635e362b84SMatt Stancliff /* Create new (potentially multi-node) quicklist from a single existing ziplist.
5645e362b84SMatt Stancliff *
5655e362b84SMatt Stancliff * Returns new quicklist. Frees passed-in ziplist 'zl'. */
quicklistCreateFromZiplist(int fill,int compress,unsigned char * zl)566abdd1414SMatt Stancliff quicklist *quicklistCreateFromZiplist(int fill, int compress,
567abdd1414SMatt Stancliff unsigned char *zl) {
568abdd1414SMatt Stancliff return quicklistAppendValuesFromZiplist(quicklistNew(fill, compress), zl);
5695e362b84SMatt Stancliff }
5705e362b84SMatt Stancliff
5715e362b84SMatt Stancliff #define quicklistDeleteIfEmpty(ql, n) \
5725e362b84SMatt Stancliff do { \
5735e362b84SMatt Stancliff if ((n)->count == 0) { \
5745e362b84SMatt Stancliff __quicklistDelNode((ql), (n)); \
575abdd1414SMatt Stancliff (n) = NULL; \
5765e362b84SMatt Stancliff } \
5775e362b84SMatt Stancliff } while (0)
5785e362b84SMatt Stancliff
__quicklistDelNode(quicklist * quicklist,quicklistNode * node)57925e12d10SMatt Stancliff REDIS_STATIC void __quicklistDelNode(quicklist *quicklist,
58025e12d10SMatt Stancliff quicklistNode *node) {
5815e362b84SMatt Stancliff if (node->next)
5825e362b84SMatt Stancliff node->next->prev = node->prev;
5835e362b84SMatt Stancliff if (node->prev)
5845e362b84SMatt Stancliff node->prev->next = node->next;
5855e362b84SMatt Stancliff
586abdd1414SMatt Stancliff if (node == quicklist->tail) {
5875e362b84SMatt Stancliff quicklist->tail = node->prev;
588abdd1414SMatt Stancliff }
5895e362b84SMatt Stancliff
590abdd1414SMatt Stancliff if (node == quicklist->head) {
5915e362b84SMatt Stancliff quicklist->head = node->next;
592abdd1414SMatt Stancliff }
593abdd1414SMatt Stancliff
594abdd1414SMatt Stancliff /* If we deleted a node within our compress depth, we
595abdd1414SMatt Stancliff * now have compressed nodes needing to be decompressed. */
596abdd1414SMatt Stancliff __quicklistCompress(quicklist, NULL);
5975e362b84SMatt Stancliff
5985e362b84SMatt Stancliff quicklist->count -= node->count;
5995e362b84SMatt Stancliff
6005e362b84SMatt Stancliff zfree(node->zl);
6015e362b84SMatt Stancliff zfree(node);
6025e362b84SMatt Stancliff quicklist->len--;
6035e362b84SMatt Stancliff }
6045e362b84SMatt Stancliff
6055e362b84SMatt Stancliff /* Delete one entry from list given the node for the entry and a pointer
6065e362b84SMatt Stancliff * to the entry in the node.
6075e362b84SMatt Stancliff *
608abdd1414SMatt Stancliff * Note: quicklistDelIndex() *requires* uncompressed nodes because you
609abdd1414SMatt Stancliff * already had to get *p from an uncompressed node somewhere.
610abdd1414SMatt Stancliff *
6115e362b84SMatt Stancliff * Returns 1 if the entire node was deleted, 0 if node still exists.
6125e362b84SMatt Stancliff * Also updates in/out param 'p' with the next offset in the ziplist. */
quicklistDelIndex(quicklist * quicklist,quicklistNode * node,unsigned char ** p)61325e12d10SMatt Stancliff REDIS_STATIC int quicklistDelIndex(quicklist *quicklist, quicklistNode *node,
6145e362b84SMatt Stancliff unsigned char **p) {
6155e362b84SMatt Stancliff int gone = 0;
616abdd1414SMatt Stancliff
6175e362b84SMatt Stancliff node->zl = ziplistDelete(node->zl, p);
6185e362b84SMatt Stancliff node->count--;
619c6bf20c2SMatt Stancliff if (node->count == 0) {
6205e362b84SMatt Stancliff gone = 1;
621abdd1414SMatt Stancliff __quicklistDelNode(quicklist, node);
622c6bf20c2SMatt Stancliff } else {
623c6bf20c2SMatt Stancliff quicklistNodeUpdateSz(node);
624c6bf20c2SMatt Stancliff }
6255e362b84SMatt Stancliff quicklist->count--;
626abdd1414SMatt Stancliff /* If we deleted the node, the original node is no longer valid */
6275e362b84SMatt Stancliff return gone ? 1 : 0;
6285e362b84SMatt Stancliff }
6295e362b84SMatt Stancliff
6305e362b84SMatt Stancliff /* Delete one element represented by 'entry'
6315e362b84SMatt Stancliff *
6325e362b84SMatt Stancliff * 'entry' stores enough metadata to delete the proper position in
6335e362b84SMatt Stancliff * the correct ziplist in the correct quicklist node. */
quicklistDelEntry(quicklistIter * iter,quicklistEntry * entry)6345e362b84SMatt Stancliff void quicklistDelEntry(quicklistIter *iter, quicklistEntry *entry) {
6355e362b84SMatt Stancliff quicklistNode *prev = entry->node->prev;
6365e362b84SMatt Stancliff quicklistNode *next = entry->node->next;
6375e362b84SMatt Stancliff int deleted_node = quicklistDelIndex((quicklist *)entry->quicklist,
6385e362b84SMatt Stancliff entry->node, &entry->zi);
6395e362b84SMatt Stancliff
6405e362b84SMatt Stancliff /* after delete, the zi is now invalid for any future usage. */
6415e362b84SMatt Stancliff iter->zi = NULL;
6425e362b84SMatt Stancliff
6435e362b84SMatt Stancliff /* If current node is deleted, we must update iterator node and offset. */
6445e362b84SMatt Stancliff if (deleted_node) {
6455e362b84SMatt Stancliff if (iter->direction == AL_START_HEAD) {
6465e362b84SMatt Stancliff iter->current = next;
6475e362b84SMatt Stancliff iter->offset = 0;
6485e362b84SMatt Stancliff } else if (iter->direction == AL_START_TAIL) {
6495e362b84SMatt Stancliff iter->current = prev;
6505e362b84SMatt Stancliff iter->offset = -1;
6515e362b84SMatt Stancliff }
6525e362b84SMatt Stancliff }
6535e362b84SMatt Stancliff /* else if (!deleted_node), no changes needed.
6545e362b84SMatt Stancliff * we already reset iter->zi above, and the existing iter->offset
6555e362b84SMatt Stancliff * doesn't move again because:
6565e362b84SMatt Stancliff * - [1, 2, 3] => delete offset 1 => [1, 3]: next element still offset 1
6575e362b84SMatt Stancliff * - [1, 2, 3] => delete offset 0 => [2, 3]: next element still offset 0
6585e362b84SMatt Stancliff * if we deleted the last element at offet N and now
6595e362b84SMatt Stancliff * length of this ziplist is N-1, the next call into
6605e362b84SMatt Stancliff * quicklistNext() will jump to the next node. */
6615e362b84SMatt Stancliff }
6625e362b84SMatt Stancliff
6635e362b84SMatt Stancliff /* Replace quicklist entry at offset 'index' by 'data' with length 'sz'.
6645e362b84SMatt Stancliff *
6655e362b84SMatt Stancliff * Returns 1 if replace happened.
6665e362b84SMatt Stancliff * Returns 0 if replace failed and no changes happened. */
quicklistReplaceAtIndex(quicklist * quicklist,long index,void * data,int sz)6675e362b84SMatt Stancliff int quicklistReplaceAtIndex(quicklist *quicklist, long index, void *data,
6685e362b84SMatt Stancliff int sz) {
6695e362b84SMatt Stancliff quicklistEntry entry;
670bbbbfb14SMatt Stancliff if (likely(quicklistIndex(quicklist, index, &entry))) {
671bbbbfb14SMatt Stancliff /* quicklistIndex provides an uncompressed node */
6725e362b84SMatt Stancliff entry.node->zl = ziplistDelete(entry.node->zl, &entry.zi);
6735e362b84SMatt Stancliff entry.node->zl = ziplistInsert(entry.node->zl, entry.zi, data, sz);
674*70419679Santirez quicklistNodeUpdateSz(entry.node);
675abdd1414SMatt Stancliff quicklistCompress(quicklist, entry.node);
6765e362b84SMatt Stancliff return 1;
6775e362b84SMatt Stancliff } else {
6785e362b84SMatt Stancliff return 0;
6795e362b84SMatt Stancliff }
6805e362b84SMatt Stancliff }
6815e362b84SMatt Stancliff
6825e362b84SMatt Stancliff /* Given two nodes, try to merge their ziplists.
6835e362b84SMatt Stancliff *
6845e362b84SMatt Stancliff * This helps us not have a quicklist with 3 element ziplists if
6855e362b84SMatt Stancliff * our fill factor can handle much higher levels.
6865e362b84SMatt Stancliff *
6875e362b84SMatt Stancliff * Note: 'a' must be to the LEFT of 'b'.
6885e362b84SMatt Stancliff *
6895e362b84SMatt Stancliff * After calling this function, both 'a' and 'b' should be considered
6905e362b84SMatt Stancliff * unusable. The return value from this function must be used
6915e362b84SMatt Stancliff * instead of re-using any of the quicklistNode input arguments.
6925e362b84SMatt Stancliff *
6935e362b84SMatt Stancliff * Returns the input node picked to merge against or NULL if
6945e362b84SMatt Stancliff * merging was not possible. */
_quicklistZiplistMerge(quicklist * quicklist,quicklistNode * a,quicklistNode * b)69525e12d10SMatt Stancliff REDIS_STATIC quicklistNode *_quicklistZiplistMerge(quicklist *quicklist,
6965e362b84SMatt Stancliff quicklistNode *a,
6975e362b84SMatt Stancliff quicklistNode *b) {
6989d2dc024SMatt Stancliff D("Requested merge (a,b) (%u, %u)", a->count, b->count);
6995e362b84SMatt Stancliff
700abdd1414SMatt Stancliff quicklistDecompressNode(a);
701abdd1414SMatt Stancliff quicklistDecompressNode(b);
7029d2dc024SMatt Stancliff if ((ziplistMerge(&a->zl, &b->zl))) {
7039d2dc024SMatt Stancliff /* We merged ziplists! Now remove the unused quicklistNode. */
7049d2dc024SMatt Stancliff quicklistNode *keep = NULL, *nokeep = NULL;
7059d2dc024SMatt Stancliff if (!a->zl) {
7069d2dc024SMatt Stancliff nokeep = a;
7079d2dc024SMatt Stancliff keep = b;
7089d2dc024SMatt Stancliff } else if (!b->zl) {
7099d2dc024SMatt Stancliff nokeep = b;
7109d2dc024SMatt Stancliff keep = a;
7119d2dc024SMatt Stancliff }
7129d2dc024SMatt Stancliff keep->count = ziplistLen(keep->zl);
713c6bf20c2SMatt Stancliff quicklistNodeUpdateSz(keep);
7149d2dc024SMatt Stancliff
7159d2dc024SMatt Stancliff nokeep->count = 0;
7169d2dc024SMatt Stancliff __quicklistDelNode(quicklist, nokeep);
717abdd1414SMatt Stancliff quicklistCompress(quicklist, keep);
7189d2dc024SMatt Stancliff return keep;
7199d2dc024SMatt Stancliff } else {
7209d2dc024SMatt Stancliff /* else, the merge returned NULL and nothing changed. */
7215e362b84SMatt Stancliff return NULL;
7225e362b84SMatt Stancliff }
7235e362b84SMatt Stancliff }
7245e362b84SMatt Stancliff
7255e362b84SMatt Stancliff /* Attempt to merge ziplists within two nodes on either side of 'center'.
7265e362b84SMatt Stancliff *
7275e362b84SMatt Stancliff * We attempt to merge:
7285e362b84SMatt Stancliff * - (center->prev->prev, center->prev)
7295e362b84SMatt Stancliff * - (center->next, center->next->next)
7305e362b84SMatt Stancliff * - (center->prev, center)
7315e362b84SMatt Stancliff * - (center, center->next)
7325e362b84SMatt Stancliff */
_quicklistMergeNodes(quicklist * quicklist,quicklistNode * center)73325e12d10SMatt Stancliff REDIS_STATIC void _quicklistMergeNodes(quicklist *quicklist,
73425e12d10SMatt Stancliff quicklistNode *center) {
735abdd1414SMatt Stancliff int fill = quicklist->fill;
7365e362b84SMatt Stancliff quicklistNode *prev, *prev_prev, *next, *next_next, *target;
7375e362b84SMatt Stancliff prev = prev_prev = next = next_next = target = NULL;
7385e362b84SMatt Stancliff
7395e362b84SMatt Stancliff if (center->prev) {
7405e362b84SMatt Stancliff prev = center->prev;
7415e362b84SMatt Stancliff if (center->prev->prev)
7425e362b84SMatt Stancliff prev_prev = center->prev->prev;
7435e362b84SMatt Stancliff }
7445e362b84SMatt Stancliff
7455e362b84SMatt Stancliff if (center->next) {
7465e362b84SMatt Stancliff next = center->next;
7475e362b84SMatt Stancliff if (center->next->next)
7485e362b84SMatt Stancliff next_next = center->next->next;
7495e362b84SMatt Stancliff }
7505e362b84SMatt Stancliff
7515e362b84SMatt Stancliff /* Try to merge prev_prev and prev */
752c6bf20c2SMatt Stancliff if (_quicklistNodeAllowMerge(prev, prev_prev, fill)) {
7535e362b84SMatt Stancliff _quicklistZiplistMerge(quicklist, prev_prev, prev);
7545e362b84SMatt Stancliff prev_prev = prev = NULL; /* they could have moved, invalidate them. */
7555e362b84SMatt Stancliff }
7565e362b84SMatt Stancliff
7575e362b84SMatt Stancliff /* Try to merge next and next_next */
758c6bf20c2SMatt Stancliff if (_quicklistNodeAllowMerge(next, next_next, fill)) {
7595e362b84SMatt Stancliff _quicklistZiplistMerge(quicklist, next, next_next);
7605e362b84SMatt Stancliff next = next_next = NULL; /* they could have moved, invalidate them. */
7615e362b84SMatt Stancliff }
7625e362b84SMatt Stancliff
7635e362b84SMatt Stancliff /* Try to merge center node and previous node */
764c6bf20c2SMatt Stancliff if (_quicklistNodeAllowMerge(center, center->prev, fill)) {
7655e362b84SMatt Stancliff target = _quicklistZiplistMerge(quicklist, center->prev, center);
7665e362b84SMatt Stancliff center = NULL; /* center could have been deleted, invalidate it. */
7675e362b84SMatt Stancliff } else {
7685e362b84SMatt Stancliff /* else, we didn't merge here, but target needs to be valid below. */
7695e362b84SMatt Stancliff target = center;
7705e362b84SMatt Stancliff }
7715e362b84SMatt Stancliff
7725e362b84SMatt Stancliff /* Use result of center merge (or original) to merge with next node. */
773c6bf20c2SMatt Stancliff if (_quicklistNodeAllowMerge(target, target->next, fill)) {
7745e362b84SMatt Stancliff _quicklistZiplistMerge(quicklist, target, target->next);
7755e362b84SMatt Stancliff }
7765e362b84SMatt Stancliff }
7775e362b84SMatt Stancliff
7785e362b84SMatt Stancliff /* Split 'node' into two parts, parameterized by 'offset' and 'after'.
7795e362b84SMatt Stancliff *
7805e362b84SMatt Stancliff * The 'after' argument controls which quicklistNode gets returned.
7815e362b84SMatt Stancliff * If 'after'==1, returned node has elements after 'offset'.
7825e362b84SMatt Stancliff * input node keeps elements up to 'offset', including 'offset'.
7835e362b84SMatt Stancliff * If 'after'==0, returned node has elements up to 'offset', including 'offset'.
7845e362b84SMatt Stancliff * input node keeps elements after 'offset'.
7855e362b84SMatt Stancliff *
7865e362b84SMatt Stancliff * If 'after'==1, returned node will have elements _after_ 'offset'.
7875e362b84SMatt Stancliff * The returned node will have elements [OFFSET+1, END].
7885e362b84SMatt Stancliff * The input node keeps elements [0, OFFSET].
7895e362b84SMatt Stancliff *
7905e362b84SMatt Stancliff * If 'after'==0, returned node will keep elements up to and including 'offset'.
7915e362b84SMatt Stancliff * The returned node will have elements [0, OFFSET].
7925e362b84SMatt Stancliff * The input node keeps elements [OFFSET+1, END].
7935e362b84SMatt Stancliff *
7945e362b84SMatt Stancliff * The input node keeps all elements not taken by the returned node.
7955e362b84SMatt Stancliff *
7965e362b84SMatt Stancliff * Returns newly created node or NULL if split not possible. */
_quicklistSplitNode(quicklistNode * node,int offset,int after)79725e12d10SMatt Stancliff REDIS_STATIC quicklistNode *_quicklistSplitNode(quicklistNode *node, int offset,
7985e362b84SMatt Stancliff int after) {
799abdd1414SMatt Stancliff size_t zl_sz = node->sz;
8005e362b84SMatt Stancliff
8015e362b84SMatt Stancliff quicklistNode *new_node = quicklistCreateNode();
8025e362b84SMatt Stancliff new_node->zl = zmalloc(zl_sz);
8035e362b84SMatt Stancliff
8045e362b84SMatt Stancliff /* Copy original ziplist so we can split it */
8055e362b84SMatt Stancliff memcpy(new_node->zl, node->zl, zl_sz);
8065e362b84SMatt Stancliff
8075e362b84SMatt Stancliff /* -1 here means "continue deleting until the list ends" */
8085e362b84SMatt Stancliff int orig_start = after ? offset + 1 : 0;
8095e362b84SMatt Stancliff int orig_extent = after ? -1 : offset;
8105e362b84SMatt Stancliff int new_start = after ? 0 : offset;
8115e362b84SMatt Stancliff int new_extent = after ? offset + 1 : -1;
8125e362b84SMatt Stancliff
8135e362b84SMatt Stancliff D("After %d (%d); ranges: [%d, %d], [%d, %d]", after, offset, orig_start,
8145e362b84SMatt Stancliff orig_extent, new_start, new_extent);
8155e362b84SMatt Stancliff
8165e362b84SMatt Stancliff node->zl = ziplistDeleteRange(node->zl, orig_start, orig_extent);
8175e362b84SMatt Stancliff node->count = ziplistLen(node->zl);
818c6bf20c2SMatt Stancliff quicklistNodeUpdateSz(node);
8195e362b84SMatt Stancliff
8205e362b84SMatt Stancliff new_node->zl = ziplistDeleteRange(new_node->zl, new_start, new_extent);
8215e362b84SMatt Stancliff new_node->count = ziplistLen(new_node->zl);
822c6bf20c2SMatt Stancliff quicklistNodeUpdateSz(new_node);
8235e362b84SMatt Stancliff
8245e362b84SMatt Stancliff D("After split lengths: orig (%d), new (%d)", node->count, new_node->count);
8255e362b84SMatt Stancliff return new_node;
8265e362b84SMatt Stancliff }
8275e362b84SMatt Stancliff
8285e362b84SMatt Stancliff /* Insert a new entry before or after existing entry 'entry'.
8295e362b84SMatt Stancliff *
8305e362b84SMatt Stancliff * If after==1, the new value is inserted after 'entry', otherwise
8315e362b84SMatt Stancliff * the new value is inserted before 'entry'. */
_quicklistInsert(quicklist * quicklist,quicklistEntry * entry,void * value,const size_t sz,int after)83225e12d10SMatt Stancliff REDIS_STATIC void _quicklistInsert(quicklist *quicklist, quicklistEntry *entry,
833abdd1414SMatt Stancliff void *value, const size_t sz, int after) {
8345e362b84SMatt Stancliff int full = 0, at_tail = 0, at_head = 0, full_next = 0, full_prev = 0;
835abdd1414SMatt Stancliff int fill = quicklist->fill;
8365e362b84SMatt Stancliff quicklistNode *node = entry->node;
8375e362b84SMatt Stancliff quicklistNode *new_node = NULL;
8385e362b84SMatt Stancliff
8395e362b84SMatt Stancliff if (!node) {
8405e362b84SMatt Stancliff /* we have no reference node, so let's create only node in the list */
8415e362b84SMatt Stancliff D("No node given!");
8425e362b84SMatt Stancliff new_node = quicklistCreateNode();
8435e362b84SMatt Stancliff new_node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_HEAD);
8445e362b84SMatt Stancliff __quicklistInsertNode(quicklist, NULL, new_node, after);
8455e362b84SMatt Stancliff new_node->count++;
8465e362b84SMatt Stancliff quicklist->count++;
8475e362b84SMatt Stancliff return;
8485e362b84SMatt Stancliff }
8495e362b84SMatt Stancliff
8505e362b84SMatt Stancliff /* Populate accounting flags for easier boolean checks later */
851c6bf20c2SMatt Stancliff if (!_quicklistNodeAllowInsert(node, fill, sz)) {
8525e362b84SMatt Stancliff D("Current node is full with count %d with requested fill %lu",
8535e362b84SMatt Stancliff node->count, fill);
8545e362b84SMatt Stancliff full = 1;
8555e362b84SMatt Stancliff }
8565e362b84SMatt Stancliff
857abdd1414SMatt Stancliff if (after && (entry->offset == node->count)) {
8585e362b84SMatt Stancliff D("At Tail of current ziplist");
8595e362b84SMatt Stancliff at_tail = 1;
860c6bf20c2SMatt Stancliff if (!_quicklistNodeAllowInsert(node->next, fill, sz)) {
8615e362b84SMatt Stancliff D("Next node is full too.");
8625e362b84SMatt Stancliff full_next = 1;
8635e362b84SMatt Stancliff }
8645e362b84SMatt Stancliff }
8655e362b84SMatt Stancliff
866abdd1414SMatt Stancliff if (!after && (entry->offset == 0)) {
8675e362b84SMatt Stancliff D("At Head");
8685e362b84SMatt Stancliff at_head = 1;
869c6bf20c2SMatt Stancliff if (!_quicklistNodeAllowInsert(node->prev, fill, sz)) {
8705e362b84SMatt Stancliff D("Prev node is full too.");
8715e362b84SMatt Stancliff full_prev = 1;
8725e362b84SMatt Stancliff }
8735e362b84SMatt Stancliff }
8745e362b84SMatt Stancliff
8755e362b84SMatt Stancliff /* Now determine where and how to insert the new element */
8765e362b84SMatt Stancliff if (!full && after) {
8775e362b84SMatt Stancliff D("Not full, inserting after current position.");
878abdd1414SMatt Stancliff quicklistDecompressNodeForUse(node);
8795e362b84SMatt Stancliff unsigned char *next = ziplistNext(node->zl, entry->zi);
8805e362b84SMatt Stancliff if (next == NULL) {
8815e362b84SMatt Stancliff node->zl = ziplistPush(node->zl, value, sz, ZIPLIST_TAIL);
8825e362b84SMatt Stancliff } else {
8835e362b84SMatt Stancliff node->zl = ziplistInsert(node->zl, next, value, sz);
8845e362b84SMatt Stancliff }
8855e362b84SMatt Stancliff node->count++;
886c6bf20c2SMatt Stancliff quicklistNodeUpdateSz(node);
887abdd1414SMatt Stancliff quicklistRecompressOnly(quicklist, node);
8885e362b84SMatt Stancliff } else if (!full && !after) {
8895e362b84SMatt Stancliff D("Not full, inserting before current position.");
890abdd1414SMatt Stancliff quicklistDecompressNodeForUse(node);
8915e362b84SMatt Stancliff node->zl = ziplistInsert(node->zl, entry->zi, value, sz);
8925e362b84SMatt Stancliff node->count++;
893c6bf20c2SMatt Stancliff quicklistNodeUpdateSz(node);
894abdd1414SMatt Stancliff quicklistRecompressOnly(quicklist, node);
8955e362b84SMatt Stancliff } else if (full && at_tail && node->next && !full_next && after) {
8965e362b84SMatt Stancliff /* If we are: at tail, next has free space, and inserting after:
8975e362b84SMatt Stancliff * - insert entry at head of next node. */
8985e362b84SMatt Stancliff D("Full and tail, but next isn't full; inserting next node head");
8995e362b84SMatt Stancliff new_node = node->next;
900abdd1414SMatt Stancliff quicklistDecompressNodeForUse(new_node);
9015e362b84SMatt Stancliff new_node->zl = ziplistPush(new_node->zl, value, sz, ZIPLIST_HEAD);
9025e362b84SMatt Stancliff new_node->count++;
903c6bf20c2SMatt Stancliff quicklistNodeUpdateSz(new_node);
904abdd1414SMatt Stancliff quicklistRecompressOnly(quicklist, new_node);
9055e362b84SMatt Stancliff } else if (full && at_head && node->prev && !full_prev && !after) {
9065e362b84SMatt Stancliff /* If we are: at head, previous has free space, and inserting before:
9075e362b84SMatt Stancliff * - insert entry at tail of previous node. */
9085e362b84SMatt Stancliff D("Full and head, but prev isn't full, inserting prev node tail");
9095e362b84SMatt Stancliff new_node = node->prev;
910abdd1414SMatt Stancliff quicklistDecompressNodeForUse(new_node);
9115e362b84SMatt Stancliff new_node->zl = ziplistPush(new_node->zl, value, sz, ZIPLIST_TAIL);
9125e362b84SMatt Stancliff new_node->count++;
913c6bf20c2SMatt Stancliff quicklistNodeUpdateSz(new_node);
914abdd1414SMatt Stancliff quicklistRecompressOnly(quicklist, new_node);
9155e362b84SMatt Stancliff } else if (full && ((at_tail && node->next && full_next && after) ||
9165e362b84SMatt Stancliff (at_head && node->prev && full_prev && !after))) {
9175e362b84SMatt Stancliff /* If we are: full, and our prev/next is full, then:
9185e362b84SMatt Stancliff * - create new node and attach to quicklist */
9195e362b84SMatt Stancliff D("\tprovisioning new node...");
9205e362b84SMatt Stancliff new_node = quicklistCreateNode();
9215e362b84SMatt Stancliff new_node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_HEAD);
9225e362b84SMatt Stancliff new_node->count++;
923c6bf20c2SMatt Stancliff quicklistNodeUpdateSz(new_node);
9245e362b84SMatt Stancliff __quicklistInsertNode(quicklist, node, new_node, after);
9255e362b84SMatt Stancliff } else if (full) {
9265e362b84SMatt Stancliff /* else, node is full we need to split it. */
9275e362b84SMatt Stancliff /* covers both after and !after cases */
9285e362b84SMatt Stancliff D("\tsplitting node...");
929abdd1414SMatt Stancliff quicklistDecompressNodeForUse(node);
9305e362b84SMatt Stancliff new_node = _quicklistSplitNode(node, entry->offset, after);
9315e362b84SMatt Stancliff new_node->zl = ziplistPush(new_node->zl, value, sz,
9325e362b84SMatt Stancliff after ? ZIPLIST_HEAD : ZIPLIST_TAIL);
9335e362b84SMatt Stancliff new_node->count++;
934c6bf20c2SMatt Stancliff quicklistNodeUpdateSz(new_node);
9355e362b84SMatt Stancliff __quicklistInsertNode(quicklist, node, new_node, after);
936abdd1414SMatt Stancliff _quicklistMergeNodes(quicklist, node);
9375e362b84SMatt Stancliff }
9385e362b84SMatt Stancliff
9395e362b84SMatt Stancliff quicklist->count++;
9405e362b84SMatt Stancliff }
9415e362b84SMatt Stancliff
quicklistInsertBefore(quicklist * quicklist,quicklistEntry * entry,void * value,const size_t sz)942abdd1414SMatt Stancliff void quicklistInsertBefore(quicklist *quicklist, quicklistEntry *entry,
943abdd1414SMatt Stancliff void *value, const size_t sz) {
944abdd1414SMatt Stancliff _quicklistInsert(quicklist, entry, value, sz, 0);
9455e362b84SMatt Stancliff }
9465e362b84SMatt Stancliff
quicklistInsertAfter(quicklist * quicklist,quicklistEntry * entry,void * value,const size_t sz)947abdd1414SMatt Stancliff void quicklistInsertAfter(quicklist *quicklist, quicklistEntry *entry,
948abdd1414SMatt Stancliff void *value, const size_t sz) {
949abdd1414SMatt Stancliff _quicklistInsert(quicklist, entry, value, sz, 1);
9505e362b84SMatt Stancliff }
9515e362b84SMatt Stancliff
9525e362b84SMatt Stancliff /* Delete a range of elements from the quicklist.
9535e362b84SMatt Stancliff *
9545e362b84SMatt Stancliff * elements may span across multiple quicklistNodes, so we
9555e362b84SMatt Stancliff * have to be careful about tracking where we start and end.
9565e362b84SMatt Stancliff *
9575e362b84SMatt Stancliff * Returns 1 if entries were deleted, 0 if nothing was deleted. */
quicklistDelRange(quicklist * quicklist,const long start,const long count)9585e362b84SMatt Stancliff int quicklistDelRange(quicklist *quicklist, const long start,
9595e362b84SMatt Stancliff const long count) {
9605e362b84SMatt Stancliff if (count <= 0)
9615e362b84SMatt Stancliff return 0;
9625e362b84SMatt Stancliff
9635e362b84SMatt Stancliff unsigned long extent = count; /* range is inclusive of start position */
9645e362b84SMatt Stancliff
9655e362b84SMatt Stancliff if (start >= 0 && extent > (quicklist->count - start)) {
9665e362b84SMatt Stancliff /* if requesting delete more elements than exist, limit to list size. */
9675e362b84SMatt Stancliff extent = quicklist->count - start;
9685e362b84SMatt Stancliff } else if (start < 0 && extent > (unsigned long)(-start)) {
9695e362b84SMatt Stancliff /* else, if at negative offset, limit max size to rest of list. */
9705e362b84SMatt Stancliff extent = -start; /* c.f. LREM -29 29; just delete until end. */
9715e362b84SMatt Stancliff }
9725e362b84SMatt Stancliff
9735e362b84SMatt Stancliff quicklistEntry entry;
9745e362b84SMatt Stancliff if (!quicklistIndex(quicklist, start, &entry))
9755e362b84SMatt Stancliff return 0;
9765e362b84SMatt Stancliff
9775e362b84SMatt Stancliff D("Quicklist delete request for start %ld, count %ld, extent: %ld", start,
9785e362b84SMatt Stancliff count, extent);
9795e362b84SMatt Stancliff quicklistNode *node = entry.node;
9805e362b84SMatt Stancliff
9815e362b84SMatt Stancliff /* iterate over next nodes until everything is deleted. */
9825e362b84SMatt Stancliff while (extent) {
9835e362b84SMatt Stancliff quicklistNode *next = node->next;
9845e362b84SMatt Stancliff
9855e362b84SMatt Stancliff unsigned long del;
9865e362b84SMatt Stancliff int delete_entire_node = 0;
9875e362b84SMatt Stancliff if (entry.offset == 0 && extent >= node->count) {
9885e362b84SMatt Stancliff /* If we are deleting more than the count of this node, we
9895e362b84SMatt Stancliff * can just delete the entire node without ziplist math. */
9905e362b84SMatt Stancliff delete_entire_node = 1;
9915e362b84SMatt Stancliff del = node->count;
9925e362b84SMatt Stancliff } else if (entry.offset >= 0 && extent >= node->count) {
9935e362b84SMatt Stancliff /* If deleting more nodes after this one, calculate delete based
9945e362b84SMatt Stancliff * on size of current node. */
9955e362b84SMatt Stancliff del = node->count - entry.offset;
9965e362b84SMatt Stancliff } else if (entry.offset < 0) {
9975e362b84SMatt Stancliff /* If offset is negative, we are in the first run of this loop
9985e362b84SMatt Stancliff * and we are deleting the entire range
9995e362b84SMatt Stancliff * from this start offset to end of list. Since the Negative
10005e362b84SMatt Stancliff * offset is the number of elements until the tail of the list,
10015e362b84SMatt Stancliff * just use it directly as the deletion count. */
10025e362b84SMatt Stancliff del = -entry.offset;
10035e362b84SMatt Stancliff
10045e362b84SMatt Stancliff /* If the positive offset is greater than the remaining extent,
10055e362b84SMatt Stancliff * we only delete the remaining extent, not the entire offset.
10065e362b84SMatt Stancliff */
10075e362b84SMatt Stancliff if (del > extent)
10085e362b84SMatt Stancliff del = extent;
10095e362b84SMatt Stancliff } else {
10105e362b84SMatt Stancliff /* else, we are deleting less than the extent of this node, so
10115e362b84SMatt Stancliff * use extent directly. */
10125e362b84SMatt Stancliff del = extent;
10135e362b84SMatt Stancliff }
10145e362b84SMatt Stancliff
10155e362b84SMatt Stancliff D("[%ld]: asking to del: %ld because offset: %d; (ENTIRE NODE: %d), "
10165e362b84SMatt Stancliff "node count: %u",
10175e362b84SMatt Stancliff extent, del, entry.offset, delete_entire_node, node->count);
10185e362b84SMatt Stancliff
10195e362b84SMatt Stancliff if (delete_entire_node) {
10205e362b84SMatt Stancliff __quicklistDelNode(quicklist, node);
10215e362b84SMatt Stancliff } else {
1022abdd1414SMatt Stancliff quicklistDecompressNodeForUse(node);
10235e362b84SMatt Stancliff node->zl = ziplistDeleteRange(node->zl, entry.offset, del);
1024c6bf20c2SMatt Stancliff quicklistNodeUpdateSz(node);
1025abdd1414SMatt Stancliff node->count -= del;
10265e362b84SMatt Stancliff quicklist->count -= del;
10275e362b84SMatt Stancliff quicklistDeleteIfEmpty(quicklist, node);
1028abdd1414SMatt Stancliff if (node)
1029abdd1414SMatt Stancliff quicklistRecompressOnly(quicklist, node);
10305e362b84SMatt Stancliff }
10315e362b84SMatt Stancliff
10325e362b84SMatt Stancliff extent -= del;
10335e362b84SMatt Stancliff
10345e362b84SMatt Stancliff node = next;
10355e362b84SMatt Stancliff
10365e362b84SMatt Stancliff entry.offset = 0;
10375e362b84SMatt Stancliff }
10385e362b84SMatt Stancliff return 1;
10395e362b84SMatt Stancliff }
10405e362b84SMatt Stancliff
10415e362b84SMatt Stancliff /* Passthrough to ziplistCompare() */
quicklistCompare(unsigned char * p1,unsigned char * p2,int p2_len)10425e362b84SMatt Stancliff int quicklistCompare(unsigned char *p1, unsigned char *p2, int p2_len) {
10435e362b84SMatt Stancliff return ziplistCompare(p1, p2, p2_len);
10445e362b84SMatt Stancliff }
10455e362b84SMatt Stancliff
10465e362b84SMatt Stancliff /* Returns a quicklist iterator 'iter'. After the initialization every
10475e362b84SMatt Stancliff * call to quicklistNext() will return the next element of the quicklist. */
quicklistGetIterator(const quicklist * quicklist,int direction)10485e362b84SMatt Stancliff quicklistIter *quicklistGetIterator(const quicklist *quicklist, int direction) {
10495e362b84SMatt Stancliff quicklistIter *iter;
10505e362b84SMatt Stancliff
10518d702189SMatt Stancliff iter = zmalloc(sizeof(*iter));
10525e362b84SMatt Stancliff
10535e362b84SMatt Stancliff if (direction == AL_START_HEAD) {
10545e362b84SMatt Stancliff iter->current = quicklist->head;
10555e362b84SMatt Stancliff iter->offset = 0;
10565e362b84SMatt Stancliff } else if (direction == AL_START_TAIL) {
10575e362b84SMatt Stancliff iter->current = quicklist->tail;
10585e362b84SMatt Stancliff iter->offset = -1;
10595e362b84SMatt Stancliff }
10605e362b84SMatt Stancliff
10615e362b84SMatt Stancliff iter->direction = direction;
10625e362b84SMatt Stancliff iter->quicklist = quicklist;
10635e362b84SMatt Stancliff
10645e362b84SMatt Stancliff iter->zi = NULL;
10655e362b84SMatt Stancliff
10665e362b84SMatt Stancliff return iter;
10675e362b84SMatt Stancliff }
10685e362b84SMatt Stancliff
10695e362b84SMatt Stancliff /* Initialize an iterator at a specific offset 'idx' and make the iterator
10705e362b84SMatt Stancliff * return nodes in 'direction' direction. */
quicklistGetIteratorAtIdx(const quicklist * quicklist,const int direction,const long long idx)10715e362b84SMatt Stancliff quicklistIter *quicklistGetIteratorAtIdx(const quicklist *quicklist,
10725e362b84SMatt Stancliff const int direction,
10735e362b84SMatt Stancliff const long long idx) {
10745e362b84SMatt Stancliff quicklistEntry entry;
10755e362b84SMatt Stancliff
10765e362b84SMatt Stancliff if (quicklistIndex(quicklist, idx, &entry)) {
10775e362b84SMatt Stancliff quicklistIter *base = quicklistGetIterator(quicklist, direction);
10785e362b84SMatt Stancliff base->zi = NULL;
10795e362b84SMatt Stancliff base->current = entry.node;
10805e362b84SMatt Stancliff base->offset = entry.offset;
10815e362b84SMatt Stancliff return base;
10825e362b84SMatt Stancliff } else {
10835e362b84SMatt Stancliff return NULL;
10845e362b84SMatt Stancliff }
10855e362b84SMatt Stancliff }
10865e362b84SMatt Stancliff
1087abdd1414SMatt Stancliff /* Release iterator.
1088abdd1414SMatt Stancliff * If we still have a valid current node, then re-encode current node. */
quicklistReleaseIterator(quicklistIter * iter)1089abdd1414SMatt Stancliff void quicklistReleaseIterator(quicklistIter *iter) {
1090abdd1414SMatt Stancliff if (iter->current)
1091abdd1414SMatt Stancliff quicklistCompress(iter->quicklist, iter->current);
1092abdd1414SMatt Stancliff
1093abdd1414SMatt Stancliff zfree(iter);
1094abdd1414SMatt Stancliff }
10955e362b84SMatt Stancliff
10965e362b84SMatt Stancliff /* Get next element in iterator.
10975e362b84SMatt Stancliff *
10985e362b84SMatt Stancliff * Note: You must NOT insert into the list while iterating over it.
10995e362b84SMatt Stancliff * You *may* delete from the list while iterating using the
11005e362b84SMatt Stancliff * quicklistDelEntry() function.
11015e362b84SMatt Stancliff * If you insert into the quicklist while iterating, you should
11025e362b84SMatt Stancliff * re-create the iterator after your addition.
11035e362b84SMatt Stancliff *
11045e362b84SMatt Stancliff * iter = quicklistGetIterator(quicklist,<direction>);
11055e362b84SMatt Stancliff * quicklistEntry entry;
11065e362b84SMatt Stancliff * while (quicklistNext(iter, &entry)) {
11075e362b84SMatt Stancliff * if (entry.value)
11085e362b84SMatt Stancliff * [[ use entry.value with entry.sz ]]
11095e362b84SMatt Stancliff * else
11105e362b84SMatt Stancliff * [[ use entry.longval ]]
11115e362b84SMatt Stancliff * }
11125e362b84SMatt Stancliff *
11135e362b84SMatt Stancliff * Populates 'entry' with values for this iteration.
11145e362b84SMatt Stancliff * Returns 0 when iteration is complete or if iteration not possible.
11155e362b84SMatt Stancliff * If return value is 0, the contents of 'entry' are not valid.
11165e362b84SMatt Stancliff */
quicklistNext(quicklistIter * iter,quicklistEntry * entry)11175e362b84SMatt Stancliff int quicklistNext(quicklistIter *iter, quicklistEntry *entry) {
11185e362b84SMatt Stancliff initEntry(entry);
11195e362b84SMatt Stancliff
11205e362b84SMatt Stancliff if (!iter) {
11215e362b84SMatt Stancliff D("Returning because no iter!");
11225e362b84SMatt Stancliff return 0;
11235e362b84SMatt Stancliff }
11245e362b84SMatt Stancliff
11255e362b84SMatt Stancliff entry->quicklist = iter->quicklist;
11265e362b84SMatt Stancliff entry->node = iter->current;
11275e362b84SMatt Stancliff
11285e362b84SMatt Stancliff if (!iter->current) {
11295e362b84SMatt Stancliff D("Returning because current node is NULL")
11305e362b84SMatt Stancliff return 0;
11315e362b84SMatt Stancliff }
11325e362b84SMatt Stancliff
11335e362b84SMatt Stancliff unsigned char *(*nextFn)(unsigned char *, unsigned char *) = NULL;
11345e362b84SMatt Stancliff int offset_update = 0;
11355e362b84SMatt Stancliff
11365e362b84SMatt Stancliff if (!iter->zi) {
11375e362b84SMatt Stancliff /* If !zi, use current index. */
1138abdd1414SMatt Stancliff quicklistDecompressNodeForUse(iter->current);
11395e362b84SMatt Stancliff iter->zi = ziplistIndex(iter->current->zl, iter->offset);
11405e362b84SMatt Stancliff } else {
11415e362b84SMatt Stancliff /* else, use existing iterator offset and get prev/next as necessary. */
11425e362b84SMatt Stancliff if (iter->direction == AL_START_HEAD) {
11435e362b84SMatt Stancliff nextFn = ziplistNext;
11445e362b84SMatt Stancliff offset_update = 1;
11455e362b84SMatt Stancliff } else if (iter->direction == AL_START_TAIL) {
11465e362b84SMatt Stancliff nextFn = ziplistPrev;
11475e362b84SMatt Stancliff offset_update = -1;
11485e362b84SMatt Stancliff }
11495e362b84SMatt Stancliff iter->zi = nextFn(iter->current->zl, iter->zi);
11505e362b84SMatt Stancliff iter->offset += offset_update;
11515e362b84SMatt Stancliff }
11525e362b84SMatt Stancliff
11535e362b84SMatt Stancliff entry->zi = iter->zi;
11545e362b84SMatt Stancliff entry->offset = iter->offset;
11555e362b84SMatt Stancliff
11565e362b84SMatt Stancliff if (iter->zi) {
11575e362b84SMatt Stancliff /* Populate value from existing ziplist position */
11585e362b84SMatt Stancliff ziplistGet(entry->zi, &entry->value, &entry->sz, &entry->longval);
11595e362b84SMatt Stancliff return 1;
11605e362b84SMatt Stancliff } else {
11615e362b84SMatt Stancliff /* We ran out of ziplist entries.
11625e362b84SMatt Stancliff * Pick next node, update offset, then re-run retrieval. */
1163abdd1414SMatt Stancliff quicklistCompress(iter->quicklist, iter->current);
11645e362b84SMatt Stancliff if (iter->direction == AL_START_HEAD) {
11655e362b84SMatt Stancliff /* Forward traversal */
11665e362b84SMatt Stancliff D("Jumping to start of next node");
11675e362b84SMatt Stancliff iter->current = iter->current->next;
11685e362b84SMatt Stancliff iter->offset = 0;
11695e362b84SMatt Stancliff } else if (iter->direction == AL_START_TAIL) {
11705e362b84SMatt Stancliff /* Reverse traversal */
11715e362b84SMatt Stancliff D("Jumping to end of previous node");
11725e362b84SMatt Stancliff iter->current = iter->current->prev;
11735e362b84SMatt Stancliff iter->offset = -1;
11745e362b84SMatt Stancliff }
11755e362b84SMatt Stancliff iter->zi = NULL;
11765e362b84SMatt Stancliff return quicklistNext(iter, entry);
11775e362b84SMatt Stancliff }
11785e362b84SMatt Stancliff }
11795e362b84SMatt Stancliff
11808d702189SMatt Stancliff /* Duplicate the quicklist.
11815e362b84SMatt Stancliff * On success a copy of the original quicklist is returned.
11825e362b84SMatt Stancliff *
11835e362b84SMatt Stancliff * The original quicklist both on success or error is never modified.
11845e362b84SMatt Stancliff *
11855e362b84SMatt Stancliff * Returns newly allocated quicklist. */
quicklistDup(quicklist * orig)11865e362b84SMatt Stancliff quicklist *quicklistDup(quicklist *orig) {
11875e362b84SMatt Stancliff quicklist *copy;
11885e362b84SMatt Stancliff
1189abdd1414SMatt Stancliff copy = quicklistNew(orig->fill, orig->compress);
11905e362b84SMatt Stancliff
11915e362b84SMatt Stancliff for (quicklistNode *current = orig->head; current;
11925e362b84SMatt Stancliff current = current->next) {
11935e362b84SMatt Stancliff quicklistNode *node = quicklistCreateNode();
11945e362b84SMatt Stancliff
1195abdd1414SMatt Stancliff if (node->encoding == QUICKLIST_NODE_ENCODING_LZF) {
1196abdd1414SMatt Stancliff quicklistLZF *lzf = (quicklistLZF *)node->zl;
1197abdd1414SMatt Stancliff size_t lzf_sz = sizeof(*lzf) + lzf->sz;
1198abdd1414SMatt Stancliff node->zl = zmalloc(lzf_sz);
1199abdd1414SMatt Stancliff memcpy(node->zl, current->zl, lzf_sz);
1200abdd1414SMatt Stancliff } else if (node->encoding == QUICKLIST_NODE_ENCODING_RAW) {
1201abdd1414SMatt Stancliff node->zl = zmalloc(current->sz);
1202abdd1414SMatt Stancliff memcpy(node->zl, current->zl, current->sz);
1203abdd1414SMatt Stancliff }
12045e362b84SMatt Stancliff
12055e362b84SMatt Stancliff node->count = current->count;
12065e362b84SMatt Stancliff copy->count += node->count;
1207c6bf20c2SMatt Stancliff node->sz = current->sz;
1208abdd1414SMatt Stancliff node->encoding = current->encoding;
12095e362b84SMatt Stancliff
12105e362b84SMatt Stancliff _quicklistInsertNodeAfter(copy, copy->tail, node);
12115e362b84SMatt Stancliff }
12125e362b84SMatt Stancliff
12135e362b84SMatt Stancliff /* copy->count must equal orig->count here */
12145e362b84SMatt Stancliff return copy;
12155e362b84SMatt Stancliff }
12165e362b84SMatt Stancliff
12175e362b84SMatt Stancliff /* Populate 'entry' with the element at the specified zero-based index
12185e362b84SMatt Stancliff * where 0 is the head, 1 is the element next to head
12195e362b84SMatt Stancliff * and so on. Negative integers are used in order to count
12205e362b84SMatt Stancliff * from the tail, -1 is the last element, -2 the penultimate
12215e362b84SMatt Stancliff * and so on. If the index is out of range 0 is returned.
12225e362b84SMatt Stancliff *
12235e362b84SMatt Stancliff * Returns 1 if element found
12245e362b84SMatt Stancliff * Returns 0 if element not found */
quicklistIndex(const quicklist * quicklist,const long long idx,quicklistEntry * entry)12255e362b84SMatt Stancliff int quicklistIndex(const quicklist *quicklist, const long long idx,
12265e362b84SMatt Stancliff quicklistEntry *entry) {
12275e362b84SMatt Stancliff quicklistNode *n;
12285e362b84SMatt Stancliff unsigned long long accum = 0;
12295e362b84SMatt Stancliff unsigned long long index;
12305e362b84SMatt Stancliff int forward = idx < 0 ? 0 : 1; /* < 0 -> reverse, 0+ -> forward */
12315e362b84SMatt Stancliff
12325e362b84SMatt Stancliff initEntry(entry);
12335e362b84SMatt Stancliff entry->quicklist = quicklist;
12345e362b84SMatt Stancliff
12355e362b84SMatt Stancliff if (!forward) {
12365e362b84SMatt Stancliff index = (-idx) - 1;
12375e362b84SMatt Stancliff n = quicklist->tail;
12385e362b84SMatt Stancliff } else {
12395e362b84SMatt Stancliff index = idx;
12405e362b84SMatt Stancliff n = quicklist->head;
12415e362b84SMatt Stancliff }
12425e362b84SMatt Stancliff
12435e362b84SMatt Stancliff if (index >= quicklist->count)
12445e362b84SMatt Stancliff return 0;
12455e362b84SMatt Stancliff
1246bbbbfb14SMatt Stancliff while (likely(n)) {
12475e362b84SMatt Stancliff if ((accum + n->count) > index) {
12485e362b84SMatt Stancliff break;
12495e362b84SMatt Stancliff } else {
12505e362b84SMatt Stancliff D("Skipping over (%p) %u at accum %lld", (void *)n, n->count,
12515e362b84SMatt Stancliff accum);
12525e362b84SMatt Stancliff accum += n->count;
12535e362b84SMatt Stancliff n = forward ? n->next : n->prev;
12545e362b84SMatt Stancliff }
12555e362b84SMatt Stancliff }
12565e362b84SMatt Stancliff
12575e362b84SMatt Stancliff if (!n)
12585e362b84SMatt Stancliff return 0;
12595e362b84SMatt Stancliff
12605e362b84SMatt Stancliff D("Found node: %p at accum %llu, idx %llu, sub+ %llu, sub- %llu", (void *)n,
12615e362b84SMatt Stancliff accum, index, index - accum, (-index) - 1 + accum);
12625e362b84SMatt Stancliff
12635e362b84SMatt Stancliff entry->node = n;
12645e362b84SMatt Stancliff if (forward) {
12655e362b84SMatt Stancliff /* forward = normal head-to-tail offset. */
12665e362b84SMatt Stancliff entry->offset = index - accum;
12675e362b84SMatt Stancliff } else {
12685e362b84SMatt Stancliff /* reverse = need negative offset for tail-to-head, so undo
12695e362b84SMatt Stancliff * the result of the original if (index < 0) above. */
12705e362b84SMatt Stancliff entry->offset = (-index) - 1 + accum;
12715e362b84SMatt Stancliff }
12725e362b84SMatt Stancliff
1273abdd1414SMatt Stancliff quicklistDecompressNodeForUse(entry->node);
12745e362b84SMatt Stancliff entry->zi = ziplistIndex(entry->node->zl, entry->offset);
12755e362b84SMatt Stancliff ziplistGet(entry->zi, &entry->value, &entry->sz, &entry->longval);
1276bbbbfb14SMatt Stancliff /* The caller will use our result, so we don't re-compress here.
1277bbbbfb14SMatt Stancliff * The caller can recompress or delete the node as needed. */
12785e362b84SMatt Stancliff return 1;
12795e362b84SMatt Stancliff }
12805e362b84SMatt Stancliff
12815e362b84SMatt Stancliff /* Rotate quicklist by moving the tail element to the head. */
quicklistRotate(quicklist * quicklist)1282abdd1414SMatt Stancliff void quicklistRotate(quicklist *quicklist) {
12835e362b84SMatt Stancliff if (quicklist->count <= 1)
12845e362b84SMatt Stancliff return;
12855e362b84SMatt Stancliff
12865e362b84SMatt Stancliff /* First, get the tail entry */
1287abdd1414SMatt Stancliff unsigned char *p = ziplistIndex(quicklist->tail->zl, -1);
12885e362b84SMatt Stancliff unsigned char *value;
12895e362b84SMatt Stancliff long long longval;
12905e362b84SMatt Stancliff unsigned int sz;
12915e362b84SMatt Stancliff char longstr[32] = {0};
12925e362b84SMatt Stancliff ziplistGet(p, &value, &sz, &longval);
12935e362b84SMatt Stancliff
12945e362b84SMatt Stancliff /* If value found is NULL, then ziplistGet populated longval instead */
12955e362b84SMatt Stancliff if (!value) {
12965e362b84SMatt Stancliff /* Write the longval as a string so we can re-add it */
12975e362b84SMatt Stancliff sz = ll2string(longstr, sizeof(longstr), longval);
12985e362b84SMatt Stancliff value = (unsigned char *)longstr;
12995e362b84SMatt Stancliff }
13005e362b84SMatt Stancliff
13015e362b84SMatt Stancliff /* Add tail entry to head (must happen before tail is deleted). */
1302abdd1414SMatt Stancliff quicklistPushHead(quicklist, value, sz);
13035e362b84SMatt Stancliff
13045e362b84SMatt Stancliff /* If quicklist has only one node, the head ziplist is also the
13055e362b84SMatt Stancliff * tail ziplist and PushHead() could have reallocated our single ziplist,
13065e362b84SMatt Stancliff * which would make our pre-existing 'p' unusable. */
1307abdd1414SMatt Stancliff if (quicklist->len == 1) {
1308abdd1414SMatt Stancliff p = ziplistIndex(quicklist->tail->zl, -1);
1309abdd1414SMatt Stancliff }
13105e362b84SMatt Stancliff
13115e362b84SMatt Stancliff /* Remove tail entry. */
1312abdd1414SMatt Stancliff quicklistDelIndex(quicklist, quicklist->tail, &p);
13135e362b84SMatt Stancliff }
13145e362b84SMatt Stancliff
13155e362b84SMatt Stancliff /* pop from quicklist and return result in 'data' ptr. Value of 'data'
13165e362b84SMatt Stancliff * is the return value of 'saver' function pointer if the data is NOT a number.
13175e362b84SMatt Stancliff *
13185e362b84SMatt Stancliff * If the quicklist element is a long long, then the return value is returned in
13195e362b84SMatt Stancliff * 'sval'.
13205e362b84SMatt Stancliff *
13215e362b84SMatt Stancliff * Return value of 0 means no elements available.
13225e362b84SMatt Stancliff * Return value of 1 means check 'data' and 'sval' for values.
13235e362b84SMatt Stancliff * 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))13245e362b84SMatt Stancliff int quicklistPopCustom(quicklist *quicklist, int where, unsigned char **data,
13255e362b84SMatt Stancliff unsigned int *sz, long long *sval,
13265e362b84SMatt Stancliff void *(*saver)(unsigned char *data, unsigned int sz)) {
13275e362b84SMatt Stancliff unsigned char *p;
13285e362b84SMatt Stancliff unsigned char *vstr;
13295e362b84SMatt Stancliff unsigned int vlen;
13305e362b84SMatt Stancliff long long vlong;
13315e362b84SMatt Stancliff int pos = (where == QUICKLIST_HEAD) ? 0 : -1;
13325e362b84SMatt Stancliff
13335e362b84SMatt Stancliff if (quicklist->count == 0)
13345e362b84SMatt Stancliff return 0;
13355e362b84SMatt Stancliff
13365e362b84SMatt Stancliff if (data)
13375e362b84SMatt Stancliff *data = NULL;
13385e362b84SMatt Stancliff if (sz)
13395e362b84SMatt Stancliff *sz = 0;
13405e362b84SMatt Stancliff if (sval)
13415e362b84SMatt Stancliff *sval = -123456789;
13425e362b84SMatt Stancliff
13435e362b84SMatt Stancliff quicklistNode *node;
13445e362b84SMatt Stancliff if (where == QUICKLIST_HEAD && quicklist->head) {
13455e362b84SMatt Stancliff node = quicklist->head;
13465e362b84SMatt Stancliff } else if (where == QUICKLIST_TAIL && quicklist->tail) {
13475e362b84SMatt Stancliff node = quicklist->tail;
13485e362b84SMatt Stancliff } else {
13495e362b84SMatt Stancliff return 0;
13505e362b84SMatt Stancliff }
13515e362b84SMatt Stancliff
13525e362b84SMatt Stancliff p = ziplistIndex(node->zl, pos);
13535e362b84SMatt Stancliff if (ziplistGet(p, &vstr, &vlen, &vlong)) {
13545e362b84SMatt Stancliff if (vstr) {
13555e362b84SMatt Stancliff if (data)
13565e362b84SMatt Stancliff *data = saver(vstr, vlen);
13575e362b84SMatt Stancliff if (sz)
13585e362b84SMatt Stancliff *sz = vlen;
13595e362b84SMatt Stancliff } else {
13605e362b84SMatt Stancliff if (data)
13615e362b84SMatt Stancliff *data = NULL;
13625e362b84SMatt Stancliff if (sval)
13635e362b84SMatt Stancliff *sval = vlong;
13645e362b84SMatt Stancliff }
13655e362b84SMatt Stancliff quicklistDelIndex(quicklist, node, &p);
13665e362b84SMatt Stancliff return 1;
13675e362b84SMatt Stancliff }
13685e362b84SMatt Stancliff return 0;
13695e362b84SMatt Stancliff }
13705e362b84SMatt Stancliff
13715e362b84SMatt Stancliff /* Return a malloc'd copy of data passed in */
_quicklistSaver(unsigned char * data,unsigned int sz)137225e12d10SMatt Stancliff REDIS_STATIC void *_quicklistSaver(unsigned char *data, unsigned int sz) {
13735e362b84SMatt Stancliff unsigned char *vstr;
13745e362b84SMatt Stancliff if (data) {
13758d702189SMatt Stancliff vstr = zmalloc(sz);
1376395e1125SJohn Doe memcpy(vstr, data, sz);
13775e362b84SMatt Stancliff return vstr;
13785e362b84SMatt Stancliff }
13795e362b84SMatt Stancliff return NULL;
13805e362b84SMatt Stancliff }
13815e362b84SMatt Stancliff
13825e362b84SMatt Stancliff /* Default pop function
13835e362b84SMatt Stancliff *
13845e362b84SMatt Stancliff * Returns malloc'd value from quicklist */
quicklistPop(quicklist * quicklist,int where,unsigned char ** data,unsigned int * sz,long long * slong)13855e362b84SMatt Stancliff int quicklistPop(quicklist *quicklist, int where, unsigned char **data,
13865e362b84SMatt Stancliff unsigned int *sz, long long *slong) {
13875e362b84SMatt Stancliff unsigned char *vstr;
13885e362b84SMatt Stancliff unsigned int vlen;
13895e362b84SMatt Stancliff long long vlong;
13905e362b84SMatt Stancliff if (quicklist->count == 0)
13915e362b84SMatt Stancliff return 0;
13925e362b84SMatt Stancliff int ret = quicklistPopCustom(quicklist, where, &vstr, &vlen, &vlong,
13935e362b84SMatt Stancliff _quicklistSaver);
13945e362b84SMatt Stancliff if (data)
13955e362b84SMatt Stancliff *data = vstr;
13965e362b84SMatt Stancliff if (slong)
13975e362b84SMatt Stancliff *slong = vlong;
13985e362b84SMatt Stancliff if (sz)
13995e362b84SMatt Stancliff *sz = vlen;
14005e362b84SMatt Stancliff return ret;
14015e362b84SMatt Stancliff }
14025e362b84SMatt Stancliff
14035e362b84SMatt Stancliff /* Wrapper to allow argument-based switching between HEAD/TAIL pop */
quicklistPush(quicklist * quicklist,void * value,const size_t sz,int where)1404abdd1414SMatt Stancliff void quicklistPush(quicklist *quicklist, void *value, const size_t sz,
1405abdd1414SMatt Stancliff int where) {
14065e362b84SMatt Stancliff if (where == QUICKLIST_HEAD) {
1407abdd1414SMatt Stancliff quicklistPushHead(quicklist, value, sz);
14085e362b84SMatt Stancliff } else if (where == QUICKLIST_TAIL) {
1409abdd1414SMatt Stancliff quicklistPushTail(quicklist, value, sz);
14105e362b84SMatt Stancliff }
14115e362b84SMatt Stancliff }
14125e362b84SMatt Stancliff
14135e362b84SMatt Stancliff /* The rest of this file is test cases and test helpers. */
14145e362b84SMatt Stancliff #ifdef REDIS_TEST
14155e362b84SMatt Stancliff #include <stdint.h>
1416abdd1414SMatt Stancliff #include <sys/time.h>
14175e362b84SMatt Stancliff
14185e362b84SMatt Stancliff #define assert(_e) \
14195e362b84SMatt Stancliff do { \
14205e362b84SMatt Stancliff if (!(_e)) { \
14215e362b84SMatt Stancliff printf("\n\n=== ASSERTION FAILED ===\n"); \
14225e362b84SMatt Stancliff printf("==> %s:%d '%s' is not true\n", __FILE__, __LINE__, #_e); \
14235e362b84SMatt Stancliff err++; \
14245e362b84SMatt Stancliff } \
14255e362b84SMatt Stancliff } while (0)
14265e362b84SMatt Stancliff
14275e362b84SMatt Stancliff #define yell(str, ...) printf("ERROR! " str "\n\n", __VA_ARGS__)
14285e362b84SMatt Stancliff
14295e362b84SMatt Stancliff #define OK printf("\tOK\n")
14305e362b84SMatt Stancliff
14315e362b84SMatt Stancliff #define ERROR \
14325e362b84SMatt Stancliff do { \
14335e362b84SMatt Stancliff printf("\tERROR!\n"); \
14345e362b84SMatt Stancliff err++; \
14355e362b84SMatt Stancliff } while (0)
14365e362b84SMatt Stancliff
14375e362b84SMatt Stancliff #define ERR(x, ...) \
14385e362b84SMatt Stancliff do { \
14395e362b84SMatt Stancliff printf("%s:%s:%d:\t", __FILE__, __FUNCTION__, __LINE__); \
14405e362b84SMatt Stancliff printf("ERROR! " x "\n", __VA_ARGS__); \
14415e362b84SMatt Stancliff err++; \
14425e362b84SMatt Stancliff } while (0)
14435e362b84SMatt Stancliff
14445e362b84SMatt Stancliff #define TEST(name) printf("test — %s\n", name);
14455e362b84SMatt Stancliff #define TEST_DESC(name, ...) printf("test — " name "\n", __VA_ARGS__);
14465e362b84SMatt Stancliff
14475e362b84SMatt Stancliff #define QL_TEST_VERBOSE 0
14485e362b84SMatt Stancliff
14495e362b84SMatt Stancliff #define UNUSED(x) (void)(x)
ql_info(quicklist * ql)14505e362b84SMatt Stancliff static void ql_info(quicklist *ql) {
14515e362b84SMatt Stancliff #if QL_TEST_VERBOSE
14525e362b84SMatt Stancliff printf("Container length: %lu\n", ql->len);
14535e362b84SMatt Stancliff printf("Container size: %lu\n", ql->count);
14545e362b84SMatt Stancliff if (ql->head)
14555e362b84SMatt Stancliff printf("\t(zsize head: %d)\n", ziplistLen(ql->head->zl));
14565e362b84SMatt Stancliff if (ql->tail)
14575e362b84SMatt Stancliff printf("\t(zsize tail: %d)\n", ziplistLen(ql->tail->zl));
14585e362b84SMatt Stancliff printf("\n");
14595e362b84SMatt Stancliff #else
14605e362b84SMatt Stancliff UNUSED(ql);
14615e362b84SMatt Stancliff #endif
14625e362b84SMatt Stancliff }
14635e362b84SMatt Stancliff
1464abdd1414SMatt Stancliff /* Return the UNIX time in microseconds */
ustime(void)1465abdd1414SMatt Stancliff static long long ustime(void) {
1466abdd1414SMatt Stancliff struct timeval tv;
1467abdd1414SMatt Stancliff long long ust;
1468abdd1414SMatt Stancliff
1469abdd1414SMatt Stancliff gettimeofday(&tv, NULL);
1470abdd1414SMatt Stancliff ust = ((long long)tv.tv_sec) * 1000000;
1471abdd1414SMatt Stancliff ust += tv.tv_usec;
1472abdd1414SMatt Stancliff return ust;
1473abdd1414SMatt Stancliff }
1474abdd1414SMatt Stancliff
1475abdd1414SMatt Stancliff /* Return the UNIX time in milliseconds */
mstime(void)1476abdd1414SMatt Stancliff static long long mstime(void) { return ustime() / 1000; }
1477abdd1414SMatt Stancliff
14785e362b84SMatt Stancliff /* Iterate over an entire quicklist.
14795e362b84SMatt Stancliff * Print the list if 'print' == 1.
14805e362b84SMatt Stancliff *
14815e362b84SMatt Stancliff * Returns physical count of elements found by iterating over the list. */
_itrprintr(quicklist * ql,int print,int forward)14825e362b84SMatt Stancliff static int _itrprintr(quicklist *ql, int print, int forward) {
14835e362b84SMatt Stancliff quicklistIter *iter =
14845e362b84SMatt Stancliff quicklistGetIterator(ql, forward ? AL_START_HEAD : AL_START_TAIL);
14855e362b84SMatt Stancliff quicklistEntry entry;
14865e362b84SMatt Stancliff int i = 0;
14875e362b84SMatt Stancliff int p = 0;
14885e362b84SMatt Stancliff quicklistNode *prev = NULL;
14895e362b84SMatt Stancliff while (quicklistNext(iter, &entry)) {
14905e362b84SMatt Stancliff if (entry.node != prev) {
14915e362b84SMatt Stancliff /* Count the number of list nodes too */
14925e362b84SMatt Stancliff p++;
14935e362b84SMatt Stancliff prev = entry.node;
14945e362b84SMatt Stancliff }
14955e362b84SMatt Stancliff if (print) {
14965e362b84SMatt Stancliff printf("[%3d (%2d)]: [%.*s] (%lld)\n", i, p, entry.sz,
14975e362b84SMatt Stancliff (char *)entry.value, entry.longval);
14985e362b84SMatt Stancliff }
14995e362b84SMatt Stancliff i++;
15005e362b84SMatt Stancliff }
15015e362b84SMatt Stancliff quicklistReleaseIterator(iter);
15025e362b84SMatt Stancliff return i;
15035e362b84SMatt Stancliff }
itrprintr(quicklist * ql,int print)15045e362b84SMatt Stancliff static int itrprintr(quicklist *ql, int print) {
15055e362b84SMatt Stancliff return _itrprintr(ql, print, 1);
15065e362b84SMatt Stancliff }
15075e362b84SMatt Stancliff
itrprintr_rev(quicklist * ql,int print)15085e362b84SMatt Stancliff static int itrprintr_rev(quicklist *ql, int print) {
15095e362b84SMatt Stancliff return _itrprintr(ql, print, 0);
15105e362b84SMatt Stancliff }
15115e362b84SMatt Stancliff
15125e362b84SMatt Stancliff #define ql_verify(a, b, c, d, e) \
15135e362b84SMatt Stancliff do { \
15145e362b84SMatt Stancliff err += _ql_verify((a), (b), (c), (d), (e)); \
15155e362b84SMatt Stancliff } while (0)
15165e362b84SMatt Stancliff
15175e362b84SMatt Stancliff /* 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)15185e362b84SMatt Stancliff static int _ql_verify(quicklist *ql, uint32_t len, uint32_t count,
15195e362b84SMatt Stancliff uint32_t head_count, uint32_t tail_count) {
1520abdd1414SMatt Stancliff int errors = 0;
15215e362b84SMatt Stancliff
15225e362b84SMatt Stancliff ql_info(ql);
15235e362b84SMatt Stancliff if (len != ql->len) {
1524abdd1414SMatt Stancliff yell("quicklist length wrong: expected %d, got %u", len, ql->len);
1525abdd1414SMatt Stancliff errors++;
15265e362b84SMatt Stancliff }
15275e362b84SMatt Stancliff
15285e362b84SMatt Stancliff if (count != ql->count) {
15295e362b84SMatt Stancliff yell("quicklist count wrong: expected %d, got %lu", count, ql->count);
1530abdd1414SMatt Stancliff errors++;
15315e362b84SMatt Stancliff }
15325e362b84SMatt Stancliff
15335e362b84SMatt Stancliff int loopr = itrprintr(ql, 0);
15345e362b84SMatt Stancliff if (loopr != (int)ql->count) {
15355e362b84SMatt Stancliff yell("quicklist cached count not match actual count: expected %lu, got "
15365e362b84SMatt Stancliff "%d",
15375e362b84SMatt Stancliff ql->count, loopr);
1538abdd1414SMatt Stancliff errors++;
15395e362b84SMatt Stancliff }
15405e362b84SMatt Stancliff
15415e362b84SMatt Stancliff int rloopr = itrprintr_rev(ql, 0);
15425e362b84SMatt Stancliff if (loopr != rloopr) {
15435e362b84SMatt Stancliff yell("quicklist has different forward count than reverse count! "
15445e362b84SMatt Stancliff "Forward count is %d, reverse count is %d.",
15455e362b84SMatt Stancliff loopr, rloopr);
1546abdd1414SMatt Stancliff errors++;
15475e362b84SMatt Stancliff }
15485e362b84SMatt Stancliff
1549abdd1414SMatt Stancliff if (ql->len == 0 && !errors) {
15505e362b84SMatt Stancliff OK;
1551abdd1414SMatt Stancliff return errors;
15525e362b84SMatt Stancliff }
15535e362b84SMatt Stancliff
1554abdd1414SMatt Stancliff if (ql->head && head_count != ql->head->count &&
15555e362b84SMatt Stancliff head_count != ziplistLen(ql->head->zl)) {
15565e362b84SMatt Stancliff yell("quicklist head count wrong: expected %d, "
15575e362b84SMatt Stancliff "got cached %d vs. actual %d",
15585e362b84SMatt Stancliff head_count, ql->head->count, ziplistLen(ql->head->zl));
1559abdd1414SMatt Stancliff errors++;
15605e362b84SMatt Stancliff }
15615e362b84SMatt Stancliff
1562abdd1414SMatt Stancliff if (ql->tail && tail_count != ql->tail->count &&
15635e362b84SMatt Stancliff tail_count != ziplistLen(ql->tail->zl)) {
15645e362b84SMatt Stancliff yell("quicklist tail count wrong: expected %d, "
15655e362b84SMatt Stancliff "got cached %u vs. actual %d",
15665e362b84SMatt Stancliff tail_count, ql->tail->count, ziplistLen(ql->tail->zl));
1567abdd1414SMatt Stancliff errors++;
15685e362b84SMatt Stancliff }
1569abdd1414SMatt Stancliff
1570abdd1414SMatt Stancliff if (quicklistAllowsCompression(ql)) {
1571abdd1414SMatt Stancliff quicklistNode *node = ql->head;
1572abdd1414SMatt Stancliff unsigned int low_raw = ql->compress;
1573abdd1414SMatt Stancliff unsigned int high_raw = ql->len - ql->compress;
1574abdd1414SMatt Stancliff
1575abdd1414SMatt Stancliff for (unsigned int at = 0; at < ql->len; at++, node = node->next) {
1576abdd1414SMatt Stancliff if (node && (at < low_raw || at >= high_raw)) {
1577abdd1414SMatt Stancliff if (node->encoding != QUICKLIST_NODE_ENCODING_RAW) {
1578abdd1414SMatt Stancliff yell("Incorrect compression: node %d is "
1579abdd1414SMatt Stancliff "compressed at depth %d ((%u, %u); total "
1580abdd1414SMatt Stancliff "nodes: %u; size: %u; recompress: %d)",
1581abdd1414SMatt Stancliff at, ql->compress, low_raw, high_raw, ql->len, node->sz,
1582abdd1414SMatt Stancliff node->recompress);
1583abdd1414SMatt Stancliff errors++;
1584abdd1414SMatt Stancliff }
1585abdd1414SMatt Stancliff } else {
1586abdd1414SMatt Stancliff if (node->encoding != QUICKLIST_NODE_ENCODING_LZF &&
1587abdd1414SMatt Stancliff !node->attempted_compress) {
1588abdd1414SMatt Stancliff yell("Incorrect non-compression: node %d is NOT "
1589abdd1414SMatt Stancliff "compressed at depth %d ((%u, %u); total "
1590abdd1414SMatt Stancliff "nodes: %u; size: %u; recompress: %d; attempted: %d)",
1591abdd1414SMatt Stancliff at, ql->compress, low_raw, high_raw, ql->len, node->sz,
1592abdd1414SMatt Stancliff node->recompress, node->attempted_compress);
1593abdd1414SMatt Stancliff errors++;
1594abdd1414SMatt Stancliff }
1595abdd1414SMatt Stancliff }
1596abdd1414SMatt Stancliff }
1597abdd1414SMatt Stancliff }
1598abdd1414SMatt Stancliff
1599abdd1414SMatt Stancliff if (!errors)
16005e362b84SMatt Stancliff OK;
1601abdd1414SMatt Stancliff return errors;
16025e362b84SMatt Stancliff }
16035e362b84SMatt Stancliff
16045e362b84SMatt Stancliff /* Generate new string concatenating integer i against string 'prefix' */
genstr(char * prefix,int i)16055e362b84SMatt Stancliff static char *genstr(char *prefix, int i) {
16065e362b84SMatt Stancliff static char result[64] = {0};
16075e362b84SMatt Stancliff snprintf(result, sizeof(result), "%s%d", prefix, i);
16085e362b84SMatt Stancliff return result;
16095e362b84SMatt Stancliff }
16105e362b84SMatt Stancliff
16115e362b84SMatt Stancliff /* main test, but callable from other files */
quicklistTest(int argc,char * argv[])16125e362b84SMatt Stancliff int quicklistTest(int argc, char *argv[]) {
1613abdd1414SMatt Stancliff UNUSED(argc);
1614abdd1414SMatt Stancliff UNUSED(argv);
1615abdd1414SMatt Stancliff
16165e362b84SMatt Stancliff unsigned int err = 0;
1617c6bf20c2SMatt Stancliff int optimize_start =
1618c6bf20c2SMatt Stancliff -(int)(sizeof(optimization_level) / sizeof(*optimization_level));
1619c6bf20c2SMatt Stancliff
1620c6bf20c2SMatt Stancliff printf("Starting optimization offset at: %d\n", optimize_start);
16215e362b84SMatt Stancliff
1622abdd1414SMatt Stancliff int options[] = {0, 1, 2, 3, 4, 5, 6, 10};
1623abdd1414SMatt Stancliff size_t option_count = sizeof(options) / sizeof(*options);
1624abdd1414SMatt Stancliff long long runtime[option_count];
1625abdd1414SMatt Stancliff
1626abdd1414SMatt Stancliff for (int _i = 0; _i < (int)option_count; _i++) {
1627abdd1414SMatt Stancliff printf("Testing Option %d\n", options[_i]);
1628abdd1414SMatt Stancliff long long start = mstime();
16295e362b84SMatt Stancliff
16305e362b84SMatt Stancliff TEST("create list") {
1631abdd1414SMatt Stancliff quicklist *ql = quicklistNew(-2, options[_i]);
16325e362b84SMatt Stancliff ql_verify(ql, 0, 0, 0, 0);
16335e362b84SMatt Stancliff quicklistRelease(ql);
16345e362b84SMatt Stancliff }
16355e362b84SMatt Stancliff
16365e362b84SMatt Stancliff TEST("add to tail of empty list") {
1637abdd1414SMatt Stancliff quicklist *ql = quicklistNew(-2, options[_i]);
1638abdd1414SMatt Stancliff quicklistPushTail(ql, "hello", 6);
16395e362b84SMatt Stancliff /* 1 for head and 1 for tail beacuse 1 node = head = tail */
16405e362b84SMatt Stancliff ql_verify(ql, 1, 1, 1, 1);
16415e362b84SMatt Stancliff quicklistRelease(ql);
16425e362b84SMatt Stancliff }
16435e362b84SMatt Stancliff
16445e362b84SMatt Stancliff TEST("add to head of empty list") {
1645abdd1414SMatt Stancliff quicklist *ql = quicklistNew(-2, options[_i]);
1646abdd1414SMatt Stancliff quicklistPushHead(ql, "hello", 6);
16475e362b84SMatt Stancliff /* 1 for head and 1 for tail beacuse 1 node = head = tail */
16485e362b84SMatt Stancliff ql_verify(ql, 1, 1, 1, 1);
16495e362b84SMatt Stancliff quicklistRelease(ql);
16505e362b84SMatt Stancliff }
16515e362b84SMatt Stancliff
1652c6bf20c2SMatt Stancliff for (int f = optimize_start; f < 32; f++) {
1653abdd1414SMatt Stancliff TEST_DESC("add to tail 5x at fill %d at compress %d", f,
1654abdd1414SMatt Stancliff options[_i]) {
1655abdd1414SMatt Stancliff quicklist *ql = quicklistNew(f, options[_i]);
16565e362b84SMatt Stancliff for (int i = 0; i < 5; i++)
1657abdd1414SMatt Stancliff quicklistPushTail(ql, genstr("hello", i), 32);
16585e362b84SMatt Stancliff if (ql->count != 5)
16595e362b84SMatt Stancliff ERROR;
16605e362b84SMatt Stancliff if (f == 32)
16615e362b84SMatt Stancliff ql_verify(ql, 1, 5, 5, 5);
16625e362b84SMatt Stancliff quicklistRelease(ql);
16635e362b84SMatt Stancliff }
16645e362b84SMatt Stancliff }
16655e362b84SMatt Stancliff
1666c6bf20c2SMatt Stancliff for (int f = optimize_start; f < 32; f++) {
1667abdd1414SMatt Stancliff TEST_DESC("add to head 5x at fill %d at compress %d", f,
1668abdd1414SMatt Stancliff options[_i]) {
1669abdd1414SMatt Stancliff quicklist *ql = quicklistNew(f, options[_i]);
16705e362b84SMatt Stancliff for (int i = 0; i < 5; i++)
1671abdd1414SMatt Stancliff quicklistPushHead(ql, genstr("hello", i), 32);
16725e362b84SMatt Stancliff if (ql->count != 5)
16735e362b84SMatt Stancliff ERROR;
16745e362b84SMatt Stancliff if (f == 32)
16755e362b84SMatt Stancliff ql_verify(ql, 1, 5, 5, 5);
16765e362b84SMatt Stancliff quicklistRelease(ql);
16775e362b84SMatt Stancliff }
16785e362b84SMatt Stancliff }
16795e362b84SMatt Stancliff
1680c6bf20c2SMatt Stancliff for (int f = optimize_start; f < 512; f++) {
1681abdd1414SMatt Stancliff TEST_DESC("add to tail 500x at fill %d at compress %d", f,
1682abdd1414SMatt Stancliff options[_i]) {
1683abdd1414SMatt Stancliff quicklist *ql = quicklistNew(f, options[_i]);
16845e362b84SMatt Stancliff for (int i = 0; i < 500; i++)
1685abdd1414SMatt Stancliff quicklistPushTail(ql, genstr("hello", i), 64);
16865e362b84SMatt Stancliff if (ql->count != 500)
16875e362b84SMatt Stancliff ERROR;
16885e362b84SMatt Stancliff if (f == 32)
16895e362b84SMatt Stancliff ql_verify(ql, 16, 500, 32, 20);
16905e362b84SMatt Stancliff quicklistRelease(ql);
16915e362b84SMatt Stancliff }
16925e362b84SMatt Stancliff }
16935e362b84SMatt Stancliff
1694c6bf20c2SMatt Stancliff for (int f = optimize_start; f < 512; f++) {
1695abdd1414SMatt Stancliff TEST_DESC("add to head 500x at fill %d at compress %d", f,
1696abdd1414SMatt Stancliff options[_i]) {
1697abdd1414SMatt Stancliff quicklist *ql = quicklistNew(f, options[_i]);
16985e362b84SMatt Stancliff for (int i = 0; i < 500; i++)
1699abdd1414SMatt Stancliff quicklistPushHead(ql, genstr("hello", i), 32);
17005e362b84SMatt Stancliff if (ql->count != 500)
17015e362b84SMatt Stancliff ERROR;
17025e362b84SMatt Stancliff if (f == 32)
17035e362b84SMatt Stancliff ql_verify(ql, 16, 500, 20, 32);
17045e362b84SMatt Stancliff quicklistRelease(ql);
17055e362b84SMatt Stancliff }
17065e362b84SMatt Stancliff }
17075e362b84SMatt Stancliff
17085e362b84SMatt Stancliff TEST("rotate empty") {
1709abdd1414SMatt Stancliff quicklist *ql = quicklistNew(-2, options[_i]);
1710abdd1414SMatt Stancliff quicklistRotate(ql);
17115e362b84SMatt Stancliff ql_verify(ql, 0, 0, 0, 0);
17125e362b84SMatt Stancliff quicklistRelease(ql);
17135e362b84SMatt Stancliff }
17145e362b84SMatt Stancliff
1715c6bf20c2SMatt Stancliff for (int f = optimize_start; f < 32; f++) {
17165e362b84SMatt Stancliff TEST("rotate one val once") {
1717abdd1414SMatt Stancliff quicklist *ql = quicklistNew(f, options[_i]);
1718abdd1414SMatt Stancliff quicklistPushHead(ql, "hello", 6);
1719abdd1414SMatt Stancliff quicklistRotate(ql);
1720abdd1414SMatt Stancliff /* Ignore compression verify because ziplist is
1721abdd1414SMatt Stancliff * too small to compress. */
17225e362b84SMatt Stancliff ql_verify(ql, 1, 1, 1, 1);
17235e362b84SMatt Stancliff quicklistRelease(ql);
17245e362b84SMatt Stancliff }
17255e362b84SMatt Stancliff }
17265e362b84SMatt Stancliff
1727abdd1414SMatt Stancliff for (int f = optimize_start; f < 3; f++) {
1728abdd1414SMatt Stancliff TEST_DESC("rotate 500 val 5000 times at fill %d at compress %d", f,
1729abdd1414SMatt Stancliff options[_i]) {
1730abdd1414SMatt Stancliff quicklist *ql = quicklistNew(f, options[_i]);
1731abdd1414SMatt Stancliff quicklistPushHead(ql, "900", 3);
1732abdd1414SMatt Stancliff quicklistPushHead(ql, "7000", 4);
1733abdd1414SMatt Stancliff quicklistPushHead(ql, "-1200", 5);
1734abdd1414SMatt Stancliff quicklistPushHead(ql, "42", 2);
17355e362b84SMatt Stancliff for (int i = 0; i < 500; i++)
1736abdd1414SMatt Stancliff quicklistPushHead(ql, genstr("hello", i), 64);
17375e362b84SMatt Stancliff ql_info(ql);
17385e362b84SMatt Stancliff for (int i = 0; i < 5000; i++) {
17395e362b84SMatt Stancliff ql_info(ql);
1740abdd1414SMatt Stancliff quicklistRotate(ql);
17415e362b84SMatt Stancliff }
17425e362b84SMatt Stancliff if (f == 1)
17435e362b84SMatt Stancliff ql_verify(ql, 504, 504, 1, 1);
17445e362b84SMatt Stancliff else if (f == 2)
17455e362b84SMatt Stancliff ql_verify(ql, 252, 504, 2, 2);
17465e362b84SMatt Stancliff else if (f == 32)
17475e362b84SMatt Stancliff ql_verify(ql, 16, 504, 32, 24);
17485e362b84SMatt Stancliff quicklistRelease(ql);
17495e362b84SMatt Stancliff }
17505e362b84SMatt Stancliff }
17515e362b84SMatt Stancliff
17525e362b84SMatt Stancliff TEST("pop empty") {
1753abdd1414SMatt Stancliff quicklist *ql = quicklistNew(-2, options[_i]);
17545e362b84SMatt Stancliff quicklistPop(ql, QUICKLIST_HEAD, NULL, NULL, NULL);
17555e362b84SMatt Stancliff ql_verify(ql, 0, 0, 0, 0);
17565e362b84SMatt Stancliff quicklistRelease(ql);
17575e362b84SMatt Stancliff }
17585e362b84SMatt Stancliff
17595e362b84SMatt Stancliff TEST("pop 1 string from 1") {
1760abdd1414SMatt Stancliff quicklist *ql = quicklistNew(-2, options[_i]);
1761552e5908SMatt Stancliff char *populate = genstr("hello", 331);
1762552e5908SMatt Stancliff quicklistPushHead(ql, populate, 32);
17635e362b84SMatt Stancliff unsigned char *data;
17645e362b84SMatt Stancliff unsigned int sz;
17655e362b84SMatt Stancliff long long lv;
17665e362b84SMatt Stancliff ql_info(ql);
17675e362b84SMatt Stancliff quicklistPop(ql, QUICKLIST_HEAD, &data, &sz, &lv);
17685e362b84SMatt Stancliff assert(data != NULL);
17695e362b84SMatt Stancliff assert(sz == 32);
1770552e5908SMatt Stancliff if (strcmp(populate, (char *)data))
1771552e5908SMatt Stancliff ERR("Pop'd value (%.*s) didn't equal original value (%s)", sz,
1772552e5908SMatt Stancliff data, populate);
17735e362b84SMatt Stancliff zfree(data);
17745e362b84SMatt Stancliff ql_verify(ql, 0, 0, 0, 0);
17755e362b84SMatt Stancliff quicklistRelease(ql);
17765e362b84SMatt Stancliff }
17775e362b84SMatt Stancliff
17785e362b84SMatt Stancliff TEST("pop head 1 number from 1") {
1779abdd1414SMatt Stancliff quicklist *ql = quicklistNew(-2, options[_i]);
1780abdd1414SMatt Stancliff quicklistPushHead(ql, "55513", 5);
17815e362b84SMatt Stancliff unsigned char *data;
17825e362b84SMatt Stancliff unsigned int sz;
17835e362b84SMatt Stancliff long long lv;
17845e362b84SMatt Stancliff ql_info(ql);
17855e362b84SMatt Stancliff quicklistPop(ql, QUICKLIST_HEAD, &data, &sz, &lv);
17865e362b84SMatt Stancliff assert(data == NULL);
17875e362b84SMatt Stancliff assert(lv == 55513);
17885e362b84SMatt Stancliff ql_verify(ql, 0, 0, 0, 0);
17895e362b84SMatt Stancliff quicklistRelease(ql);
17905e362b84SMatt Stancliff }
17915e362b84SMatt Stancliff
17925e362b84SMatt Stancliff TEST("pop head 500 from 500") {
1793abdd1414SMatt Stancliff quicklist *ql = quicklistNew(-2, options[_i]);
17945e362b84SMatt Stancliff for (int i = 0; i < 500; i++)
1795abdd1414SMatt Stancliff quicklistPushHead(ql, genstr("hello", i), 32);
17965e362b84SMatt Stancliff ql_info(ql);
17975e362b84SMatt Stancliff for (int i = 0; i < 500; i++) {
17985e362b84SMatt Stancliff unsigned char *data;
17995e362b84SMatt Stancliff unsigned int sz;
18005e362b84SMatt Stancliff long long lv;
18015e362b84SMatt Stancliff int ret = quicklistPop(ql, QUICKLIST_HEAD, &data, &sz, &lv);
18025e362b84SMatt Stancliff assert(ret == 1);
18035e362b84SMatt Stancliff assert(data != NULL);
18045e362b84SMatt Stancliff assert(sz == 32);
1805552e5908SMatt Stancliff if (strcmp(genstr("hello", 499 - i), (char *)data))
1806552e5908SMatt Stancliff ERR("Pop'd value (%.*s) didn't equal original value (%s)",
1807552e5908SMatt Stancliff sz, data, genstr("hello", 499 - i));
18085e362b84SMatt Stancliff zfree(data);
18095e362b84SMatt Stancliff }
18105e362b84SMatt Stancliff ql_verify(ql, 0, 0, 0, 0);
18115e362b84SMatt Stancliff quicklistRelease(ql);
18125e362b84SMatt Stancliff }
18135e362b84SMatt Stancliff
18145e362b84SMatt Stancliff TEST("pop head 5000 from 500") {
1815abdd1414SMatt Stancliff quicklist *ql = quicklistNew(-2, options[_i]);
18165e362b84SMatt Stancliff for (int i = 0; i < 500; i++)
1817abdd1414SMatt Stancliff quicklistPushHead(ql, genstr("hello", i), 32);
18185e362b84SMatt Stancliff for (int i = 0; i < 5000; i++) {
18195e362b84SMatt Stancliff unsigned char *data;
18205e362b84SMatt Stancliff unsigned int sz;
18215e362b84SMatt Stancliff long long lv;
18225e362b84SMatt Stancliff int ret = quicklistPop(ql, QUICKLIST_HEAD, &data, &sz, &lv);
18235e362b84SMatt Stancliff if (i < 500) {
18245e362b84SMatt Stancliff assert(ret == 1);
18255e362b84SMatt Stancliff assert(data != NULL);
18265e362b84SMatt Stancliff assert(sz == 32);
1827552e5908SMatt Stancliff if (strcmp(genstr("hello", 499 - i), (char *)data))
1828552e5908SMatt Stancliff ERR("Pop'd value (%.*s) didn't equal original value "
1829552e5908SMatt Stancliff "(%s)",
1830552e5908SMatt Stancliff sz, data, genstr("hello", 499 - i));
18315e362b84SMatt Stancliff zfree(data);
18325e362b84SMatt Stancliff } else {
18335e362b84SMatt Stancliff assert(ret == 0);
18345e362b84SMatt Stancliff }
18355e362b84SMatt Stancliff }
18365e362b84SMatt Stancliff ql_verify(ql, 0, 0, 0, 0);
18375e362b84SMatt Stancliff quicklistRelease(ql);
18385e362b84SMatt Stancliff }
18395e362b84SMatt Stancliff
18405e362b84SMatt Stancliff TEST("iterate forward over 500 list") {
1841abdd1414SMatt Stancliff quicklist *ql = quicklistNew(-2, options[_i]);
1842abdd1414SMatt Stancliff quicklistSetFill(ql, 32);
18435e362b84SMatt Stancliff for (int i = 0; i < 500; i++)
1844abdd1414SMatt Stancliff quicklistPushHead(ql, genstr("hello", i), 32);
18455e362b84SMatt Stancliff quicklistIter *iter = quicklistGetIterator(ql, AL_START_HEAD);
18465e362b84SMatt Stancliff quicklistEntry entry;
18475e362b84SMatt Stancliff int i = 499, count = 0;
18485e362b84SMatt Stancliff while (quicklistNext(iter, &entry)) {
18495e362b84SMatt Stancliff char *h = genstr("hello", i);
18505e362b84SMatt Stancliff if (strcmp((char *)entry.value, h))
1851abdd1414SMatt Stancliff ERR("value [%s] didn't match [%s] at position %d",
1852abdd1414SMatt Stancliff entry.value, h, i);
18535e362b84SMatt Stancliff i--;
18545e362b84SMatt Stancliff count++;
18555e362b84SMatt Stancliff }
18565e362b84SMatt Stancliff if (count != 500)
18575e362b84SMatt Stancliff ERR("Didn't iterate over exactly 500 elements (%d)", i);
18585e362b84SMatt Stancliff ql_verify(ql, 16, 500, 20, 32);
18595e362b84SMatt Stancliff quicklistReleaseIterator(iter);
18605e362b84SMatt Stancliff quicklistRelease(ql);
18615e362b84SMatt Stancliff }
18625e362b84SMatt Stancliff
18635e362b84SMatt Stancliff TEST("iterate reverse over 500 list") {
1864abdd1414SMatt Stancliff quicklist *ql = quicklistNew(-2, options[_i]);
1865abdd1414SMatt Stancliff quicklistSetFill(ql, 32);
18665e362b84SMatt Stancliff for (int i = 0; i < 500; i++)
1867abdd1414SMatt Stancliff quicklistPushHead(ql, genstr("hello", i), 32);
18685e362b84SMatt Stancliff quicklistIter *iter = quicklistGetIterator(ql, AL_START_TAIL);
18695e362b84SMatt Stancliff quicklistEntry entry;
18705e362b84SMatt Stancliff int i = 0;
18715e362b84SMatt Stancliff while (quicklistNext(iter, &entry)) {
18725e362b84SMatt Stancliff char *h = genstr("hello", i);
18735e362b84SMatt Stancliff if (strcmp((char *)entry.value, h))
1874abdd1414SMatt Stancliff ERR("value [%s] didn't match [%s] at position %d",
1875abdd1414SMatt Stancliff entry.value, h, i);
18765e362b84SMatt Stancliff i++;
18775e362b84SMatt Stancliff }
18785e362b84SMatt Stancliff if (i != 500)
18795e362b84SMatt Stancliff ERR("Didn't iterate over exactly 500 elements (%d)", i);
18805e362b84SMatt Stancliff ql_verify(ql, 16, 500, 20, 32);
18815e362b84SMatt Stancliff quicklistReleaseIterator(iter);
18825e362b84SMatt Stancliff quicklistRelease(ql);
18835e362b84SMatt Stancliff }
18845e362b84SMatt Stancliff
18855e362b84SMatt Stancliff TEST("insert before with 0 elements") {
1886abdd1414SMatt Stancliff quicklist *ql = quicklistNew(-2, options[_i]);
18875e362b84SMatt Stancliff quicklistEntry entry;
18885e362b84SMatt Stancliff quicklistIndex(ql, 0, &entry);
1889abdd1414SMatt Stancliff quicklistInsertBefore(ql, &entry, "abc", 4);
18905e362b84SMatt Stancliff ql_verify(ql, 1, 1, 1, 1);
18915e362b84SMatt Stancliff quicklistRelease(ql);
18925e362b84SMatt Stancliff }
18935e362b84SMatt Stancliff
18945e362b84SMatt Stancliff TEST("insert after with 0 elements") {
1895abdd1414SMatt Stancliff quicklist *ql = quicklistNew(-2, options[_i]);
18965e362b84SMatt Stancliff quicklistEntry entry;
18975e362b84SMatt Stancliff quicklistIndex(ql, 0, &entry);
1898abdd1414SMatt Stancliff quicklistInsertAfter(ql, &entry, "abc", 4);
18995e362b84SMatt Stancliff ql_verify(ql, 1, 1, 1, 1);
19005e362b84SMatt Stancliff quicklistRelease(ql);
19015e362b84SMatt Stancliff }
19025e362b84SMatt Stancliff
19035e362b84SMatt Stancliff TEST("insert after 1 element") {
1904abdd1414SMatt Stancliff quicklist *ql = quicklistNew(-2, options[_i]);
1905abdd1414SMatt Stancliff quicklistPushHead(ql, "hello", 6);
19065e362b84SMatt Stancliff quicklistEntry entry;
19075e362b84SMatt Stancliff quicklistIndex(ql, 0, &entry);
1908abdd1414SMatt Stancliff quicklistInsertAfter(ql, &entry, "abc", 4);
19095e362b84SMatt Stancliff ql_verify(ql, 1, 2, 2, 2);
19105e362b84SMatt Stancliff quicklistRelease(ql);
19115e362b84SMatt Stancliff }
19125e362b84SMatt Stancliff
19135e362b84SMatt Stancliff TEST("insert before 1 element") {
1914abdd1414SMatt Stancliff quicklist *ql = quicklistNew(-2, options[_i]);
1915abdd1414SMatt Stancliff quicklistPushHead(ql, "hello", 6);
19165e362b84SMatt Stancliff quicklistEntry entry;
19175e362b84SMatt Stancliff quicklistIndex(ql, 0, &entry);
1918abdd1414SMatt Stancliff quicklistInsertAfter(ql, &entry, "abc", 4);
19195e362b84SMatt Stancliff ql_verify(ql, 1, 2, 2, 2);
19205e362b84SMatt Stancliff quicklistRelease(ql);
19215e362b84SMatt Stancliff }
19225e362b84SMatt Stancliff
1923c6bf20c2SMatt Stancliff for (int f = optimize_start; f < 12; f++) {
1924abdd1414SMatt Stancliff TEST_DESC("insert once in elements while iterating at fill %d at "
1925abdd1414SMatt Stancliff "compress %d\n",
1926abdd1414SMatt Stancliff f, options[_i]) {
1927abdd1414SMatt Stancliff quicklist *ql = quicklistNew(f, options[_i]);
1928abdd1414SMatt Stancliff quicklistPushTail(ql, "abc", 3);
1929abdd1414SMatt Stancliff quicklistSetFill(ql, 1);
1930abdd1414SMatt Stancliff quicklistPushTail(ql, "def", 3); /* force to unique node */
1931abdd1414SMatt Stancliff quicklistSetFill(ql, f);
1932abdd1414SMatt Stancliff quicklistPushTail(ql, "bob", 3); /* force to reset for +3 */
1933abdd1414SMatt Stancliff quicklistPushTail(ql, "foo", 3);
1934abdd1414SMatt Stancliff quicklistPushTail(ql, "zoo", 3);
19355e362b84SMatt Stancliff
1936abdd1414SMatt Stancliff itrprintr(ql, 0);
19375e362b84SMatt Stancliff /* insert "bar" before "bob" while iterating over list. */
19385e362b84SMatt Stancliff quicklistIter *iter = quicklistGetIterator(ql, AL_START_HEAD);
19395e362b84SMatt Stancliff quicklistEntry entry;
19405e362b84SMatt Stancliff while (quicklistNext(iter, &entry)) {
19415e362b84SMatt Stancliff if (!strncmp((char *)entry.value, "bob", 3)) {
19425e362b84SMatt Stancliff /* Insert as fill = 1 so it spills into new node. */
1943abdd1414SMatt Stancliff quicklistInsertBefore(ql, &entry, "bar", 3);
1944abdd1414SMatt Stancliff break; /* didn't we fix insert-while-iterating? */
19455e362b84SMatt Stancliff }
19465e362b84SMatt Stancliff }
1947abdd1414SMatt Stancliff itrprintr(ql, 0);
19485e362b84SMatt Stancliff
19495e362b84SMatt Stancliff /* verify results */
19505e362b84SMatt Stancliff quicklistIndex(ql, 0, &entry);
19515e362b84SMatt Stancliff if (strncmp((char *)entry.value, "abc", 3))
19525e362b84SMatt Stancliff ERR("Value 0 didn't match, instead got: %.*s", entry.sz,
19535e362b84SMatt Stancliff entry.value);
19545e362b84SMatt Stancliff quicklistIndex(ql, 1, &entry);
19555e362b84SMatt Stancliff if (strncmp((char *)entry.value, "def", 3))
19565e362b84SMatt Stancliff ERR("Value 1 didn't match, instead got: %.*s", entry.sz,
19575e362b84SMatt Stancliff entry.value);
19585e362b84SMatt Stancliff quicklistIndex(ql, 2, &entry);
19595e362b84SMatt Stancliff if (strncmp((char *)entry.value, "bar", 3))
19605e362b84SMatt Stancliff ERR("Value 2 didn't match, instead got: %.*s", entry.sz,
19615e362b84SMatt Stancliff entry.value);
19625e362b84SMatt Stancliff quicklistIndex(ql, 3, &entry);
19635e362b84SMatt Stancliff if (strncmp((char *)entry.value, "bob", 3))
19645e362b84SMatt Stancliff ERR("Value 3 didn't match, instead got: %.*s", entry.sz,
19655e362b84SMatt Stancliff entry.value);
19665e362b84SMatt Stancliff quicklistIndex(ql, 4, &entry);
19675e362b84SMatt Stancliff if (strncmp((char *)entry.value, "foo", 3))
19685e362b84SMatt Stancliff ERR("Value 4 didn't match, instead got: %.*s", entry.sz,
19695e362b84SMatt Stancliff entry.value);
19705e362b84SMatt Stancliff quicklistIndex(ql, 5, &entry);
19715e362b84SMatt Stancliff if (strncmp((char *)entry.value, "zoo", 3))
19725e362b84SMatt Stancliff ERR("Value 5 didn't match, instead got: %.*s", entry.sz,
19735e362b84SMatt Stancliff entry.value);
19745e362b84SMatt Stancliff quicklistReleaseIterator(iter);
19755e362b84SMatt Stancliff quicklistRelease(ql);
19765e362b84SMatt Stancliff }
19775e362b84SMatt Stancliff }
19785e362b84SMatt Stancliff
1979c6bf20c2SMatt Stancliff for (int f = optimize_start; f < 1024; f++) {
1980abdd1414SMatt Stancliff TEST_DESC(
1981abdd1414SMatt Stancliff "insert [before] 250 new in middle of 500 elements at fill"
1982abdd1414SMatt Stancliff " %d at compress %d",
1983abdd1414SMatt Stancliff f, options[_i]) {
1984abdd1414SMatt Stancliff quicklist *ql = quicklistNew(f, options[_i]);
19855e362b84SMatt Stancliff for (int i = 0; i < 500; i++)
1986abdd1414SMatt Stancliff quicklistPushTail(ql, genstr("hello", i), 32);
19875e362b84SMatt Stancliff for (int i = 0; i < 250; i++) {
19885e362b84SMatt Stancliff quicklistEntry entry;
19895e362b84SMatt Stancliff quicklistIndex(ql, 250, &entry);
1990abdd1414SMatt Stancliff quicklistInsertBefore(ql, &entry, genstr("abc", i), 32);
19915e362b84SMatt Stancliff }
19925e362b84SMatt Stancliff if (f == 32)
19935e362b84SMatt Stancliff ql_verify(ql, 25, 750, 32, 20);
19945e362b84SMatt Stancliff quicklistRelease(ql);
19955e362b84SMatt Stancliff }
19965e362b84SMatt Stancliff }
19975e362b84SMatt Stancliff
1998c6bf20c2SMatt Stancliff for (int f = optimize_start; f < 1024; f++) {
1999abdd1414SMatt Stancliff TEST_DESC("insert [after] 250 new in middle of 500 elements at "
2000abdd1414SMatt Stancliff "fill %d at compress %d",
2001abdd1414SMatt Stancliff f, options[_i]) {
2002abdd1414SMatt Stancliff quicklist *ql = quicklistNew(f, options[_i]);
20035e362b84SMatt Stancliff for (int i = 0; i < 500; i++)
2004abdd1414SMatt Stancliff quicklistPushHead(ql, genstr("hello", i), 32);
20055e362b84SMatt Stancliff for (int i = 0; i < 250; i++) {
20065e362b84SMatt Stancliff quicklistEntry entry;
20075e362b84SMatt Stancliff quicklistIndex(ql, 250, &entry);
2008abdd1414SMatt Stancliff quicklistInsertAfter(ql, &entry, genstr("abc", i), 32);
20095e362b84SMatt Stancliff }
20105e362b84SMatt Stancliff
20115e362b84SMatt Stancliff if (ql->count != 750)
20125e362b84SMatt Stancliff ERR("List size not 750, but rather %ld", ql->count);
20135e362b84SMatt Stancliff
20145e362b84SMatt Stancliff if (f == 32)
20155e362b84SMatt Stancliff ql_verify(ql, 26, 750, 20, 32);
20165e362b84SMatt Stancliff quicklistRelease(ql);
20175e362b84SMatt Stancliff }
20185e362b84SMatt Stancliff }
20195e362b84SMatt Stancliff
20205e362b84SMatt Stancliff TEST("duplicate empty list") {
2021abdd1414SMatt Stancliff quicklist *ql = quicklistNew(-2, options[_i]);
20225e362b84SMatt Stancliff ql_verify(ql, 0, 0, 0, 0);
20235e362b84SMatt Stancliff quicklist *copy = quicklistDup(ql);
20245e362b84SMatt Stancliff ql_verify(copy, 0, 0, 0, 0);
20255e362b84SMatt Stancliff quicklistRelease(ql);
20265e362b84SMatt Stancliff quicklistRelease(copy);
20275e362b84SMatt Stancliff }
20285e362b84SMatt Stancliff
20295e362b84SMatt Stancliff TEST("duplicate list of 1 element") {
2030abdd1414SMatt Stancliff quicklist *ql = quicklistNew(-2, options[_i]);
2031abdd1414SMatt Stancliff quicklistPushHead(ql, genstr("hello", 3), 32);
20325e362b84SMatt Stancliff ql_verify(ql, 1, 1, 1, 1);
20335e362b84SMatt Stancliff quicklist *copy = quicklistDup(ql);
20345e362b84SMatt Stancliff ql_verify(copy, 1, 1, 1, 1);
20355e362b84SMatt Stancliff quicklistRelease(ql);
20365e362b84SMatt Stancliff quicklistRelease(copy);
20375e362b84SMatt Stancliff }
20385e362b84SMatt Stancliff
20395e362b84SMatt Stancliff TEST("duplicate list of 500") {
2040abdd1414SMatt Stancliff quicklist *ql = quicklistNew(-2, options[_i]);
2041abdd1414SMatt Stancliff quicklistSetFill(ql, 32);
20425e362b84SMatt Stancliff for (int i = 0; i < 500; i++)
2043abdd1414SMatt Stancliff quicklistPushHead(ql, genstr("hello", i), 32);
20445e362b84SMatt Stancliff ql_verify(ql, 16, 500, 20, 32);
20455e362b84SMatt Stancliff
20465e362b84SMatt Stancliff quicklist *copy = quicklistDup(ql);
20475e362b84SMatt Stancliff ql_verify(copy, 16, 500, 20, 32);
20485e362b84SMatt Stancliff quicklistRelease(ql);
20495e362b84SMatt Stancliff quicklistRelease(copy);
20505e362b84SMatt Stancliff }
20515e362b84SMatt Stancliff
2052c6bf20c2SMatt Stancliff for (int f = optimize_start; f < 512; f++) {
2053abdd1414SMatt Stancliff TEST_DESC("index 1,200 from 500 list at fill %d at compress %d", f,
2054abdd1414SMatt Stancliff options[_i]) {
2055abdd1414SMatt Stancliff quicklist *ql = quicklistNew(f, options[_i]);
20565e362b84SMatt Stancliff for (int i = 0; i < 500; i++)
2057abdd1414SMatt Stancliff quicklistPushTail(ql, genstr("hello", i + 1), 32);
20585e362b84SMatt Stancliff quicklistEntry entry;
20595e362b84SMatt Stancliff quicklistIndex(ql, 1, &entry);
20605e362b84SMatt Stancliff if (!strcmp((char *)entry.value, "hello2"))
20615e362b84SMatt Stancliff OK;
20625e362b84SMatt Stancliff else
20635e362b84SMatt Stancliff ERR("Value: %s", entry.value);
20645e362b84SMatt Stancliff quicklistIndex(ql, 200, &entry);
20655e362b84SMatt Stancliff if (!strcmp((char *)entry.value, "hello201"))
20665e362b84SMatt Stancliff OK;
20675e362b84SMatt Stancliff else
20685e362b84SMatt Stancliff ERR("Value: %s", entry.value);
20695e362b84SMatt Stancliff quicklistRelease(ql);
20705e362b84SMatt Stancliff }
20715e362b84SMatt Stancliff
2072abdd1414SMatt Stancliff TEST_DESC("index -1,-2 from 500 list at fill %d at compress %d", f,
2073abdd1414SMatt Stancliff options[_i]) {
2074abdd1414SMatt Stancliff quicklist *ql = quicklistNew(f, options[_i]);
20755e362b84SMatt Stancliff for (int i = 0; i < 500; i++)
2076abdd1414SMatt Stancliff quicklistPushTail(ql, genstr("hello", i + 1), 32);
20775e362b84SMatt Stancliff quicklistEntry entry;
20785e362b84SMatt Stancliff quicklistIndex(ql, -1, &entry);
20795e362b84SMatt Stancliff if (!strcmp((char *)entry.value, "hello500"))
20805e362b84SMatt Stancliff OK;
20815e362b84SMatt Stancliff else
20825e362b84SMatt Stancliff ERR("Value: %s", entry.value);
20835e362b84SMatt Stancliff quicklistIndex(ql, -2, &entry);
20845e362b84SMatt Stancliff if (!strcmp((char *)entry.value, "hello499"))
20855e362b84SMatt Stancliff OK;
20865e362b84SMatt Stancliff else
20875e362b84SMatt Stancliff ERR("Value: %s", entry.value);
20885e362b84SMatt Stancliff quicklistRelease(ql);
20895e362b84SMatt Stancliff }
20905e362b84SMatt Stancliff
2091abdd1414SMatt Stancliff TEST_DESC("index -100 from 500 list at fill %d at compress %d", f,
2092abdd1414SMatt Stancliff options[_i]) {
2093abdd1414SMatt Stancliff quicklist *ql = quicklistNew(f, options[_i]);
20945e362b84SMatt Stancliff for (int i = 0; i < 500; i++)
2095abdd1414SMatt Stancliff quicklistPushTail(ql, genstr("hello", i + 1), 32);
20965e362b84SMatt Stancliff quicklistEntry entry;
20975e362b84SMatt Stancliff quicklistIndex(ql, -100, &entry);
20985e362b84SMatt Stancliff if (!strcmp((char *)entry.value, "hello401"))
20995e362b84SMatt Stancliff OK;
21005e362b84SMatt Stancliff else
21015e362b84SMatt Stancliff ERR("Value: %s", entry.value);
21025e362b84SMatt Stancliff quicklistRelease(ql);
21035e362b84SMatt Stancliff }
21045e362b84SMatt Stancliff
2105abdd1414SMatt Stancliff TEST_DESC("index too big +1 from 50 list at fill %d at compress %d",
2106abdd1414SMatt Stancliff f, options[_i]) {
2107abdd1414SMatt Stancliff quicklist *ql = quicklistNew(f, options[_i]);
21085e362b84SMatt Stancliff for (int i = 0; i < 50; i++)
2109abdd1414SMatt Stancliff quicklistPushTail(ql, genstr("hello", i + 1), 32);
21105e362b84SMatt Stancliff quicklistEntry entry;
21115e362b84SMatt Stancliff if (quicklistIndex(ql, 50, &entry))
21125e362b84SMatt Stancliff ERR("Index found at 50 with 50 list: %.*s", entry.sz,
21135e362b84SMatt Stancliff entry.value);
21145e362b84SMatt Stancliff else
21155e362b84SMatt Stancliff OK;
21165e362b84SMatt Stancliff quicklistRelease(ql);
21175e362b84SMatt Stancliff }
21185e362b84SMatt Stancliff }
21195e362b84SMatt Stancliff
21205e362b84SMatt Stancliff TEST("delete range empty list") {
2121abdd1414SMatt Stancliff quicklist *ql = quicklistNew(-2, options[_i]);
21225e362b84SMatt Stancliff quicklistDelRange(ql, 5, 20);
21235e362b84SMatt Stancliff ql_verify(ql, 0, 0, 0, 0);
21245e362b84SMatt Stancliff quicklistRelease(ql);
21255e362b84SMatt Stancliff }
21265e362b84SMatt Stancliff
21275e362b84SMatt Stancliff TEST("delete range of entire node in list of one node") {
2128abdd1414SMatt Stancliff quicklist *ql = quicklistNew(-2, options[_i]);
21295e362b84SMatt Stancliff for (int i = 0; i < 32; i++)
2130abdd1414SMatt Stancliff quicklistPushHead(ql, genstr("hello", i), 32);
21315e362b84SMatt Stancliff ql_verify(ql, 1, 32, 32, 32);
21325e362b84SMatt Stancliff quicklistDelRange(ql, 0, 32);
21335e362b84SMatt Stancliff ql_verify(ql, 0, 0, 0, 0);
21345e362b84SMatt Stancliff quicklistRelease(ql);
21355e362b84SMatt Stancliff }
21365e362b84SMatt Stancliff
21375e362b84SMatt Stancliff TEST("delete range of entire node with overflow counts") {
2138abdd1414SMatt Stancliff quicklist *ql = quicklistNew(-2, options[_i]);
21395e362b84SMatt Stancliff for (int i = 0; i < 32; i++)
2140abdd1414SMatt Stancliff quicklistPushHead(ql, genstr("hello", i), 32);
21415e362b84SMatt Stancliff ql_verify(ql, 1, 32, 32, 32);
21425e362b84SMatt Stancliff quicklistDelRange(ql, 0, 128);
21435e362b84SMatt Stancliff ql_verify(ql, 0, 0, 0, 0);
21445e362b84SMatt Stancliff quicklistRelease(ql);
21455e362b84SMatt Stancliff }
21465e362b84SMatt Stancliff
21475e362b84SMatt Stancliff TEST("delete middle 100 of 500 list") {
2148abdd1414SMatt Stancliff quicklist *ql = quicklistNew(-2, options[_i]);
2149abdd1414SMatt Stancliff quicklistSetFill(ql, 32);
21505e362b84SMatt Stancliff for (int i = 0; i < 500; i++)
2151abdd1414SMatt Stancliff quicklistPushTail(ql, genstr("hello", i + 1), 32);
21525e362b84SMatt Stancliff ql_verify(ql, 16, 500, 32, 20);
21535e362b84SMatt Stancliff quicklistDelRange(ql, 200, 100);
21545e362b84SMatt Stancliff ql_verify(ql, 14, 400, 32, 20);
21555e362b84SMatt Stancliff quicklistRelease(ql);
21565e362b84SMatt Stancliff }
21575e362b84SMatt Stancliff
21585e362b84SMatt Stancliff TEST("delete negative 1 from 500 list") {
2159abdd1414SMatt Stancliff quicklist *ql = quicklistNew(-2, options[_i]);
2160abdd1414SMatt Stancliff quicklistSetFill(ql, 32);
21615e362b84SMatt Stancliff for (int i = 0; i < 500; i++)
2162abdd1414SMatt Stancliff quicklistPushTail(ql, genstr("hello", i + 1), 32);
21635e362b84SMatt Stancliff ql_verify(ql, 16, 500, 32, 20);
21645e362b84SMatt Stancliff quicklistDelRange(ql, -1, 1);
21655e362b84SMatt Stancliff ql_verify(ql, 16, 499, 32, 19);
21665e362b84SMatt Stancliff quicklistRelease(ql);
21675e362b84SMatt Stancliff }
21685e362b84SMatt Stancliff
21695e362b84SMatt Stancliff TEST("delete negative 1 from 500 list with overflow counts") {
2170abdd1414SMatt Stancliff quicklist *ql = quicklistNew(-2, options[_i]);
2171abdd1414SMatt Stancliff quicklistSetFill(ql, 32);
21725e362b84SMatt Stancliff for (int i = 0; i < 500; i++)
2173abdd1414SMatt Stancliff quicklistPushTail(ql, genstr("hello", i + 1), 32);
21745e362b84SMatt Stancliff ql_verify(ql, 16, 500, 32, 20);
21755e362b84SMatt Stancliff quicklistDelRange(ql, -1, 128);
21765e362b84SMatt Stancliff ql_verify(ql, 16, 499, 32, 19);
21775e362b84SMatt Stancliff quicklistRelease(ql);
21785e362b84SMatt Stancliff }
21795e362b84SMatt Stancliff
21805e362b84SMatt Stancliff TEST("delete negative 100 from 500 list") {
2181abdd1414SMatt Stancliff quicklist *ql = quicklistNew(-2, options[_i]);
2182abdd1414SMatt Stancliff quicklistSetFill(ql, 32);
21835e362b84SMatt Stancliff for (int i = 0; i < 500; i++)
2184abdd1414SMatt Stancliff quicklistPushTail(ql, genstr("hello", i + 1), 32);
21855e362b84SMatt Stancliff quicklistDelRange(ql, -100, 100);
21865e362b84SMatt Stancliff ql_verify(ql, 13, 400, 32, 16);
21875e362b84SMatt Stancliff quicklistRelease(ql);
21885e362b84SMatt Stancliff }
21895e362b84SMatt Stancliff
21905e362b84SMatt Stancliff TEST("delete -10 count 5 from 50 list") {
2191abdd1414SMatt Stancliff quicklist *ql = quicklistNew(-2, options[_i]);
2192abdd1414SMatt Stancliff quicklistSetFill(ql, 32);
21935e362b84SMatt Stancliff for (int i = 0; i < 50; i++)
2194abdd1414SMatt Stancliff quicklistPushTail(ql, genstr("hello", i + 1), 32);
21955e362b84SMatt Stancliff ql_verify(ql, 2, 50, 32, 18);
21965e362b84SMatt Stancliff quicklistDelRange(ql, -10, 5);
21975e362b84SMatt Stancliff ql_verify(ql, 2, 45, 32, 13);
21985e362b84SMatt Stancliff quicklistRelease(ql);
21995e362b84SMatt Stancliff }
22005e362b84SMatt Stancliff
22015e362b84SMatt Stancliff TEST("numbers only list read") {
2202abdd1414SMatt Stancliff quicklist *ql = quicklistNew(-2, options[_i]);
2203abdd1414SMatt Stancliff quicklistPushTail(ql, "1111", 4);
2204abdd1414SMatt Stancliff quicklistPushTail(ql, "2222", 4);
2205abdd1414SMatt Stancliff quicklistPushTail(ql, "3333", 4);
2206abdd1414SMatt Stancliff quicklistPushTail(ql, "4444", 4);
22075e362b84SMatt Stancliff ql_verify(ql, 1, 4, 4, 4);
22085e362b84SMatt Stancliff quicklistEntry entry;
22095e362b84SMatt Stancliff quicklistIndex(ql, 0, &entry);
22105e362b84SMatt Stancliff if (entry.longval != 1111)
22115e362b84SMatt Stancliff ERR("Not 1111, %lld", entry.longval);
22125e362b84SMatt Stancliff quicklistIndex(ql, 1, &entry);
22135e362b84SMatt Stancliff if (entry.longval != 2222)
22145e362b84SMatt Stancliff ERR("Not 2222, %lld", entry.longval);
22155e362b84SMatt Stancliff quicklistIndex(ql, 2, &entry);
22165e362b84SMatt Stancliff if (entry.longval != 3333)
22175e362b84SMatt Stancliff ERR("Not 3333, %lld", entry.longval);
22185e362b84SMatt Stancliff quicklistIndex(ql, 3, &entry);
22195e362b84SMatt Stancliff if (entry.longval != 4444)
22205e362b84SMatt Stancliff ERR("Not 4444, %lld", entry.longval);
22215e362b84SMatt Stancliff if (quicklistIndex(ql, 4, &entry))
22225e362b84SMatt Stancliff ERR("Index past elements: %lld", entry.longval);
22235e362b84SMatt Stancliff quicklistIndex(ql, -1, &entry);
22245e362b84SMatt Stancliff if (entry.longval != 4444)
22255e362b84SMatt Stancliff ERR("Not 4444 (reverse), %lld", entry.longval);
22265e362b84SMatt Stancliff quicklistIndex(ql, -2, &entry);
22275e362b84SMatt Stancliff if (entry.longval != 3333)
22285e362b84SMatt Stancliff ERR("Not 3333 (reverse), %lld", entry.longval);
22295e362b84SMatt Stancliff quicklistIndex(ql, -3, &entry);
22305e362b84SMatt Stancliff if (entry.longval != 2222)
22315e362b84SMatt Stancliff ERR("Not 2222 (reverse), %lld", entry.longval);
22325e362b84SMatt Stancliff quicklistIndex(ql, -4, &entry);
22335e362b84SMatt Stancliff if (entry.longval != 1111)
22345e362b84SMatt Stancliff ERR("Not 1111 (reverse), %lld", entry.longval);
22355e362b84SMatt Stancliff if (quicklistIndex(ql, -5, &entry))
22365e362b84SMatt Stancliff ERR("Index past elements (reverse), %lld", entry.longval);
22375e362b84SMatt Stancliff quicklistRelease(ql);
22385e362b84SMatt Stancliff }
22395e362b84SMatt Stancliff
22405e362b84SMatt Stancliff TEST("numbers larger list read") {
2241abdd1414SMatt Stancliff quicklist *ql = quicklistNew(-2, options[_i]);
2242abdd1414SMatt Stancliff quicklistSetFill(ql, 32);
22435e362b84SMatt Stancliff char num[32];
22445e362b84SMatt Stancliff long long nums[5000];
22455e362b84SMatt Stancliff for (int i = 0; i < 5000; i++) {
22465e362b84SMatt Stancliff nums[i] = -5157318210846258176 + i;
22475e362b84SMatt Stancliff int sz = ll2string(num, sizeof(num), nums[i]);
2248abdd1414SMatt Stancliff quicklistPushTail(ql, num, sz);
22495e362b84SMatt Stancliff }
2250abdd1414SMatt Stancliff quicklistPushTail(ql, "xxxxxxxxxxxxxxxxxxxx", 20);
22515e362b84SMatt Stancliff quicklistEntry entry;
22525e362b84SMatt Stancliff for (int i = 0; i < 5000; i++) {
22535e362b84SMatt Stancliff quicklistIndex(ql, i, &entry);
22545e362b84SMatt Stancliff if (entry.longval != nums[i])
22555e362b84SMatt Stancliff ERR("[%d] Not longval %lld but rather %lld", i, nums[i],
22565e362b84SMatt Stancliff entry.longval);
22575e362b84SMatt Stancliff entry.longval = 0xdeadbeef;
22585e362b84SMatt Stancliff }
22595e362b84SMatt Stancliff quicklistIndex(ql, 5000, &entry);
22605e362b84SMatt Stancliff if (strncmp((char *)entry.value, "xxxxxxxxxxxxxxxxxxxx", 20))
22615e362b84SMatt Stancliff ERR("String val not match: %s", entry.value);
22625e362b84SMatt Stancliff ql_verify(ql, 157, 5001, 32, 9);
22635e362b84SMatt Stancliff quicklistRelease(ql);
22645e362b84SMatt Stancliff }
22655e362b84SMatt Stancliff
22665e362b84SMatt Stancliff TEST("numbers larger list read B") {
2267abdd1414SMatt Stancliff quicklist *ql = quicklistNew(-2, options[_i]);
2268abdd1414SMatt Stancliff quicklistPushTail(ql, "99", 2);
2269abdd1414SMatt Stancliff quicklistPushTail(ql, "98", 2);
2270abdd1414SMatt Stancliff quicklistPushTail(ql, "xxxxxxxxxxxxxxxxxxxx", 20);
2271abdd1414SMatt Stancliff quicklistPushTail(ql, "96", 2);
2272abdd1414SMatt Stancliff quicklistPushTail(ql, "95", 2);
22735e362b84SMatt Stancliff quicklistReplaceAtIndex(ql, 1, "foo", 3);
22745e362b84SMatt Stancliff quicklistReplaceAtIndex(ql, -1, "bar", 3);
22755e362b84SMatt Stancliff quicklistRelease(ql);
22765e362b84SMatt Stancliff OK;
22775e362b84SMatt Stancliff }
22785e362b84SMatt Stancliff
2279c6bf20c2SMatt Stancliff for (int f = optimize_start; f < 16; f++) {
2280abdd1414SMatt Stancliff TEST_DESC("lrem test at fill %d at compress %d", f, options[_i]) {
2281abdd1414SMatt Stancliff quicklist *ql = quicklistNew(f, options[_i]);
22825e362b84SMatt Stancliff char *words[] = {"abc", "foo", "bar", "foobar", "foobared",
22835e362b84SMatt Stancliff "zap", "bar", "test", "foo"};
22845e362b84SMatt Stancliff char *result[] = {"abc", "foo", "foobar", "foobared",
22855e362b84SMatt Stancliff "zap", "test", "foo"};
22865e362b84SMatt Stancliff char *resultB[] = {"abc", "foo", "foobar",
22875e362b84SMatt Stancliff "foobared", "zap", "test"};
22885e362b84SMatt Stancliff for (int i = 0; i < 9; i++)
2289abdd1414SMatt Stancliff quicklistPushTail(ql, words[i], strlen(words[i]));
22905e362b84SMatt Stancliff
22915e362b84SMatt Stancliff /* lrem 0 bar */
22925e362b84SMatt Stancliff quicklistIter *iter = quicklistGetIterator(ql, AL_START_HEAD);
22935e362b84SMatt Stancliff quicklistEntry entry;
22945e362b84SMatt Stancliff int i = 0;
22955e362b84SMatt Stancliff while (quicklistNext(iter, &entry)) {
22965e362b84SMatt Stancliff if (quicklistCompare(entry.zi, (unsigned char *)"bar", 3)) {
22975e362b84SMatt Stancliff quicklistDelEntry(iter, &entry);
22985e362b84SMatt Stancliff }
22995e362b84SMatt Stancliff i++;
23005e362b84SMatt Stancliff }
23015e362b84SMatt Stancliff quicklistReleaseIterator(iter);
23025e362b84SMatt Stancliff
23035e362b84SMatt Stancliff /* check result of lrem 0 bar */
23045e362b84SMatt Stancliff iter = quicklistGetIterator(ql, AL_START_HEAD);
23055e362b84SMatt Stancliff i = 0;
23065e362b84SMatt Stancliff int ok = 1;
23075e362b84SMatt Stancliff while (quicklistNext(iter, &entry)) {
2308abdd1414SMatt Stancliff /* Result must be: abc, foo, foobar, foobared, zap, test,
2309abdd1414SMatt Stancliff * foo */
23105e362b84SMatt Stancliff if (strncmp((char *)entry.value, result[i], entry.sz)) {
2311abdd1414SMatt Stancliff ERR("No match at position %d, got %.*s instead of %s",
2312abdd1414SMatt Stancliff i, entry.sz, entry.value, result[i]);
23135e362b84SMatt Stancliff ok = 0;
23145e362b84SMatt Stancliff }
23155e362b84SMatt Stancliff i++;
23165e362b84SMatt Stancliff }
23175e362b84SMatt Stancliff quicklistReleaseIterator(iter);
23185e362b84SMatt Stancliff
2319abdd1414SMatt Stancliff quicklistPushTail(ql, "foo", 3);
23205e362b84SMatt Stancliff
23215e362b84SMatt Stancliff /* lrem -2 foo */
23225e362b84SMatt Stancliff iter = quicklistGetIterator(ql, AL_START_TAIL);
23235e362b84SMatt Stancliff i = 0;
23245e362b84SMatt Stancliff int del = 2;
23255e362b84SMatt Stancliff while (quicklistNext(iter, &entry)) {
23265e362b84SMatt Stancliff if (quicklistCompare(entry.zi, (unsigned char *)"foo", 3)) {
23275e362b84SMatt Stancliff quicklistDelEntry(iter, &entry);
23285e362b84SMatt Stancliff del--;
23295e362b84SMatt Stancliff }
23305e362b84SMatt Stancliff if (!del)
23315e362b84SMatt Stancliff break;
23325e362b84SMatt Stancliff i++;
23335e362b84SMatt Stancliff }
23345e362b84SMatt Stancliff quicklistReleaseIterator(iter);
23355e362b84SMatt Stancliff
23365e362b84SMatt Stancliff /* check result of lrem -2 foo */
2337abdd1414SMatt Stancliff /* (we're ignoring the '2' part and still deleting all foo
2338abdd1414SMatt Stancliff * because
23395e362b84SMatt Stancliff * we only have two foo) */
23405e362b84SMatt Stancliff iter = quicklistGetIterator(ql, AL_START_TAIL);
23415e362b84SMatt Stancliff i = 0;
23425e362b84SMatt Stancliff size_t resB = sizeof(resultB) / sizeof(*resultB);
23435e362b84SMatt Stancliff while (quicklistNext(iter, &entry)) {
2344abdd1414SMatt Stancliff /* Result must be: abc, foo, foobar, foobared, zap, test,
2345abdd1414SMatt Stancliff * foo */
23465e362b84SMatt Stancliff if (strncmp((char *)entry.value, resultB[resB - 1 - i],
23475e362b84SMatt Stancliff entry.sz)) {
2348abdd1414SMatt Stancliff ERR("No match at position %d, got %.*s instead of %s",
2349abdd1414SMatt Stancliff i, entry.sz, entry.value, resultB[resB - 1 - i]);
23505e362b84SMatt Stancliff ok = 0;
23515e362b84SMatt Stancliff }
23525e362b84SMatt Stancliff i++;
23535e362b84SMatt Stancliff }
23545e362b84SMatt Stancliff
23555e362b84SMatt Stancliff quicklistReleaseIterator(iter);
23565e362b84SMatt Stancliff /* final result of all tests */
23575e362b84SMatt Stancliff if (ok)
23585e362b84SMatt Stancliff OK;
23595e362b84SMatt Stancliff quicklistRelease(ql);
23605e362b84SMatt Stancliff }
23615e362b84SMatt Stancliff }
23625e362b84SMatt Stancliff
2363c6bf20c2SMatt Stancliff for (int f = optimize_start; f < 16; f++) {
2364abdd1414SMatt Stancliff TEST_DESC("iterate reverse + delete at fill %d at compress %d", f,
2365abdd1414SMatt Stancliff options[_i]) {
2366abdd1414SMatt Stancliff quicklist *ql = quicklistNew(f, options[_i]);
2367abdd1414SMatt Stancliff quicklistPushTail(ql, "abc", 3);
2368abdd1414SMatt Stancliff quicklistPushTail(ql, "def", 3);
2369abdd1414SMatt Stancliff quicklistPushTail(ql, "hij", 3);
2370abdd1414SMatt Stancliff quicklistPushTail(ql, "jkl", 3);
2371abdd1414SMatt Stancliff quicklistPushTail(ql, "oop", 3);
23725e362b84SMatt Stancliff
23735e362b84SMatt Stancliff quicklistEntry entry;
23745e362b84SMatt Stancliff quicklistIter *iter = quicklistGetIterator(ql, AL_START_TAIL);
23755e362b84SMatt Stancliff int i = 0;
23765e362b84SMatt Stancliff while (quicklistNext(iter, &entry)) {
23775e362b84SMatt Stancliff if (quicklistCompare(entry.zi, (unsigned char *)"hij", 3)) {
23785e362b84SMatt Stancliff quicklistDelEntry(iter, &entry);
23795e362b84SMatt Stancliff }
23805e362b84SMatt Stancliff i++;
23815e362b84SMatt Stancliff }
23825e362b84SMatt Stancliff quicklistReleaseIterator(iter);
23835e362b84SMatt Stancliff
23845e362b84SMatt Stancliff if (i != 5)
23855e362b84SMatt Stancliff ERR("Didn't iterate 5 times, iterated %d times.", i);
23865e362b84SMatt Stancliff
23875e362b84SMatt Stancliff /* Check results after deletion of "hij" */
23885e362b84SMatt Stancliff iter = quicklistGetIterator(ql, AL_START_HEAD);
23895e362b84SMatt Stancliff i = 0;
23905e362b84SMatt Stancliff char *vals[] = {"abc", "def", "jkl", "oop"};
23915e362b84SMatt Stancliff while (quicklistNext(iter, &entry)) {
2392abdd1414SMatt Stancliff if (!quicklistCompare(entry.zi, (unsigned char *)vals[i],
2393abdd1414SMatt Stancliff 3)) {
23945e362b84SMatt Stancliff ERR("Value at %d didn't match %s\n", i, vals[i]);
23955e362b84SMatt Stancliff }
23965e362b84SMatt Stancliff i++;
23975e362b84SMatt Stancliff }
23985e362b84SMatt Stancliff quicklistReleaseIterator(iter);
23995e362b84SMatt Stancliff quicklistRelease(ql);
24005e362b84SMatt Stancliff }
24015e362b84SMatt Stancliff }
24025e362b84SMatt Stancliff
2403c6bf20c2SMatt Stancliff for (int f = optimize_start; f < 800; f++) {
2404abdd1414SMatt Stancliff TEST_DESC("iterator at index test at fill %d at compress %d", f,
2405abdd1414SMatt Stancliff options[_i]) {
2406abdd1414SMatt Stancliff quicklist *ql = quicklistNew(f, options[_i]);
24075e362b84SMatt Stancliff char num[32];
24085e362b84SMatt Stancliff long long nums[5000];
24095e362b84SMatt Stancliff for (int i = 0; i < 760; i++) {
24105e362b84SMatt Stancliff nums[i] = -5157318210846258176 + i;
24115e362b84SMatt Stancliff int sz = ll2string(num, sizeof(num), nums[i]);
2412abdd1414SMatt Stancliff quicklistPushTail(ql, num, sz);
24135e362b84SMatt Stancliff }
24145e362b84SMatt Stancliff
24155e362b84SMatt Stancliff quicklistEntry entry;
24165e362b84SMatt Stancliff quicklistIter *iter =
24175e362b84SMatt Stancliff quicklistGetIteratorAtIdx(ql, AL_START_HEAD, 437);
24185e362b84SMatt Stancliff int i = 437;
24195e362b84SMatt Stancliff while (quicklistNext(iter, &entry)) {
24205e362b84SMatt Stancliff if (entry.longval != nums[i])
2421abdd1414SMatt Stancliff ERR("Expected %lld, but got %lld", entry.longval,
2422abdd1414SMatt Stancliff nums[i]);
24235e362b84SMatt Stancliff i++;
24245e362b84SMatt Stancliff }
24255e362b84SMatt Stancliff quicklistReleaseIterator(iter);
24265e362b84SMatt Stancliff quicklistRelease(ql);
24275e362b84SMatt Stancliff }
24285e362b84SMatt Stancliff }
24295e362b84SMatt Stancliff
2430c6bf20c2SMatt Stancliff for (int f = optimize_start; f < 40; f++) {
2431abdd1414SMatt Stancliff TEST_DESC("ltrim test A at fill %d at compress %d", f,
2432abdd1414SMatt Stancliff options[_i]) {
2433abdd1414SMatt Stancliff quicklist *ql = quicklistNew(f, options[_i]);
24345e362b84SMatt Stancliff char num[32];
24355e362b84SMatt Stancliff long long nums[5000];
24365e362b84SMatt Stancliff for (int i = 0; i < 32; i++) {
24375e362b84SMatt Stancliff nums[i] = -5157318210846258176 + i;
24385e362b84SMatt Stancliff int sz = ll2string(num, sizeof(num), nums[i]);
2439abdd1414SMatt Stancliff quicklistPushTail(ql, num, sz);
24405e362b84SMatt Stancliff }
24415e362b84SMatt Stancliff if (f == 32)
24425e362b84SMatt Stancliff ql_verify(ql, 1, 32, 32, 32);
24435e362b84SMatt Stancliff /* ltrim 25 53 (keep [25,32] inclusive = 7 remaining) */
24445e362b84SMatt Stancliff quicklistDelRange(ql, 0, 25);
24455e362b84SMatt Stancliff quicklistDelRange(ql, 0, 0);
24465e362b84SMatt Stancliff quicklistEntry entry;
24475e362b84SMatt Stancliff for (int i = 0; i < 7; i++) {
24485e362b84SMatt Stancliff quicklistIndex(ql, i, &entry);
24495e362b84SMatt Stancliff if (entry.longval != nums[25 + i])
2450abdd1414SMatt Stancliff ERR("Deleted invalid range! Expected %lld but got "
2451abdd1414SMatt Stancliff "%lld",
24525e362b84SMatt Stancliff entry.longval, nums[25 + i]);
24535e362b84SMatt Stancliff }
24545e362b84SMatt Stancliff if (f == 32)
24555e362b84SMatt Stancliff ql_verify(ql, 1, 7, 7, 7);
24565e362b84SMatt Stancliff quicklistRelease(ql);
24575e362b84SMatt Stancliff }
24585e362b84SMatt Stancliff }
24595e362b84SMatt Stancliff
2460c6bf20c2SMatt Stancliff for (int f = optimize_start; f < 40; f++) {
2461abdd1414SMatt Stancliff TEST_DESC("ltrim test B at fill %d at compress %d", f,
2462abdd1414SMatt Stancliff options[_i]) {
2463abdd1414SMatt Stancliff /* Force-disable compression because our 33 sequential
2464abdd1414SMatt Stancliff * integers don't compress and the check always fails. */
2465abdd1414SMatt Stancliff quicklist *ql = quicklistNew(f, QUICKLIST_NOCOMPRESS);
24665e362b84SMatt Stancliff char num[32];
24675e362b84SMatt Stancliff long long nums[5000];
24685e362b84SMatt Stancliff for (int i = 0; i < 33; i++) {
24695e362b84SMatt Stancliff nums[i] = i;
24705e362b84SMatt Stancliff int sz = ll2string(num, sizeof(num), nums[i]);
2471abdd1414SMatt Stancliff quicklistPushTail(ql, num, sz);
24725e362b84SMatt Stancliff }
24735e362b84SMatt Stancliff if (f == 32)
24745e362b84SMatt Stancliff ql_verify(ql, 2, 33, 32, 1);
24755e362b84SMatt Stancliff /* ltrim 5 16 (keep [5,16] inclusive = 12 remaining) */
24765e362b84SMatt Stancliff quicklistDelRange(ql, 0, 5);
24775e362b84SMatt Stancliff quicklistDelRange(ql, -16, 16);
24785e362b84SMatt Stancliff if (f == 32)
24795e362b84SMatt Stancliff ql_verify(ql, 1, 12, 12, 12);
24805e362b84SMatt Stancliff quicklistEntry entry;
24815e362b84SMatt Stancliff quicklistIndex(ql, 0, &entry);
24825e362b84SMatt Stancliff if (entry.longval != 5)
24835e362b84SMatt Stancliff ERR("A: longval not 5, but %lld", entry.longval);
24845e362b84SMatt Stancliff else
24855e362b84SMatt Stancliff OK;
24865e362b84SMatt Stancliff quicklistIndex(ql, -1, &entry);
24875e362b84SMatt Stancliff if (entry.longval != 16)
24885e362b84SMatt Stancliff ERR("B! got instead: %lld", entry.longval);
24895e362b84SMatt Stancliff else
24905e362b84SMatt Stancliff OK;
2491abdd1414SMatt Stancliff quicklistPushTail(ql, "bobobob", 7);
24925e362b84SMatt Stancliff quicklistIndex(ql, -1, &entry);
24935e362b84SMatt Stancliff if (strncmp((char *)entry.value, "bobobob", 7))
2494abdd1414SMatt Stancliff ERR("Tail doesn't match bobobob, it's %.*s instead",
2495abdd1414SMatt Stancliff entry.sz, entry.value);
24965e362b84SMatt Stancliff for (int i = 0; i < 12; i++) {
24975e362b84SMatt Stancliff quicklistIndex(ql, i, &entry);
24985e362b84SMatt Stancliff if (entry.longval != nums[5 + i])
2499abdd1414SMatt Stancliff ERR("Deleted invalid range! Expected %lld but got "
2500abdd1414SMatt Stancliff "%lld",
25015e362b84SMatt Stancliff entry.longval, nums[5 + i]);
25025e362b84SMatt Stancliff }
25035e362b84SMatt Stancliff quicklistRelease(ql);
25045e362b84SMatt Stancliff }
25055e362b84SMatt Stancliff }
25065e362b84SMatt Stancliff
2507c6bf20c2SMatt Stancliff for (int f = optimize_start; f < 40; f++) {
2508abdd1414SMatt Stancliff TEST_DESC("ltrim test C at fill %d at compress %d", f,
2509abdd1414SMatt Stancliff options[_i]) {
2510abdd1414SMatt Stancliff quicklist *ql = quicklistNew(f, options[_i]);
25115e362b84SMatt Stancliff char num[32];
25125e362b84SMatt Stancliff long long nums[5000];
25135e362b84SMatt Stancliff for (int i = 0; i < 33; i++) {
25145e362b84SMatt Stancliff nums[i] = -5157318210846258176 + i;
25155e362b84SMatt Stancliff int sz = ll2string(num, sizeof(num), nums[i]);
2516abdd1414SMatt Stancliff quicklistPushTail(ql, num, sz);
25175e362b84SMatt Stancliff }
25185e362b84SMatt Stancliff if (f == 32)
25195e362b84SMatt Stancliff ql_verify(ql, 2, 33, 32, 1);
25205e362b84SMatt Stancliff /* ltrim 3 3 (keep [3,3] inclusive = 1 remaining) */
25215e362b84SMatt Stancliff quicklistDelRange(ql, 0, 3);
2522abdd1414SMatt Stancliff quicklistDelRange(ql, -29,
2523abdd1414SMatt Stancliff 4000); /* make sure not loop forever */
25245e362b84SMatt Stancliff if (f == 32)
25255e362b84SMatt Stancliff ql_verify(ql, 1, 1, 1, 1);
25265e362b84SMatt Stancliff quicklistEntry entry;
25275e362b84SMatt Stancliff quicklistIndex(ql, 0, &entry);
25285e362b84SMatt Stancliff if (entry.longval != -5157318210846258173)
25295e362b84SMatt Stancliff ERROR;
25305e362b84SMatt Stancliff else
25315e362b84SMatt Stancliff OK;
25325e362b84SMatt Stancliff quicklistRelease(ql);
25335e362b84SMatt Stancliff }
25345e362b84SMatt Stancliff }
25355e362b84SMatt Stancliff
2536c6bf20c2SMatt Stancliff for (int f = optimize_start; f < 40; f++) {
2537abdd1414SMatt Stancliff TEST_DESC("ltrim test D at fill %d at compress %d", f,
2538abdd1414SMatt Stancliff options[_i]) {
2539abdd1414SMatt Stancliff quicklist *ql = quicklistNew(f, options[_i]);
25405e362b84SMatt Stancliff char num[32];
25415e362b84SMatt Stancliff long long nums[5000];
25425e362b84SMatt Stancliff for (int i = 0; i < 33; i++) {
25435e362b84SMatt Stancliff nums[i] = -5157318210846258176 + i;
25445e362b84SMatt Stancliff int sz = ll2string(num, sizeof(num), nums[i]);
2545abdd1414SMatt Stancliff quicklistPushTail(ql, num, sz);
25465e362b84SMatt Stancliff }
25475e362b84SMatt Stancliff if (f == 32)
25485e362b84SMatt Stancliff ql_verify(ql, 2, 33, 32, 1);
25495e362b84SMatt Stancliff quicklistDelRange(ql, -12, 3);
25505e362b84SMatt Stancliff if (ql->count != 30)
25515e362b84SMatt Stancliff ERR("Didn't delete exactly three elements! Count is: %lu",
25525e362b84SMatt Stancliff ql->count);
25535e362b84SMatt Stancliff quicklistRelease(ql);
25545e362b84SMatt Stancliff }
25555e362b84SMatt Stancliff }
25565e362b84SMatt Stancliff
2557c6bf20c2SMatt Stancliff for (int f = optimize_start; f < 72; f++) {
2558abdd1414SMatt Stancliff TEST_DESC("create quicklist from ziplist at fill %d at compress %d",
2559abdd1414SMatt Stancliff f, options[_i]) {
25605e362b84SMatt Stancliff unsigned char *zl = ziplistNew();
2561abdd1414SMatt Stancliff long long nums[64];
2562abdd1414SMatt Stancliff char num[64];
25635e362b84SMatt Stancliff for (int i = 0; i < 33; i++) {
25645e362b84SMatt Stancliff nums[i] = -5157318210846258176 + i;
25655e362b84SMatt Stancliff int sz = ll2string(num, sizeof(num), nums[i]);
2566abdd1414SMatt Stancliff zl =
2567abdd1414SMatt Stancliff ziplistPush(zl, (unsigned char *)num, sz, ZIPLIST_TAIL);
25685e362b84SMatt Stancliff }
25695e362b84SMatt Stancliff for (int i = 0; i < 33; i++) {
2570abdd1414SMatt Stancliff zl = ziplistPush(zl, (unsigned char *)genstr("hello", i),
2571abdd1414SMatt Stancliff 32, ZIPLIST_TAIL);
25725e362b84SMatt Stancliff }
2573abdd1414SMatt Stancliff quicklist *ql = quicklistCreateFromZiplist(f, options[_i], zl);
25745e362b84SMatt Stancliff if (f == 1)
25755e362b84SMatt Stancliff ql_verify(ql, 66, 66, 1, 1);
25765e362b84SMatt Stancliff else if (f == 32)
25775e362b84SMatt Stancliff ql_verify(ql, 3, 66, 32, 2);
25785e362b84SMatt Stancliff else if (f == 66)
25795e362b84SMatt Stancliff ql_verify(ql, 1, 66, 66, 66);
25805e362b84SMatt Stancliff quicklistRelease(ql);
25815e362b84SMatt Stancliff }
25825e362b84SMatt Stancliff }
25835e362b84SMatt Stancliff
2584abdd1414SMatt Stancliff long long stop = mstime();
2585abdd1414SMatt Stancliff runtime[_i] = stop - start;
2586abdd1414SMatt Stancliff }
2587abdd1414SMatt Stancliff
2588abdd1414SMatt Stancliff /* Run a longer test of compression depth outside of primary test loop. */
2589abdd1414SMatt Stancliff int list_sizes[] = {250, 251, 500, 999, 1000};
2590abdd1414SMatt Stancliff long long start = mstime();
2591abdd1414SMatt Stancliff for (int list = 0; list < (int)(sizeof(list_sizes) / sizeof(*list_sizes));
2592abdd1414SMatt Stancliff list++) {
2593abdd1414SMatt Stancliff for (int f = optimize_start; f < 128; f++) {
2594abdd1414SMatt Stancliff for (int depth = 1; depth < 40; depth++) {
2595abdd1414SMatt Stancliff /* skip over many redundant test cases */
2596abdd1414SMatt Stancliff TEST_DESC("verify specific compression of interior nodes with "
2597abdd1414SMatt Stancliff "%d list "
2598abdd1414SMatt Stancliff "at fill %d at compress %d",
2599abdd1414SMatt Stancliff list_sizes[list], f, depth) {
2600abdd1414SMatt Stancliff quicklist *ql = quicklistNew(f, depth);
2601abdd1414SMatt Stancliff for (int i = 0; i < list_sizes[list]; i++) {
2602abdd1414SMatt Stancliff quicklistPushTail(ql, genstr("hello TAIL", i + 1), 64);
2603abdd1414SMatt Stancliff quicklistPushHead(ql, genstr("hello HEAD", i + 1), 64);
2604abdd1414SMatt Stancliff }
2605abdd1414SMatt Stancliff
2606abdd1414SMatt Stancliff quicklistNode *node = ql->head;
2607abdd1414SMatt Stancliff unsigned int low_raw = ql->compress;
2608abdd1414SMatt Stancliff unsigned int high_raw = ql->len - ql->compress;
2609abdd1414SMatt Stancliff
2610abdd1414SMatt Stancliff for (unsigned int at = 0; at < ql->len;
2611abdd1414SMatt Stancliff at++, node = node->next) {
2612abdd1414SMatt Stancliff if (at < low_raw || at >= high_raw) {
2613abdd1414SMatt Stancliff if (node->encoding != QUICKLIST_NODE_ENCODING_RAW) {
2614abdd1414SMatt Stancliff ERR("Incorrect compression: node %d is "
2615abdd1414SMatt Stancliff "compressed at depth %d ((%u, %u); total "
2616abdd1414SMatt Stancliff "nodes: %u; size: %u)",
2617abdd1414SMatt Stancliff at, depth, low_raw, high_raw, ql->len,
2618abdd1414SMatt Stancliff node->sz);
2619abdd1414SMatt Stancliff }
2620abdd1414SMatt Stancliff } else {
2621abdd1414SMatt Stancliff if (node->encoding != QUICKLIST_NODE_ENCODING_LZF) {
2622abdd1414SMatt Stancliff ERR("Incorrect non-compression: node %d is NOT "
2623abdd1414SMatt Stancliff "compressed at depth %d ((%u, %u); total "
2624abdd1414SMatt Stancliff "nodes: %u; size: %u; attempted: %d)",
2625abdd1414SMatt Stancliff at, depth, low_raw, high_raw, ql->len,
2626abdd1414SMatt Stancliff node->sz, node->attempted_compress);
2627abdd1414SMatt Stancliff }
2628abdd1414SMatt Stancliff }
2629abdd1414SMatt Stancliff }
2630abdd1414SMatt Stancliff quicklistRelease(ql);
2631abdd1414SMatt Stancliff }
2632abdd1414SMatt Stancliff }
2633abdd1414SMatt Stancliff }
2634abdd1414SMatt Stancliff }
2635abdd1414SMatt Stancliff long long stop = mstime();
2636abdd1414SMatt Stancliff
2637abdd1414SMatt Stancliff printf("\n");
2638abdd1414SMatt Stancliff for (size_t i = 0; i < option_count; i++)
2639abdd1414SMatt Stancliff printf("Test Loop %02d: %0.2f seconds.\n", options[i],
2640abdd1414SMatt Stancliff (float)runtime[i] / 1000);
2641abdd1414SMatt Stancliff printf("Compressions: %0.2f seconds.\n", (float)(stop - start) / 1000);
2642abdd1414SMatt Stancliff printf("\n");
2643abdd1414SMatt Stancliff
26445e362b84SMatt Stancliff if (!err)
26455e362b84SMatt Stancliff printf("ALL TESTS PASSED!\n");
26465e362b84SMatt Stancliff else
26475e362b84SMatt Stancliff ERR("Sorry, not all tests passed! In fact, %d tests failed.", err);
26485e362b84SMatt Stancliff
26495e362b84SMatt Stancliff return err;
26505e362b84SMatt Stancliff }
26515e362b84SMatt Stancliff #endif
2652