Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support unsigned numeric config #1546

Open
wants to merge 3 commits into
base: unstable
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
19 changes: 15 additions & 4 deletions src/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -3294,10 +3294,11 @@ sds keyspaceEventsFlagsToString(int flags);
* to apply the configuration change even if the new config value is the same as \
* the old. */

#define INTEGER_CONFIG 0 /* No flags means a simple integer configuration */
#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 INTEGER_CONFIG 0 /* No flags means a simple integer configuration */
#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
Loading