diff --git a/src/module.c b/src/module.c index f81b2c042..fd2de983d 100644 --- a/src/module.c +++ b/src/module.c @@ -2311,6 +2311,20 @@ RedisModuleString *RM_CreateStringFromLongLong(RedisModuleCtx *ctx, long long ll return RM_CreateString(ctx,buf,len); } +/* Like RedisModule_CreateString(), but creates a string starting from a `unsigned long long` + * integer instead of taking a buffer and its length. + * + * The returned string must be released with RedisModule_FreeString() or by + * enabling automatic memory management. + * + * The passed context 'ctx' may be NULL if necessary, see the + * RedisModule_CreateString() documentation for more info. */ +RedisModuleString *RM_CreateStringFromULongLong(RedisModuleCtx *ctx, unsigned long long ull) { + char buf[LONG_STR_SIZE]; + size_t len = ull2string(buf,sizeof(buf),ull); + return RM_CreateString(ctx,buf,len); +} + /* Like RedisModule_CreateString(), but creates a string starting from a double * instead of taking a buffer and its length. * @@ -2519,6 +2533,14 @@ int RM_StringToLongLong(const RedisModuleString *str, long long *ll) { REDISMODULE_ERR; } +/* Convert the string into a `unsigned long long` integer, storing it at `*ull`. + * Returns REDISMODULE_OK on success. If the string can't be parsed + * as a valid, strict `unsigned long long` (no spaces before/after), REDISMODULE_ERR + * is returned. */ +int RM_StringToULongLong(const RedisModuleString *str, unsigned long long *ull) { + return string2ull(str->ptr,ull) ? REDISMODULE_OK : REDISMODULE_ERR; +} + /* Convert the string into a double, storing it at `*d`. * Returns REDISMODULE_OK on success or REDISMODULE_ERR if the string is * not a valid string representation of a double value. */ @@ -12421,6 +12443,7 @@ void moduleRegisterCoreAPI(void) { REGISTER_API(ListInsert); REGISTER_API(ListDelete); REGISTER_API(StringToLongLong); + REGISTER_API(StringToULongLong); REGISTER_API(StringToDouble); REGISTER_API(StringToLongDouble); REGISTER_API(StringToStreamID); @@ -12443,6 +12466,7 @@ void moduleRegisterCoreAPI(void) { REGISTER_API(CreateStringFromCallReply); REGISTER_API(CreateString); REGISTER_API(CreateStringFromLongLong); + REGISTER_API(CreateStringFromULongLong); REGISTER_API(CreateStringFromDouble); REGISTER_API(CreateStringFromLongDouble); REGISTER_API(CreateStringFromString); diff --git a/src/redismodule.h b/src/redismodule.h index e36d5f3c0..a55c2e712 100644 --- a/src/redismodule.h +++ b/src/redismodule.h @@ -915,6 +915,7 @@ REDISMODULE_API size_t (*RedisModule_CallReplyLength)(RedisModuleCallReply *repl REDISMODULE_API RedisModuleCallReply * (*RedisModule_CallReplyArrayElement)(RedisModuleCallReply *reply, size_t idx) REDISMODULE_ATTR; REDISMODULE_API RedisModuleString * (*RedisModule_CreateString)(RedisModuleCtx *ctx, const char *ptr, size_t len) REDISMODULE_ATTR; REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromLongLong)(RedisModuleCtx *ctx, long long ll) REDISMODULE_ATTR; +REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromULongLong)(RedisModuleCtx *ctx, unsigned long long ull) REDISMODULE_ATTR; REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromDouble)(RedisModuleCtx *ctx, double d) REDISMODULE_ATTR; REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromLongDouble)(RedisModuleCtx *ctx, long double ld, int humanfriendly) REDISMODULE_ATTR; REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromString)(RedisModuleCtx *ctx, const RedisModuleString *str) REDISMODULE_ATTR; @@ -948,6 +949,7 @@ REDISMODULE_API int (*RedisModule_ReplyWithDouble)(RedisModuleCtx *ctx, double d REDISMODULE_API int (*RedisModule_ReplyWithBigNumber)(RedisModuleCtx *ctx, const char *bignum, size_t len) REDISMODULE_ATTR; REDISMODULE_API int (*RedisModule_ReplyWithCallReply)(RedisModuleCtx *ctx, RedisModuleCallReply *reply) REDISMODULE_ATTR; REDISMODULE_API int (*RedisModule_StringToLongLong)(const RedisModuleString *str, long long *ll) REDISMODULE_ATTR; +REDISMODULE_API int (*RedisModule_StringToULongLong)(const RedisModuleString *str, unsigned long long *ull) REDISMODULE_ATTR; REDISMODULE_API int (*RedisModule_StringToDouble)(const RedisModuleString *str, double *d) REDISMODULE_ATTR; REDISMODULE_API int (*RedisModule_StringToLongDouble)(const RedisModuleString *str, long double *d) REDISMODULE_ATTR; REDISMODULE_API int (*RedisModule_StringToStreamID)(const RedisModuleString *str, RedisModuleStreamID *id) REDISMODULE_ATTR; @@ -1260,6 +1262,7 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int REDISMODULE_GET_API(ListInsert); REDISMODULE_GET_API(ListDelete); REDISMODULE_GET_API(StringToLongLong); + REDISMODULE_GET_API(StringToULongLong); REDISMODULE_GET_API(StringToDouble); REDISMODULE_GET_API(StringToLongDouble); REDISMODULE_GET_API(StringToStreamID); @@ -1282,6 +1285,7 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int REDISMODULE_GET_API(CreateStringFromCallReply); REDISMODULE_GET_API(CreateString); REDISMODULE_GET_API(CreateStringFromLongLong); + REDISMODULE_GET_API(CreateStringFromULongLong); REDISMODULE_GET_API(CreateStringFromDouble); REDISMODULE_GET_API(CreateStringFromLongDouble); REDISMODULE_GET_API(CreateStringFromString); diff --git a/tests/modules/misc.c b/tests/modules/misc.c index dce78ca2b..c9ebcc5f9 100644 --- a/tests/modules/misc.c +++ b/tests/modules/misc.c @@ -4,6 +4,7 @@ #include #include #include +#include #define UNUSED(x) (void)(x) @@ -375,6 +376,68 @@ int test_rm_call_flags(RedisModuleCtx *ctx, RedisModuleString **argv, int argc){ return REDISMODULE_OK; } +int test_ull_conv(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { + UNUSED(argv); + UNUSED(argc); + unsigned long long ull = 18446744073709551615ULL; + const char *ullstr = "18446744073709551615"; + + RedisModuleString *s1 = RedisModule_CreateStringFromULongLong(ctx, ull); + RedisModuleString *s2 = + RedisModule_CreateString(ctx, ullstr, strlen(ullstr)); + if (RedisModule_StringCompare(s1, s2) != 0) { + char err[4096]; + snprintf(err, 4096, + "Failed to convert unsigned long long to string ('%s' != '%s')", + RedisModule_StringPtrLen(s1, NULL), + RedisModule_StringPtrLen(s2, NULL)); + RedisModule_ReplyWithError(ctx, err); + goto final; + } + unsigned long long ull2 = 0; + if (RedisModule_StringToULongLong(s2, &ull2) == REDISMODULE_ERR) { + RedisModule_ReplyWithError(ctx, + "Failed to convert string to unsigned long long"); + goto final; + } + if (ull2 != ull) { + char err[4096]; + snprintf(err, 4096, + "Failed to convert string to unsigned long long (%llu != %llu)", + ull2, + ull); + RedisModule_ReplyWithError(ctx, err); + goto final; + } + + /* Make sure we can't convert a string more than ULLONG_MAX or less than 0 */ + ullstr = "18446744073709551616"; + RedisModuleString *s3 = RedisModule_CreateString(ctx, ullstr, strlen(ullstr)); + unsigned long long ull3; + if (RedisModule_StringToULongLong(s3, &ull3) == REDISMODULE_OK) { + RedisModule_ReplyWithError(ctx, "Invalid string successfully converted to unsigned long long"); + RedisModule_FreeString(ctx, s3); + goto final; + } + RedisModule_FreeString(ctx, s3); + ullstr = "-1"; + RedisModuleString *s4 = RedisModule_CreateString(ctx, ullstr, strlen(ullstr)); + unsigned long long ull4; + if (RedisModule_StringToULongLong(s4, &ull4) == REDISMODULE_OK) { + RedisModule_ReplyWithError(ctx, "Invalid string successfully converted to unsigned long long"); + RedisModule_FreeString(ctx, s4); + goto final; + } + RedisModule_FreeString(ctx, s4); + + RedisModule_ReplyWithSimpleString(ctx, "ok"); + +final: + RedisModule_FreeString(ctx, s1); + RedisModule_FreeString(ctx, s2); + return REDISMODULE_OK; +} + int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { REDISMODULE_NOT_USED(argv); REDISMODULE_NOT_USED(argc); @@ -387,6 +450,8 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) return REDISMODULE_ERR; if (RedisModule_CreateCommand(ctx,"test.ld_conversion", test_ld_conv, "",0,0,0) == REDISMODULE_ERR) return REDISMODULE_ERR; + if (RedisModule_CreateCommand(ctx,"test.ull_conversion", test_ull_conv, "",0,0,0) == REDISMODULE_ERR) + return REDISMODULE_ERR; if (RedisModule_CreateCommand(ctx,"test.flushall", test_flushall,"",0,0,0) == REDISMODULE_ERR) return REDISMODULE_ERR; if (RedisModule_CreateCommand(ctx,"test.dbsize", test_dbsize,"",0,0,0) == REDISMODULE_ERR) diff --git a/tests/unit/moduleapi/misc.tcl b/tests/unit/moduleapi/misc.tcl index 3fca1cf7f..e9efdc2b3 100644 --- a/tests/unit/moduleapi/misc.tcl +++ b/tests/unit/moduleapi/misc.tcl @@ -29,6 +29,11 @@ start_server {tags {"modules"}} { set ld [r test.ld_conversion] assert {[string match $ld "0.00000000000000001"]} } + + test {test unsigned long long conversions} { + set ret [r test.ull_conversion] + assert {[string match $ret "ok"]} + } test {test module db commands} { r set x foo