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