Files
redis/src/zset.c

186 lines
5.5 KiB
C

#include "zset.h"
/* t_zset.c prototypes (there's no t_zset.h) */
unsigned char *zzlFirstInRange(unsigned char *zl, zrangespec *range);
unsigned char *zzlFind(unsigned char *zl, robj *ele, double *score);
int zzlLexValueLteMax(unsigned char *p, zlexrangespec *spec);
/* Converted from static in t_zset.c: */
int zslValueLteMax(double value, zrangespec *spec);
/* ====================================================================
* Direct Redis DB Interaction
* ==================================================================== */
/* zset access is mostly a copy/paste from zscoreCommand() */
int zsetScore(robj *zobj, robj *member, double *score) {
if (!zobj || !member)
return 0;
if (zobj->encoding == REDIS_ENCODING_ZIPLIST) {
if (zzlFind(zobj->ptr, member, score) == NULL)
return 0;
} else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) {
zset *zs = zobj->ptr;
dictEntry *de;
member = tryObjectEncoding(member);
de = dictFind(zs->dict, member);
if (de != NULL) {
*score = *(double *)dictGetVal(de);
} else
return 0;
} else {
return 0;
}
return 1;
}
/* Largely extracted from genericZrangebyscoreCommand() in t_zset.c */
/* The zrangebyscoreCommand expects to only operate on a live redisClient,
* but we need results returned to us, not sent over an async socket. */
list *geozrangebyscore(robj *zobj, double min, double max, int limit) {
/* minex 0 = include min in range; maxex 1 = exclude max in range */
/* That's: min <= val < max */
zrangespec range = { .min = min, .max = max, .minex = 0, .maxex = 1 };
list *l = NULL; /* result list */
if (zobj->encoding == REDIS_ENCODING_ZIPLIST) {
unsigned char *zl = zobj->ptr;
unsigned char *eptr, *sptr;
unsigned char *vstr = NULL;
unsigned int vlen = 0;
long long vlong = 0;
double score = 0;
if ((eptr = zzlFirstInRange(zl, &range)) == NULL) {
/* Nothing exists starting at our min. No results. */
return NULL;
}
l = listCreate();
sptr = ziplistNext(zl, eptr);
while (eptr && limit--) {
score = zzlGetScore(sptr);
/* If we fell out of range, break. */
if (!zslValueLteMax(score, &range))
break;
/* We know the element exists. ziplistGet should always succeed */
ziplistGet(eptr, &vstr, &vlen, &vlong);
if (vstr == NULL) {
listAddNodeTail(l, result_long(score, vlong));
} else {
listAddNodeTail(l, result_str(score, vstr, vlen));
}
zzlNext(zl, &eptr, &sptr);
}
} else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) {
zset *zs = zobj->ptr;
zskiplist *zsl = zs->zsl;
zskiplistNode *ln;
if ((ln = zslFirstInRange(zsl, &range)) == NULL) {
/* Nothing exists starting at our min. No results. */
return NULL;
}
l = listCreate();
while (ln && limit--) {
robj *o = ln->obj;
/* Abort when the node is no longer in range. */
if (!zslValueLteMax(ln->score, &range))
break;
if (o->encoding == REDIS_ENCODING_INT) {
listAddNodeTail(l, result_long(ln->score, (long)o->ptr));
} else {
listAddNodeTail(l,
result_str(ln->score, o->ptr, sdslen(o->ptr)));
}
ln = ln->level[0].forward;
}
}
if (l) {
listSetFreeMethod(l, (void (*)(void *ptr)) & free_zipresult);
}
return l;
}
/* ====================================================================
* Helpers
* ==================================================================== */
/* join 'join' to 'join_to' and free 'join' container */
void listJoin(list *join_to, list *join) {
/* If the current list has zero size, move join to become join_to.
* If not, append the new list to the current list. */
if (join_to->len == 0) {
join_to->head = join->head;
} else {
join_to->tail->next = join->head;
join->head->prev = join_to->tail;
join_to->tail = join->tail;
}
/* Update total element count */
join_to->len += join->len;
/* Release original list container. Internal nodes were transferred over. */
zfree(join);
}
/* A ziplist member may be either a long long or a string. We create the
* contents of our return zipresult based on what the ziplist contained. */
static struct zipresult *result(double score, long long v, unsigned char *s,
int len) {
struct zipresult *r = zmalloc(sizeof(*r));
/* If string and length, become a string. */
/* Else, if not string or no length, become a long. */
if (s && len >= 0)
r->type = ZR_STRING;
else if (!s || len < 0)
r->type = ZR_LONG;
r->score = score;
switch (r->type) {
case(ZR_LONG) :
r->val.v = v;
break;
case(ZR_STRING) :
r->val.s = sdsnewlen(s, len);
break;
}
return r;
}
struct zipresult *result_long(double score, long long v) {
return result(score, v, NULL, -1);
}
struct zipresult *result_str(double score, unsigned char *str, int len) {
return result(score, 0, str, len);
}
void free_zipresult(struct zipresult *r) {
if (!r)
return;
switch (r->type) {
case(ZR_LONG) :
break;
case(ZR_STRING) :
sdsfree(r->val.s);
break;
}
zfree(r);
}