X-Git-Url: https://git.saurik.com/redis.git/blobdiff_plain/0d1650f8a9ee2df13a11685ff0eb739305b4fe1b..754643c1c703c773e03efec23a2b55ac5ba324cb:/src/object.c diff --git a/src/object.c b/src/object.c index 20e7f57a..91e1933a 100644 --- a/src/object.c +++ b/src/object.c @@ -1,6 +1,6 @@ #include "redis.h" -#include #include +#include robj *createObject(int type, void *ptr) { robj *o = zmalloc(sizeof(*o)); @@ -30,9 +30,7 @@ robj *createStringObject(char *ptr, size_t len) { robj *createStringObjectFromLongLong(long long value) { robj *o; - if (value >= 0 && value < REDIS_SHARED_INTEGERS && - !server.ds_enabled && - pthread_equal(pthread_self(),server.mainthread)) { + if (value >= 0 && value < REDIS_SHARED_INTEGERS) { incrRefCount(shared.integers[value]); o = shared.integers[value]; } else { @@ -47,8 +45,32 @@ robj *createStringObjectFromLongLong(long long value) { return o; } +/* Note: this function is defined into object.c since here it is where it + * belongs but it is actually designed to be used just for INCRBYFLOAT */ +robj *createStringObjectFromLongDouble(long double value) { + char buf[256]; + int len; + + /* We use 17 digits precision since with 128 bit floats that precision + * after rouding is able to represent most small decimal numbers in a way + * that is "non surprising" for the user (that is, most small decimal + * numbers will be represented in a way that when converted back into + * a string are exactly the same as what the user typed.) */ + len = snprintf(buf,sizeof(buf),"%.17Lf", value); + /* Now remove trailing zeroes after the '.' */ + if (strchr(buf,'.') != NULL) { + char *p = buf+len-1; + while(*p == '0') { + p--; + len--; + } + if (*p == '.') len--; + } + return createStringObject(buf,len); +} + robj *dupStringObject(robj *o) { - redisAssert(o->encoding == REDIS_ENCODING_RAW); + redisAssertWithInfo(NULL,o,o->encoding == REDIS_ENCODING_RAW); return createStringObject(o->ptr,sdslen(o->ptr)); } @@ -82,12 +104,9 @@ robj *createIntsetObject(void) { } robj *createHashObject(void) { - /* All the Hashes start as zipmaps. Will be automatically converted - * into hash tables if there are enough elements or big elements - * inside. */ - unsigned char *zm = zipmapNew(); - robj *o = createObject(REDIS_HASH,zm); - o->encoding = REDIS_ENCODING_ZIPMAP; + unsigned char *zl = ziplistNew(); + robj *o = createObject(REDIS_HASH, zl); + o->encoding = REDIS_ENCODING_ZIPLIST; return o; } @@ -163,7 +182,7 @@ void freeHashObject(robj *o) { case REDIS_ENCODING_HT: dictRelease((dict*) o->ptr); break; - case REDIS_ENCODING_ZIPMAP: + case REDIS_ENCODING_ZIPLIST: zfree(o->ptr); break; default: @@ -195,6 +214,23 @@ void decrRefCount(void *obj) { } } +/* This function set the ref count to zero without freeing the object. + * It is useful in order to pass a new object to functions incrementing + * the ref count of the received object. Example: + * + * functionThatWillIncrementRefCount(resetRefCount(CreateObject(...))); + * + * Otherwise you need to resort to the less elegant pattern: + * + * *obj = createObject(...); + * functionThatWillIncrementRefCount(obj); + * decrRefCount(obj); + */ +robj *resetRefCount(robj *obj) { + obj->refcount = 0; + return obj; +} + int checkType(redisClient *c, robj *o, int type) { if (o->type != type) { addReply(c,shared.wrongtypeerr); @@ -204,7 +240,7 @@ int checkType(redisClient *c, robj *o, int type) { } int isObjectRepresentableAsLongLong(robj *o, long long *llval) { - redisAssert(o->type == REDIS_STRING); + redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); if (o->encoding == REDIS_ENCODING_INT) { if (llval) *llval = (long) o->ptr; return REDIS_OK; @@ -227,7 +263,7 @@ robj *tryObjectEncoding(robj *o) { if (o->refcount > 1) return o; /* Currently we try to encode only strings */ - redisAssert(o->type == REDIS_STRING); + redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); /* Check if we can represent this string as a long integer */ if (!string2l(s,sdslen(s),&value)) return o; @@ -241,10 +277,7 @@ robj *tryObjectEncoding(robj *o) { * Note that we also avoid using shared integers when maxmemory is used * because every object needs to have a private LRU field for the LRU * algorithm to work well. */ - if (!server.ds_enabled && - server.maxmemory == 0 && value >= 0 && value < REDIS_SHARED_INTEGERS && - pthread_equal(pthread_self(),server.mainthread)) - { + if (server.maxmemory == 0 && value >= 0 && value < REDIS_SHARED_INTEGERS) { decrRefCount(o); incrRefCount(shared.integers[value]); return shared.integers[value]; @@ -285,7 +318,7 @@ robj *getDecodedObject(robj *o) { * sdscmp() from sds.c will apply memcmp() so this function ca be considered * binary safe. */ int compareStringObjects(robj *a, robj *b) { - redisAssert(a->type == REDIS_STRING && b->type == REDIS_STRING); + redisAssertWithInfo(NULL,a,a->type == REDIS_STRING && b->type == REDIS_STRING); char bufa[128], bufb[128], *astr, *bstr; int bothsds = 1; @@ -320,7 +353,7 @@ int equalStringObjects(robj *a, robj *b) { } size_t stringObjectLen(robj *o) { - redisAssert(o->type == REDIS_STRING); + redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); if (o->encoding == REDIS_ENCODING_RAW) { return sdslen(o->ptr); } else { @@ -337,17 +370,19 @@ int getDoubleFromObject(robj *o, double *target) { if (o == NULL) { value = 0; } else { - redisAssert(o->type == REDIS_STRING); + redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); if (o->encoding == REDIS_ENCODING_RAW) { + errno = 0; value = strtod(o->ptr, &eptr); - if (eptr[0] != '\0' || isnan(value)) return REDIS_ERR; + if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' || + errno == ERANGE || isnan(value)) + return REDIS_ERR; } else if (o->encoding == REDIS_ENCODING_INT) { value = (long)o->ptr; } else { redisPanic("Unknown string encoding"); } } - *target = value; return REDIS_OK; } @@ -358,11 +393,48 @@ int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const ch if (msg != NULL) { addReplyError(c,(char*)msg); } else { - addReplyError(c,"value is not a double"); + addReplyError(c,"value is not a valid float"); } return REDIS_ERR; } + *target = value; + return REDIS_OK; +} +int getLongDoubleFromObject(robj *o, long double *target) { + long double value; + char *eptr; + + if (o == NULL) { + value = 0; + } else { + redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); + if (o->encoding == REDIS_ENCODING_RAW) { + errno = 0; + value = strtold(o->ptr, &eptr); + if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' || + errno == ERANGE || isnan(value)) + return REDIS_ERR; + } else if (o->encoding == REDIS_ENCODING_INT) { + value = (long)o->ptr; + } else { + redisPanic("Unknown string encoding"); + } + } + *target = value; + return REDIS_OK; +} + +int getLongDoubleFromObjectOrReply(redisClient *c, robj *o, long double *target, const char *msg) { + long double value; + if (getLongDoubleFromObject(o, &value) != REDIS_OK) { + if (msg != NULL) { + addReplyError(c,(char*)msg); + } else { + addReplyError(c,"value is not a valid float"); + } + return REDIS_ERR; + } *target = value; return REDIS_OK; } @@ -374,11 +446,12 @@ int getLongLongFromObject(robj *o, long long *target) { if (o == NULL) { value = 0; } else { - redisAssert(o->type == REDIS_STRING); + redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); if (o->encoding == REDIS_ENCODING_RAW) { + errno = 0; value = strtoll(o->ptr, &eptr, 10); - if (eptr[0] != '\0') return REDIS_ERR; - if (errno == ERANGE && (value == LLONG_MIN || value == LLONG_MAX)) + if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' || + errno == ERANGE) return REDIS_ERR; } else if (o->encoding == REDIS_ENCODING_INT) { value = (long)o->ptr; @@ -386,7 +459,6 @@ int getLongLongFromObject(robj *o, long long *target) { redisPanic("Unknown string encoding"); } } - if (target) *target = value; return REDIS_OK; } @@ -401,7 +473,6 @@ int getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, con } return REDIS_ERR; } - *target = value; return REDIS_OK; } @@ -418,7 +489,6 @@ int getLongFromObjectOrReply(redisClient *c, robj *o, long *target, const char * } return REDIS_ERR; } - *target = value; return REDIS_OK; } @@ -428,7 +498,6 @@ char *strEncoding(int encoding) { case REDIS_ENCODING_RAW: return "raw"; case REDIS_ENCODING_INT: return "int"; case REDIS_ENCODING_HT: return "hashtable"; - case REDIS_ENCODING_ZIPMAP: return "zipmap"; case REDIS_ENCODING_LINKEDLIST: return "linkedlist"; case REDIS_ENCODING_ZIPLIST: return "ziplist"; case REDIS_ENCODING_INTSET: return "intset"; @@ -454,7 +523,7 @@ robj *objectCommandLookup(redisClient *c, robj *key) { dictEntry *de; if ((de = dictFind(c->db->dict,key->ptr)) == NULL) return NULL; - return (robj*) dictGetEntryVal(de); + return (robj*) dictGetVal(de); } robj *objectCommandLookupOrReply(redisClient *c, robj *key, robj *reply) {