]> git.saurik.com Git - redis.git/blame - src/object.c
redis-trib: when loading node info also fetch the output of CLUSTER INFO.
[redis.git] / src / object.c
CommitLineData
e2641e09 1#include "redis.h"
673e1fb7 2#include <math.h>
e2641e09 3
4robj *createObject(int type, void *ptr) {
a9b18e54 5 robj *o = zmalloc(sizeof(*o));
e2641e09 6 o->type = type;
7 o->encoding = REDIS_ENCODING_RAW;
8 o->ptr = ptr;
9 o->refcount = 1;
a9b18e54 10
ef59a8bc 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. */
e2641e09 23 return o;
24}
25
26robj *createStringObject(char *ptr, size_t len) {
27 return createObject(REDIS_STRING,sdsnewlen(ptr,len));
28}
29
30robj *createStringObjectFromLongLong(long long value) {
31 robj *o;
c9d0c362 32 if (value >= 0 && value < REDIS_SHARED_INTEGERS) {
e2641e09 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
47robj *dupStringObject(robj *o) {
48 redisAssert(o->encoding == REDIS_ENCODING_RAW);
49 return createStringObject(o->ptr,sdslen(o->ptr));
50}
51
52robj *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
60robj *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
67robj *createSetObject(void) {
68 dict *d = dictCreate(&setDictType,NULL);
96ffb2fe
PN
69 robj *o = createObject(REDIS_SET,d);
70 o->encoding = REDIS_ENCODING_HT;
71 return o;
72}
73
74robj *createIntsetObject(void) {
75 intset *is = intsetNew();
76 robj *o = createObject(REDIS_SET,is);
77 o->encoding = REDIS_ENCODING_INTSET;
78 return o;
e2641e09 79}
80
81robj *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
91robj *createZsetObject(void) {
92 zset *zs = zmalloc(sizeof(*zs));
0b7f6d09 93 robj *o;
e2641e09 94
95 zs->dict = dictCreate(&zsetDictType,NULL);
96 zs->zsl = zslCreate();
0b7f6d09 97 o = createObject(REDIS_ZSET,zs);
98 o->encoding = REDIS_ENCODING_SKIPLIST;
99 return o;
e2641e09 100}
101
9e7cee0e
PN
102robj *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
e2641e09 109void freeStringObject(robj *o) {
110 if (o->encoding == REDIS_ENCODING_RAW) {
111 sdsfree(o->ptr);
112 }
113}
114
115void 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
128void freeSetObject(robj *o) {
96ffb2fe
PN
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 }
e2641e09 139}
140
141void freeZsetObject(robj *o) {
0f23eb3b
PN
142 zset *zs;
143 switch (o->encoding) {
100ed062 144 case REDIS_ENCODING_SKIPLIST:
0f23eb3b
PN
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 }
e2641e09 156}
157
158void 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
172void incrRefCount(robj *o) {
173 o->refcount++;
174}
175
176void decrRefCount(void *obj) {
177 robj *o = obj;
178
e2641e09 179 if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0");
936c4ab6 180 if (o->refcount == 1) {
e2641e09 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 }
a9b18e54 189 zfree(o);
936c4ab6 190 } else {
191 o->refcount--;
e2641e09 192 }
193}
194
4dd444bb 195/* This function set the ref count to zero without freeing the object.
196 * It is useful in order to pass a new object to functions incrementing
197 * the ref count of the received object. Example:
198 *
199 * functionThatWillIncrementRefCount(resetRefCount(CreateObject(...)));
200 *
201 * Otherwise you need to resort to the less elegant pattern:
202 *
203 * *obj = createObject(...);
204 * functionThatWillIncrementRefCount(obj);
205 * decrRefCount(obj);
206 */
207robj *resetRefCount(robj *obj) {
208 obj->refcount = 0;
209 return obj;
210}
211
e2641e09 212int checkType(redisClient *c, robj *o, int type) {
213 if (o->type != type) {
214 addReply(c,shared.wrongtypeerr);
215 return 1;
216 }
217 return 0;
218}
219
5d081931
PN
220int isObjectRepresentableAsLongLong(robj *o, long long *llval) {
221 redisAssert(o->type == REDIS_STRING);
222 if (o->encoding == REDIS_ENCODING_INT) {
223 if (llval) *llval = (long) o->ptr;
224 return REDIS_OK;
225 } else {
226 return string2ll(o->ptr,sdslen(o->ptr),llval) ? REDIS_OK : REDIS_ERR;
227 }
228}
229
e2641e09 230/* Try to encode a string object in order to save space */
231robj *tryObjectEncoding(robj *o) {
232 long value;
233 sds s = o->ptr;
234
235 if (o->encoding != REDIS_ENCODING_RAW)
236 return o; /* Already encoded */
237
238 /* It's not safe to encode shared objects: shared objects can be shared
239 * everywhere in the "object space" of Redis. Encoded objects can only
240 * appear as "values" (and not, for instance, as keys) */
241 if (o->refcount > 1) return o;
242
243 /* Currently we try to encode only strings */
244 redisAssert(o->type == REDIS_STRING);
245
246 /* Check if we can represent this string as a long integer */
5d081931 247 if (!string2l(s,sdslen(s),&value)) return o;
e2641e09 248
0e5441d8 249 /* Ok, this object can be encoded...
250 *
251 * Can I use a shared object? Only if the object is inside a given
cea8c5cd 252 * range and if the back end in use is in-memory. For disk store every
253 * object in memory used as value should be independent.
13a49af4 254 *
255 * Note that we also avoid using shared integers when maxmemory is used
cea8c5cd 256 * because every object needs to have a private LRU field for the LRU
13a49af4 257 * algorithm to work well. */
c9d0c362 258 if (server.maxmemory == 0 && value >= 0 && value < REDIS_SHARED_INTEGERS) {
e2641e09 259 decrRefCount(o);
260 incrRefCount(shared.integers[value]);
261 return shared.integers[value];
262 } else {
263 o->encoding = REDIS_ENCODING_INT;
264 sdsfree(o->ptr);
265 o->ptr = (void*) value;
266 return o;
267 }
268}
269
270/* Get a decoded version of an encoded object (returned as a new object).
271 * If the object is already raw-encoded just increment the ref count. */
272robj *getDecodedObject(robj *o) {
273 robj *dec;
274
275 if (o->encoding == REDIS_ENCODING_RAW) {
276 incrRefCount(o);
277 return o;
278 }
279 if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_INT) {
280 char buf[32];
281
282 ll2string(buf,32,(long)o->ptr);
283 dec = createStringObject(buf,strlen(buf));
284 return dec;
285 } else {
286 redisPanic("Unknown encoding type");
287 }
288}
289
290/* Compare two string objects via strcmp() or alike.
291 * Note that the objects may be integer-encoded. In such a case we
292 * use ll2string() to get a string representation of the numbers on the stack
293 * and compare the strings, it's much faster than calling getDecodedObject().
294 *
295 * Important note: if objects are not integer encoded, but binary-safe strings,
296 * sdscmp() from sds.c will apply memcmp() so this function ca be considered
297 * binary safe. */
298int compareStringObjects(robj *a, robj *b) {
299 redisAssert(a->type == REDIS_STRING && b->type == REDIS_STRING);
300 char bufa[128], bufb[128], *astr, *bstr;
301 int bothsds = 1;
302
303 if (a == b) return 0;
304 if (a->encoding != REDIS_ENCODING_RAW) {
305 ll2string(bufa,sizeof(bufa),(long) a->ptr);
306 astr = bufa;
307 bothsds = 0;
308 } else {
309 astr = a->ptr;
310 }
311 if (b->encoding != REDIS_ENCODING_RAW) {
312 ll2string(bufb,sizeof(bufb),(long) b->ptr);
313 bstr = bufb;
314 bothsds = 0;
315 } else {
316 bstr = b->ptr;
317 }
318 return bothsds ? sdscmp(astr,bstr) : strcmp(astr,bstr);
319}
320
321/* Equal string objects return 1 if the two objects are the same from the
322 * point of view of a string comparison, otherwise 0 is returned. Note that
323 * this function is faster then checking for (compareStringObject(a,b) == 0)
324 * because it can perform some more optimization. */
325int equalStringObjects(robj *a, robj *b) {
326 if (a->encoding != REDIS_ENCODING_RAW && b->encoding != REDIS_ENCODING_RAW){
327 return a->ptr == b->ptr;
328 } else {
329 return compareStringObjects(a,b) == 0;
330 }
331}
332
333size_t stringObjectLen(robj *o) {
334 redisAssert(o->type == REDIS_STRING);
335 if (o->encoding == REDIS_ENCODING_RAW) {
336 return sdslen(o->ptr);
337 } else {
338 char buf[32];
339
340 return ll2string(buf,32,(long)o->ptr);
341 }
342}
343
344int getDoubleFromObject(robj *o, double *target) {
345 double value;
346 char *eptr;
347
348 if (o == NULL) {
349 value = 0;
350 } else {
351 redisAssert(o->type == REDIS_STRING);
352 if (o->encoding == REDIS_ENCODING_RAW) {
353 value = strtod(o->ptr, &eptr);
673e1fb7 354 if (eptr[0] != '\0' || isnan(value)) return REDIS_ERR;
e2641e09 355 } else if (o->encoding == REDIS_ENCODING_INT) {
356 value = (long)o->ptr;
357 } else {
358 redisPanic("Unknown string encoding");
359 }
360 }
361
362 *target = value;
363 return REDIS_OK;
364}
365
366int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const char *msg) {
367 double value;
368 if (getDoubleFromObject(o, &value) != REDIS_OK) {
369 if (msg != NULL) {
3ab20376 370 addReplyError(c,(char*)msg);
e2641e09 371 } else {
3ab20376 372 addReplyError(c,"value is not a double");
e2641e09 373 }
374 return REDIS_ERR;
375 }
376
377 *target = value;
378 return REDIS_OK;
379}
380
381int getLongLongFromObject(robj *o, long long *target) {
382 long long value;
383 char *eptr;
384
385 if (o == NULL) {
386 value = 0;
387 } else {
388 redisAssert(o->type == REDIS_STRING);
389 if (o->encoding == REDIS_ENCODING_RAW) {
390 value = strtoll(o->ptr, &eptr, 10);
391 if (eptr[0] != '\0') return REDIS_ERR;
c91abdcd 392 if (errno == ERANGE && (value == LLONG_MIN || value == LLONG_MAX))
393 return REDIS_ERR;
e2641e09 394 } else if (o->encoding == REDIS_ENCODING_INT) {
395 value = (long)o->ptr;
396 } else {
397 redisPanic("Unknown string encoding");
398 }
399 }
400
96ffb2fe 401 if (target) *target = value;
e2641e09 402 return REDIS_OK;
403}
404
405int getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, const char *msg) {
406 long long value;
407 if (getLongLongFromObject(o, &value) != REDIS_OK) {
408 if (msg != NULL) {
3ab20376 409 addReplyError(c,(char*)msg);
e2641e09 410 } else {
3ab20376 411 addReplyError(c,"value is not an integer or out of range");
e2641e09 412 }
413 return REDIS_ERR;
414 }
415
416 *target = value;
417 return REDIS_OK;
418}
419
420int getLongFromObjectOrReply(redisClient *c, robj *o, long *target, const char *msg) {
421 long long value;
422
423 if (getLongLongFromObjectOrReply(c, o, &value, msg) != REDIS_OK) return REDIS_ERR;
424 if (value < LONG_MIN || value > LONG_MAX) {
425 if (msg != NULL) {
3ab20376 426 addReplyError(c,(char*)msg);
e2641e09 427 } else {
3ab20376 428 addReplyError(c,"value is out of range");
e2641e09 429 }
430 return REDIS_ERR;
431 }
432
433 *target = value;
434 return REDIS_OK;
435}
436
437char *strEncoding(int encoding) {
438 switch(encoding) {
439 case REDIS_ENCODING_RAW: return "raw";
440 case REDIS_ENCODING_INT: return "int";
441 case REDIS_ENCODING_HT: return "hashtable";
442 case REDIS_ENCODING_ZIPMAP: return "zipmap";
443 case REDIS_ENCODING_LINKEDLIST: return "linkedlist";
444 case REDIS_ENCODING_ZIPLIST: return "ziplist";
96ffb2fe 445 case REDIS_ENCODING_INTSET: return "intset";
0b7f6d09 446 case REDIS_ENCODING_SKIPLIST: return "skiplist";
e2641e09 447 default: return "unknown";
448 }
449}
ef59a8bc 450
451/* Given an object returns the min number of seconds the object was never
452 * requested, using an approximated LRU algorithm. */
453unsigned long estimateObjectIdleTime(robj *o) {
454 if (server.lruclock >= o->lru) {
165346ca 455 return (server.lruclock - o->lru) * REDIS_LRU_CLOCK_RESOLUTION;
ef59a8bc 456 } else {
165346ca 457 return ((REDIS_LRU_CLOCK_MAX - o->lru) + server.lruclock) *
458 REDIS_LRU_CLOCK_RESOLUTION;
ef59a8bc 459 }
460}
ece74202 461
462/* This is an helper function for the DEBUG command. We need to lookup keys
463 * without any modification of LRU or other parameters. */
464robj *objectCommandLookup(redisClient *c, robj *key) {
465 dictEntry *de;
466
467 if ((de = dictFind(c->db->dict,key->ptr)) == NULL) return NULL;
468 return (robj*) dictGetEntryVal(de);
469}
470
471robj *objectCommandLookupOrReply(redisClient *c, robj *key, robj *reply) {
472 robj *o = objectCommandLookup(c,key);
473
474 if (!o) addReply(c, reply);
475 return o;
476}
477
478/* Object command allows to inspect the internals of an Redis Object.
479 * Usage: OBJECT <verb> ... arguments ... */
480void objectCommand(redisClient *c) {
481 robj *o;
482
483 if (!strcasecmp(c->argv[1]->ptr,"refcount") && c->argc == 3) {
484 if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
485 == NULL) return;
486 addReplyLongLong(c,o->refcount);
487 } else if (!strcasecmp(c->argv[1]->ptr,"encoding") && c->argc == 3) {
488 if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
489 == NULL) return;
490 addReplyBulkCString(c,strEncoding(o->encoding));
491 } else if (!strcasecmp(c->argv[1]->ptr,"idletime") && c->argc == 3) {
492 if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
493 == NULL) return;
494 addReplyLongLong(c,estimateObjectIdleTime(o));
495 } else {
496 addReplyError(c,"Syntax error. Try OBJECT (refcount|encoding|idletime)");
497 }
498}
499