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),"%.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 72 robj *dupStringObject(robj *o) { 73 redisAssertWithInfo(NULL,o,o->encoding == REDIS_ENCODING_RAW); 74 return createStringObject(o->ptr,sdslen(o->ptr)); 75 } 76 77 robj *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 85 robj *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 92 robj *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 99 robj *createIntsetObject(void) { 100 intset *is = intsetNew(); 101 robj *o = createObject(REDIS_SET,is); 102 o->encoding = REDIS_ENCODING_INTSET; 103 return o; 104 } 105 106 robj *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 113 robj *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 124 robj *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 131 void freeStringObject(robj *o) { 132 if (o->encoding == REDIS_ENCODING_RAW) { 133 sdsfree(o->ptr); 134 } 135 } 136 137 void 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 150 void 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 163 void 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 180 void 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 194 void incrRefCount(robj *o) { 195 o->refcount++; 196 } 197 198 void 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 */ 229 robj *resetRefCount(robj *obj) { 230 obj->refcount = 0; 231 return obj; 232 } 233 234 int 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 242 int 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 */ 253 robj *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. */ 292 robj *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. */ 318 int 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. */ 345 int 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 353 size_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 364 int 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 388 int 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 402 int 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 426 int 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 440 int 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 464 int 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 478 int 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 494 char *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. */ 509 unsigned 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. */ 520 robj *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 527 robj *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 ... */ 536 void 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 556