Merge pull request #6531 from oranagra/rm_save_long_double
Module API for loading and saving long double
This commit is contained in:
commit
bbdbfabf9e
27
src/module.c
27
src/module.c
@ -3769,6 +3769,31 @@ loaderr:
|
|||||||
return 0;
|
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
|
/* Iterate over modules, and trigger rdb aux saving for the ones modules types
|
||||||
* who asked for it. */
|
* who asked for it. */
|
||||||
ssize_t rdbSaveModulesAux(rio *rdb, int when) {
|
ssize_t rdbSaveModulesAux(rio *rdb, int when) {
|
||||||
@ -6849,6 +6874,8 @@ void moduleRegisterCoreAPI(void) {
|
|||||||
REGISTER_API(LoadDouble);
|
REGISTER_API(LoadDouble);
|
||||||
REGISTER_API(SaveFloat);
|
REGISTER_API(SaveFloat);
|
||||||
REGISTER_API(LoadFloat);
|
REGISTER_API(LoadFloat);
|
||||||
|
REGISTER_API(SaveLongDouble);
|
||||||
|
REGISTER_API(LoadLongDouble);
|
||||||
REGISTER_API(EmitAOF);
|
REGISTER_API(EmitAOF);
|
||||||
REGISTER_API(Log);
|
REGISTER_API(Log);
|
||||||
REGISTER_API(LogIOError);
|
REGISTER_API(LogIOError);
|
||||||
|
@ -530,7 +530,7 @@ void addReplyHumanLongDouble(client *c, long double d) {
|
|||||||
decrRefCount(o);
|
decrRefCount(o);
|
||||||
} else {
|
} else {
|
||||||
char buf[MAX_LONG_DOUBLE_CHARS];
|
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,",",1);
|
||||||
addReplyProto(c,buf,len);
|
addReplyProto(c,buf,len);
|
||||||
addReplyProto(c,"\r\n",2);
|
addReplyProto(c,"\r\n",2);
|
||||||
|
@ -178,7 +178,7 @@ robj *createStringObjectFromLongLongForValue(long long value) {
|
|||||||
* The 'humanfriendly' option is used for INCRBYFLOAT and HINCRBYFLOAT. */
|
* The 'humanfriendly' option is used for INCRBYFLOAT and HINCRBYFLOAT. */
|
||||||
robj *createStringObjectFromLongDouble(long double value, int humanfriendly) {
|
robj *createStringObjectFromLongDouble(long double value, int humanfriendly) {
|
||||||
char buf[MAX_LONG_DOUBLE_CHARS];
|
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);
|
return createStringObject(buf,len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -535,6 +535,8 @@ void REDISMODULE_API_FUNC(RedisModule_SaveDouble)(RedisModuleIO *io, double valu
|
|||||||
double REDISMODULE_API_FUNC(RedisModule_LoadDouble)(RedisModuleIO *io);
|
double REDISMODULE_API_FUNC(RedisModule_LoadDouble)(RedisModuleIO *io);
|
||||||
void REDISMODULE_API_FUNC(RedisModule_SaveFloat)(RedisModuleIO *io, float value);
|
void REDISMODULE_API_FUNC(RedisModule_SaveFloat)(RedisModuleIO *io, float value);
|
||||||
float REDISMODULE_API_FUNC(RedisModule_LoadFloat)(RedisModuleIO *io);
|
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_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_LogIOError)(RedisModuleIO *io, const char *levelstr, const char *fmt, ...);
|
||||||
void REDISMODULE_API_FUNC(RedisModule__Assert)(const char *estr, const char *file, int line);
|
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(LoadDouble);
|
||||||
REDISMODULE_GET_API(SaveFloat);
|
REDISMODULE_GET_API(SaveFloat);
|
||||||
REDISMODULE_GET_API(LoadFloat);
|
REDISMODULE_GET_API(LoadFloat);
|
||||||
|
REDISMODULE_GET_API(SaveLongDouble);
|
||||||
|
REDISMODULE_GET_API(LoadLongDouble);
|
||||||
REDISMODULE_GET_API(EmitAOF);
|
REDISMODULE_GET_API(EmitAOF);
|
||||||
REDISMODULE_GET_API(Log);
|
REDISMODULE_GET_API(Log);
|
||||||
REDISMODULE_GET_API(LogIOError);
|
REDISMODULE_GET_API(LogIOError);
|
||||||
|
@ -621,7 +621,7 @@ void hincrbyfloatCommand(client *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
char buf[MAX_LONG_DOUBLE_CHARS];
|
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);
|
new = sdsnewlen(buf,len);
|
||||||
hashTypeSet(o,c->argv[2]->ptr,new,HASH_SET_TAKE_VALUE);
|
hashTypeSet(o,c->argv[2]->ptr,new,HASH_SET_TAKE_VALUE);
|
||||||
addReplyBulkCBuffer(c,buf,len);
|
addReplyBulkCBuffer(c,buf,len);
|
||||||
|
62
src/util.c
62
src/util.c
@ -510,15 +510,17 @@ int d2string(char *buf, size_t len, double value) {
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Convert a long double into a string. If humanfriendly is non-zero
|
/* Create a string object from a long double.
|
||||||
* it does not use exponential format and trims trailing zeroes at the end,
|
* If mode is humanfriendly it does not use exponential format and trims trailing
|
||||||
* however this results in loss of precision. Otherwise exp format is used
|
* zeroes at the end (may result in loss of precision).
|
||||||
* and the output of snprintf() is not modified.
|
* 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
|
* The function returns the length of the string or zero if there was not
|
||||||
* enough buffer room to store it. */
|
* enough buffer room to store it. */
|
||||||
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) {
|
||||||
size_t l;
|
size_t l = 0;
|
||||||
|
|
||||||
if (isinf(value)) {
|
if (isinf(value)) {
|
||||||
/* Libc in odd systems (Hi Solaris!) will format infinite in a
|
/* 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);
|
memcpy(buf,"-inf",4);
|
||||||
l = 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--;
|
|
||||||
}
|
|
||||||
if (*p == '.') l--;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
l = snprintf(buf,len,"%.17Lg", value);
|
switch (mode) {
|
||||||
if (l+1 > len) return 0; /* No room. */
|
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--;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: return 0; /* Invalid mode. */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
buf[l] = '\0';
|
buf[l] = '\0';
|
||||||
return l;
|
return l;
|
||||||
|
@ -38,6 +38,13 @@
|
|||||||
* This should be the size of the buffer given to ld2string */
|
* This should be the size of the buffer given to ld2string */
|
||||||
#define MAX_LONG_DOUBLE_CHARS 5*1024
|
#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 stringmatchlen(const char *p, int plen, const char *s, int slen, int nocase);
|
||||||
int stringmatch(const char *p, const char *s, int nocase);
|
int stringmatch(const char *p, const char *s, int nocase);
|
||||||
int stringmatchlen_fuzz_test(void);
|
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 string2l(const char *s, size_t slen, long *value);
|
||||||
int string2ld(const char *s, size_t slen, long double *dp);
|
int string2ld(const char *s, size_t slen, long double *dp);
|
||||||
int d2string(char *buf, size_t len, double value);
|
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);
|
sds getAbsolutePath(char *filename);
|
||||||
unsigned long getTimeZone(void);
|
unsigned long getTimeZone(void);
|
||||||
int pathIsBaseName(char *path);
|
int pathIsBaseName(char *path);
|
||||||
|
@ -15,11 +15,16 @@ RedisModuleString *after_str = NULL;
|
|||||||
|
|
||||||
void *testrdb_type_load(RedisModuleIO *rdb, int encver) {
|
void *testrdb_type_load(RedisModuleIO *rdb, int encver) {
|
||||||
int count = RedisModule_LoadSigned(rdb);
|
int count = RedisModule_LoadSigned(rdb);
|
||||||
|
RedisModuleString *str = RedisModule_LoadString(rdb);
|
||||||
|
float f = RedisModule_LoadFloat(rdb);
|
||||||
|
long double ld = RedisModule_LoadLongDouble(rdb);
|
||||||
if (RedisModule_IsIOError(rdb))
|
if (RedisModule_IsIOError(rdb))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
/* Using the values only after checking for io errors. */
|
||||||
assert(count==1);
|
assert(count==1);
|
||||||
assert(encver==1);
|
assert(encver==1);
|
||||||
RedisModuleString *str = RedisModule_LoadString(rdb);
|
assert(f==1.5f);
|
||||||
|
assert(ld==0.333333333333333333L);
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,6 +32,8 @@ void testrdb_type_save(RedisModuleIO *rdb, void *value) {
|
|||||||
RedisModuleString *str = (RedisModuleString*)value;
|
RedisModuleString *str = (RedisModuleString*)value;
|
||||||
RedisModule_SaveSigned(rdb, 1);
|
RedisModule_SaveSigned(rdb, 1);
|
||||||
RedisModule_SaveString(rdb, str);
|
RedisModule_SaveString(rdb, str);
|
||||||
|
RedisModule_SaveFloat(rdb, 1.5);
|
||||||
|
RedisModule_SaveLongDouble(rdb, 0.333333333333333333L);
|
||||||
}
|
}
|
||||||
|
|
||||||
void testrdb_aux_save(RedisModuleIO *rdb, int when) {
|
void testrdb_aux_save(RedisModuleIO *rdb, int when) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user