typedef struct zskiplistNode {
struct zskiplistNode **forward;
struct zskiplistNode *backward;
- unsigned long *span;
+ unsigned int *span;
double score;
robj *obj;
} zskiplistNode;
static void blpopCommand(redisClient *c);
static void brpopCommand(redisClient *c);
static void appendCommand(redisClient *c);
+static void substrCommand(redisClient *c);
static void zrankCommand(redisClient *c);
/*================================= Globals ================================= */
{"set",setCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,0,0,0},
{"setnx",setnxCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,0,0,0},
{"append",appendCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,1,1,1},
+ {"substr",substrCommand,4,REDIS_CMD_INLINE,1,1,1},
{"del",delCommand,-2,REDIS_CMD_INLINE,0,0,0},
{"exists",existsCommand,2,REDIS_CMD_INLINE,1,1,1},
{"incr",incrCommand,2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,1,1,1},
addReplySds(c,sdscatprintf(sdsempty(),":%lu\r\n",(unsigned long)totlen));
}
+static void substrCommand(redisClient *c) {
+ robj *o;
+ long start = atoi(c->argv[2]->ptr);
+ long end = atoi(c->argv[3]->ptr);
+
+ o = lookupKeyRead(c->db,c->argv[1]);
+ if (o == NULL) {
+ addReply(c,shared.nullbulk);
+ } else {
+ if (o->type != REDIS_STRING) {
+ addReply(c,shared.wrongtypeerr);
+ } else {
+ size_t rangelen, strlen = sdslen(o->ptr);
+ sds range;
+
+ /* convert negative indexes */
+ if (start < 0) start = strlen+start;
+ if (end < 0) end = strlen+end;
+ if (start < 0) start = 0;
+ if (end < 0) end = 0;
+
+ /* indexes sanity checks */
+ if (start > end || (size_t)start >= strlen) {
+ /* Out of range start or start > end result in null reply */
+ addReply(c,shared.nullbulk);
+ return;
+ }
+ if ((size_t)end >= strlen) end = strlen-1;
+ rangelen = (end-start)+1;
+
+ /* Return the result */
+ addReplySds(c,sdscatprintf(sdsempty(),"$%lu\r\n",rangelen));
+ range = sdsnewlen((char*)o->ptr+start,rangelen);
+ addReplySds(c,range);
+ addReply(c,shared.crlf);
+ }
+ }
+}
+
/* ========================= Type agnostic commands ========================= */
static void delCommand(redisClient *c) {
zskiplistNode *zn = zmalloc(sizeof(*zn));
zn->forward = zmalloc(sizeof(zskiplistNode*) * level);
- zn->span = zmalloc(sizeof(unsigned long) * level);
+ zn->span = zmalloc(sizeof(unsigned int) * level);
zn->score = score;
zn->obj = obj;
return zn;
static void zslInsert(zskiplist *zsl, double score, robj *obj) {
zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;
- unsigned long span[ZSKIPLIST_MAXLEVEL];
+ unsigned int span[ZSKIPLIST_MAXLEVEL];
int i, level;
x = zsl->header;
double *score = dictGetEntryVal(de);
zskiplistNode *x;
- unsigned long rank = 0;
+ unsigned int rank = 0;
int i;
x = zsl->header;
while (x->forward[i] &&
(x->forward[i]->score < *score ||
(x->forward[i]->score == *score &&
- compareStringObjects(x->forward[i]->obj,c->argv[2]) < 0))) {
+ compareStringObjects(x->forward[i]->obj,c->argv[2]) <= 0))) {
rank += x->span[i];
x = x->forward[i];
}
- if (x->forward[i] && compareStringObjects(x->forward[i]->obj,c->argv[2]) == 0) {
- addReplyLong(c, rank);
+ /* x might be equal to zsl->header, so test if obj is non-NULL */
+ if (x->obj && compareStringObjects(x->obj,c->argv[2]) == 0) {
+ /* the pointer from zsl->header to the first element also spans one,
+ * which makes the rank 1-based */
+ addReplyLong(c, rank-1);
return;
}
}