]> git.saurik.com Git - redis.git/blobdiff - redis.c
reduce code complexity because zipmapLen now is O(1)
[redis.git] / redis.c
diff --git a/redis.c b/redis.c
index 03f90a02f36c3588868597bb4c70f44ba52e6dfd..e69d3a3abf89b99aaff9cf2fdca493c13c5077eb 100644 (file)
--- a/redis.c
+++ b/redis.c
@@ -697,6 +697,7 @@ static void hvalsCommand(redisClient *c);
 static void hgetallCommand(redisClient *c);
 static void hexistsCommand(redisClient *c);
 static void configCommand(redisClient *c);
+static void hincrbyCommand(redisClient *c);
 
 /*================================= Globals ================================= */
 
@@ -756,6 +757,7 @@ static struct redisCommand cmdTable[] = {
     {"zrank",zrankCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
     {"zrevrank",zrevrankCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
     {"hset",hsetCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
+    {"hincrby",hincrbyCommand,4,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,1,1,1},
     {"hget",hgetCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
     {"hdel",hdelCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
     {"hlen",hlenCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
@@ -5935,10 +5937,8 @@ static void hsetCommand(redisClient *c) {
         decrRefCount(valobj);
         o->ptr = zm;
 
-        /* And here there is the second check for hash conversion...
-         * we want to do it only if the operation was not just an update as
-         * zipmapLen() is O(N). */
-        if (!update && zipmapLen(zm) > server.hash_max_zipmap_entries)
+        /* And here there is the second check for hash conversion. */
+        if (zipmapLen(zm) > server.hash_max_zipmap_entries)
             convertToRealHash(o);
     } else {
         tryObjectEncoding(c->argv[2]);
@@ -5955,6 +5955,78 @@ static void hsetCommand(redisClient *c) {
     addReplySds(c,sdscatprintf(sdsempty(),":%d\r\n",update == 0));
 }
 
+static void hincrbyCommand(redisClient *c) {
+    long long value = 0, incr = 0;
+    robj *o = lookupKeyWrite(c->db,c->argv[1]);
+
+    if (o == NULL) {
+        o = createHashObject();
+        dictAdd(c->db->dict,c->argv[1],o);
+        incrRefCount(c->argv[1]);
+    } else {
+        if (o->type != REDIS_HASH) {
+            addReply(c,shared.wrongtypeerr);
+            return;
+        }
+    }
+
+    robj *o_incr = getDecodedObject(c->argv[3]);
+    incr = strtoll(o_incr->ptr, NULL, 10);
+    decrRefCount(o_incr);
+
+    if (o->encoding == REDIS_ENCODING_ZIPMAP) {
+        unsigned char *zm = o->ptr;
+        unsigned char *zval;
+        unsigned int zvlen;
+
+        /* Find value if already present in hash */
+        if (zipmapGet(zm,c->argv[2]->ptr,sdslen(c->argv[2]->ptr),
+            &zval,&zvlen)) {
+            /* strtoll needs the char* to have a trailing \0, but
+             * the zipmap doesn't include them. */
+            sds szval = sdsnewlen(zval, zvlen);
+            value = strtoll(szval,NULL,10);
+            sdsfree(szval);
+        }
+
+        value += incr;
+        sds svalue = sdscatprintf(sdsempty(),"%lld",value);
+        zm = zipmapSet(zm,c->argv[2]->ptr,sdslen(c->argv[2]->ptr),
+            (unsigned char*)svalue,sdslen(svalue),NULL);
+        sdsfree(svalue);
+        o->ptr = zm;
+
+        /* Check if the zipmap needs to be converted. */
+        if (zipmapLen(zm) > server.hash_max_zipmap_entries)
+            convertToRealHash(o);
+    } else {
+        robj *hval;
+        dictEntry *de;
+
+        /* Find value if already present in hash */
+        de = dictFind(o->ptr,c->argv[2]);
+        if (de != NULL) {
+            hval = dictGetEntryVal(de);
+            if (hval->encoding == REDIS_ENCODING_RAW)
+                value = strtoll(hval->ptr,NULL,10);
+            else if (hval->encoding == REDIS_ENCODING_INT)
+                value = (long)hval->ptr;
+            else
+                redisAssert(1 != 1);
+        }
+
+        value += incr;
+        hval = createObject(REDIS_STRING,sdscatprintf(sdsempty(),"%lld",value));
+        tryObjectEncoding(hval);
+        if (dictReplace(o->ptr,c->argv[2],hval)) {
+            incrRefCount(c->argv[2]);
+        }
+    }
+
+    server.dirty++;
+    addReplyLong(c, value);
+}
+
 static void hgetCommand(redisClient *c) {
     robj *o;