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