]> git.saurik.com Git - redis.git/blobdiff - src/ziplist.c
This fixes issue #327, is a very complex fix (unfortunately), details:
[redis.git] / src / ziplist.c
index 98ad3113e80dd89f4e74d4d8c7f21b0f2503f23a..639e4b61ec3cc9c0e5f742448ff19c3420e5d2be 100644 (file)
@@ -67,9 +67,9 @@
 #include <assert.h>
 #include <limits.h>
 #include "zmalloc.h"
+#include "util.h"
 #include "ziplist.h"
-
-int ll2string(char *s, size_t len, long long value);
+#include "endian.h"
 
 #define ZIP_END 255
 #define ZIP_BIGLEN 254
@@ -119,6 +119,7 @@ static unsigned int zipEntryEncoding(unsigned char *p) {
         return p[0] & 0xf0;
     }
     assert(NULL);
+    return 0;
 }
 
 /* Return bytes needed to store integer encoded by 'encoding' */
@@ -129,13 +130,14 @@ static unsigned int zipIntSize(unsigned char encoding) {
     case ZIP_INT_64B: return sizeof(int64_t);
     }
     assert(NULL);
+    return 0;
 }
 
 /* Decode the encoded length pointed by 'p'. If a pointer to 'lensize' is
  * provided, it is set to the number of bytes required to encode the length. */
 static unsigned int zipDecodeLength(unsigned char *p, unsigned int *lensize) {
     unsigned char encoding = zipEntryEncoding(p);
-    unsigned int len;
+    unsigned int len = 0;
 
     if (ZIP_IS_STR(encoding)) {
         switch(encoding) {
@@ -205,6 +207,7 @@ static unsigned int zipPrevDecodeLength(unsigned char *p, unsigned int *lensize)
     } else {
         if (lensize) *lensize = 1+sizeof(len);
         memcpy(&len,p+1,sizeof(len));
+        memrev32ifbe(&len);
     }
     return len;
 }
@@ -221,6 +224,7 @@ static unsigned int zipPrevEncodeLength(unsigned char *p, unsigned int len) {
         } else {
             p[0] = ZIP_BIGLEN;
             memcpy(p+1,&len,sizeof(len));
+            memrev32ifbe(p+1);
             return 1+sizeof(len);
         }
     }
