mirror of
https://github.com/fluencelabs/redis
synced 2025-06-26 23:41:33 +00:00
Merge branch '2.4' of github.com:antirez/redis into 2.4
This commit is contained in:
21
src/Makefile
21
src/Makefile
@ -16,9 +16,23 @@ else
|
||||
endif
|
||||
|
||||
ifeq ($(USE_TCMALLOC),yes)
|
||||
CCLINK+= -ltcmalloc
|
||||
CFLAGS+= -DUSE_TCMALLOC
|
||||
ALLOC_LINK=-ltcmalloc
|
||||
ALLOC_FLAGS=-DUSE_TCMALLOC
|
||||
endif
|
||||
|
||||
ifeq ($(USE_TCMALLOC_MINIMAL),yes)
|
||||
ALLOC_LINK=-ltcmalloc_minimal
|
||||
ALLOC_FLAGS=-DUSE_TCMALLOC
|
||||
endif
|
||||
|
||||
ifeq ($(USE_JEMALLOC),yes)
|
||||
ALLOC_LINK=-ljemalloc
|
||||
ALLOC_FLAGS=-DUSE_JEMALLOC
|
||||
endif
|
||||
|
||||
CCLINK+= $(ALLOC_LINK)
|
||||
CFLAGS+= $(ALLOC_FLAGS)
|
||||
|
||||
CCOPT= $(CFLAGS) $(CCLINK) $(ARCH) $(PROF)
|
||||
|
||||
PREFIX= /usr/local
|
||||
@ -100,8 +114,7 @@ t_string.o: t_string.c redis.h fmacros.h config.h ae.h sds.h dict.h \
|
||||
adlist.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h
|
||||
t_zset.o: t_zset.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
|
||||
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h
|
||||
util.o: util.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
|
||||
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h
|
||||
util.o: util.c util.h
|
||||
vm.o: vm.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
|
||||
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h
|
||||
ziplist.o: ziplist.c zmalloc.h ziplist.h
|
||||
|
13
src/config.h
13
src/config.h
@ -10,17 +10,30 @@
|
||||
* this expects a different allocation scheme. Therefore, *exclusively* use
|
||||
* either tcmalloc or OSX's malloc_size()! */
|
||||
#if defined(USE_TCMALLOC)
|
||||
#define REDIS_MALLOC "tcmalloc"
|
||||
#include <google/tcmalloc.h>
|
||||
#if TC_VERSION_MAJOR >= 1 && TC_VERSION_MINOR >= 6
|
||||
#define HAVE_MALLOC_SIZE 1
|
||||
#define redis_malloc_size(p) tc_malloc_size(p)
|
||||
#endif
|
||||
#elif defined(USE_JEMALLOC)
|
||||
#define REDIS_MALLOC "jemalloc"
|
||||
#define JEMALLOC_MANGLE
|
||||
#include <jemalloc/jemalloc.h>
|
||||
#if JEMALLOC_VERSION_MAJOR >= 2 && JEMALLOC_VERSION_MINOR >= 1
|
||||
#define HAVE_MALLOC_SIZE 1
|
||||
#define redis_malloc_size(p) JEMALLOC_P(malloc_usable_size)(p)
|
||||
#endif
|
||||
#elif defined(__APPLE__)
|
||||
#include <malloc/malloc.h>
|
||||
#define HAVE_MALLOC_SIZE 1
|
||||
#define redis_malloc_size(p) malloc_size(p)
|
||||
#endif
|
||||
|
||||
#ifndef REDIS_MALLOC
|
||||
#define REDIS_MALLOC "libc"
|
||||
#endif
|
||||
|
||||
/* define redis_fstat to fstat or fstat64() */
|
||||
#if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)
|
||||
#define redis_fstat fstat64
|
||||
|
@ -701,70 +701,74 @@ static void setProtocolError(redisClient *c, int pos) {
|
||||
|
||||
int processMultibulkBuffer(redisClient *c) {
|
||||
char *newline = NULL;
|
||||
char *eptr;
|
||||
int pos = 0, tolerr;
|
||||
long bulklen;
|
||||
int pos = 0, ok;
|
||||
long long ll;
|
||||
|
||||
if (c->multibulklen == 0) {
|
||||
/* The client should have been reset */
|
||||
redisAssert(c->argc == 0);
|
||||
|
||||
/* Multi bulk length cannot be read without a \r\n */
|
||||
newline = strstr(c->querybuf,"\r\n");
|
||||
newline = strchr(c->querybuf,'\r');
|
||||
if (newline == NULL)
|
||||
return REDIS_ERR;
|
||||
|
||||
/* Buffer should also contain \n */
|
||||
if (newline-(c->querybuf) > ((signed)sdslen(c->querybuf)-2))
|
||||
return REDIS_ERR;
|
||||
|
||||
/* We know for sure there is a whole line since newline != NULL,
|
||||
* so go ahead and find out the multi bulk length. */
|
||||
redisAssert(c->querybuf[0] == '*');
|
||||
c->multibulklen = strtol(c->querybuf+1,&eptr,10);
|
||||
pos = (newline-c->querybuf)+2;
|
||||
if (c->multibulklen <= 0) {
|
||||
c->querybuf = sdsrange(c->querybuf,pos,-1);
|
||||
return REDIS_OK;
|
||||
} else if (c->multibulklen > 1024*1024) {
|
||||
ok = string2ll(c->querybuf+1,newline-(c->querybuf+1),&ll);
|
||||
if (!ok || ll > 1024*1024) {
|
||||
addReplyError(c,"Protocol error: invalid multibulk length");
|
||||
setProtocolError(c,pos);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
pos = (newline-c->querybuf)+2;
|
||||
if (ll <= 0) {
|
||||
c->querybuf = sdsrange(c->querybuf,pos,-1);
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
c->multibulklen = ll;
|
||||
|
||||
/* Setup argv array on client structure */
|
||||
if (c->argv) zfree(c->argv);
|
||||
c->argv = zmalloc(sizeof(robj*)*c->multibulklen);
|
||||
|
||||
/* Search new newline */
|
||||
newline = strstr(c->querybuf+pos,"\r\n");
|
||||
}
|
||||
|
||||
redisAssert(c->multibulklen > 0);
|
||||
while(c->multibulklen) {
|
||||
/* Read bulk length if unknown */
|
||||
if (c->bulklen == -1) {
|
||||
newline = strstr(c->querybuf+pos,"\r\n");
|
||||
if (newline != NULL) {
|
||||
if (c->querybuf[pos] != '$') {
|
||||
addReplyErrorFormat(c,
|
||||
"Protocol error: expected '$', got '%c'",
|
||||
c->querybuf[pos]);
|
||||
setProtocolError(c,pos);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
bulklen = strtol(c->querybuf+pos+1,&eptr,10);
|
||||
tolerr = (eptr[0] != '\r');
|
||||
if (tolerr || bulklen == LONG_MIN || bulklen == LONG_MAX ||
|
||||
bulklen < 0 || bulklen > 512*1024*1024)
|
||||
{
|
||||
addReplyError(c,"Protocol error: invalid bulk length");
|
||||
setProtocolError(c,pos);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
pos += eptr-(c->querybuf+pos)+2;
|
||||
c->bulklen = bulklen;
|
||||
} else {
|
||||
/* No newline in current buffer, so wait for more data */
|
||||
newline = strchr(c->querybuf+pos,'\r');
|
||||
if (newline == NULL)
|
||||
break;
|
||||
|
||||
/* Buffer should also contain \n */
|
||||
if (newline-(c->querybuf) > ((signed)sdslen(c->querybuf)-2))
|
||||
break;
|
||||
|
||||
if (c->querybuf[pos] != '$') {
|
||||
addReplyErrorFormat(c,
|
||||
"Protocol error: expected '$', got '%c'",
|
||||
c->querybuf[pos]);
|
||||
setProtocolError(c,pos);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
ok = string2ll(c->querybuf+pos+1,newline-(c->querybuf+pos+1),&ll);
|
||||
if (!ok || ll < 0 || ll > 512*1024*1024) {
|
||||
addReplyError(c,"Protocol error: invalid bulk length");
|
||||
setProtocolError(c,pos);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
pos += newline-(c->querybuf+pos)+2;
|
||||
c->bulklen = ll;
|
||||
}
|
||||
|
||||
/* Read bulk argument */
|
||||
|
12
src/object.c
12
src/object.c
@ -225,6 +225,16 @@ int checkType(redisClient *c, robj *o, int type) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int isObjectRepresentableAsLongLong(robj *o, long long *llval) {
|
||||
redisAssert(o->type == REDIS_STRING);
|
||||
if (o->encoding == REDIS_ENCODING_INT) {
|
||||
if (llval) *llval = (long) o->ptr;
|
||||
return REDIS_OK;
|
||||
} else {
|
||||
return string2ll(o->ptr,sdslen(o->ptr),llval) ? REDIS_OK : REDIS_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to encode a string object in order to save space */
|
||||
robj *tryObjectEncoding(robj *o) {
|
||||
long value;
|
||||
@ -242,7 +252,7 @@ robj *tryObjectEncoding(robj *o) {
|
||||
redisAssert(o->type == REDIS_STRING);
|
||||
|
||||
/* Check if we can represent this string as a long integer */
|
||||
if (isStringRepresentableAsLong(s,&value) == REDIS_ERR) return o;
|
||||
if (!string2l(s,sdslen(s),&value)) return o;
|
||||
|
||||
/* Ok, this object can be encoded...
|
||||
*
|
||||
|
@ -1189,7 +1189,7 @@ sds genRedisInfoString(void) {
|
||||
"used_memory_peak:%zu\r\n"
|
||||
"used_memory_peak_human:%s\r\n"
|
||||
"mem_fragmentation_ratio:%.2f\r\n"
|
||||
"use_tcmalloc:%d\r\n"
|
||||
"mem_allocator:%s\r\n"
|
||||
"loading:%d\r\n"
|
||||
"aof_enabled:%d\r\n"
|
||||
"changes_since_last_save:%lld\r\n"
|
||||
@ -1231,11 +1231,7 @@ sds genRedisInfoString(void) {
|
||||
server.stat_peak_memory,
|
||||
peak_hmem,
|
||||
zmalloc_get_fragmentation_ratio(),
|
||||
#ifdef USE_TCMALLOC
|
||||
1,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
REDIS_MALLOC,
|
||||
server.loading,
|
||||
server.appendonly,
|
||||
server.dirty,
|
||||
|
14
src/redis.h
14
src/redis.h
@ -29,6 +29,7 @@
|
||||
#include "ziplist.h" /* Compact list data structure */
|
||||
#include "intset.h" /* Compact integer set structure */
|
||||
#include "version.h"
|
||||
#include "util.h"
|
||||
|
||||
/* Error codes */
|
||||
#define REDIS_OK 0
|
||||
@ -719,6 +720,7 @@ void freeHashObject(robj *o);
|
||||
robj *createObject(int type, void *ptr);
|
||||
robj *createStringObject(char *ptr, size_t len);
|
||||
robj *dupStringObject(robj *o);
|
||||
int isObjectRepresentableAsLongLong(robj *o, long long *llongval);
|
||||
robj *tryObjectEncoding(robj *o);
|
||||
robj *getDecodedObject(robj *o);
|
||||
size_t stringObjectLen(robj *o);
|
||||
@ -872,18 +874,6 @@ int pubsubUnsubscribeAllPatterns(redisClient *c, int notify);
|
||||
void freePubsubPattern(void *p);
|
||||
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 */
|
||||
void loadServerConfig(char *filename);
|
||||
void appendServerSaveParams(time_t seconds, int changes);
|
||||
|
31
src/sds.c
31
src/sds.c
@ -36,11 +36,11 @@
|
||||
|
||||
#define SDS_ABORT_ON_OOM
|
||||
|
||||
#include "sds.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include "sds.h"
|
||||
#include "zmalloc.h"
|
||||
|
||||
static void sdsOomAbort(void) {
|
||||
@ -76,11 +76,6 @@ sds sdsnew(const char *init) {
|
||||
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) {
|
||||
return sdsnewlen(s, sdslen(s));
|
||||
}
|
||||
@ -90,11 +85,6 @@ void sdsfree(sds s) {
|
||||
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) {
|
||||
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
int reallen = strlen(s);
|
||||
@ -306,12 +296,17 @@ int sdscmp(sds s1, sds s2) {
|
||||
*/
|
||||
sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count) {
|
||||
int elements = 0, slots = 5, start = 0, j;
|
||||
sds *tokens;
|
||||
|
||||
sds *tokens = zmalloc(sizeof(sds)*slots);
|
||||
if (seplen < 1 || len < 0) return NULL;
|
||||
|
||||
tokens = zmalloc(sizeof(sds)*slots);
|
||||
#ifdef SDS_ABORT_ON_OOM
|
||||
if (tokens == NULL) sdsOomAbort();
|
||||
#else
|
||||
if (tokens == NULL) return NULL;
|
||||
#endif
|
||||
if (seplen < 1 || len < 0 || tokens == NULL) return NULL;
|
||||
|
||||
if (len == 0) {
|
||||
*count = 0;
|
||||
return tokens;
|
||||
@ -401,11 +396,11 @@ sds sdscatrepr(sds s, char *p, size_t len) {
|
||||
case '"':
|
||||
s = sdscatprintf(s,"\\%c",*p);
|
||||
break;
|
||||
case '\n': s = sdscatlen(s,"\\n",1); break;
|
||||
case '\r': s = sdscatlen(s,"\\r",1); break;
|
||||
case '\t': s = sdscatlen(s,"\\t",1); break;
|
||||
case '\a': s = sdscatlen(s,"\\a",1); break;
|
||||
case '\b': s = sdscatlen(s,"\\b",1); break;
|
||||
case '\n': s = sdscatlen(s,"\\n",2); break;
|
||||
case '\r': s = sdscatlen(s,"\\r",2); break;
|
||||
case '\t': s = sdscatlen(s,"\\t",2); break;
|
||||
case '\a': s = sdscatlen(s,"\\a",2); break;
|
||||
case '\b': s = sdscatlen(s,"\\b",2); break;
|
||||
default:
|
||||
if (isprint(*p))
|
||||
s = sdscatprintf(s,"%c",*p);
|
||||
|
10
src/sds.h
10
src/sds.h
@ -42,6 +42,16 @@ struct sdshdr {
|
||||
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 sdsnew(const char *init);
|
||||
sds sdsempty();
|
||||
|
167
src/util.c
167
src/util.c
@ -1,7 +1,10 @@
|
||||
#include "redis.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include "util.h"
|
||||
|
||||
/* Glob-style pattern matching. */
|
||||
int stringmatchlen(const char *pattern, int patternLen,
|
||||
@ -213,6 +216,12 @@ int string2ll(char *s, size_t slen, long long *value) {
|
||||
if (plen == slen)
|
||||
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] == '-') {
|
||||
negative = 1;
|
||||
p++; plen++;
|
||||
@ -250,7 +259,7 @@ int string2ll(char *s, size_t slen, long long *value) {
|
||||
return 0;
|
||||
|
||||
if (negative) {
|
||||
if (v > (-(unsigned long long)LLONG_MIN)) /* Overflow. */
|
||||
if (v > ((unsigned long long)(-(LLONG_MIN+1))+1)) /* Overflow. */
|
||||
return 0;
|
||||
if (value != NULL) *value = -v;
|
||||
} else {
|
||||
@ -261,6 +270,22 @@ int string2ll(char *s, size_t slen, long long *value) {
|
||||
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
|
||||
* required. The representation should always be parsable by stdtod(3). */
|
||||
int d2string(char *buf, size_t len, double value) {
|
||||
@ -300,44 +325,116 @@ int d2string(char *buf, size_t len, double value) {
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Check if the sds string 's' can be represented by a long long
|
||||
* (that is, is a number that fits into long without any other space or
|
||||
* 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).
|
||||
*
|
||||
* If so, the function returns REDIS_OK and *llongval is set to the value
|
||||
* of the number. Otherwise REDIS_ERR is returned */
|
||||
int isStringRepresentableAsLongLong(sds s, long long *llongval) {
|
||||
char buf[32], *endptr;
|
||||
long long value;
|
||||
int slen;
|
||||
#ifdef UTIL_TEST_MAIN
|
||||
#include <assert.h>
|
||||
|
||||
value = strtoll(s, &endptr, 10);
|
||||
if (endptr[0] != '\0') return REDIS_ERR;
|
||||
slen = ll2string(buf,32,value);
|
||||
void test_string2ll(void) {
|
||||
char buf[32];
|
||||
long long v;
|
||||
|
||||
/* If the number converted back into a string is not identical
|
||||
* then it's not possible to encode the string as integer */
|
||||
if (sdslen(s) != (unsigned)slen || memcmp(buf,s,slen)) return REDIS_ERR;
|
||||
if (llongval) *llongval = value;
|
||||
return REDIS_OK;
|
||||
/* May not start with +. */
|
||||
strcpy(buf,"+1");
|
||||
assert(string2ll(buf,strlen(buf),&v) == 0);
|
||||
|
||||
/* Leading space. */
|
||||
strcpy(buf," 1");
|
||||
assert(string2ll(buf,strlen(buf),&v) == 0);
|
||||
|
||||
/* Trailing space. */
|
||||
strcpy(buf,"1 ");
|
||||
assert(string2ll(buf,strlen(buf),&v) == 0);
|
||||
|
||||
/* May not start with 0. */
|
||||
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) {
|
||||
long long ll;
|
||||
void test_string2l(void) {
|
||||
char buf[32];
|
||||
long v;
|
||||
|
||||
if (isStringRepresentableAsLongLong(s,&ll) == REDIS_ERR) return REDIS_ERR;
|
||||
if (ll < LONG_MIN || ll > LONG_MAX) return REDIS_ERR;
|
||||
*longval = (long)ll;
|
||||
return REDIS_OK;
|
||||
/* 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);
|
||||
|
||||
strcpy(buf,"99");
|
||||
assert(string2l(buf,strlen(buf),&v) == 1);
|
||||
assert(v == 99);
|
||||
|
||||
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) {
|
||||
redisAssert(o->type == REDIS_STRING);
|
||||
if (o->encoding == REDIS_ENCODING_INT) {
|
||||
if (llongval) *llongval = (long) o->ptr;
|
||||
return REDIS_OK;
|
||||
} else {
|
||||
return isStringRepresentableAsLongLong(o->ptr,llongval);
|
||||
}
|
||||
int main(int argc, char **argv) {
|
||||
test_string2ll();
|
||||
test_string2l();
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
12
src/util.h
Normal file
12
src/util.h
Normal file
@ -0,0 +1,12 @@
|
||||
#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 @@
|
||||
#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);
|
||||
|
||||
#define ZIP_END 255
|
||||
#define ZIP_BIGLEN 254
|
||||
|
||||
@ -248,22 +247,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) {
|
||||
@ -398,12 +384,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);
|
||||
@ -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 minval, maxval;
|
||||
switch(rand() % 3) {
|
||||
@ -901,10 +892,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;
|
||||
@ -1237,6 +1227,7 @@ int main(int argc, char **argv) {
|
||||
int i,j,len,where;
|
||||
unsigned char *p;
|
||||
char buf[1024];
|
||||
int buflen;
|
||||
list *ref;
|
||||
listNode *refnode;
|
||||
|
||||
@ -1245,10 +1236,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();
|
||||
@ -1258,31 +1245,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);
|
||||
}
|
||||
@ -1297,12 +1285,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);
|
||||
|
Reference in New Issue
Block a user