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