]> git.saurik.com Git - redis.git/blob - src/object.c
Fixed a crash loading the AOF file containing MULTI/EXEC, a result of WATCH implement...
[redis.git] / src / object.c
1 #include "redis.h"
2 #include <pthread.h>
3
4 robj *createObject(int type, void *ptr) {
5 robj *o;
6
7 if (server.vm_enabled) pthread_mutex_lock(&server.obj_freelist_mutex);
8 if (listLength(server.objfreelist)) {
9 listNode *head = listFirst(server.objfreelist);
10 o = listNodeValue(head);
11 listDelNode(server.objfreelist,head);
12 if (server.vm_enabled) pthread_mutex_unlock(&server.obj_freelist_mutex);
13 } else {
14 if (server.vm_enabled)
15 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 if (server.vm_enabled) {
23 /* Note that this code may run in the context of an I/O thread
24 * and accessing server.lruclock in theory is an error
25 * (no locks). But in practice this is safe, and even if we read
26 * garbage Redis will not fail. */
27 o->lru = server.lruclock;
28 o->storage = REDIS_VM_MEMORY;
29 }
30 return o;
31 }
32
33 robj *createStringObject(char *ptr, size_t len) {
34 return createObject(REDIS_STRING,sdsnewlen(ptr,len));
35 }
36
37 robj *createStringObjectFromLongLong(long long value) {
38 robj *o;
39 if (value >= 0 && value < REDIS_SHARED_INTEGERS) {
40 incrRefCount(shared.integers[value]);
41 o = shared.integers[value];
42 } else {
43 if (value >= LONG_MIN && value <= LONG_MAX) {
44 o = createObject(REDIS_STRING, NULL);
45 o->encoding = REDIS_ENCODING_INT;
46 o->ptr = (void*)((long)value);
47 } else {
48 o = createObject(REDIS_STRING,sdsfromlonglong(value));
49 }
50 }
51 return o;
52 }
53
54 robj *dupStringObject(robj *o) {
55 redisAssert(o->encoding == REDIS_ENCODING_RAW);
56 return createStringObject(o->ptr,sdslen(o->ptr));
57 }
58
59 robj *createListObject(void) {
60 list *l = listCreate();
61 robj *o = createObject(REDIS_LIST,l);
62 listSetFreeMethod(l,decrRefCount);
63 o->encoding = REDIS_ENCODING_LINKEDLIST;
64 return o;
65 }
66
67 robj *createZiplistObject(void) {
68 unsigned char *zl = ziplistNew();
69 robj *o = createObject(REDIS_LIST,zl);
70 o->encoding = REDIS_ENCODING_ZIPLIST;
71 return o;
72 }
73
74 robj *createSetObject(void) {
75 dict *d = dictCreate(&setDictType,NULL);
76 return createObject(REDIS_SET,d);
77 }
78
79 robj *createHashObject(void) {
80 /* All the Hashes start as zipmaps. Will be automatically converted
81 * into hash tables if there are enough elements or big elements
82 * inside. */
83 unsigned char *zm = zipmapNew();
84 robj *o = createObject(REDIS_HASH,zm);
85 o->encoding = REDIS_ENCODING_ZIPMAP;
86 return o;
87 }
88
89 robj *createZsetObject(void) {
90 zset *zs = zmalloc(sizeof(*zs));
91
92 zs->dict = dictCreate(&zsetDictType,NULL);
93 zs->zsl = zslCreate();
94 return createObject(REDIS_ZSET,zs);
95 }
96
97 void freeStringObject(robj *o) {
98 if (o->encoding == REDIS_ENCODING_RAW) {
99 sdsfree(o->ptr);
100 }
101 }
102
103 void freeListObject(robj *o) {
104 switch (o->encoding) {
105 case REDIS_ENCODING_LINKEDLIST:
106 listRelease((list*) o->ptr);
107 break;
108 case REDIS_ENCODING_ZIPLIST:
109 zfree(o->ptr);
110 break;
111 default:
112 redisPanic("Unknown list encoding type");
113 }
114 }
115
116 void freeSetObject(robj *o) {
117 dictRelease((dict*) o->ptr);
118 }
119
120 void freeZsetObject(robj *o) {
121 zset *zs = o->ptr;
122
123 dictRelease(zs->dict);
124 zslFree(zs->zsl);
125 zfree(zs);
126 }
127
128 void freeHashObject(robj *o) {
129 switch (o->encoding) {
130 case REDIS_ENCODING_HT:
131 dictRelease((dict*) o->ptr);
132 break;
133 case REDIS_ENCODING_ZIPMAP:
134 zfree(o->ptr);
135 break;
136 default:
137 redisPanic("Unknown hash encoding type");
138 break;
139 }
140 }
141
142 void incrRefCount(robj *o) {
143 o->refcount++;
144 }
145
146 void decrRefCount(void *obj) {
147 robj *o = obj;
148
149 /* Object is a swapped out value, or in the process of being loaded. */
150 if (server.vm_enabled &&
151 (o->storage == REDIS_VM_SWAPPED || o->storage == REDIS_VM_LOADING))
152 {
153 vmpointer *vp = obj;
154 if (o->storage == REDIS_VM_LOADING) vmCancelThreadedIOJob(o);
155 vmMarkPagesFree(vp->page,vp->usedpages);
156 server.vm_stats_swapped_objects--;
157 zfree(vp);
158 return;
159 }
160
161 if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0");
162 /* Object is in memory, or in the process of being swapped out.
163 *
164 * If the object is being swapped out, abort the operation on
165 * decrRefCount even if the refcount does not drop to 0: the object
166 * is referenced at least two times, as value of the key AND as
167 * job->val in the iojob. So if we don't invalidate the iojob, when it is
168 * done but the relevant key was removed in the meantime, the
169 * complete jobs handler will not find the key about the job and the
170 * assert will fail. */
171 if (server.vm_enabled && o->storage == REDIS_VM_SWAPPING)
172 vmCancelThreadedIOJob(o);
173 if (--(o->refcount) == 0) {
174 switch(o->type) {
175 case REDIS_STRING: freeStringObject(o); break;
176 case REDIS_LIST: freeListObject(o); break;
177 case REDIS_SET: freeSetObject(o); break;
178 case REDIS_ZSET: freeZsetObject(o); break;
179 case REDIS_HASH: freeHashObject(o); break;
180 default: redisPanic("Unknown object type"); break;
181 }
182 if (server.vm_enabled) pthread_mutex_lock(&server.obj_freelist_mutex);
183 if (listLength(server.objfreelist) > REDIS_OBJFREELIST_MAX ||
184 !listAddNodeHead(server.objfreelist,o))
185 zfree(o);
186 if (server.vm_enabled) pthread_mutex_unlock(&server.obj_freelist_mutex);
187 }
188 }
189
190 int checkType(redisClient *c, robj *o, int type) {
191 if (o->type != type) {
192 addReply(c,shared.wrongtypeerr);
193 return 1;
194 }
195 return 0;
196 }
197
198 /* Try to encode a string object in order to save space */
199 robj *tryObjectEncoding(robj *o) {
200 long value;
201 sds s = o->ptr;
202
203 if (o->encoding != REDIS_ENCODING_RAW)
204 return o; /* Already encoded */
205
206 /* It's not safe to encode shared objects: shared objects can be shared
207 * everywhere in the "object space" of Redis. Encoded objects can only
208 * appear as "values" (and not, for instance, as keys) */
209 if (o->refcount > 1) return o;
210
211 /* Currently we try to encode only strings */
212 redisAssert(o->type == REDIS_STRING);
213
214 /* Check if we can represent this string as a long integer */
215 if (isStringRepresentableAsLong(s,&value) == REDIS_ERR) return o;
216
217 /* Ok, this object can be encoded */
218 if (value >= 0 && value < REDIS_SHARED_INTEGERS) {
219 decrRefCount(o);
220 incrRefCount(shared.integers[value]);
221 return shared.integers[value];
222 } else {
223 o->encoding = REDIS_ENCODING_INT;
224 sdsfree(o->ptr);
225 o->ptr = (void*) value;
226 return o;
227 }
228 }
229
230 /* Get a decoded version of an encoded object (returned as a new object).
231 * If the object is already raw-encoded just increment the ref count. */
232 robj *getDecodedObject(robj *o) {
233 robj *dec;
234
235 if (o->encoding == REDIS_ENCODING_RAW) {
236 incrRefCount(o);
237 return o;
238 }
239 if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_INT) {
240 char buf[32];
241
242 ll2string(buf,32,(long)o->ptr);
243 dec = createStringObject(buf,strlen(buf));
244 return dec;
245 } else {
246 redisPanic("Unknown encoding type");
247 }
248 }
249
250 /* Compare two string objects via strcmp() or alike.
251 * Note that the objects may be integer-encoded. In such a case we
252 * use ll2string() to get a string representation of the numbers on the stack
253 * and compare the strings, it's much faster than calling getDecodedObject().
254 *
255 * Important note: if objects are not integer encoded, but binary-safe strings,
256 * sdscmp() from sds.c will apply memcmp() so this function ca be considered
257 * binary safe. */
258 int compareStringObjects(robj *a, robj *b) {
259 redisAssert(a->type == REDIS_STRING && b->type == REDIS_STRING);
260 char bufa[128], bufb[128], *astr, *bstr;
261 int bothsds = 1;
262
263 if (a == b) return 0;
264 if (a->encoding != REDIS_ENCODING_RAW) {
265 ll2string(bufa,sizeof(bufa),(long) a->ptr);
266 astr = bufa;
267 bothsds = 0;
268 } else {
269 astr = a->ptr;
270 }
271 if (b->encoding != REDIS_ENCODING_RAW) {
272 ll2string(bufb,sizeof(bufb),(long) b->ptr);
273 bstr = bufb;
274 bothsds = 0;
275 } else {
276 bstr = b->ptr;
277 }
278 return bothsds ? sdscmp(astr,bstr) : strcmp(astr,bstr);
279 }
280
281 /* Equal string objects return 1 if the two objects are the same from the
282 * point of view of a string comparison, otherwise 0 is returned. Note that
283 * this function is faster then checking for (compareStringObject(a,b) == 0)
284 * because it can perform some more optimization. */
285 int equalStringObjects(robj *a, robj *b) {
286 if (a->encoding != REDIS_ENCODING_RAW && b->encoding != REDIS_ENCODING_RAW){
287 return a->ptr == b->ptr;
288 } else {
289 return compareStringObjects(a,b) == 0;
290 }
291 }
292
293 size_t stringObjectLen(robj *o) {
294 redisAssert(o->type == REDIS_STRING);
295 if (o->encoding == REDIS_ENCODING_RAW) {
296 return sdslen(o->ptr);
297 } else {
298 char buf[32];
299
300 return ll2string(buf,32,(long)o->ptr);
301 }
302 }
303
304 int getDoubleFromObject(robj *o, double *target) {
305 double value;
306 char *eptr;
307
308 if (o == NULL) {
309 value = 0;
310 } else {
311 redisAssert(o->type == REDIS_STRING);
312 if (o->encoding == REDIS_ENCODING_RAW) {
313 value = strtod(o->ptr, &eptr);
314 if (eptr[0] != '\0') return REDIS_ERR;
315 } else if (o->encoding == REDIS_ENCODING_INT) {
316 value = (long)o->ptr;
317 } else {
318 redisPanic("Unknown string encoding");
319 }
320 }
321
322 *target = value;
323 return REDIS_OK;
324 }
325
326 int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const char *msg) {
327 double value;
328 if (getDoubleFromObject(o, &value) != REDIS_OK) {
329 if (msg != NULL) {
330 addReplySds(c, sdscatprintf(sdsempty(), "-ERR %s\r\n", msg));
331 } else {
332 addReplySds(c, sdsnew("-ERR value is not a double\r\n"));
333 }
334 return REDIS_ERR;
335 }
336
337 *target = value;
338 return REDIS_OK;
339 }
340
341 int getLongLongFromObject(robj *o, long long *target) {
342 long long value;
343 char *eptr;
344
345 if (o == NULL) {
346 value = 0;
347 } else {
348 redisAssert(o->type == REDIS_STRING);
349 if (o->encoding == REDIS_ENCODING_RAW) {
350 value = strtoll(o->ptr, &eptr, 10);
351 if (eptr[0] != '\0') return REDIS_ERR;
352 } else if (o->encoding == REDIS_ENCODING_INT) {
353 value = (long)o->ptr;
354 } else {
355 redisPanic("Unknown string encoding");
356 }
357 }
358
359 *target = value;
360 return REDIS_OK;
361 }
362
363 int getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, const char *msg) {
364 long long value;
365 if (getLongLongFromObject(o, &value) != REDIS_OK) {
366 if (msg != NULL) {
367 addReplySds(c, sdscatprintf(sdsempty(), "-ERR %s\r\n", msg));
368 } else {
369 addReplySds(c, sdsnew("-ERR value is not an integer\r\n"));
370 }
371 return REDIS_ERR;
372 }
373
374 *target = value;
375 return REDIS_OK;
376 }
377
378 int getLongFromObjectOrReply(redisClient *c, robj *o, long *target, const char *msg) {
379 long long value;
380
381 if (getLongLongFromObjectOrReply(c, o, &value, msg) != REDIS_OK) return REDIS_ERR;
382 if (value < LONG_MIN || value > LONG_MAX) {
383 if (msg != NULL) {
384 addReplySds(c, sdscatprintf(sdsempty(), "-ERR %s\r\n", msg));
385 } else {
386 addReplySds(c, sdsnew("-ERR value is out of range\r\n"));
387 }
388 return REDIS_ERR;
389 }
390
391 *target = value;
392 return REDIS_OK;
393 }
394
395 char *strEncoding(int encoding) {
396 switch(encoding) {
397 case REDIS_ENCODING_RAW: return "raw";
398 case REDIS_ENCODING_INT: return "int";
399 case REDIS_ENCODING_HT: return "hashtable";
400 case REDIS_ENCODING_ZIPMAP: return "zipmap";
401 case REDIS_ENCODING_LINKEDLIST: return "linkedlist";
402 case REDIS_ENCODING_ZIPLIST: return "ziplist";
403 default: return "unknown";
404 }
405 }