summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorantirez <antirez@gmail.com>2011-11-12 19:27:35 +0100
committerantirez <antirez@gmail.com>2011-11-12 19:27:35 +0100
commit5574b53eae1743cca9eed5a9dd25bf418c974701 (patch)
treec671e9fb2ab7187e074fd3ea2cb5c4d623f9a0ef /src
parent64c7499eb88040c26aba264ab52756fd764d8fa4 (diff)
downloadredis-5574b53eae1743cca9eed5a9dd25bf418c974701.tar.gz
INCRBYFLOAT implementation
Diffstat (limited to 'src')
-rw-r--r--src/object.c59
-rw-r--r--src/redis.c1
-rw-r--r--src/redis.h4
-rw-r--r--src/t_string.c26
4 files changed, 88 insertions, 2 deletions
diff --git a/src/object.c b/src/object.c
index fd7786ed6..d74fc9650 100644
--- a/src/object.c
+++ b/src/object.c
@@ -44,6 +44,21 @@ robj *createStringObjectFromLongLong(long long value) {
return o;
}
+/* Note: this function is defined into object.c since here it is where it
+ * belongs but it is actually designed to be used just for INCRBYFLOAT */
+robj *createStringObjectFromLongDouble(long double value) {
+ char buf[256];
+ int len;
+
+ /* We use 17 digits precision since with 128 bit floats that precision
+ * after rouding is able to represent most small decimal numbers in a way
+ * that is "non surprising" for the user (that is, most small decimal
+ * numbers will be represented in a way that when converted back into
+ * a string are exactly the same as what the user typed.) */
+ len = snprintf(buf,sizeof(buf),"%.17Lg", value);
+ return createStringObject(buf,len);
+}
+
robj *dupStringObject(robj *o) {
redisAssertWithInfo(NULL,o,o->encoding == REDIS_ENCODING_RAW);
return createStringObject(o->ptr,sdslen(o->ptr));
@@ -350,8 +365,10 @@ int getDoubleFromObject(robj *o, double *target) {
} else {
redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
if (o->encoding == REDIS_ENCODING_RAW) {
+ errno = 0;
value = strtod(o->ptr, &eptr);
- if (eptr[0] != '\0' || isnan(value)) return REDIS_ERR;
+ if (eptr[0] != '\0' || errno == ERANGE || isnan(value))
+ return REDIS_ERR;
} else if (o->encoding == REDIS_ENCODING_INT) {
value = (long)o->ptr;
} else {
@@ -369,7 +386,45 @@ int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const ch
if (msg != NULL) {
addReplyError(c,(char*)msg);
} else {
- addReplyError(c,"value is not a double");
+ addReplyError(c,"value is not a valid float");
+ }
+ return REDIS_ERR;
+ }
+
+ *target = value;
+ return REDIS_OK;
+}
+
+int getLongDoubleFromObject(robj *o, long double *target) {
+ long double value;
+ char *eptr;
+
+ if (o == NULL) {
+ value = 0;
+ } else {
+ redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);
+ if (o->encoding == REDIS_ENCODING_RAW) {
+ errno = 0;
+ value = strtold(o->ptr, &eptr);
+ if (eptr[0] != '\0' || errno == ERANGE || isnan(value))
+ return REDIS_ERR;
+ } else if (o->encoding == REDIS_ENCODING_INT) {
+ value = (long)o->ptr;
+ } else {
+ redisPanic("Unknown string encoding");
+ }
+ }
+ *target = value;
+ return REDIS_OK;
+}
+
+int getLongDoubleFromObjectOrReply(redisClient *c, robj *o, long double *target, const char *msg) {
+ long double value;
+ if (getLongDoubleFromObject(o, &value) != REDIS_OK) {
+ if (msg != NULL) {
+ addReplyError(c,(char*)msg);
+ } else {
+ addReplyError(c,"value is not a valid float");
}
return REDIS_ERR;
}
diff --git a/src/redis.c b/src/redis.c
index 7d2094390..527b05385 100644
--- a/src/redis.c
+++ b/src/redis.c
@@ -165,6 +165,7 @@ struct redisCommand redisCommandTable[] = {
{"hexists",hexistsCommand,3,"r",0,NULL,1,1,1,0,0},
{"incrby",incrbyCommand,3,"wm",0,NULL,1,1,1,0,0},
{"decrby",decrbyCommand,3,"wm",0,NULL,1,1,1,0,0},
+ {"incrbyfloat",incrbyfloatCommand,3,"wm",0,NULL,1,1,1,0,0},
{"getset",getsetCommand,3,"wm",0,NULL,1,1,1,0,0},
{"mset",msetCommand,-3,"wm",0,NULL,1,-1,2,0,0},
{"msetnx",msetnxCommand,-3,"wm",0,NULL,1,-1,2,0,0},
diff --git a/src/redis.h b/src/redis.h
index d660cae91..0024792cc 100644
--- a/src/redis.h
+++ b/src/redis.h
@@ -811,6 +811,7 @@ robj *tryObjectEncoding(robj *o);
robj *getDecodedObject(robj *o);
size_t stringObjectLen(robj *o);
robj *createStringObjectFromLongLong(long long value);
+robj *createStringObjectFromLongDouble(long double value);
robj *createListObject(void);
robj *createZiplistObject(void);
robj *createSetObject(void);
@@ -823,6 +824,8 @@ int checkType(redisClient *c, robj *o, int type);
int getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, const char *msg);
int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const char *msg);
int getLongLongFromObject(robj *o, long long *target);
+int getLongDoubleFromObject(robj *o, long double *target);
+int getLongDoubleFromObjectOrReply(redisClient *c, robj *o, long double *target, const char *msg);
char *strEncoding(int encoding);
int compareStringObjects(robj *a, robj *b);
int equalStringObjects(robj *a, robj *b);
@@ -1004,6 +1007,7 @@ void incrCommand(redisClient *c);
void decrCommand(redisClient *c);
void incrbyCommand(redisClient *c);
void decrbyCommand(redisClient *c);
+void incrbyfloatCommand(redisClient *c);
void selectCommand(redisClient *c);
void randomkeyCommand(redisClient *c);
void keysCommand(redisClient *c);
diff --git a/src/t_string.c b/src/t_string.c
index ce6233a98..1f0b1fbcc 100644
--- a/src/t_string.c
+++ b/src/t_string.c
@@ -1,4 +1,5 @@
#include "redis.h"
+#include <math.h> /* isnan(), isinf() */
/*-----------------------------------------------------------------------------
* String Commands
@@ -382,6 +383,31 @@ void decrbyCommand(redisClient *c) {
incrDecrCommand(c,-incr);
}
+void incrbyfloatCommand(redisClient *c) {
+ long double incr, value;
+ robj *o, *new;
+
+ o = lookupKeyWrite(c->db,c->argv[1]);
+ if (o != NULL && checkType(c,o,REDIS_STRING)) return;
+ if (getLongDoubleFromObjectOrReply(c,o,&value,NULL) != REDIS_OK ||
+ getLongDoubleFromObjectOrReply(c,c->argv[2],&incr,NULL) != REDIS_OK)
+ return;
+
+ value += incr;
+ if (isnan(value) || isinf(value)) {
+ addReplyError(c,"increment would produce NaN or Infinity");
+ return;
+ }
+ new = createStringObjectFromLongDouble(value);
+ if (o)
+ dbOverwrite(c->db,c->argv[1],new);
+ else
+ dbAdd(c->db,c->argv[1],new);
+ signalModifiedKey(c->db,c->argv[1]);
+ server.dirty++;
+ addReplyBulk(c,new);
+}
+
void appendCommand(redisClient *c) {
size_t totlen;
robj *o, *append;