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 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. */ 294 robj *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. */ 320 int 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. */ 347 int 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 355 size_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 366 int 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 390 int 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 404 int 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 428 int 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 442 int 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 466 int 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 480 int 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 496 char *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. */ 511 unsigned 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. */ 522 robj *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 529 robj *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 ... */ 538 void 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 558