LCOV - code coverage report
Current view: directory - redis/src - ziplist.c (source / functions) Found Hit Coverage
Test: redis.info Lines: 316 266 84.2 %
Date: 2012-04-04 Functions: 25 24 96.0 %
Colors: not hit hit

       1                 : /* The ziplist is a specially encoded dually linked list that is designed
       2                 :  * to be very memory efficient. It stores both strings and integer values,
       3                 :  * where integers are encoded as actual integers instead of a series of
       4                 :  * characters. It allows push and pop operations on either side of the list
       5                 :  * in O(1) time. However, because every operation requires a reallocation of
       6                 :  * the memory used by the ziplist, the actual complexity is related to the
       7                 :  * amount of memory used by the ziplist.
       8                 :  *
       9                 :  * ----------------------------------------------------------------------------
      10                 :  *
      11                 :  * ZIPLIST OVERALL LAYOUT:
      12                 :  * The general layout of the ziplist is as follows:
      13                 :  * <zlbytes><zltail><zllen><entry><entry><zlend>
      14                 :  *
      15                 :  * <zlbytes> is an unsigned integer to hold the number of bytes that the
      16                 :  * ziplist occupies. This value needs to be stored to be able to resize the
      17                 :  * entire structure without the need to traverse it first.
      18                 :  *
      19                 :  * <zltail> is the offset to the last entry in the list. This allows a pop
      20                 :  * operation on the far side of the list without the need for full traversal.
      21                 :  *
      22                 :  * <zllen> is the number of entries.When this value is larger than 2**16-2,
      23                 :  * we need to traverse the entire list to know how many items it holds.
      24                 :  *
      25                 :  * <zlend> is a single byte special value, equal to 255, which indicates the
      26                 :  * end of the list.
      27                 :  *
      28                 :  * ZIPLIST ENTRIES:
      29                 :  * Every entry in the ziplist is prefixed by a header that contains two pieces
      30                 :  * of information. First, the length of the previous entry is stored to be
      31                 :  * able to traverse the list from back to front. Second, the encoding with an
      32                 :  * optional string length of the entry itself is stored.
      33                 :  *
      34                 :  * The length of the previous entry is encoded in the following way:
      35                 :  * If this length is smaller than 254 bytes, it will only consume a single
      36                 :  * byte that takes the length as value. When the length is greater than or
      37                 :  * equal to 254, it will consume 5 bytes. The first byte is set to 254 to
      38                 :  * indicate a larger value is following. The remaining 4 bytes take the
      39                 :  * length of the previous entry as value.
      40                 :  *
      41                 :  * The other header field of the entry itself depends on the contents of the
      42                 :  * entry. When the entry is a string, the first 2 bits of this header will hold
      43                 :  * the type of encoding used to store the length of the string, followed by the
      44                 :  * actual length of the string. When the entry is an integer the first 2 bits
      45                 :  * are both set to 1. The following 2 bits are used to specify what kind of
      46                 :  * integer will be stored after this header. An overview of the different
      47                 :  * types and encodings is as follows:
      48                 :  *
      49                 :  * |00pppppp| - 1 byte
      50                 :  *      String value with length less than or equal to 63 bytes (6 bits).
      51                 :  * |01pppppp|qqqqqqqq| - 2 bytes
      52                 :  *      String value with length less than or equal to 16383 bytes (14 bits).
      53                 :  * |10______|qqqqqqqq|rrrrrrrr|ssssssss|tttttttt| - 5 bytes
      54                 :  *      String value with length greater than or equal to 16384 bytes.
      55                 :  * |1100____| - 1 byte
      56                 :  *      Integer encoded as int16_t (2 bytes).
      57                 :  * |1101____| - 1 byte
      58                 :  *      Integer encoded as int32_t (4 bytes).
      59                 :  * |1110____| - 1 byte
      60                 :  *      Integer encoded as int64_t (8 bytes).
      61                 :  */
      62                 : 
      63                 : #include <stdio.h>
      64                 : #include <stdlib.h>
      65                 : #include <string.h>
      66                 : #include <stdint.h>
      67                 : #include <assert.h>
      68                 : #include <limits.h>
      69                 : #include "zmalloc.h"
      70                 : #include "util.h"
      71                 : #include "ziplist.h"
      72                 : #include "endianconv.h"
      73                 : 
      74                 : #define ZIP_END 255
      75                 : #define ZIP_BIGLEN 254
      76                 : 
      77                 : /* Different encoding/length possibilities */
      78                 : #define ZIP_STR_MASK (0xc0)
      79                 : #define ZIP_INT_MASK (0x30)
      80                 : #define ZIP_STR_06B (0 << 6)
      81                 : #define ZIP_STR_14B (1 << 6)
      82                 : #define ZIP_STR_32B (2 << 6)
      83                 : #define ZIP_INT_16B (0xc0 | 0<<4)
      84                 : #define ZIP_INT_32B (0xc0 | 1<<4)
      85                 : #define ZIP_INT_64B (0xc0 | 2<<4)
      86                 : 
      87                 : /* Macro to determine type */
      88                 : #define ZIP_IS_STR(enc) (((enc) & ZIP_STR_MASK) < ZIP_STR_MASK)
      89                 : 
      90                 : /* Utility macros */
      91                 : #define ZIPLIST_BYTES(zl)       (*((uint32_t*)(zl)))
      92                 : #define ZIPLIST_TAIL_OFFSET(zl) (*((uint32_t*)((zl)+sizeof(uint32_t))))
      93                 : #define ZIPLIST_LENGTH(zl)      (*((uint16_t*)((zl)+sizeof(uint32_t)*2)))
      94                 : #define ZIPLIST_HEADER_SIZE     (sizeof(uint32_t)*2+sizeof(uint16_t))
      95                 : #define ZIPLIST_ENTRY_HEAD(zl)  ((zl)+ZIPLIST_HEADER_SIZE)
      96                 : #define ZIPLIST_ENTRY_TAIL(zl)  ((zl)+intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl)))
      97                 : #define ZIPLIST_ENTRY_END(zl)   ((zl)+intrev32ifbe(ZIPLIST_BYTES(zl))-1)
      98                 : 
      99                 : /* We know a positive increment can only be 1 because entries can only be
     100                 :  * pushed one at a time. */
     101                 : #define ZIPLIST_INCR_LENGTH(zl,incr) { \
     102                 :     if (ZIPLIST_LENGTH(zl) < UINT16_MAX) \
     103                 :         ZIPLIST_LENGTH(zl) = intrev16ifbe(intrev16ifbe(ZIPLIST_LENGTH(zl))+incr); \
     104                 : }
     105                 : 
     106                 : typedef struct zlentry {
     107                 :     unsigned int prevrawlensize, prevrawlen;
     108                 :     unsigned int lensize, len;
     109                 :     unsigned int headersize;
     110                 :     unsigned char encoding;
     111                 :     unsigned char *p;
     112                 : } zlentry;
     113                 : 
     114                 : #define ZIP_ENTRY_ENCODING(ptr, encoding) do {                                 \
     115                 :     (encoding) = (ptr[0]) & (ZIP_STR_MASK | ZIP_INT_MASK);                     \
     116                 :     if (((encoding) & ZIP_STR_MASK) < ZIP_STR_MASK) {                          \
     117                 :         /* String encoding: 2 MSBs */                                          \
     118                 :         (encoding) &= ZIP_STR_MASK;                                            \
     119                 :     }                                                                          \
     120                 : } while(0)
     121                 : 
     122                 : /* Return bytes needed to store integer encoded by 'encoding' */
     123         6390452 : static unsigned int zipIntSize(unsigned char encoding) {
     124         6390452 :     switch(encoding) {
     125         5117150 :     case ZIP_INT_16B: return sizeof(int16_t);
     126          493526 :     case ZIP_INT_32B: return sizeof(int32_t);
     127          779776 :     case ZIP_INT_64B: return sizeof(int64_t);
     128                 :     }
     129               0 :     assert(NULL);
     130                 :     return 0;
     131                 : }
     132                 : 
     133                 : /* Encode the length 'l' writing it in 'p'. If p is NULL it just returns
     134                 :  * the amount of bytes required to encode such a length. */
     135          384290 : static unsigned int zipEncodeLength(unsigned char *p, unsigned char encoding, unsigned int rawlen) {
     136          384290 :     unsigned char len = 1, buf[5];
     137                 : 
     138          384290 :     if (ZIP_IS_STR(encoding)) {
     139                 :         /* Although encoding is given it may not be set for strings,
     140                 :          * so we determine it here using the raw length. */
     141          132078 :         if (rawlen <= 0x3f) {
     142          103038 :             if (!p) return len;
     143           51519 :             buf[0] = ZIP_STR_06B | rawlen;
     144           29040 :         } else if (rawlen <= 0x3fff) {
     145           28118 :             len += 1;
     146           28118 :             if (!p) return len;
     147           14059 :             buf[0] = ZIP_STR_14B | ((rawlen >> 8) & 0x3f);
     148           14059 :             buf[1] = rawlen & 0xff;
     149                 :         } else {
     150             922 :             len += 4;
     151             922 :             if (!p) return len;
     152             461 :             buf[0] = ZIP_STR_32B;
     153             461 :             buf[1] = (rawlen >> 24) & 0xff;
     154             461 :             buf[2] = (rawlen >> 16) & 0xff;
     155             461 :             buf[3] = (rawlen >> 8) & 0xff;
     156             461 :             buf[4] = rawlen & 0xff;
     157                 :         }
     158                 :     } else {
     159                 :         /* Implies integer encoding, so length is always 1. */
     160          252212 :         if (!p) return len;
     161          126106 :         buf[0] = encoding;
     162                 :     }
     163                 : 
     164                 :     /* Store this length at p */
     165          192145 :     memcpy(p,buf,len);
     166          192145 :     return len;
     167                 : }
     168                 : 
     169                 : /* Decode the length encoded in 'ptr'. The 'encoding' variable will hold the
     170                 :  * entries encoding, the 'lensize' variable will hold the number of bytes
     171                 :  * required to encode the entries length, and the 'len' variable will hold the
     172                 :  * entries length. */
     173                 : #define ZIP_DECODE_LENGTH(ptr, encoding, lensize, len) do {                    \
     174                 :     ZIP_ENTRY_ENCODING((ptr), (encoding));                                     \
     175                 :     if ((encoding) < ZIP_STR_MASK) {                                           \
     176                 :         if ((encoding) == ZIP_STR_06B) {                                       \
     177                 :             (lensize) = 1;                                                     \
     178                 :             (len) = (ptr)[0] & 0x3f;                                           \
     179                 :         } else if ((encoding) == ZIP_STR_14B) {                                \
     180                 :             (lensize) = 2;                                                     \
     181                 :             (len) = (((ptr)[0] & 0x3f) << 8) | (ptr)[1];                       \
     182                 :         } else if (encoding == ZIP_STR_32B) {                                  \
     183                 :             (lensize) = 5;                                                     \
     184                 :             (len) = ((ptr)[1] << 24) |                                         \
     185                 :                     ((ptr)[2] << 16) |                                         \
     186                 :                     ((ptr)[3] <<  8) |                                         \
     187                 :                     ((ptr)[4]);                                                \
     188                 :         } else {                                                               \
     189                 :             assert(NULL);                                                      \
     190                 :         }                                                                      \
     191                 :     } else {                                                                   \
     192                 :         (lensize) = 1;                                                         \
     193                 :         (len) = zipIntSize(encoding);                                          \
     194                 :     }                                                                          \
     195                 : } while(0);
     196                 : 
     197                 : /* Encode the length of the previous entry and write it to "p". Return the
     198                 :  * number of bytes needed to encode this length if "p" is NULL. */
     199          472641 : static unsigned int zipPrevEncodeLength(unsigned char *p, unsigned int len) {
     200          472641 :     if (p == NULL) {
     201          236351 :         return (len < ZIP_BIGLEN) ? 1 : sizeof(len)+1;
     202                 :     } else {
     203          236290 :         if (len < ZIP_BIGLEN) {
     204          235406 :             p[0] = len;
     205          235406 :             return 1;
     206                 :         } else {
     207             884 :             p[0] = ZIP_BIGLEN;
     208             884 :             memcpy(p+1,&len,sizeof(len));
     209                 :             memrev32ifbe(p+1);
     210             884 :             return 1+sizeof(len);
     211                 :         }
     212                 :     }
     213                 : }
     214                 : 
     215                 : /* Encode the length of the previous entry and write it to "p". This only
     216                 :  * uses the larger encoding (required in __ziplistCascadeUpdate). */
     217                 : static void zipPrevEncodeLengthForceLarge(unsigned char *p, unsigned int len) {
     218               0 :     if (p == NULL) return;
     219               0 :     p[0] = ZIP_BIGLEN;
     220               0 :     memcpy(p+1,&len,sizeof(len));
     221                 :     memrev32ifbe(p+1);
     222                 : }
     223                 : 
     224                 : /* Decode the number of bytes required to store the length of the previous
     225                 :  * element, from the perspective of the entry pointed to by 'ptr'. */
     226                 : #define ZIP_DECODE_PREVLENSIZE(ptr, prevlensize) do {                          \
     227                 :     if ((ptr)[0] < ZIP_BIGLEN) {                                               \
     228                 :         (prevlensize) = 1;                                                     \
     229                 :     } else {                                                                   \
     230                 :         (prevlensize) = 5;                                                     \
     231                 :     }                                                                          \
     232                 : } while(0);
     233                 : 
     234                 : /* Decode the length of the previous element, from the perspective of the entry
     235                 :  * pointed to by 'ptr'. */
     236                 : #define ZIP_DECODE_PREVLEN(ptr, prevlensize, prevlen) do {                     \
     237                 :     ZIP_DECODE_PREVLENSIZE(ptr, prevlensize);                                  \
     238                 :     if ((prevlensize) == 1) {                                                  \
     239                 :         (prevlen) = (ptr)[0];                                                  \
     240                 :     } else if ((prevlensize) == 5) {                                           \
     241                 :         assert(sizeof((prevlensize)) == 4);                                    \
     242                 :         memcpy(&(prevlen), ((char*)(ptr)) + 1, 4);                             \
     243                 :         memrev32ifbe(&prevlen);                                                \
     244                 :     }                                                                          \
     245                 : } while(0);
     246                 : 
     247                 : /* Return the difference in number of bytes needed to store the length of the
     248                 :  * previous element 'len', in the entry pointed to by 'p'. */
     249                 : static int zipPrevLenByteDiff(unsigned char *p, unsigned int len) {
     250                 :     unsigned int prevlensize;
     251           44050 :     ZIP_DECODE_PREVLENSIZE(p, prevlensize);
     252           44050 :     return zipPrevEncodeLength(NULL, len) - prevlensize;
     253                 : }
     254                 : 
     255                 : /* Return the total number of bytes used by the entry pointed to by 'p'. */
     256         6666733 : static unsigned int zipRawEntryLength(unsigned char *p) {
     257                 :     unsigned int prevlensize, encoding, lensize, len;
     258         6666733 :     ZIP_DECODE_PREVLENSIZE(p, prevlensize);
     259         6666733 :     ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len);
     260         6666733 :     return prevlensize + lensize + len;
     261                 : }
     262                 : 
     263                 : /* Check if string pointed to by 'entry' can be encoded as an integer.
     264                 :  * Stores the integer value in 'v' and its encoding in 'encoding'. */
     265         2204782 : static int zipTryEncoding(unsigned char *entry, unsigned int entrylen, long long *v, unsigned char *encoding) {
     266                 :     long long value;
     267                 : 
     268         2204782 :     if (entrylen >= 32 || entrylen == 0) return 0;
     269         2184922 :     if (string2ll((char*)entry,entrylen,&value)) {
     270                 :         /* Great, the string can be encoded. Check what's the smallest
     271                 :          * of our encoding types that can hold this value. */
     272         2105440 :         if (value >= INT16_MIN && value <= INT16_MAX) {
     273         1979386 :             *encoding = ZIP_INT_16B;
     274          126054 :         } else if (value >= INT32_MIN && value <= INT32_MAX) {
     275           50228 :             *encoding = ZIP_INT_32B;
     276                 :         } else {
     277           75826 :             *encoding = ZIP_INT_64B;
     278                 :         }
     279         2105440 :         *v = value;
     280         2105440 :         return 1;
     281                 :     }
     282           79482 :     return 0;
     283                 : }
     284                 : 
     285                 : /* Store integer 'value' at 'p', encoded as 'encoding' */
     286          126106 : static void zipSaveInteger(unsigned char *p, int64_t value, unsigned char encoding) {
     287                 :     int16_t i16;
     288                 :     int32_t i32;
     289                 :     int64_t i64;
     290          126106 :     if (encoding == ZIP_INT_16B) {
     291           33484 :         i16 = value;
     292                 :         memcpy(p,&i16,sizeof(i16));
     293                 :         memrev16ifbe(p);
     294           92622 :     } else if (encoding == ZIP_INT_32B) {
     295           34439 :         i32 = value;
     296                 :         memcpy(p,&i32,sizeof(i32));
     297                 :         memrev32ifbe(p);
     298           58183 :     } else if (encoding == ZIP_INT_64B) {
     299           58183 :         i64 = value;
     300                 :         memcpy(p,&i64,sizeof(i64));
     301                 :         memrev64ifbe(p);
     302                 :     } else {
     303               0 :         assert(NULL);
     304                 :     }
     305          126106 : }
     306                 : 
     307                 : /* Read integer encoded as 'encoding' from 'p' */
     308         2167767 : static int64_t zipLoadInteger(unsigned char *p, unsigned char encoding) {
     309                 :     int16_t i16;
     310                 :     int32_t i32;
     311         2167767 :     int64_t i64, ret = 0;
     312         2167767 :     if (encoding == ZIP_INT_16B) {
     313                 :         memcpy(&i16,p,sizeof(i16));
     314                 :         memrev16ifbe(&i16);
     315         2028020 :         ret = i16;
     316          139747 :     } else if (encoding == ZIP_INT_32B) {
     317                 :         memcpy(&i32,p,sizeof(i32));
     318                 :         memrev32ifbe(&i32);
     319           61400 :         ret = i32;
     320           78347 :     } else if (encoding == ZIP_INT_64B) {
     321                 :         memcpy(&i64,p,sizeof(i64));
     322                 :         memrev64ifbe(&i64);
     323           78347 :         ret = i64;
     324                 :     } else {
     325               0 :         assert(NULL);
     326                 :     }
     327         2167767 :     return ret;
     328                 : }
     329                 : 
     330                 : /* Return a struct with all information about an entry. */
     331         2837447 : static zlentry zipEntry(unsigned char *p) {
     332                 :     zlentry e;
     333                 : 
     334         2837447 :     ZIP_DECODE_PREVLEN(p, e.prevrawlensize, e.prevrawlen);
     335         2837447 :     ZIP_DECODE_LENGTH(p + e.prevrawlensize, e.encoding, e.lensize, e.len);
     336         2837447 :     e.headersize = e.prevrawlensize + e.lensize;
     337         2837447 :     e.p = p;
     338         2837447 :     return e;
     339                 : }
     340                 : 
     341                 : /* Create a new empty ziplist. */
     342           54162 : unsigned char *ziplistNew(void) {
     343           54162 :     unsigned int bytes = ZIPLIST_HEADER_SIZE+1;
     344           54162 :     unsigned char *zl = zmalloc(bytes);
     345           54162 :     ZIPLIST_BYTES(zl) = intrev32ifbe(bytes);
     346           54162 :     ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(ZIPLIST_HEADER_SIZE);
     347           54162 :     ZIPLIST_LENGTH(zl) = 0;
     348           54162 :     zl[bytes-1] = ZIP_END;
     349           54162 :     return zl;
     350                 : }
     351                 : 
     352                 : /* Resize the ziplist. */
     353                 : static unsigned char *ziplistResize(unsigned char *zl, unsigned int len) {
     354          246951 :     zl = zrealloc(zl,len);
     355          246951 :     ZIPLIST_BYTES(zl) = intrev32ifbe(len);
     356          246951 :     zl[len-1] = ZIP_END;
     357          246951 :     return zl;
     358                 : }
     359                 : 
     360                 : /* When an entry is inserted, we need to set the prevlen field of the next
     361                 :  * entry to equal the length of the inserted entry. It can occur that this
     362                 :  * length cannot be encoded in 1 byte and the next entry needs to be grow
     363                 :  * a bit larger to hold the 5-byte encoded prevlen. This can be done for free,
     364                 :  * because this only happens when an entry is already being inserted (which
     365                 :  * causes a realloc and memmove). However, encoding the prevlen may require
     366                 :  * that this entry is grown as well. This effect may cascade throughout
     367                 :  * the ziplist when there are consecutive entries with a size close to
     368                 :  * ZIP_BIGLEN, so we need to check that the prevlen can be encoded in every
     369                 :  * consecutive entry.
     370                 :  *
     371                 :  * Note that this effect can also happen in reverse, where the bytes required
     372                 :  * to encode the prevlen field can shrink. This effect is deliberately ignored,
     373                 :  * because it can cause a "flapping" effect where a chain prevlen fields is
     374                 :  * first grown and then shrunk again after consecutive inserts. Rather, the
     375                 :  * field is allowed to stay larger than necessary, because a large prevlen
     376                 :  * field implies the ziplist is holding large entries anyway.
     377                 :  *
     378                 :  * The pointer "p" points to the first entry that does NOT need to be
     379                 :  * updated, i.e. consecutive fields MAY need an update. */
     380             156 : static unsigned char *__ziplistCascadeUpdate(unsigned char *zl, unsigned char *p) {
     381             156 :     size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), rawlen, rawlensize;
     382                 :     size_t offset, noffset, extra;
     383                 :     unsigned char *np;
     384                 :     zlentry cur, next;
     385                 : 
     386             312 :     while (p[0] != ZIP_END) {
     387             156 :         cur = zipEntry(p);
     388             156 :         rawlen = cur.headersize + cur.len;
     389             156 :         rawlensize = zipPrevEncodeLength(NULL,rawlen);
     390                 : 
     391                 :         /* Abort if there is no next entry. */
     392             156 :         if (p[rawlen] == ZIP_END) break;
     393              95 :         next = zipEntry(p+rawlen);
     394                 : 
     395                 :         /* Abort when "prevlen" has not changed. */
     396              95 :         if (next.prevrawlen == rawlen) break;
     397                 : 
     398              95 :         if (next.prevrawlensize < rawlensize) {
     399                 :             /* The "prevlen" field of "next" needs more bytes to hold
     400                 :              * the raw length of "cur". */
     401               0 :             offset = p-zl;
     402               0 :             extra = rawlensize-next.prevrawlensize;
     403               0 :             zl = ziplistResize(zl,curlen+extra);
     404               0 :             p = zl+offset;
     405                 : 
     406                 :             /* Current pointer and offset for next element. */
     407               0 :             np = p+rawlen;
     408               0 :             noffset = np-zl;
     409                 : 
     410                 :             /* Update tail offset when next element is not the tail element. */
     411               0 :             if ((zl+intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))) != np) {
     412               0 :                 ZIPLIST_TAIL_OFFSET(zl) =
     413               0 :                     intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+extra);
     414                 :             }
     415                 : 
     416                 :             /* Move the tail to the back. */
     417               0 :             memmove(np+rawlensize,
     418                 :                 np+next.prevrawlensize,
     419               0 :                 curlen-noffset-next.prevrawlensize-1);
     420               0 :             zipPrevEncodeLength(np,rawlen);
     421                 : 
     422                 :             /* Advance the cursor */
     423               0 :             p += rawlen;
     424               0 :             curlen += extra;
     425                 :         } else {
     426              95 :             if (next.prevrawlensize > rawlensize) {
     427                 :                 /* This would result in shrinking, which we want to avoid.
     428                 :                  * So, set "rawlen" in the available bytes. */
     429               0 :                 zipPrevEncodeLengthForceLarge(p+rawlen,rawlen);
     430                 :             } else {
     431              95 :                 zipPrevEncodeLength(p+rawlen,rawlen);
     432                 :             }
     433                 : 
     434                 :             /* Stop here, as the raw length of "next" has not changed. */
     435                 :             break;
     436                 :         }
     437                 :     }
     438             156 :     return zl;
     439                 : }
     440                 : 
     441                 : /* Delete "num" entries, starting at "p". Returns pointer to the ziplist. */
     442           55360 : static unsigned char *__ziplistDelete(unsigned char *zl, unsigned char *p, unsigned int num) {
     443           55360 :     unsigned int i, totlen, deleted = 0;
     444                 :     size_t offset;
     445           55360 :     int nextdiff = 0;
     446                 :     zlentry first, tail;
     447                 : 
     448           55360 :     first = zipEntry(p);
     449          129457 :     for (i = 0; p[0] != ZIP_END && i < num; i++) {
     450           74097 :         p += zipRawEntryLength(p);
     451           74097 :         deleted++;
     452                 :     }
     453                 : 
     454           55360 :     totlen = p-first.p;
     455           55360 :     if (totlen > 0) {
     456           54806 :         if (p[0] != ZIP_END) {
     457                 :             /* Tricky: storing the prevlen in this entry might reduce or
     458                 :              * increase the number of bytes needed, compared to the current
     459                 :              * prevlen. Note that we can always store this length because
     460                 :              * it was previously stored by an entry that is being deleted. */
     461           31690 :             nextdiff = zipPrevLenByteDiff(p,first.prevrawlen);
     462           15845 :             zipPrevEncodeLength(p-nextdiff,first.prevrawlen);
     463                 : 
     464                 :             /* Update offset for tail */
     465           31690 :             ZIPLIST_TAIL_OFFSET(zl) =
     466           15845 :                 intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))-totlen);
     467                 : 
     468                 :             /* When the tail contains more than one entry, we need to take
     469                 :              * "nextdiff" in account as well. Otherwise, a change in the
     470                 :              * size of prevlen doesn't have an effect on the *tail* offset. */
     471           15845 :             tail = zipEntry(p);
     472           15845 :             if (p[tail.headersize+tail.len] != ZIP_END) {
     473            8536 :                 ZIPLIST_TAIL_OFFSET(zl) =
     474            4268 :                    intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+nextdiff);
     475                 :             }
     476                 : 
     477                 :             /* Move tail to the front of the ziplist */
     478           15845 :             memmove(first.p,p-nextdiff,
     479           15845 :                 intrev32ifbe(ZIPLIST_BYTES(zl))-(p-zl)-1+nextdiff);
     480                 :         } else {
     481                 :             /* The entire tail was deleted. No need to move memory. */
     482           38961 :             ZIPLIST_TAIL_OFFSET(zl) =
     483                 :                 intrev32ifbe((first.p-zl)-first.prevrawlen);
     484                 :         }
     485                 : 
     486                 :         /* Resize and update length */
     487           54806 :         offset = first.p-zl;
     488          109612 :         zl = ziplistResize(zl, intrev32ifbe(ZIPLIST_BYTES(zl))-totlen+nextdiff);
     489           54806 :         ZIPLIST_INCR_LENGTH(zl,-deleted);
     490           54806 :         p = zl+offset;
     491                 : 
     492                 :         /* When nextdiff != 0, the raw length of the next entry has changed, so
     493                 :          * we need to cascade the update throughout the ziplist */
     494           54806 :         if (nextdiff != 0)
     495              60 :             zl = __ziplistCascadeUpdate(zl,p);
     496                 :     }
     497           55360 :     return zl;
     498                 : }
     499                 : 
     500                 : /* Insert item at "p". */
     501          192145 : static unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {
     502          192145 :     size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), reqlen, prevlen = 0;
     503                 :     size_t offset;
     504          192145 :     int nextdiff = 0;
     505          192145 :     unsigned char encoding = 0;
     506          192145 :     long long value = 123456789; /* initialized to avoid warning. Using a value
     507                 :                                     that is easy to see if for some reason
     508                 :                                     we use it uninitialized. */
     509                 :     zlentry entry, tail;
     510                 : 
     511                 :     /* Find out prevlen for the entry that is inserted. */
     512          192145 :     if (p[0] != ZIP_END) {
     513           28205 :         entry = zipEntry(p);
     514           28205 :         prevlen = entry.prevrawlen;
     515                 :     } else {
     516          163940 :         unsigned char *ptail = ZIPLIST_ENTRY_TAIL(zl);
     517          163940 :         if (ptail[0] != ZIP_END) {
     518          112601 :             prevlen = zipRawEntryLength(ptail);
     519                 :         }
     520                 :     }
     521                 : 
     522                 :     /* See if the entry can be encoded */
     523          192145 :     if (zipTryEncoding(s,slen,&value,&encoding)) {
     524                 :         /* 'encoding' is set to the appropriate integer encoding */
     525          126106 :         reqlen = zipIntSize(encoding);
     526                 :     } else {
     527                 :         /* 'encoding' is untouched, however zipEncodeLength will use the
     528                 :          * string length to figure out how to encode it. */
     529           66039 :         reqlen = slen;
     530                 :     }
     531                 :     /* We need space for both the length of the previous entry and
     532                 :      * the length of the payload. */
     533          192145 :     reqlen += zipPrevEncodeLength(NULL,prevlen);
     534          192145 :     reqlen += zipEncodeLength(NULL,encoding,slen);
     535                 : 
     536                 :     /* When the insert position is not equal to the tail, we need to
     537                 :      * make sure that the next entry can hold this entry's length in
     538                 :      * its prevlen field. */
     539          220350 :     nextdiff = (p[0] != ZIP_END) ? zipPrevLenByteDiff(p,reqlen) : 0;
     540                 : 
     541                 :     /* Store offset because a realloc may change the address of zl. */
     542          192145 :     offset = p-zl;
     543          384290 :     zl = ziplistResize(zl,curlen+reqlen+nextdiff);
     544          192145 :     p = zl+offset;
     545                 : 
     546                 :     /* Apply memory move when necessary and update tail offset. */
     547          192145 :     if (p[0] != ZIP_END) {
     548                 :         /* Subtract one because of the ZIP_END bytes */
     549           28205 :         memmove(p+reqlen,p-nextdiff,curlen-offset-1+nextdiff);
     550                 : 
     551                 :         /* Encode this entry's raw length in the next entry. */
     552           28205 :         zipPrevEncodeLength(p+reqlen,reqlen);
     553                 : 
     554                 :         /* Update offset for tail */
     555           56410 :         ZIPLIST_TAIL_OFFSET(zl) =
     556           28205 :             intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+reqlen);
     557                 : 
     558                 :         /* When the tail contains more than one entry, we need to take
     559                 :          * "nextdiff" in account as well. Otherwise, a change in the
     560                 :          * size of prevlen doesn't have an effect on the *tail* offset. */
     561           28205 :         tail = zipEntry(p+reqlen);
     562           28205 :         if (p[reqlen+tail.headersize+tail.len] != ZIP_END) {
     563           51212 :             ZIPLIST_TAIL_OFFSET(zl) =
     564           25606 :                 intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+nextdiff);
     565                 :         }
     566                 :     } else {
     567                 :         /* This element will be the new tail. */
     568          163940 :         ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(p-zl);
     569                 :     }
     570                 : 
     571                 :     /* When nextdiff != 0, the raw length of the next entry has changed, so
     572                 :      * we need to cascade the update throughout the ziplist */
     573          192145 :     if (nextdiff != 0) {
     574              96 :         offset = p-zl;
     575              96 :         zl = __ziplistCascadeUpdate(zl,p+reqlen);
     576              96 :         p = zl+offset;
     577                 :     }
     578                 : 
     579                 :     /* Write the entry */
     580          192145 :     p += zipPrevEncodeLength(p,prevlen);
     581          192145 :     p += zipEncodeLength(p,encoding,slen);
     582          192145 :     if (ZIP_IS_STR(encoding)) {
     583           66039 :         memcpy(p,s,slen);
     584                 :     } else {
     585          126106 :         zipSaveInteger(p,value,encoding);
     586                 :     }
     587          192145 :     ZIPLIST_INCR_LENGTH(zl,1);
     588          192145 :     return zl;
     589                 : }
     590                 : 
     591          165984 : unsigned char *ziplistPush(unsigned char *zl, unsigned char *s, unsigned int slen, int where) {
     592                 :     unsigned char *p;
     593          165984 :     p = (where == ZIPLIST_HEAD) ? ZIPLIST_ENTRY_HEAD(zl) : ZIPLIST_ENTRY_END(zl);
     594          165984 :     return __ziplistInsert(zl,p,s,slen);
     595                 : }
     596                 : 
     597                 : /* Returns an offset to use for iterating with ziplistNext. When the given
     598                 :  * index is negative, the list is traversed back to front. When the list
     599                 :  * doesn't contain an element at the provided index, NULL is returned. */
     600          262674 : unsigned char *ziplistIndex(unsigned char *zl, int index) {
     601                 :     unsigned char *p;
     602                 :     zlentry entry;
     603          262674 :     if (index < 0) {
     604            5512 :         index = (-index)-1;
     605            5512 :         p = ZIPLIST_ENTRY_TAIL(zl);
     606            5512 :         if (p[0] != ZIP_END) {
     607            5512 :             entry = zipEntry(p);
     608          139726 :             while (entry.prevrawlen > 0 && index--) {
     609          128702 :                 p -= entry.prevrawlen;
     610          128702 :                 entry = zipEntry(p);
     611                 :             }
     612                 :         }
     613                 :     } else {
     614          257162 :         p = ZIPLIST_ENTRY_HEAD(zl);
     615         2254920 :         while (p[0] != ZIP_END && index--) {
     616         1740596 :             p += zipRawEntryLength(p);
     617                 :         }
     618                 :     }
     619          262674 :     return (p[0] == ZIP_END || index > 0) ? NULL : p;
     620                 : }
     621                 : 
     622                 : /* Return pointer to next entry in ziplist.
     623                 :  *
     624                 :  * zl is the pointer to the ziplist
     625                 :  * p is the pointer to the current element
     626                 :  *
     627                 :  * The element after 'p' is returned, otherwise NULL if we are at the end. */
     628         4741884 : unsigned char *ziplistNext(unsigned char *zl, unsigned char *p) {
     629                 :     ((void) zl);
     630                 : 
     631                 :     /* "p" could be equal to ZIP_END, caused by ziplistDelete,
     632                 :      * and we should return NULL. Otherwise, we should return NULL
     633                 :      * when the *next* element is ZIP_END (there is no next entry). */
     634         4741884 :     if (p[0] == ZIP_END) {
     635            2445 :         return NULL;
     636                 :     }
     637                 : 
     638         4739439 :     p += zipRawEntryLength(p);
     639         4739439 :     if (p[0] == ZIP_END) {
     640           78089 :         return NULL;
     641                 :     }
     642                 : 
     643         4661350 :     return p;
     644                 : }
     645                 : 
     646                 : /* Return pointer to previous entry in ziplist. */
     647             660 : unsigned char *ziplistPrev(unsigned char *zl, unsigned char *p) {
     648                 :     zlentry entry;
     649                 : 
     650                 :     /* Iterating backwards from ZIP_END should return the tail. When "p" is
     651                 :      * equal to the first element of the list, we're already at the head,
     652                 :      * and should return NULL. */
     653             660 :     if (p[0] == ZIP_END) {
     654               2 :         p = ZIPLIST_ENTRY_TAIL(zl);
     655               2 :         return (p[0] == ZIP_END) ? NULL : p;
     656             658 :     } else if (p == ZIPLIST_ENTRY_HEAD(zl)) {
     657               9 :         return NULL;
     658                 :     } else {
     659             649 :         entry = zipEntry(p);
     660             649 :         assert(entry.prevrawlen > 0);
     661             649 :         return p-entry.prevrawlen;
     662                 :     }
     663                 : }
     664                 : 
     665                 : /* Get entry pointer to by 'p' and store in either 'e' or 'v' depending
     666                 :  * on the encoding of the entry. 'e' is always set to NULL to be able
     667                 :  * to find out whether the string pointer or the integer value was set.
     668                 :  * Return 0 if 'p' points to the end of the zipmap, 1 otherwise. */
     669          545084 : unsigned int ziplistGet(unsigned char *p, unsigned char **sstr, unsigned int *slen, long long *sval) {
     670                 :     zlentry entry;
     671          545084 :     if (p == NULL || p[0] == ZIP_END) return 0;
     672          545084 :     if (sstr) *sstr = NULL;
     673                 : 
     674          545084 :     entry = zipEntry(p);
     675          545084 :     if (ZIP_IS_STR(entry.encoding)) {
     676          346838 :         if (sstr) {
     677          346838 :             *slen = entry.len;
     678          346838 :             *sstr = p+entry.headersize;
     679                 :         }
     680                 :     } else {
     681          198246 :         if (sval) {
     682          198246 :             *sval = zipLoadInteger(p+entry.headersize,entry.encoding);
     683                 :         }
     684                 :     }
     685          545084 :     return 1;
     686                 : }
     687                 : 
     688                 : /* Insert an entry at "p". */
     689           26161 : unsigned char *ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {
     690           26161 :     return __ziplistInsert(zl,p,s,slen);
     691                 : }
     692                 : 
     693                 : /* Delete a single entry from the ziplist, pointed to by *p.
     694                 :  * Also update *p in place, to be able to iterate over the
     695                 :  * ziplist, while deleting entries. */
     696           53329 : unsigned char *ziplistDelete(unsigned char *zl, unsigned char **p) {
     697           53329 :     size_t offset = *p-zl;
     698           53329 :     zl = __ziplistDelete(zl,*p,1);
     699                 : 
     700                 :     /* Store pointer to current element in p, because ziplistDelete will
     701                 :      * do a realloc which might result in a different "zl"-pointer.
     702                 :      * When the delete direction is back to front, we might delete the last
     703                 :      * entry and end up with "p" pointing to ZIP_END, so check this. */
     704           53329 :     *p = zl+offset;
     705           53329 :     return zl;
     706                 : }
     707                 : 
     708                 : /* Delete a range of entries from the ziplist. */
     709            2032 : unsigned char *ziplistDeleteRange(unsigned char *zl, unsigned int index, unsigned int num) {
     710            2032 :     unsigned char *p = ziplistIndex(zl,index);
     711            2032 :     return (p == NULL) ? zl : __ziplistDelete(zl,p,num);
     712                 : }
     713                 : 
     714                 : /* Compare entry pointer to by 'p' with 'entry'. Return 1 if equal. */
     715         2032073 : unsigned int ziplistCompare(unsigned char *p, unsigned char *sstr, unsigned int slen) {
     716                 :     zlentry entry;
     717                 :     unsigned char sencoding;
     718                 :     long long zval, sval;
     719         2032073 :     if (p[0] == ZIP_END) return 0;
     720                 : 
     721         2029634 :     entry = zipEntry(p);
     722         2029634 :     if (ZIP_IS_STR(entry.encoding)) {
     723                 :         /* Raw compare */
     724           29444 :         if (entry.len == slen) {
     725            8678 :             return memcmp(p+entry.headersize,sstr,slen) == 0;
     726                 :         } else {
     727           20766 :             return 0;
     728                 :         }
     729                 :     } else {
     730                 :         /* Try to compare encoded values */
     731         2000190 :         if (zipTryEncoding(sstr,slen,&sval,&sencoding)) {
     732         1967189 :             if (entry.encoding == sencoding) {
     733         1955742 :                 zval = zipLoadInteger(p+entry.headersize,entry.encoding);
     734         1955742 :                 return zval == sval;
     735                 :             }
     736                 :         }
     737                 :     }
     738           44448 :     return 0;
     739                 : }
     740                 : 
     741                 : /* Find pointer to the entry equal to the specified entry. Skip 'skip' entries
     742                 :  * between every comparison. Returns NULL when the field could not be found. */
     743           60659 : unsigned char *ziplistFind(unsigned char *p, unsigned char *vstr, unsigned int vlen, unsigned int skip) {
     744           60659 :     int skipcnt = 0;
     745           60659 :     unsigned char vencoding = 0;
     746           60659 :     long long vll = 0;
     747                 : 
     748          146856 :     while (p[0] != ZIP_END) {
     749                 :         unsigned int prevlensize, encoding, lensize, len;
     750                 :         unsigned char *q;
     751                 : 
     752           84552 :         ZIP_DECODE_PREVLENSIZE(p, prevlensize);
     753           84552 :         ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len);
     754           84552 :         q = p + prevlensize + lensize;
     755                 : 
     756           84552 :         if (skipcnt == 0) {
     757                 :             /* Compare current entry with specified entry */
     758           71783 :             if (ZIP_IS_STR(encoding)) {
     759           54785 :                 if (len == vlen && memcmp(q, vstr, vlen) == 0) {
     760           47830 :                     return p;
     761                 :                 }
     762                 :             } else {
     763                 :                 /* Find out if the specified entry can be encoded */
     764           16998 :                 if (vencoding == 0) {
     765                 :                     /* UINT_MAX when the entry CANNOT be encoded */
     766           12447 :                     if (!zipTryEncoding(vstr, vlen, &vll, &vencoding)) {
     767             302 :                         vencoding = UCHAR_MAX;
     768                 :                     }
     769                 : 
     770                 :                     /* Must be non-zero by now */
     771           12447 :                     assert(vencoding);
     772                 :                 }
     773                 : 
     774                 :                 /* Compare current entry with specified entry */
     775           16998 :                 if (encoding == vencoding) {
     776           13779 :                     long long ll = zipLoadInteger(q, encoding);
     777           13779 :                     if (ll == vll) {
     778           11184 :                         return p;
     779                 :                     }
     780                 :                 }
     781                 :             }
     782                 : 
     783                 :             /* Reset skip count */
     784           12769 :             skipcnt = skip;
     785                 :         } else {
     786                 :             /* Skip entry */
     787           12769 :             skipcnt--;
     788                 :         }
     789                 : 
     790                 :         /* Move to next entry */
     791           25538 :         p = q + len;
     792                 :     }
     793                 : 
     794            1645 :     return NULL;
     795                 : }
     796                 : 
     797                 : /* Return length of ziplist. */
     798          266050 : unsigned int ziplistLen(unsigned char *zl) {
     799          266050 :     unsigned int len = 0;
     800          266050 :     if (intrev16ifbe(ZIPLIST_LENGTH(zl)) < UINT16_MAX) {
     801          266050 :         len = intrev16ifbe(ZIPLIST_LENGTH(zl));
     802                 :     } else {
     803               0 :         unsigned char *p = zl+ZIPLIST_HEADER_SIZE;
     804               0 :         while (*p != ZIP_END) {
     805               0 :             p += zipRawEntryLength(p);
     806               0 :             len++;
     807                 :         }
     808                 : 
     809                 :         /* Re-store length if small enough */
     810               0 :         if (len < UINT16_MAX) ZIPLIST_LENGTH(zl) = intrev16ifbe(len);
     811                 :     }
     812          266050 :     return len;
     813                 : }
     814                 : 
     815                 : /* Return ziplist blob size in bytes. */
     816           23924 : size_t ziplistBlobLen(unsigned char *zl) {
     817           23924 :     return intrev32ifbe(ZIPLIST_BYTES(zl));
     818                 : }
     819                 : 
     820               0 : void ziplistRepr(unsigned char *zl) {
     821                 :     unsigned char *p;
     822               0 :     int index = 0;
     823                 :     zlentry entry;
     824                 : 
     825               0 :     printf(
     826                 :         "{total bytes %d} "
     827                 :         "{length %u}\n"
     828                 :         "{tail offset %u}\n",
     829               0 :         intrev32ifbe(ZIPLIST_BYTES(zl)),
     830               0 :         intrev16ifbe(ZIPLIST_LENGTH(zl)),
     831               0 :         intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl)));
     832               0 :     p = ZIPLIST_ENTRY_HEAD(zl);
     833               0 :     while(*p != ZIP_END) {
     834               0 :         entry = zipEntry(p);
     835               0 :         printf(
     836                 :             "{"
     837                 :                 "addr 0x%08lx, "
     838                 :                 "index %2d, "
     839                 :                 "offset %5ld, "
     840                 :                 "rl: %5u, "
     841                 :                 "hs %2u, "
     842                 :                 "pl: %5u, "
     843                 :                 "pls: %2u, "
     844                 :                 "payload %5u"
     845                 :             "} ",
     846                 :             (long unsigned)p,
     847                 :             index,
     848               0 :             (unsigned long) (p-zl),
     849                 :             entry.headersize+entry.len,
     850                 :             entry.headersize,
     851                 :             entry.prevrawlen,
     852                 :             entry.prevrawlensize,
     853                 :             entry.len);
     854               0 :         p += entry.headersize;
     855               0 :         if (ZIP_IS_STR(entry.encoding)) {
     856               0 :             if (entry.len > 40) {
     857               0 :                 if (fwrite(p,40,1,stdout) == 0) perror("fwrite");
     858               0 :                 printf("...");
     859                 :             } else {
     860               0 :                 if (entry.len &&
     861               0 :                     fwrite(p,entry.len,1,stdout) == 0) perror("fwrite");
     862                 :             }
     863                 :         } else {
     864               0 :             printf("%lld", (long long) zipLoadInteger(p,entry.encoding));
     865                 :         }
     866               0 :         printf("\n");
     867               0 :         p += entry.len;
     868               0 :         index++;
     869                 :     }
     870               0 :     printf("{end}\n\n");
     871               0 : }
     872                 : 
     873                 : #ifdef ZIPLIST_TEST_MAIN
     874                 : #include <sys/time.h>
     875                 : #include "adlist.h"
     876                 : #include "sds.h"
     877                 : 
     878                 : #define debug(f, ...) { if (DEBUG) printf(f, __VA_ARGS__); }
     879                 : 
     880                 : unsigned char *createList() {
     881                 :     unsigned char *zl = ziplistNew();
     882                 :     zl = ziplistPush(zl, (unsigned char*)"foo", 3, ZIPLIST_TAIL);
     883                 :     zl = ziplistPush(zl, (unsigned char*)"quux", 4, ZIPLIST_TAIL);
     884                 :     zl = ziplistPush(zl, (unsigned char*)"hello", 5, ZIPLIST_HEAD);
     885                 :     zl = ziplistPush(zl, (unsigned char*)"1024", 4, ZIPLIST_TAIL);
     886                 :     return zl;
     887                 : }
     888                 : 
     889                 : unsigned char *createIntList() {
     890                 :     unsigned char *zl = ziplistNew();
     891                 :     char buf[32];
     892                 : 
     893                 :     sprintf(buf, "100");
     894                 :     zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);
     895                 :     sprintf(buf, "128000");
     896                 :     zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);
     897                 :     sprintf(buf, "-100");
     898                 :     zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_HEAD);
     899                 :     sprintf(buf, "4294967296");
     900                 :     zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_HEAD);
     901                 :     sprintf(buf, "non integer");
     902                 :     zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);
     903                 :     sprintf(buf, "much much longer non integer");
     904                 :     zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);
     905                 :     return zl;
     906                 : }
     907                 : 
     908                 : long long usec(void) {
     909                 :     struct timeval tv;
     910                 :     gettimeofday(&tv,NULL);
     911                 :     return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;
     912                 : }
     913                 : 
     914                 : void stress(int pos, int num, int maxsize, int dnum) {
     915                 :     int i,j,k;
     916                 :     unsigned char *zl;
     917                 :     char posstr[2][5] = { "HEAD", "TAIL" };
     918                 :     long long start;
     919                 :     for (i = 0; i < maxsize; i+=dnum) {
     920                 :         zl = ziplistNew();
     921                 :         for (j = 0; j < i; j++) {
     922                 :             zl = ziplistPush(zl,(unsigned char*)"quux",4,ZIPLIST_TAIL);
     923                 :         }
     924                 : 
     925                 :         /* Do num times a push+pop from pos */
     926                 :         start = usec();
     927                 :         for (k = 0; k < num; k++) {
     928                 :             zl = ziplistPush(zl,(unsigned char*)"quux",4,pos);
     929                 :             zl = ziplistDeleteRange(zl,0,1);
     930                 :         }
     931                 :         printf("List size: %8d, bytes: %8d, %dx push+pop (%s): %6lld usec\n",
     932                 :             i,intrev32ifbe(ZIPLIST_BYTES(zl)),num,posstr[pos],usec()-start);
     933                 :         zfree(zl);
     934                 :     }
     935                 : }
     936                 : 
     937                 : void pop(unsigned char *zl, int where) {
     938                 :     unsigned char *p, *vstr;
     939                 :     unsigned int vlen;
     940                 :     long long vlong;
     941                 : 
     942                 :     p = ziplistIndex(zl,where == ZIPLIST_HEAD ? 0 : -1);
     943                 :     if (ziplistGet(p,&vstr,&vlen,&vlong)) {
     944                 :         if (where == ZIPLIST_HEAD)
     945                 :             printf("Pop head: ");
     946                 :         else
     947                 :             printf("Pop tail: ");
     948                 : 
     949                 :         if (vstr)
     950                 :             if (vlen && fwrite(vstr,vlen,1,stdout) == 0) perror("fwrite");
     951                 :         else
     952                 :             printf("%lld", vlong);
     953                 : 
     954                 :         printf("\n");
     955                 :         ziplistDeleteRange(zl,-1,1);
     956                 :     } else {
     957                 :         printf("ERROR: Could not pop\n");
     958                 :         exit(1);
     959                 :     }
     960                 : }
     961                 : 
     962                 : int randstring(char *target, unsigned int min, unsigned int max) {
     963                 :     int p, len = min+rand()%(max-min+1);
     964                 :     int minval, maxval;
     965                 :     switch(rand() % 3) {
     966                 :     case 0:
     967                 :         minval = 0;
     968                 :         maxval = 255;
     969                 :     break;
     970                 :     case 1:
     971                 :         minval = 48;
     972                 :         maxval = 122;
     973                 :     break;
     974                 :     case 2:
     975                 :         minval = 48;
     976                 :         maxval = 52;
     977                 :     break;
     978                 :     default:
     979                 :         assert(NULL);
     980                 :     }
     981                 : 
     982                 :     while(p < len)
     983                 :         target[p++] = minval+rand()%(maxval-minval+1);
     984                 :     return len;
     985                 : }
     986                 : 
     987                 : int main(int argc, char **argv) {
     988                 :     unsigned char *zl, *p;
     989                 :     unsigned char *entry;
     990                 :     unsigned int elen;
     991                 :     long long value;
     992                 : 
     993                 :     /* If an argument is given, use it as the random seed. */
     994                 :     if (argc == 2)
     995                 :         srand(atoi(argv[1]));
     996                 : 
     997                 :     zl = createIntList();
     998                 :     ziplistRepr(zl);
     999                 : 
    1000                 :     zl = createList();
    1001                 :     ziplistRepr(zl);
    1002                 : 
    1003                 :     pop(zl,ZIPLIST_TAIL);
    1004                 :     ziplistRepr(zl);
    1005                 : 
    1006                 :     pop(zl,ZIPLIST_HEAD);
    1007                 :     ziplistRepr(zl);
    1008                 : 
    1009                 :     pop(zl,ZIPLIST_TAIL);
    1010                 :     ziplistRepr(zl);
    1011                 : 
    1012                 :     pop(zl,ZIPLIST_TAIL);
    1013                 :     ziplistRepr(zl);
    1014                 : 
    1015                 :     printf("Get element at index 3:\n");
    1016                 :     {
    1017                 :         zl = createList();
    1018                 :         p = ziplistIndex(zl, 3);
    1019                 :         if (!ziplistGet(p, &entry, &elen, &value)) {
    1020                 :             printf("ERROR: Could not access index 3\n");
    1021                 :             return 1;
    1022                 :         }
    1023                 :         if (entry) {
    1024                 :             if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite");
    1025                 :             printf("\n");
    1026                 :         } else {
    1027                 :             printf("%lld\n", value);
    1028                 :         }
    1029                 :         printf("\n");
    1030                 :     }
    1031                 : 
    1032                 :     printf("Get element at index 4 (out of range):\n");
    1033                 :     {
    1034                 :         zl = createList();
    1035                 :         p = ziplistIndex(zl, 4);
    1036                 :         if (p == NULL) {
    1037                 :             printf("No entry\n");
    1038                 :         } else {
    1039                 :             printf("ERROR: Out of range index should return NULL, returned offset: %ld\n", p-zl);
    1040                 :             return 1;
    1041                 :         }
    1042                 :         printf("\n");
    1043                 :     }
    1044                 : 
    1045                 :     printf("Get element at index -1 (last element):\n");
    1046                 :     {
    1047                 :         zl = createList();
    1048                 :         p = ziplistIndex(zl, -1);
    1049                 :         if (!ziplistGet(p, &entry, &elen, &value)) {
    1050                 :             printf("ERROR: Could not access index -1\n");
    1051                 :             return 1;
    1052                 :         }
    1053                 :         if (entry) {
    1054                 :             if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite");
    1055                 :             printf("\n");
    1056                 :         } else {
    1057                 :             printf("%lld\n", value);
    1058                 :         }
    1059                 :         printf("\n");
    1060                 :     }
    1061                 : 
    1062                 :     printf("Get element at index -4 (first element):\n");
    1063                 :     {
    1064                 :         zl = createList();
    1065                 :         p = ziplistIndex(zl, -4);
    1066                 :         if (!ziplistGet(p, &entry, &elen, &value)) {
    1067                 :             printf("ERROR: Could not access index -4\n");
    1068                 :             return 1;
    1069                 :         }
    1070                 :         if (entry) {
    1071                 :             if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite");
    1072                 :             printf("\n");
    1073                 :         } else {
    1074                 :             printf("%lld\n", value);
    1075                 :         }
    1076                 :         printf("\n");
    1077                 :     }
    1078                 : 
    1079                 :     printf("Get element at index -5 (reverse out of range):\n");
    1080                 :     {
    1081                 :         zl = createList();
    1082                 :         p = ziplistIndex(zl, -5);
    1083                 :         if (p == NULL) {
    1084                 :             printf("No entry\n");
    1085                 :         } else {
    1086                 :             printf("ERROR: Out of range index should return NULL, returned offset: %ld\n", p-zl);
    1087                 :             return 1;
    1088                 :         }
    1089                 :         printf("\n");
    1090                 :     }
    1091                 : 
    1092                 :     printf("Iterate list from 0 to end:\n");
    1093                 :     {
    1094                 :         zl = createList();
    1095                 :         p = ziplistIndex(zl, 0);
    1096                 :         while (ziplistGet(p, &entry, &elen, &value)) {
    1097                 :             printf("Entry: ");
    1098                 :             if (entry) {
    1099                 :                 if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite");
    1100                 :             } else {
    1101                 :                 printf("%lld", value);
    1102                 :             }
    1103                 :             p = ziplistNext(zl,p);
    1104                 :             printf("\n");
    1105                 :         }
    1106                 :         printf("\n");
    1107                 :     }
    1108                 : 
    1109                 :     printf("Iterate list from 1 to end:\n");
    1110                 :     {
    1111                 :         zl = createList();
    1112                 :         p = ziplistIndex(zl, 1);
    1113                 :         while (ziplistGet(p, &entry, &elen, &value)) {
    1114                 :             printf("Entry: ");
    1115                 :             if (entry) {
    1116                 :                 if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite");
    1117                 :             } else {
    1118                 :                 printf("%lld", value);
    1119                 :             }
    1120                 :             p = ziplistNext(zl,p);
    1121                 :             printf("\n");
    1122                 :         }
    1123                 :         printf("\n");
    1124                 :     }
    1125                 : 
    1126                 :     printf("Iterate list from 2 to end:\n");
    1127                 :     {
    1128                 :         zl = createList();
    1129                 :         p = ziplistIndex(zl, 2);
    1130                 :         while (ziplistGet(p, &entry, &elen, &value)) {
    1131                 :             printf("Entry: ");
    1132                 :             if (entry) {
    1133                 :                 if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite");
    1134                 :             } else {
    1135                 :                 printf("%lld", value);
    1136                 :             }
    1137                 :             p = ziplistNext(zl,p);
    1138                 :             printf("\n");
    1139                 :         }
    1140                 :         printf("\n");
    1141                 :     }
    1142                 : 
    1143                 :     printf("Iterate starting out of range:\n");
    1144                 :     {
    1145                 :         zl = createList();
    1146                 :         p = ziplistIndex(zl, 4);
    1147                 :         if (!ziplistGet(p, &entry, &elen, &value)) {
    1148                 :             printf("No entry\n");
    1149                 :         } else {
    1150                 :             printf("ERROR\n");
    1151                 :         }
    1152                 :         printf("\n");
    1153                 :     }
    1154                 : 
    1155                 :     printf("Iterate from back to front:\n");
    1156                 :     {
    1157                 :         zl = createList();
    1158                 :         p = ziplistIndex(zl, -1);
    1159                 :         while (ziplistGet(p, &entry, &elen, &value)) {
    1160                 :             printf("Entry: ");
    1161                 :             if (entry) {
    1162                 :                 if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite");
    1163                 :             } else {
    1164                 :                 printf("%lld", value);
    1165                 :             }
    1166                 :             p = ziplistPrev(zl,p);
    1167                 :             printf("\n");
    1168                 :         }
    1169                 :         printf("\n");
    1170                 :     }
    1171                 : 
    1172                 :     printf("Iterate from back to front, deleting all items:\n");
    1173                 :     {
    1174                 :         zl = createList();
    1175                 :         p = ziplistIndex(zl, -1);
    1176                 :         while (ziplistGet(p, &entry, &elen, &value)) {
    1177                 :             printf("Entry: ");
    1178                 :             if (entry) {
    1179                 :                 if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite");
    1180                 :             } else {
    1181                 :                 printf("%lld", value);
    1182                 :             }
    1183                 :             zl = ziplistDelete(zl,&p);
    1184                 :             p = ziplistPrev(zl,p);
    1185                 :             printf("\n");
    1186                 :         }
    1187                 :         printf("\n");
    1188                 :     }
    1189                 : 
    1190                 :     printf("Delete inclusive range 0,0:\n");
    1191                 :     {
    1192                 :         zl = createList();
    1193                 :         zl = ziplistDeleteRange(zl, 0, 1);
    1194                 :         ziplistRepr(zl);
    1195                 :     }
    1196                 : 
    1197                 :     printf("Delete inclusive range 0,1:\n");
    1198                 :     {
    1199                 :         zl = createList();
    1200                 :         zl = ziplistDeleteRange(zl, 0, 2);
    1201                 :         ziplistRepr(zl);
    1202                 :     }
    1203                 : 
    1204                 :     printf("Delete inclusive range 1,2:\n");
    1205                 :     {
    1206                 :         zl = createList();
    1207                 :         zl = ziplistDeleteRange(zl, 1, 2);
    1208                 :         ziplistRepr(zl);
    1209                 :     }
    1210                 : 
    1211                 :     printf("Delete with start index out of range:\n");
    1212                 :     {
    1213                 :         zl = createList();
    1214                 :         zl = ziplistDeleteRange(zl, 5, 1);
    1215                 :         ziplistRepr(zl);
    1216                 :     }
    1217                 : 
    1218                 :     printf("Delete with num overflow:\n");
    1219                 :     {
    1220                 :         zl = createList();
    1221                 :         zl = ziplistDeleteRange(zl, 1, 5);
    1222                 :         ziplistRepr(zl);
    1223                 :     }
    1224                 : 
    1225                 :     printf("Delete foo while iterating:\n");
    1226                 :     {
    1227                 :         zl = createList();
    1228                 :         p = ziplistIndex(zl,0);
    1229                 :         while (ziplistGet(p,&entry,&elen,&value)) {
    1230                 :             if (entry && strncmp("foo",(char*)entry,elen) == 0) {
    1231                 :                 printf("Delete foo\n");
    1232                 :                 zl = ziplistDelete(zl,&p);
    1233                 :             } else {
    1234                 :                 printf("Entry: ");
    1235                 :                 if (entry) {
    1236                 :                     if (elen && fwrite(entry,elen,1,stdout) == 0)
    1237                 :                         perror("fwrite");
    1238                 :                 } else {
    1239                 :                     printf("%lld",value);
    1240                 :                 }
    1241                 :                 p = ziplistNext(zl,p);
    1242                 :                 printf("\n");
    1243                 :             }
    1244                 :         }
    1245                 :         printf("\n");
    1246                 :         ziplistRepr(zl);
    1247                 :     }
    1248                 : 
    1249                 :     printf("Regression test for >255 byte strings:\n");
    1250                 :     {
    1251                 :         char v1[257],v2[257];
    1252                 :         memset(v1,'x',256);
    1253                 :         memset(v2,'y',256);
    1254                 :         zl = ziplistNew();
    1255                 :         zl = ziplistPush(zl,(unsigned char*)v1,strlen(v1),ZIPLIST_TAIL);
    1256                 :         zl = ziplistPush(zl,(unsigned char*)v2,strlen(v2),ZIPLIST_TAIL);
    1257                 : 
    1258                 :         /* Pop values again and compare their value. */
    1259                 :         p = ziplistIndex(zl,0);
    1260                 :         assert(ziplistGet(p,&entry,&elen,&value));
    1261                 :         assert(strncmp(v1,(char*)entry,elen) == 0);
    1262                 :         p = ziplistIndex(zl,1);
    1263                 :         assert(ziplistGet(p,&entry,&elen,&value));
    1264                 :         assert(strncmp(v2,(char*)entry,elen) == 0);
    1265                 :         printf("SUCCESS\n\n");
    1266                 :     }
    1267                 : 
    1268                 :     printf("Create long list and check indices:\n");
    1269                 :     {
    1270                 :         zl = ziplistNew();
    1271                 :         char buf[32];
    1272                 :         int i,len;
    1273                 :         for (i = 0; i < 1000; i++) {
    1274                 :             len = sprintf(buf,"%d",i);
    1275                 :             zl = ziplistPush(zl,(unsigned char*)buf,len,ZIPLIST_TAIL);
    1276                 :         }
    1277                 :         for (i = 0; i < 1000; i++) {
    1278                 :             p = ziplistIndex(zl,i);
    1279                 :             assert(ziplistGet(p,NULL,NULL,&value));
    1280                 :             assert(i == value);
    1281                 : 
    1282                 :             p = ziplistIndex(zl,-i-1);
    1283                 :             assert(ziplistGet(p,NULL,NULL,&value));
    1284                 :             assert(999-i == value);
    1285                 :         }
    1286                 :         printf("SUCCESS\n\n");
    1287                 :     }
    1288                 : 
    1289                 :     printf("Compare strings with ziplist entries:\n");
    1290                 :     {
    1291                 :         zl = createList();
    1292                 :         p = ziplistIndex(zl,0);
    1293                 :         if (!ziplistCompare(p,(unsigned char*)"hello",5)) {
    1294                 :             printf("ERROR: not \"hello\"\n");
    1295                 :             return 1;
    1296                 :         }
    1297                 :         if (ziplistCompare(p,(unsigned char*)"hella",5)) {
    1298                 :             printf("ERROR: \"hella\"\n");
    1299                 :             return 1;
    1300                 :         }
    1301                 : 
    1302                 :         p = ziplistIndex(zl,3);
    1303                 :         if (!ziplistCompare(p,(unsigned char*)"1024",4)) {
    1304                 :             printf("ERROR: not \"1024\"\n");
    1305                 :             return 1;
    1306                 :         }
    1307                 :         if (ziplistCompare(p,(unsigned char*)"1025",4)) {
    1308                 :             printf("ERROR: \"1025\"\n");
    1309                 :             return 1;
    1310                 :         }
    1311                 :         printf("SUCCESS\n\n");
    1312                 :     }
    1313                 : 
    1314                 :     printf("Stress with random payloads of different encoding:\n");
    1315                 :     {
    1316                 :         int i,j,len,where;
    1317                 :         unsigned char *p;
    1318                 :         char buf[1024];
    1319                 :         int buflen;
    1320                 :         list *ref;
    1321                 :         listNode *refnode;
    1322                 : 
    1323                 :         /* Hold temp vars from ziplist */
    1324                 :         unsigned char *sstr;
    1325                 :         unsigned int slen;
    1326                 :         long long sval;
    1327                 : 
    1328                 :         for (i = 0; i < 20000; i++) {
    1329                 :             zl = ziplistNew();
    1330                 :             ref = listCreate();
    1331                 :             listSetFreeMethod(ref,sdsfree);
    1332                 :             len = rand() % 256;
    1333                 : 
    1334                 :             /* Create lists */
    1335                 :             for (j = 0; j < len; j++) {
    1336                 :                 where = (rand() & 1) ? ZIPLIST_HEAD : ZIPLIST_TAIL;
    1337                 :                 if (rand() % 2) {
    1338                 :                     buflen = randstring(buf,1,sizeof(buf)-1);
    1339                 :                 } else {
    1340                 :                     switch(rand() % 3) {
    1341                 :                     case 0:
    1342                 :                         buflen = sprintf(buf,"%lld",(0LL + rand()) >> 20);
    1343                 :                         break;
    1344                 :                     case 1:
    1345                 :                         buflen = sprintf(buf,"%lld",(0LL + rand()));
    1346                 :                         break;
    1347                 :                     case 2:
    1348                 :                         buflen = sprintf(buf,"%lld",(0LL + rand()) << 20);
    1349                 :                         break;
    1350                 :                     default:
    1351                 :                         assert(NULL);
    1352                 :                     }
    1353                 :                 }
    1354                 : 
    1355                 :                 /* Add to ziplist */
    1356                 :                 zl = ziplistPush(zl, (unsigned char*)buf, buflen, where);
    1357                 : 
    1358                 :                 /* Add to reference list */
    1359                 :                 if (where == ZIPLIST_HEAD) {
    1360                 :                     listAddNodeHead(ref,sdsnewlen(buf, buflen));
    1361                 :                 } else if (where == ZIPLIST_TAIL) {
    1362                 :                     listAddNodeTail(ref,sdsnewlen(buf, buflen));
    1363                 :                 } else {
    1364                 :                     assert(NULL);
    1365                 :                 }
    1366                 :             }
    1367                 : 
    1368                 :             assert(listLength(ref) == ziplistLen(zl));
    1369                 :             for (j = 0; j < len; j++) {
    1370                 :                 /* Naive way to get elements, but similar to the stresser
    1371                 :                  * executed from the Tcl test suite. */
    1372                 :                 p = ziplistIndex(zl,j);
    1373                 :                 refnode = listIndex(ref,j);
    1374                 : 
    1375                 :                 assert(ziplistGet(p,&sstr,&slen,&sval));
    1376                 :                 if (sstr == NULL) {
    1377                 :                     buflen = sprintf(buf,"%lld",sval);
    1378                 :                 } else {
    1379                 :                     buflen = slen;
    1380                 :                     memcpy(buf,sstr,buflen);
    1381                 :                     buf[buflen] = '\0';
    1382                 :                 }
    1383                 :                 assert(memcmp(buf,listNodeValue(refnode),buflen) == 0);
    1384                 :             }
    1385                 :             zfree(zl);
    1386                 :             listRelease(ref);
    1387                 :         }
    1388                 :         printf("SUCCESS\n\n");
    1389                 :     }
    1390                 : 
    1391                 :     printf("Stress with variable ziplist size:\n");
    1392                 :     {
    1393                 :         stress(ZIPLIST_HEAD,100000,16384,256);
    1394                 :         stress(ZIPLIST_TAIL,100000,16384,256);
    1395                 :     }
    1396                 : 
    1397                 :     return 0;
    1398                 : }
    1399                 : 
    1400                 : #endif

Generated by: LCOV version 1.7