2012-11-08 12:25:23 -05:00
/* Redis Object implementation.
*
* Copyright ( c ) 2009 - 2012 , Salvatore Sanfilippo < antirez at gmail dot com >
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
*
* * Redistributions of source code must retain the above copyright notice ,
* this list of conditions and the following disclaimer .
* * Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS "
* AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR
* CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS
* INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN
* CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE )
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE .
*/
2015-07-26 09:14:57 -04:00
# include "server.h"
2010-07-29 16:13:31 -04:00
# include <math.h>
2011-11-14 09:34:44 -05:00
# include <ctype.h>
2010-06-21 18:07:48 -04:00
2014-05-12 11:09:07 -04:00
# ifdef __CYGWIN__
# define strtold(a,b) ((long double)strtod((a),(b)))
# endif
2016-05-09 11:01:09 -04:00
/* ===================== Creation and parsing of objects ==================== */
2010-06-21 18:07:48 -04:00
robj * createObject ( int type , void * ptr ) {
2010-11-04 05:09:30 -04:00
robj * o = zmalloc ( sizeof ( * o ) ) ;
2010-06-21 18:07:48 -04:00
o - > type = type ;
2015-07-26 09:28:00 -04:00
o - > encoding = OBJ_ENCODING_RAW ;
2010-06-21 18:07:48 -04:00
o - > ptr = ptr ;
o - > refcount = 1 ;
2010-11-04 05:09:30 -04:00
2016-07-15 06:12:52 -04:00
/* Set the LRU to the current lruclock (minutes resolution), or
* alternatively the LFU counter . */
if ( server . maxmemory_policy & MAXMEMORY_FLAG_LFU ) {
o - > lru = ( LFUGetTimeInMinutes ( ) < < 8 ) | LFU_INIT_VAL ;
} else {
o - > lru = LRU_CLOCK ( ) ;
}
2010-06-21 18:07:48 -04:00
return o ;
}
2015-07-30 05:46:31 -04:00
/* Set a special refcount in the object to make it "shared":
* incrRefCount and decrRefCount ( ) will test for this special refcount
* and will not touch the object . This way it is free to access shared
* objects such as small integers from different threads without any
* mutex .
*
* A common patter to create shared objects :
*
* robj * myobject = makeObjectShared ( createObject ( . . . ) ) ;
*
*/
robj * makeObjectShared ( robj * o ) {
serverAssert ( o - > refcount = = 1 ) ;
o - > refcount = OBJ_SHARED_REFCOUNT ;
return o ;
}
2015-07-26 09:28:00 -04:00
/* Create a string object with encoding OBJ_ENCODING_RAW, that is a plain
2012-06-05 15:50:10 -04:00
* string object where o - > ptr points to a proper sds string . */
2015-03-11 11:59:56 -04:00
robj * createRawStringObject ( const char * ptr , size_t len ) {
2015-07-26 09:28:00 -04:00
return createObject ( OBJ_STRING , sdsnewlen ( ptr , len ) ) ;
2010-06-21 18:07:48 -04:00
}
2015-07-26 09:28:00 -04:00
/* Create a string object with encoding OBJ_ENCODING_EMBSTR, that is
2012-06-05 15:50:10 -04:00
* an object where the sds string is actually an unmodifiable string
* allocated in the same chunk as the object itself . */
2015-03-11 11:59:56 -04:00
robj * createEmbeddedStringObject ( const char * ptr , size_t len ) {
2015-04-09 03:37:01 -04:00
robj * o = zmalloc ( sizeof ( robj ) + sizeof ( struct sdshdr8 ) + len + 1 ) ;
struct sdshdr8 * sh = ( void * ) ( o + 1 ) ;
2012-06-05 15:50:10 -04:00
2015-07-26 09:28:00 -04:00
o - > type = OBJ_STRING ;
o - > encoding = OBJ_ENCODING_EMBSTR ;
2012-06-05 15:50:10 -04:00
o - > ptr = sh + 1 ;
o - > refcount = 1 ;
2016-07-15 06:12:52 -04:00
if ( server . maxmemory_policy & MAXMEMORY_FLAG_LFU ) {
o - > lru = ( LFUGetTimeInMinutes ( ) < < 8 ) | LFU_INIT_VAL ;
} else {
o - > lru = LRU_CLOCK ( ) ;
}
2012-06-05 15:50:10 -04:00
sh - > len = len ;
2015-04-09 03:37:01 -04:00
sh - > alloc = len ;
sh - > flags = SDS_TYPE_8 ;
2012-06-05 15:50:10 -04:00
if ( ptr ) {
memcpy ( sh - > buf , ptr , len ) ;
sh - > buf [ len ] = ' \0 ' ;
} else {
memset ( sh - > buf , 0 , len + 1 ) ;
}
return o ;
}
/* Create a string object with EMBSTR encoding if it is smaller than
2016-05-30 04:57:36 -04:00
* OBJ_ENCODING_EMBSTR_SIZE_LIMIT , otherwise the RAW encoding is
2014-05-07 11:05:09 -04:00
* used .
*
* The current limit of 39 is chosen so that the biggest string object
* we allocate as EMBSTR will still fit into the 64 byte arena of jemalloc . */
2015-07-26 09:28:00 -04:00
# define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44
2015-03-11 11:59:56 -04:00
robj * createStringObject ( const char * ptr , size_t len ) {
2015-07-26 09:28:00 -04:00
if ( len < = OBJ_ENCODING_EMBSTR_SIZE_LIMIT )
2012-06-05 15:50:10 -04:00
return createEmbeddedStringObject ( ptr , len ) ;
else
return createRawStringObject ( ptr , len ) ;
}
2010-06-21 18:07:48 -04:00
robj * createStringObjectFromLongLong ( long long value ) {
robj * o ;
2015-07-27 03:41:48 -04:00
if ( value > = 0 & & value < OBJ_SHARED_INTEGERS ) {
2010-06-21 18:07:48 -04:00
incrRefCount ( shared . integers [ value ] ) ;
o = shared . integers [ value ] ;
} else {
if ( value > = LONG_MIN & & value < = LONG_MAX ) {
2015-07-26 09:28:00 -04:00
o = createObject ( OBJ_STRING , NULL ) ;
o - > encoding = OBJ_ENCODING_INT ;
2010-06-21 18:07:48 -04:00
o - > ptr = ( void * ) ( ( long ) value ) ;
} else {
2015-07-26 09:28:00 -04:00
o = createObject ( OBJ_STRING , sdsfromlonglong ( value ) ) ;
2010-06-21 18:07:48 -04:00
}
}
return o ;
}
2014-12-02 12:19:30 -05:00
/* Create a string object from a long double. If humanfriendly is non-zero
* it does not use exponential format and trims trailing zeroes at the end ,
2014-12-03 04:33:00 -05:00
* however this results in loss of precision . Otherwise exp format is used
* and the output of snprintf ( ) is not modified .
*
* The ' humanfriendly ' option is used for INCRBYFLOAT and HINCRBYFLOAT . */
2014-12-02 12:19:30 -05:00
robj * createStringObjectFromLongDouble ( long double value , int humanfriendly ) {
2012-03-22 13:16:41 -04:00
char buf [ 256 ] ;
2015-09-15 08:43:14 -04:00
int len = ld2string ( buf , sizeof ( buf ) , value , humanfriendly ) ;
2011-11-12 13:27:35 -05:00
return createStringObject ( buf , len ) ;
}
2012-06-05 15:50:10 -04:00
/* Duplicate a string object, with the guarantee that the returned object
* has the same encoding as the original one .
*
* This function also guarantees that duplicating a small integere object
* ( or a string object that contains a representation of a small integer )
* will always result in a fresh object that is unshared ( refcount = = 1 ) .
*
* The resulting object always has refcount set to 1. */
2016-06-22 13:57:24 -04:00
robj * dupStringObject ( const robj * o ) {
2012-06-05 15:50:10 -04:00
robj * d ;
2015-07-26 09:29:53 -04:00
serverAssert ( o - > type = = OBJ_STRING ) ;
2012-06-05 15:50:10 -04:00
switch ( o - > encoding ) {
2015-07-26 09:28:00 -04:00
case OBJ_ENCODING_RAW :
2012-06-05 15:50:10 -04:00
return createRawStringObject ( o - > ptr , sdslen ( o - > ptr ) ) ;
2015-07-26 09:28:00 -04:00
case OBJ_ENCODING_EMBSTR :
2012-06-05 15:50:10 -04:00
return createEmbeddedStringObject ( o - > ptr , sdslen ( o - > ptr ) ) ;
2015-07-26 09:28:00 -04:00
case OBJ_ENCODING_INT :
d = createObject ( OBJ_STRING , NULL ) ;
d - > encoding = OBJ_ENCODING_INT ;
2012-06-05 15:50:10 -04:00
d - > ptr = o - > ptr ;
return d ;
default :
2015-07-27 03:41:48 -04:00
serverPanic ( " Wrong encoding. " ) ;
2012-06-05 15:50:10 -04:00
break ;
}
2010-06-21 18:07:48 -04:00
}
2014-11-13 14:11:47 -05:00
robj * createQuicklistObject ( void ) {
quicklist * l = quicklistCreate ( ) ;
2015-07-26 09:28:00 -04:00
robj * o = createObject ( OBJ_LIST , l ) ;
o - > encoding = OBJ_ENCODING_QUICKLIST ;
2010-06-21 18:07:48 -04:00
return o ;
}
robj * createZiplistObject ( void ) {
unsigned char * zl = ziplistNew ( ) ;
2015-07-26 09:28:00 -04:00
robj * o = createObject ( OBJ_LIST , zl ) ;
o - > encoding = OBJ_ENCODING_ZIPLIST ;
2010-06-21 18:07:48 -04:00
return o ;
}
robj * createSetObject ( void ) {
dict * d = dictCreate ( & setDictType , NULL ) ;
2015-07-26 09:28:00 -04:00
robj * o = createObject ( OBJ_SET , d ) ;
o - > encoding = OBJ_ENCODING_HT ;
2010-07-02 13:57:12 -04:00
return o ;
}
robj * createIntsetObject ( void ) {
intset * is = intsetNew ( ) ;
2015-07-26 09:28:00 -04:00
robj * o = createObject ( OBJ_SET , is ) ;
o - > encoding = OBJ_ENCODING_INTSET ;
2010-07-02 13:57:12 -04:00
return o ;
2010-06-21 18:07:48 -04:00
}
robj * createHashObject ( void ) {
2012-01-03 01:14:10 -05:00
unsigned char * zl = ziplistNew ( ) ;
2015-07-26 09:28:00 -04:00
robj * o = createObject ( OBJ_HASH , zl ) ;
o - > encoding = OBJ_ENCODING_ZIPLIST ;
2010-06-21 18:07:48 -04:00
return o ;
}
robj * createZsetObject ( void ) {
zset * zs = zmalloc ( sizeof ( * zs ) ) ;
2011-04-06 09:36:10 -04:00
robj * o ;
2010-06-21 18:07:48 -04:00
zs - > dict = dictCreate ( & zsetDictType , NULL ) ;
zs - > zsl = zslCreate ( ) ;
2015-07-26 09:28:00 -04:00
o = createObject ( OBJ_ZSET , zs ) ;
o - > encoding = OBJ_ENCODING_SKIPLIST ;
2011-04-06 09:36:10 -04:00
return o ;
2010-06-21 18:07:48 -04:00
}
2011-03-08 10:08:52 -05:00
robj * createZsetZiplistObject ( void ) {
unsigned char * zl = ziplistNew ( ) ;
2015-07-26 09:28:00 -04:00
robj * o = createObject ( OBJ_ZSET , zl ) ;
o - > encoding = OBJ_ENCODING_ZIPLIST ;
2011-03-08 10:08:52 -05:00
return o ;
}
2016-05-18 05:45:40 -04:00
robj * createModuleObject ( moduleType * mt , void * value ) {
moduleValue * mv = zmalloc ( sizeof ( * mv ) ) ;
mv - > type = mt ;
mv - > value = value ;
return createObject ( OBJ_MODULE , mv ) ;
}
2010-06-21 18:07:48 -04:00
void freeStringObject ( robj * o ) {
2015-07-26 09:28:00 -04:00
if ( o - > encoding = = OBJ_ENCODING_RAW ) {
2010-06-21 18:07:48 -04:00
sdsfree ( o - > ptr ) ;
}
}
void freeListObject ( robj * o ) {
switch ( o - > encoding ) {
2015-07-26 09:28:00 -04:00
case OBJ_ENCODING_QUICKLIST :
2014-11-13 14:11:47 -05:00
quicklistRelease ( o - > ptr ) ;
2010-06-21 18:07:48 -04:00
break ;
default :
2015-07-27 03:41:48 -04:00
serverPanic ( " Unknown list encoding type " ) ;
2010-06-21 18:07:48 -04:00
}
}
void freeSetObject ( robj * o ) {
2010-07-02 13:57:12 -04:00
switch ( o - > encoding ) {
2015-07-26 09:28:00 -04:00
case OBJ_ENCODING_HT :
2010-07-02 13:57:12 -04:00
dictRelease ( ( dict * ) o - > ptr ) ;
break ;
2015-07-26 09:28:00 -04:00
case OBJ_ENCODING_INTSET :
2010-07-02 13:57:12 -04:00
zfree ( o - > ptr ) ;
break ;
default :
2015-07-27 03:41:48 -04:00
serverPanic ( " Unknown set encoding type " ) ;
2010-07-02 13:57:12 -04:00
}
2010-06-21 18:07:48 -04:00
}
void freeZsetObject ( robj * o ) {
2011-03-08 17:56:59 -05:00
zset * zs ;
switch ( o - > encoding ) {
2015-07-26 09:28:00 -04:00
case OBJ_ENCODING_SKIPLIST :
2011-03-08 17:56:59 -05:00
zs = o - > ptr ;
dictRelease ( zs - > dict ) ;
zslFree ( zs - > zsl ) ;
zfree ( zs ) ;
break ;
2015-07-26 09:28:00 -04:00
case OBJ_ENCODING_ZIPLIST :
2011-03-08 17:56:59 -05:00
zfree ( o - > ptr ) ;
break ;
default :
2015-07-27 03:41:48 -04:00
serverPanic ( " Unknown sorted set encoding " ) ;
2011-03-08 17:56:59 -05:00
}
2010-06-21 18:07:48 -04:00
}
void freeHashObject ( robj * o ) {
switch ( o - > encoding ) {
2015-07-26 09:28:00 -04:00
case OBJ_ENCODING_HT :
2010-06-21 18:07:48 -04:00
dictRelease ( ( dict * ) o - > ptr ) ;
break ;
2015-07-26 09:28:00 -04:00
case OBJ_ENCODING_ZIPLIST :
2010-06-21 18:07:48 -04:00
zfree ( o - > ptr ) ;
break ;
default :
2015-07-27 03:41:48 -04:00
serverPanic ( " Unknown hash encoding type " ) ;
2010-06-21 18:07:48 -04:00
break ;
}
}
2016-05-18 05:45:40 -04:00
void freeModuleObject ( robj * o ) {
moduleValue * mv = o - > ptr ;
mv - > type - > free ( mv - > value ) ;
zfree ( mv ) ;
}
2010-06-21 18:07:48 -04:00
void incrRefCount ( robj * o ) {
2015-07-30 05:46:31 -04:00
if ( o - > refcount ! = OBJ_SHARED_REFCOUNT ) o - > refcount + + ;
2010-06-21 18:07:48 -04:00
}
2013-01-24 05:27:10 -05:00
void decrRefCount ( robj * o ) {
2011-06-02 11:41:42 -04:00
if ( o - > refcount = = 1 ) {
2010-06-21 18:07:48 -04:00
switch ( o - > type ) {
2015-07-26 09:28:00 -04:00
case OBJ_STRING : freeStringObject ( o ) ; break ;
case OBJ_LIST : freeListObject ( o ) ; break ;
case OBJ_SET : freeSetObject ( o ) ; break ;
case OBJ_ZSET : freeZsetObject ( o ) ; break ;
case OBJ_HASH : freeHashObject ( o ) ; break ;
2016-05-18 05:45:40 -04:00
case OBJ_MODULE : freeModuleObject ( o ) ; break ;
2015-07-27 03:41:48 -04:00
default : serverPanic ( " Unknown object type " ) ; break ;
2010-06-21 18:07:48 -04:00
}
2010-11-04 05:09:30 -04:00
zfree ( o ) ;
2011-06-02 11:41:42 -04:00
} else {
2015-07-30 05:46:31 -04:00
if ( o - > refcount < = 0 ) serverPanic ( " decrRefCount against refcount <= 0 " ) ;
if ( o - > refcount ! = OBJ_SHARED_REFCOUNT ) o - > refcount - - ;
2010-06-21 18:07:48 -04:00
}
}
2013-01-24 05:27:10 -05:00
/* This variant of decrRefCount() gets its argument as void, and is useful
* as free method in data structures that expect a ' void free_object ( void * ) '
* prototype for the free method . */
void decrRefCountVoid ( void * o ) {
decrRefCount ( o ) ;
}
2011-07-13 09:38:03 -04:00
/* This function set the ref count to zero without freeing the object.
* It is useful in order to pass a new object to functions incrementing
* the ref count of the received object . Example :
*
* functionThatWillIncrementRefCount ( resetRefCount ( CreateObject ( . . . ) ) ) ;
*
* Otherwise you need to resort to the less elegant pattern :
*
* * obj = createObject ( . . . ) ;
* functionThatWillIncrementRefCount ( obj ) ;
* decrRefCount ( obj ) ;
*/
robj * resetRefCount ( robj * obj ) {
obj - > refcount = 0 ;
return obj ;
}
2015-07-26 09:20:46 -04:00
int checkType ( client * c , robj * o , int type ) {
2010-06-21 18:07:48 -04:00
if ( o - > type ! = type ) {
addReply ( c , shared . wrongtypeerr ) ;
return 1 ;
}
return 0 ;
}
2015-07-31 12:01:23 -04:00
int isSdsRepresentableAsLongLong ( sds s , long long * llval ) {
return string2ll ( s , sdslen ( s ) , llval ) ? C_OK : C_ERR ;
}
2011-04-27 07:24:52 -04:00
int isObjectRepresentableAsLongLong ( robj * o , long long * llval ) {
2015-07-26 09:29:53 -04:00
serverAssertWithInfo ( NULL , o , o - > type = = OBJ_STRING ) ;
2015-07-26 09:28:00 -04:00
if ( o - > encoding = = OBJ_ENCODING_INT ) {
2011-04-27 07:24:52 -04:00
if ( llval ) * llval = ( long ) o - > ptr ;
2015-07-26 17:17:55 -04:00
return C_OK ;
2011-04-27 07:24:52 -04:00
} else {
2015-07-31 12:01:23 -04:00
return isSdsRepresentableAsLongLong ( o - > ptr , llval ) ;
2011-04-27 07:24:52 -04:00
}
}
2010-06-21 18:07:48 -04:00
/* Try to encode a string object in order to save space */
robj * tryObjectEncoding ( robj * o ) {
long value ;
sds s = o - > ptr ;
2013-08-27 05:56:47 -04:00
size_t len ;
2010-06-21 18:07:48 -04:00
2014-04-04 04:28:34 -04:00
/* Make sure this is a string object, the only type we encode
* in this function . Other types use encoded memory efficient
* representations but are handled by the commands implementing
* the type . */
2015-07-26 09:29:53 -04:00
serverAssertWithInfo ( NULL , o , o - > type = = OBJ_STRING ) ;
2014-04-04 04:28:34 -04:00
/* We try some specialized encoding only for objects that are
* RAW or EMBSTR encoded , in other words objects that are still
* in represented by an actually array of chars . */
if ( ! sdsEncodedObject ( o ) ) return o ;
2010-06-21 18:07:48 -04:00
/* It's not safe to encode shared objects: shared objects can be shared
2014-04-04 04:28:34 -04:00
* everywhere in the " object space " of Redis and may end in places where
* they are not handled . We handle them only as values in the keyspace . */
2010-06-21 18:07:48 -04:00
if ( o - > refcount > 1 ) return o ;
2013-08-27 05:56:47 -04:00
/* Check if we can represent this string as a long integer.
2016-06-23 07:53:56 -04:00
* Note that we are sure that a string larger than 20 chars is not
2014-04-04 04:28:34 -04:00
* representable as a 32 nor 64 bit integer . */
2013-08-27 05:56:47 -04:00
len = sdslen ( s ) ;
2016-06-23 07:53:56 -04:00
if ( len < = 20 & & string2l ( s , len , & value ) ) {
2014-04-04 04:28:34 -04:00
/* This object is encodable as a long. Try to use a shared object.
* Note that we avoid using shared integers when maxmemory is used
* because every object needs to have a private LRU field for the LRU
* algorithm to work well . */
2014-07-18 04:09:51 -04:00
if ( ( server . maxmemory = = 0 | |
2016-07-21 05:14:15 -04:00
! ( server . maxmemory_policy & MAXMEMORY_FLAG_NO_SHARED_INTEGERS ) ) & &
2014-04-04 04:28:34 -04:00
value > = 0 & &
2015-07-27 03:41:48 -04:00
value < OBJ_SHARED_INTEGERS )
2014-04-04 04:28:34 -04:00
{
2012-06-05 15:50:10 -04:00
decrRefCount ( o ) ;
2014-04-04 04:28:34 -04:00
incrRefCount ( shared . integers [ value ] ) ;
return shared . integers [ value ] ;
2012-06-05 15:50:10 -04:00
} else {
2015-07-26 09:28:00 -04:00
if ( o - > encoding = = OBJ_ENCODING_RAW ) sdsfree ( o - > ptr ) ;
o - > encoding = OBJ_ENCODING_INT ;
2014-04-04 04:28:34 -04:00
o - > ptr = ( void * ) value ;
2012-06-05 15:50:10 -04:00
return o ;
}
}
2010-06-21 18:07:48 -04:00
2014-04-04 04:28:34 -04:00
/* If the string is small and is still RAW encoded,
* try the EMBSTR encoding which is more efficient .
* In this representation the object and the SDS string are allocated
* in the same chunk of memory to save space and cache misses . */
2015-07-26 09:28:00 -04:00
if ( len < = OBJ_ENCODING_EMBSTR_SIZE_LIMIT ) {
2014-04-04 04:28:34 -04:00
robj * emb ;
2015-07-26 09:28:00 -04:00
if ( o - > encoding = = OBJ_ENCODING_EMBSTR ) return o ;
2014-04-04 04:28:34 -04:00
emb = createEmbeddedStringObject ( s , sdslen ( s ) ) ;
decrRefCount ( o ) ;
return emb ;
}
/* We can't encode the object...
2010-07-22 07:08:02 -04:00
*
2014-04-04 04:28:34 -04:00
* Do the last try , and at least optimize the SDS string inside
* the string object to require little space , in case there
* is more than 10 % of free space at the end of the SDS string .
2010-10-15 12:04:05 -04:00
*
2014-04-04 04:28:34 -04:00
* We do that only for relatively large strings as this branch
* is only entered if the length of the string is greater than
2015-07-26 09:28:00 -04:00
* OBJ_ENCODING_EMBSTR_SIZE_LIMIT . */
if ( o - > encoding = = OBJ_ENCODING_RAW & &
2014-04-04 04:28:34 -04:00
sdsavail ( s ) > len / 10 )
{
o - > ptr = sdsRemoveFreeSpace ( o - > ptr ) ;
2010-06-21 18:07:48 -04:00
}
2014-04-04 04:28:34 -04:00
/* Return the original object. */
return o ;
2010-06-21 18:07:48 -04:00
}
/* Get a decoded version of an encoded object (returned as a new object).
* If the object is already raw - encoded just increment the ref count . */
robj * getDecodedObject ( robj * o ) {
robj * dec ;
2012-06-05 15:50:10 -04:00
if ( sdsEncodedObject ( o ) ) {
2010-06-21 18:07:48 -04:00
incrRefCount ( o ) ;
return o ;
}
2015-07-26 09:28:00 -04:00
if ( o - > type = = OBJ_STRING & & o - > encoding = = OBJ_ENCODING_INT ) {
2010-06-21 18:07:48 -04:00
char buf [ 32 ] ;
ll2string ( buf , 32 , ( long ) o - > ptr ) ;
dec = createStringObject ( buf , strlen ( buf ) ) ;
return dec ;
} else {
2015-07-27 03:41:48 -04:00
serverPanic ( " Unknown encoding type " ) ;
2010-06-21 18:07:48 -04:00
}
}
Fixed compareStringObject() and introduced collateStringObject().
compareStringObject was not always giving the same result when comparing
two exact strings, but encoded as integers or as sds strings, since it
switched to strcmp() when at least one of the strings were not sds
encoded.
For instance the two strings "123" and "123\x00456", where the first
string was integer encoded, would result into the old implementation of
compareStringObject() to return 0 as if the strings were equal, while
instead the second string is "greater" than the first in a binary
comparison.
The same compasion, but with "123" encoded as sds string, would instead
return a value < 0, as it is correct. It is not impossible that the
above caused some obscure bug, since the comparison was not always
deterministic, and compareStringObject() is used in the implementation
of skiplists, hash tables, and so forth.
At the same time, collateStringObject() was introduced by this commit, so
that can be used by SORT command to return sorted strings usign
collation instead of binary comparison. See next commit.
2013-07-12 05:56:52 -04:00
/* Compare two string objects via strcmp() or strcoll() depending on flags.
2010-06-21 18:07:48 -04:00
* Note that the objects may be integer - encoded . In such a case we
* use ll2string ( ) to get a string representation of the numbers on the stack
* and compare the strings , it ' s much faster than calling getDecodedObject ( ) .
*
Fixed compareStringObject() and introduced collateStringObject().
compareStringObject was not always giving the same result when comparing
two exact strings, but encoded as integers or as sds strings, since it
switched to strcmp() when at least one of the strings were not sds
encoded.
For instance the two strings "123" and "123\x00456", where the first
string was integer encoded, would result into the old implementation of
compareStringObject() to return 0 as if the strings were equal, while
instead the second string is "greater" than the first in a binary
comparison.
The same compasion, but with "123" encoded as sds string, would instead
return a value < 0, as it is correct. It is not impossible that the
above caused some obscure bug, since the comparison was not always
deterministic, and compareStringObject() is used in the implementation
of skiplists, hash tables, and so forth.
At the same time, collateStringObject() was introduced by this commit, so
that can be used by SORT command to return sorted strings usign
collation instead of binary comparison. See next commit.
2013-07-12 05:56:52 -04:00
* Important note : when REDIS_COMPARE_BINARY is used a binary - safe comparison
* is used . */
# define REDIS_COMPARE_BINARY (1<<0)
# define REDIS_COMPARE_COLL (1<<1)
int compareStringObjectsWithFlags ( robj * a , robj * b , int flags ) {
2015-07-26 09:29:53 -04:00
serverAssertWithInfo ( NULL , a , a - > type = = OBJ_STRING & & b - > type = = OBJ_STRING ) ;
2010-06-21 18:07:48 -04:00
char bufa [ 128 ] , bufb [ 128 ] , * astr , * bstr ;
Fixed compareStringObject() and introduced collateStringObject().
compareStringObject was not always giving the same result when comparing
two exact strings, but encoded as integers or as sds strings, since it
switched to strcmp() when at least one of the strings were not sds
encoded.
For instance the two strings "123" and "123\x00456", where the first
string was integer encoded, would result into the old implementation of
compareStringObject() to return 0 as if the strings were equal, while
instead the second string is "greater" than the first in a binary
comparison.
The same compasion, but with "123" encoded as sds string, would instead
return a value < 0, as it is correct. It is not impossible that the
above caused some obscure bug, since the comparison was not always
deterministic, and compareStringObject() is used in the implementation
of skiplists, hash tables, and so forth.
At the same time, collateStringObject() was introduced by this commit, so
that can be used by SORT command to return sorted strings usign
collation instead of binary comparison. See next commit.
2013-07-12 05:56:52 -04:00
size_t alen , blen , minlen ;
2010-06-21 18:07:48 -04:00
if ( a = = b ) return 0 ;
2012-06-05 15:50:10 -04:00
if ( sdsEncodedObject ( a ) ) {
astr = a - > ptr ;
alen = sdslen ( astr ) ;
} else {
Fixed compareStringObject() and introduced collateStringObject().
compareStringObject was not always giving the same result when comparing
two exact strings, but encoded as integers or as sds strings, since it
switched to strcmp() when at least one of the strings were not sds
encoded.
For instance the two strings "123" and "123\x00456", where the first
string was integer encoded, would result into the old implementation of
compareStringObject() to return 0 as if the strings were equal, while
instead the second string is "greater" than the first in a binary
comparison.
The same compasion, but with "123" encoded as sds string, would instead
return a value < 0, as it is correct. It is not impossible that the
above caused some obscure bug, since the comparison was not always
deterministic, and compareStringObject() is used in the implementation
of skiplists, hash tables, and so forth.
At the same time, collateStringObject() was introduced by this commit, so
that can be used by SORT command to return sorted strings usign
collation instead of binary comparison. See next commit.
2013-07-12 05:56:52 -04:00
alen = ll2string ( bufa , sizeof ( bufa ) , ( long ) a - > ptr ) ;
2010-06-21 18:07:48 -04:00
astr = bufa ;
}
2012-06-05 15:50:10 -04:00
if ( sdsEncodedObject ( b ) ) {
bstr = b - > ptr ;
blen = sdslen ( bstr ) ;
} else {
Fixed compareStringObject() and introduced collateStringObject().
compareStringObject was not always giving the same result when comparing
two exact strings, but encoded as integers or as sds strings, since it
switched to strcmp() when at least one of the strings were not sds
encoded.
For instance the two strings "123" and "123\x00456", where the first
string was integer encoded, would result into the old implementation of
compareStringObject() to return 0 as if the strings were equal, while
instead the second string is "greater" than the first in a binary
comparison.
The same compasion, but with "123" encoded as sds string, would instead
return a value < 0, as it is correct. It is not impossible that the
above caused some obscure bug, since the comparison was not always
deterministic, and compareStringObject() is used in the implementation
of skiplists, hash tables, and so forth.
At the same time, collateStringObject() was introduced by this commit, so
that can be used by SORT command to return sorted strings usign
collation instead of binary comparison. See next commit.
2013-07-12 05:56:52 -04:00
blen = ll2string ( bufb , sizeof ( bufb ) , ( long ) b - > ptr ) ;
2010-06-21 18:07:48 -04:00
bstr = bufb ;
Fixed compareStringObject() and introduced collateStringObject().
compareStringObject was not always giving the same result when comparing
two exact strings, but encoded as integers or as sds strings, since it
switched to strcmp() when at least one of the strings were not sds
encoded.
For instance the two strings "123" and "123\x00456", where the first
string was integer encoded, would result into the old implementation of
compareStringObject() to return 0 as if the strings were equal, while
instead the second string is "greater" than the first in a binary
comparison.
The same compasion, but with "123" encoded as sds string, would instead
return a value < 0, as it is correct. It is not impossible that the
above caused some obscure bug, since the comparison was not always
deterministic, and compareStringObject() is used in the implementation
of skiplists, hash tables, and so forth.
At the same time, collateStringObject() was introduced by this commit, so
that can be used by SORT command to return sorted strings usign
collation instead of binary comparison. See next commit.
2013-07-12 05:56:52 -04:00
}
if ( flags & REDIS_COMPARE_COLL ) {
return strcoll ( astr , bstr ) ;
} else {
int cmp ;
minlen = ( alen < blen ) ? alen : blen ;
cmp = memcmp ( astr , bstr , minlen ) ;
if ( cmp = = 0 ) return alen - blen ;
return cmp ;
2010-06-21 18:07:48 -04:00
}
Fixed compareStringObject() and introduced collateStringObject().
compareStringObject was not always giving the same result when comparing
two exact strings, but encoded as integers or as sds strings, since it
switched to strcmp() when at least one of the strings were not sds
encoded.
For instance the two strings "123" and "123\x00456", where the first
string was integer encoded, would result into the old implementation of
compareStringObject() to return 0 as if the strings were equal, while
instead the second string is "greater" than the first in a binary
comparison.
The same compasion, but with "123" encoded as sds string, would instead
return a value < 0, as it is correct. It is not impossible that the
above caused some obscure bug, since the comparison was not always
deterministic, and compareStringObject() is used in the implementation
of skiplists, hash tables, and so forth.
At the same time, collateStringObject() was introduced by this commit, so
that can be used by SORT command to return sorted strings usign
collation instead of binary comparison. See next commit.
2013-07-12 05:56:52 -04:00
}
/* Wrapper for compareStringObjectsWithFlags() using binary comparison. */
int compareStringObjects ( robj * a , robj * b ) {
return compareStringObjectsWithFlags ( a , b , REDIS_COMPARE_BINARY ) ;
}
/* Wrapper for compareStringObjectsWithFlags() using collation. */
int collateStringObjects ( robj * a , robj * b ) {
return compareStringObjectsWithFlags ( a , b , REDIS_COMPARE_COLL ) ;
2010-06-21 18:07:48 -04:00
}
/* Equal string objects return 1 if the two objects are the same from the
* point of view of a string comparison , otherwise 0 is returned . Note that
* this function is faster then checking for ( compareStringObject ( a , b ) = = 0 )
* because it can perform some more optimization . */
int equalStringObjects ( robj * a , robj * b ) {
2015-07-26 09:28:00 -04:00
if ( a - > encoding = = OBJ_ENCODING_INT & &
b - > encoding = = OBJ_ENCODING_INT ) {
2012-06-05 15:50:10 -04:00
/* If both strings are integer encoded just check if the stored
* long is the same . */
2010-06-21 18:07:48 -04:00
return a - > ptr = = b - > ptr ;
} else {
return compareStringObjects ( a , b ) = = 0 ;
}
}
size_t stringObjectLen ( robj * o ) {
2015-07-26 09:29:53 -04:00
serverAssertWithInfo ( NULL , o , o - > type = = OBJ_STRING ) ;
2012-06-05 15:50:10 -04:00
if ( sdsEncodedObject ( o ) ) {
2010-06-21 18:07:48 -04:00
return sdslen ( o - > ptr ) ;
} else {
2015-02-27 10:08:50 -05:00
return sdigits10 ( ( long ) o - > ptr ) ;
2010-06-21 18:07:48 -04:00
}
}
2016-06-20 16:08:06 -04:00
int getDoubleFromObject ( const robj * o , double * target ) {
2010-06-21 18:07:48 -04:00
double value ;
char * eptr ;
if ( o = = NULL ) {
value = 0 ;
} else {
2015-07-26 09:29:53 -04:00
serverAssertWithInfo ( NULL , o , o - > type = = OBJ_STRING ) ;
2012-06-05 15:50:10 -04:00
if ( sdsEncodedObject ( o ) ) {
2011-11-12 13:27:35 -05:00
errno = 0 ;
2010-06-21 18:07:48 -04:00
value = strtod ( o - > ptr , & eptr ) ;
2016-06-20 16:08:06 -04:00
if ( isspace ( ( ( const char * ) o - > ptr ) [ 0 ] ) | |
2013-07-16 09:05:13 -04:00
eptr [ 0 ] ! = ' \0 ' | |
( errno = = ERANGE & &
( value = = HUGE_VAL | | value = = - HUGE_VAL | | value = = 0 ) ) | |
errno = = EINVAL | |
isnan ( value ) )
2015-07-26 17:17:55 -04:00
return C_ERR ;
2015-07-26 09:28:00 -04:00
} else if ( o - > encoding = = OBJ_ENCODING_INT ) {
2010-06-21 18:07:48 -04:00
value = ( long ) o - > ptr ;
} else {
2015-07-27 03:41:48 -04:00
serverPanic ( " Unknown string encoding " ) ;
2010-06-21 18:07:48 -04:00
}
}
* target = value ;
2015-07-26 17:17:55 -04:00
return C_OK ;
2010-06-21 18:07:48 -04:00
}
2015-07-26 09:20:46 -04:00
int getDoubleFromObjectOrReply ( client * c , robj * o , double * target , const char * msg ) {
2010-06-21 18:07:48 -04:00
double value ;
2015-07-26 17:17:55 -04:00
if ( getDoubleFromObject ( o , & value ) ! = C_OK ) {
2010-06-21 18:07:48 -04:00
if ( msg ! = NULL ) {
2010-09-02 13:52:24 -04:00
addReplyError ( c , ( char * ) msg ) ;
2010-06-21 18:07:48 -04:00
} else {
2011-11-12 13:27:35 -05:00
addReplyError ( c , " value is not a valid float " ) ;
}
2015-07-26 17:17:55 -04:00
return C_ERR ;
2011-11-12 13:27:35 -05:00
}
* target = value ;
2015-07-26 17:17:55 -04:00
return C_OK ;
2011-11-12 13:27:35 -05:00
}
int getLongDoubleFromObject ( robj * o , long double * target ) {
long double value ;
char * eptr ;
if ( o = = NULL ) {
value = 0 ;
} else {
2015-07-26 09:29:53 -04:00
serverAssertWithInfo ( NULL , o , o - > type = = OBJ_STRING ) ;
2012-06-05 15:50:10 -04:00
if ( sdsEncodedObject ( o ) ) {
2011-11-12 13:27:35 -05:00
errno = 0 ;
value = strtold ( o - > ptr , & eptr ) ;
2011-11-14 09:34:44 -05:00
if ( isspace ( ( ( char * ) o - > ptr ) [ 0 ] ) | | eptr [ 0 ] ! = ' \0 ' | |
errno = = ERANGE | | isnan ( value ) )
2015-07-26 17:17:55 -04:00
return C_ERR ;
2015-07-26 09:28:00 -04:00
} else if ( o - > encoding = = OBJ_ENCODING_INT ) {
2011-11-12 13:27:35 -05:00
value = ( long ) o - > ptr ;
} else {
2015-07-27 03:41:48 -04:00
serverPanic ( " Unknown string encoding " ) ;
2011-11-12 13:27:35 -05:00
}
}
* target = value ;
2015-07-26 17:17:55 -04:00
return C_OK ;
2011-11-12 13:27:35 -05:00
}
2015-07-26 09:20:46 -04:00
int getLongDoubleFromObjectOrReply ( client * c , robj * o , long double * target , const char * msg ) {
2011-11-12 13:27:35 -05:00
long double value ;
2015-07-26 17:17:55 -04:00
if ( getLongDoubleFromObject ( o , & value ) ! = C_OK ) {
2011-11-12 13:27:35 -05:00
if ( msg ! = NULL ) {
addReplyError ( c , ( char * ) msg ) ;
} else {
addReplyError ( c , " value is not a valid float " ) ;
2010-06-21 18:07:48 -04:00
}
2015-07-26 17:17:55 -04:00
return C_ERR ;
2010-06-21 18:07:48 -04:00
}
* target = value ;
2015-07-26 17:17:55 -04:00
return C_OK ;
2010-06-21 18:07:48 -04:00
}
int getLongLongFromObject ( robj * o , long long * target ) {
long long value ;
if ( o = = NULL ) {
value = 0 ;
} else {
2015-07-26 09:29:53 -04:00
serverAssertWithInfo ( NULL , o , o - > type = = OBJ_STRING ) ;
2012-06-05 15:50:10 -04:00
if ( sdsEncodedObject ( o ) ) {
2016-07-06 05:43:33 -04:00
if ( string2ll ( o - > ptr , sdslen ( o - > ptr ) , & value ) = = 0 ) return C_ERR ;
2015-07-26 09:28:00 -04:00
} else if ( o - > encoding = = OBJ_ENCODING_INT ) {
2010-06-21 18:07:48 -04:00
value = ( long ) o - > ptr ;
} else {
2015-07-27 03:41:48 -04:00
serverPanic ( " Unknown string encoding " ) ;
2010-06-21 18:07:48 -04:00
}
}
2010-07-02 13:57:12 -04:00
if ( target ) * target = value ;
2015-07-26 17:17:55 -04:00
return C_OK ;
2010-06-21 18:07:48 -04:00
}
2015-07-26 09:20:46 -04:00
int getLongLongFromObjectOrReply ( client * c , robj * o , long long * target , const char * msg ) {
2010-06-21 18:07:48 -04:00
long long value ;
2015-07-26 17:17:55 -04:00
if ( getLongLongFromObject ( o , & value ) ! = C_OK ) {
2010-06-21 18:07:48 -04:00
if ( msg ! = NULL ) {
2010-09-02 13:52:24 -04:00
addReplyError ( c , ( char * ) msg ) ;
2010-06-21 18:07:48 -04:00
} else {
2010-09-02 13:52:24 -04:00
addReplyError ( c , " value is not an integer or out of range " ) ;
2010-06-21 18:07:48 -04:00
}
2015-07-26 17:17:55 -04:00
return C_ERR ;
2010-06-21 18:07:48 -04:00
}
* target = value ;
2015-07-26 17:17:55 -04:00
return C_OK ;
2010-06-21 18:07:48 -04:00
}
2015-07-26 09:20:46 -04:00
int getLongFromObjectOrReply ( client * c , robj * o , long * target , const char * msg ) {
2010-06-21 18:07:48 -04:00
long long value ;
2015-07-26 17:17:55 -04:00
if ( getLongLongFromObjectOrReply ( c , o , & value , msg ) ! = C_OK ) return C_ERR ;
2010-06-21 18:07:48 -04:00
if ( value < LONG_MIN | | value > LONG_MAX ) {
if ( msg ! = NULL ) {
2010-09-02 13:52:24 -04:00
addReplyError ( c , ( char * ) msg ) ;
2010-06-21 18:07:48 -04:00
} else {
2010-09-02 13:52:24 -04:00
addReplyError ( c , " value is out of range " ) ;
2010-06-21 18:07:48 -04:00
}
2015-07-26 17:17:55 -04:00
return C_ERR ;
2010-06-21 18:07:48 -04:00
}
* target = value ;
2015-07-26 17:17:55 -04:00
return C_OK ;
2010-06-21 18:07:48 -04:00
}
char * strEncoding ( int encoding ) {
switch ( encoding ) {
2015-07-26 09:28:00 -04:00
case OBJ_ENCODING_RAW : return " raw " ;
case OBJ_ENCODING_INT : return " int " ;
case OBJ_ENCODING_HT : return " hashtable " ;
case OBJ_ENCODING_QUICKLIST : return " quicklist " ;
case OBJ_ENCODING_ZIPLIST : return " ziplist " ;
case OBJ_ENCODING_INTSET : return " intset " ;
case OBJ_ENCODING_SKIPLIST : return " skiplist " ;
case OBJ_ENCODING_EMBSTR : return " embstr " ;
2010-06-21 18:07:48 -04:00
default : return " unknown " ;
}
}
2010-10-14 07:52:58 -04:00
2016-05-09 11:01:09 -04:00
/* ========================== Objects introspection ========================= */
/* Returns the size in bytes consumed by the key's value in RAM */
size_t objectComputeSize ( robj * o ) {
robj * ele ;
list * l ;
listNode * ln ;
dict * d ;
dictIterator * di ;
listIter li ;
struct dictEntry * de ;
size_t asize = 0 , elesize ;
if ( o - > type = = OBJ_STRING ) {
if ( o - > encoding = = OBJ_ENCODING_INT ) {
asize = sizeof ( * o ) ;
}
else if ( o - > encoding = = OBJ_ENCODING_RAW ) {
asize = sdsAllocSize ( o - > ptr ) + sizeof ( * o ) ;
} else if ( o - > encoding = = OBJ_ENCODING_EMBSTR ) {
asize = sdslen ( o - > ptr ) + 2 + sizeof ( * o ) ;
} else {
serverPanic ( " Unknown string encoding " ) ;
}
} else if ( o - > type = = OBJ_LIST ) {
if ( o - > encoding = = OBJ_ENCODING_QUICKLIST ) {
quicklist * ql = o - > ptr ;
quicklistNode * node = ql - > head ;
asize = sizeof ( * o ) + sizeof ( quicklist ) ;
do {
asize + = sizeof ( quicklistNode ) + ziplistBlobLen ( node - > zl ) ;
} while ( ( node = node - > next ) ) ;
} else if ( o - > encoding = = OBJ_ENCODING_ZIPLIST ) {
asize = sizeof ( * o ) + ziplistBlobLen ( o - > ptr ) ;
} else if ( o - > encoding = = OBJ_ENCODING_LINKEDLIST ) {
l = o - > ptr ;
asize = sizeof ( * o ) + sizeof ( list ) ;
listRewind ( l , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
ele = ln - > value ;
elesize = ( ele - > encoding = = OBJ_ENCODING_RAW ) ?
( sizeof ( * o ) + sdsAllocSize ( ele - > ptr ) ) : sizeof ( * o ) ;
asize + = ( sizeof ( listNode ) + elesize ) ;
}
} else {
serverPanic ( " Unknown list encoding " ) ;
}
} else if ( o - > type = = OBJ_SET ) {
if ( o - > encoding = = OBJ_ENCODING_HT ) {
d = o - > ptr ;
di = dictGetIterator ( d ) ;
asize = sizeof ( * o ) + sizeof ( dict ) + ( sizeof ( struct dictEntry * ) * dictSlots ( d ) ) ;
while ( ( de = dictNext ( di ) ) ! = NULL ) {
ele = dictGetKey ( de ) ;
elesize = ( ele - > encoding = = OBJ_ENCODING_RAW ) ?
( sizeof ( * o ) + sdsAllocSize ( ele - > ptr ) ) : sizeof ( * o ) ;
asize + = ( sizeof ( struct dictEntry ) + elesize ) ;
}
dictReleaseIterator ( di ) ;
} else if ( o - > encoding = = OBJ_ENCODING_INTSET ) {
intset * is = o - > ptr ;
asize = sizeof ( * o ) + sizeof ( * is ) + is - > encoding * is - > length ;
} else {
serverPanic ( " Unknown set encoding " ) ;
}
} else if ( o - > type = = OBJ_ZSET ) {
if ( o - > encoding = = OBJ_ENCODING_ZIPLIST ) {
asize = sizeof ( * o ) + ( ziplistBlobLen ( o - > ptr ) ) ;
} else if ( o - > encoding = = OBJ_ENCODING_SKIPLIST ) {
d = ( ( zset * ) o - > ptr ) - > dict ;
di = dictGetIterator ( d ) ;
asize = sizeof ( * o ) + sizeof ( zset ) + ( sizeof ( struct dictEntry * ) * dictSlots ( d ) ) ;
while ( ( de = dictNext ( di ) ) ! = NULL ) {
ele = dictGetKey ( de ) ;
elesize = ( ele - > encoding = = OBJ_ENCODING_RAW ) ?
( sizeof ( * o ) + sdsAllocSize ( ele - > ptr ) ) : sizeof ( * o ) ;
asize + = ( sizeof ( struct dictEntry ) + elesize ) ;
asize + = sizeof ( zskiplistNode ) * dictSize ( d ) ;
}
dictReleaseIterator ( di ) ;
} else {
serverPanic ( " Unknown sorted set encoding " ) ;
}
} else if ( o - > type = = OBJ_HASH ) {
if ( o - > encoding = = OBJ_ENCODING_ZIPLIST ) {
asize = sizeof ( * o ) + ( ziplistBlobLen ( o - > ptr ) ) ;
} else if ( o - > encoding = = OBJ_ENCODING_HT ) {
d = o - > ptr ;
di = dictGetIterator ( d ) ;
asize = sizeof ( * o ) + sizeof ( dict ) + ( sizeof ( struct dictEntry * ) * dictSlots ( d ) ) ;
while ( ( de = dictNext ( di ) ) ! = NULL ) {
ele = dictGetKey ( de ) ;
elesize = ( ele - > encoding = = OBJ_ENCODING_RAW ) ?
( sizeof ( * o ) + sdsAllocSize ( ele - > ptr ) ) : sizeof ( * o ) ;
ele = dictGetVal ( de ) ;
elesize = ( ele - > encoding = = OBJ_ENCODING_RAW ) ?
( sizeof ( * o ) + sdsAllocSize ( ele - > ptr ) ) : sizeof ( * o ) ;
asize + = ( sizeof ( struct dictEntry ) + elesize ) ;
}
dictReleaseIterator ( di ) ;
} else {
serverPanic ( " Unknown hash encoding " ) ;
}
} else {
serverPanic ( " Unknown object type " ) ;
}
return asize ;
}
/* ============================ The OBJECT command ========================== */
2014-03-30 12:32:17 -04:00
/* This is a helper function for the OBJECT command. We need to lookup keys
2011-04-06 06:19:45 -04:00
* without any modification of LRU or other parameters . */
2015-07-26 09:20:46 -04:00
robj * objectCommandLookup ( client * c , robj * key ) {
2011-04-06 06:19:45 -04:00
dictEntry * de ;
if ( ( de = dictFind ( c - > db - > dict , key - > ptr ) ) = = NULL ) return NULL ;
2011-11-08 11:07:55 -05:00
return ( robj * ) dictGetVal ( de ) ;
2011-04-06 06:19:45 -04:00
}
2015-07-26 09:20:46 -04:00
robj * objectCommandLookupOrReply ( client * c , robj * key , robj * reply ) {
2011-04-06 06:19:45 -04:00
robj * o = objectCommandLookup ( c , key ) ;
if ( ! o ) addReply ( c , reply ) ;
return o ;
}
/* Object command allows to inspect the internals of an Redis Object.
2014-07-11 16:03:26 -04:00
* Usage : OBJECT < refcount | encoding | idletime > < key > */
2015-07-26 09:20:46 -04:00
void objectCommand ( client * c ) {
2011-04-06 06:19:45 -04:00
robj * o ;
if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " refcount " ) & & c - > argc = = 3 ) {
if ( ( o = objectCommandLookupOrReply ( c , c - > argv [ 2 ] , shared . nullbulk ) )
= = NULL ) return ;
addReplyLongLong ( c , o - > refcount ) ;
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " encoding " ) & & c - > argc = = 3 ) {
if ( ( o = objectCommandLookupOrReply ( c , c - > argv [ 2 ] , shared . nullbulk ) )
= = NULL ) return ;
addReplyBulkCString ( c , strEncoding ( o - > encoding ) ) ;
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " idletime " ) & & c - > argc = = 3 ) {
if ( ( o = objectCommandLookupOrReply ( c , c - > argv [ 2 ] , shared . nullbulk ) )
= = NULL ) return ;
2016-07-18 07:49:31 -04:00
if ( server . maxmemory_policy & MAXMEMORY_FLAG_LFU ) {
addReplyError ( c , " An LFU maxmemory policy is selected, idle time not tracked. Please note that when switching between policies at runtime LRU and LFU data will take some time to adjust. " ) ;
return ;
}
2014-03-20 06:55:18 -04:00
addReplyLongLong ( c , estimateObjectIdleTime ( o ) / 1000 ) ;
2016-07-15 06:12:52 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " freq " ) & & c - > argc = = 3 ) {
if ( ( o = objectCommandLookupOrReply ( c , c - > argv [ 2 ] , shared . nullbulk ) )
= = NULL ) return ;
2016-07-18 07:49:31 -04:00
if ( server . maxmemory_policy & MAXMEMORY_FLAG_LRU ) {
addReplyError ( c , " An LRU maxmemory policy is selected, access frequency not tracked. Please note that when switching between policies at runtime LRU and LFU data will take some time to adjust. " ) ;
return ;
}
2016-07-15 06:12:52 -04:00
addReplyLongLong ( c , o - > lru & 255 ) ;
2011-04-06 06:19:45 -04:00
} else {
2016-07-15 06:12:52 -04:00
addReplyError ( c , " Syntax error. Try OBJECT (refcount|encoding|idletime|freq) " ) ;
2011-04-06 06:19:45 -04:00
}
}