Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
35cabcb5
Commit
35cabcb5
authored
Jun 11, 2010
by
Pieter Noordhuis
Browse files
wrapper functions for the set type to support multiple encodings
parent
3ab98cef
Changes
1
Show whitespace changes
Inline
Side-by-side
redis.c
View file @
35cabcb5
...
@@ -3064,7 +3064,9 @@ static robj *createZiplistObject(void) {
...
@@ -3064,7 +3064,9 @@ static robj *createZiplistObject(void) {
static robj *createSetObject(void) {
static robj *createSetObject(void) {
dict *d = dictCreate(&setDictType,NULL);
dict *d = dictCreate(&setDictType,NULL);
return createObject(REDIS_SET,d);
robj *o = createObject(REDIS_SET,d);
o->encoding = REDIS_ENCODING_HT;
return o;
}
}
static robj *createHashObject(void) {
static robj *createHashObject(void) {
...
@@ -5457,6 +5459,99 @@ static void rpoplpushcommand(redisClient *c) {
...
@@ -5457,6 +5459,99 @@ static void rpoplpushcommand(redisClient *c) {
/* ==================================== Sets ================================ */
/* ==================================== Sets ================================ */
static int setTypeAdd(robj *subject, robj *value) {
if (subject->encoding == REDIS_ENCODING_HT) {
if (dictAdd(subject->ptr,value,NULL) == DICT_OK) {
incrRefCount(value);
return 1;
}
} else {
redisPanic("Unknown set encoding");
}
return 0;
}
static int setTypeRemove(robj *subject, robj *value) {
if (subject->encoding == REDIS_ENCODING_HT) {
if (dictDelete(subject->ptr,value) == DICT_OK) {
if (htNeedsResize(subject->ptr)) dictResize(subject->ptr);
return 1;
}
} else {
redisPanic("Unknown set encoding");
}
return 0;
}
static int setTypeIsMember(robj *subject, robj *value) {
if (subject->encoding == REDIS_ENCODING_HT) {
return dictFind((dict*)subject->ptr,value) != NULL;
} else {
redisPanic("Unknown set encoding");
}
}
/* Structure to hold set iteration abstraction. */
typedef struct {
int encoding;
dictIterator *di;
} setIterator;
static setIterator *setTypeInitIterator(robj *subject) {
setIterator *si = zmalloc(sizeof(setIterator));
si->encoding = subject->encoding;
if (si->encoding == REDIS_ENCODING_HT) {
si->di = dictGetIterator(subject->ptr);
} else {
redisPanic("Unknown set encoding");
}
return si;
}
static void setTypeReleaseIterator(setIterator *si) {
if (si->encoding == REDIS_ENCODING_HT)
dictReleaseIterator(si->di);
zfree(si);
}
/* Move to the next entry in the set. Returns the object at the current
* position, or NULL when the end is reached. This object will have its
* refcount incremented, so the caller needs to take care of this. */
static robj *setTypeNext(setIterator *si) {
robj *ret = NULL;
if (si->encoding == REDIS_ENCODING_HT) {
dictEntry *de = dictNext(si->di);
if (de != NULL) {
ret = dictGetEntryKey(de);
incrRefCount(ret);
}
}
return ret;
}
/* Return random element from set. The returned object will always have
* an incremented refcount. */
robj *setTypeRandomElement(robj *subject) {
robj *ret = NULL;
if (subject->encoding == REDIS_ENCODING_HT) {
dictEntry *de = dictGetRandomKey(subject->ptr);
ret = dictGetEntryKey(de);
incrRefCount(ret);
} else {
redisPanic("Unknown set encoding");
}
return ret;
}
static unsigned long setTypeSize(robj *subject) {
if (subject->encoding == REDIS_ENCODING_HT) {
return dictSize((dict*)subject->ptr);
} else {
redisPanic("Unknown set encoding");
}
}
static void saddCommand(redisClient *c) {
static void saddCommand(redisClient *c) {
robj *set;
robj *set;
...
@@ -5470,8 +5565,7 @@ static void saddCommand(redisClient *c) {
...
@@ -5470,8 +5565,7 @@ static void saddCommand(redisClient *c) {
return;
return;
}
}
}
}
if (dictAdd(set->ptr,c->argv[2],NULL) == DICT_OK) {
if (setTypeAdd(set,c->argv[2])) {
incrRefCount(c->argv[2]);
server.dirty++;
server.dirty++;
addReply(c,shared.cone);
addReply(c,shared.cone);
} else {
} else {
...
@@ -5485,10 +5579,9 @@ static void sremCommand(redisClient *c) {
...
@@ -5485,10 +5579,9 @@ static void sremCommand(redisClient *c) {
if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
checkType(c,set,REDIS_SET)) return;
checkType(c,set,REDIS_SET)) return;
if (dictDelete(set->ptr,c->argv[2]) == DICT_OK) {
if (setTypeRemove(set,c->argv[2])) {
if (setTypeSize(set) == 0) dbDelete(c->db,c->argv[1]);
server.dirty++;
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);
addReply(c,shared.cone);
} else {
} else {
addReply(c,shared.czero);
addReply(c,shared.czero);
...
@@ -5513,12 +5606,12 @@ static void smoveCommand(redisClient *c) {
...
@@ -5513,12 +5606,12 @@ static void smoveCommand(redisClient *c) {
return;
return;
}
}
/* Remove the element from the source set */
/* Remove the element from the source set */
if (
dictDelet
e(srcset
->ptr
,c->argv[3])
== DICT_ERR
) {
if (
!setTypeRemov
e(srcset,c->argv[3])) {
/* Key not found in the src set! return zero */
/* Key not found in the src set! return zero */
addReply(c,shared.czero);
addReply(c,shared.czero);
return;
return;
}
}
if (
dictSize((dict*)
srcset
->ptr
) == 0 && srcset != dstset)
if (
setTypeSize(
srcset) == 0 && srcset != dstset)
dbDelete(c->db,c->argv[1]);
dbDelete(c->db,c->argv[1]);
server.dirty++;
server.dirty++;
/* Add the element to the destination set */
/* Add the element to the destination set */
...
@@ -5526,8 +5619,7 @@ static void smoveCommand(redisClient *c) {
...
@@ -5526,8 +5619,7 @@ static void smoveCommand(redisClient *c) {
dstset = createSetObject();
dstset = createSetObject();
dbAdd(c->db,c->argv[2],dstset);
dbAdd(c->db,c->argv[2],dstset);
}
}
if (dictAdd(dstset->ptr,c->argv[3],NULL) == DICT_OK)
setTypeAdd(dstset,c->argv[3]);
incrRefCount(c->argv[3]);
addReply(c,shared.cone);
addReply(c,shared.cone);
}
}
...
@@ -5537,7 +5629,7 @@ static void sismemberCommand(redisClient *c) {
...
@@ -5537,7 +5629,7 @@ static void sismemberCommand(redisClient *c) {
if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
checkType(c,set,REDIS_SET)) return;
checkType(c,set,REDIS_SET)) return;
if (
dictFind(set->ptr
,c->argv[2]))
if (
setTypeIsMember(set
,c->argv[2]))
addReply(c,shared.cone);
addReply(c,shared.cone);
else
else
addReply(c,shared.czero);
addReply(c,shared.czero);
...
@@ -5545,74 +5637,62 @@ static void sismemberCommand(redisClient *c) {
...
@@ -5545,74 +5637,62 @@ static void sismemberCommand(redisClient *c) {
static void scardCommand(redisClient *c) {
static void scardCommand(redisClient *c) {
robj *o;
robj *o;
dict *s;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
checkType(c,o,REDIS_SET)) return;
checkType(c,o,REDIS_SET)) return;
s = o->ptr;
addReplyUlong(c,setTypeSize(o));
addReplyUlong(c,dictSize(s));
}
}
static void spopCommand(redisClient *c) {
static void spopCommand(redisClient *c) {
robj *set;
robj *set, *ele;
dictEntry *de;
if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
checkType(c,set,REDIS_SET)) return;
checkType(c,set,REDIS_SET)) return;
d
e =
dictGetRandomKey(set->ptr
);
el
e =
setTypeRandomElement(set
);
if (
d
e == NULL) {
if (
el
e == NULL) {
addReply(c,shared.nullbulk);
addReply(c,shared.nullbulk);
} else {
} else {
robj *ele = dictGetEntryKey(de);
setTypeRemove(set,ele);
addReplyBulk(c,ele);
addReplyBulk(c,ele);
dictDelete(set->ptr,ele);
decrRefCount(ele);
if (htNeedsResize(set->ptr)) dictResize(set->ptr);
if (setTypeSize(set) == 0) dbDelete(c->db,c->argv[1]);
if (dictSize((dict*)set->ptr) == 0) dbDelete(c->db,c->argv[1]);
server.dirty++;
server.dirty++;
}
}
}
}
static void srandmemberCommand(redisClient *c) {
static void srandmemberCommand(redisClient *c) {
robj *set;
robj *set, *ele;
dictEntry *de;
if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
checkType(c,set,REDIS_SET)) return;
checkType(c,set,REDIS_SET)) return;
d
e =
dictGetRandomKey(set->ptr
);
el
e =
setTypeRandomElement(set
);
if (
d
e == NULL) {
if (
el
e == NULL) {
addReply(c,shared.nullbulk);
addReply(c,shared.nullbulk);
} else {
} else {
robj *ele = dictGetEntryKey(de);
addReplyBulk(c,ele);
addReplyBulk(c,ele);
decrRefCount(ele);
}
}
}
}
static int qsortCompareSetsByCardinality(const void *s1, const void *s2) {
static int qsortCompareSetsByCardinality(const void *s1, const void *s2) {
dict **d1 = (void*) s1, **d2 = (void*) s2;
return setTypeSize(*(robj**)s1)-setTypeSize(*(robj**)s2);
return dictSize(*d1)-dictSize(*d2);
}
}
static void sinterGenericCommand(redisClient *c, robj **setskeys, unsigned long setsnum, robj *dstkey) {
static void sinterGenericCommand(redisClient *c, robj **setkeys, unsigned long setnum, robj *dstkey) {
dict **dv = zmalloc(sizeof(dict*)*setsnum);
robj **sets = zmalloc(sizeof(robj*)*setnum);
dictIterator *di;
setIterator *si;
dictEntry *de;
robj *ele, *lenobj = NULL, *dstset = NULL;
robj *lenobj = NULL, *dstset = NULL;
unsigned long j, cardinality = 0;
unsigned long j, cardinality = 0;
for (j = 0; j < setsnum; j++) {
for (j = 0; j < setnum; j++) {
robj *setobj;
robj *setobj = dstkey ?
lookupKeyWrite(c->db,setkeys[j]) :
setobj = dstkey ?
lookupKeyRead(c->db,setkeys[j]);
lookupKeyWrite(c->db,setskeys[j]) :
lookupKeyRead(c->db,setskeys[j]);
if (!setobj) {
if (!setobj) {
zfree(
dv
);
zfree(
sets
);
if (dstkey) {
if (dstkey) {
if (dbDelete(c->db,dstkey))
if (dbDelete(c->db,dstkey))
server.dirty++;
server.dirty++;
...
@@ -5622,16 +5702,15 @@ static void sinterGenericCommand(redisClient *c, robj **setskeys, unsigned long
...
@@ -5622,16 +5702,15 @@ static void sinterGenericCommand(redisClient *c, robj **setskeys, unsigned long
}
}
return;
return;
}
}
if (setobj->type != REDIS_SET) {
if (checkType(c,setobj,REDIS_SET)) {
zfree(dv);
zfree(sets);
addReply(c,shared.wrongtypeerr);
return;
return;
}
}
dv
[j] = setobj
->ptr
;
sets
[j] = setobj;
}
}
/* Sort sets from the smallest to largest, this will improve our
/* Sort sets from the smallest to largest, this will improve our
* algorithm's performace */
* algorithm's performace */
qsort(
dv
,set
s
num,sizeof(
dict
*),qsortCompareSetsByCardinality);
qsort(
sets
,setnum,sizeof(
robj
*),qsortCompareSetsByCardinality);
/* The first thing we should output is the total number of elements...
/* 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
* since this is a multi-bulk write, but at this stage we don't know
...
@@ -5651,33 +5730,31 @@ static void sinterGenericCommand(redisClient *c, robj **setskeys, unsigned long
...
@@ -5651,33 +5730,31 @@ static void sinterGenericCommand(redisClient *c, robj **setskeys, unsigned long
/* Iterate all the elements of the first (smallest) set, and test
/* Iterate all the elements of the first (smallest) set, and test
* the element against all the other sets, if at least one set does
* the element against all the other sets, if at least one set does
* not include the element it is discarded */
* not include the element it is discarded */
d
i =
dictGe
tIterator(
dv
[0]);
s
i =
setTypeIni
tIterator(
sets
[0]);
while((ele = setTypeNext(si)) != NULL) {
while((de = dictNext(di)) != NULL) {
for (j = 1; j < setnum; j++)
robj *ele
;
if (!setTypeIsMember(sets[j],ele)) break
;
for (j = 1; j < setsnum; j++)
/* Only take action when all sets contain the member */
if (dictFind(dv[j],dictGetEntryKey(de)) == NULL) break;
if (j == setnum) {
if (j != setsnum)
continue; /* at least one set does not contain the member */
ele = dictGetEntryKey(de);
if (!dstkey) {
if (!dstkey) {
addReplyBulk(c,ele);
addReplyBulk(c,ele);
cardinality++;
cardinality++;
} else {
} else {
dictAdd(dstset->ptr,ele,NULL);
setTypeAdd(dstset,ele);
incrRefCount(ele);
}
}
}
}
dictReleaseIterator(di);
decrRefCount(ele);
}
setTypeReleaseIterator(si);
if (dstkey) {
if (dstkey) {
/* Store the resulting set into the target, if the intersection
/* Store the resulting set into the target, if the intersection
* is not an empty set. */
* is not an empty set. */
dbDelete(c->db,dstkey);
dbDelete(c->db,dstkey);
if (
dictSize((dict*)
dstset
->ptr
) > 0) {
if (
setTypeSize(
dstset) > 0) {
dbAdd(c->db,dstkey,dstset);
dbAdd(c->db,dstkey,dstset);
addReplyLongLong(c,
dictSize((dict*)
dstset
->ptr
));
addReplyLongLong(c,
setTypeSize(
dstset));
} else {
} else {
decrRefCount(dstset);
decrRefCount(dstset);
addReply(c,shared.czero);
addReply(c,shared.czero);
...
@@ -5686,7 +5763,7 @@ static void sinterGenericCommand(redisClient *c, robj **setskeys, unsigned long
...
@@ -5686,7 +5763,7 @@ static void sinterGenericCommand(redisClient *c, robj **setskeys, unsigned long
} else {
} else {
lenobj->ptr = sdscatprintf(sdsempty(),"*%lu\r\n",cardinality);
lenobj->ptr = sdscatprintf(sdsempty(),"*%lu\r\n",cardinality);
}
}
zfree(
dv
);
zfree(
sets
);
}
}
static void sinterCommand(redisClient *c) {
static void sinterCommand(redisClient *c) {
...
@@ -5701,29 +5778,25 @@ static void sinterstoreCommand(redisClient *c) {
...
@@ -5701,29 +5778,25 @@ static void sinterstoreCommand(redisClient *c) {
#define REDIS_OP_DIFF 1
#define REDIS_OP_DIFF 1
#define REDIS_OP_INTER 2
#define REDIS_OP_INTER 2
static void sunionDiffGenericCommand(redisClient *c, robj **setskeys, int setsnum, robj *dstkey, int op) {
static void sunionDiffGenericCommand(redisClient *c, robj **setkeys, int setnum, robj *dstkey, int op) {
dict **dv = zmalloc(sizeof(dict*)*setsnum);
robj **sets = zmalloc(sizeof(robj*)*setnum);
dictIterator *di;
setIterator *si;
dictEntry *de;
robj *ele, *dstset = NULL;
robj *dstset = NULL;
int j, cardinality = 0;
int j, cardinality = 0;
for (j = 0; j < setsnum; j++) {
for (j = 0; j < setnum; j++) {
robj *setobj;
robj *setobj = dstkey ?
lookupKeyWrite(c->db,setkeys[j]) :
setobj = dstkey ?
lookupKeyRead(c->db,setkeys[j]);
lookupKeyWrite(c->db,setskeys[j]) :
lookupKeyRead(c->db,setskeys[j]);
if (!setobj) {
if (!setobj) {
dv
[j] = NULL;
sets
[j] = NULL;
continue;
continue;
}
}
if (setobj->type != REDIS_SET) {
if (checkType(c,setobj,REDIS_SET)) {
zfree(dv);
zfree(sets);
addReply(c,shared.wrongtypeerr);
return;
return;
}
}
dv
[j] = setobj
->ptr
;
sets
[j] = setobj;
}
}
/* We need a temp set object to store our union. If the dstkey
/* We need a temp set object to store our union. If the dstkey
...
@@ -5733,60 +5806,53 @@ static void sunionDiffGenericCommand(redisClient *c, robj **setskeys, int setsnu
...
@@ -5733,60 +5806,53 @@ static void sunionDiffGenericCommand(redisClient *c, robj **setskeys, int setsnu
/* Iterate all the elements of all the sets, add every element a single
/* Iterate all the elements of all the sets, add every element a single
* time to the result set */
* time to the result set */
for (j = 0; j < set
s
num; j++) {
for (j = 0; j < setnum; j++) {
if (op == REDIS_OP_DIFF && j == 0 && !
dv
[j]) break; /* result set is empty */
if (op == REDIS_OP_DIFF && j == 0 && !
sets
[j]) break; /* result set is empty */
if (!
dv
[j]) continue; /* non existing keys are like empty sets */
if (!
sets
[j]) continue; /* non existing keys are like empty sets */
di = dictGetIterator(dv[j]);
si = setTypeInitIterator(sets[j]);
while((ele = setTypeNext(si)) != NULL) {
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 (op == REDIS_OP_UNION || j == 0) {
if (dictAdd(dstset->ptr,ele,NULL) == DICT_OK) {
if (setTypeAdd(dstset,ele)) {
incrRefCount(ele);
cardinality++;
cardinality++;
}
}
} else if (op == REDIS_OP_DIFF) {
} else if (op == REDIS_OP_DIFF) {
if (
dictDelete(dstset->ptr,ele) == DICT_OK
) {
if (
setTypeRemove(dstset,ele)
) {
cardinality--;
cardinality--;
}
}
}
}
decrRefCount(ele);
}
}
dict
ReleaseIterator(
d
i);
setType
ReleaseIterator(
s
i);
/* result set is empty
? Exit asap
. */
/*
Exit when
result set is empty. */
if (op == REDIS_OP_DIFF && cardinality == 0) break;
if (op == REDIS_OP_DIFF && cardinality == 0) break;
}
}
/* Output the content of the resulting set, if not in STORE mode */
/* Output the content of the resulting set, if not in STORE mode */
if (!dstkey) {
if (!dstkey) {
addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",cardinality));
addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",cardinality));
di = dictGetIterator(dstset->ptr);
si = setTypeInitIterator(dstset);
while((de = dictNext(di)) != NULL) {
while((ele = setTypeNext(si)) != NULL) {
robj *ele;
ele = dictGetEntryKey(de);
addReplyBulk(c,ele);
addReplyBulk(c,ele);
decrRefCount(ele);
}
}
dict
ReleaseIterator(
d
i);
setType
ReleaseIterator(
s
i);
decrRefCount(dstset);
decrRefCount(dstset);
} else {
} else {
/* If we have a target key where to store the resulting set
/* If we have a target key where to store the resulting set
* create this key with the result set inside */
* create this key with the result set inside */
dbDelete(c->db,dstkey);
dbDelete(c->db,dstkey);
if (
dictSize((dict*)
dstset
->ptr
) > 0) {
if (
setTypeSize(
dstset) > 0) {
dbAdd(c->db,dstkey,dstset);
dbAdd(c->db,dstkey,dstset);
addReplyLongLong(c,
dictSize((dict*)
dstset
->ptr
));
addReplyLongLong(c,
setTypeSize(
dstset));
} else {
} else {
decrRefCount(dstset);
decrRefCount(dstset);
addReply(c,shared.czero);
addReply(c,shared.czero);
}
}
server.dirty++;
server.dirty++;
}
}
zfree(
dv
);
zfree(
sets
);
}
}
static void sunionCommand(redisClient *c) {
static void sunionCommand(redisClient *c) {
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment