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