xref: /redis-3.2.3/src/object.c (revision f2305046)
1 #include "redis.h"
2 #include <math.h>
3 #include <ctype.h>
4 
5 robj *createObject(int type, void *ptr) {
6     robj *o = zmalloc(sizeof(*o));
7     o->type = type;
8     o->encoding = REDIS_ENCODING_RAW;
9     o->ptr = ptr;
10     o->refcount = 1;
11 
12     /* Set the LRU to the current lruclock (minutes resolution).
13      * We do this regardless of the fact VM is active as LRU is also
14      * used for the maxmemory directive when Redis is used as cache.
15      *
16      * Note that this code may run in the context of an I/O thread
17      * and accessing server.lruclock in theory is an error
18      * (no locks). But in practice this is safe, and even if we read
19      * garbage Redis will not fail. */
20     o->lru = server.lruclock;
21     /* The following is only needed if VM is active, but since the conditional
22      * is probably more costly than initializing the field it's better to
23      * have every field properly initialized anyway. */
24     return o;
25 }
26 
27 robj *createStringObject(char *ptr, size_t len) {
28     return createObject(REDIS_STRING,sdsnewlen(ptr,len));
29 }
30 
31 robj *createStringObjectFromLongLong(long long value) {
32     robj *o;
33     if (value >= 0 && value < REDIS_SHARED_INTEGERS) {
34         incrRefCount(shared.integers[value]);
35         o = shared.integers[value];
36     } else {
37         if (value >= LONG_MIN && value <= LONG_MAX) {
38             o = createObject(REDIS_STRING, NULL);
39             o->encoding = REDIS_ENCODING_INT;
40             o->ptr = (void*)((long)value);
41         } else {
42             o = createObject(REDIS_STRING,sdsfromlonglong(value));
43         }
44     }
45     return o;
46 }
47 
48 /* Note: this function is defined into object.c since here it is where it
49  * belongs but it is actually designed to be used just for INCRBYFLOAT */
50 robj *createStringObjectFromLongDouble(long double value) {
51     char buf[256];
52     int len;
53 
54     /* We use 17 digits precision since with 128 bit floats that precision
55      * after rouding is able to represent most small decimal numbers in a way
56      * that is "non surprising" for the user (that is, most small decimal
57      * numbers will be represented in a way that when converted back into
58      * a string are exactly the same as what the user typed.) */
59     len = snprintf(buf,sizeof(buf),"%.17Lf", value);
60     /* Now remove trailing zeroes after the '.' */
61     if (strchr(buf,'.') != NULL) {
62         char *p = buf+len-1;
63         while(*p == '0') {
64             p--;
65             len--;
66         }
67         if (*p == '.') len--;
68     }
69     return createStringObject(buf,len);
70 }
71 
72 robj *dupStringObject(robj *o) {
73     redisAssertWithInfo(NULL,o,o->encoding == REDIS_ENCODING_RAW);
74     return createStringObject(o->ptr,sdslen(o->ptr));
75 }
76 
77 robj *createListObject(void) {
78     list *l = listCreate();
79     robj *o = createObject(REDIS_LIST,l);
80     listSetFreeMethod(l,decrRefCount);
81     o->encoding = REDIS_ENCODING_LINKEDLIST;
82     return o;
83 }
84 
85 robj *createZiplistObject(void) {
86     unsigned char *zl = ziplistNew();
87     robj *o = createObject(REDIS_LIST,zl);
88     o->encoding = REDIS_ENCODING_ZIPLIST;
89     return o;
90 }
91 
92 robj *createSetObject(void) {
93     dict *d = dictCreate(&setDictType,NULL);
94     robj *o = createObject(REDIS_SET,d);
95     o->encoding = REDIS_ENCODING_HT;
96     return o;
97 }
98 
99 robj *createIntsetObject(void) {
100     intset *is = intsetNew();
101     robj *o = createObject(REDIS_SET,is);
102     o->encoding = REDIS_ENCODING_INTSET;
103     return o;
104 }
105 
106 robj *createHashObject(void) {
107     unsigned char *zl = ziplistNew();
108     robj *o = createObject(REDIS_HASH, zl);
109     o->encoding = REDIS_ENCODING_ZIPLIST;
110     return o;
111 }
112 
113 robj *createZsetObject(void) {
114     zset *zs = zmalloc(sizeof(*zs));
115     robj *o;
116 
117     zs->dict = dictCreate(&zsetDictType,NULL);
118     zs->zsl = zslCreate();
119     o = createObject(REDIS_ZSET,zs);
120     o->encoding = REDIS_ENCODING_SKIPLIST;
121     return o;
122 }
123 
124 robj *createZsetZiplistObject(void) {
125     unsigned char *zl = ziplistNew();
126     robj *o = createObject(REDIS_ZSET,zl);
127     o->encoding = REDIS_ENCODING_ZIPLIST;
128     return o;
129 }
130 
131 void freeStringObject(robj *o) {
132     if (o->encoding == REDIS_ENCODING_RAW) {
133         sdsfree(o->ptr);
134     }
135 }
136 
137 void freeListObject(robj *o) {
138     switch (o->encoding) {
139     case REDIS_ENCODING_LINKEDLIST:
140         listRelease((list*) o->ptr);
141         break;
142     case REDIS_ENCODING_ZIPLIST:
143         zfree(o->ptr);
144         break;
145     default:
146         redisPanic("Unknown list encoding type");
147     }
148 }
149 
150 void freeSetObject(robj *o) {
151     switch (o->encoding) {
152     case REDIS_ENCODING_HT:
153         dictRelease((dict*) o->ptr);
154         break;
155     case REDIS_ENCODING_INTSET:
156         zfree(o->ptr);
157         break;
158     default:
159         redisPanic("Unknown set encoding type");
160     }
161 }
162 
163 void freeZsetObject(robj *o) {
164     zset *zs;
165     switch (o->encoding) {
166     case REDIS_ENCODING_SKIPLIST:
167         zs = o->ptr;
168         dictRelease(zs->dict);
169         zslFree(zs->zsl);
170         zfree(zs);
171         break;
172     case REDIS_ENCODING_ZIPLIST:
173         zfree(o->ptr);
174         break;
175     default:
176         redisPanic("Unknown sorted set encoding");
177     }
178 }
179 
180 void freeHashObject(robj *o) {
181     switch (o->encoding) {
182     case REDIS_ENCODING_HT:
183         dictRelease((dict*) o->ptr);
184         break;
185     case REDIS_ENCODING_ZIPLIST:
186         zfree(o->ptr);
187         break;
188     default:
189         redisPanic("Unknown hash encoding type");
190         break;
191     }
192 }
193 
194 void incrRefCount(robj *o) {
195     o->refcount++;
196 }
197 
198 void decrRefCount(void *obj) {
199     robj *o = obj;
200 
201     if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0");
202     if (o->refcount == 1) {
203         switch(o->type) {
204         case REDIS_STRING: freeStringObject(o); break;
205         case REDIS_LIST: freeListObject(o); break;
206         case REDIS_SET: freeSetObject(o); break;
207         case REDIS_ZSET: freeZsetObject(o); break;
208         case REDIS_HASH: freeHashObject(o); break;
209         default: redisPanic("Unknown object type"); break;
210         }
211         zfree(o);
212     } else {
213         o->refcount--;
214     }
215 }
216 
217 /* This function set the ref count to zero without freeing the object.
218  * It is useful in order to pass a new object to functions incrementing
219  * the ref count of the received object. Example:
220  *
221  *    functionThatWillIncrementRefCount(resetRefCount(CreateObject(...)));
222  *
223  * Otherwise you need to resort to the less elegant pattern:
224  *
225  *    *obj = createObject(...);
226  *    functionThatWillIncrementRefCount(obj);
227  *    decrRefCount(obj);
228  */
229 robj *resetRefCount(robj *obj) {
230     obj->refcount = 0;
231     return obj;
232 }
233 
234 int checkType(redisClient *c, robj *o, int type) {
235     if (o->type != type) {
236         addReply(c,shared.wrongtypeerr);
237         return 1;
238     }
239     return 0;
240 }
241 
242 int isObjectRepresentableAsLongLong(robj *o, long long *llval) {
243     redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
244     if (o->encoding == REDIS_ENCODING_INT) {
245         if (llval) *llval = (long) o->ptr;
246         return REDIS_OK;
247     } else {
248         return string2ll(o->ptr,sdslen(o->ptr),llval) ? REDIS_OK : REDIS_ERR;
249     }
250 }
251 
252 /* Try to encode a string object in order to save space */
253 robj *tryObjectEncoding(robj *o) {
254     long value;
255     sds s = o->ptr;
256 
257     if (o->encoding != REDIS_ENCODING_RAW)
258         return o; /* Already encoded */
259 
260     /* It's not safe to encode shared objects: shared objects can be shared
261      * everywhere in the "object space" of Redis. Encoded objects can only
262      * appear as "values" (and not, for instance, as keys) */
263      if (o->refcount > 1) return o;
264 
265     /* Currently we try to encode only strings */
266     redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
267 
268     /* Check if we can represent this string as a long integer */
269     if (!string2l(s,sdslen(s),&value)) return o;
270 
271     /* Ok, this object can be encoded...
272      *
273      * Can I use a shared object? Only if the object is inside a given range
274      *
275      * Note that we also avoid using shared integers when maxmemory is used
276      * because every object needs to have a private LRU field for the LRU
277      * algorithm to work well. */
278     if (server.maxmemory == 0 && value >= 0 && value < REDIS_SHARED_INTEGERS) {
279         decrRefCount(o);
280         incrRefCount(shared.integers[value]);
281         return shared.integers[value];
282     } else {
283         o->encoding = REDIS_ENCODING_INT;
284         sdsfree(o->ptr);
285         o->ptr = (void*) value;
286         return o;
287     }
288 }
289 
290 /* Get a decoded version of an encoded object (returned as a new object).
291  * If the object is already raw-encoded just increment the ref count. */
292 robj *getDecodedObject(robj *o) {
293     robj *dec;
294 
295     if (o->encoding == REDIS_ENCODING_RAW) {
296         incrRefCount(o);
297         return o;
298     }
299     if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_INT) {
300         char buf[32];
301 
302         ll2string(buf,32,(long)o->ptr);
303         dec = createStringObject(buf,strlen(buf));
304         return dec;
305     } else {
306         redisPanic("Unknown encoding type");
307     }
308 }
309 
310 /* Compare two string objects via strcmp() or alike.
311  * Note that the objects may be integer-encoded. In such a case we
312  * use ll2string() to get a string representation of the numbers on the stack
313  * and compare the strings, it's much faster than calling getDecodedObject().
314  *
315  * Important note: if objects are not integer encoded, but binary-safe strings,
316  * sdscmp() from sds.c will apply memcmp() so this function ca be considered
317  * binary safe. */
318 int compareStringObjects(robj *a, robj *b) {
319     redisAssertWithInfo(NULL,a,a->type == REDIS_STRING && b->type == REDIS_STRING);
320     char bufa[128], bufb[128], *astr, *bstr;
321     int bothsds = 1;
322 
323     if (a == b) return 0;
324     if (a->encoding != REDIS_ENCODING_RAW) {
325         ll2string(bufa,sizeof(bufa),(long) a->ptr);
326         astr = bufa;
327         bothsds = 0;
328     } else {
329         astr = a->ptr;
330     }
331     if (b->encoding != REDIS_ENCODING_RAW) {
332         ll2string(bufb,sizeof(bufb),(long) b->ptr);
333         bstr = bufb;
334         bothsds = 0;
335     } else {
336         bstr = b->ptr;
337     }
338     return bothsds ? sdscmp(astr,bstr) : strcmp(astr,bstr);
339 }
340 
341 /* Equal string objects return 1 if the two objects are the same from the
342  * point of view of a string comparison, otherwise 0 is returned. Note that
343  * this function is faster then checking for (compareStringObject(a,b) == 0)
344  * because it can perform some more optimization. */
345 int equalStringObjects(robj *a, robj *b) {
346     if (a->encoding != REDIS_ENCODING_RAW && b->encoding != REDIS_ENCODING_RAW){
347         return a->ptr == b->ptr;
348     } else {
349         return compareStringObjects(a,b) == 0;
350     }
351 }
352 
353 size_t stringObjectLen(robj *o) {
354     redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
355     if (o->encoding == REDIS_ENCODING_RAW) {
356         return sdslen(o->ptr);
357     } else {
358         char buf[32];
359 
360         return ll2string(buf,32,(long)o->ptr);
361     }
362 }
363 
364 int getDoubleFromObject(robj *o, double *target) {
365     double value;
366     char *eptr;
367 
368     if (o == NULL) {
369         value = 0;
370     } else {
371         redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
372         if (o->encoding == REDIS_ENCODING_RAW) {
373             errno = 0;
374             value = strtod(o->ptr, &eptr);
375             if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' ||
376                 errno == ERANGE || isnan(value))
377                 return REDIS_ERR;
378         } else if (o->encoding == REDIS_ENCODING_INT) {
379             value = (long)o->ptr;
380         } else {
381             redisPanic("Unknown string encoding");
382         }
383     }
384     *target = value;
385     return REDIS_OK;
386 }
387 
388 int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const char *msg) {
389     double value;
390     if (getDoubleFromObject(o, &value) != REDIS_OK) {
391         if (msg != NULL) {
392             addReplyError(c,(char*)msg);
393         } else {
394             addReplyError(c,"value is not a valid float");
395         }
396         return REDIS_ERR;
397     }
398     *target = value;
399     return REDIS_OK;
400 }
401 
402 int getLongDoubleFromObject(robj *o, long double *target) {
403     long double value;
404     char *eptr;
405 
406     if (o == NULL) {
407         value = 0;
408     } else {
409         redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
410         if (o->encoding == REDIS_ENCODING_RAW) {
411             errno = 0;
412             value = strtold(o->ptr, &eptr);
413             if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' ||
414                 errno == ERANGE || isnan(value))
415                 return REDIS_ERR;
416         } else if (o->encoding == REDIS_ENCODING_INT) {
417             value = (long)o->ptr;
418         } else {
419             redisPanic("Unknown string encoding");
420         }
421     }
422     *target = value;
423     return REDIS_OK;
424 }
425 
426 int getLongDoubleFromObjectOrReply(redisClient *c, robj *o, long double *target, const char *msg) {
427     long double value;
428     if (getLongDoubleFromObject(o, &value) != REDIS_OK) {
429         if (msg != NULL) {
430             addReplyError(c,(char*)msg);
431         } else {
432             addReplyError(c,"value is not a valid float");
433         }
434         return REDIS_ERR;
435     }
436     *target = value;
437     return REDIS_OK;
438 }
439 
440 int getLongLongFromObject(robj *o, long long *target) {
441     long long value;
442     char *eptr;
443 
444     if (o == NULL) {
445         value = 0;
446     } else {
447         redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
448         if (o->encoding == REDIS_ENCODING_RAW) {
449             errno = 0;
450             value = strtoll(o->ptr, &eptr, 10);
451             if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' ||
452                 errno == ERANGE)
453                 return REDIS_ERR;
454         } else if (o->encoding == REDIS_ENCODING_INT) {
455             value = (long)o->ptr;
456         } else {
457             redisPanic("Unknown string encoding");
458         }
459     }
460     if (target) *target = value;
461     return REDIS_OK;
462 }
463 
464 int getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, const char *msg) {
465     long long value;
466     if (getLongLongFromObject(o, &value) != REDIS_OK) {
467         if (msg != NULL) {
468             addReplyError(c,(char*)msg);
469         } else {
470             addReplyError(c,"value is not an integer or out of range");
471         }
472         return REDIS_ERR;
473     }
474     *target = value;
475     return REDIS_OK;
476 }
477 
478 int getLongFromObjectOrReply(redisClient *c, robj *o, long *target, const char *msg) {
479     long long value;
480 
481     if (getLongLongFromObjectOrReply(c, o, &value, msg) != REDIS_OK) return REDIS_ERR;
482     if (value < LONG_MIN || value > LONG_MAX) {
483         if (msg != NULL) {
484             addReplyError(c,(char*)msg);
485         } else {
486             addReplyError(c,"value is out of range");
487         }
488         return REDIS_ERR;
489     }
490     *target = value;
491     return REDIS_OK;
492 }
493 
494 char *strEncoding(int encoding) {
495     switch(encoding) {
496     case REDIS_ENCODING_RAW: return "raw";
497     case REDIS_ENCODING_INT: return "int";
498     case REDIS_ENCODING_HT: return "hashtable";
499     case REDIS_ENCODING_LINKEDLIST: return "linkedlist";
500     case REDIS_ENCODING_ZIPLIST: return "ziplist";
501     case REDIS_ENCODING_INTSET: return "intset";
502     case REDIS_ENCODING_SKIPLIST: return "skiplist";
503     default: return "unknown";
504     }
505 }
506 
507 /* Given an object returns the min number of seconds the object was never
508  * requested, using an approximated LRU algorithm. */
509 unsigned long estimateObjectIdleTime(robj *o) {
510     if (server.lruclock >= o->lru) {
511         return (server.lruclock - o->lru) * REDIS_LRU_CLOCK_RESOLUTION;
512     } else {
513         return ((REDIS_LRU_CLOCK_MAX - o->lru) + server.lruclock) *
514                     REDIS_LRU_CLOCK_RESOLUTION;
515     }
516 }
517 
518 /* This is an helper function for the DEBUG command. We need to lookup keys
519  * without any modification of LRU or other parameters. */
520 robj *objectCommandLookup(redisClient *c, robj *key) {
521     dictEntry *de;
522 
523     if ((de = dictFind(c->db->dict,key->ptr)) == NULL) return NULL;
524     return (robj*) dictGetVal(de);
525 }
526 
527 robj *objectCommandLookupOrReply(redisClient *c, robj *key, robj *reply) {
528     robj *o = objectCommandLookup(c,key);
529 
530     if (!o) addReply(c, reply);
531     return o;
532 }
533 
534 /* Object command allows to inspect the internals of an Redis Object.
535  * Usage: OBJECT <verb> ... arguments ... */
536 void objectCommand(redisClient *c) {
537     robj *o;
538 
539     if (!strcasecmp(c->argv[1]->ptr,"refcount") && c->argc == 3) {
540         if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
541                 == NULL) return;
542         addReplyLongLong(c,o->refcount);
543     } else if (!strcasecmp(c->argv[1]->ptr,"encoding") && c->argc == 3) {
544         if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
545                 == NULL) return;
546         addReplyBulkCString(c,strEncoding(o->encoding));
547     } else if (!strcasecmp(c->argv[1]->ptr,"idletime") && c->argc == 3) {
548         if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
549                 == NULL) return;
550         addReplyLongLong(c,estimateObjectIdleTime(o));
551     } else {
552         addReplyError(c,"Syntax error. Try OBJECT (refcount|encoding|idletime)");
553     }
554 }
555 
556