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