xref: /redis-3.2.3/src/object.c (revision 21736b41)
14365e5b2Santirez /* Redis Object implementation.
24365e5b2Santirez  *
34365e5b2Santirez  * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
44365e5b2Santirez  * All rights reserved.
54365e5b2Santirez  *
64365e5b2Santirez  * Redistribution and use in source and binary forms, with or without
74365e5b2Santirez  * modification, are permitted provided that the following conditions are met:
84365e5b2Santirez  *
94365e5b2Santirez  *   * Redistributions of source code must retain the above copyright notice,
104365e5b2Santirez  *     this list of conditions and the following disclaimer.
114365e5b2Santirez  *   * Redistributions in binary form must reproduce the above copyright
124365e5b2Santirez  *     notice, this list of conditions and the following disclaimer in the
134365e5b2Santirez  *     documentation and/or other materials provided with the distribution.
144365e5b2Santirez  *   * Neither the name of Redis nor the names of its contributors may be used
154365e5b2Santirez  *     to endorse or promote products derived from this software without
164365e5b2Santirez  *     specific prior written permission.
174365e5b2Santirez  *
184365e5b2Santirez  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
194365e5b2Santirez  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
204365e5b2Santirez  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
214365e5b2Santirez  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
224365e5b2Santirez  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
234365e5b2Santirez  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
244365e5b2Santirez  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
254365e5b2Santirez  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
264365e5b2Santirez  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
274365e5b2Santirez  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
284365e5b2Santirez  * POSSIBILITY OF SUCH DAMAGE.
294365e5b2Santirez  */
304365e5b2Santirez 
31cef054e8Santirez #include "server.h"
32673e1fb7SPieter Noordhuis #include <math.h>
33d93f9a86Santirez #include <ctype.h>
34e2641e09Santirez 
357c4decb1SMatt Stancliff #ifdef __CYGWIN__
367c4decb1SMatt Stancliff #define strtold(a,b) ((long double)strtod((a),(b)))
377c4decb1SMatt Stancliff #endif
387c4decb1SMatt Stancliff 
createObject(int type,void * ptr)39e2641e09Santirez robj *createObject(int type, void *ptr) {
40a9b18e54SPieter Noordhuis     robj *o = zmalloc(sizeof(*o));
41e2641e09Santirez     o->type = type;
4214ff5724Santirez     o->encoding = OBJ_ENCODING_RAW;
43e2641e09Santirez     o->ptr = ptr;
44e2641e09Santirez     o->refcount = 1;
45a9b18e54SPieter Noordhuis 
468918de92SPremysl Hruby     /* Set the LRU to the current lruclock (minutes resolution). */
4778782ed5SMatt Stancliff     o->lru = LRU_CLOCK();
48e2641e09Santirez     return o;
49e2641e09Santirez }
50e2641e09Santirez 
5114ff5724Santirez /* Create a string object with encoding OBJ_ENCODING_RAW, that is a plain
52894eba07Santirez  * string object where o->ptr points to a proper sds string. */
createRawStringObject(const char * ptr,size_t len)538e219224Santirez robj *createRawStringObject(const char *ptr, size_t len) {
5414ff5724Santirez     return createObject(OBJ_STRING,sdsnewlen(ptr,len));
55e2641e09Santirez }
56e2641e09Santirez 
5714ff5724Santirez /* Create a string object with encoding OBJ_ENCODING_EMBSTR, that is
58894eba07Santirez  * an object where the sds string is actually an unmodifiable string
59894eba07Santirez  * allocated in the same chunk as the object itself. */
createEmbeddedStringObject(const char * ptr,size_t len)608e219224Santirez robj *createEmbeddedStringObject(const char *ptr, size_t len) {
61f15df8baSOran Agra     robj *o = zmalloc(sizeof(robj)+sizeof(struct sdshdr8)+len+1);
62f15df8baSOran Agra     struct sdshdr8 *sh = (void*)(o+1);
63894eba07Santirez 
6414ff5724Santirez     o->type = OBJ_STRING;
6514ff5724Santirez     o->encoding = OBJ_ENCODING_EMBSTR;
66894eba07Santirez     o->ptr = sh+1;
67894eba07Santirez     o->refcount = 1;
6878782ed5SMatt Stancliff     o->lru = LRU_CLOCK();
69894eba07Santirez 
70894eba07Santirez     sh->len = len;
71f15df8baSOran Agra     sh->alloc = len;
72f15df8baSOran Agra     sh->flags = SDS_TYPE_8;
73894eba07Santirez     if (ptr) {
74894eba07Santirez         memcpy(sh->buf,ptr,len);
75894eba07Santirez         sh->buf[len] = '\0';
76894eba07Santirez     } else {
77894eba07Santirez         memset(sh->buf,0,len+1);
78894eba07Santirez     }
79894eba07Santirez     return o;
80894eba07Santirez }
81894eba07Santirez 
82894eba07Santirez /* Create a string object with EMBSTR encoding if it is smaller than
83894eba07Santirez  * REIDS_ENCODING_EMBSTR_SIZE_LIMIT, otherwise the RAW encoding is
840b0f872fSantirez  * used.
850b0f872fSantirez  *
860b0f872fSantirez  * The current limit of 39 is chosen so that the biggest string object
870b0f872fSantirez  * we allocate as EMBSTR will still fit into the 64 byte arena of jemalloc. */
8814ff5724Santirez #define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44
createStringObject(const char * ptr,size_t len)898e219224Santirez robj *createStringObject(const char *ptr, size_t len) {
9014ff5724Santirez     if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT)
91894eba07Santirez         return createEmbeddedStringObject(ptr,len);
92894eba07Santirez     else
93894eba07Santirez         return createRawStringObject(ptr,len);
94894eba07Santirez }
95894eba07Santirez 
createStringObjectFromLongLong(long long value)96e2641e09Santirez robj *createStringObjectFromLongLong(long long value) {
97e2641e09Santirez     robj *o;
9832f80e2fSantirez     if (value >= 0 && value < OBJ_SHARED_INTEGERS) {
99e2641e09Santirez         incrRefCount(shared.integers[value]);
100e2641e09Santirez         o = shared.integers[value];
101e2641e09Santirez     } else {
102e2641e09Santirez         if (value >= LONG_MIN && value <= LONG_MAX) {
10314ff5724Santirez             o = createObject(OBJ_STRING, NULL);
10414ff5724Santirez             o->encoding = OBJ_ENCODING_INT;
105e2641e09Santirez             o->ptr = (void*)((long)value);
106e2641e09Santirez         } else {
10714ff5724Santirez             o = createObject(OBJ_STRING,sdsfromlonglong(value));
108e2641e09Santirez         }
109e2641e09Santirez     }
110e2641e09Santirez     return o;
111e2641e09Santirez }
112e2641e09Santirez 
11392c5ab40Santirez /* Create a string object from a long double. If humanfriendly is non-zero
11492c5ab40Santirez  * it does not use exponential format and trims trailing zeroes at the end,
11536320262Santirez  * however this results in loss of precision. Otherwise exp format is used
11636320262Santirez  * and the output of snprintf() is not modified.
11736320262Santirez  *
11836320262Santirez  * The 'humanfriendly' option is used for INCRBYFLOAT and HINCRBYFLOAT. */
createStringObjectFromLongDouble(long double value,int humanfriendly)11992c5ab40Santirez robj *createStringObjectFromLongDouble(long double value, int humanfriendly) {
1207b558b1dSantirez     char buf[256];
1215574b53eSantirez     int len;
1225574b53eSantirez 
12336320262Santirez     if (isinf(value)) {
12436320262Santirez         /* Libc in odd systems (Hi Solaris!) will format infinite in a
12536320262Santirez          * different way, so better to handle it in an explicit way. */
12636320262Santirez         if (value > 0) {
12736320262Santirez             memcpy(buf,"inf",3);
12836320262Santirez             len = 3;
12936320262Santirez         } else {
13036320262Santirez             memcpy(buf,"-inf",4);
13136320262Santirez             len = 4;
13236320262Santirez         }
13336320262Santirez     } else if (humanfriendly) {
1345574b53eSantirez         /* We use 17 digits precision since with 128 bit floats that precision
13536320262Santirez          * after rounding is able to represent most small decimal numbers in a
13636320262Santirez          * way that is "non surprising" for the user (that is, most small
13736320262Santirez          * decimal numbers will be represented in a way that when converted
13836320262Santirez          * back into a string are exactly the same as what the user typed.) */
1391f6146dfSantirez         len = snprintf(buf,sizeof(buf),"%.17Lf", value);
1401f6146dfSantirez         /* Now remove trailing zeroes after the '.' */
1417b558b1dSantirez         if (strchr(buf,'.') != NULL) {
1427b558b1dSantirez             char *p = buf+len-1;
1431f6146dfSantirez             while(*p == '0') {
1441f6146dfSantirez                 p--;
1451f6146dfSantirez                 len--;
1461f6146dfSantirez             }
1471f6146dfSantirez             if (*p == '.') len--;
1481f6146dfSantirez         }
14992c5ab40Santirez     } else {
15092c5ab40Santirez         len = snprintf(buf,sizeof(buf),"%.17Lg", value);
15192c5ab40Santirez     }
1525574b53eSantirez     return createStringObject(buf,len);
1535574b53eSantirez }
1545574b53eSantirez 
155894eba07Santirez /* Duplicate a string object, with the guarantee that the returned object
156894eba07Santirez  * has the same encoding as the original one.
157894eba07Santirez  *
158894eba07Santirez  * This function also guarantees that duplicating a small integere object
159894eba07Santirez  * (or a string object that contains a representation of a small integer)
160894eba07Santirez  * will always result in a fresh object that is unshared (refcount == 1).
161894eba07Santirez  *
162894eba07Santirez  * The resulting object always has refcount set to 1. */
dupStringObject(robj * o)163e2641e09Santirez robj *dupStringObject(robj *o) {
164894eba07Santirez     robj *d;
165894eba07Santirez 
1662d9e3eb1Santirez     serverAssert(o->type == OBJ_STRING);
167894eba07Santirez 
168894eba07Santirez     switch(o->encoding) {
16914ff5724Santirez     case OBJ_ENCODING_RAW:
170894eba07Santirez         return createRawStringObject(o->ptr,sdslen(o->ptr));
17114ff5724Santirez     case OBJ_ENCODING_EMBSTR:
172894eba07Santirez         return createEmbeddedStringObject(o->ptr,sdslen(o->ptr));
17314ff5724Santirez     case OBJ_ENCODING_INT:
17414ff5724Santirez         d = createObject(OBJ_STRING, NULL);
17514ff5724Santirez         d->encoding = OBJ_ENCODING_INT;
176894eba07Santirez         d->ptr = o->ptr;
177894eba07Santirez         return d;
178894eba07Santirez     default:
17932f80e2fSantirez         serverPanic("Wrong encoding.");
180894eba07Santirez         break;
181894eba07Santirez     }
182e2641e09Santirez }
183e2641e09Santirez 
createQuicklistObject(void)1845e362b84SMatt Stancliff robj *createQuicklistObject(void) {
1855e362b84SMatt Stancliff     quicklist *l = quicklistCreate();
18614ff5724Santirez     robj *o = createObject(OBJ_LIST,l);
18714ff5724Santirez     o->encoding = OBJ_ENCODING_QUICKLIST;
188e2641e09Santirez     return o;
189e2641e09Santirez }
190e2641e09Santirez 
createZiplistObject(void)191e2641e09Santirez robj *createZiplistObject(void) {
192e2641e09Santirez     unsigned char *zl = ziplistNew();
19314ff5724Santirez     robj *o = createObject(OBJ_LIST,zl);
19414ff5724Santirez     o->encoding = OBJ_ENCODING_ZIPLIST;
195e2641e09Santirez     return o;
196e2641e09Santirez }
197e2641e09Santirez 
createSetObject(void)198e2641e09Santirez robj *createSetObject(void) {
199e2641e09Santirez     dict *d = dictCreate(&setDictType,NULL);
20014ff5724Santirez     robj *o = createObject(OBJ_SET,d);
20114ff5724Santirez     o->encoding = OBJ_ENCODING_HT;
20296ffb2feSPieter Noordhuis     return o;
20396ffb2feSPieter Noordhuis }
20496ffb2feSPieter Noordhuis 
createIntsetObject(void)20596ffb2feSPieter Noordhuis robj *createIntsetObject(void) {
20696ffb2feSPieter Noordhuis     intset *is = intsetNew();
20714ff5724Santirez     robj *o = createObject(OBJ_SET,is);
20814ff5724Santirez     o->encoding = OBJ_ENCODING_INTSET;
20996ffb2feSPieter Noordhuis     return o;
210e2641e09Santirez }
211e2641e09Santirez 
createHashObject(void)212e2641e09Santirez robj *createHashObject(void) {
213ebd85e9aSPieter Noordhuis     unsigned char *zl = ziplistNew();
21414ff5724Santirez     robj *o = createObject(OBJ_HASH, zl);
21514ff5724Santirez     o->encoding = OBJ_ENCODING_ZIPLIST;
216e2641e09Santirez     return o;
217e2641e09Santirez }
218e2641e09Santirez 
createZsetObject(void)219e2641e09Santirez robj *createZsetObject(void) {
220e2641e09Santirez     zset *zs = zmalloc(sizeof(*zs));
2210b7f6d09Santirez     robj *o;
222e2641e09Santirez 
223e2641e09Santirez     zs->dict = dictCreate(&zsetDictType,NULL);
224e2641e09Santirez     zs->zsl = zslCreate();
22514ff5724Santirez     o = createObject(OBJ_ZSET,zs);
22614ff5724Santirez     o->encoding = OBJ_ENCODING_SKIPLIST;
2270b7f6d09Santirez     return o;
228e2641e09Santirez }
229e2641e09Santirez 
createZsetZiplistObject(void)2309e7cee0eSPieter Noordhuis robj *createZsetZiplistObject(void) {
2319e7cee0eSPieter Noordhuis     unsigned char *zl = ziplistNew();
23214ff5724Santirez     robj *o = createObject(OBJ_ZSET,zl);
23314ff5724Santirez     o->encoding = OBJ_ENCODING_ZIPLIST;
2349e7cee0eSPieter Noordhuis     return o;
2359e7cee0eSPieter Noordhuis }
2369e7cee0eSPieter Noordhuis 
freeStringObject(robj * o)237e2641e09Santirez void freeStringObject(robj *o) {
23814ff5724Santirez     if (o->encoding == OBJ_ENCODING_RAW) {
239e2641e09Santirez         sdsfree(o->ptr);
240e2641e09Santirez     }
241e2641e09Santirez }
242e2641e09Santirez 
freeListObject(robj * o)243e2641e09Santirez void freeListObject(robj *o) {
244e2641e09Santirez     switch (o->encoding) {
24514ff5724Santirez     case OBJ_ENCODING_QUICKLIST:
2465e362b84SMatt Stancliff         quicklistRelease(o->ptr);
247e2641e09Santirez         break;
248e2641e09Santirez     default:
24932f80e2fSantirez         serverPanic("Unknown list encoding type");
250e2641e09Santirez     }
251e2641e09Santirez }
252e2641e09Santirez 
freeSetObject(robj * o)253e2641e09Santirez void freeSetObject(robj *o) {
25496ffb2feSPieter Noordhuis     switch (o->encoding) {
25514ff5724Santirez     case OBJ_ENCODING_HT:
256e2641e09Santirez         dictRelease((dict*) o->ptr);
25796ffb2feSPieter Noordhuis         break;
25814ff5724Santirez     case OBJ_ENCODING_INTSET:
25996ffb2feSPieter Noordhuis         zfree(o->ptr);
26096ffb2feSPieter Noordhuis         break;
26196ffb2feSPieter Noordhuis     default:
26232f80e2fSantirez         serverPanic("Unknown set encoding type");
26396ffb2feSPieter Noordhuis     }
264e2641e09Santirez }
265e2641e09Santirez 
freeZsetObject(robj * o)266e2641e09Santirez void freeZsetObject(robj *o) {
2670f23eb3bSPieter Noordhuis     zset *zs;
2680f23eb3bSPieter Noordhuis     switch (o->encoding) {
26914ff5724Santirez     case OBJ_ENCODING_SKIPLIST:
2700f23eb3bSPieter Noordhuis         zs = o->ptr;
271e2641e09Santirez         dictRelease(zs->dict);
272e2641e09Santirez         zslFree(zs->zsl);
273e2641e09Santirez         zfree(zs);
2740f23eb3bSPieter Noordhuis         break;
27514ff5724Santirez     case OBJ_ENCODING_ZIPLIST:
2760f23eb3bSPieter Noordhuis         zfree(o->ptr);
2770f23eb3bSPieter Noordhuis         break;
2780f23eb3bSPieter Noordhuis     default:
27932f80e2fSantirez         serverPanic("Unknown sorted set encoding");
2800f23eb3bSPieter Noordhuis     }
281e2641e09Santirez }
282e2641e09Santirez 
freeHashObject(robj * o)283e2641e09Santirez void freeHashObject(robj *o) {
284e2641e09Santirez     switch (o->encoding) {
28514ff5724Santirez     case OBJ_ENCODING_HT:
286e2641e09Santirez         dictRelease((dict*) o->ptr);
287e2641e09Santirez         break;
28814ff5724Santirez     case OBJ_ENCODING_ZIPLIST:
289e2641e09Santirez         zfree(o->ptr);
290e2641e09Santirez         break;
291e2641e09Santirez     default:
29232f80e2fSantirez         serverPanic("Unknown hash encoding type");
293e2641e09Santirez         break;
294e2641e09Santirez     }
295e2641e09Santirez }
296e2641e09Santirez 
incrRefCount(robj * o)297e2641e09Santirez void incrRefCount(robj *o) {
298e2641e09Santirez     o->refcount++;
299e2641e09Santirez }
300e2641e09Santirez 
decrRefCount(robj * o)3018766e810Santirez void decrRefCount(robj *o) {
30232f80e2fSantirez     if (o->refcount <= 0) serverPanic("decrRefCount against refcount <= 0");
303936c4ab6Santirez     if (o->refcount == 1) {
304e2641e09Santirez         switch(o->type) {
30514ff5724Santirez         case OBJ_STRING: freeStringObject(o); break;
30614ff5724Santirez         case OBJ_LIST: freeListObject(o); break;
30714ff5724Santirez         case OBJ_SET: freeSetObject(o); break;
30814ff5724Santirez         case OBJ_ZSET: freeZsetObject(o); break;
30914ff5724Santirez         case OBJ_HASH: freeHashObject(o); break;
31032f80e2fSantirez         default: serverPanic("Unknown object type"); break;
311e2641e09Santirez         }
312e2641e09Santirez         zfree(o);
313936c4ab6Santirez     } else {
314936c4ab6Santirez         o->refcount--;
315e2641e09Santirez     }
316e2641e09Santirez }
317e2641e09Santirez 
3188766e810Santirez /* This variant of decrRefCount() gets its argument as void, and is useful
3198766e810Santirez  * as free method in data structures that expect a 'void free_object(void*)'
3208766e810Santirez  * prototype for the free method. */
decrRefCountVoid(void * o)3218766e810Santirez void decrRefCountVoid(void *o) {
3228766e810Santirez     decrRefCount(o);
3238766e810Santirez }
3248766e810Santirez 
3254dd444bbSantirez /* This function set the ref count to zero without freeing the object.
3264dd444bbSantirez  * It is useful in order to pass a new object to functions incrementing
3274dd444bbSantirez  * the ref count of the received object. Example:
3284dd444bbSantirez  *
3294dd444bbSantirez  *    functionThatWillIncrementRefCount(resetRefCount(CreateObject(...)));
3304dd444bbSantirez  *
3314dd444bbSantirez  * Otherwise you need to resort to the less elegant pattern:
3324dd444bbSantirez  *
3334dd444bbSantirez  *    *obj = createObject(...);
3344dd444bbSantirez  *    functionThatWillIncrementRefCount(obj);
3354dd444bbSantirez  *    decrRefCount(obj);
3364dd444bbSantirez  */
resetRefCount(robj * obj)3374dd444bbSantirez robj *resetRefCount(robj *obj) {
3384dd444bbSantirez     obj->refcount = 0;
3394dd444bbSantirez     return obj;
3404dd444bbSantirez }
3414dd444bbSantirez 
checkType(client * c,robj * o,int type)342554bd0e7Santirez int checkType(client *c, robj *o, int type) {
343e2641e09Santirez     if (o->type != type) {
344e2641e09Santirez         addReply(c,shared.wrongtypeerr);
345e2641e09Santirez         return 1;
346e2641e09Santirez     }
347e2641e09Santirez     return 0;
348e2641e09Santirez }
349e2641e09Santirez 
isObjectRepresentableAsLongLong(robj * o,long long * llval)3505d081931SPieter Noordhuis int isObjectRepresentableAsLongLong(robj *o, long long *llval) {
3512d9e3eb1Santirez     serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);
35214ff5724Santirez     if (o->encoding == OBJ_ENCODING_INT) {
3535d081931SPieter Noordhuis         if (llval) *llval = (long) o->ptr;
35440eb548aSantirez         return C_OK;
3555d081931SPieter Noordhuis     } else {
35640eb548aSantirez         return string2ll(o->ptr,sdslen(o->ptr),llval) ? C_OK : C_ERR;
3575d081931SPieter Noordhuis     }
3585d081931SPieter Noordhuis }
3595d081931SPieter Noordhuis 
360e2641e09Santirez /* Try to encode a string object in order to save space */
tryObjectEncoding(robj * o)361e2641e09Santirez robj *tryObjectEncoding(robj *o) {
362e2641e09Santirez     long value;
363e2641e09Santirez     sds s = o->ptr;
3646672bc8bSantirez     size_t len;
365e2641e09Santirez 
3661c12bcbcSantirez     /* Make sure this is a string object, the only type we encode
3671c12bcbcSantirez      * in this function. Other types use encoded memory efficient
3681c12bcbcSantirez      * representations but are handled by the commands implementing
3691c12bcbcSantirez      * the type. */
3702d9e3eb1Santirez     serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);
3711c12bcbcSantirez 
3721c12bcbcSantirez     /* We try some specialized encoding only for objects that are
3731c12bcbcSantirez      * RAW or EMBSTR encoded, in other words objects that are still
3741c12bcbcSantirez      * in represented by an actually array of chars. */
3751c12bcbcSantirez     if (!sdsEncodedObject(o)) return o;
376e2641e09Santirez 
377e2641e09Santirez     /* It's not safe to encode shared objects: shared objects can be shared
3781c12bcbcSantirez      * everywhere in the "object space" of Redis and may end in places where
3791c12bcbcSantirez      * they are not handled. We handle them only as values in the keyspace. */
380e2641e09Santirez      if (o->refcount > 1) return o;
381e2641e09Santirez 
3826672bc8bSantirez     /* Check if we can represent this string as a long integer.
3837f1e1caeStielei      * Note that we are sure that a string larger than 20 chars is not
3841c12bcbcSantirez      * representable as a 32 nor 64 bit integer. */
3856672bc8bSantirez     len = sdslen(s);
3867f1e1caeStielei     if (len <= 20 && string2l(s,len,&value)) {
3871c12bcbcSantirez         /* This object is encodable as a long. Try to use a shared object.
3881c12bcbcSantirez          * Note that we avoid using shared integers when maxmemory is used
3891c12bcbcSantirez          * because every object needs to have a private LRU field for the LRU
3901c12bcbcSantirez          * algorithm to work well. */
391b3c042cdSantirez         if ((server.maxmemory == 0 ||
39232f80e2fSantirez              (server.maxmemory_policy != MAXMEMORY_VOLATILE_LRU &&
39332f80e2fSantirez               server.maxmemory_policy != MAXMEMORY_ALLKEYS_LRU)) &&
3941c12bcbcSantirez             value >= 0 &&
39532f80e2fSantirez             value < OBJ_SHARED_INTEGERS)
3961c12bcbcSantirez         {
3971c12bcbcSantirez             decrRefCount(o);
3981c12bcbcSantirez             incrRefCount(shared.integers[value]);
3991c12bcbcSantirez             return shared.integers[value];
4001c12bcbcSantirez         } else {
40114ff5724Santirez             if (o->encoding == OBJ_ENCODING_RAW) sdsfree(o->ptr);
40214ff5724Santirez             o->encoding = OBJ_ENCODING_INT;
4031c12bcbcSantirez             o->ptr = (void*) value;
4041c12bcbcSantirez             return o;
4051c12bcbcSantirez         }
4061c12bcbcSantirez     }
4071c12bcbcSantirez 
4081c12bcbcSantirez     /* If the string is small and is still RAW encoded,
4091c12bcbcSantirez      * try the EMBSTR encoding which is more efficient.
4101c12bcbcSantirez      * In this representation the object and the SDS string are allocated
4111c12bcbcSantirez      * in the same chunk of memory to save space and cache misses. */
41214ff5724Santirez     if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT) {
4131c12bcbcSantirez         robj *emb;
4141c12bcbcSantirez 
41514ff5724Santirez         if (o->encoding == OBJ_ENCODING_EMBSTR) return o;
4161c12bcbcSantirez         emb = createEmbeddedStringObject(s,sdslen(s));
417894eba07Santirez         decrRefCount(o);
418894eba07Santirez         return emb;
4191c12bcbcSantirez     }
4201c12bcbcSantirez 
42138b2e65eSantirez     /* We can't encode the object...
42238b2e65eSantirez      *
42338b2e65eSantirez      * Do the last try, and at least optimize the SDS string inside
42438b2e65eSantirez      * the string object to require little space, in case there
42538b2e65eSantirez      * is more than 10% of free space at the end of the SDS string.
42638b2e65eSantirez      *
42738b2e65eSantirez      * We do that only for relatively large strings as this branch
42838b2e65eSantirez      * is only entered if the length of the string is greater than
42914ff5724Santirez      * OBJ_ENCODING_EMBSTR_SIZE_LIMIT. */
43014ff5724Santirez     if (o->encoding == OBJ_ENCODING_RAW &&
43138b2e65eSantirez         sdsavail(s) > len/10)
43238b2e65eSantirez     {
43338b2e65eSantirez         o->ptr = sdsRemoveFreeSpace(o->ptr);
43438b2e65eSantirez     }
4351c12bcbcSantirez 
43638b2e65eSantirez     /* Return the original object. */
437894eba07Santirez     return o;
438894eba07Santirez }
439e2641e09Santirez 
440e2641e09Santirez /* Get a decoded version of an encoded object (returned as a new object).
441e2641e09Santirez  * If the object is already raw-encoded just increment the ref count. */
getDecodedObject(robj * o)442e2641e09Santirez robj *getDecodedObject(robj *o) {
443e2641e09Santirez     robj *dec;
444e2641e09Santirez 
445894eba07Santirez     if (sdsEncodedObject(o)) {
446e2641e09Santirez         incrRefCount(o);
447e2641e09Santirez         return o;
448e2641e09Santirez     }
44914ff5724Santirez     if (o->type == OBJ_STRING && o->encoding == OBJ_ENCODING_INT) {
450e2641e09Santirez         char buf[32];
451e2641e09Santirez 
452e2641e09Santirez         ll2string(buf,32,(long)o->ptr);
453e2641e09Santirez         dec = createStringObject(buf,strlen(buf));
454e2641e09Santirez         return dec;
455e2641e09Santirez     } else {
45632f80e2fSantirez         serverPanic("Unknown encoding type");
457e2641e09Santirez     }
458e2641e09Santirez }
459e2641e09Santirez 
46081e55ec0Santirez /* Compare two string objects via strcmp() or strcoll() depending on flags.
461e2641e09Santirez  * Note that the objects may be integer-encoded. In such a case we
462e2641e09Santirez  * use ll2string() to get a string representation of the numbers on the stack
463e2641e09Santirez  * and compare the strings, it's much faster than calling getDecodedObject().
464e2641e09Santirez  *
46581e55ec0Santirez  * Important note: when REDIS_COMPARE_BINARY is used a binary-safe comparison
46681e55ec0Santirez  * is used. */
46781e55ec0Santirez 
46881e55ec0Santirez #define REDIS_COMPARE_BINARY (1<<0)
46981e55ec0Santirez #define REDIS_COMPARE_COLL (1<<1)
47081e55ec0Santirez 
compareStringObjectsWithFlags(robj * a,robj * b,int flags)47181e55ec0Santirez int compareStringObjectsWithFlags(robj *a, robj *b, int flags) {
4722d9e3eb1Santirez     serverAssertWithInfo(NULL,a,a->type == OBJ_STRING && b->type == OBJ_STRING);
473e2641e09Santirez     char bufa[128], bufb[128], *astr, *bstr;
47481e55ec0Santirez     size_t alen, blen, minlen;
475e2641e09Santirez 
476e2641e09Santirez     if (a == b) return 0;
477894eba07Santirez     if (sdsEncodedObject(a)) {
478894eba07Santirez         astr = a->ptr;
479894eba07Santirez         alen = sdslen(astr);
480894eba07Santirez     } else {
48181e55ec0Santirez         alen = ll2string(bufa,sizeof(bufa),(long) a->ptr);
482e2641e09Santirez         astr = bufa;
483e2641e09Santirez     }
484894eba07Santirez     if (sdsEncodedObject(b)) {
485894eba07Santirez         bstr = b->ptr;
486894eba07Santirez         blen = sdslen(bstr);
487894eba07Santirez     } else {
48881e55ec0Santirez         blen = ll2string(bufb,sizeof(bufb),(long) b->ptr);
489e2641e09Santirez         bstr = bufb;
490e2641e09Santirez     }
49181e55ec0Santirez     if (flags & REDIS_COMPARE_COLL) {
49281e55ec0Santirez         return strcoll(astr,bstr);
49381e55ec0Santirez     } else {
49481e55ec0Santirez         int cmp;
49581e55ec0Santirez 
49681e55ec0Santirez         minlen = (alen < blen) ? alen : blen;
49781e55ec0Santirez         cmp = memcmp(astr,bstr,minlen);
49881e55ec0Santirez         if (cmp == 0) return alen-blen;
49981e55ec0Santirez         return cmp;
50081e55ec0Santirez     }
50181e55ec0Santirez }
50281e55ec0Santirez 
50381e55ec0Santirez /* Wrapper for compareStringObjectsWithFlags() using binary comparison. */
compareStringObjects(robj * a,robj * b)50481e55ec0Santirez int compareStringObjects(robj *a, robj *b) {
50581e55ec0Santirez     return compareStringObjectsWithFlags(a,b,REDIS_COMPARE_BINARY);
50681e55ec0Santirez }
50781e55ec0Santirez 
50881e55ec0Santirez /* Wrapper for compareStringObjectsWithFlags() using collation. */
collateStringObjects(robj * a,robj * b)50981e55ec0Santirez int collateStringObjects(robj *a, robj *b) {
51081e55ec0Santirez     return compareStringObjectsWithFlags(a,b,REDIS_COMPARE_COLL);
511e2641e09Santirez }
512e2641e09Santirez 
513e2641e09Santirez /* Equal string objects return 1 if the two objects are the same from the
514e2641e09Santirez  * point of view of a string comparison, otherwise 0 is returned. Note that
515e2641e09Santirez  * this function is faster then checking for (compareStringObject(a,b) == 0)
516e2641e09Santirez  * because it can perform some more optimization. */
equalStringObjects(robj * a,robj * b)517e2641e09Santirez int equalStringObjects(robj *a, robj *b) {
51814ff5724Santirez     if (a->encoding == OBJ_ENCODING_INT &&
51914ff5724Santirez         b->encoding == OBJ_ENCODING_INT){
520894eba07Santirez         /* If both strings are integer encoded just check if the stored
521894eba07Santirez          * long is the same. */
522e2641e09Santirez         return a->ptr == b->ptr;
523e2641e09Santirez     } else {
524e2641e09Santirez         return compareStringObjects(a,b) == 0;
525e2641e09Santirez     }
526e2641e09Santirez }
527e2641e09Santirez 
stringObjectLen(robj * o)528e2641e09Santirez size_t stringObjectLen(robj *o) {
5292d9e3eb1Santirez     serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);
530894eba07Santirez     if (sdsEncodedObject(o)) {
531e2641e09Santirez         return sdslen(o->ptr);
532e2641e09Santirez     } else {
5334f56f035Santirez         return sdigits10((long)o->ptr);
534e2641e09Santirez     }
535e2641e09Santirez }
536e2641e09Santirez 
getDoubleFromObject(robj * o,double * target)537e2641e09Santirez int getDoubleFromObject(robj *o, double *target) {
538e2641e09Santirez     double value;
539e2641e09Santirez     char *eptr;
540e2641e09Santirez 
541e2641e09Santirez     if (o == NULL) {
542e2641e09Santirez         value = 0;
543e2641e09Santirez     } else {
5442d9e3eb1Santirez         serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);
545894eba07Santirez         if (sdsEncodedObject(o)) {
5465574b53eSantirez             errno = 0;
547e2641e09Santirez             value = strtod(o->ptr, &eptr);
5489d520a7fSantirez             if (isspace(((char*)o->ptr)[0]) ||
5499d520a7fSantirez                 eptr[0] != '\0' ||
5509d520a7fSantirez                 (errno == ERANGE &&
5519d520a7fSantirez                     (value == HUGE_VAL || value == -HUGE_VAL || value == 0)) ||
5529d520a7fSantirez                 errno == EINVAL ||
5539d520a7fSantirez                 isnan(value))
55440eb548aSantirez                 return C_ERR;
55514ff5724Santirez         } else if (o->encoding == OBJ_ENCODING_INT) {
556e2641e09Santirez             value = (long)o->ptr;
557e2641e09Santirez         } else {
55832f80e2fSantirez             serverPanic("Unknown string encoding");
559e2641e09Santirez         }
560e2641e09Santirez     }
561e2641e09Santirez     *target = value;
56240eb548aSantirez     return C_OK;
563e2641e09Santirez }
564e2641e09Santirez 
getDoubleFromObjectOrReply(client * c,robj * o,double * target,const char * msg)565554bd0e7Santirez int getDoubleFromObjectOrReply(client *c, robj *o, double *target, const char *msg) {
566e2641e09Santirez     double value;
56740eb548aSantirez     if (getDoubleFromObject(o, &value) != C_OK) {
568e2641e09Santirez         if (msg != NULL) {
5693ab20376SPieter Noordhuis             addReplyError(c,(char*)msg);
570e2641e09Santirez         } else {
5715574b53eSantirez             addReplyError(c,"value is not a valid float");
5725574b53eSantirez         }
57340eb548aSantirez         return C_ERR;
5745574b53eSantirez     }
5755574b53eSantirez     *target = value;
57640eb548aSantirez     return C_OK;
5775574b53eSantirez }
5785574b53eSantirez 
getLongDoubleFromObject(robj * o,long double * target)5795574b53eSantirez int getLongDoubleFromObject(robj *o, long double *target) {
5805574b53eSantirez     long double value;
5815574b53eSantirez     char *eptr;
5825574b53eSantirez 
5835574b53eSantirez     if (o == NULL) {
5845574b53eSantirez         value = 0;
5855574b53eSantirez     } else {
5862d9e3eb1Santirez         serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);
587894eba07Santirez         if (sdsEncodedObject(o)) {
5885574b53eSantirez             errno = 0;
5895574b53eSantirez             value = strtold(o->ptr, &eptr);
590d93f9a86Santirez             if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' ||
591d93f9a86Santirez                 errno == ERANGE || isnan(value))
59240eb548aSantirez                 return C_ERR;
59314ff5724Santirez         } else if (o->encoding == OBJ_ENCODING_INT) {
5945574b53eSantirez             value = (long)o->ptr;
5955574b53eSantirez         } else {
59632f80e2fSantirez             serverPanic("Unknown string encoding");
5975574b53eSantirez         }
5985574b53eSantirez     }
5995574b53eSantirez     *target = value;
60040eb548aSantirez     return C_OK;
6015574b53eSantirez }
6025574b53eSantirez 
getLongDoubleFromObjectOrReply(client * c,robj * o,long double * target,const char * msg)603554bd0e7Santirez int getLongDoubleFromObjectOrReply(client *c, robj *o, long double *target, const char *msg) {
6045574b53eSantirez     long double value;
60540eb548aSantirez     if (getLongDoubleFromObject(o, &value) != C_OK) {
6065574b53eSantirez         if (msg != NULL) {
6075574b53eSantirez             addReplyError(c,(char*)msg);
6085574b53eSantirez         } else {
6095574b53eSantirez             addReplyError(c,"value is not a valid float");
610e2641e09Santirez         }
61140eb548aSantirez         return C_ERR;
612e2641e09Santirez     }
613e2641e09Santirez     *target = value;
61440eb548aSantirez     return C_OK;
615e2641e09Santirez }
616e2641e09Santirez 
getLongLongFromObject(robj * o,long long * target)617e2641e09Santirez int getLongLongFromObject(robj *o, long long *target) {
618e2641e09Santirez     long long value;
619e2641e09Santirez 
620e2641e09Santirez     if (o == NULL) {
621e2641e09Santirez         value = 0;
622e2641e09Santirez     } else {
6232d9e3eb1Santirez         serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);
624894eba07Santirez         if (sdsEncodedObject(o)) {
625*21736b41Santirez             if (string2ll(o->ptr,sdslen(o->ptr),&value) == 0) return C_ERR;
62614ff5724Santirez         } else if (o->encoding == OBJ_ENCODING_INT) {
627e2641e09Santirez             value = (long)o->ptr;
628e2641e09Santirez         } else {
62932f80e2fSantirez             serverPanic("Unknown string encoding");
630e2641e09Santirez         }
631e2641e09Santirez     }
63296ffb2feSPieter Noordhuis     if (target) *target = value;
63340eb548aSantirez     return C_OK;
634e2641e09Santirez }
635e2641e09Santirez 
getLongLongFromObjectOrReply(client * c,robj * o,long long * target,const char * msg)636554bd0e7Santirez int getLongLongFromObjectOrReply(client *c, robj *o, long long *target, const char *msg) {
637e2641e09Santirez     long long value;
63840eb548aSantirez     if (getLongLongFromObject(o, &value) != C_OK) {
639e2641e09Santirez         if (msg != NULL) {
6403ab20376SPieter Noordhuis             addReplyError(c,(char*)msg);
641e2641e09Santirez         } else {
6423ab20376SPieter Noordhuis             addReplyError(c,"value is not an integer or out of range");
643e2641e09Santirez         }
64440eb548aSantirez         return C_ERR;
645e2641e09Santirez     }
646e2641e09Santirez     *target = value;
64740eb548aSantirez     return C_OK;
648e2641e09Santirez }
649e2641e09Santirez 
getLongFromObjectOrReply(client * c,robj * o,long * target,const char * msg)650554bd0e7Santirez int getLongFromObjectOrReply(client *c, robj *o, long *target, const char *msg) {
651e2641e09Santirez     long long value;
652e2641e09Santirez 
65340eb548aSantirez     if (getLongLongFromObjectOrReply(c, o, &value, msg) != C_OK) return C_ERR;
654e2641e09Santirez     if (value < LONG_MIN || value > LONG_MAX) {
655e2641e09Santirez         if (msg != NULL) {
6563ab20376SPieter Noordhuis             addReplyError(c,(char*)msg);
657e2641e09Santirez         } else {
6583ab20376SPieter Noordhuis             addReplyError(c,"value is out of range");
659e2641e09Santirez         }
66040eb548aSantirez         return C_ERR;
661e2641e09Santirez     }
662e2641e09Santirez     *target = value;
66340eb548aSantirez     return C_OK;
664e2641e09Santirez }
665e2641e09Santirez 
strEncoding(int encoding)666e2641e09Santirez char *strEncoding(int encoding) {
667e2641e09Santirez     switch(encoding) {
66814ff5724Santirez     case OBJ_ENCODING_RAW: return "raw";
66914ff5724Santirez     case OBJ_ENCODING_INT: return "int";
67014ff5724Santirez     case OBJ_ENCODING_HT: return "hashtable";
67114ff5724Santirez     case OBJ_ENCODING_QUICKLIST: return "quicklist";
67214ff5724Santirez     case OBJ_ENCODING_ZIPLIST: return "ziplist";
67314ff5724Santirez     case OBJ_ENCODING_INTSET: return "intset";
67414ff5724Santirez     case OBJ_ENCODING_SKIPLIST: return "skiplist";
67514ff5724Santirez     case OBJ_ENCODING_EMBSTR: return "embstr";
676e2641e09Santirez     default: return "unknown";
677e2641e09Santirez     }
678e2641e09Santirez }
679ef59a8bcSantirez 
6806d5790d6Santirez /* Given an object returns the min number of milliseconds the object was never
681ef59a8bcSantirez  * requested, using an approximated LRU algorithm. */
estimateObjectIdleTime(robj * o)682d77e2316Santirez unsigned long long estimateObjectIdleTime(robj *o) {
683ad6b0f70Santirez     unsigned long long lruclock = LRU_CLOCK();
684ad6b0f70Santirez     if (lruclock >= o->lru) {
68532f80e2fSantirez         return (lruclock - o->lru) * LRU_CLOCK_RESOLUTION;
686ef59a8bcSantirez     } else {
68732f80e2fSantirez         return (lruclock + (LRU_CLOCK_MAX - o->lru)) *
68832f80e2fSantirez                     LRU_CLOCK_RESOLUTION;
689ef59a8bcSantirez     }
690ef59a8bcSantirez }
691ece74202Santirez 
692543ede03Santirez /* This is a helper function for the OBJECT command. We need to lookup keys
693ece74202Santirez  * without any modification of LRU or other parameters. */
objectCommandLookup(client * c,robj * key)694554bd0e7Santirez robj *objectCommandLookup(client *c, robj *key) {
695ece74202Santirez     dictEntry *de;
696ece74202Santirez 
697ece74202Santirez     if ((de = dictFind(c->db->dict,key->ptr)) == NULL) return NULL;
698c0ba9ebeSantirez     return (robj*) dictGetVal(de);
699ece74202Santirez }
700ece74202Santirez 
objectCommandLookupOrReply(client * c,robj * key,robj * reply)701554bd0e7Santirez robj *objectCommandLookupOrReply(client *c, robj *key, robj *reply) {
702ece74202Santirez     robj *o = objectCommandLookup(c,key);
703ece74202Santirez 
704ece74202Santirez     if (!o) addReply(c, reply);
705ece74202Santirez     return o;
706ece74202Santirez }
707ece74202Santirez 
708ece74202Santirez /* Object command allows to inspect the internals of an Redis Object.
709ce8a68b1Smichael-grunder  * Usage: OBJECT <refcount|encoding|idletime> <key> */
objectCommand(client * c)710554bd0e7Santirez void objectCommand(client *c) {
711ece74202Santirez     robj *o;
712ece74202Santirez 
713ece74202Santirez     if (!strcasecmp(c->argv[1]->ptr,"refcount") && c->argc == 3) {
714ece74202Santirez         if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
715ece74202Santirez                 == NULL) return;
716ece74202Santirez         addReplyLongLong(c,o->refcount);
717ece74202Santirez     } else if (!strcasecmp(c->argv[1]->ptr,"encoding") && c->argc == 3) {
718ece74202Santirez         if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
719ece74202Santirez                 == NULL) return;
720ece74202Santirez         addReplyBulkCString(c,strEncoding(o->encoding));
721ece74202Santirez     } else if (!strcasecmp(c->argv[1]->ptr,"idletime") && c->argc == 3) {
722ece74202Santirez         if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
723ece74202Santirez                 == NULL) return;
7246d5790d6Santirez         addReplyLongLong(c,estimateObjectIdleTime(o)/1000);
725ece74202Santirez     } else {
726ece74202Santirez         addReplyError(c,"Syntax error. Try OBJECT (refcount|encoding|idletime)");
727ece74202Santirez     }
728ece74202Santirez }
729ece74202Santirez 
730