]> git.saurik.com Git - redis.git/blame - src/object.c
added support for command renaming/suppression in redis.conf
[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) {
6 robj *o;
7
8 if (server.vm_enabled) pthread_mutex_lock(&server.obj_freelist_mutex);
9 if (listLength(server.objfreelist)) {
10 listNode *head = listFirst(server.objfreelist);
11 o = listNodeValue(head);
12 listDelNode(server.objfreelist,head);
13 if (server.vm_enabled) pthread_mutex_unlock(&server.obj_freelist_mutex);
14 } else {
2cffe299 15 if (server.vm_enabled) pthread_mutex_unlock(&server.obj_freelist_mutex);
e2641e09 16 o = zmalloc(sizeof(*o));
17 }
18 o->type = type;
19 o->encoding = REDIS_ENCODING_RAW;
20 o->ptr = ptr;
21 o->refcount = 1;
ef59a8bc 22 /* Set the LRU to the current lruclock (minutes resolution).
23 * We do this regardless of the fact VM is active as LRU is also
24 * used for the maxmemory directive when Redis is used as cache.
25 *
26 * Note that this code may run in the context of an I/O thread
27 * and accessing server.lruclock in theory is an error
28 * (no locks). But in practice this is safe, and even if we read
29 * garbage Redis will not fail. */
30 o->lru = server.lruclock;
31 /* The following is only needed if VM is active, but since the conditional
32 * is probably more costly than initializing the field it's better to
33 * have every field properly initialized anyway. */
34 o->storage = REDIS_VM_MEMORY;
e2641e09 35 return o;
36}
37
38robj *createStringObject(char *ptr, size_t len) {
39 return createObject(REDIS_STRING,sdsnewlen(ptr,len));
40}
41
42robj *createStringObjectFromLongLong(long long value) {
43 robj *o;
e002ec68 44 if (value >= 0 && value < REDIS_SHARED_INTEGERS &&
45 pthread_equal(pthread_self(),server.mainthread)) {
e2641e09 46 incrRefCount(shared.integers[value]);
47 o = shared.integers[value];
48 } else {
49 if (value >= LONG_MIN && value <= LONG_MAX) {
50 o = createObject(REDIS_STRING, NULL);
51 o->encoding = REDIS_ENCODING_INT;
52 o->ptr = (void*)((long)value);
53 } else {
54 o = createObject(REDIS_STRING,sdsfromlonglong(value));
55 }
56 }
57 return o;
58}
59
60robj *dupStringObject(robj *o) {
61 redisAssert(o->encoding == REDIS_ENCODING_RAW);
62 return createStringObject(o->ptr,sdslen(o->ptr));
63}
64
65robj *createListObject(void) {
66 list *l = listCreate();
67 robj *o = createObject(REDIS_LIST,l);
68 listSetFreeMethod(l,decrRefCount);
69 o->encoding = REDIS_ENCODING_LINKEDLIST;
70 return o;
71}
72
73robj *createZiplistObject(void) {
74 unsigned char *zl = ziplistNew();
75 robj *o = createObject(REDIS_LIST,zl);
76 o->encoding = REDIS_ENCODING_ZIPLIST;
77 return o;
78}
79
80robj *createSetObject(void) {
81 dict *d = dictCreate(&setDictType,NULL);
96ffb2fe
PN
82 robj *o = createObject(REDIS_SET,d);
83 o->encoding = REDIS_ENCODING_HT;
84 return o;
85}
86
87robj *createIntsetObject(void) {
88 intset *is = intsetNew();
89 robj *o = createObject(REDIS_SET,is);
90 o->encoding = REDIS_ENCODING_INTSET;
91 return o;
e2641e09 92}
93
94robj *createHashObject(void) {
95 /* All the Hashes start as zipmaps. Will be automatically converted
96 * into hash tables if there are enough elements or big elements
97 * inside. */
98 unsigned char *zm = zipmapNew();
99 robj *o = createObject(REDIS_HASH,zm);
100 o->encoding = REDIS_ENCODING_ZIPMAP;
101 return o;
102}
103
104robj *createZsetObject(void) {
105 zset *zs = zmalloc(sizeof(*zs));
106
107 zs->dict = dictCreate(&zsetDictType,NULL);
108 zs->zsl = zslCreate();
109 return createObject(REDIS_ZSET,zs);
110}
111
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) {
145 zset *zs = o->ptr;
146
147 dictRelease(zs->dict);
148 zslFree(zs->zsl);
149 zfree(zs);
150}
151
152void freeHashObject(robj *o) {
153 switch (o->encoding) {
154 case REDIS_ENCODING_HT:
155 dictRelease((dict*) o->ptr);
156 break;
157 case REDIS_ENCODING_ZIPMAP:
158 zfree(o->ptr);
159 break;
160 default:
161 redisPanic("Unknown hash encoding type");
162 break;
163 }
164}
165
166void incrRefCount(robj *o) {
167 o->refcount++;
168}
169
170void decrRefCount(void *obj) {
171 robj *o = obj;
172
173 /* Object is a swapped out value, or in the process of being loaded. */
174 if (server.vm_enabled &&
175 (o->storage == REDIS_VM_SWAPPED || o->storage == REDIS_VM_LOADING))
176 {
177 vmpointer *vp = obj;
178 if (o->storage == REDIS_VM_LOADING) vmCancelThreadedIOJob(o);
179 vmMarkPagesFree(vp->page,vp->usedpages);
180 server.vm_stats_swapped_objects--;
181 zfree(vp);
182 return;
183 }
184
185 if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0");
186 /* Object is in memory, or in the process of being swapped out.
187 *
188 * If the object is being swapped out, abort the operation on
189 * decrRefCount even if the refcount does not drop to 0: the object
190 * is referenced at least two times, as value of the key AND as
191 * job->val in the iojob. So if we don't invalidate the iojob, when it is
192 * done but the relevant key was removed in the meantime, the
193 * complete jobs handler will not find the key about the job and the
194 * assert will fail. */
195 if (server.vm_enabled && o->storage == REDIS_VM_SWAPPING)
196 vmCancelThreadedIOJob(o);
197 if (--(o->refcount) == 0) {
198 switch(o->type) {
199 case REDIS_STRING: freeStringObject(o); break;
200 case REDIS_LIST: freeListObject(o); break;
201 case REDIS_SET: freeSetObject(o); break;
202 case REDIS_ZSET: freeZsetObject(o); break;
203 case REDIS_HASH: freeHashObject(o); break;
204 default: redisPanic("Unknown object type"); break;
205 }
2f996f02 206 o->ptr = NULL; /* defensive programming. We'll see NULL in traces. */
e2641e09 207 if (server.vm_enabled) pthread_mutex_lock(&server.obj_freelist_mutex);
208 if (listLength(server.objfreelist) > REDIS_OBJFREELIST_MAX ||
209 !listAddNodeHead(server.objfreelist,o))
210 zfree(o);
211 if (server.vm_enabled) pthread_mutex_unlock(&server.obj_freelist_mutex);
212 }
213}
214
215int checkType(redisClient *c, robj *o, int type) {
216 if (o->type != type) {
217 addReply(c,shared.wrongtypeerr);
218 return 1;
219 }
220 return 0;
221}
222
223/* Try to encode a string object in order to save space */
224robj *tryObjectEncoding(robj *o) {
225 long value;
226 sds s = o->ptr;
227
228 if (o->encoding != REDIS_ENCODING_RAW)
229 return o; /* Already encoded */
230
231 /* It's not safe to encode shared objects: shared objects can be shared
232 * everywhere in the "object space" of Redis. Encoded objects can only
233 * appear as "values" (and not, for instance, as keys) */
234 if (o->refcount > 1) return o;
235
236 /* Currently we try to encode only strings */
237 redisAssert(o->type == REDIS_STRING);
238
239 /* Check if we can represent this string as a long integer */
240 if (isStringRepresentableAsLong(s,&value) == REDIS_ERR) return o;
241
0e5441d8 242 /* Ok, this object can be encoded...
243 *
244 * Can I use a shared object? Only if the object is inside a given
cdbea20a 245 * range and if this is the main thread, since when VM is enabled we
0e5441d8 246 * have the constraint that I/O thread should only handle non-shared
247 * objects, in order to avoid race conditions (we don't have per-object
13a49af4 248 * locking).
249 *
250 * Note that we also avoid using shared integers when maxmemory is used
251 * because very object needs to have a private LRU field for the LRU
252 * algorithm to work well. */
253 if (server.maxmemory == 0 && value >= 0 && value < REDIS_SHARED_INTEGERS &&
0e5441d8 254 pthread_equal(pthread_self(),server.mainthread)) {
e2641e09 255 decrRefCount(o);
256 incrRefCount(shared.integers[value]);
257 return shared.integers[value];
258 } else {
259 o->encoding = REDIS_ENCODING_INT;
260 sdsfree(o->ptr);
261 o->ptr = (void*) value;
262 return o;
263 }
264}
265
266/* Get a decoded version of an encoded object (returned as a new object).
267 * If the object is already raw-encoded just increment the ref count. */
268robj *getDecodedObject(robj *o) {
269 robj *dec;
270
271 if (o->encoding == REDIS_ENCODING_RAW) {
272 incrRefCount(o);
273 return o;
274 }
275 if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_INT) {
276 char buf[32];
277
278 ll2string(buf,32,(long)o->ptr);
279 dec = createStringObject(buf,strlen(buf));
280 return dec;
281 } else {
282 redisPanic("Unknown encoding type");
283 }
284}
285
286/* Compare two string objects via strcmp() or alike.
287 * Note that the objects may be integer-encoded. In such a case we
288 * use ll2string() to get a string representation of the numbers on the stack
289 * and compare the strings, it's much faster than calling getDecodedObject().
290 *
291 * Important note: if objects are not integer encoded, but binary-safe strings,
292 * sdscmp() from sds.c will apply memcmp() so this function ca be considered
293 * binary safe. */
294int compareStringObjects(robj *a, robj *b) {
295 redisAssert(a->type == REDIS_STRING && b->type == REDIS_STRING);
296 char bufa[128], bufb[128], *astr, *bstr;
297 int bothsds = 1;
298
299 if (a == b) return 0;
300 if (a->encoding != REDIS_ENCODING_RAW) {
301 ll2string(bufa,sizeof(bufa),(long) a->ptr);
302 astr = bufa;
303 bothsds = 0;
304 } else {
305 astr = a->ptr;
306 }
307 if (b->encoding != REDIS_ENCODING_RAW) {
308 ll2string(bufb,sizeof(bufb),(long) b->ptr);
309 bstr = bufb;
310 bothsds = 0;
311 } else {
312 bstr = b->ptr;
313 }
314 return bothsds ? sdscmp(astr,bstr) : strcmp(astr,bstr);
315}
316
317/* Equal string objects return 1 if the two objects are the same from the
318 * point of view of a string comparison, otherwise 0 is returned. Note that
319 * this function is faster then checking for (compareStringObject(a,b) == 0)
320 * because it can perform some more optimization. */
321int equalStringObjects(robj *a, robj *b) {
322 if (a->encoding != REDIS_ENCODING_RAW && b->encoding != REDIS_ENCODING_RAW){
323 return a->ptr == b->ptr;
324 } else {
325 return compareStringObjects(a,b) == 0;
326 }
327}
328
329size_t stringObjectLen(robj *o) {
330 redisAssert(o->type == REDIS_STRING);
331 if (o->encoding == REDIS_ENCODING_RAW) {
332 return sdslen(o->ptr);
333 } else {
334 char buf[32];
335
336 return ll2string(buf,32,(long)o->ptr);
337 }
338}
339
340int getDoubleFromObject(robj *o, double *target) {
341 double value;
342 char *eptr;
343
344 if (o == NULL) {
345 value = 0;
346 } else {
347 redisAssert(o->type == REDIS_STRING);
348 if (o->encoding == REDIS_ENCODING_RAW) {
349 value = strtod(o->ptr, &eptr);
673e1fb7 350 if (eptr[0] != '\0' || isnan(value)) return REDIS_ERR;
e2641e09 351 } else if (o->encoding == REDIS_ENCODING_INT) {
352 value = (long)o->ptr;
353 } else {
354 redisPanic("Unknown string encoding");
355 }
356 }
357
358 *target = value;
359 return REDIS_OK;
360}
361
362int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const char *msg) {
363 double value;
364 if (getDoubleFromObject(o, &value) != REDIS_OK) {
365 if (msg != NULL) {
3ab20376 366 addReplyError(c,(char*)msg);
e2641e09 367 } else {
3ab20376 368 addReplyError(c,"value is not a double");
e2641e09 369 }
370 return REDIS_ERR;
371 }
372
373 *target = value;
374 return REDIS_OK;
375}
376
377int getLongLongFromObject(robj *o, long long *target) {
378 long long value;
379 char *eptr;
380
381 if (o == NULL) {
382 value = 0;
383 } else {
384 redisAssert(o->type == REDIS_STRING);
385 if (o->encoding == REDIS_ENCODING_RAW) {
386 value = strtoll(o->ptr, &eptr, 10);
387 if (eptr[0] != '\0') return REDIS_ERR;
c91abdcd 388 if (errno == ERANGE && (value == LLONG_MIN || value == LLONG_MAX))
389 return REDIS_ERR;
e2641e09 390 } else if (o->encoding == REDIS_ENCODING_INT) {
391 value = (long)o->ptr;
392 } else {
393 redisPanic("Unknown string encoding");
394 }
395 }
396
96ffb2fe 397 if (target) *target = value;
e2641e09 398 return REDIS_OK;
399}
400
401int getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, const char *msg) {
402 long long value;
403 if (getLongLongFromObject(o, &value) != REDIS_OK) {
404 if (msg != NULL) {
3ab20376 405 addReplyError(c,(char*)msg);
e2641e09 406 } else {
3ab20376 407 addReplyError(c,"value is not an integer or out of range");
e2641e09 408 }
409 return REDIS_ERR;
410 }
411
412 *target = value;
413 return REDIS_OK;
414}
415
416int getLongFromObjectOrReply(redisClient *c, robj *o, long *target, const char *msg) {
417 long long value;
418
419 if (getLongLongFromObjectOrReply(c, o, &value, msg) != REDIS_OK) return REDIS_ERR;
420 if (value < LONG_MIN || value > LONG_MAX) {
421 if (msg != NULL) {
3ab20376 422 addReplyError(c,(char*)msg);
e2641e09 423 } else {
3ab20376 424 addReplyError(c,"value is out of range");
e2641e09 425 }
426 return REDIS_ERR;
427 }
428
429 *target = value;
430 return REDIS_OK;
431}
432
433char *strEncoding(int encoding) {
434 switch(encoding) {
435 case REDIS_ENCODING_RAW: return "raw";
436 case REDIS_ENCODING_INT: return "int";
437 case REDIS_ENCODING_HT: return "hashtable";
438 case REDIS_ENCODING_ZIPMAP: return "zipmap";
439 case REDIS_ENCODING_LINKEDLIST: return "linkedlist";
440 case REDIS_ENCODING_ZIPLIST: return "ziplist";
96ffb2fe 441 case REDIS_ENCODING_INTSET: return "intset";
e2641e09 442 default: return "unknown";
443 }
444}
ef59a8bc 445
446/* Given an object returns the min number of seconds the object was never
447 * requested, using an approximated LRU algorithm. */
448unsigned long estimateObjectIdleTime(robj *o) {
449 if (server.lruclock >= o->lru) {
165346ca 450 return (server.lruclock - o->lru) * REDIS_LRU_CLOCK_RESOLUTION;
ef59a8bc 451 } else {
165346ca 452 return ((REDIS_LRU_CLOCK_MAX - o->lru) + server.lruclock) *
453 REDIS_LRU_CLOCK_RESOLUTION;
ef59a8bc 454 }
455}