X-Git-Url: https://git.saurik.com/redis.git/blobdiff_plain/586500c0ef32f480dba9bc10aedb3f72bd525118..648e9654604e1d2c6191e3aceb938da4e1c031b2:/src/t_string.c diff --git a/src/t_string.c b/src/t_string.c index dcb37e55..736b1673 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 */ @@ -88,10 +96,8 @@ static int getBitOffsetFromArgument(redisClient *c, robj *o, size_t *offset) { if (getLongLongFromObjectOrReply(c,o,&loffset,err) != REDIS_OK) return REDIS_ERR; - /* Limit offset to SIZE_T_MAX or 512MB in bytes */ - if ((loffset < 0) || - ((unsigned long long)loffset >= (unsigned)SIZE_T_MAX) || - ((unsigned long long)loffset >> 3) >= (512*1024*1024)) + /* Limit offset to 512MB in bytes */ + if ((loffset < 0) || ((unsigned long long)loffset >> 3) >= (512*1024*1024)) { addReplyError(c,err); return REDIS_ERR; @@ -139,7 +145,7 @@ void setbitCommand(redisClient *c) { byte = bitoffset >> 3; bit = 7 - (bitoffset & 0x7); on = bitvalue & 0x1; - o->ptr = sdsgrowsafe(o->ptr,byte+1); + o->ptr = sdsgrowzero(o->ptr,byte+1); ((char*)o->ptr)[byte] |= on << bit; ((char*)o->ptr)[byte] &= ~((!on) << bit); @@ -172,6 +178,120 @@ 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 getrangeCommand(redisClient *c) { + robj *o; + long start, end; + char *str, llbuf[32]; + size_t strlen; + + if (getLongFromObjectOrReply(c,c->argv[2],&start,NULL) != REDIS_OK) + return; + if (getLongFromObjectOrReply(c,c->argv[3],&end,NULL) != REDIS_OK) + return; + if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL || + checkType(c,o,REDIS_STRING)) return; + + if (o->encoding == REDIS_ENCODING_INT) { + str = llbuf; + strlen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr); + } else { + str = o->ptr; + strlen = sdslen(str); + } + + /* 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; + if ((unsigned)end >= strlen) end = strlen-1; + + /* Precondition: end >= 0 && end < strlen, so the only condition where + * nothing can be returned is: start > end. */ + if (start > end) { + addReply(c,shared.nullbulk); + } else { + addReplyBulkCBuffer(c,(char*)str+start,end-start+1); + } +} + void mgetCommand(redisClient *c) { int j; @@ -315,50 +435,20 @@ void appendCommand(redisClient *c) { addReplyLongLong(c,totlen); } -void substrCommand(redisClient *c) { - robj *o; - long start = atoi(c->argv[2]->ptr); - long end = atoi(c->argv[3]->ptr); - size_t rangelen, strlen; - sds range; - - if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL || - checkType(c,o,REDIS_STRING)) return; - - o = getDecodedObject(o); - strlen = sdslen(o->ptr); - - /* 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); - decrRefCount(o); - return; - } - if ((size_t)end >= strlen) end = strlen-1; - rangelen = (end-start)+1; - - /* Return the result */ - addReplySds(c,sdscatprintf(sdsempty(),"$%zu\r\n",rangelen)); - range = sdsnewlen((char*)o->ptr+start,rangelen); - addReplySds(c,range); - addReply(c,shared.crlf); - decrRefCount(o); -} - void strlenCommand(redisClient *c) { robj *o; if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL || checkType(c,o,REDIS_STRING)) return; - o = getDecodedObject(o); - addReplyLongLong(c,sdslen(o->ptr)); - decrRefCount(o); + if (o->encoding == REDIS_ENCODING_RAW) { + addReplyLongLong(c,sdslen(o->ptr)); + } else if (o->encoding == REDIS_ENCODING_INT) { + char llbuf[32]; + int len = ll2string(llbuf,sizeof(llbuf),(long)o->ptr); + addReplyLongLong(c,len); + } else { + redisPanic("Unknown string encoding"); + } } +