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