Skip to content

Commit

Permalink
Support unsigned numeric config
Browse files Browse the repository at this point in the history
Signed-off-by: artikell <[email protected]>
  • Loading branch information
artikell committed Jan 12, 2025
1 parent e60990e commit 2c0d54e
Show file tree
Hide file tree
Showing 11 changed files with 184 additions and 13 deletions.
56 changes: 53 additions & 3 deletions src/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -1357,6 +1357,17 @@ void rewriteConfigOctalOption(struct rewriteConfigState *state,
rewriteConfigRewriteLine(state, option, line, force);
}

/* Rewrite an unsigned number option. */
void rewriteConfigUnsignedOption(struct rewriteConfigState *state,
const char *option,
unsigned long long value,
unsigned long long defvalue) {
int force = value != defvalue;
sds line = sdscatprintf(sdsempty(), "%s %llu", option, value);

rewriteConfigRewriteLine(state, option, line, force);
}

/* Rewrite an enumeration option. It takes as usually state and option name,
* and in addition the enumeration array and the default value for the
* option. */
Expand Down Expand Up @@ -2026,7 +2037,10 @@ int setNumericType(standardConfig *config, long long val, const char **err) {
else
*(config->data.numeric.config.ll) = (long long)val;
} else if (config->data.numeric.numeric_type == NUMERIC_TYPE_ULONG_LONG) {
*(config->data.numeric.config.ull) = (unsigned long long)val;
if (config->flags & MODULE_CONFIG)
return setModuleUnsignedNumericConfig(config->privdata, (unsigned long long)val, err);
else
*(config->data.numeric.config.ull) = (unsigned long long)val;
} else if (config->data.numeric.numeric_type == NUMERIC_TYPE_SIZE_T) {
*(config->data.numeric.config.st) = (size_t)val;
} else if (config->data.numeric.numeric_type == NUMERIC_TYPE_SSIZE_T) {
Expand Down Expand Up @@ -2056,7 +2070,10 @@ int setNumericType(standardConfig *config, long long val, const char **err) {
else \
val = *(config->data.numeric.config.ll); \
} else if (config->data.numeric.numeric_type == NUMERIC_TYPE_ULONG_LONG) { \
val = *(config->data.numeric.config.ull); \
if (config->flags & MODULE_CONFIG) \
val = getModuleUnsignedNumericConfig(config->privdata); \
else \
val = *(config->data.numeric.config.ull); \
} else if (config->data.numeric.numeric_type == NUMERIC_TYPE_SIZE_T) { \
val = *(config->data.numeric.config.st); \
} else if (config->data.numeric.numeric_type == NUMERIC_TYPE_SSIZE_T) { \
Expand Down Expand Up @@ -2133,10 +2150,20 @@ static int numericParseString(standardConfig *config, sds value, const char **er
if (config->data.numeric.flags & OCTAL_CONFIG) {
char *endptr;
errno = 0;
*res = strtoll(value, &endptr, 8);
*res = strtoull(value, &endptr, 8);
if (errno == 0 && *endptr == '\0') return 1; /* No overflow or invalid characters */
}

/* Attempt to parse as an unsigned number */
if (config->data.numeric.flags & UNSIGNED_CONFIG) {
unsigned long long ull;
int ok = string2ull(value, sdslen(value), &ull);
if (ok) {
*res = (long long)ull;
return 1; /* No overflow or invalid characters */
}
}

/* Attempt a simple number (no special flags set) */
if (!config->data.numeric.flags && string2ll(value, sdslen(value), res)) return 1;

Expand All @@ -2147,6 +2174,8 @@ static int numericParseString(standardConfig *config, sds value, const char **er
*err = "argument must be a memory value";
else if (config->data.numeric.flags & OCTAL_CONFIG)
*err = "argument couldn't be parsed as an octal number";
else if (config->data.numeric.flags & UNSIGNED_CONFIG)
*err = "argument couldn't be parsed as an unsigned number";
else
*err = "argument couldn't be parsed into an integer";
return 0;
Expand Down Expand Up @@ -2184,6 +2213,8 @@ static sds numericConfigGet(standardConfig *config) {
ull2string(buf, sizeof(buf), value);
} else if (config->data.numeric.flags & OCTAL_CONFIG) {
snprintf(buf, sizeof(buf), "%llo", value);
} else if (config->data.numeric.flags & UNSIGNED_CONFIG) {
ull2string(buf, sizeof(buf), (unsigned long long)value);
} else {
ll2string(buf, sizeof(buf), value);
}
Expand All @@ -2201,6 +2232,8 @@ static void numericConfigRewrite(standardConfig *config, const char *name, struc
rewriteConfigBytesOption(state, name, value, config->data.numeric.default_value);
} else if (config->data.numeric.flags & OCTAL_CONFIG) {
rewriteConfigOctalOption(state, name, value, config->data.numeric.default_value);
} else if (config->data.numeric.flags & UNSIGNED_CONFIG) {
rewriteConfigUnsignedOption(state, name, value, config->data.numeric.default_value);
} else {
rewriteConfigNumericalOption(state, name, value, config->data.numeric.default_value);
}
Expand Down Expand Up @@ -3488,6 +3521,23 @@ void addModuleNumericConfig(const char *module_name,
registerConfigValue(config_name, &module_config, 0);
}

void addModuleUnsignedNumericConfig(const char *module_name,
const char *name,
int flags,
void *privdata,
unsigned long long default_val,
int conf_flags,
unsigned long long lower,
unsigned long long upper) {
sds config_name = sdscatfmt(sdsempty(), "%s.%s", module_name, name);
unsigned long long config_dummy_address;
standardConfig module_config = createULongLongConfig(config_name, NULL, flags | MODULE_CONFIG, lower, upper,
config_dummy_address, default_val, conf_flags, NULL, NULL);
module_config.data.numeric.config.ull = NULL;
module_config.privdata = privdata;
registerConfigValue(config_name, &module_config, 0);
}

/*-----------------------------------------------------------------------------
* CONFIG HELP
*----------------------------------------------------------------------------*/
Expand Down
2 changes: 1 addition & 1 deletion src/db.c
Original file line number Diff line number Diff line change
Expand Up @@ -1066,7 +1066,7 @@ void hashtableScanCallback(void *privdata, void *entry) {
* returns C_OK. Otherwise return C_ERR and send an error to the
* client. */
int parseScanCursorOrReply(client *c, robj *o, unsigned long long *cursor) {
if (!string2ull(o->ptr, cursor)) {
if (!string2ull(o->ptr, sdslen(o->ptr), cursor)) {
addReplyError(c, "invalid cursor");
return C_ERR;
}
Expand Down
56 changes: 52 additions & 4 deletions src/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ typedef struct ValkeyModuleKeyOptCtx {
/* The function signatures for module config get callbacks. These are identical to the ones exposed in valkeymodule.h. */
typedef ValkeyModuleString *(*ValkeyModuleConfigGetStringFunc)(const char *name, void *privdata);
typedef long long (*ValkeyModuleConfigGetNumericFunc)(const char *name, void *privdata);
typedef unsigned long long (*ValkeyModuleConfigGetUnsignedNumericFunc)(const char *name, void *privdata);
typedef int (*ValkeyModuleConfigGetBoolFunc)(const char *name, void *privdata);
typedef int (*ValkeyModuleConfigGetEnumFunc)(const char *name, void *privdata);
/* The function signatures for module config set callbacks. These are identical to the ones exposed in valkeymodule.h. */
Expand All @@ -463,6 +464,10 @@ typedef int (*ValkeyModuleConfigSetNumericFunc)(const char *name,
long long val,
void *privdata,
ValkeyModuleString **err);
typedef int (*ValkeyModuleConfigSetUnsignedNumericFunc)(const char *name,
unsigned long long val,
void *privdata,
ValkeyModuleString **err);
typedef int (*ValkeyModuleConfigSetBoolFunc)(const char *name, int val, void *privdata, ValkeyModuleString **err);
typedef int (*ValkeyModuleConfigSetEnumFunc)(const char *name, int val, void *privdata, ValkeyModuleString **err);
/* Apply signature, identical to valkeymodule.h */
Expand All @@ -475,12 +480,14 @@ struct ModuleConfig {
union get_fn { /* The get callback specified by the module */
ValkeyModuleConfigGetStringFunc get_string;
ValkeyModuleConfigGetNumericFunc get_numeric;
ValkeyModuleConfigGetUnsignedNumericFunc get_unsigned_numeric;
ValkeyModuleConfigGetBoolFunc get_bool;
ValkeyModuleConfigGetEnumFunc get_enum;
} get_fn;
union set_fn { /* The set callback specified by the module */
ValkeyModuleConfigSetStringFunc set_string;
ValkeyModuleConfigSetNumericFunc set_numeric;
ValkeyModuleConfigSetUnsignedNumericFunc set_unsigned_numeric;
ValkeyModuleConfigSetBoolFunc set_bool;
ValkeyModuleConfigSetEnumFunc set_enum;
} set_fn;
Expand Down Expand Up @@ -2965,7 +2972,7 @@ int VM_StringToLongLong(const ValkeyModuleString *str, long long *ll) {
* as a valid, strict `unsigned long long` (no spaces before/after), VALKEYMODULE_ERR
* is returned. */
int VM_StringToULongLong(const ValkeyModuleString *str, unsigned long long *ull) {
return string2ull(str->ptr, ull) ? VALKEYMODULE_OK : VALKEYMODULE_ERR;
return string2ull(str->ptr, sdslen(str->ptr), ull) ? VALKEYMODULE_OK : VALKEYMODULE_ERR;
}

/* Convert the string into a double, storing it at `*d`.
Expand Down Expand Up @@ -10526,7 +10533,7 @@ unsigned long long VM_ServerInfoGetFieldUnsigned(ValkeyModuleServerInfoData *dat
return 0;
}
sds val = result;
if (!string2ull(val, &ll)) {
if (!string2ull(val, sdslen(val), &ll)) {
if (out_err) *out_err = VALKEYMODULE_ERR;
return 0;
}
Expand Down Expand Up @@ -12570,11 +12577,11 @@ int isModuleConfigNameRegistered(ValkeyModule *module, const char *name) {
int moduleVerifyConfigFlags(unsigned int flags, configType type) {
if ((flags & ~(VALKEYMODULE_CONFIG_DEFAULT | VALKEYMODULE_CONFIG_IMMUTABLE | VALKEYMODULE_CONFIG_SENSITIVE |
VALKEYMODULE_CONFIG_HIDDEN | VALKEYMODULE_CONFIG_PROTECTED | VALKEYMODULE_CONFIG_DENY_LOADING |
VALKEYMODULE_CONFIG_BITFLAGS | VALKEYMODULE_CONFIG_MEMORY))) {
VALKEYMODULE_CONFIG_BITFLAGS | VALKEYMODULE_CONFIG_MEMORY | VALKEYMODULE_CONFIG_UNSIGNED))) {
serverLogRaw(LL_WARNING, "Invalid flag(s) for configuration");
return VALKEYMODULE_ERR;
}
if (type != NUMERIC_CONFIG && flags & VALKEYMODULE_CONFIG_MEMORY) {
if (type != NUMERIC_CONFIG && (flags & (VALKEYMODULE_CONFIG_MEMORY | VALKEYMODULE_CONFIG_UNSIGNED))) {
serverLogRaw(LL_WARNING, "Numeric flag provided for non-numeric configuration.");
return VALKEYMODULE_ERR;
}
Expand Down Expand Up @@ -12646,6 +12653,13 @@ int setModuleNumericConfig(ModuleConfig *config, long long val, const char **err
return return_code == VALKEYMODULE_OK ? 1 : 0;
}

int setModuleUnsignedNumericConfig(ModuleConfig *config, unsigned long long val, const char **err) {
ValkeyModuleString *error = NULL;
int return_code = config->set_fn.set_unsigned_numeric(config->name, val, config->privdata, &error);
propagateErrorString(error, err);
return return_code == VALKEYMODULE_OK ? 1 : 0;
}

/* This is a series of get functions for each type that act as dispatchers for
* config.c to call module set callbacks. */
int getModuleBoolConfig(ModuleConfig *module_config) {
Expand All @@ -12665,6 +12679,10 @@ long long getModuleNumericConfig(ModuleConfig *module_config) {
return module_config->get_fn.get_numeric(module_config->name, module_config->privdata);
}

unsigned long long getModuleUnsignedNumericConfig(ModuleConfig *module_config) {
return module_config->get_fn.get_unsigned_numeric(module_config->name, module_config->privdata);
}

/* This function takes a module and a list of configs stored as sds NAME VALUE pairs.
* It attempts to call set on each of these configs. */
int loadModuleConfigs(ValkeyModule *module) {
Expand Down Expand Up @@ -12785,6 +12803,7 @@ unsigned int maskModuleConfigFlags(unsigned int flags) {
unsigned int maskModuleNumericConfigFlags(unsigned int flags) {
unsigned int new_flags = 0;
if (flags & VALKEYMODULE_CONFIG_MEMORY) new_flags |= MEMORY_CONFIG;
if (flags & VALKEYMODULE_CONFIG_UNSIGNED) new_flags |= UNSIGNED_CONFIG;
return new_flags;
}

Expand Down Expand Up @@ -13009,6 +13028,34 @@ int VM_RegisterNumericConfig(ValkeyModuleCtx *ctx,
return VALKEYMODULE_OK;
}

/*
* Create an unsigned integer config that server clients can interact with via the
* `CONFIG SET`, `CONFIG GET`, and `CONFIG REWRITE` commands. See
* ValkeyModule_RegisterStringConfig for detailed information about configs. */
int VM_RegisterUnsignedNumericConfig(ValkeyModuleCtx *ctx,
const char *name,
unsigned long long default_val,
unsigned int flags,
unsigned long long min,
unsigned long long max,
ValkeyModuleConfigGetUnsignedNumericFunc getfn,
ValkeyModuleConfigSetUnsignedNumericFunc setfn,
ValkeyModuleConfigApplyFunc applyfn,
void *privdata) {
ValkeyModule *module = ctx->module;
if (moduleConfigValidityCheck(module, name, flags, NUMERIC_CONFIG)) {
return VALKEYMODULE_ERR;
}
ModuleConfig *new_config = createModuleConfig(name, applyfn, privdata, module);
new_config->get_fn.get_unsigned_numeric = getfn;
new_config->set_fn.set_unsigned_numeric = setfn;
listAddNodeTail(module->module_configs, new_config);
unsigned int numeric_flags = maskModuleNumericConfigFlags(flags);
flags = maskModuleConfigFlags(flags);
addModuleUnsignedNumericConfig(module->name, name, flags, new_config, default_val, numeric_flags, min, max);
return VALKEYMODULE_OK;
}

/* Applies all pending configurations on the module load. This should be called
* after all of the configurations have been registered for the module inside of ValkeyModule_OnLoad.
* This will return VALKEYMODULE_ERR if it is called outside ValkeyModule_OnLoad.
Expand Down Expand Up @@ -14073,6 +14120,7 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API(Yield);
REGISTER_API(RegisterBoolConfig);
REGISTER_API(RegisterNumericConfig);
REGISTER_API(RegisterUnsignedNumericConfig);
REGISTER_API(RegisterStringConfig);
REGISTER_API(RegisterEnumConfig);
REGISTER_API(LoadConfigs);
Expand Down
1 change: 1 addition & 0 deletions src/redismodule.h
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,7 @@
#define RedisModule_EventLoopAddOneShot ValkeyModule_EventLoopAddOneShot
#define RedisModule_RegisterBoolConfig ValkeyModule_RegisterBoolConfig
#define RedisModule_RegisterNumericConfig ValkeyModule_RegisterNumericConfig
#define RedisModule_RegisterUnsignedNumericConfig ValkeyModule_RegisterUnsignedNumericConfig
#define RedisModule_RegisterStringConfig ValkeyModule_RegisterStringConfig
#define RedisModule_RegisterEnumConfig ValkeyModule_RegisterEnumConfig
#define RedisModule_LoadConfigs ValkeyModule_LoadConfigs
Expand Down
11 changes: 11 additions & 0 deletions src/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -3298,6 +3298,7 @@ sds keyspaceEventsFlagsToString(int flags);
#define MEMORY_CONFIG (1 << 0) /* Indicates if this value can be loaded as a memory value */
#define PERCENT_CONFIG (1 << 1) /* Indicates if this value can be loaded as a percent (and stored as a negative int) */
#define OCTAL_CONFIG (1 << 2) /* This value uses octal representation */
#define UNSIGNED_CONFIG (1 << 3) /* This value uses unsigned representation */

/* Enum Configs contain an array of configEnum objects that match a string with an integer. */
typedef struct configEnum {
Expand Down Expand Up @@ -3350,6 +3351,14 @@ void addModuleNumericConfig(const char *module_name,
int conf_flags,
long long lower,
long long upper);
void addModuleUnsignedNumericConfig(const char *module_name,
const char *name,
int flags,
void *privdata,
unsigned long long default_val,
int conf_flags,
unsigned long long lower,
unsigned long long upper);
void addModuleConfigApply(list *module_configs, ModuleConfig *module_config);
int moduleConfigApplyConfig(list *module_configs, const char **err, const char **err_arg_name);
int getModuleBoolConfig(ModuleConfig *module_config);
Expand All @@ -3360,6 +3369,8 @@ int getModuleEnumConfig(ModuleConfig *module_config);
int setModuleEnumConfig(ModuleConfig *config, int val, const char **err);
long long getModuleNumericConfig(ModuleConfig *module_config);
int setModuleNumericConfig(ModuleConfig *config, long long val, const char **err);
unsigned long long getModuleUnsignedNumericConfig(ModuleConfig *module_config);
int setModuleUnsignedNumericConfig(ModuleConfig *config, unsigned long long val, const char **err);

/* db.c -- Keyspace access API */
int removeExpire(serverDb *db, robj *key);
Expand Down
4 changes: 2 additions & 2 deletions src/t_stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -1893,14 +1893,14 @@ int streamGenericParseIDOrReply(client *c,
unsigned long long ms, seq;
char *dot = strchr(buf, '-');
if (dot) *dot = '\0';
if (string2ull(buf, &ms) == 0) goto invalid;
if (string2ull(buf, strlen(buf), &ms) == 0) goto invalid;
if (dot) {
size_t seqlen = strlen(dot + 1);
if (seq_given != NULL && seqlen == 1 && *(dot + 1) == '*') {
/* Handle the <ms>-* form. */
seq = 0;
*seq_given = 0;
} else if (string2ull(dot + 1, &seq) == 0) {
} else if (string2ull(dot + 1, seqlen, &seq) == 0) {
goto invalid;
}
} else {
Expand Down
4 changes: 2 additions & 2 deletions src/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -497,9 +497,9 @@ int string2ll(const char *s, size_t slen, long long *value) {
* Valkey: if it fails, strtoull() is used instead. The function returns
* 1 if the conversion happened successfully or 0 if the number is
* invalid or out of range. */
int string2ull(const char *s, unsigned long long *value) {
int string2ull(const char *s, size_t slen, unsigned long long *value) {
long long ll;
if (string2ll(s, strlen(s), &ll)) {
if (string2ll(s, slen, &ll)) {
if (ll < 0) return 0; /* Negative values are out of range. */
*value = ll;
return 1;
Expand Down
2 changes: 1 addition & 1 deletion src/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ uint32_t sdigits10(int64_t v);
int ll2string(char *s, size_t len, long long value);
int ull2string(char *s, size_t len, unsigned long long value);
int string2ll(const char *s, size_t slen, long long *value);
int string2ull(const char *s, unsigned long long *value);
int string2ull(const char *s, size_t slen, unsigned long long *value);
int string2l(const char *s, size_t slen, long *value);
int string2ul_base16_async_signal_safe(const char *src, size_t slen, unsigned long *result_output);
int string2ld(const char *s, size_t slen, long double *dp);
Expand Down
Loading

0 comments on commit 2c0d54e

Please sign in to comment.