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