xref: /redis-3.2.3/src/object.c (revision 03116904)
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
274      * range and if the back end in use is in-memory. For disk store every
275      * object in memory used as value should be independent.
276      *
277      * Note that we also avoid using shared integers when maxmemory is used
278      * because every object needs to have a private LRU field for the LRU
279      * algorithm to work well. */
280     if (server.maxmemory == 0 && value >= 0 && value < REDIS_SHARED_INTEGERS) {
281         decrRefCount(o);
282         incrRefCount(shared.integers[value]);
283         return shared.integers[value];
284     } else {
285         o->encoding = REDIS_ENCODING_INT;
286         sdsfree(o->ptr);
287         o->ptr = (void*) value;
288         return o;
289     }
290 }
291 
292 /* Get a decoded version of an encoded object (returned as a new object).
293  * If the object is already raw-encoded just increment the ref count. */
294 robj *getDecodedObject(robj *o) {
295     robj *dec;
296 
297     if (o->encoding == REDIS_ENCODING_RAW) {
298         incrRefCount(o);
299         return o;
300     }
301     if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_INT) {
302         char buf[32];
303 
304         ll2string(buf,32,(long)o->ptr);
305         dec = createStringObject(buf,strlen(buf));
306         return dec;
307     } else {
308         redisPanic("Unknown encoding type");
309     }
310 }
311 
312 /* Compare two string objects via strcmp() or alike.
313  * Note that the objects may be integer-encoded. In such a case we
314  * use ll2string() to get a string representation of the numbers on the stack
315  * and compare the strings, it's much faster than calling getDecodedObject().
316  *
317  * Important note: if objects are not integer encoded, but binary-safe strings,
318  * sdscmp() from sds.c will apply memcmp() so this function ca be considered
319  * binary safe. */
320 int compareStringObjects(robj *a, robj *b) {
321     redisAssertWithInfo(NULL,a,a->type == REDIS_STRING && b->type == REDIS_STRING);
322     char bufa[128], bufb[128], *astr, *bstr;
323     int bothsds = 1;
324 
325     if (a == b) return 0;
326     if (a->encoding != REDIS_ENCODING_RAW) {
327         ll2string(bufa,sizeof(bufa),(long) a->ptr);
328         astr = bufa;
329         bothsds = 0;
330     } else {
331         astr = a->ptr;
332     }
333     if (b->encoding != REDIS_ENCODING_RAW) {
334         ll2string(bufb,sizeof(bufb),(long) b->ptr);
335         bstr = bufb;
336         bothsds = 0;
337     } else {
338         bstr = b->ptr;
339     }
340     return bothsds ? sdscmp(astr,bstr) : strcmp(astr,bstr);
341 }
342 
343 /* Equal string objects return 1 if the two objects are the same from the
344  * point of view of a string comparison, otherwise 0 is returned. Note that
345  * this function is faster then checking for (compareStringObject(a,b) == 0)
346  * because it can perform some more optimization. */
347 int equalStringObjects(robj *a, robj *b) {
348     if (a->encoding != REDIS_ENCODING_RAW && b->encoding != REDIS_ENCODING_RAW){
349         return a->ptr == b->ptr;
350     } else {
351         return compareStringObjects(a,b) == 0;
352     }
353 }
354 
355 size_t stringObjectLen(robj *o) {
356     redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
357     if (o->encoding == REDIS_ENCODING_RAW) {
358         return sdslen(o->ptr);
359     } else {
360         char buf[32];
361 
362         return ll2string(buf,32,(long)o->ptr);
363     }
364 }
365 
366 int getDoubleFromObject(robj *o, double *target) {
367     double value;
368     char *eptr;
369 
370     if (o == NULL) {
371         value = 0;
372     } else {
373         redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
374         if (o->encoding == REDIS_ENCODING_RAW) {
375             errno = 0;
376             value = strtod(o->ptr, &eptr);
377             if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' ||
378                 errno == ERANGE || isnan(value))
379                 return REDIS_ERR;
380         } else if (o->encoding == REDIS_ENCODING_INT) {
381             value = (long)o->ptr;
382         } else {
383             redisPanic("Unknown string encoding");
384         }
385     }
386     *target = value;
387     return REDIS_OK;
388 }
389 
390 int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const char *msg) {
391     double value;
392     if (getDoubleFromObject(o, &value) != REDIS_OK) {
393         if (msg != NULL) {
394             addReplyError(c,(char*)msg);
395         } else {
396             addReplyError(c,"value is not a valid float");
397         }
398         return REDIS_ERR;
399     }
400     *target = value;
401     return REDIS_OK;
402 }
403 
404 int getLongDoubleFromObject(robj *o, long double *target) {
405     long double value;
406     char *eptr;
407 
408     if (o == NULL) {
409         value = 0;
410     } else {
411         redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
412         if (o->encoding == REDIS_ENCODING_RAW) {
413             errno = 0;
414             value = strtold(o->ptr, &eptr);
415             if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' ||
416                 errno == ERANGE || isnan(value))
417                 return REDIS_ERR;
418         } else if (o->encoding == REDIS_ENCODING_INT) {
419             value = (long)o->ptr;
420         } else {
421             redisPanic("Unknown string encoding");
422         }
423     }
424     *target = value;
425     return REDIS_OK;
426 }
427 
428 int getLongDoubleFromObjectOrReply(redisClient *c, robj *o, long double *target, const char *msg) {
429     long double value;
430     if (getLongDoubleFromObject(o, &value) != REDIS_OK) {
431         if (msg != NULL) {
432             addReplyError(c,(char*)msg);
433         } else {
434             addReplyError(c,"value is not a valid float");
435         }
436         return REDIS_ERR;
437     }
438     *target = value;
439     return REDIS_OK;
440 }
441 
442 int getLongLongFromObject(robj *o, long long *target) {
443     long long value;
444     char *eptr;
445 
446     if (o == NULL) {
447         value = 0;
448     } else {
449         redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
450         if (o->encoding == REDIS_ENCODING_RAW) {
451             errno = 0;
452             value = strtoll(o->ptr, &eptr, 10);
453             if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' ||
454                 errno == ERANGE)
455                 return REDIS_ERR;
456         } else if (o->encoding == REDIS_ENCODING_INT) {
457             value = (long)o->ptr;
458         } else {
459             redisPanic("Unknown string encoding");
460         }
461     }
462     if (target) *target = value;
463     return REDIS_OK;
464 }
465 
466 int getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, const char *msg) {
467     long long value;
468     if (getLongLongFromObject(o, &value) != REDIS_OK) {
469         if (msg != NULL) {
470             addReplyError(c,(char*)msg);
471         } else {
472             addReplyError(c,"value is not an integer or out of range");
473         }
474         return REDIS_ERR;
475     }
476     *target = value;
477     return REDIS_OK;
478 }
479 
480 int getLongFromObjectOrReply(redisClient *c, robj *o, long *target, const char *msg) {
481     long long value;
482 
483     if (getLongLongFromObjectOrReply(c, o, &value, msg) != REDIS_OK) return REDIS_ERR;
484     if (value < LONG_MIN || value > LONG_MAX) {
485         if (msg != NULL) {
486             addReplyError(c,(char*)msg);
487         } else {
488             addReplyError(c,"value is out of range");
489         }
490         return REDIS_ERR;
491     }
492     *target = value;
493     return REDIS_OK;
494 }
495 
496 char *strEncoding(int encoding) {
497     switch(encoding) {
498     case REDIS_ENCODING_RAW: return "raw";
499     case REDIS_ENCODING_INT: return "int";
500     case REDIS_ENCODING_HT: return "hashtable";
501     case REDIS_ENCODING_LINKEDLIST: return "linkedlist";
502     case REDIS_ENCODING_ZIPLIST: return "ziplist";
503     case REDIS_ENCODING_INTSET: return "intset";
504     case REDIS_ENCODING_SKIPLIST: return "skiplist";
505     default: return "unknown";
506     }
507 }
508 
509 /* Given an object returns the min number of seconds the object was never
510  * requested, using an approximated LRU algorithm. */
511 unsigned long estimateObjectIdleTime(robj *o) {
512     if (server.lruclock >= o->lru) {
513         return (server.lruclock - o->lru) * REDIS_LRU_CLOCK_RESOLUTION;
514     } else {
515         return ((REDIS_LRU_CLOCK_MAX - o->lru) + server.lruclock) *
516                     REDIS_LRU_CLOCK_RESOLUTION;
517     }
518 }
519 
520 /* This is an helper function for the DEBUG command. We need to lookup keys
521  * without any modification of LRU or other parameters. */
522 robj *objectCommandLookup(redisClient *c, robj *key) {
523     dictEntry *de;
524 
525     if ((de = dictFind(c->db->dict,key->ptr)) == NULL) return NULL;
526     return (robj*) dictGetVal(de);
527 }
528 
529 robj *objectCommandLookupOrReply(redisClient *c, robj *key, robj *reply) {
530     robj *o = objectCommandLookup(c,key);
531 
532     if (!o) addReply(c, reply);
533     return o;
534 }
535 
536 /* Object command allows to inspect the internals of an Redis Object.
537  * Usage: OBJECT <verb> ... arguments ... */
538 void objectCommand(redisClient *c) {
539     robj *o;
540 
541     if (!strcasecmp(c->argv[1]->ptr,"refcount") && c->argc == 3) {
542         if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
543                 == NULL) return;
544         addReplyLongLong(c,o->refcount);
545     } else if (!strcasecmp(c->argv[1]->ptr,"encoding") && c->argc == 3) {
546         if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
547                 == NULL) return;
548         addReplyBulkCString(c,strEncoding(o->encoding));
549     } else if (!strcasecmp(c->argv[1]->ptr,"idletime") && c->argc == 3) {
550         if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
551                 == NULL) return;
552         addReplyLongLong(c,estimateObjectIdleTime(o));
553     } else {
554         addReplyError(c,"Syntax error. Try OBJECT (refcount|encoding|idletime)");
555     }
556 }
557 
558