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