Back to home page

Redis cross reference

 
 

    


0001 /* Redis Object implementation.
0002  *
0003  * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
0004  * All rights reserved.
0005  *
0006  * Redistribution and use in source and binary forms, with or without
0007  * modification, are permitted provided that the following conditions are met:
0008  *
0009  *   * Redistributions of source code must retain the above copyright notice,
0010  *     this list of conditions and the following disclaimer.
0011  *   * Redistributions in binary form must reproduce the above copyright
0012  *     notice, this list of conditions and the following disclaimer in the
0013  *     documentation and/or other materials provided with the distribution.
0014  *   * Neither the name of Redis nor the names of its contributors may be used
0015  *     to endorse or promote products derived from this software without
0016  *     specific prior written permission.
0017  *
0018  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0019  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0020  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0021  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0022  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0023  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0024  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0025  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0026  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0027  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0028  * POSSIBILITY OF SUCH DAMAGE.
0029  */
0030 
0031 #include "redis.h"
0032 #include <math.h>
0033 #include <ctype.h>
0034 
0035 robj *createObject(int type, void *ptr) {
0036     robj *o = zmalloc(sizeof(*o));
0037     o->type = type;
0038     o->encoding = REDIS_ENCODING_RAW;
0039     o->ptr = ptr;
0040     o->refcount = 1;
0041 
0042     /* Set the LRU to the current lruclock (minutes resolution). */
0043     o->lru = server.lruclock;
0044     return o;
0045 }
0046 
0047 robj *createStringObject(char *ptr, size_t len) {
0048     return createObject(REDIS_STRING,sdsnewlen(ptr,len));
0049 }
0050 
0051 robj *createStringObjectFromLongLong(long long value) {
0052     robj *o;
0053     if (value >= 0 && value < REDIS_SHARED_INTEGERS) {
0054         incrRefCount(shared.integers[value]);
0055         o = shared.integers[value];
0056     } else {
0057         if (value >= LONG_MIN && value <= LONG_MAX) {
0058             o = createObject(REDIS_STRING, NULL);
0059             o->encoding = REDIS_ENCODING_INT;
0060             o->ptr = (void*)((long)value);
0061         } else {
0062             o = createObject(REDIS_STRING,sdsfromlonglong(value));
0063         }
0064     }
0065     return o;
0066 }
0067 
0068 /* Note: this function is defined into object.c since here it is where it
0069  * belongs but it is actually designed to be used just for INCRBYFLOAT */
0070 robj *createStringObjectFromLongDouble(long double value) {
0071     char buf[256];
0072     int len;
0073 
0074     /* We use 17 digits precision since with 128 bit floats that precision
0075      * after rounding is able to represent most small decimal numbers in a way
0076      * that is "non surprising" for the user (that is, most small decimal
0077      * numbers will be represented in a way that when converted back into
0078      * a string are exactly the same as what the user typed.) */
0079     len = snprintf(buf,sizeof(buf),"%.17Lf", value);
0080     /* Now remove trailing zeroes after the '.' */
0081     if (strchr(buf,'.') != NULL) {
0082         char *p = buf+len-1;
0083         while(*p == '0') {
0084             p--;
0085             len--;
0086         }
0087         if (*p == '.') len--;
0088     }
0089     return createStringObject(buf,len);
0090 }
0091 
0092 robj *dupStringObject(robj *o) {
0093     redisAssertWithInfo(NULL,o,o->encoding == REDIS_ENCODING_RAW);
0094     return createStringObject(o->ptr,sdslen(o->ptr));
0095 }
0096 
0097 robj *createListObject(void) {
0098     list *l = listCreate();
0099     robj *o = createObject(REDIS_LIST,l);
0100     listSetFreeMethod(l,decrRefCount);
0101     o->encoding = REDIS_ENCODING_LINKEDLIST;
0102     return o;
0103 }
0104 
0105 robj *createZiplistObject(void) {
0106     unsigned char *zl = ziplistNew();
0107     robj *o = createObject(REDIS_LIST,zl);
0108     o->encoding = REDIS_ENCODING_ZIPLIST;
0109     return o;
0110 }
0111 
0112 robj *createSetObject(void) {
0113     dict *d = dictCreate(&setDictType,NULL);
0114     robj *o = createObject(REDIS_SET,d);
0115     o->encoding = REDIS_ENCODING_HT;
0116     return o;
0117 }
0118 
0119 robj *createIntsetObject(void) {
0120     intset *is = intsetNew();
0121     robj *o = createObject(REDIS_SET,is);
0122     o->encoding = REDIS_ENCODING_INTSET;
0123     return o;
0124 }
0125 
0126 robj *createHashObject(void) {
0127     unsigned char *zl = ziplistNew();
0128     robj *o = createObject(REDIS_HASH, zl);
0129     o->encoding = REDIS_ENCODING_ZIPLIST;
0130     return o;
0131 }
0132 
0133 robj *createZsetObject(void) {
0134     zset *zs = zmalloc(sizeof(*zs));
0135     robj *o;
0136 
0137     zs->dict = dictCreate(&zsetDictType,NULL);
0138     zs->zsl = zslCreate();
0139     o = createObject(REDIS_ZSET,zs);
0140     o->encoding = REDIS_ENCODING_SKIPLIST;
0141     return o;
0142 }
0143 
0144 robj *createZsetZiplistObject(void) {
0145     unsigned char *zl = ziplistNew();
0146     robj *o = createObject(REDIS_ZSET,zl);
0147     o->encoding = REDIS_ENCODING_ZIPLIST;
0148     return o;
0149 }
0150 
0151 void freeStringObject(robj *o) {
0152     if (o->encoding == REDIS_ENCODING_RAW) {
0153         sdsfree(o->ptr);
0154     }
0155 }
0156 
0157 void freeListObject(robj *o) {
0158     switch (o->encoding) {
0159     case REDIS_ENCODING_LINKEDLIST:
0160         listRelease((list*) o->ptr);
0161         break;
0162     case REDIS_ENCODING_ZIPLIST:
0163         zfree(o->ptr);
0164         break;
0165     default:
0166         redisPanic("Unknown list encoding type");
0167     }
0168 }
0169 
0170 void freeSetObject(robj *o) {
0171     switch (o->encoding) {
0172     case REDIS_ENCODING_HT:
0173         dictRelease((dict*) o->ptr);
0174         break;
0175     case REDIS_ENCODING_INTSET:
0176         zfree(o->ptr);
0177         break;
0178     default:
0179         redisPanic("Unknown set encoding type");
0180     }
0181 }
0182 
0183 void freeZsetObject(robj *o) {
0184     zset *zs;
0185     switch (o->encoding) {
0186     case REDIS_ENCODING_SKIPLIST:
0187         zs = o->ptr;
0188         dictRelease(zs->dict);
0189         zslFree(zs->zsl);
0190         zfree(zs);
0191         break;
0192     case REDIS_ENCODING_ZIPLIST:
0193         zfree(o->ptr);
0194         break;
0195     default:
0196         redisPanic("Unknown sorted set encoding");
0197     }
0198 }
0199 
0200 void freeHashObject(robj *o) {
0201     switch (o->encoding) {
0202     case REDIS_ENCODING_HT:
0203         dictRelease((dict*) o->ptr);
0204         break;
0205     case REDIS_ENCODING_ZIPLIST:
0206         zfree(o->ptr);
0207         break;
0208     default:
0209         redisPanic("Unknown hash encoding type");
0210         break;
0211     }
0212 }
0213 
0214 void incrRefCount(robj *o) {
0215     o->refcount++;
0216 }
0217 
0218 void decrRefCount(void *obj) {
0219     robj *o = obj;
0220 
0221     if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0");
0222     if (o->refcount == 1) {
0223         switch(o->type) {
0224         case REDIS_STRING: freeStringObject(o); break;
0225         case REDIS_LIST: freeListObject(o); break;
0226         case REDIS_SET: freeSetObject(o); break;
0227         case REDIS_ZSET: freeZsetObject(o); break;
0228         case REDIS_HASH: freeHashObject(o); break;
0229         default: redisPanic("Unknown object type"); break;
0230         }
0231         zfree(o);
0232     } else {
0233         o->refcount--;
0234     }
0235 }
0236 
0237 /* This function set the ref count to zero without freeing the object.
0238  * It is useful in order to pass a new object to functions incrementing
0239  * the ref count of the received object. Example:
0240  *
0241  *    functionThatWillIncrementRefCount(resetRefCount(CreateObject(...)));
0242  *
0243  * Otherwise you need to resort to the less elegant pattern:
0244  *
0245  *    *obj = createObject(...);
0246  *    functionThatWillIncrementRefCount(obj);
0247  *    decrRefCount(obj);
0248  */
0249 robj *resetRefCount(robj *obj) {
0250     obj->refcount = 0;
0251     return obj;
0252 }
0253 
0254 int checkType(redisClient *c, robj *o, int type) {
0255     if (o->type != type) {
0256         addReply(c,shared.wrongtypeerr);
0257         return 1;
0258     }
0259     return 0;
0260 }
0261 
0262 int isObjectRepresentableAsLongLong(robj *o, long long *llval) {
0263     redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
0264     if (o->encoding == REDIS_ENCODING_INT) {
0265         if (llval) *llval = (long) o->ptr;
0266         return REDIS_OK;
0267     } else {
0268         return string2ll(o->ptr,sdslen(o->ptr),llval) ? REDIS_OK : REDIS_ERR;
0269     }
0270 }
0271 
0272 /* Try to encode a string object in order to save space */
0273 robj *tryObjectEncoding(robj *o) {
0274     long value;
0275     sds s = o->ptr;
0276 
0277     if (o->encoding != REDIS_ENCODING_RAW)
0278         return o; /* Already encoded */
0279 
0280     /* It's not safe to encode shared objects: shared objects can be shared
0281      * everywhere in the "object space" of Redis. Encoded objects can only
0282      * appear as "values" (and not, for instance, as keys) */
0283      if (o->refcount > 1) return o;
0284 
0285     /* Currently we try to encode only strings */
0286     redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
0287 
0288     /* Check if we can represent this string as a long integer */
0289     if (!string2l(s,sdslen(s),&value)) return o;
0290 
0291     /* Ok, this object can be encoded...
0292      *
0293      * Can I use a shared object? Only if the object is inside a given range
0294      *
0295      * Note that we also avoid using shared integers when maxmemory is used
0296      * because every object needs to have a private LRU field for the LRU
0297      * algorithm to work well. */
0298     if (server.maxmemory == 0 && value >= 0 && value < REDIS_SHARED_INTEGERS) {
0299         decrRefCount(o);
0300         incrRefCount(shared.integers[value]);
0301         return shared.integers[value];
0302     } else {
0303         o->encoding = REDIS_ENCODING_INT;
0304         sdsfree(o->ptr);
0305         o->ptr = (void*) value;
0306         return o;
0307     }
0308 }
0309 
0310 /* Get a decoded version of an encoded object (returned as a new object).
0311  * If the object is already raw-encoded just increment the ref count. */
0312 robj *getDecodedObject(robj *o) {
0313     robj *dec;
0314 
0315     if (o->encoding == REDIS_ENCODING_RAW) {
0316         incrRefCount(o);
0317         return o;
0318     }
0319     if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_INT) {
0320         char buf[32];
0321 
0322         ll2string(buf,32,(long)o->ptr);
0323         dec = createStringObject(buf,strlen(buf));
0324         return dec;
0325     } else {
0326         redisPanic("Unknown encoding type");
0327     }
0328 }
0329 
0330 /* Compare two string objects via strcmp() or alike.
0331  * Note that the objects may be integer-encoded. In such a case we
0332  * use ll2string() to get a string representation of the numbers on the stack
0333  * and compare the strings, it's much faster than calling getDecodedObject().
0334  *
0335  * Important note: if objects are not integer encoded, but binary-safe strings,
0336  * sdscmp() from sds.c will apply memcmp() so this function ca be considered
0337  * binary safe. */
0338 int compareStringObjects(robj *a, robj *b) {
0339     redisAssertWithInfo(NULL,a,a->type == REDIS_STRING && b->type == REDIS_STRING);
0340     char bufa[128], bufb[128], *astr, *bstr;
0341     int bothsds = 1;
0342 
0343     if (a == b) return 0;
0344     if (a->encoding != REDIS_ENCODING_RAW) {
0345         ll2string(bufa,sizeof(bufa),(long) a->ptr);
0346         astr = bufa;
0347         bothsds = 0;
0348     } else {
0349         astr = a->ptr;
0350     }
0351     if (b->encoding != REDIS_ENCODING_RAW) {
0352         ll2string(bufb,sizeof(bufb),(long) b->ptr);
0353         bstr = bufb;
0354         bothsds = 0;
0355     } else {
0356         bstr = b->ptr;
0357     }
0358     return bothsds ? sdscmp(astr,bstr) : strcmp(astr,bstr);
0359 }
0360 
0361 /* Equal string objects return 1 if the two objects are the same from the
0362  * point of view of a string comparison, otherwise 0 is returned. Note that
0363  * this function is faster then checking for (compareStringObject(a,b) == 0)
0364  * because it can perform some more optimization. */
0365 int equalStringObjects(robj *a, robj *b) {
0366     if (a->encoding != REDIS_ENCODING_RAW && b->encoding != REDIS_ENCODING_RAW){
0367         return a->ptr == b->ptr;
0368     } else {
0369         return compareStringObjects(a,b) == 0;
0370     }
0371 }
0372 
0373 size_t stringObjectLen(robj *o) {
0374     redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
0375     if (o->encoding == REDIS_ENCODING_RAW) {
0376         return sdslen(o->ptr);
0377     } else {
0378         char buf[32];
0379 
0380         return ll2string(buf,32,(long)o->ptr);
0381     }
0382 }
0383 
0384 int getDoubleFromObject(robj *o, double *target) {
0385     double value;
0386     char *eptr;
0387 
0388     if (o == NULL) {
0389         value = 0;
0390     } else {
0391         redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
0392         if (o->encoding == REDIS_ENCODING_RAW) {
0393             errno = 0;
0394             value = strtod(o->ptr, &eptr);
0395             if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' ||
0396                 errno == ERANGE || isnan(value))
0397                 return REDIS_ERR;
0398         } else if (o->encoding == REDIS_ENCODING_INT) {
0399             value = (long)o->ptr;
0400         } else {
0401             redisPanic("Unknown string encoding");
0402         }
0403     }
0404     *target = value;
0405     return REDIS_OK;
0406 }
0407 
0408 int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const char *msg) {
0409     double value;
0410     if (getDoubleFromObject(o, &value) != REDIS_OK) {
0411         if (msg != NULL) {
0412             addReplyError(c,(char*)msg);
0413         } else {
0414             addReplyError(c,"value is not a valid float");
0415         }
0416         return REDIS_ERR;
0417     }
0418     *target = value;
0419     return REDIS_OK;
0420 }
0421 
0422 int getLongDoubleFromObject(robj *o, long double *target) {
0423     long double value;
0424     char *eptr;
0425 
0426     if (o == NULL) {
0427         value = 0;
0428     } else {
0429         redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
0430         if (o->encoding == REDIS_ENCODING_RAW) {
0431             errno = 0;
0432             value = strtold(o->ptr, &eptr);
0433             if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' ||
0434                 errno == ERANGE || isnan(value))
0435                 return REDIS_ERR;
0436         } else if (o->encoding == REDIS_ENCODING_INT) {
0437             value = (long)o->ptr;
0438         } else {
0439             redisPanic("Unknown string encoding");
0440         }
0441     }
0442     *target = value;
0443     return REDIS_OK;
0444 }
0445 
0446 int getLongDoubleFromObjectOrReply(redisClient *c, robj *o, long double *target, const char *msg) {
0447     long double value;
0448     if (getLongDoubleFromObject(o, &value) != REDIS_OK) {
0449         if (msg != NULL) {
0450             addReplyError(c,(char*)msg);
0451         } else {
0452             addReplyError(c,"value is not a valid float");
0453         }
0454         return REDIS_ERR;
0455     }
0456     *target = value;
0457     return REDIS_OK;
0458 }
0459 
0460 int getLongLongFromObject(robj *o, long long *target) {
0461     long long value;
0462     char *eptr;
0463 
0464     if (o == NULL) {
0465         value = 0;
0466     } else {
0467         redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
0468         if (o->encoding == REDIS_ENCODING_RAW) {
0469             errno = 0;
0470             value = strtoll(o->ptr, &eptr, 10);
0471             if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' ||
0472                 errno == ERANGE)
0473                 return REDIS_ERR;
0474         } else if (o->encoding == REDIS_ENCODING_INT) {
0475             value = (long)o->ptr;
0476         } else {
0477             redisPanic("Unknown string encoding");
0478         }
0479     }
0480     if (target) *target = value;
0481     return REDIS_OK;
0482 }
0483 
0484 int getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, const char *msg) {
0485     long long value;
0486     if (getLongLongFromObject(o, &value) != REDIS_OK) {
0487         if (msg != NULL) {
0488             addReplyError(c,(char*)msg);
0489         } else {
0490             addReplyError(c,"value is not an integer or out of range");
0491         }
0492         return REDIS_ERR;
0493     }
0494     *target = value;
0495     return REDIS_OK;
0496 }
0497 
0498 int getLongFromObjectOrReply(redisClient *c, robj *o, long *target, const char *msg) {
0499     long long value;
0500 
0501     if (getLongLongFromObjectOrReply(c, o, &value, msg) != REDIS_OK) return REDIS_ERR;
0502     if (value < LONG_MIN || value > LONG_MAX) {
0503         if (msg != NULL) {
0504             addReplyError(c,(char*)msg);
0505         } else {
0506             addReplyError(c,"value is out of range");
0507         }
0508         return REDIS_ERR;
0509     }
0510     *target = value;
0511     return REDIS_OK;
0512 }
0513 
0514 char *strEncoding(int encoding) {
0515     switch(encoding) {
0516     case REDIS_ENCODING_RAW: return "raw";
0517     case REDIS_ENCODING_INT: return "int";
0518     case REDIS_ENCODING_HT: return "hashtable";
0519     case REDIS_ENCODING_LINKEDLIST: return "linkedlist";
0520     case REDIS_ENCODING_ZIPLIST: return "ziplist";
0521     case REDIS_ENCODING_INTSET: return "intset";
0522     case REDIS_ENCODING_SKIPLIST: return "skiplist";
0523     default: return "unknown";
0524     }
0525 }
0526 
0527 /* Given an object returns the min number of seconds the object was never
0528  * requested, using an approximated LRU algorithm. */
0529 unsigned long estimateObjectIdleTime(robj *o) {
0530     if (server.lruclock >= o->lru) {
0531         return (server.lruclock - o->lru) * REDIS_LRU_CLOCK_RESOLUTION;
0532     } else {
0533         return ((REDIS_LRU_CLOCK_MAX - o->lru) + server.lruclock) *
0534                     REDIS_LRU_CLOCK_RESOLUTION;
0535     }
0536 }
0537 
0538 /* This is an helper function for the DEBUG command. We need to lookup keys
0539  * without any modification of LRU or other parameters. */
0540 robj *objectCommandLookup(redisClient *c, robj *key) {
0541     dictEntry *de;
0542 
0543     if ((de = dictFind(c->db->dict,key->ptr)) == NULL) return NULL;
0544     return (robj*) dictGetVal(de);
0545 }
0546 
0547 robj *objectCommandLookupOrReply(redisClient *c, robj *key, robj *reply) {
0548     robj *o = objectCommandLookup(c,key);
0549 
0550     if (!o) addReply(c, reply);
0551     return o;
0552 }
0553 
0554 /* Object command allows to inspect the internals of an Redis Object.
0555  * Usage: OBJECT <verb> ... arguments ... */
0556 void objectCommand(redisClient *c) {
0557     robj *o;
0558 
0559     if (!strcasecmp(c->argv[1]->ptr,"refcount") && c->argc == 3) {
0560         if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
0561                 == NULL) return;
0562         addReplyLongLong(c,o->refcount);
0563     } else if (!strcasecmp(c->argv[1]->ptr,"encoding") && c->argc == 3) {
0564         if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
0565                 == NULL) return;
0566         addReplyBulkCString(c,strEncoding(o->encoding));
0567     } else if (!strcasecmp(c->argv[1]->ptr,"idletime") && c->argc == 3) {
0568         if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
0569                 == NULL) return;
0570         addReplyLongLong(c,estimateObjectIdleTime(o));
0571     } else {
0572         addReplyError(c,"Syntax error. Try OBJECT (refcount|encoding|idletime)");
0573     }
0574 }
0575