1 /* Redis Object implementation. 2 * 3 * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * * Redistributions of source code must retain the above copyright notice, 10 * this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * * Neither the name of Redis nor the names of its contributors may be used 15 * to endorse or promote products derived from this software without 16 * specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "redis.h" 32 #include <math.h> 33 #include <ctype.h> 34 35 robj *createObject(int type, void *ptr) { 36 robj *o = zmalloc(sizeof(*o)); 37 o->type = type; 38 o->encoding = REDIS_ENCODING_RAW; 39 o->ptr = ptr; 40 o->refcount = 1; 41 42 /* Set the LRU to the current lruclock (minutes resolution). */ 43 o->lru = server.lruclock; 44 return o; 45 } 46 47 robj *createStringObject(char *ptr, size_t len) { 48 return createObject(REDIS_STRING,sdsnewlen(ptr,len)); 49 } 50 51 robj *createStringObjectFromLongLong(long long value) { 52 robj *o; 53 if (value >= 0 && value < REDIS_SHARED_INTEGERS) { 54 incrRefCount(shared.integers[value]); 55 o = shared.integers[value]; 56 } else { 57 if (value >= LONG_MIN && value <= LONG_MAX) { 58 o = createObject(REDIS_STRING, NULL); 59 o->encoding = REDIS_ENCODING_INT; 60 o->ptr = (void*)((long)value); 61 } else { 62 o = createObject(REDIS_STRING,sdsfromlonglong(value)); 63 } 64 } 65 return o; 66 } 67 68 /* Note: this function is defined into object.c since here it is where it 69 * belongs but it is actually designed to be used just for INCRBYFLOAT */ 70 robj *createStringObjectFromLongDouble(long double value) { 71 char buf[256]; 72 int len; 73 74 /* We use 17 digits precision since with 128 bit floats that precision 75 * after rounding is able to represent most small decimal numbers in a way 76 * that is "non surprising" for the user (that is, most small decimal 77 * numbers will be represented in a way that when converted back into 78 * a string are exactly the same as what the user typed.) */ 79 len = snprintf(buf,sizeof(buf),"%.17Lf", value); 80 /* Now remove trailing zeroes after the '.' */ 81 if (strchr(buf,'.') != NULL) { 82 char *p = buf+len-1; 83 while(*p == '0') { 84 p--; 85 len--; 86 } 87 if (*p == '.') len--; 88 } 89 return createStringObject(buf,len); 90 } 91 92 robj *dupStringObject(robj *o) { 93 redisAssertWithInfo(NULL,o,o->encoding == REDIS_ENCODING_RAW); 94 return createStringObject(o->ptr,sdslen(o->ptr)); 95 } 96 97 robj *createListObject(void) { 98 list *l = listCreate(); 99 robj *o = createObject(REDIS_LIST,l); 100 listSetFreeMethod(l,decrRefCountVoid); 101 o->encoding = REDIS_ENCODING_LINKEDLIST; 102 return o; 103 } 104 105 robj *createZiplistObject(void) { 106 unsigned char *zl = ziplistNew(); 107 robj *o = createObject(REDIS_LIST,zl); 108 o->encoding = REDIS_ENCODING_ZIPLIST; 109 return o; 110 } 111 112 robj *createSetObject(void) { 113 dict *d = dictCreate(&setDictType,NULL); 114 robj *o = createObject(REDIS_SET,d); 115 o->encoding = REDIS_ENCODING_HT; 116 return o; 117 } 118 119 robj *createIntsetObject(void) { 120 intset *is = intsetNew(); 121 robj *o = createObject(REDIS_SET,is); 122 o->encoding = REDIS_ENCODING_INTSET; 123 return o; 124 } 125 126 robj *createHashObject(void) { 127 unsigned char *zl = ziplistNew(); 128 robj *o = createObject(REDIS_HASH, zl); 129 o->encoding = REDIS_ENCODING_ZIPLIST; 130 return o; 131 } 132 133 robj *createZsetObject(void) { 134 zset *zs = zmalloc(sizeof(*zs)); 135 robj *o; 136 137 zs->dict = dictCreate(&zsetDictType,NULL); 138 zs->zsl = zslCreate(); 139 o = createObject(REDIS_ZSET,zs); 140 o->encoding = REDIS_ENCODING_SKIPLIST; 141 return o; 142 } 143 144 robj *createZsetZiplistObject(void) { 145 unsigned char *zl = ziplistNew(); 146 robj *o = createObject(REDIS_ZSET,zl); 147 o->encoding = REDIS_ENCODING_ZIPLIST; 148 return o; 149 } 150 151 void freeStringObject(robj *o) { 152 if (o->encoding == REDIS_ENCODING_RAW) { 153 sdsfree(o->ptr); 154 } 155 } 156 157 void freeListObject(robj *o) { 158 switch (o->encoding) { 159 case REDIS_ENCODING_LINKEDLIST: 160 listRelease((list*) o->ptr); 161 break; 162 case REDIS_ENCODING_ZIPLIST: 163 zfree(o->ptr); 164 break; 165 default: 166 redisPanic("Unknown list encoding type"); 167 } 168 } 169 170 void freeSetObject(robj *o) { 171 switch (o->encoding) { 172 case REDIS_ENCODING_HT: 173 dictRelease((dict*) o->ptr); 174 break; 175 case REDIS_ENCODING_INTSET: 176 zfree(o->ptr); 177 break; 178 default: 179 redisPanic("Unknown set encoding type"); 180 } 181 } 182 183 void freeZsetObject(robj *o) { 184 zset *zs; 185 switch (o->encoding) { 186 case REDIS_ENCODING_SKIPLIST: 187 zs = o->ptr; 188 dictRelease(zs->dict); 189 zslFree(zs->zsl); 190 zfree(zs); 191 break; 192 case REDIS_ENCODING_ZIPLIST: 193 zfree(o->ptr); 194 break; 195 default: 196 redisPanic("Unknown sorted set encoding"); 197 } 198 } 199 200 void freeHashObject(robj *o) { 201 switch (o->encoding) { 202 case REDIS_ENCODING_HT: 203 dictRelease((dict*) o->ptr); 204 break; 205 case REDIS_ENCODING_ZIPLIST: 206 zfree(o->ptr); 207 break; 208 default: 209 redisPanic("Unknown hash encoding type"); 210 break; 211 } 212 } 213 214 void incrRefCount(robj *o) { 215 o->refcount++; 216 } 217 218 void decrRefCount(robj *o) { 219 if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0"); 220 if (o->refcount == 1) { 221 switch(o->type) { 222 case REDIS_STRING: freeStringObject(o); break; 223 case REDIS_LIST: freeListObject(o); break; 224 case REDIS_SET: freeSetObject(o); break; 225 case REDIS_ZSET: freeZsetObject(o); break; 226 case REDIS_HASH: freeHashObject(o); break; 227 default: redisPanic("Unknown object type"); break; 228 } 229 zfree(o); 230 } else { 231 o->refcount--; 232 } 233 } 234 235 /* This variant of decrRefCount() gets its argument as void, and is useful 236 * as free method in data structures that expect a 'void free_object(void*)' 237 * prototype for the free method. */ 238 void decrRefCountVoid(void *o) { 239 decrRefCount(o); 240 } 241 242 /* This function set the ref count to zero without freeing the object. 243 * It is useful in order to pass a new object to functions incrementing 244 * the ref count of the received object. Example: 245 * 246 * functionThatWillIncrementRefCount(resetRefCount(CreateObject(...))); 247 * 248 * Otherwise you need to resort to the less elegant pattern: 249 * 250 * *obj = createObject(...); 251 * functionThatWillIncrementRefCount(obj); 252 * decrRefCount(obj); 253 */ 254 robj *resetRefCount(robj *obj) { 255 obj->refcount = 0; 256 return obj; 257 } 258 259 int checkType(redisClient *c, robj *o, int type) { 260 if (o->type != type) { 261 addReply(c,shared.wrongtypeerr); 262 return 1; 263 } 264 return 0; 265 } 266 267 int isObjectRepresentableAsLongLong(robj *o, long long *llval) { 268 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); 269 if (o->encoding == REDIS_ENCODING_INT) { 270 if (llval) *llval = (long) o->ptr; 271 return REDIS_OK; 272 } else { 273 return string2ll(o->ptr,sdslen(o->ptr),llval) ? REDIS_OK : REDIS_ERR; 274 } 275 } 276 277 /* Try to encode a string object in order to save space */ 278 robj *tryObjectEncoding(robj *o) { 279 long value; 280 sds s = o->ptr; 281 282 if (o->encoding != REDIS_ENCODING_RAW) 283 return o; /* Already encoded */ 284 285 /* It's not safe to encode shared objects: shared objects can be shared 286 * everywhere in the "object space" of Redis. Encoded objects can only 287 * appear as "values" (and not, for instance, as keys) */ 288 if (o->refcount > 1) return o; 289 290 /* Currently we try to encode only strings */ 291 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); 292 293 /* Check if we can represent this string as a long integer */ 294 if (!string2l(s,sdslen(s),&value)) return o; 295 296 /* Ok, this object can be encoded... 297 * 298 * Can I use a shared object? Only if the object is inside a given range 299 * 300 * Note that we also avoid using shared integers when maxmemory is used 301 * because every object needs to have a private LRU field for the LRU 302 * algorithm to work well. */ 303 if (server.maxmemory == 0 && value >= 0 && value < REDIS_SHARED_INTEGERS) { 304 decrRefCount(o); 305 incrRefCount(shared.integers[value]); 306 return shared.integers[value]; 307 } else { 308 o->encoding = REDIS_ENCODING_INT; 309 sdsfree(o->ptr); 310 o->ptr = (void*) value; 311 return o; 312 } 313 } 314 315 /* Get a decoded version of an encoded object (returned as a new object). 316 * If the object is already raw-encoded just increment the ref count. */ 317 robj *getDecodedObject(robj *o) { 318 robj *dec; 319 320 if (o->encoding == REDIS_ENCODING_RAW) { 321 incrRefCount(o); 322 return o; 323 } 324 if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_INT) { 325 char buf[32]; 326 327 ll2string(buf,32,(long)o->ptr); 328 dec = createStringObject(buf,strlen(buf)); 329 return dec; 330 } else { 331 redisPanic("Unknown encoding type"); 332 } 333 } 334 335 /* Compare two string objects via strcmp() or alike. 336 * Note that the objects may be integer-encoded. In such a case we 337 * use ll2string() to get a string representation of the numbers on the stack 338 * and compare the strings, it's much faster than calling getDecodedObject(). 339 * 340 * Important note: if objects are not integer encoded, but binary-safe strings, 341 * sdscmp() from sds.c will apply memcmp() so this function ca be considered 342 * binary safe. */ 343 int compareStringObjects(robj *a, robj *b) { 344 redisAssertWithInfo(NULL,a,a->type == REDIS_STRING && b->type == REDIS_STRING); 345 char bufa[128], bufb[128], *astr, *bstr; 346 int bothsds = 1; 347 348 if (a == b) return 0; 349 if (a->encoding != REDIS_ENCODING_RAW) { 350 ll2string(bufa,sizeof(bufa),(long) a->ptr); 351 astr = bufa; 352 bothsds = 0; 353 } else { 354 astr = a->ptr; 355 } 356 if (b->encoding != REDIS_ENCODING_RAW) { 357 ll2string(bufb,sizeof(bufb),(long) b->ptr); 358 bstr = bufb; 359 bothsds = 0; 360 } else { 361 bstr = b->ptr; 362 } 363 return bothsds ? sdscmp(astr,bstr) : strcmp(astr,bstr); 364 } 365 366 /* Equal string objects return 1 if the two objects are the same from the 367 * point of view of a string comparison, otherwise 0 is returned. Note that 368 * this function is faster then checking for (compareStringObject(a,b) == 0) 369 * because it can perform some more optimization. */ 370 int equalStringObjects(robj *a, robj *b) { 371 if (a->encoding != REDIS_ENCODING_RAW && b->encoding != REDIS_ENCODING_RAW){ 372 return a->ptr == b->ptr; 373 } else { 374 return compareStringObjects(a,b) == 0; 375 } 376 } 377 378 size_t stringObjectLen(robj *o) { 379 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); 380 if (o->encoding == REDIS_ENCODING_RAW) { 381 return sdslen(o->ptr); 382 } else { 383 char buf[32]; 384 385 return ll2string(buf,32,(long)o->ptr); 386 } 387 } 388 389 int getDoubleFromObject(robj *o, double *target) { 390 double value; 391 char *eptr; 392 393 if (o == NULL) { 394 value = 0; 395 } else { 396 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); 397 if (o->encoding == REDIS_ENCODING_RAW) { 398 errno = 0; 399 value = strtod(o->ptr, &eptr); 400 if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' || 401 errno == ERANGE || isnan(value)) 402 return REDIS_ERR; 403 } else if (o->encoding == REDIS_ENCODING_INT) { 404 value = (long)o->ptr; 405 } else { 406 redisPanic("Unknown string encoding"); 407 } 408 } 409 *target = value; 410 return REDIS_OK; 411 } 412 413 int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const char *msg) { 414 double value; 415 if (getDoubleFromObject(o, &value) != REDIS_OK) { 416 if (msg != NULL) { 417 addReplyError(c,(char*)msg); 418 } else { 419 addReplyError(c,"value is not a valid float"); 420 } 421 return REDIS_ERR; 422 } 423 *target = value; 424 return REDIS_OK; 425 } 426 427 int getLongDoubleFromObject(robj *o, long double *target) { 428 long double value; 429 char *eptr; 430 431 if (o == NULL) { 432 value = 0; 433 } else { 434 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); 435 if (o->encoding == REDIS_ENCODING_RAW) { 436 errno = 0; 437 value = strtold(o->ptr, &eptr); 438 if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' || 439 errno == ERANGE || isnan(value)) 440 return REDIS_ERR; 441 } else if (o->encoding == REDIS_ENCODING_INT) { 442 value = (long)o->ptr; 443 } else { 444 redisPanic("Unknown string encoding"); 445 } 446 } 447 *target = value; 448 return REDIS_OK; 449 } 450 451 int getLongDoubleFromObjectOrReply(redisClient *c, robj *o, long double *target, const char *msg) { 452 long double value; 453 if (getLongDoubleFromObject(o, &value) != REDIS_OK) { 454 if (msg != NULL) { 455 addReplyError(c,(char*)msg); 456 } else { 457 addReplyError(c,"value is not a valid float"); 458 } 459 return REDIS_ERR; 460 } 461 *target = value; 462 return REDIS_OK; 463 } 464 465 int getLongLongFromObject(robj *o, long long *target) { 466 long long value; 467 char *eptr; 468 469 if (o == NULL) { 470 value = 0; 471 } else { 472 redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); 473 if (o->encoding == REDIS_ENCODING_RAW) { 474 errno = 0; 475 value = strtoll(o->ptr, &eptr, 10); 476 if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' || 477 errno == ERANGE) 478 return REDIS_ERR; 479 } else if (o->encoding == REDIS_ENCODING_INT) { 480 value = (long)o->ptr; 481 } else { 482 redisPanic("Unknown string encoding"); 483 } 484 } 485 if (target) *target = value; 486 return REDIS_OK; 487 } 488 489 int getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, const char *msg) { 490 long long value; 491 if (getLongLongFromObject(o, &value) != REDIS_OK) { 492 if (msg != NULL) { 493 addReplyError(c,(char*)msg); 494 } else { 495 addReplyError(c,"value is not an integer or out of range"); 496 } 497 return REDIS_ERR; 498 } 499 *target = value; 500 return REDIS_OK; 501 } 502 503 int getLongFromObjectOrReply(redisClient *c, robj *o, long *target, const char *msg) { 504 long long value; 505 506 if (getLongLongFromObjectOrReply(c, o, &value, msg) != REDIS_OK) return REDIS_ERR; 507 if (value < LONG_MIN || value > LONG_MAX) { 508 if (msg != NULL) { 509 addReplyError(c,(char*)msg); 510 } else { 511 addReplyError(c,"value is out of range"); 512 } 513 return REDIS_ERR; 514 } 515 *target = value; 516 return REDIS_OK; 517 } 518 519 char *strEncoding(int encoding) { 520 switch(encoding) { 521 case REDIS_ENCODING_RAW: return "raw"; 522 case REDIS_ENCODING_INT: return "int"; 523 case REDIS_ENCODING_HT: return "hashtable"; 524 case REDIS_ENCODING_LINKEDLIST: return "linkedlist"; 525 case REDIS_ENCODING_ZIPLIST: return "ziplist"; 526 case REDIS_ENCODING_INTSET: return "intset"; 527 case REDIS_ENCODING_SKIPLIST: return "skiplist"; 528 default: return "unknown"; 529 } 530 } 531 532 /* Given an object returns the min number of seconds the object was never 533 * requested, using an approximated LRU algorithm. */ 534 unsigned long estimateObjectIdleTime(robj *o) { 535 if (server.lruclock >= o->lru) { 536 return (server.lruclock - o->lru) * REDIS_LRU_CLOCK_RESOLUTION; 537 } else { 538 return ((REDIS_LRU_CLOCK_MAX - o->lru) + server.lruclock) * 539 REDIS_LRU_CLOCK_RESOLUTION; 540 } 541 } 542 543 /* This is an helper function for the DEBUG command. We need to lookup keys 544 * without any modification of LRU or other parameters. */ 545 robj *objectCommandLookup(redisClient *c, robj *key) { 546 dictEntry *de; 547 548 if ((de = dictFind(c->db->dict,key->ptr)) == NULL) return NULL; 549 return (robj*) dictGetVal(de); 550 } 551 552 robj *objectCommandLookupOrReply(redisClient *c, robj *key, robj *reply) { 553 robj *o = objectCommandLookup(c,key); 554 555 if (!o) addReply(c, reply); 556 return o; 557 } 558 559 /* Object command allows to inspect the internals of an Redis Object. 560 * Usage: OBJECT <verb> ... arguments ... */ 561 void objectCommand(redisClient *c) { 562 robj *o; 563 564 if (!strcasecmp(c->argv[1]->ptr,"refcount") && c->argc == 3) { 565 if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk)) 566 == NULL) return; 567 addReplyLongLong(c,o->refcount); 568 } else if (!strcasecmp(c->argv[1]->ptr,"encoding") && c->argc == 3) { 569 if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk)) 570 == NULL) return; 571 addReplyBulkCString(c,strEncoding(o->encoding)); 572 } else if (!strcasecmp(c->argv[1]->ptr,"idletime") && c->argc == 3) { 573 if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk)) 574 == NULL) return; 575 addReplyLongLong(c,estimateObjectIdleTime(o)); 576 } else { 577 addReplyError(c,"Syntax error. Try OBJECT (refcount|encoding|idletime)"); 578 } 579 } 580 581