1 : #include "redis.h"
2 : #include <math.h>
3 : #include <ctype.h>
4 :
5 11598102 : robj *createObject(int type, void *ptr) {
6 11598102 : robj *o = zmalloc(sizeof(*o));
7 11598102 : o->type = type;
8 11598102 : o->encoding = REDIS_ENCODING_RAW;
9 11598102 : o->ptr = ptr;
10 11598102 : 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 11598102 : 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 11598102 : return o;
25 : }
26 :
27 8250188 : robj *createStringObject(char *ptr, size_t len) {
28 8250188 : return createObject(REDIS_STRING,sdsnewlen(ptr,len));
29 : }
30 :
31 176565 : robj *createStringObjectFromLongLong(long long value) {
32 : robj *o;
33 176565 : if (value >= 0 && value < REDIS_SHARED_INTEGERS) {
34 60257 : incrRefCount(shared.integers[value]);
35 60257 : o = shared.integers[value];
36 : } else {
37 : if (value >= LONG_MIN && value <= LONG_MAX) {
38 116308 : o = createObject(REDIS_STRING, NULL);
39 116308 : o->encoding = REDIS_ENCODING_INT;
40 116308 : o->ptr = (void*)((long)value);
41 : } else {
42 : o = createObject(REDIS_STRING,sdsfromlonglong(value));
43 : }
44 : }
45 176565 : 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 17 : 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 34 : len = snprintf(buf,sizeof(buf),"%.17Lf", value);
60 : /* Now remove trailing zeroes after the '.' */
61 17 : if (strchr(buf,'.') != NULL) {
62 17 : char *p = buf+len-1;
63 314 : while(*p == '0') {
64 280 : p--;
65 280 : len--;
66 : }
67 17 : if (*p == '.') len--;
68 : }
69 17 : return createStringObject(buf,len);
70 : }
71 :
72 2592 : robj *dupStringObject(robj *o) {
73 2592 : redisAssertWithInfo(NULL,o,o->encoding == REDIS_ENCODING_RAW);
74 5184 : return createStringObject(o->ptr,sdslen(o->ptr));
75 : }
76 :
77 4 : robj *createListObject(void) {
78 4 : list *l = listCreate();
79 4 : robj *o = createObject(REDIS_LIST,l);
80 4 : listSetFreeMethod(l,decrRefCount);
81 4 : o->encoding = REDIS_ENCODING_LINKEDLIST;
82 4 : return o;
83 : }
84 :
85 15250 : robj *createZiplistObject(void) {
86 15250 : unsigned char *zl = ziplistNew();
87 15250 : robj *o = createObject(REDIS_LIST,zl);
88 15250 : o->encoding = REDIS_ENCODING_ZIPLIST;
89 15250 : return o;
90 : }
91 :
92 3607 : robj *createSetObject(void) {
93 3607 : dict *d = dictCreate(&setDictType,NULL);
94 3607 : robj *o = createObject(REDIS_SET,d);
95 3607 : o->encoding = REDIS_ENCODING_HT;
96 3607 : return o;
97 : }
98 :
99 17208 : robj *createIntsetObject(void) {
100 17208 : intset *is = intsetNew();
101 17208 : robj *o = createObject(REDIS_SET,is);
102 17208 : o->encoding = REDIS_ENCODING_INTSET;
103 17208 : return o;
104 : }
105 :
106 25060 : robj *createHashObject(void) {
107 25060 : unsigned char *zl = ziplistNew();
108 25060 : robj *o = createObject(REDIS_HASH, zl);
109 25060 : o->encoding = REDIS_ENCODING_ZIPLIST;
110 25060 : return o;
111 : }
112 :
113 7540 : robj *createZsetObject(void) {
114 7540 : zset *zs = zmalloc(sizeof(*zs));
115 : robj *o;
116 :
117 7540 : zs->dict = dictCreate(&zsetDictType,NULL);
118 7540 : zs->zsl = zslCreate();
119 7540 : o = createObject(REDIS_ZSET,zs);
120 7540 : o->encoding = REDIS_ENCODING_SKIPLIST;
121 7540 : return o;
122 : }
123 :
124 12010 : robj *createZsetZiplistObject(void) {
125 12010 : unsigned char *zl = ziplistNew();
126 12010 : robj *o = createObject(REDIS_ZSET,zl);
127 12010 : o->encoding = REDIS_ENCODING_ZIPLIST;
128 12010 : return o;
129 : }
130 :
131 9283494 : void freeStringObject(robj *o) {
132 9283494 : if (o->encoding == REDIS_ENCODING_RAW) {
133 9054980 : sdsfree(o->ptr);
134 : }
135 9283494 : }
136 :
137 9689 : void freeListObject(robj *o) {
138 9689 : switch (o->encoding) {
139 : case REDIS_ENCODING_LINKEDLIST:
140 1852 : listRelease((list*) o->ptr);
141 1852 : break;
142 : case REDIS_ENCODING_ZIPLIST:
143 7837 : zfree(o->ptr);
144 7837 : break;
145 : default:
146 0 : redisPanic("Unknown list encoding type");
147 : }
148 9689 : }
149 :
150 7539 : void freeSetObject(robj *o) {
151 7539 : switch (o->encoding) {
152 : case REDIS_ENCODING_HT:
153 1473 : dictRelease((dict*) o->ptr);
154 1473 : break;
155 : case REDIS_ENCODING_INTSET:
156 6066 : zfree(o->ptr);
157 6066 : break;
158 : default:
159 0 : redisPanic("Unknown set encoding type");
160 : }
161 7539 : }
162 :
163 7456 : void freeZsetObject(robj *o) {
164 : zset *zs;
165 7456 : switch (o->encoding) {
166 : case REDIS_ENCODING_SKIPLIST:
167 2895 : zs = o->ptr;
168 2895 : dictRelease(zs->dict);
169 2895 : zslFree(zs->zsl);
170 2895 : zfree(zs);
171 2895 : break;
172 : case REDIS_ENCODING_ZIPLIST:
173 4561 : zfree(o->ptr);
174 4561 : break;
175 : default:
176 0 : redisPanic("Unknown sorted set encoding");
177 : }
178 7456 : }
179 :
180 17970 : void freeHashObject(robj *o) {
181 17970 : switch (o->encoding) {
182 : case REDIS_ENCODING_HT:
183 4 : dictRelease((dict*) o->ptr);
184 4 : break;
185 : case REDIS_ENCODING_ZIPLIST:
186 17966 : zfree(o->ptr);
187 17966 : break;
188 : default:
189 0 : redisPanic("Unknown hash encoding type");
190 : break;
191 : }
192 17970 : }
193 :
194 1229370 : void incrRefCount(robj *o) {
195 4954568 : o->refcount++;
196 1229370 : }
197 :
198 14263814 : void decrRefCount(void *obj) {
199 14263814 : robj *o = obj;
200 :
201 14263814 : if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0");
202 14263814 : if (o->refcount == 1) {
203 9326148 : switch(o->type) {
204 9283494 : case REDIS_STRING: freeStringObject(o); break;
205 9689 : case REDIS_LIST: freeListObject(o); break;
206 7539 : case REDIS_SET: freeSetObject(o); break;
207 7456 : case REDIS_ZSET: freeZsetObject(o); break;
208 17970 : case REDIS_HASH: freeHashObject(o); break;
209 0 : default: redisPanic("Unknown object type"); break;
210 : }
211 9326148 : zfree(o);
212 : } else {
213 4937666 : o->refcount--;
214 : }
215 14263814 : }
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 : */
229 23 : robj *resetRefCount(robj *obj) {
230 23 : obj->refcount = 0;
231 23 : return obj;
232 : }
233 :
234 176272 : int checkType(redisClient *c, robj *o, int type) {
235 176272 : if (o->type != type) {
236 18 : addReply(c,shared.wrongtypeerr);
237 18 : return 1;
238 : }
239 176254 : return 0;
240 : }
241 :
242 54746 : int isObjectRepresentableAsLongLong(robj *o, long long *llval) {
243 54746 : redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
244 54746 : if (o->encoding == REDIS_ENCODING_INT) {
245 33512 : if (llval) *llval = (long) o->ptr;
246 33512 : return REDIS_OK;
247 : } else {
248 42468 : 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 */
253 2073561 : robj *tryObjectEncoding(robj *o) {
254 : long value;
255 2073561 : sds s = o->ptr;
256 :
257 2073561 : if (o->encoding != REDIS_ENCODING_RAW)
258 5786 : 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 2067775 : if (o->refcount > 1) return o;
264 :
265 : /* Currently we try to encode only strings */
266 2067775 : redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
267 :
268 : /* Check if we can represent this string as a long integer */
269 2067775 : 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 209995 : if (server.maxmemory == 0 && value >= 0 && value < REDIS_SHARED_INTEGERS) {
279 87100 : decrRefCount(o);
280 87100 : incrRefCount(shared.integers[value]);
281 87100 : return shared.integers[value];
282 : } else {
283 122895 : o->encoding = REDIS_ENCODING_INT;
284 122895 : sdsfree(o->ptr);
285 122895 : o->ptr = (void*) value;
286 122895 : 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. */
292 3947271 : robj *getDecodedObject(robj *o) {
293 : robj *dec;
294 :
295 3947271 : if (o->encoding == REDIS_ENCODING_RAW) {
296 : incrRefCount(o);
297 3577841 : return o;
298 : }
299 369430 : if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_INT) {
300 : char buf[32];
301 :
302 369430 : ll2string(buf,32,(long)o->ptr);
303 369430 : dec = createStringObject(buf,strlen(buf));
304 369430 : return dec;
305 : } else {
306 0 : 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. */
318 9378 : int compareStringObjects(robj *a, robj *b) {
319 9378 : redisAssertWithInfo(NULL,a,a->type == REDIS_STRING && b->type == REDIS_STRING);
320 : char bufa[128], bufb[128], *astr, *bstr;
321 9378 : int bothsds = 1;
322 :
323 9378 : if (a == b) return 0;
324 4356 : if (a->encoding != REDIS_ENCODING_RAW) {
325 1372 : ll2string(bufa,sizeof(bufa),(long) a->ptr);
326 1372 : astr = bufa;
327 1372 : bothsds = 0;
328 : } else {
329 2984 : astr = a->ptr;
330 : }
331 4356 : if (b->encoding != REDIS_ENCODING_RAW) {
332 643 : ll2string(bufb,sizeof(bufb),(long) b->ptr);
333 643 : bstr = bufb;
334 643 : bothsds = 0;
335 : } else {
336 3713 : bstr = b->ptr;
337 : }
338 4356 : 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. */
345 14217 : int equalStringObjects(robj *a, robj *b) {
346 14217 : if (a->encoding != REDIS_ENCODING_RAW && b->encoding != REDIS_ENCODING_RAW){
347 12253 : return a->ptr == b->ptr;
348 : } else {
349 1964 : return compareStringObjects(a,b) == 0;
350 : }
351 : }
352 :
353 3016 : size_t stringObjectLen(robj *o) {
354 3016 : redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
355 3016 : if (o->encoding == REDIS_ENCODING_RAW) {
356 6012 : return sdslen(o->ptr);
357 : } else {
358 : char buf[32];
359 :
360 10 : return ll2string(buf,32,(long)o->ptr);
361 : }
362 : }
363 :
364 27231 : int getDoubleFromObject(robj *o, double *target) {
365 : double value;
366 : char *eptr;
367 :
368 27231 : if (o == NULL) {
369 0 : value = 0;
370 : } else {
371 27231 : redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
372 27231 : if (o->encoding == REDIS_ENCODING_RAW) {
373 27231 : errno = 0;
374 27231 : value = strtod(o->ptr, &eptr);
375 81689 : if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' ||
376 54458 : errno == ERANGE || isnan(value))
377 10 : return REDIS_ERR;
378 0 : } else if (o->encoding == REDIS_ENCODING_INT) {
379 0 : value = (long)o->ptr;
380 : } else {
381 0 : redisPanic("Unknown string encoding");
382 : }
383 : }
384 27221 : *target = value;
385 27221 : return REDIS_OK;
386 : }
387 :
388 27231 : int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const char *msg) {
389 : double value;
390 27231 : if (getDoubleFromObject(o, &value) != REDIS_OK) {
391 10 : if (msg != NULL) {
392 4 : addReplyError(c,(char*)msg);
393 : } else {
394 6 : addReplyError(c,"value is not a valid float");
395 : }
396 10 : return REDIS_ERR;
397 : }
398 27221 : *target = value;
399 27221 : return REDIS_OK;
400 : }
401 :
402 44 : int getLongDoubleFromObject(robj *o, long double *target) {
403 : long double value;
404 : char *eptr;
405 :
406 44 : if (o == NULL) {
407 1 : value = 0;
408 : } else {
409 43 : redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
410 43 : if (o->encoding == REDIS_ENCODING_RAW) {
411 33 : errno = 0;
412 33 : value = strtold(o->ptr, &eptr);
413 85 : if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' ||
414 52 : errno == ERANGE || isnan(value))
415 7 : return REDIS_ERR;
416 10 : } else if (o->encoding == REDIS_ENCODING_INT) {
417 10 : value = (long)o->ptr;
418 : } else {
419 0 : redisPanic("Unknown string encoding");
420 : }
421 : }
422 37 : *target = value;
423 37 : return REDIS_OK;
424 : }
425 :
426 44 : int getLongDoubleFromObjectOrReply(redisClient *c, robj *o, long double *target, const char *msg) {
427 : long double value;
428 44 : if (getLongDoubleFromObject(o, &value) != REDIS_OK) {
429 7 : if (msg != NULL) {
430 4 : addReplyError(c,(char*)msg);
431 : } else {
432 3 : addReplyError(c,"value is not a valid float");
433 : }
434 7 : return REDIS_ERR;
435 : }
436 37 : *target = value;
437 37 : return REDIS_OK;
438 : }
439 :
440 100701 : int getLongLongFromObject(robj *o, long long *target) {
441 : long long value;
442 : char *eptr;
443 :
444 100701 : if (o == NULL) {
445 3 : value = 0;
446 : } else {
447 100698 : redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
448 100698 : if (o->encoding == REDIS_ENCODING_RAW) {
449 100678 : errno = 0;
450 100678 : value = strtoll(o->ptr, &eptr, 10);
451 201346 : if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' ||
452 100668 : errno == ERANGE)
453 10 : return REDIS_ERR;
454 20 : } else if (o->encoding == REDIS_ENCODING_INT) {
455 20 : value = (long)o->ptr;
456 : } else {
457 0 : redisPanic("Unknown string encoding");
458 : }
459 : }
460 100691 : if (target) *target = value;
461 100691 : return REDIS_OK;
462 : }
463 :
464 100676 : int getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, const char *msg) {
465 : long long value;
466 100676 : if (getLongLongFromObject(o, &value) != REDIS_OK) {
467 10 : if (msg != NULL) {
468 6 : addReplyError(c,(char*)msg);
469 : } else {
470 4 : addReplyError(c,"value is not an integer or out of range");
471 : }
472 10 : return REDIS_ERR;
473 : }
474 100666 : *target = value;
475 100666 : return REDIS_OK;
476 : }
477 :
478 78733 : int getLongFromObjectOrReply(redisClient *c, robj *o, long *target, const char *msg) {
479 : long long value;
480 :
481 78733 : 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 78731 : *target = value;
491 78731 : return REDIS_OK;
492 : }
493 :
494 3414 : char *strEncoding(int encoding) {
495 3414 : switch(encoding) {
496 3 : case REDIS_ENCODING_RAW: return "raw";
497 7 : case REDIS_ENCODING_INT: return "int";
498 37 : case REDIS_ENCODING_HT: return "hashtable";
499 56 : case REDIS_ENCODING_LINKEDLIST: return "linkedlist";
500 1662 : case REDIS_ENCODING_ZIPLIST: return "ziplist";
501 27 : case REDIS_ENCODING_INTSET: return "intset";
502 1622 : case REDIS_ENCODING_SKIPLIST: return "skiplist";
503 0 : 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. */
509 10765 : unsigned long estimateObjectIdleTime(robj *o) {
510 10765 : if (server.lruclock >= o->lru) {
511 10765 : return (server.lruclock - o->lru) * REDIS_LRU_CLOCK_RESOLUTION;
512 : } else {
513 0 : 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. */
520 14 : robj *objectCommandLookup(redisClient *c, robj *key) {
521 : dictEntry *de;
522 :
523 14 : if ((de = dictFind(c->db->dict,key->ptr)) == NULL) return NULL;
524 14 : return (robj*) dictGetVal(de);
525 : }
526 :
527 14 : robj *objectCommandLookupOrReply(redisClient *c, robj *key, robj *reply) {
528 14 : robj *o = objectCommandLookup(c,key);
529 :
530 14 : if (!o) addReply(c, reply);
531 14 : return o;
532 : }
533 :
534 : /* Object command allows to inspect the internals of an Redis Object.
535 : * Usage: OBJECT <verb> ... arguments ... */
536 14 : void objectCommand(redisClient *c) {
537 : robj *o;
538 :
539 14 : if (!strcasecmp(c->argv[1]->ptr,"refcount") && c->argc == 3) {
540 0 : if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
541 : == NULL) return;
542 0 : addReplyLongLong(c,o->refcount);
543 28 : } else if (!strcasecmp(c->argv[1]->ptr,"encoding") && c->argc == 3) {
544 14 : if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
545 : == NULL) return;
546 14 : addReplyBulkCString(c,strEncoding(o->encoding));
547 0 : } else if (!strcasecmp(c->argv[1]->ptr,"idletime") && c->argc == 3) {
548 0 : if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
549 : == NULL) return;
550 0 : addReplyLongLong(c,estimateObjectIdleTime(o));
551 : } else {
552 0 : addReplyError(c,"Syntax error. Try OBJECT (refcount|encoding|idletime)");
553 : }
554 : }
555 :
|