1 #include "redis.h" 2 #include <math.h> 3 4 robj *createObject(int type, void *ptr) { 5 robj *o = zmalloc(sizeof(*o)); 6 o->type = type; 7 o->encoding = REDIS_ENCODING_RAW; 8 o->ptr = ptr; 9 o->refcount = 1; 10 11 /* Set the LRU to the current lruclock (minutes resolution). 12 * We do this regardless of the fact VM is active as LRU is also 13 * used for the maxmemory directive when Redis is used as cache. 14 * 15 * Note that this code may run in the context of an I/O thread 16 * and accessing server.lruclock in theory is an error 17 * (no locks). But in practice this is safe, and even if we read 18 * garbage Redis will not fail. */ 19 o->lru = server.lruclock; 20 /* The following is only needed if VM is active, but since the conditional 21 * is probably more costly than initializing the field it's better to 22 * have every field properly initialized anyway. */ 23 return o; 24 } 25 26 robj *createStringObject(char *ptr, size_t len) { 27 return createObject(REDIS_STRING,sdsnewlen(ptr,len)); 28 } 29 30 robj *createStringObjectFromLongLong(long long value) { 31 robj *o; 32 if (value >= 0 && value < REDIS_SHARED_INTEGERS) { 33 incrRefCount(shared.integers[value]); 34 o = shared.integers[value]; 35 } else { 36 if (value >= LONG_MIN && value <= LONG_MAX) { 37 o = createObject(REDIS_STRING, NULL); 38 o->encoding = REDIS_ENCODING_INT; 39 o->ptr = (void*)((long)value); 40 } else { 41 o = createObject(REDIS_STRING,sdsfromlonglong(value)); 42 } 43 } 44 return o; 45 } 46 47 robj *dupStringObject(robj *o) { 48 redisAssert(o->encoding == REDIS_ENCODING_RAW); 49 return createStringObject(o->ptr,sdslen(o->ptr)); 50 } 51 52 robj *createListObject(void) { 53 list *l = listCreate(); 54 robj *o = createObject(REDIS_LIST,l); 55 listSetFreeMethod(l,decrRefCount); 56 o->encoding = REDIS_ENCODING_LINKEDLIST; 57 return o; 58 } 59 60 robj *createZiplistObject(void) { 61 unsigned char *zl = ziplistNew(); 62 robj *o = createObject(REDIS_LIST,zl); 63 o->encoding = REDIS_ENCODING_ZIPLIST; 64 return o; 65 } 66 67 robj *createSetObject(void) { 68 dict *d = dictCreate(&setDictType,NULL); 69 robj *o = createObject(REDIS_SET,d); 70 o->encoding = REDIS_ENCODING_HT; 71 return o; 72 } 73 74 robj *createIntsetObject(void) { 75 intset *is = intsetNew(); 76 robj *o = createObject(REDIS_SET,is); 77 o->encoding = REDIS_ENCODING_INTSET; 78 return o; 79 } 80 81 robj *createHashObject(void) { 82 /* All the Hashes start as zipmaps. Will be automatically converted 83 * into hash tables if there are enough elements or big elements 84 * inside. */ 85 unsigned char *zm = zipmapNew(); 86 robj *o = createObject(REDIS_HASH,zm); 87 o->encoding = REDIS_ENCODING_ZIPMAP; 88 return o; 89 } 90 91 robj *createZsetObject(void) { 92 zset *zs = zmalloc(sizeof(*zs)); 93 robj *o; 94 95 zs->dict = dictCreate(&zsetDictType,NULL); 96 zs->zsl = zslCreate(); 97 o = createObject(REDIS_ZSET,zs); 98 o->encoding = REDIS_ENCODING_SKIPLIST; 99 return o; 100 } 101 102 robj *createZsetZiplistObject(void) { 103 unsigned char *zl = ziplistNew(); 104 robj *o = createObject(REDIS_ZSET,zl); 105 o->encoding = REDIS_ENCODING_ZIPLIST; 106 return o; 107 } 108 109 void freeStringObject(robj *o) { 110 if (o->encoding == REDIS_ENCODING_RAW) { 111 sdsfree(o->ptr); 112 } 113 } 114 115 void freeListObject(robj *o) { 116 switch (o->encoding) { 117 case REDIS_ENCODING_LINKEDLIST: 118 listRelease((list*) o->ptr); 119 break; 120 case REDIS_ENCODING_ZIPLIST: 121 zfree(o->ptr); 122 break; 123 default: 124 redisPanic("Unknown list encoding type"); 125 } 126 } 127 128 void freeSetObject(robj *o) { 129 switch (o->encoding) { 130 case REDIS_ENCODING_HT: 131 dictRelease((dict*) o->ptr); 132 break; 133 case REDIS_ENCODING_INTSET: 134 zfree(o->ptr); 135 break; 136 default: 137 redisPanic("Unknown set encoding type"); 138 } 139 } 140 141 void freeZsetObject(robj *o) { 142 zset *zs; 143 switch (o->encoding) { 144 case REDIS_ENCODING_SKIPLIST: 145 zs = o->ptr; 146 dictRelease(zs->dict); 147 zslFree(zs->zsl); 148 zfree(zs); 149 break; 150 case REDIS_ENCODING_ZIPLIST: 151 zfree(o->ptr); 152 break; 153 default: 154 redisPanic("Unknown sorted set encoding"); 155 } 156 } 157 158 void freeHashObject(robj *o) { 159 switch (o->encoding) { 160 case REDIS_ENCODING_HT: 161 dictRelease((dict*) o->ptr); 162 break; 163 case REDIS_ENCODING_ZIPMAP: 164 zfree(o->ptr); 165 break; 166 default: 167 redisPanic("Unknown hash encoding type"); 168 break; 169 } 170 } 171 172 void incrRefCount(robj *o) { 173 o->refcount++; 174 } 175 176 void decrRefCount(void *obj) { 177 robj *o = obj; 178 179 if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0"); 180 if (o->refcount == 1) { 181 switch(o->type) { 182 case REDIS_STRING: freeStringObject(o); break; 183 case REDIS_LIST: freeListObject(o); break; 184 case REDIS_SET: freeSetObject(o); break; 185 case REDIS_ZSET: freeZsetObject(o); break; 186 case REDIS_HASH: freeHashObject(o); break; 187 default: redisPanic("Unknown object type"); break; 188 } 189 zfree(o); 190 } else { 191 o->refcount--; 192 } 193 } 194 195 int checkType(redisClient *c, robj *o, int type) { 196 if (o->type != type) { 197 addReply(c,shared.wrongtypeerr); 198 return 1; 199 } 200 return 0; 201 } 202 203 int isObjectRepresentableAsLongLong(robj *o, long long *llval) { 204 redisAssert(o->type == REDIS_STRING); 205 if (o->encoding == REDIS_ENCODING_INT) { 206 if (llval) *llval = (long) o->ptr; 207 return REDIS_OK; 208 } else { 209 return string2ll(o->ptr,sdslen(o->ptr),llval) ? REDIS_OK : REDIS_ERR; 210 } 211 } 212 213 /* Try to encode a string object in order to save space */ 214 robj *tryObjectEncoding(robj *o) { 215 long value; 216 sds s = o->ptr; 217 218 if (o->encoding != REDIS_ENCODING_RAW) 219 return o; /* Already encoded */ 220 221 /* It's not safe to encode shared objects: shared objects can be shared 222 * everywhere in the "object space" of Redis. Encoded objects can only 223 * appear as "values" (and not, for instance, as keys) */ 224 if (o->refcount > 1) return o; 225 226 /* Currently we try to encode only strings */ 227 redisAssert(o->type == REDIS_STRING); 228 229 /* Check if we can represent this string as a long integer */ 230 if (!string2l(s,sdslen(s),&value)) return o; 231 232 /* Ok, this object can be encoded... 233 * 234 * Can I use a shared object? Only if the object is inside a given 235 * range and if the back end in use is in-memory. For disk store every 236 * object in memory used as value should be independent. 237 * 238 * Note that we also avoid using shared integers when maxmemory is used 239 * because every object needs to have a private LRU field for the LRU 240 * algorithm to work well. */ 241 if (server.maxmemory == 0 && value >= 0 && value < REDIS_SHARED_INTEGERS) { 242 decrRefCount(o); 243 incrRefCount(shared.integers[value]); 244 return shared.integers[value]; 245 } else { 246 o->encoding = REDIS_ENCODING_INT; 247 sdsfree(o->ptr); 248 o->ptr = (void*) value; 249 return o; 250 } 251 } 252 253 /* Get a decoded version of an encoded object (returned as a new object). 254 * If the object is already raw-encoded just increment the ref count. */ 255 robj *getDecodedObject(robj *o) { 256 robj *dec; 257 258 if (o->encoding == REDIS_ENCODING_RAW) { 259 incrRefCount(o); 260 return o; 261 } 262 if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_INT) { 263 char buf[32]; 264 265 ll2string(buf,32,(long)o->ptr); 266 dec = createStringObject(buf,strlen(buf)); 267 return dec; 268 } else { 269 redisPanic("Unknown encoding type"); 270 } 271 } 272 273 /* Compare two string objects via strcmp() or alike. 274 * Note that the objects may be integer-encoded. In such a case we 275 * use ll2string() to get a string representation of the numbers on the stack 276 * and compare the strings, it's much faster than calling getDecodedObject(). 277 * 278 * Important note: if objects are not integer encoded, but binary-safe strings, 279 * sdscmp() from sds.c will apply memcmp() so this function ca be considered 280 * binary safe. */ 281 int compareStringObjects(robj *a, robj *b) { 282 redisAssert(a->type == REDIS_STRING && b->type == REDIS_STRING); 283 char bufa[128], bufb[128], *astr, *bstr; 284 int bothsds = 1; 285 286 if (a == b) return 0; 287 if (a->encoding != REDIS_ENCODING_RAW) { 288 ll2string(bufa,sizeof(bufa),(long) a->ptr); 289 astr = bufa; 290 bothsds = 0; 291 } else { 292 astr = a->ptr; 293 } 294 if (b->encoding != REDIS_ENCODING_RAW) { 295 ll2string(bufb,sizeof(bufb),(long) b->ptr); 296 bstr = bufb; 297 bothsds = 0; 298 } else { 299 bstr = b->ptr; 300 } 301 return bothsds ? sdscmp(astr,bstr) : strcmp(astr,bstr); 302 } 303 304 /* Equal string objects return 1 if the two objects are the same from the 305 * point of view of a string comparison, otherwise 0 is returned. Note that 306 * this function is faster then checking for (compareStringObject(a,b) == 0) 307 * because it can perform some more optimization. */ 308 int equalStringObjects(robj *a, robj *b) { 309 if (a->encoding != REDIS_ENCODING_RAW && b->encoding != REDIS_ENCODING_RAW){ 310 return a->ptr == b->ptr; 311 } else { 312 return compareStringObjects(a,b) == 0; 313 } 314 } 315 316 size_t stringObjectLen(robj *o) { 317 redisAssert(o->type == REDIS_STRING); 318 if (o->encoding == REDIS_ENCODING_RAW) { 319 return sdslen(o->ptr); 320 } else { 321 char buf[32]; 322 323 return ll2string(buf,32,(long)o->ptr); 324 } 325 } 326 327 int getDoubleFromObject(robj *o, double *target) { 328 double value; 329 char *eptr; 330 331 if (o == NULL) { 332 value = 0; 333 } else { 334 redisAssert(o->type == REDIS_STRING); 335 if (o->encoding == REDIS_ENCODING_RAW) { 336 value = strtod(o->ptr, &eptr); 337 if (eptr[0] != '\0' || isnan(value)) return REDIS_ERR; 338 } else if (o->encoding == REDIS_ENCODING_INT) { 339 value = (long)o->ptr; 340 } else { 341 redisPanic("Unknown string encoding"); 342 } 343 } 344 345 *target = value; 346 return REDIS_OK; 347 } 348 349 int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const char *msg) { 350 double value; 351 if (getDoubleFromObject(o, &value) != REDIS_OK) { 352 if (msg != NULL) { 353 addReplyError(c,(char*)msg); 354 } else { 355 addReplyError(c,"value is not a double"); 356 } 357 return REDIS_ERR; 358 } 359 360 *target = value; 361 return REDIS_OK; 362 } 363 364 int getLongLongFromObject(robj *o, long long *target) { 365 long long value; 366 char *eptr; 367 368 if (o == NULL) { 369 value = 0; 370 } else { 371 redisAssert(o->type == REDIS_STRING); 372 if (o->encoding == REDIS_ENCODING_RAW) { 373 value = strtoll(o->ptr, &eptr, 10); 374 if (eptr[0] != '\0') return REDIS_ERR; 375 if (errno == ERANGE && (value == LLONG_MIN || value == LLONG_MAX)) 376 return REDIS_ERR; 377 } else if (o->encoding == REDIS_ENCODING_INT) { 378 value = (long)o->ptr; 379 } else { 380 redisPanic("Unknown string encoding"); 381 } 382 } 383 384 if (target) *target = value; 385 return REDIS_OK; 386 } 387 388 int getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, const char *msg) { 389 long long value; 390 if (getLongLongFromObject(o, &value) != REDIS_OK) { 391 if (msg != NULL) { 392 addReplyError(c,(char*)msg); 393 } else { 394 addReplyError(c,"value is not an integer or out of range"); 395 } 396 return REDIS_ERR; 397 } 398 399 *target = value; 400 return REDIS_OK; 401 } 402 403 int getLongFromObjectOrReply(redisClient *c, robj *o, long *target, const char *msg) { 404 long long value; 405 406 if (getLongLongFromObjectOrReply(c, o, &value, msg) != REDIS_OK) return REDIS_ERR; 407 if (value < LONG_MIN || value > LONG_MAX) { 408 if (msg != NULL) { 409 addReplyError(c,(char*)msg); 410 } else { 411 addReplyError(c,"value is out of range"); 412 } 413 return REDIS_ERR; 414 } 415 416 *target = value; 417 return REDIS_OK; 418 } 419 420 char *strEncoding(int encoding) { 421 switch(encoding) { 422 case REDIS_ENCODING_RAW: return "raw"; 423 case REDIS_ENCODING_INT: return "int"; 424 case REDIS_ENCODING_HT: return "hashtable"; 425 case REDIS_ENCODING_ZIPMAP: return "zipmap"; 426 case REDIS_ENCODING_LINKEDLIST: return "linkedlist"; 427 case REDIS_ENCODING_ZIPLIST: return "ziplist"; 428 case REDIS_ENCODING_INTSET: return "intset"; 429 case REDIS_ENCODING_SKIPLIST: return "skiplist"; 430 default: return "unknown"; 431 } 432 } 433 434 /* Given an object returns the min number of seconds the object was never 435 * requested, using an approximated LRU algorithm. */ 436 unsigned long estimateObjectIdleTime(robj *o) { 437 if (server.lruclock >= o->lru) { 438 return (server.lruclock - o->lru) * REDIS_LRU_CLOCK_RESOLUTION; 439 } else { 440 return ((REDIS_LRU_CLOCK_MAX - o->lru) + server.lruclock) * 441 REDIS_LRU_CLOCK_RESOLUTION; 442 } 443 } 444 445 /* This is an helper function for the DEBUG command. We need to lookup keys 446 * without any modification of LRU or other parameters. */ 447 robj *objectCommandLookup(redisClient *c, robj *key) { 448 dictEntry *de; 449 450 if ((de = dictFind(c->db->dict,key->ptr)) == NULL) return NULL; 451 return (robj*) dictGetEntryVal(de); 452 } 453 454 robj *objectCommandLookupOrReply(redisClient *c, robj *key, robj *reply) { 455 robj *o = objectCommandLookup(c,key); 456 457 if (!o) addReply(c, reply); 458 return o; 459 } 460 461 /* Object command allows to inspect the internals of an Redis Object. 462 * Usage: OBJECT <verb> ... arguments ... */ 463 void objectCommand(redisClient *c) { 464 robj *o; 465 466 if (!strcasecmp(c->argv[1]->ptr,"refcount") && c->argc == 3) { 467 if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk)) 468 == NULL) return; 469 addReplyLongLong(c,o->refcount); 470 } else if (!strcasecmp(c->argv[1]->ptr,"encoding") && c->argc == 3) { 471 if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk)) 472 == NULL) return; 473 addReplyBulkCString(c,strEncoding(o->encoding)); 474 } else if (!strcasecmp(c->argv[1]->ptr,"idletime") && c->argc == 3) { 475 if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk)) 476 == NULL) return; 477 addReplyLongLong(c,estimateObjectIdleTime(o)); 478 } else { 479 addReplyError(c,"Syntax error. Try OBJECT (refcount|encoding|idletime)"); 480 } 481 } 482 483