@@ -232,6 +236,7 @@ static void zipPrevEncodeLengthForceLarge(unsigned char *p, unsigned int len) {
     if (p == NULL) return;
     p[0] = ZIP_BIGLEN;
     memcpy(p+1,&len,sizeof(len));
+    memrev32ifbe(p+1);
 }
 
 /* Return the difference in number of bytes needed to store the new length
@@ -246,22 +251,9 @@ static int zipPrevLenByteDiff(unsigned char *p, unsigned int len) {
  * Stores the integer value in 'v' and its encoding in 'encoding'. */
 static int zipTryEncoding(unsigned char *entry, unsigned int entrylen, long long *v, unsigned char *encoding) {
     long long value;
-    char *eptr;
-    char buf[32];
 
     if (entrylen >= 32 || entrylen == 0) return 0;
-    if (entry[0] == '-' || (entry[0] >= '0' && entry[0] <= '9')) {
-        int slen;
-
-        /* Perform a back-and-forth conversion to make sure that
-         * the string turned into an integer is not losing any info. */
-        memcpy(buf,entry,entrylen);
-        buf[entrylen] = '\0';
-        value = strtoll(buf,&eptr,10);
-        if (eptr[0] != '\0') return 0;
-        slen = ll2string(buf,32,value);
-        if (entrylen != (unsigned)slen || memcmp(buf,entry,slen)) return 0;
-
+    if (string2ll((char*)entry,entrylen,&value)) {
         /* Great, the string can be encoded. Check what's the smallest
          * of our encoding types that can hold this value. */
         if (value >= INT16_MIN && value <= INT16_MAX) {
@@ -285,12 +277,15 @@ static void zipSaveInteger(unsigned char *p, int64_t value, unsigned char encodi
     if (encoding == ZIP_INT_16B) {
         i16 = value;
         memcpy(p,&i16,sizeof(i16));
+        memrev16ifbe(p);
     } else if (encoding == ZIP_INT_32B) {
         i32 = value;
         memcpy(p,&i32,sizeof(i32));
+        memrev32ifbe(p);
     } else if (encoding == ZIP_INT_64B) {
         i64 = value;
         memcpy(p,&i64,sizeof(i64));
+        memrev64ifbe(p);
     } else {
         assert(NULL);
     }
@@ -300,15 +295,18 @@ static void zipSaveInteger(unsigned char *p, int64_t value, unsigned char encodi
 static int64_t zipLoadInteger(unsigned char *p, unsigned char encoding) {
     int16_t i16;
     int32_t i32;
-    int64_t i64, ret;
+    int64_t i64, ret = 0;
     if (encoding == ZIP_INT_16B) {
         memcpy(&i16,p,sizeof(i16));
+        memrev16ifbe(&i16);
         ret = i16;
     } else if (encoding == ZIP_INT_32B) {
         memcpy(&i32,p,sizeof(i32));
+        memrev16ifbe(&i32);
         ret = i32;
     } else if (encoding == ZIP_INT_64B) {
         memcpy(&i64,p,sizeof(i64));
+        memrev16ifbe(&i64);
         ret = i64;
     } else {
         assert(NULL);
@@ -373,8 +371,8 @@ static unsigned char *ziplistResize(unsigned char *zl, unsigned int len) {
  * The pointer "p" points to the first entry that does NOT need to be
  * updated, i.e. consecutive fields MAY need an update. */
 static unsigned char *__ziplistCascadeUpdate(unsigned char *zl, unsigned char *p) {
-    unsigned int curlen = ZIPLIST_BYTES(zl), rawlen, rawlensize;
-    unsigned int offset, noffset, extra;
+    size_t curlen = ZIPLIST_BYTES(zl), rawlen, rawlensize;
+    size_t offset, noffset, extra;
     unsigned char *np;
     zlentry cur, next;
 
@@ -396,12 +394,17 @@ static unsigned char *__ziplistCascadeUpdate(unsigned char *zl, unsigned char *p
             offset = p-zl;
             extra = rawlensize-next.prevrawlensize;
             zl = ziplistResize(zl,curlen+extra);
-            ZIPLIST_TAIL_OFFSET(zl) += extra;
             p = zl+offset;
 
-            /* Move the tail to the back. */
+            /* Current pointer and offset for next element. */
             np = p+rawlen;
             noffset = np-zl;
+
+            /* Update tail offset when next element is not the tail element. */
+            if ((zl+ZIPLIST_TAIL_OFFSET(zl)) != np)
+                ZIPLIST_TAIL_OFFSET(zl) += extra;
+
+            /* Move the tail to the back. */
             memmove(np+rawlensize,
                 np+next.prevrawlensize,
                 curlen-noffset-next.prevrawlensize-1);
@@ -429,7 +432,8 @@ static unsigned char *__ziplistCascadeUpdate(unsigned char *zl, unsigned char *p
 /* Delete "num" entries, starting at "p". Returns pointer to the ziplist. */
 static unsigned char *__ziplistDelete(unsigned char *zl, unsigned char *p, unsigned int num) {
     unsigned int i, totlen, deleted = 0;
-    int offset, nextdiff = 0;
+    size_t offset;
+    int nextdiff = 0;
     zlentry first, tail;
 
     first = zipEntry(p);
@@ -481,10 +485,13 @@ static unsigned char *__ziplistDelete(unsigned char *zl, unsigned char *p, unsig
 
 /* Insert item at "p". */
 static unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {
-    unsigned int curlen = ZIPLIST_BYTES(zl), reqlen, prevlen = 0;
-    unsigned int offset, nextdiff = 0;
+    size_t curlen = ZIPLIST_BYTES(zl), reqlen, prevlen = 0;
+    size_t offset;
+    int nextdiff = 0;
     unsigned char encoding = 0;
-    long long value;
+    long long value = 123456789; /* initialized to avoid warning. Using a value
+                                    that is easy to see if for some reason
+                                    we use it uninitialized. */
     zlentry entry, tail;
 
     /* Find out prevlen for the entry that is inserted. */
@@ -595,7 +602,12 @@ unsigned char *ziplistIndex(unsigned char *zl, int index) {
     return (p[0] == ZIP_END || index > 0) ? NULL : p;
 }
 
-/* Return pointer to next entry in ziplist. */
+/* Return pointer to next entry in ziplist.
+ *
+ * zl is the pointer to the ziplist
+ * p is the pointer to the current element
+ *
+ * The element after 'p' is returned, otherwise NULL if we are at the end. */
 unsigned char *ziplistNext(unsigned char *zl, unsigned char *p) {
     ((void) zl);
 
@@ -661,7 +673,7 @@ unsigned char *ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char
  * Also update *p in place, to be able to iterate over the
  * ziplist, while deleting entries. */
 unsigned char *ziplistDelete(unsigned char *zl, unsigned char **p) {
-    unsigned int offset = *p-zl;
+    size_t offset = *p-zl;
     zl = __ziplistDelete(zl,*p,1);
 
     /* Store pointer to current element in p, because ziplistDelete will
@@ -723,8 +735,8 @@ unsigned int ziplistLen(unsigned char *zl) {
     return len;
 }
 
-/* Return size in bytes of ziplist. */
-unsigned int ziplistSize(unsigned char *zl) {
+/* Return ziplist blob size in bytes. */
+size_t ziplistBlobLen(unsigned char *zl) {
     return ZIPLIST_BYTES(zl);
 }
 
@@ -754,9 +766,9 @@ void ziplistRepr(unsigned char *zl) {
                 "pls: %2u, "
                 "payload %5u"
             "} ",
-            (long unsigned int)p,
+            (long unsigned)p,
             index,
-            p-zl,
+            (unsigned long) (p-zl),
             entry.headersize+entry.len,
             entry.headersize,
             entry.prevrawlen,
@@ -765,10 +777,11 @@ void ziplistRepr(unsigned char *zl) {
         p += entry.headersize;
         if (ZIP_IS_STR(entry.encoding)) {
             if (entry.len > 40) {
-                fwrite(p,40,1,stdout);
+                if (fwrite(p,40,1,stdout) == 0) perror("fwrite");
                 printf("...");
             } else {
-                fwrite(p,entry.len,1,stdout);
+                if (entry.len &&
+                    fwrite(p,entry.len,1,stdout) == 0) perror("fwrite");
             }
         } else {
             printf("%lld", (long long) zipLoadInteger(p,entry.encoding));
@@ -857,7 +870,7 @@ void pop(unsigned char *zl, int where) {
             printf("Pop tail: ");
 
         if (vstr)
-            fwrite(vstr,vlen,1,stdout);
+            if (vlen && fwrite(vstr,vlen,1,stdout) == 0) perror("fwrite");
         else
             printf("%lld", vlong);
 
@@ -869,7 +882,7 @@ void pop(unsigned char *zl, int where) {
     }
 }
 
-void randstring(char *target, unsigned int min, unsigned int max) {
+int randstring(char *target, unsigned int min, unsigned int max) {
     int p, len = min+rand()%(max-min+1);
     int minval, maxval;
     switch(rand() % 3) {
@@ -891,10 +904,9 @@ void randstring(char *target, unsigned int min, unsigned int max) {
 
     while(p < len)
         target[p++] = minval+rand()%(maxval-minval+1);
-    return;
+    return len;
 }
 
-
 int main(int argc, char **argv) {
     unsigned char *zl, *p;
     unsigned char *entry;
@@ -932,7 +944,7 @@ int main(int argc, char **argv) {
             return 1;
         }
         if (entry) {
-            fwrite(entry,elen,1,stdout);
+            if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite");
             printf("\n");
         } else {
             printf("%lld\n", value);
@@ -962,7 +974,7 @@ int main(int argc, char **argv) {
             return 1;
         }
         if (entry) {
-            fwrite(entry,elen,1,stdout);
+            if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite");
             printf("\n");
         } else {
             printf("%lld\n", value);
@@ -979,7 +991,7 @@ int main(int argc, char **argv) {
             return 1;
         }
         if (entry) {
-            fwrite(entry,elen,1,stdout);
+            if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite");
             printf("\n");
         } else {
             printf("%lld\n", value);
@@ -1007,7 +1019,7 @@ int main(int argc, char **argv) {
         while (ziplistGet(p, &entry, &elen, &value)) {
             printf("Entry: ");
             if (entry) {
-                fwrite(entry,elen,1,stdout);
+                if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite");
             } else {
                 printf("%lld", value);
             }
@@ -1024,7 +1036,7 @@ int main(int argc, char **argv) {
         while (ziplistGet(p, &entry, &elen, &value)) {
             printf("Entry: ");
             if (entry) {
-                fwrite(entry,elen,1,stdout);
+                if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite");
             } else {
                 printf("%lld", value);
             }
@@ -1041,7 +1053,7 @@ int main(int argc, char **argv) {
         while (ziplistGet(p, &entry, &elen, &value)) {
             printf("Entry: ");
             if (entry) {
-                fwrite(entry,elen,1,stdout);
+                if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite");
             } else {
                 printf("%lld", value);
             }
@@ -1070,7 +1082,7 @@ int main(int argc, char **argv) {
         while (ziplistGet(p, &entry, &elen, &value)) {
             printf("Entry: ");
             if (entry) {
-                fwrite(entry,elen,1,stdout);
+                if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite");
             } else {
                 printf("%lld", value);
             }
@@ -1087,7 +1099,7 @@ int main(int argc, char **argv) {
         while (ziplistGet(p, &entry, &elen, &value)) {
             printf("Entry: ");
             if (entry) {
-                fwrite(entry,elen,1,stdout);
+                if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite");
             } else {
                 printf("%lld", value);
             }
@@ -1144,7 +1156,8 @@ int main(int argc, char **argv) {
             } else {
                 printf("Entry: ");
                 if (entry) {
-                    fwrite(entry,elen,1,stdout);
+                    if (elen && fwrite(entry,elen,1,stdout) == 0)
+                        perror("fwrite");
                 } else {
                     printf("%lld",value);
                 }
@@ -1226,6 +1239,7 @@ int main(int argc, char **argv) {
         int i,j,len,where;
         unsigned char *p;
         char buf[1024];
+        int buflen;
         list *ref;
         listNode *refnode;
 
@@ -1234,10 +1248,6 @@ int main(int argc, char **argv) {
         unsigned int slen;
         long long sval;
 
-        /* In the regression for the cascade bug, it was triggered
-         * with a random seed of 2. */
-        srand(2);
-
         for (i = 0; i < 20000; i++) {
             zl = ziplistNew();
             ref = listCreate();
@@ -1247,31 +1257,32 @@ int main(int argc, char **argv) {
             /* Create lists */
             for (j = 0; j < len; j++) {
                 where = (rand() & 1) ? ZIPLIST_HEAD : ZIPLIST_TAIL;
-                switch(rand() % 4) {
-                case 0:
-                    sprintf(buf,"%lld",(0LL + rand()) >> 20);
-                    break;
-                case 1:
-                    sprintf(buf,"%lld",(0LL + rand()));
-                    break;
-                case 2:
-                    sprintf(buf,"%lld",(0LL + rand()) << 20);
-                    break;
-                case 3:
-                    randstring(buf,0,256);
-                break;
-                default:
-                    assert(NULL);
+                if (rand() % 2) {
+                    buflen = randstring(buf,1,sizeof(buf)-1);
+                } else {
+                    switch(rand() % 3) {
+                    case 0:
+                        buflen = sprintf(buf,"%lld",(0LL + rand()) >> 20);
+                        break;
+                    case 1:
+                        buflen = sprintf(buf,"%lld",(0LL + rand()));
+                        break;
+                    case 2:
+                        buflen = sprintf(buf,"%lld",(0LL + rand()) << 20);
+                        break;
+                    default:
+                        assert(NULL);
+                    }
                 }
 
                 /* Add to ziplist */
-                zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), where);
+                zl = ziplistPush(zl, (unsigned char*)buf, buflen, where);
 
                 /* Add to reference list */
                 if (where == ZIPLIST_HEAD) {
-                    listAddNodeHead(ref,sdsnew(buf));
+                    listAddNodeHead(ref,sdsnewlen(buf, buflen));
                 } else if (where == ZIPLIST_TAIL) {
-                    listAddNodeTail(ref,sdsnew(buf));
+                    listAddNodeTail(ref,sdsnewlen(buf, buflen));
                 } else {
                     assert(NULL);
                 }
@@ -1286,12 +1297,13 @@ int main(int argc, char **argv) {
 
                 assert(ziplistGet(p,&sstr,&slen,&sval));
                 if (sstr == NULL) {
-                    sprintf(buf,"%lld",sval);
+                    buflen = sprintf(buf,"%lld",sval);
                 } else {
-                    memcpy(buf,sstr,slen);
-                    buf[slen] = '\0';
+                    buflen = slen;
+                    memcpy(buf,sstr,buflen);
+                    buf[buflen] = '\0';
                 }
-                assert(strcmp(buf,listNodeValue(refnode)) == 0);
+                assert(memcmp(buf,listNodeValue(refnode),buflen) == 0);
             }
             zfree(zl);
             listRelease(ref);