From 42df4c3503e2bb6f23f7f7f6a20bb358bc3ac419 Mon Sep 17 00:00:00 2001 From: Dvir Volk Date: Mon, 3 Apr 2017 13:42:43 +0300 Subject: [PATCH] improvements to util functions --- rmutil/util.c | 309 ++++++++++++++++++++++++++------------------------ rmutil/util.h | 75 ++++++------ 2 files changed, 200 insertions(+), 184 deletions(-) diff --git a/rmutil/util.c b/rmutil/util.c index 819dd83..c810a2d 100644 --- a/rmutil/util.c +++ b/rmutil/util.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "util.h" /** @@ -14,188 +15,200 @@ Check if an argument exists in an argument list (argv,argc), starting at offset. */ int RMUtil_ArgExists(const char *arg, RedisModuleString **argv, int argc, int offset) { - for (; offset < argc; offset++) { - size_t l; - const char *carg = RedisModule_StringPtrLen(argv[offset], &l); - if (carg != NULL && strcasecmp(carg, arg) == 0) { - return offset; - } + size_t larg = strlen(arg); + for (; offset < argc; offset++) { + size_t l; + const char *carg = RedisModule_StringPtrLen(argv[offset], &l); + if (l != larg) continue; + if (carg != NULL && strncasecmp(carg, arg, larg) == 0) { + return offset; } - return 0; + } + return 0; +} + +/** +Check if an argument exists in an argument list (argv,argc) +@return -1 if it doesn't exist, otherwise the offset it exists in +*/ +int RMUtil_ArgIndex(const char *arg, RedisModuleString **argv, int argc) { + + size_t larg = strlen(arg); + for (int offset = 0; offset < argc; offset++) { + size_t l; + const char *carg = RedisModule_StringPtrLen(argv[offset], &l); + if (l != larg) continue; + if (carg != NULL && strncasecmp(carg, arg, larg) == 0) { + return offset; + } + } + return -1; } RMUtilInfo *RMUtil_GetRedisInfo(RedisModuleCtx *ctx) { - - RedisModuleCallReply *r = RedisModule_Call(ctx, "INFO", "c", "all"); - if (r == NULL || RedisModule_CallReplyType(r) == REDISMODULE_REPLY_ERROR) { - return NULL; + + RedisModuleCallReply *r = RedisModule_Call(ctx, "INFO", "c", "all"); + if (r == NULL || RedisModule_CallReplyType(r) == REDISMODULE_REPLY_ERROR) { + return NULL; + } + + int cap = 100; // rough estimate of info lines + RMUtilInfo *info = malloc(sizeof(RMUtilInfo)); + info->entries = calloc(cap, sizeof(RMUtilInfoEntry)); + + int i = 0; + char *text = (char *)RedisModule_StringPtrLen(RedisModule_CreateStringFromCallReply(r), NULL); + char *line = text; + while (line) { + char *line = strsep(&text, "\r\n"); + if (line == NULL) break; + + if (!(*line >= 'a' && *line <= 'z')) { // skip non entry lines + continue; } - - - int cap = 100; // rough estimate of info lines - RMUtilInfo *info = malloc(sizeof(RMUtilInfo)); - info->entries = calloc(cap, sizeof(RMUtilInfoEntry)); - - - int i = 0; - char *text = (char *)RedisModule_StringPtrLen(RedisModule_CreateStringFromCallReply(r), NULL); - char *line = text; - while (line) { - char *line = strsep(&text, "\r\n"); - if (line == NULL) break; - - if (!(*line >= 'a' && *line <= 'z')) { //skip non entry lines - continue; - } - - char *key = strsep(&line, ":"); - info->entries[i].key = key; - info->entries[i].val = line; - printf("Got info '%s' = '%s'\n", key, line); - i++; - if (i >= cap) { - cap *=2; - info->entries = realloc(info->entries, cap*sizeof(RMUtilInfoEntry)); - } - } - info->numEntries = i; - - return info; - + + char *key = strsep(&line, ":"); + info->entries[i].key = key; + info->entries[i].val = line; + //printf("Got info '%s' = '%s'\n", key, line); + i++; + if (i >= cap) { + cap *= 2; + info->entries = realloc(info->entries, cap * sizeof(RMUtilInfoEntry)); + } + } + info->numEntries = i; + + return info; } void RMUtilRedisInfo_Free(RMUtilInfo *info) { - - free(info->entries); - free(info); - + + free(info->entries); + free(info); } int RMUtilInfo_GetInt(RMUtilInfo *info, const char *key, long long *val) { - - const char *p = NULL; - if (!RMUtilInfo_GetString(info, key, &p)) { - return 0; - } - - *val = strtoll(p, NULL, 10); - if ((errno == ERANGE && (*val == LONG_MAX || *val == LONG_MIN)) || - (errno != 0 && *val == 0)) { - *val = -1; - return 0; - } - - - return 1; -} + const char *p = NULL; + if (!RMUtilInfo_GetString(info, key, &p)) { + return 0; + } -int RMUtilInfo_GetString(RMUtilInfo *info, const char *key, const char **str) { - int i; - for (i = 0; i < info->numEntries; i++) { - if (!strcmp(key, info->entries[i].key)) { - *str = info->entries[i].val; - return 1; - } - } + *val = strtoll(p, NULL, 10); + if ((errno == ERANGE && (*val == LONG_MAX || *val == LONG_MIN)) || (errno != 0 && *val == 0)) { + *val = -1; return 0; + } + + return 1; } -int RMUtilInfo_GetDouble(RMUtilInfo *info, const char *key, double *d) { - const char *p = NULL; - if (!RMUtilInfo_GetString(info, key, &p)) { - printf("not found %s\n", key); - return 0; - } - - *d = strtod(p, NULL); - printf("p: %s, d: %f\n",p,*d); - if ((errno == ERANGE && (*d == HUGE_VAL || *d == -HUGE_VAL)) || - (errno != 0 && *d == 0)) { - return 0; +int RMUtilInfo_GetString(RMUtilInfo *info, const char *key, const char **str) { + int i; + for (i = 0; i < info->numEntries; i++) { + if (!strcmp(key, info->entries[i].key)) { + *str = info->entries[i].val; + return 1; } - - - return 1; + } + return 0; } +int RMUtilInfo_GetDouble(RMUtilInfo *info, const char *key, double *d) { + const char *p = NULL; + if (!RMUtilInfo_GetString(info, key, &p)) { + printf("not found %s\n", key); + return 0; + } + + *d = strtod(p, NULL); + //printf("p: %s, d: %f\n", p, *d); + if ((errno == ERANGE && (*d == HUGE_VAL || *d == -HUGE_VAL)) || (errno != 0 && *d == 0)) { + return 0; + } + + return 1; +} /* c -- pointer to a Null terminated C string pointer. +b -- pointer to a C buffer, followed by pointer to a size_t for its length s -- pointer to a RedisModuleString l -- pointer to Long long integer. d -- pointer to a Double * -- do not parse this argument at all */ int RMUtil_ParseArgs(RedisModuleString **argv, int argc, int offset, const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - int rc = rmutil_vparseArgs(argv, argc, offset, fmt, ap); - va_end(ap); - return rc; + va_list ap; + va_start(ap, fmt); + int rc = rmutil_vparseArgs(argv, argc, offset, fmt, ap); + va_end(ap); + return rc; } - // Internal function that parses arguments based on the format described above int rmutil_vparseArgs(RedisModuleString **argv, int argc, int offset, const char *fmt, va_list ap) { - - int i = offset; - char *c = (char *)fmt; - while (*c && i < argc) { - - // read c string - if (*c == 'c') { - char **p = va_arg(ap, char**); - *p = (char *)RedisModule_StringPtrLen(argv[i], NULL); - } else if (*c == 's') { //read redis string - - RedisModuleString **s = va_arg(ap, void*); - *s = argv[i]; - - } else if (*c == 'l') { //read long - long long *l = va_arg(ap, long long *); - - if (RedisModule_StringToLongLong(argv[i], l) != REDISMODULE_OK) { - return REDISMODULE_ERR; - } - } else if (*c == 'd') { //read double - double *d = va_arg(ap, double *); - if (RedisModule_StringToDouble(argv[i], d) != REDISMODULE_OK) { - return REDISMODULE_ERR; - } - } else if (*c == '*') { //skip current arg - //do nothing - } else { - return REDISMODULE_ERR; //WAT? - } - c++; - i++; - } - // if the format is longer than argc, retun an error - if (*c != 0) { - return REDISMODULE_ERR; - } - return REDISMODULE_OK; - -} + int i = offset; + char *c = (char *)fmt; + while (*c && i < argc) { + + // read c string + if (*c == 'c') { + char **p = va_arg(ap, char **); + *p = (char *)RedisModule_StringPtrLen(argv[i], NULL); + } else if (*c == 'b') { + char **p = va_arg(ap, char **); + size_t *len = va_arg(ap, size_t *); + *p = (char *)RedisModule_StringPtrLen(argv[i], len); + } else if (*c == 's') { // read redis string + + RedisModuleString **s = va_arg(ap, void *); + *s = argv[i]; + + } else if (*c == 'l') { // read long + long long *l = va_arg(ap, long long *); -int RMUtil_ParseArgsAfter(const char *token, RedisModuleString **argv, int argc, const char *fmt, ...) { - - int pos = RMUtil_ArgExists(token, argv, argc, 0); - if (pos == 0) { + if (RedisModule_StringToLongLong(argv[i], l) != REDISMODULE_OK) { return REDISMODULE_ERR; + } + } else if (*c == 'd') { // read double + double *d = va_arg(ap, double *); + if (RedisModule_StringToDouble(argv[i], d) != REDISMODULE_OK) { + return REDISMODULE_ERR; + } + } else if (*c == '*') { // skip current arg + // do nothing + } else { + return REDISMODULE_ERR; // WAT? } - - va_list ap; - va_start(ap, fmt); - int rc = rmutil_vparseArgs(argv, argc, pos+1, fmt, ap); - va_end(ap); - return rc; - + c++; + i++; + } + // if the format is longer than argc, retun an error + if (*c != 0) { + return REDISMODULE_ERR; + } + return REDISMODULE_OK; +} + +int RMUtil_ParseArgsAfter(const char *token, RedisModuleString **argv, int argc, const char *fmt, + ...) { + + int pos = RMUtil_ArgIndex(token, argv, argc); + if (pos < 0) { + return REDISMODULE_ERR; + } + + va_list ap; + va_start(ap, fmt); + int rc = rmutil_vparseArgs(argv, argc, pos + 1, fmt, ap); + va_end(ap); + return rc; } -RedisModuleCallReply *RedisModule_CallReplyArrayElementByPath( - RedisModuleCallReply *rep, const char *path) { +RedisModuleCallReply *RedisModule_CallReplyArrayElementByPath(RedisModuleCallReply *rep, + const char *path) { if (rep == NULL) return NULL; RedisModuleCallReply *ele = rep; @@ -206,10 +219,8 @@ RedisModuleCallReply *RedisModule_CallReplyArrayElementByPath( errno = 0; idx = strtol(s, &e, 10); - if ((errno == ERANGE && (idx == LONG_MAX || idx == LONG_MIN)) || - (errno != 0 && idx == 0) || - (REDISMODULE_REPLY_ARRAY != RedisModule_CallReplyType(ele)) || - (s == e)) { + if ((errno == ERANGE && (idx == LONG_MAX || idx == LONG_MIN)) || (errno != 0 && idx == 0) || + (REDISMODULE_REPLY_ARRAY != RedisModule_CallReplyType(ele)) || (s == e)) { ele = NULL; break; } diff --git a/rmutil/util.h b/rmutil/util.h index 57fd2a3..46bd918 100644 --- a/rmutil/util.h +++ b/rmutil/util.h @@ -3,35 +3,40 @@ #include #include -#include - -/// make sure the response is not NULL or an error, and if it is sends the error to the client and exit the current function -#define RMUTIL_ASSERT_NOERROR(r) \ - if (r == NULL) { \ - return RedisModule_ReplyWithError(ctx,"ERR reply is NULL"); \ - } else if (RedisModule_CallReplyType(r) == REDISMODULE_REPLY_ERROR) { \ - RedisModule_ReplyWithCallReply(ctx,r); \ - return REDISMODULE_ERR; \ - } - -#define __rmutil_register_cmd(ctx, cmd, f, mode) \ - if (RedisModule_CreateCommand(ctx, cmd, f, \ - (!strcmp(mode, "readonly ")) ? "readonly" : \ - (!strcmp(mode, "write ")) ? "write" : mode, \ - 1, 1, 1) == REDISMODULE_ERR) return REDISMODULE_ERR; - -#define RMUtil_RegisterReadCmd(ctx, cmd, f, ...) __rmutil_register_cmd(ctx, cmd, f, "readonly " __VA_ARGS__) - -#define RMUtil_RegisterWriteCmd(ctx, cmd, f, ...) __rmutil_register_cmd(ctx, cmd, f, "write " __VA_ARGS__) + +/// make sure the response is not NULL or an error, and if it is sends the error to the client and +/// exit the current function +#define RMUTIL_ASSERT_NOERROR(ctx, r) \ + if (r == NULL) { \ + return RedisModule_ReplyWithError(ctx, "ERR reply is NULL"); \ + } else if (RedisModule_CallReplyType(r) == REDISMODULE_REPLY_ERROR) { \ + RedisModule_ReplyWithCallReply(ctx, r); \ + return REDISMODULE_ERR; \ + } + +#define __rmutil_register_cmd(ctx, cmd, f, mode) \ + if (RedisModule_CreateCommand(ctx, cmd, f, mode, 1, 1, 1) == REDISMODULE_ERR) \ + return REDISMODULE_ERR; + +#define RMUtil_RegisterReadCmd(ctx, cmd, f) \ + __rmutil_register_cmd(ctx, cmd, f, "readonly") \ + } + +#define RMUtil_RegisterWriteCmd(ctx, cmd, f) __rmutil_register_cmd(ctx, cmd, f, "write") /* RedisModule utilities. */ -/** Return the offset of an arg if it exists in the arg list, or 0 if it's not there */ +/** DEPRECATED: Return the offset of an arg if it exists in the arg list, or 0 if it's not there */ int RMUtil_ArgExists(const char *arg, RedisModuleString **argv, int argc, int offset); +/* Same as argExists but returns -1 if not found. Use this, RMUtil_ArgExists is kept for backwards +compatibility. */ +int RMUtil_ArgIndex(const char *arg, RedisModuleString **argv, int argc); + /** Automatically conver the arg list to corresponding variable pointers according to a given format. -You pass it the command arg list and count, the starting offset, a parsing format, and pointers to the variables. +You pass it the command arg list and count, the starting offset, a parsing format, and pointers to +the variables. The format is a string consisting of the following identifiers: c -- pointer to a Null terminated C string pointer. @@ -39,7 +44,7 @@ The format is a string consisting of the following identifiers: l -- pointer to Long long integer. d -- pointer to a Double * -- do not parse this argument at all - + Example: If I want to parse args[1], args[2] as a long long and double, I do: double d; long long l; @@ -48,23 +53,24 @@ Example: If I want to parse args[1], args[2] as a long long and double, I do: int RMUtil_ParseArgs(RedisModuleString **argv, int argc, int offset, const char *fmt, ...); /** -Same as RMUtil_ParseArgs, but only parses the arguments after `token`, if it was found. +Same as RMUtil_ParseArgs, but only parses the arguments after `token`, if it was found. This is useful for optional stuff like [LIMIT [offset] [limit]] */ -int RMUtil_ParseArgsAfter(const char *token, RedisModuleString **argv, int argc, const char *fmt, ...); +int RMUtil_ParseArgsAfter(const char *token, RedisModuleString **argv, int argc, const char *fmt, + ...); int rmutil_vparseArgs(RedisModuleString **argv, int argc, int offset, const char *fmt, va_list ap); // A single key/value entry in a redis info map typedef struct { - const char *key; - const char *val; + const char *key; + const char *val; } RMUtilInfoEntry; // Representation of INFO command response, as a list of k/v pairs typedef struct { - RMUtilInfoEntry *entries; - int numEntries; + RMUtilInfoEntry *entries; + int numEntries; } RMUtilInfo; /** @@ -80,19 +86,19 @@ RMUtilInfo *RMUtil_GetRedisInfo(RedisModuleCtx *ctx); void RMUtilRedisInfo_Free(RMUtilInfo *info); /** -* Get an integer value from an info object. Returns 1 if the value was found and +* Get an integer value from an info object. Returns 1 if the value was found and * is an integer, 0 otherwise. the value is placed in 'val' */ int RMUtilInfo_GetInt(RMUtilInfo *info, const char *key, long long *val); /** * Get a string value from an info object. The value is placed in str. -* Returns 1 if the key was found, 0 if not +* Returns 1 if the key was found, 0 if not */ int RMUtilInfo_GetString(RMUtilInfo *info, const char *key, const char **str); /** -* Get a double value from an info object. Returns 1 if the value was found and is +* Get a double value from an info object. Returns 1 if the value was found and is * a correctly formatted double, 0 otherwise. the value is placed in 'd' */ int RMUtilInfo_GetDouble(RMUtilInfo *info, const char *key, double *d); @@ -102,8 +108,7 @@ int RMUtilInfo_GetDouble(RMUtilInfo *info, const char *key, double *d); * the path "1 2 3" will return the 3rd element from the 2 element of the 1st * element from an array (or NULL if not found) */ -RedisModuleCallReply *RedisModule_CallReplyArrayElementByPath( - RedisModuleCallReply *rep, const char *path); - +RedisModuleCallReply *RedisModule_CallReplyArrayElementByPath(RedisModuleCallReply *rep, + const char *path); #endif