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