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