summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/module.c27
-rw-r--r--src/networking.c2
-rw-r--r--src/object.c2
-rw-r--r--src/redismodule.h4
-rw-r--r--src/t_hash.c2
-rw-r--r--src/t_stream.c23
-rw-r--r--src/util.c60
-rw-r--r--src/util.h9
8 files changed, 91 insertions, 38 deletions
diff --git a/src/module.c b/src/module.c
index 3a161c05f..ad34e7b64 100644
--- a/src/module.c
+++ b/src/module.c
@@ -3769,6 +3769,31 @@ loaderr:
return 0;
}
+/* In the context of the rdb_save method of a module data type, saves a long double
+ * value to the RDB file. The double can be a valid number, a NaN or infinity.
+ * It is possible to load back the value with RedisModule_LoadLongDouble(). */
+void RM_SaveLongDouble(RedisModuleIO *io, long double value) {
+ if (io->error) return;
+ char buf[MAX_LONG_DOUBLE_CHARS];
+ /* Long double has different number of bits in different platforms, so we
+ * save it as a string type. */
+ size_t len = ld2string(buf,sizeof(buf),value,LD_STR_HEX);
+ RM_SaveStringBuffer(io,buf,len+1); /* len+1 for '\0' */
+}
+
+/* In the context of the rdb_save method of a module data type, loads back the
+ * long double value saved by RedisModule_SaveLongDouble(). */
+long double RM_LoadLongDouble(RedisModuleIO *io) {
+ if (io->error) return 0;
+ long double value;
+ size_t len;
+ char* str = RM_LoadStringBuffer(io,&len);
+ if (!str) return 0;
+ string2ld(str,len,&value);
+ RM_Free(str);
+ return value;
+}
+
/* Iterate over modules, and trigger rdb aux saving for the ones modules types
* who asked for it. */
ssize_t rdbSaveModulesAux(rio *rdb, int when) {
@@ -6849,6 +6874,8 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API(LoadDouble);
REGISTER_API(SaveFloat);
REGISTER_API(LoadFloat);
+ REGISTER_API(SaveLongDouble);
+ REGISTER_API(LoadLongDouble);
REGISTER_API(EmitAOF);
REGISTER_API(Log);
REGISTER_API(LogIOError);
diff --git a/src/networking.c b/src/networking.c
index 9336c177c..901ce0a7b 100644
--- a/src/networking.c
+++ b/src/networking.c
@@ -530,7 +530,7 @@ void addReplyHumanLongDouble(client *c, long double d) {
decrRefCount(o);
} else {
char buf[MAX_LONG_DOUBLE_CHARS];
- int len = ld2string(buf,sizeof(buf),d,1);
+ int len = ld2string(buf,sizeof(buf),d,LD_STR_HUMAN);
addReplyProto(c,",",1);
addReplyProto(c,buf,len);
addReplyProto(c,"\r\n",2);
diff --git a/src/object.c b/src/object.c
index 47b21ae1f..5e9a99dec 100644
--- a/src/object.c
+++ b/src/object.c
@@ -178,7 +178,7 @@ robj *createStringObjectFromLongLongForValue(long long value) {
* The 'humanfriendly' option is used for INCRBYFLOAT and HINCRBYFLOAT. */
robj *createStringObjectFromLongDouble(long double value, int humanfriendly) {
char buf[MAX_LONG_DOUBLE_CHARS];
- int len = ld2string(buf,sizeof(buf),value,humanfriendly);
+ int len = ld2string(buf,sizeof(buf),value,humanfriendly? LD_STR_HUMAN: LD_STR_AUTO);
return createStringObject(buf,len);
}
diff --git a/src/redismodule.h b/src/redismodule.h
index 26604d63c..728c7f584 100644
--- a/src/redismodule.h
+++ b/src/redismodule.h
@@ -535,6 +535,8 @@ void REDISMODULE_API_FUNC(RedisModule_SaveDouble)(RedisModuleIO *io, double valu
double REDISMODULE_API_FUNC(RedisModule_LoadDouble)(RedisModuleIO *io);
void REDISMODULE_API_FUNC(RedisModule_SaveFloat)(RedisModuleIO *io, float value);
float REDISMODULE_API_FUNC(RedisModule_LoadFloat)(RedisModuleIO *io);
+void REDISMODULE_API_FUNC(RedisModule_SaveLongDouble)(RedisModuleIO *io, long double value);
+long double REDISMODULE_API_FUNC(RedisModule_LoadLongDouble)(RedisModuleIO *io);
void REDISMODULE_API_FUNC(RedisModule_Log)(RedisModuleCtx *ctx, const char *level, const char *fmt, ...);
void REDISMODULE_API_FUNC(RedisModule_LogIOError)(RedisModuleIO *io, const char *levelstr, const char *fmt, ...);
void REDISMODULE_API_FUNC(RedisModule__Assert)(const char *estr, const char *file, int line);
@@ -741,6 +743,8 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API(LoadDouble);
REDISMODULE_GET_API(SaveFloat);
REDISMODULE_GET_API(LoadFloat);
+ REDISMODULE_GET_API(SaveLongDouble);
+ REDISMODULE_GET_API(LoadLongDouble);
REDISMODULE_GET_API(EmitAOF);
REDISMODULE_GET_API(Log);
REDISMODULE_GET_API(LogIOError);
diff --git a/src/t_hash.c b/src/t_hash.c
index e6ed33819..b9f0db7fc 100644
--- a/src/t_hash.c
+++ b/src/t_hash.c
@@ -621,7 +621,7 @@ void hincrbyfloatCommand(client *c) {
}
char buf[MAX_LONG_DOUBLE_CHARS];
- int len = ld2string(buf,sizeof(buf),value,1);
+ int len = ld2string(buf,sizeof(buf),value,LD_STR_HUMAN);
new = sdsnewlen(buf,len);
hashTypeSet(o,c->argv[2]->ptr,new,HASH_SET_TAKE_VALUE);
addReplyBulkCBuffer(c,buf,len);
diff --git a/src/t_stream.c b/src/t_stream.c
index ea9a620f1..58b59f521 100644
--- a/src/t_stream.c
+++ b/src/t_stream.c
@@ -173,9 +173,19 @@ int streamCompareID(streamID *a, streamID *b) {
* C_ERR if an ID was given via 'use_id', but adding it failed since the
* current top ID is greater or equal. */
int streamAppendItem(stream *s, robj **argv, int64_t numfields, streamID *added_id, streamID *use_id) {
- /* If an ID was given, check that it's greater than the last entry ID
- * or return an error. */
- if (use_id && streamCompareID(use_id,&s->last_id) <= 0) return C_ERR;
+
+ /* Generate the new entry ID. */
+ streamID id;
+ if (use_id)
+ id = *use_id;
+ else
+ streamNextID(&s->last_id,&id);
+
+ /* Check that the new ID is greater than the last entry ID
+ * or return an error. Automatically generated IDs might
+ * overflow (and wrap-around) when incrementing the sequence
+ part. */
+ if (streamCompareID(&id,&s->last_id) <= 0) return C_ERR;
/* Add the new entry. */
raxIterator ri;
@@ -192,13 +202,6 @@ int streamAppendItem(stream *s, robj **argv, int64_t numfields, streamID *added_
}
raxStop(&ri);
- /* Generate the new entry ID. */
- streamID id;
- if (use_id)
- id = *use_id;
- else
- streamNextID(&s->last_id,&id);
-
/* We have to add the key into the radix tree in lexicographic order,
* to do so we consider the ID as a single 128 bit number written in
* big endian, so that the most significant bytes are the first ones. */
diff --git a/src/util.c b/src/util.c
index 783bcf83b..062a572d4 100644
--- a/src/util.c
+++ b/src/util.c
@@ -510,15 +510,17 @@ int d2string(char *buf, size_t len, double value) {
return len;
}
-/* Convert a long double into a string. If humanfriendly is non-zero
- * it does not use exponential format and trims trailing zeroes at the end,
- * however this results in loss of precision. Otherwise exp format is used
- * and the output of snprintf() is not modified.
+/* Create a string object from a long double.
+ * If mode is humanfriendly it does not use exponential format and trims trailing
+ * zeroes at the end (may result in loss of precision).
+ * If mode is default exp format is used and the output of snprintf()
+ * is not modified (may result in loss of precision).
+ * If mode is hex hexadecimal format is used (no loss of precision)
*
* The function returns the length of the string or zero if there was not
* enough buffer room to store it. */
-int ld2string(char *buf, size_t len, long double value, int humanfriendly) {
- size_t l;
+int ld2string(char *buf, size_t len, long double value, ld2string_mode mode) {
+ size_t l = 0;
if (isinf(value)) {
/* Libc in odd systems (Hi Solaris!) will format infinite in a
@@ -531,26 +533,36 @@ int ld2string(char *buf, size_t len, long double value, int humanfriendly) {
memcpy(buf,"-inf",4);
l = 4;
}
- } else if (humanfriendly) {
- /* We use 17 digits precision since with 128 bit floats that precision
- * after rounding 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.) */
- l = snprintf(buf,len,"%.17Lf", value);
- if (l+1 > len) return 0; /* No room. */
- /* Now remove trailing zeroes after the '.' */
- if (strchr(buf,'.') != NULL) {
- char *p = buf+l-1;
- while(*p == '0') {
- p--;
- l--;
+ } else {
+ switch (mode) {
+ case LD_STR_AUTO:
+ l = snprintf(buf,len,"%.17Lg",value);
+ if (l+1 > len) return 0; /* No room. */
+ break;
+ case LD_STR_HEX:
+ l = snprintf(buf,len,"%La",value);
+ if (l+1 > len) return 0; /* No room. */
+ break;
+ case LD_STR_HUMAN:
+ /* We use 17 digits precision since with 128 bit floats that precision
+ * after rounding 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.) */
+ l = snprintf(buf,len,"%.17Lf",value);
+ if (l+1 > len) return 0; /* No room. */
+ /* Now remove trailing zeroes after the '.' */
+ if (strchr(buf,'.') != NULL) {
+ char *p = buf+l-1;
+ while(*p == '0') {
+ p--;
+ l--;
+ }
+ if (*p == '.') l--;
}
- if (*p == '.') l--;
+ break;
+ default: return 0; /* Invalid mode. */
}
- } else {
- l = snprintf(buf,len,"%.17Lg", value);
- if (l+1 > len) return 0; /* No room. */
}
buf[l] = '\0';
return l;
diff --git a/src/util.h b/src/util.h
index b6c01aa59..a91addb80 100644
--- a/src/util.h
+++ b/src/util.h
@@ -38,6 +38,13 @@
* This should be the size of the buffer given to ld2string */
#define MAX_LONG_DOUBLE_CHARS 5*1024
+/* long double to string convertion options */
+typedef enum {
+ LD_STR_AUTO, /* %.17Lg */
+ LD_STR_HUMAN, /* %.17Lf + Trimming of trailing zeros */
+ LD_STR_HEX /* %La */
+} ld2string_mode;
+
int stringmatchlen(const char *p, int plen, const char *s, int slen, int nocase);
int stringmatch(const char *p, const char *s, int nocase);
int stringmatchlen_fuzz_test(void);
@@ -49,7 +56,7 @@ int string2ll(const char *s, size_t slen, long long *value);
int string2l(const char *s, size_t slen, long *value);
int string2ld(const char *s, size_t slen, long double *dp);
int d2string(char *buf, size_t len, double value);
-int ld2string(char *buf, size_t len, long double value, int humanfriendly);
+int ld2string(char *buf, size_t len, long double value, ld2string_mode mode);
sds getAbsolutePath(char *filename);
unsigned long getTimeZone(void);
int pathIsBaseName(char *path);