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