LCOV - code coverage report
Current view: directory - redis/src - t_string.c (source / functions) Found Hit Coverage
Test: redis.info Lines: 259 251 96.9 %
Date: 2012-04-04 Functions: 25 24 96.0 %
Colors: not hit hit

       1                 : #include "redis.h"
       2                 : #include <math.h> /* isnan(), isinf() */
       3                 : 
       4                 : /*-----------------------------------------------------------------------------
       5                 :  * String Commands
       6                 :  *----------------------------------------------------------------------------*/
       7                 : 
       8                 : static int checkStringLength(redisClient *c, long long size) {
       9            3010 :     if (size > 512*1024*1024) {
      10               2 :         addReplyError(c,"string exceeds maximum allowed size (512MB)");
      11               2 :         return REDIS_ERR;
      12                 :     }
      13            3008 :     return REDIS_OK;
      14                 : }
      15                 : 
      16          841524 : void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expire, int unit) {
      17          841524 :     long long milliseconds = 0; /* initialized to avoid an harmness warning */
      18                 : 
      19          841524 :     if (expire) {
      20           19236 :         if (getLongLongFromObjectOrReply(c, expire, &milliseconds, NULL) != REDIS_OK)
      21                 :             return;
      22           19236 :         if (milliseconds <= 0) {
      23               1 :             addReplyError(c,"invalid expire time in SETEX");
      24               1 :             return;
      25                 :         }
      26           19235 :         if (unit == UNIT_SECONDS) milliseconds *= 1000;
      27                 :     }
      28                 : 
      29          841523 :     if (lookupKeyWrite(c->db,key) != NULL && nx) {
      30               2 :         addReply(c,shared.czero);
      31               2 :         return;
      32                 :     }
      33          841521 :     setKey(c->db,key,val);
      34          841521 :     server.dirty++;
      35          841521 :     if (expire) setExpire(c->db,key,mstime()+milliseconds);
      36          841521 :     addReply(c, nx ? shared.cone : shared.ok);
      37                 : }
      38                 : 
      39          822284 : void setCommand(redisClient *c) {
      40          822284 :     c->argv[2] = tryObjectEncoding(c->argv[2]);
      41          822284 :     setGenericCommand(c,0,c->argv[1],c->argv[2],NULL,0);
      42          822284 : }
      43                 : 
      44               4 : void setnxCommand(redisClient *c) {
      45               4 :     c->argv[2] = tryObjectEncoding(c->argv[2]);
      46               4 :     setGenericCommand(c,1,c->argv[1],c->argv[2],NULL,0);
      47               4 : }
      48                 : 
      49           19229 : void setexCommand(redisClient *c) {
      50           19229 :     c->argv[3] = tryObjectEncoding(c->argv[3]);
      51           19229 :     setGenericCommand(c,0,c->argv[1],c->argv[3],c->argv[2],UNIT_SECONDS);
      52           19229 : }
      53                 : 
      54               7 : void psetexCommand(redisClient *c) {
      55               7 :     c->argv[3] = tryObjectEncoding(c->argv[3]);
      56               7 :     setGenericCommand(c,0,c->argv[1],c->argv[3],c->argv[2],UNIT_MILLISECONDS);
      57               7 : }
      58                 : 
      59          116316 : int getGenericCommand(redisClient *c) {
      60                 :     robj *o;
      61                 : 
      62          116316 :     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL)
      63              21 :         return REDIS_OK;
      64                 : 
      65          116295 :     if (o->type != REDIS_STRING) {
      66               0 :         addReply(c,shared.wrongtypeerr);
      67               0 :         return REDIS_ERR;
      68                 :     } else {
      69          116295 :         addReplyBulk(c,o);
      70          116295 :         return REDIS_OK;
      71                 :     }
      72                 : }
      73                 : 
      74          116314 : void getCommand(redisClient *c) {
      75          116314 :     getGenericCommand(c);
      76          116314 : }
      77                 : 
      78               2 : void getsetCommand(redisClient *c) {
      79               2 :     if (getGenericCommand(c) == REDIS_ERR) return;
      80               2 :     c->argv[2] = tryObjectEncoding(c->argv[2]);
      81               2 :     setKey(c->db,c->argv[1],c->argv[2]);
      82               2 :     server.dirty++;
      83                 : }
      84                 : 
      85            2027 : static int getBitOffsetFromArgument(redisClient *c, robj *o, size_t *offset) {
      86                 :     long long loffset;
      87            2027 :     char *err = "bit offset is not an integer or out of range";
      88                 : 
      89            2027 :     if (getLongLongFromObjectOrReply(c,o,&loffset,err) != REDIS_OK)
      90               0 :         return REDIS_ERR;
      91                 : 
      92                 :     /* Limit offset to 512MB in bytes */
      93            2027 :     if ((loffset < 0) || ((unsigned long long)loffset >> 3) >= (512*1024*1024))
      94                 :     {
      95               2 :         addReplyError(c,err);
      96               2 :         return REDIS_ERR;
      97                 :     }
      98                 : 
      99            2025 :     *offset = (size_t)loffset;
     100            2025 :     return REDIS_OK;
     101                 : }
     102                 : 
     103            2012 : void setbitCommand(redisClient *c) {
     104                 :     robj *o;
     105            2012 :     char *err = "bit is not an integer or out of range";
     106                 :     size_t bitoffset;
     107                 :     int byte, bit;
     108                 :     int byteval, bitval;
     109                 :     long on;
     110                 : 
     111            2012 :     if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK)
     112                 :         return;
     113                 : 
     114            2010 :     if (getLongFromObjectOrReply(c,c->argv[3],&on,err) != REDIS_OK)
     115                 :         return;
     116                 : 
     117                 :     /* Bits can only be set or cleared... */
     118            2010 :     if (on & ~1) {
     119               4 :         addReplyError(c,err);
     120               4 :         return;
     121                 :     }
     122                 : 
     123            2006 :     o = lookupKeyWrite(c->db,c->argv[1]);
     124            2006 :     if (o == NULL) {
     125               2 :         o = createObject(REDIS_STRING,sdsempty());
     126               2 :         dbAdd(c->db,c->argv[1],o);
     127                 :     } else {
     128            2004 :         if (checkType(c,o,REDIS_STRING)) return;
     129                 : 
     130                 :         /* Create a copy when the object is shared or encoded. */
     131            2003 :         if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
     132               1 :             robj *decoded = getDecodedObject(o);
     133               2 :             o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
     134               1 :             decrRefCount(decoded);
     135               1 :             dbOverwrite(c->db,c->argv[1],o);
     136                 :         }
     137                 :     }
     138                 : 
     139                 :     /* Grow sds value to the right length if necessary */
     140            2005 :     byte = bitoffset >> 3;
     141            2005 :     o->ptr = sdsgrowzero(o->ptr,byte+1);
     142                 : 
     143                 :     /* Get current values */
     144            2005 :     byteval = ((char*)o->ptr)[byte];
     145            2005 :     bit = 7 - (bitoffset & 0x7);
     146            2005 :     bitval = byteval & (1 << bit);
     147                 : 
     148                 :     /* Update byte with new bit value and return original value */
     149            2005 :     byteval &= ~(1 << bit);
     150            2005 :     byteval |= ((on & 0x1) << bit);
     151            2005 :     ((char*)o->ptr)[byte] = byteval;
     152            2005 :     signalModifiedKey(c->db,c->argv[1]);
     153            2005 :     server.dirty++;
     154            2005 :     addReply(c, bitval ? shared.cone : shared.czero);
     155                 : }
     156                 : 
     157              15 : void getbitCommand(redisClient *c) {
     158                 :     robj *o;
     159                 :     char llbuf[32];
     160                 :     size_t bitoffset;
     161                 :     size_t byte, bit;
     162              15 :     size_t bitval = 0;
     163                 : 
     164              15 :     if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK)
     165                 :         return;
     166                 : 
     167              29 :     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
     168              14 :         checkType(c,o,REDIS_STRING)) return;
     169                 : 
     170              14 :     byte = bitoffset >> 3;
     171              14 :     bit = 7 - (bitoffset & 0x7);
     172              14 :     if (o->encoding != REDIS_ENCODING_RAW) {
     173               7 :         if (byte < (size_t)ll2string(llbuf,sizeof(llbuf),(long)o->ptr))
     174               4 :             bitval = llbuf[byte] & (1 << bit);
     175                 :     } else {
     176              14 :         if (byte < sdslen(o->ptr))
     177               4 :             bitval = ((char*)o->ptr)[byte] & (1 << bit);
     178                 :     }
     179                 : 
     180              14 :     addReply(c, bitval ? shared.cone : shared.czero);
     181                 : }
     182                 : 
     183              15 : void setrangeCommand(redisClient *c) {
     184                 :     robj *o;
     185                 :     long offset;
     186              15 :     sds value = c->argv[3]->ptr;
     187                 : 
     188              15 :     if (getLongFromObjectOrReply(c,c->argv[2],&offset,NULL) != REDIS_OK)
     189                 :         return;
     190                 : 
     191              15 :     if (offset < 0) {
     192               1 :         addReplyError(c,"offset is out of range");
     193               1 :         return;
     194                 :     }
     195                 : 
     196              14 :     o = lookupKeyWrite(c->db,c->argv[1]);
     197              14 :     if (o == NULL) {
     198                 :         /* Return 0 when setting nothing on a non-existing string */
     199               4 :         if (sdslen(value) == 0) {
     200               1 :             addReply(c,shared.czero);
     201               1 :             return;
     202                 :         }
     203                 : 
     204                 :         /* Return when the resulting string exceeds allowed size */
     205               6 :         if (checkStringLength(c,offset+sdslen(value)) != REDIS_OK)
     206                 :             return;
     207                 : 
     208               2 :         o = createObject(REDIS_STRING,sdsempty());
     209               2 :         dbAdd(c->db,c->argv[1],o);
     210                 :     } else {
     211                 :         size_t olen;
     212                 : 
     213                 :         /* Key exists, check type */
     214              10 :         if (checkType(c,o,REDIS_STRING))
     215                 :             return;
     216                 : 
     217                 :         /* Return existing string length when setting nothing */
     218               9 :         olen = stringObjectLen(o);
     219               9 :         if (sdslen(value) == 0) {
     220               2 :             addReplyLongLong(c,olen);
     221               2 :             return;
     222                 :         }
     223                 : 
     224                 :         /* Return when the resulting string exceeds allowed size */
     225              14 :         if (checkStringLength(c,offset+sdslen(value)) != REDIS_OK)
     226                 :             return;
     227                 : 
     228                 :         /* Create a copy when the object is shared or encoded. */
     229               6 :         if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
     230               3 :             robj *decoded = getDecodedObject(o);
     231               6 :             o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
     232               3 :             decrRefCount(decoded);
     233               3 :             dbOverwrite(c->db,c->argv[1],o);
     234                 :         }
     235                 :     }
     236                 : 
     237               8 :     if (sdslen(value) > 0) {
     238               8 :         o->ptr = sdsgrowzero(o->ptr,offset+sdslen(value));
     239               8 :         memcpy((char*)o->ptr+offset,value,sdslen(value));
     240               8 :         signalModifiedKey(c->db,c->argv[1]);
     241               8 :         server.dirty++;
     242                 :     }
     243              16 :     addReplyLongLong(c,sdslen(o->ptr));
     244                 : }
     245                 : 
     246            1013 : void getrangeCommand(redisClient *c) {
     247                 :     robj *o;
     248                 :     long start, end;
     249                 :     char *str, llbuf[32];
     250                 :     size_t strlen;
     251                 : 
     252            1013 :     if (getLongFromObjectOrReply(c,c->argv[2],&start,NULL) != REDIS_OK)
     253                 :         return;
     254            1013 :     if (getLongFromObjectOrReply(c,c->argv[3],&end,NULL) != REDIS_OK)
     255                 :         return;
     256            2025 :     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptybulk)) == NULL ||
     257            1012 :         checkType(c,o,REDIS_STRING)) return;
     258                 : 
     259            1012 :     if (o->encoding == REDIS_ENCODING_INT) {
     260               6 :         str = llbuf;
     261               6 :         strlen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr);
     262                 :     } else {
     263            1006 :         str = o->ptr;
     264            1006 :         strlen = sdslen(str);
     265                 :     }
     266                 : 
     267                 :     /* Convert negative indexes */
     268            1012 :     if (start < 0) start = strlen+start;
     269            1012 :     if (end < 0) end = strlen+end;
     270            1012 :     if (start < 0) start = 0;
     271            1012 :     if (end < 0) end = 0;
     272            1012 :     if ((unsigned)end >= strlen) end = strlen-1;
     273                 : 
     274                 :     /* Precondition: end >= 0 && end < strlen, so the only condition where
     275                 :      * nothing can be returned is: start > end. */
     276            1012 :     if (start > end) {
     277             741 :         addReply(c,shared.emptybulk);
     278                 :     } else {
     279             271 :         addReplyBulkCBuffer(c,(char*)str+start,end-start+1);
     280                 :     }
     281                 : }
     282                 : 
     283               5 : void mgetCommand(redisClient *c) {
     284                 :     int j;
     285                 : 
     286               5 :     addReplyMultiBulkLen(c,c->argc-1);
     287              20 :     for (j = 1; j < c->argc; j++) {
     288              15 :         robj *o = lookupKeyRead(c->db,c->argv[j]);
     289              15 :         if (o == NULL) {
     290               5 :             addReply(c,shared.nullbulk);
     291                 :         } else {
     292              10 :             if (o->type != REDIS_STRING) {
     293               1 :                 addReply(c,shared.nullbulk);
     294                 :             } else {
     295               9 :                 addReplyBulk(c,o);
     296                 :             }
     297                 :         }
     298                 :     }
     299               5 : }
     300                 : 
     301               5 : void msetGenericCommand(redisClient *c, int nx) {
     302               5 :     int j, busykeys = 0;
     303                 : 
     304               5 :     if ((c->argc % 2) == 0) {
     305               1 :         addReplyError(c,"wrong number of arguments for MSET");
     306               1 :         return;
     307                 :     }
     308                 :     /* Handle the NX flag. The MSETNX semantic is to return zero and don't
     309                 :      * set nothing at all if at least one already key exists. */
     310               4 :     if (nx) {
     311               7 :         for (j = 1; j < c->argc; j += 2) {
     312               5 :             if (lookupKeyWrite(c->db,c->argv[j]) != NULL) {
     313               1 :                 busykeys++;
     314                 :             }
     315                 :         }
     316               2 :         if (busykeys) {
     317               1 :             addReply(c, shared.czero);
     318               1 :             return;
     319                 :         }
     320                 :     }
     321                 : 
     322              12 :     for (j = 1; j < c->argc; j += 2) {
     323               9 :         c->argv[j+1] = tryObjectEncoding(c->argv[j+1]);
     324               9 :         setKey(c->db,c->argv[j],c->argv[j+1]);
     325                 :     }
     326               3 :     server.dirty += (c->argc-1)/2;
     327               3 :     addReply(c, nx ? shared.cone : shared.ok);
     328                 : }
     329                 : 
     330               3 : void msetCommand(redisClient *c) {
     331               3 :     msetGenericCommand(c,0);
     332               3 : }
     333                 : 
     334               2 : void msetnxCommand(redisClient *c) {
     335               2 :     msetGenericCommand(c,1);
     336               2 : }
     337                 : 
     338              18 : void incrDecrCommand(redisClient *c, long long incr) {
     339                 :     long long value, oldvalue;
     340                 :     robj *o, *new;
     341                 : 
     342              18 :     o = lookupKeyWrite(c->db,c->argv[1]);
     343              18 :     if (o != NULL && checkType(c,o,REDIS_STRING)) return;
     344              17 :     if (getLongLongFromObjectOrReply(c,o,&value,NULL) != REDIS_OK) return;
     345                 : 
     346              13 :     oldvalue = value;
     347              35 :     if ((incr < 0 && oldvalue < 0 && incr < (LLONG_MIN-oldvalue)) ||
     348              22 :         (incr > 0 && oldvalue > 0 && incr > (LLONG_MAX-oldvalue))) {
     349               0 :         addReplyError(c,"increment or decrement would overflow");
     350               0 :         return;
     351                 :     }
     352              13 :     value += incr;
     353              13 :     new = createStringObjectFromLongLong(value);
     354              13 :     if (o)
     355              10 :         dbOverwrite(c->db,c->argv[1],new);
     356                 :     else
     357               3 :         dbAdd(c->db,c->argv[1],new);
     358              13 :     signalModifiedKey(c->db,c->argv[1]);
     359              13 :     server.dirty++;
     360              13 :     addReply(c,shared.colon);
     361              13 :     addReply(c,new);
     362              13 :     addReply(c,shared.crlf);
     363                 : }
     364                 : 
     365              16 : void incrCommand(redisClient *c) {
     366              16 :     incrDecrCommand(c,1);
     367              16 : }
     368                 : 
     369               0 : void decrCommand(redisClient *c) {
     370               0 :     incrDecrCommand(c,-1);
     371               0 : }
     372                 : 
     373               1 : void incrbyCommand(redisClient *c) {
     374                 :     long long incr;
     375                 : 
     376               1 :     if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;
     377               1 :     incrDecrCommand(c,incr);
     378                 : }
     379                 : 
     380               1 : void decrbyCommand(redisClient *c) {
     381                 :     long long incr;
     382                 : 
     383               1 :     if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;
     384               1 :     incrDecrCommand(c,-incr);
     385                 : }
     386                 : 
     387              11 : void incrbyfloatCommand(redisClient *c) {
     388                 :     long double incr, value;
     389                 :     robj *o, *new, *aux;
     390                 : 
     391              11 :     o = lookupKeyWrite(c->db,c->argv[1]);
     392              11 :     if (o != NULL && checkType(c,o,REDIS_STRING)) return;
     393              17 :     if (getLongDoubleFromObjectOrReply(c,o,&value,NULL) != REDIS_OK ||
     394               7 :         getLongDoubleFromObjectOrReply(c,c->argv[2],&incr,NULL) != REDIS_OK)
     395                 :         return;
     396                 : 
     397               7 :     value += incr;
     398               7 :     if (isnan(value) || isinf(value)) {
     399               1 :         addReplyError(c,"increment would produce NaN or Infinity");
     400               1 :         return;
     401                 :     }
     402               6 :     new = createStringObjectFromLongDouble(value);
     403               6 :     if (o)
     404               5 :         dbOverwrite(c->db,c->argv[1],new);
     405                 :     else
     406               1 :         dbAdd(c->db,c->argv[1],new);
     407               6 :     signalModifiedKey(c->db,c->argv[1]);
     408               6 :     server.dirty++;
     409               6 :     addReplyBulk(c,new);
     410                 : 
     411                 :     /* Always replicate INCRBYFLOAT as a SET command with the final value
     412                 :      * in order to make sure that differences in float pricision or formatting
     413                 :      * will not create differences in replicas or after an AOF restart. */
     414               6 :     aux = createStringObject("SET",3);
     415               6 :     rewriteClientCommandArgument(c,0,aux);
     416               6 :     decrRefCount(aux);
     417               6 :     rewriteClientCommandArgument(c,2,new);
     418                 : }
     419                 : 
     420            3005 : void appendCommand(redisClient *c) {
     421                 :     size_t totlen;
     422                 :     robj *o, *append;
     423                 : 
     424            3005 :     o = lookupKeyWrite(c->db,c->argv[1]);
     425            3005 :     if (o == NULL) {
     426                 :         /* Create the key */
     427               5 :         c->argv[2] = tryObjectEncoding(c->argv[2]);
     428               5 :         dbAdd(c->db,c->argv[1],c->argv[2]);
     429               5 :         incrRefCount(c->argv[2]);
     430               5 :         totlen = stringObjectLen(c->argv[2]);
     431                 :     } else {
     432                 :         /* Key exists, check type */
     433            3000 :         if (checkType(c,o,REDIS_STRING))
     434                 :             return;
     435                 : 
     436                 :         /* "append" is an argument, so always an sds */
     437            3000 :         append = c->argv[2];
     438            6000 :         totlen = stringObjectLen(o)+sdslen(append->ptr);
     439            6000 :         if (checkStringLength(c,totlen) != REDIS_OK)
     440                 :             return;
     441                 : 
     442                 :         /* If the object is shared or encoded, we have to make a copy */
     443            3000 :         if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
     444               3 :             robj *decoded = getDecodedObject(o);
     445               6 :             o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
     446               3 :             decrRefCount(decoded);
     447               3 :             dbOverwrite(c->db,c->argv[1],o);
     448                 :         }
     449                 : 
     450                 :         /* Append the value */
     451            6000 :         o->ptr = sdscatlen(o->ptr,append->ptr,sdslen(append->ptr));
     452            6000 :         totlen = sdslen(o->ptr);
     453                 :     }
     454            3005 :     signalModifiedKey(c->db,c->argv[1]);
     455            3005 :     server.dirty++;
     456            3005 :     addReplyLongLong(c,totlen);
     457                 : }
     458                 : 
     459               3 : void strlenCommand(redisClient *c) {
     460                 :     robj *o;
     461               5 :     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
     462               2 :         checkType(c,o,REDIS_STRING)) return;
     463               2 :     addReplyLongLong(c,stringObjectLen(o));
     464                 : }
     465                 : 

Generated by: LCOV version 1.7