]> git.saurik.com Git - redis.git/blame - src/object.c
command line option in test suite to activate valgrind mode
[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. */
24 o->storage = REDIS_VM_MEMORY;
e2641e09 25 return o;
26}
27
28robj *createStringObject(char *ptr, size_t len) {
29 return createObject(REDIS_STRING,sdsnewlen(ptr,len));
30}
31
32robj *createStringObjectFromLongLong(long long value) {
33 robj *o;
e002ec68 34 if (value >= 0 && value < REDIS_SHARED_INTEGERS &&
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));
96
97 zs->dict = dictCreate(&zsetDictType,NULL);
98 zs->zsl = zslCreate();
99 return createObject(REDIS_ZSET,zs);
100}
101
102void freeStringObject(robj *o) {
103 if (o->encoding == REDIS_ENCODING_RAW) {
104 sdsfree(o->ptr);
105 }
106}
107
108void freeListObject(robj *o) {
109 switch (o->encoding) {
110 case REDIS_ENCODING_LINKEDLIST:
111 listRelease((list*) o->ptr);
112 break;
113 case REDIS_ENCODING_ZIPLIST:
114 zfree(o->ptr);
115 break;
116 default:
117 redisPanic("Unknown list encoding type");
118 }
119}
120
121void freeSetObject(robj *o) {
96ffb2fe
PN
122 switch (o->encoding) {
123 case REDIS_ENCODING_HT:
124 dictRelease((dict*) o->ptr);
125 break;
126 case REDIS_ENCODING_INTSET:
127 zfree(o->ptr);
128 break;
129 default:
130 redisPanic("Unknown set encoding type");
131 }
e2641e09 132}
133
134void freeZsetObject(robj *o) {
135 zset *zs = o->ptr;
136
137 dictRelease(zs->dict);
138 zslFree(zs->zsl);
139 zfree(zs);
140}
141
142void freeHashObject(robj *o) {
143 switch (o->encoding) {
144 case REDIS_ENCODING_HT:
145 dictRelease((dict*) o->ptr);
146 break;
147 case REDIS_ENCODING_ZIPMAP:
148 zfree(o->ptr);
149 break;
150 default:
151 redisPanic("Unknown hash encoding type");
152 break;
153 }
154}
155
156void incrRefCount(robj *o) {
157 o->refcount++;
158}
159
160void decrRefCount(void *obj) {
161 robj *o = obj;
162
163 /* Object is a swapped out value, or in the process of being loaded. */
164 if (server.vm_enabled &&
165 (o->storage == REDIS_VM_SWAPPED || o->storage == REDIS_VM_LOADING))
166 {
167 vmpointer *vp = obj;
168 if (o->storage == REDIS_VM_LOADING) vmCancelThreadedIOJob(o);
169 vmMarkPagesFree(vp->page,vp->usedpages);
170 server.vm_stats_swapped_objects--;
171 zfree(vp);
172 return;
173 }
174
175 if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0");
176 /* Object is in memory, or in the process of being swapped out.
177 *
178 * If the object is being swapped out, abort the operation on
179 * decrRefCount even if the refcount does not drop to 0: the object
180 * is referenced at least two times, as value of the key AND as
181 * job->val in the iojob. So if we don't invalidate the iojob, when it is
182 * done but the relevant key was removed in the meantime, the
183 * complete jobs handler will not find the key about the job and the
184 * assert will fail. */
185 if (server.vm_enabled && o->storage == REDIS_VM_SWAPPING)
186 vmCancelThreadedIOJob(o);
187 if (--(o->refcount) == 0) {
188 switch(o->type) {
189 case REDIS_STRING: freeStringObject(o); break;
190 case REDIS_LIST: freeListObject(o); break;
191 case REDIS_SET: freeSetObject(o); break;
192 case REDIS_ZSET: freeZsetObject(o); break;
193 case REDIS_HASH: freeHashObject(o); break;
194 default: redisPanic("Unknown object type"); break;
195 }
2f996f02 196 o->ptr = NULL; /* defensive programming. We'll see NULL in traces. */
a9b18e54 197 zfree(o);
e2641e09 198 }
199}
200
201int checkType(redisClient *c, robj *o, int type) {
202 if (o->type != type) {
203 addReply(c,shared.wrongtypeerr);
204 return 1;
205 }
206 return 0;
207}
208
209/* Try to encode a string object in order to save space */
210robj *tryObjectEncoding(robj *o) {
211 long value;
212 sds s = o->ptr;
213
214 if (o->encoding != REDIS_ENCODING_RAW)
215 return o; /* Already encoded */
216
217 /* It's not safe to encode shared objects: shared objects can be shared
218 * everywhere in the "object space" of Redis. Encoded objects can only
219 * appear as "values" (and not, for instance, as keys) */
220 if (o->refcount > 1) return o;
221
222 /* Currently we try to encode only strings */
223 redisAssert(o->type == REDIS_STRING);
224
225 /* Check if we can represent this string as a long integer */
226 if (isStringRepresentableAsLong(s,&value) == REDIS_ERR) return o;
227
0e5441d8 228 /* Ok, this object can be encoded...
229 *
230 * Can I use a shared object? Only if the object is inside a given
cdbea20a 231 * range and if this is the main thread, since when VM is enabled we
0e5441d8 232 * have the constraint that I/O thread should only handle non-shared
233 * objects, in order to avoid race conditions (we don't have per-object
13a49af4 234 * locking).
235 *
236 * Note that we also avoid using shared integers when maxmemory is used
237 * because very object needs to have a private LRU field for the LRU
238 * algorithm to work well. */
239 if (server.maxmemory == 0 && value >= 0 && value < REDIS_SHARED_INTEGERS &&
0e5441d8 240 pthread_equal(pthread_self(),server.mainthread)) {
e2641e09 241 decrRefCount(o);
242 incrRefCount(shared.integers[value]);
243 return shared.integers[value];
244 } else {
245 o->encoding = REDIS_ENCODING_INT;
246 sdsfree(o->ptr);
247 o->ptr = (void*) value;
248 return o;
249 }
250}
251
252/* Get a decoded version of an encoded object (returned as a new object).
253 * If the object is already raw-encoded just increment the ref count. */
254robj *getDecodedObject(robj *o) {
255 robj *dec;
256
257 if (o->encoding == REDIS_ENCODING_RAW) {
258 incrRefCount(o);
259 return o;
260 }
261 if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_INT) {
262 char buf[32];
263
264 ll2string(buf,32,(long)o->ptr);
265 dec = createStringObject(buf,strlen(buf));
266 return dec;
267 } else {
268 redisPanic("Unknown encoding type");
269 }
270}
271
272/* Compare two string objects via strcmp() or alike.
273 * Note that the objects may be integer-encoded. In such a case we
274 * use ll2string() to get a string representation of the numbers on the stack
275 * and compare the strings, it's much faster than calling getDecodedObject().
276 *
277 * Important note: if objects are not integer encoded, but binary-safe strings,
278 * sdscmp() from sds.c will apply memcmp() so this function ca be considered
279 * binary safe. */
280int compareStringObjects(robj *a, robj *b) {
281 redisAssert(a->type == REDIS_STRING && b->type == REDIS_STRING);
282 char bufa[128], bufb[128], *astr, *bstr;
283 int bothsds = 1;
284
285 if (a == b) return 0;
286 if (a->encoding != REDIS_ENCODING_RAW) {
287 ll2string(bufa,sizeof(bufa),(long) a->ptr);
288 astr = bufa;
289 bothsds = 0;
290 } else {
291 astr = a->ptr;
292 }
293 if (b->encoding != REDIS_ENCODING_RAW) {
294 ll2string(bufb,sizeof(bufb),(long) b->ptr);
295 bstr = bufb;
296 bothsds = 0;
297 } else {
298 bstr = b->ptr;
299 }
300 return bothsds ? sdscmp(astr,bstr) : strcmp(astr,bstr);
301}
302
303/* Equal string objects return 1 if the two objects are the same from the
304 * point of view of a string comparison, otherwise 0 is returned. Note that
305 * this function is faster then checking for (compareStringObject(a,b) == 0)
306 * because it can perform some more optimization. */
307int equalStringObjects(robj *a, robj *b) {
308 if (a->encoding != REDIS_ENCODING_RAW && b->encoding != REDIS_ENCODING_RAW){
309 return a->ptr == b->ptr;
310 } else {
311 return compareStringObjects(a,b) == 0;
312 }
313}
314
315size_t stringObjectLen(robj *o) {
316 redisAssert(o->type == REDIS_STRING);
317 if (o->encoding == REDIS_ENCODING_RAW) {
318 return sdslen(o->ptr);
319 } else {
320 char buf[32];
321
322 return ll2string(buf,32,(long)o->ptr);
323 }
324}
325
326int getDoubleFromObject(robj *o, double *target) {
327 double value;
328 char *eptr;
329
330 if (o == NULL) {
331 value = 0;
332 } else {
333 redisAssert(o->type == REDIS_STRING);
334 if (o->encoding == REDIS_ENCODING_RAW) {
335 value = strtod(o->ptr, &eptr);
673e1fb7 336 if (eptr[0] != '\0' || isnan(value)) return REDIS_ERR;
e2641e09 337 } else if (o->encoding == REDIS_ENCODING_INT) {
338 value = (long)o->ptr;
339 } else {
340 redisPanic("Unknown string encoding");
341 }
342 }
343
344 *target = value;
345 return REDIS_OK;
346}
347
348int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const char *msg) {
349 double value;
350 if (getDoubleFromObject(o, &value) != REDIS_OK) {
351 if (msg != NULL) {
3ab20376 352 addReplyError(c,(char*)msg);
e2641e09 353 } else {
3ab20376 354 addReplyError(c,"value is not a double");
e2641e09 355 }
356 return REDIS_ERR;
357 }
358
359 *target = value;
360 return REDIS_OK;
361}
362
363int getLongLongFromObject(robj *o, long long *target) {
364 long long value;
365 char *eptr;
366
367 if (o == NULL) {
368 value = 0;
369 } else {
370 redisAssert(o->type == REDIS_STRING);
371 if (o->encoding == REDIS_ENCODING_RAW) {
372 value = strtoll(o->ptr, &eptr, 10);
373 if (eptr[0] != '\0') return REDIS_ERR;
c91abdcd 374 if (errno == ERANGE && (value == LLONG_MIN || value == LLONG_MAX))
375 return REDIS_ERR;
e2641e09 376 } else if (o->encoding == REDIS_ENCODING_INT) {
377 value = (long)o->ptr;
378 } else {
379 redisPanic("Unknown string encoding");
380 }
381 }
382
96ffb2fe 383 if (target) *target = value;
e2641e09 384 return REDIS_OK;
385}
386
387int getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, const char *msg) {
388 long long value;
389 if (getLongLongFromObject(o, &value) != REDIS_OK) {
390 if (msg != NULL) {
3ab20376 391 addReplyError(c,(char*)msg);
e2641e09 392 } else {
3ab20376 393 addReplyError(c,"value is not an integer or out of range");
e2641e09 394 }
395 return REDIS_ERR;
396 }
397
398 *target = value;
399 return REDIS_OK;
400}
401
402int getLongFromObjectOrReply(redisClient *c, robj *o, long *target, const char *msg) {
403 long long value;
404
405 if (getLongLongFromObjectOrReply(c, o, &value, msg) != REDIS_OK) return REDIS_ERR;
406 if (value < LONG_MIN || value > LONG_MAX) {
407 if (msg != NULL) {
3ab20376 408 addReplyError(c,(char*)msg);
e2641e09 409 } else {
3ab20376 410 addReplyError(c,"value is out of range");
e2641e09 411 }
412 return REDIS_ERR;
413 }
414
415 *target = value;
416 return REDIS_OK;
417}
418
419char *strEncoding(int encoding) {
420 switch(encoding) {
421 case REDIS_ENCODING_RAW: return "raw";
422 case REDIS_ENCODING_INT: return "int";
423 case REDIS_ENCODING_HT: return "hashtable";
424 case REDIS_ENCODING_ZIPMAP: return "zipmap";
425 case REDIS_ENCODING_LINKEDLIST: return "linkedlist";
426 case REDIS_ENCODING_ZIPLIST: return "ziplist";
96ffb2fe 427 case REDIS_ENCODING_INTSET: return "intset";
e2641e09 428 default: return "unknown";
429 }
430}
ef59a8bc 431
432/* Given an object returns the min number of seconds the object was never
433 * requested, using an approximated LRU algorithm. */
434unsigned long estimateObjectIdleTime(robj *o) {
435 if (server.lruclock >= o->lru) {
165346ca 436 return (server.lruclock - o->lru) * REDIS_LRU_CLOCK_RESOLUTION;
ef59a8bc 437 } else {
165346ca 438 return ((REDIS_LRU_CLOCK_MAX - o->lru) + server.lruclock) *
439 REDIS_LRU_CLOCK_RESOLUTION;
ef59a8bc 440 }
441}