-#include "redis.h"
-
-#include <math.h>
+/*
+ * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
+ * Copyright (c) 2009-2012, Pieter Noordhuis <pcnoordhuis 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.
+ */
/*-----------------------------------------------------------------------------
* Sorted set API
/* This skiplist implementation is almost a C translation of the original
* algorithm described by William Pugh in "Skip Lists: A Probabilistic
* Alternative to Balanced Trees", modified in three ways:
- * a) this implementation allows for repeated values.
+ * a) this implementation allows for repeated scores.
* b) the comparison is not just by key (our 'score') but by satellite data.
* c) there is a back pointer, so it's a doubly linked list with the back
* pointers being only at "level 1". This allows to traverse the list
* from tail to head, useful for ZREVRANGE. */
+#include "redis.h"
+#include <math.h>
+
zskiplistNode *zslCreateNode(int level, double score, robj *obj) {
zskiplistNode *zn = zmalloc(sizeof(*zn)+level*sizeof(struct zskiplistLevel));
zn->score = score;
zfree(zsl);
}
+/* Returns a random level for the new skiplist node we are going to create.
+ * The return value of this function is between 1 and ZSKIPLIST_MAXLEVEL
+ * (both inclusive), with a powerlaw-alike distribution where higher
+ * levels are less likely to be returned. */
int zslRandomLevel(void) {
int level = 1;
while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF))
unsigned int rank[ZSKIPLIST_MAXLEVEL];
int i, level;
+ redisAssert(!isnan(score));
x = zsl->header;
for (i = zsl->level-1; i >= 0; i--) {
/* store rank that is crossed to reach the insert position */
return 0;
p = ziplistIndex(zl,-1); /* Last score. */
- redisAssert(p != NULL);
+ if (p == NULL) return 0; /* Empty sorted set */
score = zzlGetScore(p);
if (!zslValueGteMin(score,range))
return 0;
ele = getDecodedObject(ele);
while (eptr != NULL) {
sptr = ziplistNext(zl,eptr);
- redisAssert(sptr != NULL);
+ redisAssertWithInfo(NULL,ele,sptr != NULL);
if (ziplistCompare(eptr,ele->ptr,sdslen(ele->ptr))) {
/* Matching element, pull out score. */
int scorelen;
size_t offset;
- redisAssert(ele->encoding == REDIS_ENCODING_RAW);
+ redisAssertWithInfo(NULL,ele,ele->encoding == REDIS_ENCODING_RAW);
scorelen = d2string(scorebuf,sizeof(scorebuf),score);
if (eptr == NULL) {
zl = ziplistPush(zl,ele->ptr,sdslen(ele->ptr),ZIPLIST_TAIL);
eptr = zl+offset;
/* Insert score after the element. */
- redisAssert((sptr = ziplistNext(zl,eptr)) != NULL);
+ redisAssertWithInfo(NULL,ele,(sptr = ziplistNext(zl,eptr)) != NULL);
zl = ziplistInsert(zl,sptr,(unsigned char*)scorebuf,scorelen);
}
ele = getDecodedObject(ele);
while (eptr != NULL) {
sptr = ziplistNext(zl,eptr);
- redisAssert(sptr != NULL);
+ redisAssertWithInfo(NULL,ele,sptr != NULL);
s = zzlGetScore(sptr);
if (s > score) {
zs->zsl = zslCreate();
eptr = ziplistIndex(zl,0);
- redisAssert(eptr != NULL);
+ redisAssertWithInfo(NULL,zobj,eptr != NULL);
sptr = ziplistNext(zl,eptr);
- redisAssert(sptr != NULL);
+ redisAssertWithInfo(NULL,zobj,sptr != NULL);
while (eptr != NULL) {
score = zzlGetScore(sptr);
- redisAssert(ziplistGet(eptr,&vstr,&vlen,&vlong));
+ redisAssertWithInfo(NULL,zobj,ziplistGet(eptr,&vstr,&vlen,&vlong));
if (vstr == NULL)
ele = createStringObjectFromLongLong(vlong);
else
/* Has incremented refcount since it was just created. */
node = zslInsert(zs->zsl,score,ele);
- redisAssert(dictAdd(zs->dict,ele,&node->score) == DICT_OK);
+ redisAssertWithInfo(NULL,zobj,dictAdd(zs->dict,ele,&node->score) == DICT_OK);
incrRefCount(ele); /* Added to dictionary. */
zzlNext(zl,&eptr,&sptr);
}
ele = c->argv[3+j*2] = tryObjectEncoding(c->argv[3+j*2]);
de = dictFind(zs->dict,ele);
if (de != NULL) {
- curobj = dictGetEntryKey(de);
- curscore = *(double*)dictGetEntryVal(de);
+ curobj = dictGetKey(de);
+ curscore = *(double*)dictGetVal(de);
if (incr) {
score += curscore;
* delete the key object from the skiplist, since the
* dictionary still has a reference to it. */
if (score != curscore) {
- redisAssert(zslDelete(zs->zsl,curscore,curobj));
+ redisAssertWithInfo(c,curobj,zslDelete(zs->zsl,curscore,curobj));
znode = zslInsert(zs->zsl,score,curobj);
incrRefCount(curobj); /* Re-inserted in skiplist. */
- dictGetEntryVal(de) = &znode->score; /* Update score ptr. */
+ dictGetVal(de) = &znode->score; /* Update score ptr. */
signalModifiedKey(c->db,key);
server.dirty++;
} else {
znode = zslInsert(zs->zsl,score,ele);
incrRefCount(ele); /* Inserted in skiplist. */
- redisAssert(dictAdd(zs->dict,ele,&znode->score) == DICT_OK);
+ redisAssertWithInfo(c,NULL,dictAdd(zs->dict,ele,&znode->score) == DICT_OK);
incrRefCount(ele); /* Added to dictionary. */
signalModifiedKey(c->db,key);
deleted++;
/* Delete from the skiplist */
- score = *(double*)dictGetEntryVal(de);
- redisAssert(zslDelete(zs->zsl,score,c->argv[j]));
+ score = *(double*)dictGetVal(de);
+ redisAssertWithInfo(c,c->argv[j],zslDelete(zs->zsl,score,c->argv[j]));
/* Delete from the hash table */
dictDelete(zs->dict,c->argv[j]);
/* Parse the range arguments. */
if (zslParseRange(c->argv[2],c->argv[3],&range) != REDIS_OK) {
- addReplyError(c,"min or max is not a double");
+ addReplyError(c,"min or max is not a float");
return;
}
if (val->flags & OPVAL_DIRTY_ROBJ)
decrRefCount(val->ele);
- bzero(val,sizeof(zsetopval));
+ memset(val,0,sizeof(zsetopval));
if (op->type == REDIS_SET) {
iterset *it = &op->iter.set;
if (op->encoding == REDIS_ENCODING_INTSET) {
- if (!intsetGet(it->is.is,it->is.ii,(int64_t*)&val->ell))
+ int64_t ell;
+
+ if (!intsetGet(it->is.is,it->is.ii,&ell))
return 0;
+ val->ell = ell;
val->score = 1.0;
/* Move to next element. */
} else if (op->encoding == REDIS_ENCODING_HT) {
if (it->ht.de == NULL)
return 0;
- val->ele = dictGetEntryKey(it->ht.de);
+ val->ele = dictGetKey(it->ht.de);
val->score = 1.0;
/* Move to next element. */
} else if (op->encoding == REDIS_ENCODING_SKIPLIST) {
dictEntry *de;
if ((de = dictFind(it->sl.zs->dict,val->ele)) != NULL) {
- *score = *(double*)dictGetEntryVal(de);
+ *score = *(double*)dictGetVal(de);
return 1;
} else {
return 0;
#define REDIS_AGGR_SUM 1
#define REDIS_AGGR_MIN 2
#define REDIS_AGGR_MAX 3
-#define zunionInterDictValue(_e) (dictGetEntryVal(_e) == NULL ? 1.0 : *(double*)dictGetEntryVal(_e))
+#define zunionInterDictValue(_e) (dictGetVal(_e) == NULL ? 1.0 : *(double*)dictGetVal(_e))
inline static void zunionInterAggregate(double *target, double val, int aggregate) {
if (aggregate == REDIS_AGGR_SUM) {
}
void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) {
- int i, j, setnum;
+ int i, j;
+ long setnum;
int aggregate = REDIS_AGGR_SUM;
zsetopsrc *src;
zsetopval zval;
int touched = 0;
/* expect setnum input keys to be given */
- setnum = atoi(c->argv[2]->ptr);
+ if ((getLongFromObjectOrReply(c, c->argv[2], &setnum, NULL) != REDIS_OK))
+ return;
+
if (setnum < 1) {
addReplyError(c,
"at least 1 input key is needed for ZUNIONSTORE/ZINTERSTORE");
j++; remaining--;
for (i = 0; i < setnum; i++, j++, remaining--) {
if (getDoubleFromObjectOrReply(c,c->argv[j],&src[i].weight,
- "weight value is not a double") != REDIS_OK)
+ "weight value is not a float") != REDIS_OK)
{
zfree(src);
return;
double score, value;
score = src[0].weight * zval.score;
+ if (isnan(score)) score = 0;
+
for (j = 1; j < setnum; j++) {
/* It is not safe to access the zset we are
* iterating, so explicitly check for equal object. */
/* Initialize score */
score = src[i].weight * zval.score;
+ if (isnan(score)) score = 0;
/* Because the inputs are sorted by size, it's only possible
* for sets at larger indices to hold this element. */
else
eptr = ziplistIndex(zl,2*start);
- redisAssert(eptr != NULL);
+ redisAssertWithInfo(c,zobj,eptr != NULL);
sptr = ziplistNext(zl,eptr);
while (rangelen--) {
- redisAssert(eptr != NULL && sptr != NULL);
- redisAssert(ziplistGet(eptr,&vstr,&vlen,&vlong));
+ redisAssertWithInfo(c,zobj,eptr != NULL && sptr != NULL);
+ redisAssertWithInfo(c,zobj,ziplistGet(eptr,&vstr,&vlen,&vlong));
if (vstr == NULL)
addReplyBulkLongLong(c,vlong);
else
}
while(rangelen--) {
- redisAssert(ln != NULL);
+ redisAssertWithInfo(c,zobj,ln != NULL);
ele = ln->obj;
addReplyBulk(c,ele);
if (withscores)
zrangespec range;
robj *key = c->argv[1];
robj *zobj;
- int offset = 0, limit = -1;
+ long offset = 0, limit = -1;
int withscores = 0;
unsigned long rangelen = 0;
void *replylen = NULL;
}
if (zslParseRange(c->argv[minidx],c->argv[maxidx],&range) != REDIS_OK) {
- addReplyError(c,"min or max is not a double");
+ addReplyError(c,"min or max is not a float");
return;
}
pos++; remaining--;
withscores = 1;
} else if (remaining >= 3 && !strcasecmp(c->argv[pos]->ptr,"limit")) {
- offset = atoi(c->argv[pos+1]->ptr);
- limit = atoi(c->argv[pos+2]->ptr);
+ if ((getLongFromObjectOrReply(c, c->argv[pos+1], &offset, NULL) != REDIS_OK) ||
+ (getLongFromObjectOrReply(c, c->argv[pos+2], &limit, NULL) != REDIS_OK)) return;
pos += 3; remaining -= 3;
} else {
addReply(c,shared.syntaxerr);
}
/* Get score pointer for the first element. */
- redisAssert(eptr != NULL);
+ redisAssertWithInfo(c,zobj,eptr != NULL);
sptr = ziplistNext(zl,eptr);
/* We don't know in advance how many matching elements there are in the
}
/* We know the element exists, so ziplistGet should always succeed */
- redisAssert(ziplistGet(eptr,&vstr,&vlen,&vlong));
+ redisAssertWithInfo(c,zobj,ziplistGet(eptr,&vstr,&vlen,&vlong));
rangelen++;
if (vstr == NULL) {
/* Parse the range arguments */
if (zslParseRange(c->argv[2],c->argv[3],&range) != REDIS_OK) {
- addReplyError(c,"min or max is not a double");
+ addReplyError(c,"min or max is not a float");
return;
}
/* First element is in range */
sptr = ziplistNext(zl,eptr);
score = zzlGetScore(sptr);
- redisAssert(zslValueLteMax(score,&range));
+ redisAssertWithInfo(c,zobj,zslValueLteMax(score,&range));
/* Iterate over elements in range */
while (eptr) {
c->argv[2] = tryObjectEncoding(c->argv[2]);
de = dictFind(zs->dict,c->argv[2]);
if (de != NULL) {
- score = *(double*)dictGetEntryVal(de);
+ score = *(double*)dictGetVal(de);
addReplyDouble(c,score);
} else {
addReply(c,shared.nullbulk);
checkType(c,zobj,REDIS_ZSET)) return;
llen = zsetLength(zobj);
- redisAssert(ele->encoding == REDIS_ENCODING_RAW);
+ redisAssertWithInfo(c,ele,ele->encoding == REDIS_ENCODING_RAW);
if (zobj->encoding == REDIS_ENCODING_ZIPLIST) {
unsigned char *zl = zobj->ptr;
unsigned char *eptr, *sptr;
eptr = ziplistIndex(zl,0);
- redisAssert(eptr != NULL);
+ redisAssertWithInfo(c,zobj,eptr != NULL);
sptr = ziplistNext(zl,eptr);
- redisAssert(sptr != NULL);
+ redisAssertWithInfo(c,zobj,sptr != NULL);
rank = 1;
while(eptr != NULL) {
ele = c->argv[2] = tryObjectEncoding(c->argv[2]);
de = dictFind(zs->dict,ele);
if (de != NULL) {
- score = *(double*)dictGetEntryVal(de);
+ score = *(double*)dictGetVal(de);
rank = zslGetRank(zsl,score,ele);
- redisAssert(rank); /* Existing elements always have a rank. */
+ redisAssertWithInfo(c,ele,rank); /* Existing elements always have a rank. */
if (reverse)
addReplyLongLong(c,llen-rank);
else