Commit ba07e1f5 authored by antirez's avatar antirez
Browse files

Merge branch '2.4' of github.com:antirez/redis into 2.4

parents 8ec9d6ef deff2338
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "ziplist.h" /* Compact list data structure */ #include "ziplist.h" /* Compact list data structure */
#include "intset.h" /* Compact integer set structure */ #include "intset.h" /* Compact integer set structure */
#include "version.h" #include "version.h"
#include "util.h"
/* Error codes */ /* Error codes */
#define REDIS_OK 0 #define REDIS_OK 0
...@@ -719,6 +720,7 @@ void freeHashObject(robj *o); ...@@ -719,6 +720,7 @@ void freeHashObject(robj *o);
robj *createObject(int type, void *ptr); robj *createObject(int type, void *ptr);
robj *createStringObject(char *ptr, size_t len); robj *createStringObject(char *ptr, size_t len);
robj *dupStringObject(robj *o); robj *dupStringObject(robj *o);
int isObjectRepresentableAsLongLong(robj *o, long long *llongval);
robj *tryObjectEncoding(robj *o); robj *tryObjectEncoding(robj *o);
robj *getDecodedObject(robj *o); robj *getDecodedObject(robj *o);
size_t stringObjectLen(robj *o); size_t stringObjectLen(robj *o);
...@@ -872,18 +874,6 @@ int pubsubUnsubscribeAllPatterns(redisClient *c, int notify); ...@@ -872,18 +874,6 @@ int pubsubUnsubscribeAllPatterns(redisClient *c, int notify);
void freePubsubPattern(void *p); void freePubsubPattern(void *p);
int listMatchPubsubPattern(void *a, void *b); int listMatchPubsubPattern(void *a, void *b);
/* Utility functions */
int stringmatchlen(const char *pattern, int patternLen,
const char *string, int stringLen, int nocase);
int stringmatch(const char *pattern, const char *string, int nocase);
long long memtoll(const char *p, int *err);
int ll2string(char *s, size_t len, long long value);
int string2ll(char *s, size_t len, long long *value);
int d2string(char *s, size_t len, double value);
int isStringRepresentableAsLong(sds s, long *longval);
int isStringRepresentableAsLongLong(sds s, long long *longval);
int isObjectRepresentableAsLongLong(robj *o, long long *llongval);
/* Configuration */ /* Configuration */
void loadServerConfig(char *filename); void loadServerConfig(char *filename);
void appendServerSaveParams(time_t seconds, int changes); void appendServerSaveParams(time_t seconds, int changes);
......
...@@ -36,11 +36,11 @@ ...@@ -36,11 +36,11 @@
#define SDS_ABORT_ON_OOM #define SDS_ABORT_ON_OOM
#include "sds.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include "sds.h"
#include "zmalloc.h" #include "zmalloc.h"
static void sdsOomAbort(void) { static void sdsOomAbort(void) {
...@@ -76,11 +76,6 @@ sds sdsnew(const char *init) { ...@@ -76,11 +76,6 @@ sds sdsnew(const char *init) {
return sdsnewlen(init, initlen); return sdsnewlen(init, initlen);
} }
size_t sdslen(const sds s) {
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
return sh->len;
}
sds sdsdup(const sds s) { sds sdsdup(const sds s) {
return sdsnewlen(s, sdslen(s)); return sdsnewlen(s, sdslen(s));
} }
...@@ -90,11 +85,6 @@ void sdsfree(sds s) { ...@@ -90,11 +85,6 @@ void sdsfree(sds s) {
zfree(s-sizeof(struct sdshdr)); zfree(s-sizeof(struct sdshdr));
} }
size_t sdsavail(sds s) {
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
return sh->free;
}
void sdsupdatelen(sds s) { void sdsupdatelen(sds s) {
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
int reallen = strlen(s); int reallen = strlen(s);
...@@ -306,12 +296,17 @@ int sdscmp(sds s1, sds s2) { ...@@ -306,12 +296,17 @@ int sdscmp(sds s1, sds s2) {
*/ */
sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count) { sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count) {
int elements = 0, slots = 5, start = 0, j; int elements = 0, slots = 5, start = 0, j;
sds *tokens;
if (seplen < 1 || len < 0) return NULL;
sds *tokens = zmalloc(sizeof(sds)*slots); tokens = zmalloc(sizeof(sds)*slots);
#ifdef SDS_ABORT_ON_OOM #ifdef SDS_ABORT_ON_OOM
if (tokens == NULL) sdsOomAbort(); if (tokens == NULL) sdsOomAbort();
#else
if (tokens == NULL) return NULL;
#endif #endif
if (seplen < 1 || len < 0 || tokens == NULL) return NULL;
if (len == 0) { if (len == 0) {
*count = 0; *count = 0;
return tokens; return tokens;
...@@ -401,11 +396,11 @@ sds sdscatrepr(sds s, char *p, size_t len) { ...@@ -401,11 +396,11 @@ sds sdscatrepr(sds s, char *p, size_t len) {
case '"': case '"':
s = sdscatprintf(s,"\\%c",*p); s = sdscatprintf(s,"\\%c",*p);
break; break;
case '\n': s = sdscatlen(s,"\\n",1); break; case '\n': s = sdscatlen(s,"\\n",2); break;
case '\r': s = sdscatlen(s,"\\r",1); break; case '\r': s = sdscatlen(s,"\\r",2); break;
case '\t': s = sdscatlen(s,"\\t",1); break; case '\t': s = sdscatlen(s,"\\t",2); break;
case '\a': s = sdscatlen(s,"\\a",1); break; case '\a': s = sdscatlen(s,"\\a",2); break;
case '\b': s = sdscatlen(s,"\\b",1); break; case '\b': s = sdscatlen(s,"\\b",2); break;
default: default:
if (isprint(*p)) if (isprint(*p))
s = sdscatprintf(s,"%c",*p); s = sdscatprintf(s,"%c",*p);
......
...@@ -42,6 +42,16 @@ struct sdshdr { ...@@ -42,6 +42,16 @@ struct sdshdr {
char buf[]; char buf[];
}; };
static inline size_t sdslen(const sds s) {
struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
return sh->len;
}
static inline size_t sdsavail(const sds s) {
struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
return sh->free;
}
sds sdsnewlen(const void *init, size_t initlen); sds sdsnewlen(const void *init, size_t initlen);
sds sdsnew(const char *init); sds sdsnew(const char *init);
sds sdsempty(); sds sdsempty();
......
#include "redis.h" #include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h> #include <ctype.h>
#include <limits.h> #include <limits.h>
#include <math.h> #include <math.h>
#include "util.h"
/* Glob-style pattern matching. */ /* Glob-style pattern matching. */
int stringmatchlen(const char *pattern, int patternLen, int stringmatchlen(const char *pattern, int patternLen,
...@@ -213,6 +216,12 @@ int string2ll(char *s, size_t slen, long long *value) { ...@@ -213,6 +216,12 @@ int string2ll(char *s, size_t slen, long long *value) {
if (plen == slen) if (plen == slen)
return 0; return 0;
/* Special case: first and only digit is 0. */
if (slen == 1 && p[0] == '0') {
if (value != NULL) *value = 0;
return 1;
}
if (p[0] == '-') { if (p[0] == '-') {
negative = 1; negative = 1;
p++; plen++; p++; plen++;
...@@ -250,7 +259,7 @@ int string2ll(char *s, size_t slen, long long *value) { ...@@ -250,7 +259,7 @@ int string2ll(char *s, size_t slen, long long *value) {
return 0; return 0;
if (negative) { if (negative) {
if (v > (-(unsigned long long)LLONG_MIN)) /* Overflow. */ if (v > ((unsigned long long)(-(LLONG_MIN+1))+1)) /* Overflow. */
return 0; return 0;
if (value != NULL) *value = -v; if (value != NULL) *value = -v;
} else { } else {
...@@ -261,6 +270,22 @@ int string2ll(char *s, size_t slen, long long *value) { ...@@ -261,6 +270,22 @@ int string2ll(char *s, size_t slen, long long *value) {
return 1; return 1;
} }
/* Convert a string into a long. Returns 1 if the string could be parsed into a
* (non-overflowing) long, 0 otherwise. The value will be set to the parsed
* value when appropriate. */
int string2l(char *s, size_t slen, long *lval) {
long long llval;
if (!string2ll(s,slen,&llval))
return 0;
if (llval < LONG_MIN || llval > LONG_MAX)
return 0;
*lval = (long)llval;
return 1;
}
/* Convert a double to a string representation. Returns the number of bytes /* Convert a double to a string representation. Returns the number of bytes
* required. The representation should always be parsable by stdtod(3). */ * required. The representation should always be parsable by stdtod(3). */
int d2string(char *buf, size_t len, double value) { int d2string(char *buf, size_t len, double value) {
...@@ -300,44 +325,116 @@ int d2string(char *buf, size_t len, double value) { ...@@ -300,44 +325,116 @@ int d2string(char *buf, size_t len, double value) {
return len; return len;
} }
/* Check if the sds string 's' can be represented by a long long #ifdef UTIL_TEST_MAIN
* (that is, is a number that fits into long without any other space or #include <assert.h>
* character before or after the digits, so that converting this number
* back to a string will result in the same bytes as the original string). void test_string2ll(void) {
* char buf[32];
* If so, the function returns REDIS_OK and *llongval is set to the value long long v;
* of the number. Otherwise REDIS_ERR is returned */
int isStringRepresentableAsLongLong(sds s, long long *llongval) { /* May not start with +. */
char buf[32], *endptr; strcpy(buf,"+1");
long long value; assert(string2ll(buf,strlen(buf),&v) == 0);
int slen;
/* Leading space. */
value = strtoll(s, &endptr, 10); strcpy(buf," 1");
if (endptr[0] != '\0') return REDIS_ERR; assert(string2ll(buf,strlen(buf),&v) == 0);
slen = ll2string(buf,32,value);
/* Trailing space. */
/* If the number converted back into a string is not identical strcpy(buf,"1 ");
* then it's not possible to encode the string as integer */ assert(string2ll(buf,strlen(buf),&v) == 0);
if (sdslen(s) != (unsigned)slen || memcmp(buf,s,slen)) return REDIS_ERR;
if (llongval) *llongval = value; /* May not start with 0. */
return REDIS_OK; strcpy(buf,"01");
assert(string2ll(buf,strlen(buf),&v) == 0);
strcpy(buf,"-1");
assert(string2ll(buf,strlen(buf),&v) == 1);
assert(v == -1);
strcpy(buf,"0");
assert(string2ll(buf,strlen(buf),&v) == 1);
assert(v == 0);
strcpy(buf,"1");
assert(string2ll(buf,strlen(buf),&v) == 1);
assert(v == 1);
strcpy(buf,"99");
assert(string2ll(buf,strlen(buf),&v) == 1);
assert(v == 99);
strcpy(buf,"-99");
assert(string2ll(buf,strlen(buf),&v) == 1);
assert(v == -99);
strcpy(buf,"-9223372036854775808");
assert(string2ll(buf,strlen(buf),&v) == 1);
assert(v == LLONG_MIN);
strcpy(buf,"-9223372036854775809"); /* overflow */
assert(string2ll(buf,strlen(buf),&v) == 0);
strcpy(buf,"9223372036854775807");
assert(string2ll(buf,strlen(buf),&v) == 1);
assert(v == LLONG_MAX);
strcpy(buf,"9223372036854775808"); /* overflow */
assert(string2ll(buf,strlen(buf),&v) == 0);
} }
int isStringRepresentableAsLong(sds s, long *longval) { void test_string2l(void) {
long long ll; char buf[32];
long v;
/* May not start with +. */
strcpy(buf,"+1");
assert(string2l(buf,strlen(buf),&v) == 0);
/* May not start with 0. */
strcpy(buf,"01");
assert(string2l(buf,strlen(buf),&v) == 0);
strcpy(buf,"-1");
assert(string2l(buf,strlen(buf),&v) == 1);
assert(v == -1);
strcpy(buf,"0");
assert(string2l(buf,strlen(buf),&v) == 1);
assert(v == 0);
strcpy(buf,"1");
assert(string2l(buf,strlen(buf),&v) == 1);
assert(v == 1);
if (isStringRepresentableAsLongLong(s,&ll) == REDIS_ERR) return REDIS_ERR; strcpy(buf,"99");
if (ll < LONG_MIN || ll > LONG_MAX) return REDIS_ERR; assert(string2l(buf,strlen(buf),&v) == 1);
*longval = (long)ll; assert(v == 99);
return REDIS_OK;
strcpy(buf,"-99");
assert(string2l(buf,strlen(buf),&v) == 1);
assert(v == -99);
#if LONG_MAX != LLONG_MAX
strcpy(buf,"-2147483648");
assert(string2l(buf,strlen(buf),&v) == 1);
assert(v == LONG_MIN);
strcpy(buf,"-2147483649"); /* overflow */
assert(string2l(buf,strlen(buf),&v) == 0);
strcpy(buf,"2147483647");
assert(string2l(buf,strlen(buf),&v) == 1);
assert(v == LONG_MAX);
strcpy(buf,"2147483648"); /* overflow */
assert(string2l(buf,strlen(buf),&v) == 0);
#endif
} }
int isObjectRepresentableAsLongLong(robj *o, long long *llongval) { int main(int argc, char **argv) {
redisAssert(o->type == REDIS_STRING); test_string2ll();
if (o->encoding == REDIS_ENCODING_INT) { test_string2l();
if (llongval) *llongval = (long) o->ptr; return 0;
return REDIS_OK;
} else {
return isStringRepresentableAsLongLong(o->ptr,llongval);
}
} }
#endif
#ifndef __REDIS_UTIL_H
#define __REDIS_UTIL_H
int stringmatchlen(const char *p, int plen, const char *s, int slen, int nocase);
int stringmatch(const char *p, const char *s, int nocase);
long long memtoll(const char *p, int *err);
int ll2string(char *s, size_t len, long long value);
int string2ll(char *s, size_t slen, long long *value);
int string2l(char *s, size_t slen, long *value);
int d2string(char *buf, size_t len, double value);
#endif
...@@ -67,10 +67,9 @@ ...@@ -67,10 +67,9 @@
#include <assert.h> #include <assert.h>
#include <limits.h> #include <limits.h>
#include "zmalloc.h" #include "zmalloc.h"
#include "util.h"
#include "ziplist.h" #include "ziplist.h"
int ll2string(char *s, size_t len, long long value);
#define ZIP_END 255 #define ZIP_END 255
#define ZIP_BIGLEN 254 #define ZIP_BIGLEN 254
...@@ -248,22 +247,9 @@ static int zipPrevLenByteDiff(unsigned char *p, unsigned int len) { ...@@ -248,22 +247,9 @@ static int zipPrevLenByteDiff(unsigned char *p, unsigned int len) {
* Stores the integer value in 'v' and its encoding in 'encoding'. */ * 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) { static int zipTryEncoding(unsigned char *entry, unsigned int entrylen, long long *v, unsigned char *encoding) {
long long value; long long value;
char *eptr;
char buf[32];
if (entrylen >= 32 || entrylen == 0) return 0; if (entrylen >= 32 || entrylen == 0) return 0;
if (entry[0] == '-' || (entry[0] >= '0' && entry[0] <= '9')) { if (string2ll((char*)entry,entrylen,&value)) {
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;
/* Great, the string can be encoded. Check what's the smallest /* Great, the string can be encoded. Check what's the smallest
* of our encoding types that can hold this value. */ * of our encoding types that can hold this value. */
if (value >= INT16_MIN && value <= INT16_MAX) { if (value >= INT16_MIN && value <= INT16_MAX) {
...@@ -398,12 +384,17 @@ static unsigned char *__ziplistCascadeUpdate(unsigned char *zl, unsigned char *p ...@@ -398,12 +384,17 @@ static unsigned char *__ziplistCascadeUpdate(unsigned char *zl, unsigned char *p
offset = p-zl; offset = p-zl;
extra = rawlensize-next.prevrawlensize; extra = rawlensize-next.prevrawlensize;
zl = ziplistResize(zl,curlen+extra); zl = ziplistResize(zl,curlen+extra);
ZIPLIST_TAIL_OFFSET(zl) += extra;
p = zl+offset; p = zl+offset;
/* Move the tail to the back. */ /* Current pointer and offset for next element. */
np = p+rawlen; np = p+rawlen;
noffset = np-zl; 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, memmove(np+rawlensize,
np+next.prevrawlensize, np+next.prevrawlensize,
curlen-noffset-next.prevrawlensize-1); curlen-noffset-next.prevrawlensize-1);
...@@ -879,7 +870,7 @@ void pop(unsigned char *zl, int where) { ...@@ -879,7 +870,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 p, len = min+rand()%(max-min+1);
int minval, maxval; int minval, maxval;
switch(rand() % 3) { switch(rand() % 3) {
...@@ -901,10 +892,9 @@ void randstring(char *target, unsigned int min, unsigned int max) { ...@@ -901,10 +892,9 @@ void randstring(char *target, unsigned int min, unsigned int max) {
while(p < len) while(p < len)
target[p++] = minval+rand()%(maxval-minval+1); target[p++] = minval+rand()%(maxval-minval+1);
return; return len;
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {
unsigned char *zl, *p; unsigned char *zl, *p;
unsigned char *entry; unsigned char *entry;
...@@ -1237,6 +1227,7 @@ int main(int argc, char **argv) { ...@@ -1237,6 +1227,7 @@ int main(int argc, char **argv) {
int i,j,len,where; int i,j,len,where;
unsigned char *p; unsigned char *p;
char buf[1024]; char buf[1024];
int buflen;
list *ref; list *ref;
listNode *refnode; listNode *refnode;
...@@ -1245,10 +1236,6 @@ int main(int argc, char **argv) { ...@@ -1245,10 +1236,6 @@ int main(int argc, char **argv) {
unsigned int slen; unsigned int slen;
long long sval; 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++) { for (i = 0; i < 20000; i++) {
zl = ziplistNew(); zl = ziplistNew();
ref = listCreate(); ref = listCreate();
...@@ -1258,31 +1245,32 @@ int main(int argc, char **argv) { ...@@ -1258,31 +1245,32 @@ int main(int argc, char **argv) {
/* Create lists */ /* Create lists */
for (j = 0; j < len; j++) { for (j = 0; j < len; j++) {
where = (rand() & 1) ? ZIPLIST_HEAD : ZIPLIST_TAIL; where = (rand() & 1) ? ZIPLIST_HEAD : ZIPLIST_TAIL;
switch(rand() % 4) { if (rand() % 2) {
case 0: buflen = randstring(buf,1,sizeof(buf)-1);
sprintf(buf,"%lld",(0LL + rand()) >> 20); } else {
break; switch(rand() % 3) {
case 1: case 0:
sprintf(buf,"%lld",(0LL + rand())); buflen = sprintf(buf,"%lld",(0LL + rand()) >> 20);
break; break;
case 2: case 1:
sprintf(buf,"%lld",(0LL + rand()) << 20); buflen = sprintf(buf,"%lld",(0LL + rand()));
break; break;
case 3: case 2:
randstring(buf,0,256); buflen = sprintf(buf,"%lld",(0LL + rand()) << 20);
break; break;
default: default:
assert(NULL); assert(NULL);
}
} }
/* Add to ziplist */ /* Add to ziplist */
zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), where); zl = ziplistPush(zl, (unsigned char*)buf, buflen, where);
/* Add to reference list */ /* Add to reference list */
if (where == ZIPLIST_HEAD) { if (where == ZIPLIST_HEAD) {
listAddNodeHead(ref,sdsnew(buf)); listAddNodeHead(ref,sdsnewlen(buf, buflen));
} else if (where == ZIPLIST_TAIL) { } else if (where == ZIPLIST_TAIL) {
listAddNodeTail(ref,sdsnew(buf)); listAddNodeTail(ref,sdsnewlen(buf, buflen));
} else { } else {
assert(NULL); assert(NULL);
} }
...@@ -1297,12 +1285,13 @@ int main(int argc, char **argv) { ...@@ -1297,12 +1285,13 @@ int main(int argc, char **argv) {
assert(ziplistGet(p,&sstr,&slen,&sval)); assert(ziplistGet(p,&sstr,&slen,&sval));
if (sstr == NULL) { if (sstr == NULL) {
sprintf(buf,"%lld",sval); buflen = sprintf(buf,"%lld",sval);
} else { } else {
memcpy(buf,sstr,slen); buflen = slen;
buf[slen] = '\0'; memcpy(buf,sstr,buflen);
buf[buflen] = '\0';
} }
assert(strcmp(buf,listNodeValue(refnode)) == 0); assert(memcmp(buf,listNodeValue(refnode),buflen) == 0);
} }
zfree(zl); zfree(zl);
listRelease(ref); listRelease(ref);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment