]> git.saurik.com Git - redis.git/blob - src/object.c
Support dual encoding in ZREM
[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 return o;
25 }
26
27 robj *createStringObject(char *ptr, size_t len) {
28 return createObject(REDIS_STRING,sdsnewlen(ptr,len));
29 }
30
31 robj *createStringObjectFromLongLong(long long value) {
32 robj *o;
33 if (value >= 0 && value < REDIS_SHARED_INTEGERS &&
34 !server.ds_enabled &&
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 zs->dict = dictCreate(&zsetDictType,NULL);
97 zs->zsl = zslCreate();
98 return createObject(REDIS_ZSET,zs);
99 }
100
101 robj *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
108 void freeStringObject(robj *o) {
109 if (o->encoding == REDIS_ENCODING_RAW) {
110 sdsfree(o->ptr);
111 }
112 }
113
114 void 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
127 void freeSetObject(robj *o) {
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 }
138 }
139
140 void freeZsetObject(robj *o) {
141 zset *zs = o->ptr;
142
143 dictRelease(zs->dict);
144 zslFree(zs->zsl);
145 zfree(zs);
146 }
147
148 void freeHashObject(robj *o) {
149 switch (o->encoding) {
150 case REDIS_ENCODING_HT:
151 dictRelease((dict*) o->ptr);
152 break;
153 case REDIS_ENCODING_ZIPMAP:
154 zfree(o->ptr);
155 break;
156 default:
157 redisPanic("Unknown hash encoding type");
158 break;
159 }
160 }
161
162 void incrRefCount(robj *o) {
163 o->refcount++;
164 }
165
166 void decrRefCount(void *obj) {
167 robj *o = obj;
168
169 if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0");
170 if (--(o->refcount) == 0) {
171 switch(o->type) {
172 case REDIS_STRING: freeStringObject(o); break;
173 case REDIS_LIST: freeListObject(o); break;
174 case REDIS_SET: freeSetObject(o); break;
175 case REDIS_ZSET: freeZsetObject(o); break;
176 case REDIS_HASH: freeHashObject(o); break;
177 default: redisPanic("Unknown object type"); break;
178 }
179 o->ptr = NULL; /* defensive programming. We'll see NULL in traces. */
180 zfree(o);
181 }
182 }
183
184 int checkType(redisClient *c, robj *o, int type) {
185 if (o->type != type) {
186 addReply(c,shared.wrongtypeerr);
187 return 1;
188 }
189 return 0;
190 }
191
192 /* Try to encode a string object in order to save space */
193 robj *tryObjectEncoding(robj *o) {
194 long value;
195 sds s = o->ptr;
196
197 if (o->encoding != REDIS_ENCODING_RAW)
198 return o; /* Already encoded */
199
200 /* It's not safe to encode shared objects: shared objects can be shared
201 * everywhere in the "object space" of Redis. Encoded objects can only
202 * appear as "values" (and not, for instance, as keys) */
203 if (o->refcount > 1) return o;
204
205 /* Currently we try to encode only strings */
206 redisAssert(o->type == REDIS_STRING);
207
208 /* Check if we can represent this string as a long integer */
209 if (isStringRepresentableAsLong(s,&value) == REDIS_ERR) return o;
210
211 /* Ok, this object can be encoded...
212 *
213 * Can I use a shared object? Only if the object is inside a given
214 * range and if the back end in use is in-memory. For disk store every
215 * object in memory used as value should be independent.
216 *
217 * Note that we also avoid using shared integers when maxmemory is used
218 * because every object needs to have a private LRU field for the LRU
219 * algorithm to work well. */
220 if (!server.ds_enabled &&
221 server.maxmemory == 0 && value >= 0 && value < REDIS_SHARED_INTEGERS &&
222 pthread_equal(pthread_self(),server.mainthread))
223 {
224 decrRefCount(o);
225 incrRefCount(shared.integers[value]);
226 return shared.integers[value];
227 } else {
228 o->encoding = REDIS_ENCODING_INT;
229 sdsfree(o->ptr);
230 o->ptr = (void*) value;
231 return o;
232 }
233 }
234
235 /* Get a decoded version of an encoded object (returned as a new object).
236 * If the object is already raw-encoded just increment the ref count. */
237 robj *getDecodedObject(robj *o) {
238 robj *dec;
239
240 if (o->encoding == REDIS_ENCODING_RAW) {
241 incrRefCount(o);
242 return o;
243 }
244 if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_INT) {
245 char buf[32];
246
247 ll2string(buf,32,(long)o->ptr);
248 dec = createStringObject(buf,strlen(buf));
249 return dec;
250 } else {
251 redisPanic("Unknown encoding type");
252 }
253 }
254
255 /* Compare two string objects via strcmp() or alike.
256 * Note that the objects may be integer-encoded. In such a case we
257 * use ll2string() to get a string representation of the numbers on the stack
258 * and compare the strings, it's much faster than calling getDecodedObject().
259 *
260 * Important note: if objects are not integer encoded, but binary-safe strings,
261 * sdscmp() from sds.c will apply memcmp() so this function ca be considered
262 * binary safe. */
263 int compareStringObjects(robj *a, robj *b) {
264 redisAssert(a->type == REDIS_STRING && b->type == REDIS_STRING);
265 char bufa[128], bufb[128], *astr, *bstr;
266 int bothsds = 1;
267
268 if (a == b) return 0;
269 if (a->encoding != REDIS_ENCODING_RAW) {
270 ll2string(bufa,sizeof(bufa),(long) a->ptr);
271 astr = bufa;
272 bothsds = 0;
273 } else {
274 astr = a->ptr;
275 }
276 if (b->encoding != REDIS_ENCODING_RAW) {
277 ll2string(bufb,sizeof(bufb),(long) b->ptr);
278 bstr = bufb;
279 bothsds = 0;
280 } else {
281 bstr = b->ptr;
282 }
283 return bothsds ? sdscmp(astr,bstr) : strcmp(astr,bstr);
284 }
285
286 /* Equal string objects return 1 if the two objects are the same from the
287 * point of view of a string comparison, otherwise 0 is returned. Note that
288 * this function is faster then checking for (compareStringObject(a,b) == 0)
289 * because it can perform some more optimization. */
290 int equalStringObjects(robj *a, robj *b) {
291 if (a->encoding != REDIS_ENCODING_RAW && b->encoding != REDIS_ENCODING_RAW){
292 return a->ptr == b->ptr;
293 } else {
294 return compareStringObjects(a,b) == 0;
295 }
296 }
297
298 size_t stringObjectLen(robj *o) {
299 redisAssert(o->type == REDIS_STRING);
300 if (o->encoding == REDIS_ENCODING_RAW) {
301 return sdslen(o->ptr);
302 } else {
303 char buf[32];
304
305 return ll2string(buf,32,(long)o->ptr);
306 }
307 }
308
309 int getDoubleFromObject(robj *o, double *target) {
310 double value;
311 char *eptr;
312
313 if (o == NULL) {
314 value = 0;
315 } else {
316 redisAssert(o->type == REDIS_STRING);
317 if (o->encoding == REDIS_ENCODING_RAW) {
318 value = strtod(o->ptr, &eptr);
319 if (eptr[0] != '\0' || isnan(value)) return REDIS_ERR;
320 } else if (o->encoding == REDIS_ENCODING_INT) {
321 value = (long)o->ptr;
322 } else {
323 redisPanic("Unknown string encoding");
324 }
325 }
326
327 *target = value;
328 return REDIS_OK;
329 }
330
331 int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const char *msg) {
332 double value;
333 if (getDoubleFromObject(o, &value) != REDIS_OK) {
334 if (msg != NULL) {
335 addReplyError(c,(char*)msg);
336 } else {
337 addReplyError(c,"value is not a double");
338 }
339 return REDIS_ERR;
340 }
341
342 *target = value;
343 return REDIS_OK;
344 }
345
346 int getLongLongFromObject(robj *o, long long *target) {
347 long long value;
348 char *eptr;
349
350 if (o == NULL) {
351 value = 0;
352 } else {
353 redisAssert(o->type == REDIS_STRING);
354 if (o->encoding == REDIS_ENCODING_RAW) {
355 value = strtoll(o->ptr, &eptr, 10);
356 if (eptr[0] != '\0') return REDIS_ERR;
357 if (errno == ERANGE && (value == LLONG_MIN || value == LLONG_MAX))
358 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 if (target) *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 addReplyError(c,(char*)msg);
375 } else {
376 addReplyError(c,"value is not an integer or out of range");
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 addReplyError(c,(char*)msg);
392 } else {
393 addReplyError(c,"value is out of range");
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 case REDIS_ENCODING_INTSET: return "intset";
411 default: return "unknown";
412 }
413 }
414
415 /* Given an object returns the min number of seconds the object was never
416 * requested, using an approximated LRU algorithm. */
417 unsigned long estimateObjectIdleTime(robj *o) {
418 if (server.lruclock >= o->lru) {
419 return (server.lruclock - o->lru) * REDIS_LRU_CLOCK_RESOLUTION;
420 } else {
421 return ((REDIS_LRU_CLOCK_MAX - o->lru) + server.lruclock) *
422 REDIS_LRU_CLOCK_RESOLUTION;
423 }
424 }