improvements to util functions

master
Dvir Volk 8 years ago
parent d681a51699
commit 42df4c3503

@ -6,6 +6,7 @@
#include <stdarg.h> #include <stdarg.h>
#include <limits.h> #include <limits.h>
#include <string.h> #include <string.h>
#include <redismodule.h>
#include "util.h" #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) { int RMUtil_ArgExists(const char *arg, RedisModuleString **argv, int argc, int offset) {
for (; offset < argc; offset++) { size_t larg = strlen(arg);
size_t l; for (; offset < argc; offset++) {
const char *carg = RedisModule_StringPtrLen(argv[offset], &l); size_t l;
if (carg != NULL && strcasecmp(carg, arg) == 0) { const char *carg = RedisModule_StringPtrLen(argv[offset], &l);
return offset; if (l != larg) continue;
} if (carg != NULL && strncasecmp(carg, arg, larg) == 0) {
return offset;
} }
return 0; }
return 0;
} }
RMUtilInfo *RMUtil_GetRedisInfo(RedisModuleCtx *ctx) { /**
Check if an argument exists in an argument list (argv,argc)
RedisModuleCallReply *r = RedisModule_Call(ctx, "INFO", "c", "all"); @return -1 if it doesn't exist, otherwise the offset it exists in
if (r == NULL || RedisModule_CallReplyType(r) == REDISMODULE_REPLY_ERROR) { */
return NULL; 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) {
int cap = 100; // rough estimate of info lines RedisModuleCallReply *r = RedisModule_Call(ctx, "INFO", "c", "all");
RMUtilInfo *info = malloc(sizeof(RMUtilInfo)); if (r == NULL || RedisModule_CallReplyType(r) == REDISMODULE_REPLY_ERROR) {
info->entries = calloc(cap, sizeof(RMUtilInfoEntry)); return NULL;
}
int i = 0; int cap = 100; // rough estimate of info lines
char *text = (char *)RedisModule_StringPtrLen(RedisModule_CreateStringFromCallReply(r), NULL); RMUtilInfo *info = malloc(sizeof(RMUtilInfo));
char *line = text; info->entries = calloc(cap, sizeof(RMUtilInfoEntry));
while (line) {
char *line = strsep(&text, "\r\n");
if (line == NULL) break;
if (!(*line >= 'a' && *line <= 'z')) { //skip non entry lines int i = 0;
continue; char *text = (char *)RedisModule_StringPtrLen(RedisModule_CreateStringFromCallReply(r), NULL);
} char *line = text;
while (line) {
char *line = strsep(&text, "\r\n");
if (line == NULL) break;
char *key = strsep(&line, ":"); if (!(*line >= 'a' && *line <= 'z')) { // skip non entry lines
info->entries[i].key = key; continue;
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) { void RMUtilRedisInfo_Free(RMUtilInfo *info) {
free(info->entries); free(info->entries);
free(info); free(info);
} }
int RMUtilInfo_GetInt(RMUtilInfo *info, const char *key, long long *val) { int RMUtilInfo_GetInt(RMUtilInfo *info, const char *key, long long *val) {
const char *p = NULL; const char *p = NULL;
if (!RMUtilInfo_GetString(info, key, &p)) { if (!RMUtilInfo_GetString(info, key, &p)) {
return 0; return 0;
} }
*val = strtoll(p, NULL, 10);
if ((errno == ERANGE && (*val == LONG_MAX || *val == LONG_MIN)) ||
(errno != 0 && *val == 0)) {
*val = -1;
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; return 1;
} }
int RMUtilInfo_GetString(RMUtilInfo *info, const char *key, const char **str) { int RMUtilInfo_GetString(RMUtilInfo *info, const char *key, const char **str) {
int i; int i;
for (i = 0; i < info->numEntries; i++) { for (i = 0; i < info->numEntries; i++) {
if (!strcmp(key, info->entries[i].key)) { if (!strcmp(key, info->entries[i].key)) {
*str = info->entries[i].val; *str = info->entries[i].val;
return 1; return 1;
}
} }
return 0; }
return 0;
} }
int RMUtilInfo_GetDouble(RMUtilInfo *info, const char *key, double *d) { int RMUtilInfo_GetDouble(RMUtilInfo *info, const char *key, double *d) {
const char *p = NULL; const char *p = NULL;
if (!RMUtilInfo_GetString(info, key, &p)) { if (!RMUtilInfo_GetString(info, key, &p)) {
printf("not found %s\n", key); printf("not found %s\n", key);
return 0; 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;
}
*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; return 1;
} }
/* /*
c -- pointer to a Null terminated C string pointer. 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 s -- pointer to a RedisModuleString
l -- pointer to Long long integer. l -- pointer to Long long integer.
d -- pointer to a Double d -- pointer to a Double
* -- do not parse this argument at all * -- do not parse this argument at all
*/ */
int RMUtil_ParseArgs(RedisModuleString **argv, int argc, int offset, const char *fmt, ...) { int RMUtil_ParseArgs(RedisModuleString **argv, int argc, int offset, const char *fmt, ...) {
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
int rc = rmutil_vparseArgs(argv, argc, offset, fmt, ap); int rc = rmutil_vparseArgs(argv, argc, offset, fmt, ap);
va_end(ap); va_end(ap);
return rc; return rc;
} }
// Internal function that parses arguments based on the format described above // 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 rmutil_vparseArgs(RedisModuleString **argv, int argc, int offset, const char *fmt, va_list ap) {
int i = offset; int i = offset;
char *c = (char *)fmt; char *c = (char *)fmt;
while (*c && i < argc) { 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;
// 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];
int RMUtil_ParseArgsAfter(const char *token, RedisModuleString **argv, int argc, const char *fmt, ...) { } else if (*c == 'l') { // read long
long long *l = va_arg(ap, long long *);
int pos = RMUtil_ArgExists(token, argv, argc, 0); if (RedisModule_StringToLongLong(argv[i], l) != REDISMODULE_OK) {
if (pos == 0) {
return REDISMODULE_ERR; 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 RMUtil_ParseArgsAfter(const char *token, RedisModuleString **argv, int argc, const char *fmt,
...) {
va_list ap; int pos = RMUtil_ArgIndex(token, argv, argc);
va_start(ap, fmt); if (pos < 0) {
int rc = rmutil_vparseArgs(argv, argc, pos+1, fmt, ap); return REDISMODULE_ERR;
va_end(ap); }
return rc;
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 *RedisModule_CallReplyArrayElementByPath(RedisModuleCallReply *rep,
RedisModuleCallReply *rep, const char *path) { const char *path) {
if (rep == NULL) return NULL; if (rep == NULL) return NULL;
RedisModuleCallReply *ele = rep; RedisModuleCallReply *ele = rep;
@ -206,10 +219,8 @@ RedisModuleCallReply *RedisModule_CallReplyArrayElementByPath(
errno = 0; errno = 0;
idx = strtol(s, &e, 10); idx = strtol(s, &e, 10);
if ((errno == ERANGE && (idx == LONG_MAX || idx == LONG_MIN)) || if ((errno == ERANGE && (idx == LONG_MAX || idx == LONG_MIN)) || (errno != 0 && idx == 0) ||
(errno != 0 && idx == 0) || (REDISMODULE_REPLY_ARRAY != RedisModule_CallReplyType(ele)) || (s == e)) {
(REDISMODULE_REPLY_ARRAY != RedisModule_CallReplyType(ele)) ||
(s == e)) {
ele = NULL; ele = NULL;
break; break;
} }

@ -3,35 +3,40 @@
#include <redismodule.h> #include <redismodule.h>
#include <stdarg.h> #include <stdarg.h>
#include <string.h>
/// 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 /// make sure the response is not NULL or an error, and if it is sends the error to the client and
#define RMUTIL_ASSERT_NOERROR(r) \ /// exit the current function
if (r == NULL) { \ #define RMUTIL_ASSERT_NOERROR(ctx, r) \
return RedisModule_ReplyWithError(ctx,"ERR reply is NULL"); \ if (r == NULL) { \
} else if (RedisModule_CallReplyType(r) == REDISMODULE_REPLY_ERROR) { \ return RedisModule_ReplyWithError(ctx, "ERR reply is NULL"); \
RedisModule_ReplyWithCallReply(ctx,r); \ } else if (RedisModule_CallReplyType(r) == REDISMODULE_REPLY_ERROR) { \
return REDISMODULE_ERR; \ RedisModule_ReplyWithCallReply(ctx, r); \
} return REDISMODULE_ERR; \
}
#define __rmutil_register_cmd(ctx, cmd, f, mode) \ #define __rmutil_register_cmd(ctx, cmd, f, mode) \
if (RedisModule_CreateCommand(ctx, cmd, f, \ if (RedisModule_CreateCommand(ctx, cmd, f, mode, 1, 1, 1) == REDISMODULE_ERR) \
(!strcmp(mode, "readonly ")) ? "readonly" : \ return REDISMODULE_ERR;
(!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_RegisterReadCmd(ctx, cmd, f) \
__rmutil_register_cmd(ctx, cmd, f, "readonly") \
}
#define RMUtil_RegisterWriteCmd(ctx, cmd, f, ...) __rmutil_register_cmd(ctx, cmd, f, "write " __VA_ARGS__) #define RMUtil_RegisterWriteCmd(ctx, cmd, f) __rmutil_register_cmd(ctx, cmd, f, "write")
/* RedisModule utilities. */ /* 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); 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. 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: The format is a string consisting of the following identifiers:
c -- pointer to a Null terminated C string pointer. c -- pointer to a Null terminated C string pointer.
@ -51,20 +56,21 @@ int RMUtil_ParseArgs(RedisModuleString **argv, int argc, int offset, const char
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]] 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); 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 // A single key/value entry in a redis info map
typedef struct { typedef struct {
const char *key; const char *key;
const char *val; const char *val;
} RMUtilInfoEntry; } RMUtilInfoEntry;
// Representation of INFO command response, as a list of k/v pairs // Representation of INFO command response, as a list of k/v pairs
typedef struct { typedef struct {
RMUtilInfoEntry *entries; RMUtilInfoEntry *entries;
int numEntries; int numEntries;
} RMUtilInfo; } RMUtilInfo;
/** /**
@ -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 * 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) * element from an array (or NULL if not found)
*/ */
RedisModuleCallReply *RedisModule_CallReplyArrayElementByPath( RedisModuleCallReply *RedisModule_CallReplyArrayElementByPath(RedisModuleCallReply *rep,
RedisModuleCallReply *rep, const char *path); const char *path);
#endif #endif

Loading…
Cancel
Save