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