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