]> git.saurik.com Git - redis.git/blob - src/object.c
b1eae96329ebe59a4cdc7366d8ca0d393ed27761
[redis.git] / src / object.c
1 #include "redis.h"
2 #include <pthread.h>
3 #include <math.h>
4
5 robj *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 {
15 if (server.vm_enabled) pthread_mutex_unlock(&server.obj_freelist_mutex);
16 o = zmalloc(sizeof(*o));
17 }
18 o->type = type;
19 o->encoding = REDIS_ENCODING_RAW;
20 o->ptr = ptr;
21 o->refcount = 1;
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;
35 return o;
36 }
37
38 robj *createStringObject(char *ptr, size_t len) {
39 return createObject(REDIS_STRING,sdsnewlen(ptr,len));
40 }
41
42 robj *createStringObjectFromLongLong(long long value) {
43 robj *o;
44 if (value >= 0 && value < REDIS_SHARED_INTEGERS &&
45 pthread_equal(pthread_self(),server.mainthread)) {
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
60 robj *dupStringObject(robj *o) {
61 redisAssert(o->encoding == REDIS_ENCODING_RAW);
62 return createStringObject(o->ptr,sdslen(o->ptr));
63 }
64
65 robj *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
73 robj *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
80 robj *createSetObject(void) {
81 dict *d = dictCreate(&setDictType,NULL);
82 robj *o = createObject(REDIS_SET,d);
83 o->encoding = REDIS_ENCODING_HT;
84 return o;
85 }
86
87 robj *createIntsetObject(void) {
88 intset *is = intsetNew();
89 robj *o = createObject(REDIS_SET,is);
90 o->encoding = REDIS_ENCODING_INTSET;
91 return o;
92 }
93
94 robj *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
104 robj *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
112 void freeStringObject(robj *o) {
113 if (o->encoding == REDIS_ENCODING_RAW) {
114 sdsfree(o->ptr);
115 }
116 }
117
118 void 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
131 void freeSetObject(robj *o) {
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 }
142 }
143
144 void freeZsetObject(robj *o) {
145 zset *zs = o->ptr;
146
147 dictRelease(zs->dict);
148 zslFree(zs->zsl);
149 zfree(zs);
150 }
151
152 void 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
166 void incrRefCount(robj *o) {
167 o->refcount++;
168 }
169
170 void 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 }
206 o->ptr = NULL; /* defensive programming. We'll see NULL in traces. */
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
215 int 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 */
224 robj *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
242 /* Ok, this object can be encoded...
243 *
244 * Can I use a shared object? Only if the object is inside a given
245 * range and if this is the main thread, since when VM is enabled we
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
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 &&
254 pthread_equal(pthread_self(),server.mainthread)) {
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. */
268 robj *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. */
294 int 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. */
321 int 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
329 size_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
340 int 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);
350 if (eptr[0] != '\0' || isnan(value)) return REDIS_ERR;
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
362 int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const char *msg) {
363 double value;
364 if (getDoubleFromObject(o, &value) != REDIS_OK) {
365 if (msg != NULL) {
366 addReplyError(c,(char*)msg);
367 } else {
368 addReplyError(c,"value is not a double");
369 }
370 return REDIS_ERR;
371 }
372
373 *target = value;
374 return REDIS_OK;
375 }
376
377 int 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;
388 if (errno == ERANGE && (value == LLONG_MIN || value == LLONG_MAX))
389 return REDIS_ERR;
390 } else if (o->encoding == REDIS_ENCODING_INT) {
391 value = (long)o->ptr;
392 } else {
393 redisPanic("Unknown string encoding");
394 }
395 }
396
397 if (target) *target = value;
398 return REDIS_OK;
399 }
400
401 int 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) {
405 addReplyError(c,(char*)msg);
406 } else {
407 addReplyError(c,"value is not an integer or out of range");
408 }
409 return REDIS_ERR;
410 }
411
412 *target = value;
413 return REDIS_OK;
414 }
415
416 int 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) {
422 addReplyError(c,(char*)msg);
423 } else {
424 addReplyError(c,"value is out of range");
425 }
426 return REDIS_ERR;
427 }
428
429 *target = value;
430 return REDIS_OK;
431 }
432
433 char *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";
441 case REDIS_ENCODING_INTSET: return "intset";
442 default: return "unknown";
443 }
444 }
445
446 /* Given an object returns the min number of seconds the object was never
447 * requested, using an approximated LRU algorithm. */
448 unsigned long estimateObjectIdleTime(robj *o) {
449 if (server.lruclock >= o->lru) {
450 return (server.lruclock - o->lru) * REDIS_LRU_CLOCK_RESOLUTION;
451 } else {
452 return ((REDIS_LRU_CLOCK_MAX - o->lru) + server.lruclock) *
453 REDIS_LRU_CLOCK_RESOLUTION;
454 }
455 }