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