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