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