mirror of
https://github.com/fluencelabs/redis
synced 2025-06-18 03:31:21 +00:00
redis.c split into many different C files.
networking related stuff moved into networking.c moved more code more work on layout of source code SDS instantaneuos memory saving. By Pieter and Salvatore at VMware ;) cleanly compiling again after the first split, now splitting it in more C files moving more things around... work in progress split replication code splitting more Sets split Hash split replication split even more splitting more splitting minor change
This commit is contained in:
349
src/t_set.c
Normal file
349
src/t_set.c
Normal file
@ -0,0 +1,349 @@
|
||||
#include "redis.h"
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Set Commands
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
void saddCommand(redisClient *c) {
|
||||
robj *set;
|
||||
|
||||
set = lookupKeyWrite(c->db,c->argv[1]);
|
||||
if (set == NULL) {
|
||||
set = createSetObject();
|
||||
dbAdd(c->db,c->argv[1],set);
|
||||
} else {
|
||||
if (set->type != REDIS_SET) {
|
||||
addReply(c,shared.wrongtypeerr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (dictAdd(set->ptr,c->argv[2],NULL) == DICT_OK) {
|
||||
incrRefCount(c->argv[2]);
|
||||
server.dirty++;
|
||||
addReply(c,shared.cone);
|
||||
} else {
|
||||
addReply(c,shared.czero);
|
||||
}
|
||||
}
|
||||
|
||||
void sremCommand(redisClient *c) {
|
||||
robj *set;
|
||||
|
||||
if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
|
||||
checkType(c,set,REDIS_SET)) return;
|
||||
|
||||
if (dictDelete(set->ptr,c->argv[2]) == DICT_OK) {
|
||||
server.dirty++;
|
||||
if (htNeedsResize(set->ptr)) dictResize(set->ptr);
|
||||
if (dictSize((dict*)set->ptr) == 0) dbDelete(c->db,c->argv[1]);
|
||||
addReply(c,shared.cone);
|
||||
} else {
|
||||
addReply(c,shared.czero);
|
||||
}
|
||||
}
|
||||
|
||||
void smoveCommand(redisClient *c) {
|
||||
robj *srcset, *dstset;
|
||||
|
||||
srcset = lookupKeyWrite(c->db,c->argv[1]);
|
||||
dstset = lookupKeyWrite(c->db,c->argv[2]);
|
||||
|
||||
/* If the source key does not exist return 0, if it's of the wrong type
|
||||
* raise an error */
|
||||
if (srcset == NULL || srcset->type != REDIS_SET) {
|
||||
addReply(c, srcset ? shared.wrongtypeerr : shared.czero);
|
||||
return;
|
||||
}
|
||||
/* Error if the destination key is not a set as well */
|
||||
if (dstset && dstset->type != REDIS_SET) {
|
||||
addReply(c,shared.wrongtypeerr);
|
||||
return;
|
||||
}
|
||||
/* Remove the element from the source set */
|
||||
if (dictDelete(srcset->ptr,c->argv[3]) == DICT_ERR) {
|
||||
/* Key not found in the src set! return zero */
|
||||
addReply(c,shared.czero);
|
||||
return;
|
||||
}
|
||||
if (dictSize((dict*)srcset->ptr) == 0 && srcset != dstset)
|
||||
dbDelete(c->db,c->argv[1]);
|
||||
server.dirty++;
|
||||
/* Add the element to the destination set */
|
||||
if (!dstset) {
|
||||
dstset = createSetObject();
|
||||
dbAdd(c->db,c->argv[2],dstset);
|
||||
}
|
||||
if (dictAdd(dstset->ptr,c->argv[3],NULL) == DICT_OK)
|
||||
incrRefCount(c->argv[3]);
|
||||
addReply(c,shared.cone);
|
||||
}
|
||||
|
||||
void sismemberCommand(redisClient *c) {
|
||||
robj *set;
|
||||
|
||||
if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
|
||||
checkType(c,set,REDIS_SET)) return;
|
||||
|
||||
if (dictFind(set->ptr,c->argv[2]))
|
||||
addReply(c,shared.cone);
|
||||
else
|
||||
addReply(c,shared.czero);
|
||||
}
|
||||
|
||||
void scardCommand(redisClient *c) {
|
||||
robj *o;
|
||||
dict *s;
|
||||
|
||||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
|
||||
checkType(c,o,REDIS_SET)) return;
|
||||
|
||||
s = o->ptr;
|
||||
addReplyUlong(c,dictSize(s));
|
||||
}
|
||||
|
||||
void spopCommand(redisClient *c) {
|
||||
robj *set;
|
||||
dictEntry *de;
|
||||
|
||||
if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
|
||||
checkType(c,set,REDIS_SET)) return;
|
||||
|
||||
de = dictGetRandomKey(set->ptr);
|
||||
if (de == NULL) {
|
||||
addReply(c,shared.nullbulk);
|
||||
} else {
|
||||
robj *ele = dictGetEntryKey(de);
|
||||
|
||||
addReplyBulk(c,ele);
|
||||
dictDelete(set->ptr,ele);
|
||||
if (htNeedsResize(set->ptr)) dictResize(set->ptr);
|
||||
if (dictSize((dict*)set->ptr) == 0) dbDelete(c->db,c->argv[1]);
|
||||
server.dirty++;
|
||||
}
|
||||
}
|
||||
|
||||
void srandmemberCommand(redisClient *c) {
|
||||
robj *set;
|
||||
dictEntry *de;
|
||||
|
||||
if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
|
||||
checkType(c,set,REDIS_SET)) return;
|
||||
|
||||
de = dictGetRandomKey(set->ptr);
|
||||
if (de == NULL) {
|
||||
addReply(c,shared.nullbulk);
|
||||
} else {
|
||||
robj *ele = dictGetEntryKey(de);
|
||||
|
||||
addReplyBulk(c,ele);
|
||||
}
|
||||
}
|
||||
|
||||
int qsortCompareSetsByCardinality(const void *s1, const void *s2) {
|
||||
dict **d1 = (void*) s1, **d2 = (void*) s2;
|
||||
|
||||
return dictSize(*d1)-dictSize(*d2);
|
||||
}
|
||||
|
||||
void sinterGenericCommand(redisClient *c, robj **setskeys, unsigned long setsnum, robj *dstkey) {
|
||||
dict **dv = zmalloc(sizeof(dict*)*setsnum);
|
||||
dictIterator *di;
|
||||
dictEntry *de;
|
||||
robj *lenobj = NULL, *dstset = NULL;
|
||||
unsigned long j, cardinality = 0;
|
||||
|
||||
for (j = 0; j < setsnum; j++) {
|
||||
robj *setobj;
|
||||
|
||||
setobj = dstkey ?
|
||||
lookupKeyWrite(c->db,setskeys[j]) :
|
||||
lookupKeyRead(c->db,setskeys[j]);
|
||||
if (!setobj) {
|
||||
zfree(dv);
|
||||
if (dstkey) {
|
||||
if (dbDelete(c->db,dstkey))
|
||||
server.dirty++;
|
||||
addReply(c,shared.czero);
|
||||
} else {
|
||||
addReply(c,shared.emptymultibulk);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (setobj->type != REDIS_SET) {
|
||||
zfree(dv);
|
||||
addReply(c,shared.wrongtypeerr);
|
||||
return;
|
||||
}
|
||||
dv[j] = setobj->ptr;
|
||||
}
|
||||
/* Sort sets from the smallest to largest, this will improve our
|
||||
* algorithm's performace */
|
||||
qsort(dv,setsnum,sizeof(dict*),qsortCompareSetsByCardinality);
|
||||
|
||||
/* The first thing we should output is the total number of elements...
|
||||
* since this is a multi-bulk write, but at this stage we don't know
|
||||
* the intersection set size, so we use a trick, append an empty object
|
||||
* to the output list and save the pointer to later modify it with the
|
||||
* right length */
|
||||
if (!dstkey) {
|
||||
lenobj = createObject(REDIS_STRING,NULL);
|
||||
addReply(c,lenobj);
|
||||
decrRefCount(lenobj);
|
||||
} else {
|
||||
/* If we have a target key where to store the resulting set
|
||||
* create this key with an empty set inside */
|
||||
dstset = createSetObject();
|
||||
}
|
||||
|
||||
/* Iterate all the elements of the first (smallest) set, and test
|
||||
* the element against all the other sets, if at least one set does
|
||||
* not include the element it is discarded */
|
||||
di = dictGetIterator(dv[0]);
|
||||
|
||||
while((de = dictNext(di)) != NULL) {
|
||||
robj *ele;
|
||||
|
||||
for (j = 1; j < setsnum; j++)
|
||||
if (dictFind(dv[j],dictGetEntryKey(de)) == NULL) break;
|
||||
if (j != setsnum)
|
||||
continue; /* at least one set does not contain the member */
|
||||
ele = dictGetEntryKey(de);
|
||||
if (!dstkey) {
|
||||
addReplyBulk(c,ele);
|
||||
cardinality++;
|
||||
} else {
|
||||
dictAdd(dstset->ptr,ele,NULL);
|
||||
incrRefCount(ele);
|
||||
}
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
|
||||
if (dstkey) {
|
||||
/* Store the resulting set into the target, if the intersection
|
||||
* is not an empty set. */
|
||||
dbDelete(c->db,dstkey);
|
||||
if (dictSize((dict*)dstset->ptr) > 0) {
|
||||
dbAdd(c->db,dstkey,dstset);
|
||||
addReplyLongLong(c,dictSize((dict*)dstset->ptr));
|
||||
} else {
|
||||
decrRefCount(dstset);
|
||||
addReply(c,shared.czero);
|
||||
}
|
||||
server.dirty++;
|
||||
} else {
|
||||
lenobj->ptr = sdscatprintf(sdsempty(),"*%lu\r\n",cardinality);
|
||||
}
|
||||
zfree(dv);
|
||||
}
|
||||
|
||||
void sinterCommand(redisClient *c) {
|
||||
sinterGenericCommand(c,c->argv+1,c->argc-1,NULL);
|
||||
}
|
||||
|
||||
void sinterstoreCommand(redisClient *c) {
|
||||
sinterGenericCommand(c,c->argv+2,c->argc-2,c->argv[1]);
|
||||
}
|
||||
|
||||
void sunionDiffGenericCommand(redisClient *c, robj **setskeys, int setsnum, robj *dstkey, int op) {
|
||||
dict **dv = zmalloc(sizeof(dict*)*setsnum);
|
||||
dictIterator *di;
|
||||
dictEntry *de;
|
||||
robj *dstset = NULL;
|
||||
int j, cardinality = 0;
|
||||
|
||||
for (j = 0; j < setsnum; j++) {
|
||||
robj *setobj;
|
||||
|
||||
setobj = dstkey ?
|
||||
lookupKeyWrite(c->db,setskeys[j]) :
|
||||
lookupKeyRead(c->db,setskeys[j]);
|
||||
if (!setobj) {
|
||||
dv[j] = NULL;
|
||||
continue;
|
||||
}
|
||||
if (setobj->type != REDIS_SET) {
|
||||
zfree(dv);
|
||||
addReply(c,shared.wrongtypeerr);
|
||||
return;
|
||||
}
|
||||
dv[j] = setobj->ptr;
|
||||
}
|
||||
|
||||
/* We need a temp set object to store our union. If the dstkey
|
||||
* is not NULL (that is, we are inside an SUNIONSTORE operation) then
|
||||
* this set object will be the resulting object to set into the target key*/
|
||||
dstset = createSetObject();
|
||||
|
||||
/* Iterate all the elements of all the sets, add every element a single
|
||||
* time to the result set */
|
||||
for (j = 0; j < setsnum; j++) {
|
||||
if (op == REDIS_OP_DIFF && j == 0 && !dv[j]) break; /* result set is empty */
|
||||
if (!dv[j]) continue; /* non existing keys are like empty sets */
|
||||
|
||||
di = dictGetIterator(dv[j]);
|
||||
|
||||
while((de = dictNext(di)) != NULL) {
|
||||
robj *ele;
|
||||
|
||||
/* dictAdd will not add the same element multiple times */
|
||||
ele = dictGetEntryKey(de);
|
||||
if (op == REDIS_OP_UNION || j == 0) {
|
||||
if (dictAdd(dstset->ptr,ele,NULL) == DICT_OK) {
|
||||
incrRefCount(ele);
|
||||
cardinality++;
|
||||
}
|
||||
} else if (op == REDIS_OP_DIFF) {
|
||||
if (dictDelete(dstset->ptr,ele) == DICT_OK) {
|
||||
cardinality--;
|
||||
}
|
||||
}
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
|
||||
/* result set is empty? Exit asap. */
|
||||
if (op == REDIS_OP_DIFF && cardinality == 0) break;
|
||||
}
|
||||
|
||||
/* Output the content of the resulting set, if not in STORE mode */
|
||||
if (!dstkey) {
|
||||
addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",cardinality));
|
||||
di = dictGetIterator(dstset->ptr);
|
||||
while((de = dictNext(di)) != NULL) {
|
||||
robj *ele;
|
||||
|
||||
ele = dictGetEntryKey(de);
|
||||
addReplyBulk(c,ele);
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
decrRefCount(dstset);
|
||||
} else {
|
||||
/* If we have a target key where to store the resulting set
|
||||
* create this key with the result set inside */
|
||||
dbDelete(c->db,dstkey);
|
||||
if (dictSize((dict*)dstset->ptr) > 0) {
|
||||
dbAdd(c->db,dstkey,dstset);
|
||||
addReplyLongLong(c,dictSize((dict*)dstset->ptr));
|
||||
} else {
|
||||
decrRefCount(dstset);
|
||||
addReply(c,shared.czero);
|
||||
}
|
||||
server.dirty++;
|
||||
}
|
||||
zfree(dv);
|
||||
}
|
||||
|
||||
void sunionCommand(redisClient *c) {
|
||||
sunionDiffGenericCommand(c,c->argv+1,c->argc-1,NULL,REDIS_OP_UNION);
|
||||
}
|
||||
|
||||
void sunionstoreCommand(redisClient *c) {
|
||||
sunionDiffGenericCommand(c,c->argv+2,c->argc-2,c->argv[1],REDIS_OP_UNION);
|
||||
}
|
||||
|
||||
void sdiffCommand(redisClient *c) {
|
||||
sunionDiffGenericCommand(c,c->argv+1,c->argc-1,NULL,REDIS_OP_DIFF);
|
||||
}
|
||||
|
||||
void sdiffstoreCommand(redisClient *c) {
|
||||
sunionDiffGenericCommand(c,c->argv+2,c->argc-2,c->argv[1],REDIS_OP_DIFF);
|
||||
}
|
Reference in New Issue
Block a user