diff --git a/src/redis.c b/src/redis.c index 5ca5c8ef96e..456d27bac2a 100644 --- a/src/redis.c +++ b/src/redis.c @@ -124,6 +124,8 @@ struct redisCommand redisCommandTable[] = { {"setex",setexCommand,4,"wm",0,NULL,1,1,1,0,0}, {"psetex",psetexCommand,4,"wm",0,NULL,1,1,1,0,0}, {"append",appendCommand,3,"wm",0,NULL,1,1,1,0,0}, + {"prepend",prependCommand,3,"wm",0,NULL,1,1,1,0,0}, + {"prependfe",prependfeCommand,3,"wm",0,NULL,1,1,1,0,0}, {"strlen",strlenCommand,2,"rF",0,NULL,1,1,1,0,0}, {"del",delCommand,-2,"w",0,NULL,1,-1,1,0,0}, {"exists",existsCommand,2,"rF",0,NULL,1,1,1,0,0}, diff --git a/src/redis.h b/src/redis.h index f04dd12d595..f200550c42a 100644 --- a/src/redis.h +++ b/src/redis.h @@ -1394,6 +1394,8 @@ void blpopCommand(redisClient *c); void brpopCommand(redisClient *c); void brpoplpushCommand(redisClient *c); void appendCommand(redisClient *c); +void prependCommand(redisClient *c); +void prependfeCommand(redisClient *c); void strlenCommand(redisClient *c); void zrankCommand(redisClient *c); void zrevrankCommand(redisClient *c); diff --git a/src/sds.c b/src/sds.c index 7c24318075f..e3502feede6 100644 --- a/src/sds.c +++ b/src/sds.c @@ -250,6 +250,26 @@ sds sdscatlen(sds s, const void *t, size_t len) { return s; } +/* Prepend the specified binary-safe string pointed by 't' of 'len' bytes to the + * beginning of the specified sds string 's'. + * + * After the call, the passed sds string is no longer valid and all the + * references must be substituted with the new pointer returned by the call. */ +sds sdsprecatlen(sds s, const void *t, size_t len) { + struct sdshdr *sh; + size_t curlen = sdslen(s); + + s = sdsMakeRoomFor(s,len); + if (s == NULL) return NULL; + sh = (void*) (s-(sizeof(struct sdshdr))); + memmove(s+len, s, curlen); + memcpy(s, t, len); + sh->len = curlen+len; + sh->free = sh->free-len; + s[curlen+len] = '\0'; + return s; +} + /* Append the specified null termianted C string to the sds string 's'. * * After the call, the passed sds string is no longer valid and all the diff --git a/src/sds.h b/src/sds.h index 37aaf7a28a4..f5a2b65a529 100644 --- a/src/sds.h +++ b/src/sds.h @@ -63,6 +63,7 @@ void sdsfree(sds s); size_t sdsavail(const sds s); sds sdsgrowzero(sds s, size_t len); sds sdscatlen(sds s, const void *t, size_t len); +sds sdsprecatlen(sds s, const void *t, size_t len); sds sdscat(sds s, const char *t); sds sdscatsds(sds s, const sds t); sds sdscpylen(sds s, const char *t, size_t len); diff --git a/src/t_string.c b/src/t_string.c index 96c978add8a..23c02c32aa0 100644 --- a/src/t_string.c +++ b/src/t_string.c @@ -439,6 +439,81 @@ void appendCommand(redisClient *c) { addReplyLongLong(c,totlen); } +void prependCommand(redisClient *c) { + size_t totlen; + robj *o, *prepend; + + o = lookupKeyWrite(c->db,c->argv[1]); + if (o == NULL) { + /* Create the key */ + c->argv[2] = tryObjectEncoding(c->argv[2]); + dbAdd(c->db,c->argv[1],c->argv[2]); + incrRefCount(c->argv[2]); + totlen = stringObjectLen(c->argv[2]); + } else { + /* Key exists, check type */ + if (checkType(c,o,REDIS_STRING)) + return; + + /* "prepend" is an argument, so always an sds */ + prepend = c->argv[2]; + totlen = stringObjectLen(o)+sdslen(prepend->ptr); + if (checkStringLength(c,totlen) != REDIS_OK) + return; + + /* Prepend the value */ + o = dbUnshareStringValue(c->db,c->argv[1],o); + o->ptr = sdsprecatlen(o->ptr,prepend->ptr,sdslen(prepend->ptr)); + totlen = sdslen(o->ptr); + } + signalModifiedKey(c->db,c->argv[1]); + notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"prepend",c->argv[1],c->db->id); + server.dirty++; + addReplyLongLong(c,totlen); +} + +void prependfeCommand(redisClient *c) { + size_t totlen; + robj *o, *prepend; + + o = lookupKeyWrite(c->db,c->argv[1]); + if (o == NULL) { + /* Create the key */ + c->argv[2] = tryObjectEncoding(c->argv[2]); + dbAdd(c->db,c->argv[1],c->argv[2]); + incrRefCount(c->argv[2]); + totlen = stringObjectLen(c->argv[2]); + } else { + /* Key exists, check type */ + if (checkType(c,o,REDIS_STRING)) + return; + + /* "prepend" is an argument, so always an sds */ + prepend = c->argv[2]; + totlen = stringObjectLen(o)+sdslen(prepend->ptr); + if (checkStringLength(c,totlen) != REDIS_OK) + return; + + /* Check to see if the first bytes match */ + /* if they do, no change is needed */ + if(((char*)o->ptr)[0] == ((char*)prepend->ptr)[0]){ + addReplyLongLong(c,stringObjectLen(o)); + return; + } + + /* Prepend the value */ + else { + o = dbUnshareStringValue(c->db,c->argv[1],o); + o->ptr = sdsprecatlen(o->ptr,prepend->ptr,sdslen(prepend->ptr)); + totlen = sdslen(o->ptr); + } + } + signalModifiedKey(c->db,c->argv[1]); + notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"prepend",c->argv[1],c->db->id); + server.dirty++; + addReplyLongLong(c,totlen); +} + void strlenCommand(redisClient *c) { robj *o; if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||