xref: /redis-3.2.3/src/object.c (revision 4942145d)
1 #include "redis.h"
2 #include <pthread.h>
3 #include <math.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         !server.ds_enabled &&
35         pthread_equal(pthread_self(),server.mainthread)) {
36         incrRefCount(shared.integers[value]);
37         o = shared.integers[value];
38     } else {
39         if (value >= LONG_MIN && value <= LONG_MAX) {
40             o = createObject(REDIS_STRING, NULL);
41             o->encoding = REDIS_ENCODING_INT;
42             o->ptr = (void*)((long)value);
43         } else {
44             o = createObject(REDIS_STRING,sdsfromlonglong(value));
45         }
46     }
47     return o;
48 }
49 
50 robj *dupStringObject(robj *o) {
51     redisAssert(o->encoding == REDIS_ENCODING_RAW);
52     return createStringObject(o->ptr,sdslen(o->ptr));
53 }
54 
55 robj *createListObject(void) {
56     list *l = listCreate();
57     robj *o = createObject(REDIS_LIST,l);
58     listSetFreeMethod(l,decrRefCount);
59     o->encoding = REDIS_ENCODING_LINKEDLIST;
60     return o;
61 }
62 
63 robj *createZiplistObject(void) {
64     unsigned char *zl = ziplistNew();
65     robj *o = createObject(REDIS_LIST,zl);
66     o->encoding = REDIS_ENCODING_ZIPLIST;
67     return o;
68 }
69 
70 robj *createSetObject(void) {
71     dict *d = dictCreate(&setDictType,NULL);
72     robj *o = createObject(REDIS_SET,d);
73     o->encoding = REDIS_ENCODING_HT;
74     return o;
75 }
76 
77 robj *createIntsetObject(void) {
78     intset *is = intsetNew();
79     robj *o = createObject(REDIS_SET,is);
80     o->encoding = REDIS_ENCODING_INTSET;
81     return o;
82 }
83 
84 robj *createHashObject(void) {
85     /* All the Hashes start as zipmaps. Will be automatically converted
86      * into hash tables if there are enough elements or big elements
87      * inside. */
88     unsigned char *zm = zipmapNew();
89     robj *o = createObject(REDIS_HASH,zm);
90     o->encoding = REDIS_ENCODING_ZIPMAP;
91     return o;
92 }
93 
94 robj *createZsetObject(void) {
95     zset *zs = zmalloc(sizeof(*zs));
96 
97     zs->dict = dictCreate(&zsetDictType,NULL);
98     zs->zsl = zslCreate();
99     return createObject(REDIS_ZSET,zs);
100 }
101 
102 void freeStringObject(robj *o) {
103     if (o->encoding == REDIS_ENCODING_RAW) {
104         sdsfree(o->ptr);
105     }
106 }
107 
108 void freeListObject(robj *o) {
109     switch (o->encoding) {
110     case REDIS_ENCODING_LINKEDLIST:
111         listRelease((list*) o->ptr);
112         break;
113     case REDIS_ENCODING_ZIPLIST:
114         zfree(o->ptr);
115         break;
116     default:
117         redisPanic("Unknown list encoding type");
118     }
119 }
120 
121 void freeSetObject(robj *o) {
122     switch (o->encoding) {
123     case REDIS_ENCODING_HT:
124         dictRelease((dict*) o->ptr);
125         break;
126     case REDIS_ENCODING_INTSET:
127         zfree(o->ptr);
128         break;
129     default:
130         redisPanic("Unknown set encoding type");
131     }
132 }
133 
134 void freeZsetObject(robj *o) {
135     zset *zs = o->ptr;
136 
137     dictRelease(zs->dict);
138     zslFree(zs->zsl);
139     zfree(zs);
140 }
141 
142 void freeHashObject(robj *o) {
143     switch (o->encoding) {
144     case REDIS_ENCODING_HT:
145         dictRelease((dict*) o->ptr);
146         break;
147     case REDIS_ENCODING_ZIPMAP:
148         zfree(o->ptr);
149         break;
150     default:
151         redisPanic("Unknown hash encoding type");
152         break;
153     }
154 }
155 
156 void incrRefCount(robj *o) {
157     o->refcount++;
158 }
159 
160 void decrRefCount(void *obj) {
161     robj *o = obj;
162 
163     if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0");
164     if (--(o->refcount) == 0) {
165         switch(o->type) {
166         case REDIS_STRING: freeStringObject(o); break;
167         case REDIS_LIST: freeListObject(o); break;
168         case REDIS_SET: freeSetObject(o); break;
169         case REDIS_ZSET: freeZsetObject(o); break;
170         case REDIS_HASH: freeHashObject(o); break;
171         default: redisPanic("Unknown object type"); break;
172         }
173         o->ptr = NULL; /* defensive programming. We'll see NULL in traces. */
174         zfree(o);
175     }
176 }
177 
178 int checkType(redisClient *c, robj *o, int type) {
179     if (o->type != type) {
180         addReply(c,shared.wrongtypeerr);
181         return 1;
182     }
183     return 0;
184 }
185 
186 /* Try to encode a string object in order to save space */
187 robj *tryObjectEncoding(robj *o) {
188     long value;
189     sds s = o->ptr;
190 
191     if (o->encoding != REDIS_ENCODING_RAW)
192         return o; /* Already encoded */
193 
194     /* It's not safe to encode shared objects: shared objects can be shared
195      * everywhere in the "object space" of Redis. Encoded objects can only
196      * appear as "values" (and not, for instance, as keys) */
197      if (o->refcount > 1) return o;
198 
199     /* Currently we try to encode only strings */
200     redisAssert(o->type == REDIS_STRING);
201 
202     /* Check if we can represent this string as a long integer */
203     if (isStringRepresentableAsLong(s,&value) == REDIS_ERR) return o;
204 
205     /* Ok, this object can be encoded...
206      *
207      * Can I use a shared object? Only if the object is inside a given
208      * range and if the back end in use is in-memory. For disk store every
209      * object in memory used as value should be independent.
210      *
211      * Note that we also avoid using shared integers when maxmemory is used
212      * because every object needs to have a private LRU field for the LRU
213      * algorithm to work well. */
214     if (!server.ds_enabled &&
215         server.maxmemory == 0 && value >= 0 && value < REDIS_SHARED_INTEGERS &&
216         pthread_equal(pthread_self(),server.mainthread))
217     {
218         decrRefCount(o);
219         incrRefCount(shared.integers[value]);
220         return shared.integers[value];
221     } else {
222         o->encoding = REDIS_ENCODING_INT;
223         sdsfree(o->ptr);
224         o->ptr = (void*) value;
225         return o;
226     }
227 }
228 
229 /* Get a decoded version of an encoded object (returned as a new object).
230  * If the object is already raw-encoded just increment the ref count. */
231 robj *getDecodedObject(robj *o) {
232     robj *dec;
233 
234     if (o->encoding == REDIS_ENCODING_RAW) {
235         incrRefCount(o);
236         return o;
237     }
238     if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_INT) {
239         char buf[32];
240 
241         ll2string(buf,32,(long)o->ptr);
242         dec = createStringObject(buf,strlen(buf));
243         return dec;
244     } else {
245         redisPanic("Unknown encoding type");
246     }
247 }
248 
249 /* Compare two string objects via strcmp() or alike.
250  * Note that the objects may be integer-encoded. In such a case we
251  * use ll2string() to get a string representation of the numbers on the stack
252  * and compare the strings, it's much faster than calling getDecodedObject().
253  *
254  * Important note: if objects are not integer encoded, but binary-safe strings,
255  * sdscmp() from sds.c will apply memcmp() so this function ca be considered
256  * binary safe. */
257 int compareStringObjects(robj *a, robj *b) {
258     redisAssert(a->type == REDIS_STRING && b->type == REDIS_STRING);
259     char bufa[128], bufb[128], *astr, *bstr;
260     int bothsds = 1;
261 
262     if (a == b) return 0;
263     if (a->encoding != REDIS_ENCODING_RAW) {
264         ll2string(bufa,sizeof(bufa),(long) a->ptr);
265         astr = bufa;
266         bothsds = 0;
267     } else {
268         astr = a->ptr;
269     }
270     if (b->encoding != REDIS_ENCODING_RAW) {
271         ll2string(bufb,sizeof(bufb),(long) b->ptr);
272         bstr = bufb;
273         bothsds = 0;
274     } else {
275         bstr = b->ptr;
276     }
277     return bothsds ? sdscmp(astr,bstr) : strcmp(astr,bstr);
278 }
279 
280 /* Equal string objects return 1 if the two objects are the same from the
281  * point of view of a string comparison, otherwise 0 is returned. Note that
282  * this function is faster then checking for (compareStringObject(a,b) == 0)
283  * because it can perform some more optimization. */
284 int equalStringObjects(robj *a, robj *b) {
285     if (a->encoding != REDIS_ENCODING_RAW && b->encoding != REDIS_ENCODING_RAW){
286         return a->ptr == b->ptr;
287     } else {
288         return compareStringObjects(a,b) == 0;
289     }
290 }
291 
292 size_t stringObjectLen(robj *o) {
293     redisAssert(o->type == REDIS_STRING);
294     if (o->encoding == REDIS_ENCODING_RAW) {
295         return sdslen(o->ptr);
296     } else {
297         char buf[32];
298 
299         return ll2string(buf,32,(long)o->ptr);
300     }
301 }
302 
303 int getDoubleFromObject(robj *o, double *target) {
304     double value;
305     char *eptr;
306 
307     if (o == NULL) {
308         value = 0;
309     } else {
310         redisAssert(o->type == REDIS_STRING);
311         if (o->encoding == REDIS_ENCODING_RAW) {
312             value = strtod(o->ptr, &eptr);
313             if (eptr[0] != '\0' || isnan(value)) return REDIS_ERR;
314         } else if (o->encoding == REDIS_ENCODING_INT) {
315             value = (long)o->ptr;
316         } else {
317             redisPanic("Unknown string encoding");
318         }
319     }
320 
321     *target = value;
322     return REDIS_OK;
323 }
324 
325 int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const char *msg) {
326     double value;
327     if (getDoubleFromObject(o, &value) != REDIS_OK) {
328         if (msg != NULL) {
329             addReplyError(c,(char*)msg);
330         } else {
331             addReplyError(c,"value is not a double");
332         }
333         return REDIS_ERR;
334     }
335 
336     *target = value;
337     return REDIS_OK;
338 }
339 
340 int getLongLongFromObject(robj *o, long long *target) {
341     long long value;
342     char *eptr;
343 
344     if (o == NULL) {
345         value = 0;
346     } else {
347         redisAssert(o->type == REDIS_STRING);
348         if (o->encoding == REDIS_ENCODING_RAW) {
349             value = strtoll(o->ptr, &eptr, 10);
350             if (eptr[0] != '\0') return REDIS_ERR;
351             if (errno == ERANGE && (value == LLONG_MIN || value == LLONG_MAX))
352                 return REDIS_ERR;
353         } else if (o->encoding == REDIS_ENCODING_INT) {
354             value = (long)o->ptr;
355         } else {
356             redisPanic("Unknown string encoding");
357         }
358     }
359 
360     if (target) *target = value;
361     return REDIS_OK;
362 }
363 
364 int getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, const char *msg) {
365     long long value;
366     if (getLongLongFromObject(o, &value) != REDIS_OK) {
367         if (msg != NULL) {
368             addReplyError(c,(char*)msg);
369         } else {
370             addReplyError(c,"value is not an integer or out of range");
371         }
372         return REDIS_ERR;
373     }
374 
375     *target = value;
376     return REDIS_OK;
377 }
378 
379 int getLongFromObjectOrReply(redisClient *c, robj *o, long *target, const char *msg) {
380     long long value;
381 
382     if (getLongLongFromObjectOrReply(c, o, &value, msg) != REDIS_OK) return REDIS_ERR;
383     if (value < LONG_MIN || value > LONG_MAX) {
384         if (msg != NULL) {
385             addReplyError(c,(char*)msg);
386         } else {
387             addReplyError(c,"value is out of range");
388         }
389         return REDIS_ERR;
390     }
391 
392     *target = value;
393     return REDIS_OK;
394 }
395 
396 char *strEncoding(int encoding) {
397     switch(encoding) {
398     case REDIS_ENCODING_RAW: return "raw";
399     case REDIS_ENCODING_INT: return "int";
400     case REDIS_ENCODING_HT: return "hashtable";
401     case REDIS_ENCODING_ZIPMAP: return "zipmap";
402     case REDIS_ENCODING_LINKEDLIST: return "linkedlist";
403     case REDIS_ENCODING_ZIPLIST: return "ziplist";
404     case REDIS_ENCODING_INTSET: return "intset";
405     default: return "unknown";
406     }
407 }
408 
409 /* Given an object returns the min number of seconds the object was never
410  * requested, using an approximated LRU algorithm. */
411 unsigned long estimateObjectIdleTime(robj *o) {
412     if (server.lruclock >= o->lru) {
413         return (server.lruclock - o->lru) * REDIS_LRU_CLOCK_RESOLUTION;
414     } else {
415         return ((REDIS_LRU_CLOCK_MAX - o->lru) + server.lruclock) *
416                     REDIS_LRU_CLOCK_RESOLUTION;
417     }
418 }
419