X-Git-Url: https://git.saurik.com/redis.git/blobdiff_plain/bfe85f7ca97259256e8089349e1a462b6c7dbd00..65fd32ab0a7ff1d3d052e3b70dcceac6719de58c:/src/ziplist.c diff --git a/src/ziplist.c b/src/ziplist.c index f5f9e9a6..b214b2da 100644 --- a/src/ziplist.c +++ b/src/ziplist.c @@ -69,12 +69,14 @@ #include "zmalloc.h" #include "util.h" #include "ziplist.h" -#include "endian.h" +#include "endianconv.h" #define ZIP_END 255 #define ZIP_BIGLEN 254 /* Different encoding/length possibilities */ +#define ZIP_STR_MASK (0xc0) +#define ZIP_INT_MASK (0x30) #define ZIP_STR_06B (0 << 6) #define ZIP_STR_14B (1 << 6) #define ZIP_STR_32B (2 << 6) @@ -82,9 +84,8 @@ #define ZIP_INT_32B (0xc0 | 1<<4) #define ZIP_INT_64B (0xc0 | 2<<4) -/* Macro's to determine type */ -#define ZIP_IS_STR(enc) (((enc) & 0xc0) < 0xc0) -#define ZIP_IS_INT(enc) (!ZIP_IS_STR(enc) && ((enc) & 0x30) < 0x30) +/* Macro to determine type */ +#define ZIP_IS_STR(enc) (((enc) & ZIP_STR_MASK) < ZIP_STR_MASK) /* Utility macros */ #define ZIPLIST_BYTES(zl) (*((uint32_t*)(zl))) @@ -92,13 +93,15 @@ #define ZIPLIST_LENGTH(zl) (*((uint16_t*)((zl)+sizeof(uint32_t)*2))) #define ZIPLIST_HEADER_SIZE (sizeof(uint32_t)*2+sizeof(uint16_t)) #define ZIPLIST_ENTRY_HEAD(zl) ((zl)+ZIPLIST_HEADER_SIZE) -#define ZIPLIST_ENTRY_TAIL(zl) ((zl)+ZIPLIST_TAIL_OFFSET(zl)) -#define ZIPLIST_ENTRY_END(zl) ((zl)+ZIPLIST_BYTES(zl)-1) +#define ZIPLIST_ENTRY_TAIL(zl) ((zl)+intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))) +#define ZIPLIST_ENTRY_END(zl) ((zl)+intrev32ifbe(ZIPLIST_BYTES(zl))-1) /* We know a positive increment can only be 1 because entries can only be * pushed one at a time. */ #define ZIPLIST_INCR_LENGTH(zl,incr) { \ - if (ZIPLIST_LENGTH(zl) < UINT16_MAX) ZIPLIST_LENGTH(zl)+=incr; } + if (ZIPLIST_LENGTH(zl) < UINT16_MAX) \ + ZIPLIST_LENGTH(zl) = intrev16ifbe(intrev16ifbe(ZIPLIST_LENGTH(zl))+incr); \ +} typedef struct zlentry { unsigned int prevrawlensize, prevrawlen; @@ -108,19 +111,13 @@ typedef struct zlentry { unsigned char *p; } zlentry; -/* Return the encoding pointer to by 'p'. */ -static unsigned int zipEntryEncoding(unsigned char *p) { - /* String encoding: 2 MSBs */ - unsigned char b = p[0] & 0xc0; - if (b < 0xc0) { - return b; - } else { - /* Integer encoding: 4 MSBs */ - return p[0] & 0xf0; - } - assert(NULL); - return 0; -} +#define ZIP_ENTRY_ENCODING(ptr, encoding) do { \ + (encoding) = (ptr[0]) & (ZIP_STR_MASK | ZIP_INT_MASK); \ + if (((encoding) & ZIP_STR_MASK) < ZIP_STR_MASK) { \ + /* String encoding: 2 MSBs */ \ + (encoding) &= ZIP_STR_MASK; \ + } \ +} while(0) /* Return bytes needed to store integer encoded by 'encoding' */ static unsigned int zipIntSize(unsigned char encoding) { @@ -133,36 +130,6 @@ static unsigned int zipIntSize(unsigned char encoding) { 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 = 0; - - if (ZIP_IS_STR(encoding)) { - switch(encoding) { - case ZIP_STR_06B: - len = p[0] & 0x3f; - if (lensize) *lensize = 1; - break; - case ZIP_STR_14B: - len = ((p[0] & 0x3f) << 8) | p[1]; - if (lensize) *lensize = 2; - break; - case ZIP_STR_32B: - len = (p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4]; - if (lensize) *lensize = 5; - break; - default: - assert(NULL); - } - } else { - len = zipIntSize(encoding); - if (lensize) *lensize = 1; - } - return len; -} - /* Encode the length 'l' writing it in 'p'. If p is NULL it just returns * the amount of bytes required to encode such a length. */ static unsigned int zipEncodeLength(unsigned char *p, unsigned char encoding, unsigned int rawlen) { @@ -199,18 +166,33 @@ static unsigned int zipEncodeLength(unsigned char *p, unsigned char encoding, un return len; } -/* Decode the length of the previous element stored at "p". */ -static unsigned int zipPrevDecodeLength(unsigned char *p, unsigned int *lensize) { - unsigned int len = *p; - if (len < ZIP_BIGLEN) { - if (lensize) *lensize = 1; - } else { - if (lensize) *lensize = 1+sizeof(len); - memcpy(&len,p+1,sizeof(len)); - memrev32ifbe(&len); - } - return len; -} +/* Decode the length encoded in 'ptr'. The 'encoding' variable will hold the + * entries encoding, the 'lensize' variable will hold the number of bytes + * required to encode the entries length, and the 'len' variable will hold the + * entries length. */ +#define ZIP_DECODE_LENGTH(ptr, encoding, lensize, len) do { \ + ZIP_ENTRY_ENCODING((ptr), (encoding)); \ + if ((encoding) < ZIP_STR_MASK) { \ + if ((encoding) == ZIP_STR_06B) { \ + (lensize) = 1; \ + (len) = (ptr)[0] & 0x3f; \ + } else if ((encoding) == ZIP_STR_14B) { \ + (lensize) = 2; \ + (len) = (((ptr)[0] & 0x3f) << 8) | (ptr)[1]; \ + } else if (encoding == ZIP_STR_32B) { \ + (lensize) = 5; \ + (len) = ((ptr)[1] << 24) | \ + ((ptr)[2] << 16) | \ + ((ptr)[3] << 8) | \ + ((ptr)[4]); \ + } else { \ + assert(NULL); \ + } \ + } else { \ + (lensize) = 1; \ + (len) = zipIntSize(encoding); \ + } \ +} while(0); /* Encode the length of the previous entry and write it to "p". Return the * number of bytes needed to encode this length if "p" is NULL. */ @@ -239,12 +221,43 @@ static void zipPrevEncodeLengthForceLarge(unsigned char *p, unsigned int len) { memrev32ifbe(p+1); } -/* Return the difference in number of bytes needed to store the new length - * "len" on the entry pointed to by "p". */ +/* Decode the number of bytes required to store the length of the previous + * element, from the perspective of the entry pointed to by 'ptr'. */ +#define ZIP_DECODE_PREVLENSIZE(ptr, prevlensize) do { \ + if ((ptr)[0] < ZIP_BIGLEN) { \ + (prevlensize) = 1; \ + } else { \ + (prevlensize) = 5; \ + } \ +} while(0); + +/* Decode the length of the previous element, from the perspective of the entry + * pointed to by 'ptr'. */ +#define ZIP_DECODE_PREVLEN(ptr, prevlensize, prevlen) do { \ + ZIP_DECODE_PREVLENSIZE(ptr, prevlensize); \ + if ((prevlensize) == 1) { \ + (prevlen) = (ptr)[0]; \ + } else if ((prevlensize) == 5) { \ + assert(sizeof((prevlensize)) == 4); \ + memcpy(&(prevlen), ((char*)(ptr)) + 1, 4); \ + memrev32ifbe(&prevlen); \ + } \ +} while(0); + +/* Return the difference in number of bytes needed to store the length of the + * previous element 'len', in the entry pointed to by 'p'. */ static int zipPrevLenByteDiff(unsigned char *p, unsigned int len) { unsigned int prevlensize; - zipPrevDecodeLength(p,&prevlensize); - return zipPrevEncodeLength(NULL,len)-prevlensize; + ZIP_DECODE_PREVLENSIZE(p, prevlensize); + return zipPrevEncodeLength(NULL, len) - prevlensize; +} + +/* Return the total number of bytes used by the entry pointed to by 'p'. */ +static unsigned int zipRawEntryLength(unsigned char *p) { + unsigned int prevlensize, encoding, lensize, len; + ZIP_DECODE_PREVLENSIZE(p, prevlensize); + ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len); + return prevlensize + lensize + len; } /* Check if string pointed to by 'entry' can be encoded as an integer. @@ -302,11 +315,11 @@ static int64_t zipLoadInteger(unsigned char *p, unsigned char encoding) { ret = i16; } else if (encoding == ZIP_INT_32B) { memcpy(&i32,p,sizeof(i32)); - memrev16ifbe(&i32); + memrev32ifbe(&i32); ret = i32; } else if (encoding == ZIP_INT_64B) { memcpy(&i64,p,sizeof(i64)); - memrev16ifbe(&i64); + memrev64ifbe(&i64); ret = i64; } else { assert(NULL); @@ -317,26 +330,20 @@ static int64_t zipLoadInteger(unsigned char *p, unsigned char encoding) { /* Return a struct with all information about an entry. */ static zlentry zipEntry(unsigned char *p) { zlentry e; - e.prevrawlen = zipPrevDecodeLength(p,&e.prevrawlensize); - e.len = zipDecodeLength(p+e.prevrawlensize,&e.lensize); - e.headersize = e.prevrawlensize+e.lensize; - e.encoding = zipEntryEncoding(p+e.prevrawlensize); + + ZIP_DECODE_PREVLEN(p, e.prevrawlensize, e.prevrawlen); + ZIP_DECODE_LENGTH(p + e.prevrawlensize, e.encoding, e.lensize, e.len); + e.headersize = e.prevrawlensize + e.lensize; e.p = p; return e; } -/* Return the total number of bytes used by the entry at "p". */ -static unsigned int zipRawEntryLength(unsigned char *p) { - zlentry e = zipEntry(p); - return e.headersize + e.len; -} - /* Create a new empty ziplist. */ unsigned char *ziplistNew(void) { unsigned int bytes = ZIPLIST_HEADER_SIZE+1; unsigned char *zl = zmalloc(bytes); - ZIPLIST_BYTES(zl) = bytes; - ZIPLIST_TAIL_OFFSET(zl) = ZIPLIST_HEADER_SIZE; + ZIPLIST_BYTES(zl) = intrev32ifbe(bytes); + ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(ZIPLIST_HEADER_SIZE); ZIPLIST_LENGTH(zl) = 0; zl[bytes-1] = ZIP_END; return zl; @@ -345,7 +352,7 @@ unsigned char *ziplistNew(void) { /* Resize the ziplist. */ static unsigned char *ziplistResize(unsigned char *zl, unsigned int len) { zl = zrealloc(zl,len); - ZIPLIST_BYTES(zl) = len; + ZIPLIST_BYTES(zl) = intrev32ifbe(len); zl[len-1] = ZIP_END; return zl; } @@ -371,7 +378,7 @@ 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) { - size_t curlen = ZIPLIST_BYTES(zl), rawlen, rawlensize; + size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), rawlen, rawlensize; size_t offset, noffset, extra; unsigned char *np; zlentry cur, next; @@ -401,8 +408,10 @@ static unsigned char *__ziplistCascadeUpdate(unsigned char *zl, unsigned char *p 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; + if ((zl+intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))) != np) { + ZIPLIST_TAIL_OFFSET(zl) = + intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+extra); + } /* Move the tail to the back. */ memmove(np+rawlensize, @@ -453,25 +462,30 @@ static unsigned char *__ziplistDelete(unsigned char *zl, unsigned char *p, unsig zipPrevEncodeLength(p-nextdiff,first.prevrawlen); /* Update offset for tail */ - ZIPLIST_TAIL_OFFSET(zl) -= totlen; + ZIPLIST_TAIL_OFFSET(zl) = + intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))-totlen); /* When the tail contains more than one entry, we need to take * "nextdiff" in account as well. Otherwise, a change in the * size of prevlen doesn't have an effect on the *tail* offset. */ tail = zipEntry(p); - if (p[tail.headersize+tail.len] != ZIP_END) - ZIPLIST_TAIL_OFFSET(zl) += nextdiff; + if (p[tail.headersize+tail.len] != ZIP_END) { + ZIPLIST_TAIL_OFFSET(zl) = + intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+nextdiff); + } /* Move tail to the front of the ziplist */ - memmove(first.p,p-nextdiff,ZIPLIST_BYTES(zl)-(p-zl)-1+nextdiff); + memmove(first.p,p-nextdiff, + intrev32ifbe(ZIPLIST_BYTES(zl))-(p-zl)-1+nextdiff); } else { /* The entire tail was deleted. No need to move memory. */ - ZIPLIST_TAIL_OFFSET(zl) = (first.p-zl)-first.prevrawlen; + ZIPLIST_TAIL_OFFSET(zl) = + intrev32ifbe((first.p-zl)-first.prevrawlen); } /* Resize and update length */ offset = first.p-zl; - zl = ziplistResize(zl, ZIPLIST_BYTES(zl)-totlen+nextdiff); + zl = ziplistResize(zl, intrev32ifbe(ZIPLIST_BYTES(zl))-totlen+nextdiff); ZIPLIST_INCR_LENGTH(zl,-deleted); p = zl+offset; @@ -485,11 +499,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) { - size_t curlen = ZIPLIST_BYTES(zl), reqlen, prevlen = 0; + size_t curlen = intrev32ifbe(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. */ @@ -536,17 +552,20 @@ static unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsig zipPrevEncodeLength(p+reqlen,reqlen); /* Update offset for tail */ - ZIPLIST_TAIL_OFFSET(zl) += reqlen; + ZIPLIST_TAIL_OFFSET(zl) = + intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+reqlen); /* When the tail contains more than one entry, we need to take * "nextdiff" in account as well. Otherwise, a change in the * size of prevlen doesn't have an effect on the *tail* offset. */ tail = zipEntry(p+reqlen); - if (p[reqlen+tail.headersize+tail.len] != ZIP_END) - ZIPLIST_TAIL_OFFSET(zl) += nextdiff; + if (p[reqlen+tail.headersize+tail.len] != ZIP_END) { + ZIPLIST_TAIL_OFFSET(zl) = + intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+nextdiff); + } } else { /* This element will be the new tail. */ - ZIPLIST_TAIL_OFFSET(zl) = p-zl; + ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(p-zl); } /* When nextdiff != 0, the raw length of the next entry has changed, so @@ -614,10 +633,14 @@ unsigned char *ziplistNext(unsigned char *zl, unsigned char *p) { * when the *next* element is ZIP_END (there is no next entry). */ if (p[0] == ZIP_END) { return NULL; - } else { - p = p+zipRawEntryLength(p); - return (p[0] == ZIP_END) ? NULL : p; } + + p += zipRawEntryLength(p); + if (p[0] == ZIP_END) { + return NULL; + } + + return p; } /* Return pointer to previous entry in ziplist. */ @@ -715,11 +738,67 @@ unsigned int ziplistCompare(unsigned char *p, unsigned char *sstr, unsigned int return 0; } +/* Find pointer to the entry equal to the specified entry. Skip 'skip' entries + * between every comparison. Returns NULL when the field could not be found. */ +unsigned char *ziplistFind(unsigned char *p, unsigned char *vstr, unsigned int vlen, unsigned int skip) { + int skipcnt = 0; + unsigned char vencoding = 0; + long long vll = 0; + + while (p[0] != ZIP_END) { + unsigned int prevlensize, encoding, lensize, len; + unsigned char *q; + + ZIP_DECODE_PREVLENSIZE(p, prevlensize); + ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len); + q = p + prevlensize + lensize; + + if (skipcnt == 0) { + /* Compare current entry with specified entry */ + if (ZIP_IS_STR(encoding)) { + if (len == vlen && memcmp(q, vstr, vlen) == 0) { + return p; + } + } else { + /* Find out if the specified entry can be encoded */ + if (vencoding == 0) { + /* UINT_MAX when the entry CANNOT be encoded */ + if (!zipTryEncoding(vstr, vlen, &vll, &vencoding)) { + vencoding = UCHAR_MAX; + } + + /* Must be non-zero by now */ + assert(vencoding); + } + + /* Compare current entry with specified entry */ + if (encoding == vencoding) { + long long ll = zipLoadInteger(q, encoding); + if (ll == vll) { + return p; + } + } + } + + /* Reset skip count */ + skipcnt = skip; + } else { + /* Skip entry */ + skipcnt--; + } + + /* Move to next entry */ + p = q + len; + } + + return NULL; +} + /* Return length of ziplist. */ unsigned int ziplistLen(unsigned char *zl) { unsigned int len = 0; - if (ZIPLIST_LENGTH(zl) < UINT16_MAX) { - len = ZIPLIST_LENGTH(zl); + if (intrev16ifbe(ZIPLIST_LENGTH(zl)) < UINT16_MAX) { + len = intrev16ifbe(ZIPLIST_LENGTH(zl)); } else { unsigned char *p = zl+ZIPLIST_HEADER_SIZE; while (*p != ZIP_END) { @@ -728,14 +807,14 @@ unsigned int ziplistLen(unsigned char *zl) { } /* Re-store length if small enough */ - if (len < UINT16_MAX) ZIPLIST_LENGTH(zl) = len; + if (len < UINT16_MAX) ZIPLIST_LENGTH(zl) = intrev16ifbe(len); } return len; } /* Return ziplist blob size in bytes. */ size_t ziplistBlobLen(unsigned char *zl) { - return ZIPLIST_BYTES(zl); + return intrev32ifbe(ZIPLIST_BYTES(zl)); } void ziplistRepr(unsigned char *zl) { @@ -747,9 +826,9 @@ void ziplistRepr(unsigned char *zl) { "{total bytes %d} " "{length %u}\n" "{tail offset %u}\n", - ZIPLIST_BYTES(zl), - ZIPLIST_LENGTH(zl), - ZIPLIST_TAIL_OFFSET(zl)); + intrev32ifbe(ZIPLIST_BYTES(zl)), + intrev16ifbe(ZIPLIST_LENGTH(zl)), + intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))); p = ZIPLIST_ENTRY_HEAD(zl); while(*p != ZIP_END) { entry = zipEntry(p); @@ -850,7 +929,7 @@ void stress(int pos, int num, int maxsize, int dnum) { zl = ziplistDeleteRange(zl,0,1); } printf("List size: %8d, bytes: %8d, %dx push+pop (%s): %6lld usec\n", - i,ZIPLIST_BYTES(zl),num,posstr[pos],usec()-start); + i,intrev32ifbe(ZIPLIST_BYTES(zl)),num,posstr[pos],usec()-start); zfree(zl); } }