diff --git a/src/db.c b/src/db.c index e3eeb69428f74cc124f72b2cdf610c35243c0414..92a590822d537a8de509bbc272195d594a6cf5fb 100644 --- a/src/db.c +++ b/src/db.c @@ -415,8 +415,8 @@ void scanCallback(void *privdata, const dictEntry *de) { sds sdskey = dictGetKey(de); key = createStringObject(sdskey, sdslen(sdskey)); } else if (o->type == OBJ_SET) { - key = dictGetKey(de); - incrRefCount(key); + sds keysds = dictGetKey(de); + key = createStringObject(keysds,sdslen(keysds)); } else if (o->type == OBJ_HASH) { key = dictGetKey(de); incrRefCount(key); diff --git a/src/debug.c b/src/debug.c index 704e4bbf126200e49ef8960d6f6c93b3394cd1a1..61c1c3c885d1a0ce27364c6763b8ecd83adf2932 100644 --- a/src/debug.c +++ b/src/debug.c @@ -164,7 +164,9 @@ void computeDatasetDigest(unsigned char *final) { } else if (o->type == OBJ_SET) { setTypeIterator *si = setTypeInitIterator(o); robj *ele; - while((ele = setTypeNextObject(si)) != NULL) { + sds sdsele; + while((sdsele = setTypeNextObject(si)) != NULL) { + ele = createObject(OBJ_STRING,sdsele); xorObjectDigest(digest,ele); decrRefCount(ele); } diff --git a/src/networking.c b/src/networking.c index 044e9a4a3a8ff334dcffd39e4fd7bd04859a040e..ff2d29a8e4b1b52bf616c39a90dc4988896c74ef 100644 --- a/src/networking.c +++ b/src/networking.c @@ -117,13 +117,13 @@ client *createClient(int fd) { listSetDupMethod(c->reply,dupClientReplyValue); c->btype = BLOCKED_NONE; c->bpop.timeout = 0; - c->bpop.keys = dictCreate(&setDictType,NULL); + c->bpop.keys = dictCreate(&objectKeyPointerValueDictType,NULL); c->bpop.target = NULL; c->bpop.numreplicas = 0; c->bpop.reploffset = 0; c->woff = 0; c->watched_keys = listCreate(); - c->pubsub_channels = dictCreate(&setDictType,NULL); + c->pubsub_channels = dictCreate(&objectKeyPointerValueDictType,NULL); c->pubsub_patterns = listCreate(); c->peerid = NULL; listSetFreeMethod(c->pubsub_patterns,decrRefCountVoid); diff --git a/src/object.c b/src/object.c index 6990727df8f5390560914749f7c0d3a4e54a0d82..3a9f6092599b1e1b0b2aff51b0f5c1e42a665da1 100644 --- a/src/object.c +++ b/src/object.c @@ -364,13 +364,17 @@ int checkType(client *c, robj *o, int type) { return 0; } +int isSdsRepresentableAsLongLong(sds s, long long *llval) { + return string2ll(s,sdslen(s),llval) ? C_OK : C_ERR; +} + int isObjectRepresentableAsLongLong(robj *o, long long *llval) { serverAssertWithInfo(NULL,o,o->type == OBJ_STRING); if (o->encoding == OBJ_ENCODING_INT) { if (llval) *llval = (long) o->ptr; return C_OK; } else { - return string2ll(o->ptr,sdslen(o->ptr),llval) ? C_OK : C_ERR; + return isSdsRepresentableAsLongLong(o->ptr,llval); } } diff --git a/src/rdb.c b/src/rdb.c index 5566acb0010765dc37b3112880b0b27f29cdc58b..3d98a7673760936dfba3cf9fcdd2c245cac18aba 100644 --- a/src/rdb.c +++ b/src/rdb.c @@ -43,6 +43,7 @@ #define RDB_LOAD_NONE 0 #define RDB_LOAD_ENC (1<<0) #define RDB_LOAD_PLAIN (1<<1) +#define RDB_LOAD_SDS (1<<2) #define rdbExitReportCorruptRDB(reason) rdbCheckThenExit(reason, __LINE__); @@ -179,6 +180,7 @@ int rdbEncodeInteger(long long value, unsigned char *enc) { * rdbGenerincLoadStringObject() for more info. */ void *rdbLoadIntegerObject(rio *rdb, int enctype, int flags) { int plain = flags & RDB_LOAD_PLAIN; + int sds = flags & RDB_LOAD_SDS; int encode = flags & RDB_LOAD_ENC; unsigned char enc[4]; long long val; @@ -200,10 +202,10 @@ void *rdbLoadIntegerObject(rio *rdb, int enctype, int flags) { val = 0; /* anti-warning */ rdbExitReportCorruptRDB("Unknown RDB integer encoding type"); } - if (plain) { + if (plain || sds) { char buf[LONG_STR_SIZE], *p; int len = ll2string(buf,sizeof(buf),val); - p = zmalloc(len); + p = plain ? zmalloc(len) : sdsnewlen(NULL,len); memcpy(p,buf,len); return p; } else if (encode) { @@ -280,9 +282,10 @@ ssize_t rdbSaveLzfStringObject(rio *rdb, unsigned char *s, size_t len) { * rdbGenericLoadStringObject() function. */ void *rdbLoadLzfStringObject(rio *rdb, int flags) { int plain = flags & RDB_LOAD_PLAIN; + int sds = flags & RDB_LOAD_SDS; unsigned int len, clen; unsigned char *c = NULL; - sds val = NULL; + char *val = NULL; if ((clen = rdbLoadLen(rdb,NULL)) == RDB_LENERR) return NULL; if ((len = rdbLoadLen(rdb,NULL)) == RDB_LENERR) return NULL; @@ -292,7 +295,7 @@ void *rdbLoadLzfStringObject(rio *rdb, int flags) { if (plain) { val = zmalloc(len); } else { - if ((val = sdsnewlen(NULL,len)) == NULL) goto err; + val = sdsnewlen(NULL,len); } /* Load the compressed representation and uncompress it to target. */ @@ -300,10 +303,11 @@ void *rdbLoadLzfStringObject(rio *rdb, int flags) { if (lzf_decompress(c,clen,val,len) == 0) goto err; zfree(c); - if (plain) + if (plain || sds) { return val; - else + } else { return createObject(OBJ_STRING,val); + } err: zfree(c); if (plain) @@ -366,7 +370,7 @@ ssize_t rdbSaveLongLongAsStringObject(rio *rdb, long long value) { return nwritten; } -/* Like rdbSaveStringObjectRaw() but handle encoded objects */ +/* Like rdbSaveRawString() gets a Redis object instead. */ int rdbSaveStringObject(rio *rdb, robj *obj) { /* Avoid to decode the object, then encode it again, if the * object is already integer encoded. */ @@ -387,10 +391,12 @@ int rdbSaveStringObject(rio *rdb, robj *obj) { * no longer guarantees that obj->ptr is an SDS string. * RDB_LOAD_PLAIN: Return a plain string allocated with zmalloc() * instead of a Redis object. + * RDB_LOAD_SDS: Return an SDS string instead of a Redis object. */ void *rdbGenericLoadStringObject(rio *rdb, int flags) { int encode = flags & RDB_LOAD_ENC; int plain = flags & RDB_LOAD_PLAIN; + int sds = flags & RDB_LOAD_SDS; int isencoded; uint32_t len; @@ -409,7 +415,17 @@ void *rdbGenericLoadStringObject(rio *rdb, int flags) { } if (len == RDB_LENERR) return NULL; - if (!plain) { + if (plain || sds) { + void *buf = plain ? zmalloc(len) : sdsnewlen(NULL,len); + if (len && rioRead(rdb,buf,len) == 0) { + if (plain) + zfree(buf); + else + sdsfree(buf); + return NULL; + } + return buf; + } else { robj *o = encode ? createStringObject(NULL,len) : createRawStringObject(NULL,len); if (len && rioRead(rdb,o->ptr,len) == 0) { @@ -417,13 +433,6 @@ void *rdbGenericLoadStringObject(rio *rdb, int flags) { return NULL; } return o; - } else { - void *buf = zmalloc(len); - if (len && rioRead(rdb,buf,len) == 0) { - zfree(buf); - return NULL; - } - return buf; } } @@ -583,8 +592,9 @@ ssize_t rdbSaveObject(rio *rdb, robj *o) { nwritten += n; while((de = dictNext(di)) != NULL) { - robj *eleobj = dictGetKey(de); - if ((n = rdbSaveStringObject(rdb,eleobj)) == -1) return -1; + sds ele = dictGetKey(de); + if ((n = rdbSaveRawString(rdb,(unsigned char*)ele,sdslen(ele))) + == -1) return -1; nwritten += n; } dictReleaseIterator(di); @@ -959,7 +969,7 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) { decrRefCount(ele); } } else if (rdbtype == RDB_TYPE_SET) { - /* Read list/set value */ + /* Read Set value */ if ((len = rdbLoadLen(rdb,NULL)) == RDB_LENERR) return NULL; /* Use a regular set when there are too many entries. */ @@ -973,15 +983,17 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) { o = createIntsetObject(); } - /* Load every single element of the list/set */ + /* Load every single element of the set */ for (i = 0; i < len; i++) { long long llval; - if ((ele = rdbLoadEncodedStringObject(rdb)) == NULL) return NULL; - ele = tryObjectEncoding(ele); + sds sdsele; + + if ((sdsele = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS)) == NULL) + return NULL; if (o->encoding == OBJ_ENCODING_INTSET) { /* Fetch integer value from element */ - if (isObjectRepresentableAsLongLong(ele,&llval) == C_OK) { + if (isSdsRepresentableAsLongLong(sdsele,&llval) == C_OK) { o->ptr = intsetAdd(o->ptr,llval,NULL); } else { setTypeConvert(o,OBJ_ENCODING_HT); @@ -992,9 +1004,9 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) { /* This will also be called when the set was just converted * to a regular hash table encoded set */ if (o->encoding == OBJ_ENCODING_HT) { - dictAdd((dict*)o->ptr,ele,NULL); + dictAdd((dict*)o->ptr,sdsele,NULL); } else { - decrRefCount(ele); + sdsfree(sdsele); } } } else if (rdbtype == RDB_TYPE_ZSET) { diff --git a/src/server.c b/src/server.c index 2194fbde0740d1fcef1040dfb8fe07e36cf8eebd..bd45e7d293d034d4563fe76914748f21b250e370 100644 --- a/src/server.c +++ b/src/server.c @@ -531,8 +531,9 @@ unsigned int dictEncObjHash(const void *key) { } } -/* Sets type hash table */ -dictType setDictType = { +/* Generic hash table type where keys are Redis Objects, Values + * dummy pointers. */ +dictType objectKeyPointerValueDictType = { dictEncObjHash, /* hash function */ NULL, /* key dup */ NULL, /* val dup */ @@ -541,13 +542,23 @@ dictType setDictType = { NULL /* val destructor */ }; +/* Set dictionary type. Keys are SDS strings, values are ot used. */ +dictType setDictType = { + dictSdsHash, /* hash function */ + NULL, /* key dup */ + NULL, /* val dup */ + dictSdsKeyCompare, /* key compare */ + dictSdsDestructor, /* key destructor */ + NULL /* val destructor */ +}; + /* Sorted sets hash (note: a skiplist is used in addition to the hash table) */ dictType zsetDictType = { dictEncObjHash, /* hash function */ NULL, /* key dup */ NULL, /* val dup */ dictEncObjKeyCompare, /* key compare */ - dictObjectDestructor, /* key destructor */ + dictObjectDestructor, /* key destructor */ NULL /* val destructor */ }; @@ -1834,7 +1845,7 @@ void initServer(void) { server.db[j].dict = dictCreate(&dbDictType,NULL); server.db[j].expires = dictCreate(&keyptrDictType,NULL); server.db[j].blocking_keys = dictCreate(&keylistDictType,NULL); - server.db[j].ready_keys = dictCreate(&setDictType,NULL); + server.db[j].ready_keys = dictCreate(&objectKeyPointerValueDictType,NULL); server.db[j].watched_keys = dictCreate(&keylistDictType,NULL); server.db[j].eviction_pool = evictionPoolAlloc(); server.db[j].id = j; diff --git a/src/server.h b/src/server.h index 67d5c22788816cd163116a2ec76ff3c4602fe96c..50899f8fe02bc4182167bd71c4d31daf7ebfe0df 100644 --- a/src/server.h +++ b/src/server.h @@ -1043,6 +1043,7 @@ typedef struct { extern struct redisServer server; extern struct sharedObjectsStruct shared; +extern dictType objectKeyPointerValueDictType; extern dictType setDictType; extern dictType zsetDictType; extern dictType clusterNodesDictType; @@ -1173,6 +1174,7 @@ robj *createStringObject(const char *ptr, size_t len); robj *createRawStringObject(const char *ptr, size_t len); robj *createEmbeddedStringObject(const char *ptr, size_t len); robj *dupStringObject(robj *o); +int isSdsRepresentableAsLongLong(sds s, long long *llval); int isObjectRepresentableAsLongLong(robj *o, long long *llongval); robj *tryObjectEncoding(robj *o); robj *getDecodedObject(robj *o); @@ -1313,15 +1315,15 @@ unsigned int getLRUClock(void); const char *maxmemoryToString(void); /* Set data type */ -robj *setTypeCreate(robj *value); -int setTypeAdd(robj *subject, robj *value); -int setTypeRemove(robj *subject, robj *value); -int setTypeIsMember(robj *subject, robj *value); +robj *setTypeCreate(sds value); +int setTypeAdd(robj *subject, sds value); +int setTypeRemove(robj *subject, sds value); +int setTypeIsMember(robj *subject, sds value); setTypeIterator *setTypeInitIterator(robj *subject); void setTypeReleaseIterator(setTypeIterator *si); -int setTypeNext(setTypeIterator *si, robj **objele, int64_t *llele); -robj *setTypeNextObject(setTypeIterator *si); -int setTypeRandomElement(robj *setobj, robj **objele, int64_t *llele); +int setTypeNext(setTypeIterator *si, sds *sdsele, int64_t *llele); +sds setTypeNextObject(setTypeIterator *si); +int setTypeRandomElement(robj *setobj, sds *sdsele, int64_t *llele); unsigned long setTypeRandomElements(robj *set, unsigned long count, robj *aux_set); unsigned long setTypeSize(robj *subject); void setTypeConvert(robj *subject, int enc); diff --git a/src/sort.c b/src/sort.c index f7c220e186ad92ae53f46f31313663179809895b..af185dbd423ea6d308c930f803c73b7889b5ae10 100644 --- a/src/sort.c +++ b/src/sort.c @@ -380,9 +380,9 @@ void sortCommand(client *c) { listTypeReleaseIterator(li); } else if (sortval->type == OBJ_SET) { setTypeIterator *si = setTypeInitIterator(sortval); - robj *ele; - while((ele = setTypeNextObject(si)) != NULL) { - vector[j].obj = ele; + sds sdsele; + while((sdsele = setTypeNextObject(si)) != NULL) { + vector[j].obj = createObject(OBJ_STRING,sdsele); vector[j].u.score = 0; vector[j].u.cmpobj = NULL; j++; diff --git a/src/t_set.c b/src/t_set.c index 03f48d945db6f983dd27ff165e27c67daab6bc9d..7a2a77ff6aa0b959a9c45d1e1849c94c4b592b44 100644 --- a/src/t_set.c +++ b/src/t_set.c @@ -39,26 +39,28 @@ void sunionDiffGenericCommand(client *c, robj **setkeys, int setnum, /* Factory method to return a set that *can* hold "value". When the object has * an integer-encodable value, an intset will be returned. Otherwise a regular * hash table. */ -robj *setTypeCreate(robj *value) { - if (isObjectRepresentableAsLongLong(value,NULL) == C_OK) +robj *setTypeCreate(sds value) { + if (isSdsRepresentableAsLongLong(value,NULL) == C_OK) return createIntsetObject(); return createSetObject(); } -/* Add the specified value into a set. The function takes care of incrementing - * the reference count of the object if needed in order to retain a copy. +/* Add the specified value into a set. * * If the value was already member of the set, nothing is done and 0 is * returned, otherwise the new element is added and 1 is returned. */ -int setTypeAdd(robj *subject, robj *value) { +int setTypeAdd(robj *subject, sds value) { long long llval; if (subject->encoding == OBJ_ENCODING_HT) { - if (dictAdd(subject->ptr,value,NULL) == DICT_OK) { - incrRefCount(value); + dict *ht = subject->ptr; + dictEntry *de = dictAddRaw(ht,value); + if (de) { + dictSetKey(ht,de,sdsdup(value)); + dictSetVal(ht,de,NULL); return 1; } } else if (subject->encoding == OBJ_ENCODING_INTSET) { - if (isObjectRepresentableAsLongLong(value,&llval) == C_OK) { + if (isSdsRepresentableAsLongLong(value,&llval) == C_OK) { uint8_t success = 0; subject->ptr = intsetAdd(subject->ptr,llval,&success); if (success) { @@ -74,9 +76,7 @@ int setTypeAdd(robj *subject, robj *value) { /* The set *was* an intset and this value is not integer * encodable, so dictAdd should always work. */ - serverAssertWithInfo(NULL,value, - dictAdd(subject->ptr,value,NULL) == DICT_OK); - incrRefCount(value); + serverAssert(dictAdd(subject->ptr,sdsdup(value),NULL) == DICT_OK); return 1; } } else { @@ -85,7 +85,7 @@ int setTypeAdd(robj *subject, robj *value) { return 0; } -int setTypeRemove(robj *setobj, robj *value) { +int setTypeRemove(robj *setobj, sds value) { long long llval; if (setobj->encoding == OBJ_ENCODING_HT) { if (dictDelete(setobj->ptr,value) == DICT_OK) { @@ -93,7 +93,7 @@ int setTypeRemove(robj *setobj, robj *value) { return 1; } } else if (setobj->encoding == OBJ_ENCODING_INTSET) { - if (isObjectRepresentableAsLongLong(value,&llval) == C_OK) { + if (isSdsRepresentableAsLongLong(value,&llval) == C_OK) { int success; setobj->ptr = intsetRemove(setobj->ptr,llval,&success); if (success) return 1; @@ -104,12 +104,12 @@ int setTypeRemove(robj *setobj, robj *value) { return 0; } -int setTypeIsMember(robj *subject, robj *value) { +int setTypeIsMember(robj *subject, sds value) { long long llval; if (subject->encoding == OBJ_ENCODING_HT) { return dictFind((dict*)subject->ptr,value) != NULL; } else if (subject->encoding == OBJ_ENCODING_INTSET) { - if (isObjectRepresentableAsLongLong(value,&llval) == C_OK) { + if (isSdsRepresentableAsLongLong(value,&llval) == C_OK) { return intsetFind((intset*)subject->ptr,llval); } } else { @@ -141,28 +141,26 @@ void setTypeReleaseIterator(setTypeIterator *si) { /* Move to the next entry in the set. Returns the object at the current * position. * - * Since set elements can be internally be stored as redis objects or + * Since set elements can be internally be stored as SDS strings or * simple arrays of integers, setTypeNext returns the encoding of the * set object you are iterating, and will populate the appropriate pointer - * (objele) or (llele) accordingly. + * (sdsele) or (llele) accordingly. * - * Note that both the objele and llele pointers should be passed and cannot + * Note that both the sdsele and llele pointers should be passed and cannot * be NULL since the function will try to defensively populate the non * used field with values which are easy to trap if misused. * - * When there are no longer elements -1 is returned. - * Returned objects ref count is not incremented, so this function is - * copy on write friendly. */ -int setTypeNext(setTypeIterator *si, robj **objele, int64_t *llele) { + * When there are no longer elements -1 is returned. */ +int setTypeNext(setTypeIterator *si, sds *sdsele, int64_t *llele) { if (si->encoding == OBJ_ENCODING_HT) { dictEntry *de = dictNext(si->di); if (de == NULL) return -1; - *objele = dictGetKey(de); + *sdsele = dictGetKey(de); *llele = -123456789; /* Not needed. Defensive. */ } else if (si->encoding == OBJ_ENCODING_INTSET) { if (!intsetGet(si->subject->ptr,si->ii++,llele)) return -1; - *objele = NULL; /* Not needed. Defensive. */ + *sdsele = NULL; /* Not needed. Defensive. */ } else { serverPanic("Wrong set encoding in setTypeNext"); } @@ -170,25 +168,24 @@ int setTypeNext(setTypeIterator *si, robj **objele, int64_t *llele) { } /* The not copy on write friendly version but easy to use version - * of setTypeNext() is setTypeNextObject(), returning new objects - * or incrementing the ref count of returned objects. So if you don't - * retain a pointer to this object you should call decrRefCount() against it. + * of setTypeNext() is setTypeNextObject(), returning new SDS + * strings. So if you don't retain a pointer to this object you should call + * sdsfree() against it. * * This function is the way to go for write operations where COW is not - * an issue as the result will be anyway of incrementing the ref count. */ -robj *setTypeNextObject(setTypeIterator *si) { + * an issue. */ +sds setTypeNextObject(setTypeIterator *si) { int64_t intele; - robj *objele; + sds sdsele; int encoding; - encoding = setTypeNext(si,&objele,&intele); + encoding = setTypeNext(si,&sdsele,&intele); switch(encoding) { case -1: return NULL; case OBJ_ENCODING_INTSET: - return createStringObjectFromLongLong(intele); + return sdsfromlonglong(intele); case OBJ_ENCODING_HT: - incrRefCount(objele); - return objele; + return sdsdup(sdsele); default: serverPanic("Unsupported encoding"); } @@ -197,7 +194,7 @@ robj *setTypeNextObject(setTypeIterator *si) { /* Return random element from a non empty set. * The returned element can be a int64_t value if the set is encoded - * as an "intset" blob of integers, or a redis object if the set + * as an "intset" blob of integers, or an SDS string if the set * is a regular set. * * The caller provides both pointers to be populated with the right @@ -205,21 +202,17 @@ robj *setTypeNextObject(setTypeIterator *si) { * field of the object and is used by the caller to check if the * int64_t pointer or the redis object pointer was populated. * - * Note that both the objele and llele pointers should be passed and cannot + * Note that both the sdsele and llele pointers should be passed and cannot * be NULL since the function will try to defensively populate the non - * used field with values which are easy to trap if misused. - * - * When an object is returned (the set was a real set) the ref count - * of the object is not incremented so this function can be considered - * copy on write friendly. */ -int setTypeRandomElement(robj *setobj, robj **objele, int64_t *llele) { + * used field with values which are easy to trap if misused. */ +int setTypeRandomElement(robj *setobj, sds *sdsele, int64_t *llele) { if (setobj->encoding == OBJ_ENCODING_HT) { dictEntry *de = dictGetRandomKey(setobj->ptr); - *objele = dictGetKey(de); + *sdsele = dictGetKey(de); *llele = -123456789; /* Not needed. Defensive. */ } else if (setobj->encoding == OBJ_ENCODING_INTSET) { *llele = intsetRandom(setobj->ptr); - *objele = NULL; /* Not needed. Defensive. */ + *sdsele = NULL; /* Not needed. Defensive. */ } else { serverPanic("Unknown set encoding"); } @@ -247,7 +240,7 @@ void setTypeConvert(robj *setobj, int enc) { if (enc == OBJ_ENCODING_HT) { int64_t intele; dict *d = dictCreate(&setDictType,NULL); - robj *element; + sds element; /* Presize the dict to avoid rehashing */ dictExpand(d,intsetLen(setobj->ptr)); @@ -255,9 +248,8 @@ void setTypeConvert(robj *setobj, int enc) { /* To add the elements we extract integers and create redis objects */ si = setTypeInitIterator(setobj); while (setTypeNext(si,&element,&intele) != -1) { - element = createStringObjectFromLongLong(intele); - serverAssertWithInfo(NULL,element, - dictAdd(d,element,NULL) == DICT_OK); + element = sdsfromlonglong(intele); + serverAssert(dictAdd(d,element,NULL) == DICT_OK); } setTypeReleaseIterator(si); @@ -275,7 +267,7 @@ void saddCommand(client *c) { set = lookupKeyWrite(c->db,c->argv[1]); if (set == NULL) { - set = setTypeCreate(c->argv[2]); + set = setTypeCreate(c->argv[2]->ptr); dbAdd(c->db,c->argv[1],set); } else { if (set->type != OBJ_SET) { @@ -285,8 +277,7 @@ void saddCommand(client *c) { } for (j = 2; j < c->argc; j++) { - c->argv[j] = tryObjectEncoding(c->argv[j]); - if (setTypeAdd(set,c->argv[j])) added++; + if (setTypeAdd(set,c->argv[j]->ptr)) added++; } if (added) { signalModifiedKey(c->db,c->argv[1]); @@ -304,7 +295,7 @@ void sremCommand(client *c) { checkType(c,set,OBJ_SET)) return; for (j = 2; j < c->argc; j++) { - if (setTypeRemove(set,c->argv[j])) { + if (setTypeRemove(set,c->argv[j]->ptr)) { deleted++; if (setTypeSize(set) == 0) { dbDelete(c->db,c->argv[1]); @@ -328,7 +319,7 @@ void smoveCommand(client *c) { robj *srcset, *dstset, *ele; srcset = lookupKeyWrite(c->db,c->argv[1]); dstset = lookupKeyWrite(c->db,c->argv[2]); - ele = c->argv[3] = tryObjectEncoding(c->argv[3]); + ele = c->argv[3]; /* If the source key does not exist return 0 */ if (srcset == NULL) { @@ -343,12 +334,13 @@ void smoveCommand(client *c) { /* If srcset and dstset are equal, SMOVE is a no-op */ if (srcset == dstset) { - addReply(c,setTypeIsMember(srcset,ele) ? shared.cone : shared.czero); + addReply(c,setTypeIsMember(srcset,ele->ptr) ? + shared.cone : shared.czero); return; } /* If the element cannot be removed from the src set, return 0. */ - if (!setTypeRemove(srcset,ele)) { + if (!setTypeRemove(srcset,ele->ptr)) { addReply(c,shared.czero); return; } @@ -365,12 +357,12 @@ void smoveCommand(client *c) { /* Create the destination set when it doesn't exist */ if (!dstset) { - dstset = setTypeCreate(ele); + dstset = setTypeCreate(ele->ptr); dbAdd(c->db,c->argv[2],dstset); } /* An extra key has changed when ele was successfully added to dstset */ - if (setTypeAdd(dstset,ele)) { + if (setTypeAdd(dstset,ele->ptr)) { server.dirty++; notifyKeyspaceEvent(NOTIFY_SET,"sadd",c->argv[2],c->db->id); } @@ -383,8 +375,7 @@ void sismemberCommand(client *c) { if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL || checkType(c,set,OBJ_SET)) return; - c->argv[2] = tryObjectEncoding(c->argv[2]); - if (setTypeIsMember(set,c->argv[2])) + if (setTypeIsMember(set,c->argv[2]->ptr)) addReply(c,shared.cone); else addReply(c,shared.czero); @@ -457,7 +448,7 @@ void spopWithCountCommand(client *c) { return; } - /* Case 2 and 3 require to replicate SPOP as a set of SERM commands. + /* Case 2 and 3 require to replicate SPOP as a set of SREM commands. * Prepare our replication argument vector. Also send the array length * which is common to both the code paths. */ robj *propargv[3]; @@ -466,6 +457,7 @@ void spopWithCountCommand(client *c) { addReplyMultiBulkLen(c,count); /* Common iteration vars. */ + sds sdsele; robj *objele; int encoding; int64_t llele; @@ -480,17 +472,18 @@ void spopWithCountCommand(client *c) { * the set. */ if (remaining*SPOP_MOVE_STRATEGY_MUL > count) { while(count--) { - encoding = setTypeRandomElement(set,&objele,&llele); + /* Emit and remove. */ + encoding = setTypeRandomElement(set,&sdsele,&llele); if (encoding == OBJ_ENCODING_INTSET) { + addReplyBulkLongLong(c,llele); objele = createStringObjectFromLongLong(llele); + set->ptr = intsetRemove(set->ptr,llele,NULL); } else { - incrRefCount(objele); + addReplyBulkCBuffer(c,sdsele,sdslen(sdsele)); + objele = createStringObject(sdsele,sdslen(sdsele)); + setTypeRemove(set,sdsele); } - /* Return the element to the client and remove from the set. */ - addReplyBulk(c,objele); - setTypeRemove(set,objele); - /* Replicate/AOF this command as an SREM operation */ propargv[2] = objele; alsoPropagate(server.sremCommand,c->db->id,propargv,3, @@ -510,16 +503,16 @@ void spopWithCountCommand(client *c) { /* Create a new set with just the remaining elements. */ while(remaining--) { - encoding = setTypeRandomElement(set,&objele,&llele); + encoding = setTypeRandomElement(set,&sdsele,&llele); if (encoding == OBJ_ENCODING_INTSET) { - objele = createStringObjectFromLongLong(llele); + sdsele = sdsfromlonglong(llele); } else { - incrRefCount(objele); + sdsele = sdsdup(sdsele); } - if (!newset) newset = setTypeCreate(objele); - setTypeAdd(newset,objele); - setTypeRemove(set,objele); - decrRefCount(objele); + if (!newset) newset = setTypeCreate(sdsele); + setTypeAdd(newset,sdsele); + setTypeRemove(set,sdsele); + sdsfree(sdsele); } /* Assign the new set as the key value. */ @@ -529,19 +522,19 @@ void spopWithCountCommand(client *c) { /* Tranfer the old set to the client and release it. */ setTypeIterator *si; si = setTypeInitIterator(set); - while((encoding = setTypeNext(si,&objele,&llele)) != -1) { + while((encoding = setTypeNext(si,&sdsele,&llele)) != -1) { if (encoding == OBJ_ENCODING_INTSET) { + addReplyBulkLongLong(c,llele); objele = createStringObjectFromLongLong(llele); } else { - incrRefCount(objele); + addReplyBulkCBuffer(c,sdsele,sdslen(sdsele)); + objele = createStringObject(sdsele,sdslen(sdsele)); } - addReplyBulk(c,objele); /* Replicate/AOF this command as an SREM operation */ propargv[2] = objele; alsoPropagate(server.sremCommand,c->db->id,propargv,3, PROPAGATE_AOF|PROPAGATE_REPL); - decrRefCount(objele); } setTypeReleaseIterator(si); @@ -558,6 +551,7 @@ void spopWithCountCommand(client *c) { void spopCommand(client *c) { robj *set, *ele, *aux; + sds sdsele; int64_t llele; int encoding; @@ -575,15 +569,15 @@ void spopCommand(client *c) { checkType(c,set,OBJ_SET)) return; /* Get a random element from the set */ - encoding = setTypeRandomElement(set,&ele,&llele); + encoding = setTypeRandomElement(set,&sdsele,&llele); /* Remove the element from the set */ if (encoding == OBJ_ENCODING_INTSET) { ele = createStringObjectFromLongLong(llele); set->ptr = intsetRemove(set->ptr,llele,NULL); } else { - incrRefCount(ele); - setTypeRemove(set,ele); + ele = createStringObject(sdsele,sdslen(sdsele)); + setTypeRemove(set,ele->ptr); } notifyKeyspaceEvent(NOTIFY_SET,"spop",c->argv[1],c->db->id); @@ -591,11 +585,11 @@ void spopCommand(client *c) { /* Replicate/AOF this command as an SREM operation */ aux = createStringObject("SREM",4); rewriteClientCommandVector(c,3,aux,c->argv[1],ele); - decrRefCount(ele); decrRefCount(aux); /* Add the element to the reply */ addReplyBulk(c,ele); + decrRefCount(ele); /* Delete the set if it's empty */ if (setTypeSize(set) == 0) { @@ -620,7 +614,8 @@ void srandmemberWithCountCommand(client *c) { long l; unsigned long count, size; int uniq = 1; - robj *set, *ele; + robj *set; + sds ele; int64_t llele; int encoding; @@ -657,7 +652,7 @@ void srandmemberWithCountCommand(client *c) { if (encoding == OBJ_ENCODING_INTSET) { addReplyBulkLongLong(c,llele); } else { - addReplyBulk(c,ele); + addReplyBulkCBuffer(c,ele,sdslen(ele)); } } return; @@ -672,7 +667,7 @@ void srandmemberWithCountCommand(client *c) { } /* For CASE 3 and CASE 4 we need an auxiliary dictionary. */ - d = dictCreate(&setDictType,NULL); + d = dictCreate(&objectKeyPointerValueDictType,NULL); /* CASE 3: * The number of elements inside the set is not greater than @@ -694,7 +689,7 @@ void srandmemberWithCountCommand(client *c) { if (encoding == OBJ_ENCODING_INTSET) { retval = dictAdd(d,createStringObjectFromLongLong(llele),NULL); } else { - retval = dictAdd(d,dupStringObject(ele),NULL); + retval = dictAdd(d,createStringObject(ele,sdslen(ele)),NULL); } serverAssert(retval == DICT_OK); } @@ -717,21 +712,22 @@ void srandmemberWithCountCommand(client *c) { * to reach the specified count. */ else { unsigned long added = 0; + robj *objele; while(added < count) { encoding = setTypeRandomElement(set,&ele,&llele); if (encoding == OBJ_ENCODING_INTSET) { - ele = createStringObjectFromLongLong(llele); + objele = createStringObjectFromLongLong(llele); } else { - ele = dupStringObject(ele); + objele = createStringObject(ele,sdslen(ele)); } /* Try to add the object to the dictionary. If it already exists * free it, otherwise increment the number of objects we have * in the result dictionary. */ - if (dictAdd(d,ele,NULL) == DICT_OK) + if (dictAdd(d,objele,NULL) == DICT_OK) added++; else - decrRefCount(ele); + decrRefCount(objele); } } @@ -750,7 +746,8 @@ void srandmemberWithCountCommand(client *c) { } void srandmemberCommand(client *c) { - robj *set, *ele; + robj *set; + sds ele; int64_t llele; int encoding; @@ -769,7 +766,7 @@ void srandmemberCommand(client *c) { if (encoding == OBJ_ENCODING_INTSET) { addReplyBulkLongLong(c,llele); } else { - addReplyBulk(c,ele); + addReplyBulkCBuffer(c,ele,sdslen(ele)); } } @@ -789,7 +786,8 @@ void sinterGenericCommand(client *c, robj **setkeys, unsigned long setnum, robj *dstkey) { robj **sets = zmalloc(sizeof(robj*)*setnum); setTypeIterator *si; - robj *eleobj, *dstset = NULL; + robj *dstset = NULL; + sds elesds; int64_t intobj; void *replylen = NULL; unsigned long j, cardinality = 0; @@ -839,7 +837,7 @@ void sinterGenericCommand(client *c, robj **setkeys, * the element against all the other sets, if at least one set does * not include the element it is discarded */ si = setTypeInitIterator(sets[0]); - while((encoding = setTypeNext(si,&eleobj,&intobj)) != -1) { + while((encoding = setTypeNext(si,&elesds,&intobj)) != -1) { for (j = 1; j < setnum; j++) { if (sets[j] == sets[0]) continue; if (encoding == OBJ_ENCODING_INTSET) { @@ -852,25 +850,15 @@ void sinterGenericCommand(client *c, robj **setkeys, * have to use the generic function, creating an object * for this */ } else if (sets[j]->encoding == OBJ_ENCODING_HT) { - eleobj = createStringObjectFromLongLong(intobj); - if (!setTypeIsMember(sets[j],eleobj)) { - decrRefCount(eleobj); + elesds = sdsfromlonglong(intobj); + if (!setTypeIsMember(sets[j],elesds)) { + sdsfree(elesds); break; } - decrRefCount(eleobj); + sdsfree(elesds); } } else if (encoding == OBJ_ENCODING_HT) { - /* Optimization... if the source object is integer - * encoded AND the target set is an intset, we can get - * a much faster path. */ - if (eleobj->encoding == OBJ_ENCODING_INT && - sets[j]->encoding == OBJ_ENCODING_INTSET && - !intsetFind((intset*)sets[j]->ptr,(long)eleobj->ptr)) - { - break; - /* else... object to object check is easy as we use the - * type agnostic API here. */ - } else if (!setTypeIsMember(sets[j],eleobj)) { + if (!setTypeIsMember(sets[j],elesds)) { break; } } @@ -880,17 +868,17 @@ void sinterGenericCommand(client *c, robj **setkeys, if (j == setnum) { if (!dstkey) { if (encoding == OBJ_ENCODING_HT) - addReplyBulk(c,eleobj); + addReplyBulkCBuffer(c,elesds,sdslen(elesds)); else addReplyBulkLongLong(c,intobj); cardinality++; } else { if (encoding == OBJ_ENCODING_INTSET) { - eleobj = createStringObjectFromLongLong(intobj); - setTypeAdd(dstset,eleobj); - decrRefCount(eleobj); + elesds = sdsfromlonglong(intobj); + setTypeAdd(dstset,elesds); + sdsfree(elesds); } else { - setTypeAdd(dstset,eleobj); + setTypeAdd(dstset,elesds); } } } @@ -937,7 +925,8 @@ void sunionDiffGenericCommand(client *c, robj **setkeys, int setnum, robj *dstkey, int op) { robj **sets = zmalloc(sizeof(robj*)*setnum); setTypeIterator *si; - robj *ele, *dstset = NULL; + robj *dstset = NULL; + sds ele; int j, cardinality = 0; int diff_algo = 1; @@ -1003,7 +992,7 @@ void sunionDiffGenericCommand(client *c, robj **setkeys, int setnum, si = setTypeInitIterator(sets[j]); while((ele = setTypeNextObject(si)) != NULL) { if (setTypeAdd(dstset,ele)) cardinality++; - decrRefCount(ele); + sdsfree(ele); } setTypeReleaseIterator(si); } @@ -1028,7 +1017,7 @@ void sunionDiffGenericCommand(client *c, robj **setkeys, int setnum, setTypeAdd(dstset,ele); cardinality++; } - decrRefCount(ele); + sdsfree(ele); } setTypeReleaseIterator(si); } else if (op == SET_OP_DIFF && sets[0] && diff_algo == 2) { @@ -1049,7 +1038,7 @@ void sunionDiffGenericCommand(client *c, robj **setkeys, int setnum, } else { if (setTypeRemove(dstset,ele)) cardinality--; } - decrRefCount(ele); + sdsfree(ele); } setTypeReleaseIterator(si); @@ -1064,8 +1053,8 @@ void sunionDiffGenericCommand(client *c, robj **setkeys, int setnum, addReplyMultiBulkLen(c,cardinality); si = setTypeInitIterator(dstset); while((ele = setTypeNextObject(si)) != NULL) { - addReplyBulk(c,ele); - decrRefCount(ele); + addReplyBulkCBuffer(c,ele,sdslen(ele)); + sdsfree(ele); } setTypeReleaseIterator(si); decrRefCount(dstset);