diff options
author | Pieter Noordhuis <pcnoordhuis@gmail.com> | 2010-12-14 14:20:51 +0100 |
---|---|---|
committer | Pieter Noordhuis <pcnoordhuis@gmail.com> | 2010-12-14 14:20:51 +0100 |
commit | 9f9e1ceaa0d4451a21aa56739e63cca012399317 (patch) | |
tree | a56e52aba66700cb2bdb35fdd560b4b487c37574 /src/t_string.c | |
parent | 7ecd4644e7fe48309d4b8445ec62c7fe8c0443ff (diff) | |
download | redis-9f9e1ceaa0d4451a21aa56739e63cca012399317.tar.gz |
Add SETRANGE command implementation and tests
Diffstat (limited to 'src/t_string.c')
-rw-r--r-- | src/t_string.c | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/src/t_string.c b/src/t_string.c index d537c8b90..e442a49fd 100644 --- a/src/t_string.c +++ b/src/t_string.c @@ -5,6 +5,14 @@ * String Commands *----------------------------------------------------------------------------*/ +static int checkStringLength(redisClient *c, long long size) { + if (size > 512*1024*1024) { + addReplyError(c,"string exceeds maximum allowed size (512MB)"); + return REDIS_ERR; + } + return REDIS_OK; +} + void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expire) { int retval; long seconds = 0; /* initialized to avoid an harmness warning */ @@ -172,6 +180,83 @@ void getbitCommand(redisClient *c) { addReply(c, on ? shared.cone : shared.czero); } +void setrangeCommand(redisClient *c) { + robj *o; + long offset; + sds value = c->argv[3]->ptr; + + if (getLongFromObjectOrReply(c,c->argv[2],&offset,NULL) != REDIS_OK) + return; + + o = lookupKeyWrite(c->db,c->argv[1]); + if (o == NULL) { + /* Negative offset is always 0 for non-existing keys */ + if (offset < 0) offset = 0; + + /* Return 0 when setting nothing on a non-existing string */ + if (sdslen(value) == 0) { + addReply(c,shared.czero); + return; + } + + /* Return when the resulting string exceeds allowed size */ + if (checkStringLength(c,offset+sdslen(value)) != REDIS_OK) + return; + + o = createObject(REDIS_STRING,sdsempty()); + dbAdd(c->db,c->argv[1],o); + } else { + int olen; + + /* Key exists, check type */ + if (checkType(c,o,REDIS_STRING)) + return; + + /* Find out existing value length */ + if (o->encoding == REDIS_ENCODING_INT) { + char llbuf[32]; + olen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr); + } else { + olen = sdslen(o->ptr); + } + + /* Return existing string length when setting nothing */ + if (sdslen(value) == 0) { + addReplyLongLong(c,olen); + return; + } + + /* Convert negative indexes. Note that for SETRANGE, the meaning of a + * negative index is a little different than for other commands. + * Here, an offset of -1 points to the trailing NULL byte of the + * string instead of the last character. */ + if (offset < 0) { + offset = olen+1+offset; + if (offset < 0) offset = 0; + } + + /* Return when the resulting string exceeds allowed size */ + if (checkStringLength(c,offset+sdslen(value)) != REDIS_OK) + return; + + /* Create a copy when the object is shared or encoded. */ + if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) { + robj *decoded = getDecodedObject(o); + o = createStringObject(decoded->ptr, sdslen(decoded->ptr)); + decrRefCount(decoded); + dbReplace(c->db,c->argv[1],o); + } + } + + if (sdslen(value) > 0) { + o->ptr = sdsgrowzero(o->ptr,offset+sdslen(value)); + memcpy((char*)o->ptr+offset,value,sdslen(value)); + touchWatchedKey(c->db,c->argv[1]); + server.dirty++; + } + addReplyLongLong(c,sdslen(o->ptr)); +} + void mgetCommand(redisClient *c) { int j; |