mirror of
https://codeberg.org/redict/redict.git
synced 2025-01-22 16:18:28 -05:00
Module API for loading and saving long double
looks like each platform implements long double differently (different bit count) so we can't save them as binary, and we also want to avoid creating a new RDB format version, so we save these are hex strings using "%La". This commit includes a change in the arguments of ld2string to support this. as well as tests for coverage and short reads. coded by @guybe7
This commit is contained in:
parent
fdaea2a7a7
commit
779aebc91c
27
src/module.c
27
src/module.c
@ -3716,6 +3716,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) {
|
||||
@ -6669,6 +6694,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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -457,6 +457,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);
|
||||
@ -658,6 +660,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);
|
||||
|
@ -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);
|
||||
|
62
src/util.c
62
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--;
|
||||
}
|
||||
if (*p == '.') l--;
|
||||
}
|
||||
} else {
|
||||
l = snprintf(buf,len,"%.17Lg", value);
|
||||
if (l+1 > len) return 0; /* No room. */
|
||||
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--;
|
||||
}
|
||||
break;
|
||||
default: return 0; /* Invalid mode. */
|
||||
}
|
||||
}
|
||||
buf[l] = '\0';
|
||||
return l;
|
||||
|
@ -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);
|
||||
|
@ -15,11 +15,16 @@ RedisModuleString *after_str = NULL;
|
||||
|
||||
void *testrdb_type_load(RedisModuleIO *rdb, int encver) {
|
||||
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))
|
||||
return NULL;
|
||||
/* Using the values only after checking for io errors. */
|
||||
assert(count==1);
|
||||
assert(encver==1);
|
||||
RedisModuleString *str = RedisModule_LoadString(rdb);
|
||||
assert(f==1.5f);
|
||||
assert(ld==0.333333333333333333L);
|
||||
return str;
|
||||
}
|
||||
|
||||
@ -27,6 +32,8 @@ void testrdb_type_save(RedisModuleIO *rdb, void *value) {
|
||||
RedisModuleString *str = (RedisModuleString*)value;
|
||||
RedisModule_SaveSigned(rdb, 1);
|
||||
RedisModule_SaveString(rdb, str);
|
||||
RedisModule_SaveFloat(rdb, 1.5);
|
||||
RedisModule_SaveLongDouble(rdb, 0.333333333333333333L);
|
||||
}
|
||||
|
||||
void testrdb_aux_save(RedisModuleIO *rdb, int when) {
|
||||
|
Loading…
Reference in New Issue
Block a user