From eae33c1c813953e27dc37dfe7c3dcc246fda294f Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Fri, 10 Dec 2010 11:58:14 +0100 Subject: [PATCH] Add generic function to grow an sds value Move logic concerned with setting a bit in an sds to the SETBIT command instead of keeping it in sds.c. The function to grow an sds can and will be reused for a command to set a range within a string value. --- src/sds.c | 45 +++++++++++++++++++------------------------- src/sds.h | 2 +- src/t_string.c | 26 +++++++++++++++++-------- tests/unit/basic.tcl | 8 ++++---- 4 files changed, 42 insertions(+), 39 deletions(-) diff --git a/src/sds.c b/src/sds.c index ff477285..de620191 100644 --- a/src/sds.c +++ b/src/sds.c @@ -116,6 +116,25 @@ static sds sdsMakeRoomFor(sds s, size_t addlen) { return newsh->buf; } +/* Grow the sds to have the specified length. Bytes that were not part of + * the original length of the sds will be set to NULL. */ +sds sdsgrowsafe(sds s, size_t len) { + struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr))); + size_t totlen, curlen = sh->len; + + if (len <= curlen) return s; + s = sdsMakeRoomFor(s,len-curlen); + if (s == NULL) return NULL; + + /* Make sure added region doesn't contain garbage */ + sh = (void*)(s-(sizeof(struct sdshdr))); + memset(s+curlen,0,(len-curlen+1)); /* also set trailing NULL byte */ + totlen = sh->len+sh->free; + sh->len = len; + sh->free = totlen-sh->len; + return s; +} + sds sdscatlen(sds s, void *t, size_t len) { struct sdshdr *sh; size_t curlen = sdslen(s); @@ -155,32 +174,6 @@ sds sdscpy(sds s, char *t) { return sdscpylen(s, t, strlen(t)); } -sds sdssetbit(sds s, size_t bit, int on) { - struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr))); - int byte = bit >> 3; - int reqlen = byte+1; - - if (reqlen > sh->len) { - size_t totlen; - - s = sdsMakeRoomFor(s,reqlen-sh->len); - if (s == NULL) return NULL; - sh = (void*)(s-(sizeof(struct sdshdr))); - - /* Make sure added region doesn't contain garbage */ - totlen = sh->len+sh->free; - memset(s+sh->len,0,sh->free+1); - sh->len = reqlen; - sh->free = totlen-sh->len; - } - - bit = 7 - (bit & 0x7); - on &= 0x1; - s[byte] |= on << bit; - s[byte] &= ~((!on) << bit); - return s; -} - sds sdscatvprintf(sds s, const char *fmt, va_list ap) { va_list cpy; char *buf, *t; diff --git a/src/sds.h b/src/sds.h index 61ef36b6..44880183 100644 --- a/src/sds.h +++ b/src/sds.h @@ -49,11 +49,11 @@ size_t sdslen(const sds s); sds sdsdup(const sds s); void sdsfree(sds s); size_t sdsavail(sds s); +sds sdsgrowsafe(sds s, size_t len); sds sdscatlen(sds s, void *t, size_t len); sds sdscat(sds s, char *t); sds sdscpylen(sds s, char *t, size_t len); sds sdscpy(sds s, char *t); -sds sdssetbit(sds s, size_t bit, int on); sds sdscatvprintf(sds s, const char *fmt, va_list ap); #ifdef __GNUC__ diff --git a/src/t_string.c b/src/t_string.c index b77bcf0a..b41a1522 100644 --- a/src/t_string.c +++ b/src/t_string.c @@ -103,22 +103,26 @@ static int getBitOffsetFromArgument(redisClient *c, robj *o, size_t *offset) { void setbitCommand(redisClient *c) { robj *o; + char *err = "bit is not an integer or out of range"; size_t bitoffset; - int on; + long long bitvalue; + int byte, bit, on; if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK) return; - on = ((sds)c->argv[3]->ptr)[0] - '0'; - if (sdslen(c->argv[3]->ptr) != 1 || (on & ~1)) { - addReplyError(c,"bit should be 0 or 1"); + if (getLongLongFromObjectOrReply(c,c->argv[3],&bitvalue,err) != REDIS_OK) + return; + + /* A bit can only be set to be on or off... */ + if (bitvalue & ~1) { + addReplyError(c,err); return; } o = lookupKeyWrite(c->db,c->argv[1]); if (o == NULL) { - sds value = sdssetbit(sdsempty(),bitoffset,on); - o = createObject(REDIS_STRING,value); + o = createObject(REDIS_STRING,sdsempty()); dbAdd(c->db,c->argv[1],o); } else { if (checkType(c,o,REDIS_STRING)) return; @@ -130,9 +134,15 @@ void setbitCommand(redisClient *c) { decrRefCount(decoded); dbReplace(c->db,c->argv[1],o); } - - o->ptr = sdssetbit(o->ptr,bitoffset,on); } + + byte = bitoffset >> 3; + bit = 7 - (bitoffset & 0x7); + on = bitvalue & 0x1; + o->ptr = sdsgrowsafe(o->ptr,byte+1); + ((char*)o->ptr)[byte] |= on << bit; + ((char*)o->ptr)[byte] &= ~((!on) << bit); + touchWatchedKey(c->db,c->argv[1]); server.dirty++; addReply(c,shared.cone); diff --git a/tests/unit/basic.tcl b/tests/unit/basic.tcl index 8b1512f3..1324246f 100644 --- a/tests/unit/basic.tcl +++ b/tests/unit/basic.tcl @@ -416,10 +416,10 @@ start_server {tags {"basic"}} { test "SETBIT with non-bit argument" { r del mykey - assert_error "*0 or 1*" {r setbit mykey 0 -1} - assert_error "*0 or 1*" {r setbit mykey 0 2} - assert_error "*0 or 1*" {r setbit mykey 0 10} - assert_error "*0 or 1*" {r setbit mykey 0 01} + assert_error "*out of range*" {r setbit mykey 0 -1} + assert_error "*out of range*" {r setbit mykey 0 2} + assert_error "*out of range*" {r setbit mykey 0 10} + assert_error "*out of range*" {r setbit mykey 0 20} } test "GETBIT against non-existing key" { -- 2.45.2