]> git.saurik.com Git - redis.git/blame_incremental - src/object.c
convert-zipmap-hash-on-load test enabled
[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
274 * range and if the back end in use is in-memory. For disk store every
275 * object in memory used as value should be independent.
276 *
277 * Note that we also avoid using shared integers when maxmemory is used
278 * because every object needs to have a private LRU field for the LRU
279 * algorithm to work well. */
280 if (server.maxmemory == 0 && value >= 0 && value < REDIS_SHARED_INTEGERS) {
281 decrRefCount(o);
282 incrRefCount(shared.integers[value]);
283 return shared.integers[value];
284 } else {
285 o->encoding = REDIS_ENCODING_INT;
286 sdsfree(o->ptr);
287 o->ptr = (void*) value;
288 return o;
289 }
290}
291
292/* Get a decoded version of an encoded object (returned as a new object).
293 * If the object is already raw-encoded just increment the ref count. */
294robj *getDecodedObject(robj *o) {
295 robj *dec;
296
297 if (o->encoding == REDIS_ENCODING_RAW) {
298 incrRefCount(o);
299 return o;
300 }
301 if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_INT) {
302 char buf[32];
303
304 ll2string(buf,32,(long)o->ptr);
305 dec = createStringObject(buf,strlen(buf));
306 return dec;
307 } else {
308 redisPanic("Unknown encoding type");
309 }
310}
311
312/* Compare two string objects via strcmp() or alike.
313 * Note that the objects may be integer-encoded. In such a case we
314 * use ll2string() to get a string representation of the numbers on the stack
315 * and compare the strings, it's much faster than calling getDecodedObject().
316 *
317 * Important note: if objects are not integer encoded, but binary-safe strings,
318 * sdscmp() from sds.c will apply memcmp() so this function ca be considered
319 * binary safe. */
320int compareStringObjects(robj *a, robj *b) {
321 redisAssertWithInfo(NULL,a,a->type == REDIS_STRING && b->type == REDIS_STRING);
322 char bufa[128], bufb[128], *astr, *bstr;
323 int bothsds = 1;
324
325 if (a == b) return 0;
326 if (a->encoding != REDIS_ENCODING_RAW) {
327 ll2string(bufa,sizeof(bufa),(long) a->ptr);
328 astr = bufa;
329 bothsds = 0;
330 } else {
331 astr = a->ptr;
332 }
333 if (b->encoding != REDIS_ENCODING_RAW) {
334 ll2string(bufb,sizeof(bufb),(long) b->ptr);
335 bstr = bufb;
336 bothsds = 0;
337 } else {
338 bstr = b->ptr;
339 }
340 return bothsds ? sdscmp(astr,bstr) : strcmp(astr,bstr);
341}
342
343/* Equal string objects return 1 if the two objects are the same from the
344 * point of view of a string comparison, otherwise 0 is returned. Note that
345 * this function is faster then checking for (compareStringObject(a,b) == 0)
346 * because it can perform some more optimization. */
347int equalStringObjects(robj *a, robj *b) {
348 if (a->encoding != REDIS_ENCODING_RAW && b->encoding != REDIS_ENCODING_RAW){
349 return a->ptr == b->ptr;
350 } else {
351 return compareStringObjects(a,b) == 0;
352 }
353}
354
355size_t stringObjectLen(robj *o) {
356 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
357 if (o->encoding == REDIS_ENCODING_RAW) {
358 return sdslen(o->ptr);
359 } else {
360 char buf[32];
361
362 return ll2string(buf,32,(long)o->ptr);
363 }
364}
365
366int getDoubleFromObject(robj *o, double *target) {
367 double value;
368 char *eptr;
369
370 if (o == NULL) {
371 value = 0;
372 } else {
373 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
374 if (o->encoding == REDIS_ENCODING_RAW) {
375 errno = 0;
376 value = strtod(o->ptr, &eptr);
377 if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' ||
378 errno == ERANGE || isnan(value))
379 return REDIS_ERR;
380 } else if (o->encoding == REDIS_ENCODING_INT) {
381 value = (long)o->ptr;
382 } else {
383 redisPanic("Unknown string encoding");
384 }
385 }
386 *target = value;
387 return REDIS_OK;
388}
389
390int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const char *msg) {
391 double value;
392 if (getDoubleFromObject(o, &value) != REDIS_OK) {
393 if (msg != NULL) {
394 addReplyError(c,(char*)msg);
395 } else {
396 addReplyError(c,"value is not a valid float");
397 }
398 return REDIS_ERR;
399 }
400 *target = value;
401 return REDIS_OK;
402}
403
404int getLongDoubleFromObject(robj *o, long double *target) {
405 long double value;
406 char *eptr;
407
408 if (o == NULL) {
409 value = 0;
410 } else {
411 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
412 if (o->encoding == REDIS_ENCODING_RAW) {
413 errno = 0;
414 value = strtold(o->ptr, &eptr);
415 if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' ||
416 errno == ERANGE || isnan(value))
417 return REDIS_ERR;
418 } else if (o->encoding == REDIS_ENCODING_INT) {
419 value = (long)o->ptr;
420 } else {
421 redisPanic("Unknown string encoding");
422 }
423 }
424 *target = value;
425 return REDIS_OK;
426}
427
428int getLongDoubleFromObjectOrReply(redisClient *c, robj *o, long double *target, const char *msg) {
429 long double value;
430 if (getLongDoubleFromObject(o, &value) != REDIS_OK) {
431 if (msg != NULL) {
432 addReplyError(c,(char*)msg);
433 } else {
434 addReplyError(c,"value is not a valid float");
435 }
436 return REDIS_ERR;
437 }
438 *target = value;
439 return REDIS_OK;
440}
441
442int getLongLongFromObject(robj *o, long long *target) {
443 long long value;
444 char *eptr;
445
446 if (o == NULL) {
447 value = 0;
448 } else {
449 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
450 if (o->encoding == REDIS_ENCODING_RAW) {
451 errno = 0;
452 value = strtoll(o->ptr, &eptr, 10);
453 if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' ||
454 errno == ERANGE)
455 return REDIS_ERR;
456 } else if (o->encoding == REDIS_ENCODING_INT) {
457 value = (long)o->ptr;
458 } else {
459 redisPanic("Unknown string encoding");
460 }
461 }
462 if (target) *target = value;
463 return REDIS_OK;
464}
465
466int getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, const char *msg) {
467 long long value;
468 if (getLongLongFromObject(o, &value) != REDIS_OK) {
469 if (msg != NULL) {
470 addReplyError(c,(char*)msg);
471 } else {
472 addReplyError(c,"value is not an integer or out of range");
473 }
474 return REDIS_ERR;
475 }
476 *target = value;
477 return REDIS_OK;
478}
479
480int getLongFromObjectOrReply(redisClient *c, robj *o, long *target, const char *msg) {
481 long long value;
482
483 if (getLongLongFromObjectOrReply(c, o, &value, msg) != REDIS_OK) return REDIS_ERR;
484 if (value < LONG_MIN || value > LONG_MAX) {
485 if (msg != NULL) {
486 addReplyError(c,(char*)msg);
487 } else {
488 addReplyError(c,"value is out of range");
489 }
490 return REDIS_ERR;
491 }
492 *target = value;
493 return REDIS_OK;
494}
495
496char *strEncoding(int encoding) {
497 switch(encoding) {
498 case REDIS_ENCODING_RAW: return "raw";
499 case REDIS_ENCODING_INT: return "int";
500 case REDIS_ENCODING_HT: return "hashtable";
501 case REDIS_ENCODING_LINKEDLIST: return "linkedlist";
502 case REDIS_ENCODING_ZIPLIST: return "ziplist";
503 case REDIS_ENCODING_INTSET: return "intset";
504 case REDIS_ENCODING_SKIPLIST: return "skiplist";
505 default: return "unknown";
506 }
507}
508
509/* Given an object returns the min number of seconds the object was never
510 * requested, using an approximated LRU algorithm. */
511unsigned long estimateObjectIdleTime(robj *o) {
512 if (server.lruclock >= o->lru) {
513 return (server.lruclock - o->lru) * REDIS_LRU_CLOCK_RESOLUTION;
514 } else {
515 return ((REDIS_LRU_CLOCK_MAX - o->lru) + server.lruclock) *
516 REDIS_LRU_CLOCK_RESOLUTION;
517 }
518}
519
520/* This is an helper function for the DEBUG command. We need to lookup keys
521 * without any modification of LRU or other parameters. */
522robj *objectCommandLookup(redisClient *c, robj *key) {
523 dictEntry *de;
524
525 if ((de = dictFind(c->db->dict,key->ptr)) == NULL) return NULL;
526 return (robj*) dictGetVal(de);
527}
528
529robj *objectCommandLookupOrReply(redisClient *c, robj *key, robj *reply) {
530 robj *o = objectCommandLookup(c,key);
531
532 if (!o) addReply(c, reply);
533 return o;
534}
535
536/* Object command allows to inspect the internals of an Redis Object.
537 * Usage: OBJECT <verb> ... arguments ... */
538void objectCommand(redisClient *c) {
539 robj *o;
540
541 if (!strcasecmp(c->argv[1]->ptr,"refcount") && c->argc == 3) {
542 if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
543 == NULL) return;
544 addReplyLongLong(c,o->refcount);
545 } else if (!strcasecmp(c->argv[1]->ptr,"encoding") && c->argc == 3) {
546 if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
547 == NULL) return;
548 addReplyBulkCString(c,strEncoding(o->encoding));
549 } else if (!strcasecmp(c->argv[1]->ptr,"idletime") && c->argc == 3) {
550 if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
551 == NULL) return;
552 addReplyLongLong(c,estimateObjectIdleTime(o));
553 } else {
554 addReplyError(c,"Syntax error. Try OBJECT (refcount|encoding|idletime)");
555 }
556}
557