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