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