]> git.saurik.com Git - redis.git/blame - src/object.c
Rename iterator to setTypeIterator for consistency
[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;
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
33robj *createStringObject(char *ptr, size_t len) {
34 return createObject(REDIS_STRING,sdsnewlen(ptr,len));
35}
36
37robj *createStringObjectFromLongLong(long long value) {
38 robj *o;
e002ec68 39 if (value >= 0 && value < REDIS_SHARED_INTEGERS &&
40 pthread_equal(pthread_self(),server.mainthread)) {
e2641e09 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
55robj *dupStringObject(robj *o) {
56 redisAssert(o->encoding == REDIS_ENCODING_RAW);
57 return createStringObject(o->ptr,sdslen(o->ptr));
58}
59
60robj *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
68robj *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
75robj *createSetObject(void) {
76 dict *d = dictCreate(&setDictType,NULL);
96ffb2fe
PN
77 robj *o = createObject(REDIS_SET,d);
78 o->encoding = REDIS_ENCODING_HT;
79 return o;
80}
81
82robj *createIntsetObject(void) {
83 intset *is = intsetNew();
84 robj *o = createObject(REDIS_SET,is);
85 o->encoding = REDIS_ENCODING_INTSET;
86 return o;
e2641e09 87}
88
89robj *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
99robj *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
107void freeStringObject(robj *o) {
108 if (o->encoding == REDIS_ENCODING_RAW) {
109 sdsfree(o->ptr);
110 }
111}
112
113void 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
126void freeSetObject(robj *o) {
96ffb2fe
PN
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 }
e2641e09 137}
138
139void freeZsetObject(robj *o) {
140 zset *zs = o->ptr;
141
142 dictRelease(zs->dict);
143 zslFree(zs->zsl);
144 zfree(zs);
145}
146
147void 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
161void incrRefCount(robj *o) {
162 o->refcount++;
163}
164
165void 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 default: redisPanic("Unknown object type"); break;
200 }
2f996f02 201 o->ptr = NULL; /* defensive programming. We'll see NULL in traces. */
e2641e09 202 if (server.vm_enabled) pthread_mutex_lock(&server.obj_freelist_mutex);
203 if (listLength(server.objfreelist) > REDIS_OBJFREELIST_MAX ||
204 !listAddNodeHead(server.objfreelist,o))
205 zfree(o);
206 if (server.vm_enabled) pthread_mutex_unlock(&server.obj_freelist_mutex);
207 }
208}
209
210int checkType(redisClient *c, robj *o, int type) {
211 if (o->type != type) {
212 addReply(c,shared.wrongtypeerr);
213 return 1;
214 }
215 return 0;
216}
217
218/* Try to encode a string object in order to save space */
219robj *tryObjectEncoding(robj *o) {
220 long value;
221 sds s = o->ptr;
222
223 if (o->encoding != REDIS_ENCODING_RAW)
224 return o; /* Already encoded */
225
226 /* It's not safe to encode shared objects: shared objects can be shared
227 * everywhere in the "object space" of Redis. Encoded objects can only
228 * appear as "values" (and not, for instance, as keys) */
229 if (o->refcount > 1) return o;
230
231 /* Currently we try to encode only strings */
232 redisAssert(o->type == REDIS_STRING);
233
234 /* Check if we can represent this string as a long integer */
235 if (isStringRepresentableAsLong(s,&value) == REDIS_ERR) return o;
236
0e5441d8 237 /* Ok, this object can be encoded...
238 *
239 * Can I use a shared object? Only if the object is inside a given
cdbea20a 240 * range and if this is the main thread, since when VM is enabled we
0e5441d8 241 * have the constraint that I/O thread should only handle non-shared
242 * objects, in order to avoid race conditions (we don't have per-object
243 * locking). */
244 if (value >= 0 && value < REDIS_SHARED_INTEGERS &&
245 pthread_equal(pthread_self(),server.mainthread)) {
e2641e09 246 decrRefCount(o);
247 incrRefCount(shared.integers[value]);
248 return shared.integers[value];
249 } else {
250 o->encoding = REDIS_ENCODING_INT;
251 sdsfree(o->ptr);
252 o->ptr = (void*) value;
253 return o;
254 }
255}
256
257/* Get a decoded version of an encoded object (returned as a new object).
258 * If the object is already raw-encoded just increment the ref count. */
259robj *getDecodedObject(robj *o) {
260 robj *dec;
261
262 if (o->encoding == REDIS_ENCODING_RAW) {
263 incrRefCount(o);
264 return o;
265 }
266 if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_INT) {
267 char buf[32];
268
269 ll2string(buf,32,(long)o->ptr);
270 dec = createStringObject(buf,strlen(buf));
271 return dec;
272 } else {
273 redisPanic("Unknown encoding type");
274 }
275}
276
277/* Compare two string objects via strcmp() or alike.
278 * Note that the objects may be integer-encoded. In such a case we
279 * use ll2string() to get a string representation of the numbers on the stack
280 * and compare the strings, it's much faster than calling getDecodedObject().
281 *
282 * Important note: if objects are not integer encoded, but binary-safe strings,
283 * sdscmp() from sds.c will apply memcmp() so this function ca be considered
284 * binary safe. */
285int compareStringObjects(robj *a, robj *b) {
286 redisAssert(a->type == REDIS_STRING && b->type == REDIS_STRING);
287 char bufa[128], bufb[128], *astr, *bstr;
288 int bothsds = 1;
289
290 if (a == b) return 0;
291 if (a->encoding != REDIS_ENCODING_RAW) {
292 ll2string(bufa,sizeof(bufa),(long) a->ptr);
293 astr = bufa;
294 bothsds = 0;
295 } else {
296 astr = a->ptr;
297 }
298 if (b->encoding != REDIS_ENCODING_RAW) {
299 ll2string(bufb,sizeof(bufb),(long) b->ptr);
300 bstr = bufb;
301 bothsds = 0;
302 } else {
303 bstr = b->ptr;
304 }
305 return bothsds ? sdscmp(astr,bstr) : strcmp(astr,bstr);
306}
307
308/* Equal string objects return 1 if the two objects are the same from the
309 * point of view of a string comparison, otherwise 0 is returned. Note that
310 * this function is faster then checking for (compareStringObject(a,b) == 0)
311 * because it can perform some more optimization. */
312int equalStringObjects(robj *a, robj *b) {
313 if (a->encoding != REDIS_ENCODING_RAW && b->encoding != REDIS_ENCODING_RAW){
314 return a->ptr == b->ptr;
315 } else {
316 return compareStringObjects(a,b) == 0;
317 }
318}
319
320size_t stringObjectLen(robj *o) {
321 redisAssert(o->type == REDIS_STRING);
322 if (o->encoding == REDIS_ENCODING_RAW) {
323 return sdslen(o->ptr);
324 } else {
325 char buf[32];
326
327 return ll2string(buf,32,(long)o->ptr);
328 }
329}
330
331int getDoubleFromObject(robj *o, double *target) {
332 double value;
333 char *eptr;
334
335 if (o == NULL) {
336 value = 0;
337 } else {
338 redisAssert(o->type == REDIS_STRING);
339 if (o->encoding == REDIS_ENCODING_RAW) {
340 value = strtod(o->ptr, &eptr);
673e1fb7 341 if (eptr[0] != '\0' || isnan(value)) return REDIS_ERR;
e2641e09 342 } else if (o->encoding == REDIS_ENCODING_INT) {
343 value = (long)o->ptr;
344 } else {
345 redisPanic("Unknown string encoding");
346 }
347 }
348
349 *target = value;
350 return REDIS_OK;
351}
352
353int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const char *msg) {
354 double value;
355 if (getDoubleFromObject(o, &value) != REDIS_OK) {
356 if (msg != NULL) {
357 addReplySds(c, sdscatprintf(sdsempty(), "-ERR %s\r\n", msg));
358 } else {
359 addReplySds(c, sdsnew("-ERR value is not a double\r\n"));
360 }
361 return REDIS_ERR;
362 }
363
364 *target = value;
365 return REDIS_OK;
366}
367
368int getLongLongFromObject(robj *o, long long *target) {
369 long long value;
370 char *eptr;
371
372 if (o == NULL) {
373 value = 0;
374 } else {
375 redisAssert(o->type == REDIS_STRING);
376 if (o->encoding == REDIS_ENCODING_RAW) {
377 value = strtoll(o->ptr, &eptr, 10);
87c74dfa 378 if (errno == ERANGE) return REDIS_ERR;
e2641e09 379 if (eptr[0] != '\0') return REDIS_ERR;
380 } else if (o->encoding == REDIS_ENCODING_INT) {
381 value = (long)o->ptr;
382 } else {
383 redisPanic("Unknown string encoding");
384 }
385 }
386
96ffb2fe 387 if (target) *target = value;
e2641e09 388 return REDIS_OK;
389}
390
391int getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, const char *msg) {
392 long long value;
393 if (getLongLongFromObject(o, &value) != REDIS_OK) {
394 if (msg != NULL) {
395 addReplySds(c, sdscatprintf(sdsempty(), "-ERR %s\r\n", msg));
396 } else {
397 addReplySds(c, sdsnew("-ERR value is not an integer\r\n"));
398 }
399 return REDIS_ERR;
400 }
401
402 *target = value;
403 return REDIS_OK;
404}
405
406int getLongFromObjectOrReply(redisClient *c, robj *o, long *target, const char *msg) {
407 long long value;
408
409 if (getLongLongFromObjectOrReply(c, o, &value, msg) != REDIS_OK) return REDIS_ERR;
410 if (value < LONG_MIN || value > LONG_MAX) {
411 if (msg != NULL) {
412 addReplySds(c, sdscatprintf(sdsempty(), "-ERR %s\r\n", msg));
413 } else {
414 addReplySds(c, sdsnew("-ERR value is out of range\r\n"));
415 }
416 return REDIS_ERR;
417 }
418
419 *target = value;
420 return REDIS_OK;
421}
422
423char *strEncoding(int encoding) {
424 switch(encoding) {
425 case REDIS_ENCODING_RAW: return "raw";
426 case REDIS_ENCODING_INT: return "int";
427 case REDIS_ENCODING_HT: return "hashtable";
428 case REDIS_ENCODING_ZIPMAP: return "zipmap";
429 case REDIS_ENCODING_LINKEDLIST: return "linkedlist";
430 case REDIS_ENCODING_ZIPLIST: return "ziplist";
96ffb2fe 431 case REDIS_ENCODING_INTSET: return "intset";
e2641e09 432 default: return "unknown";
433 }
434}