diff --git a/rmutil/strings.c b/rmutil/strings.c index db20852..3933013 100644 --- a/rmutil/strings.c +++ b/rmutil/strings.c @@ -2,6 +2,7 @@ #include #include #include "strings.h" +#include "alloc.h" #include "sds.h" @@ -68,3 +69,13 @@ void RMUtil_StringToUpper(RedisModuleString *s) { ++c; } } + +void RMUtil_StringConvert(RedisModuleString **rs, const char **ss, size_t n, int options) { + for (size_t ii = 0; ii < n; ++ii) { + const char *p = RedisModule_StringPtrLen(rs[ii], NULL); + if (options & RMUTIL_STRINGCONVERT_COPY) { + p = strdup(p); + } + ss[ii] = p; + } +} \ No newline at end of file diff --git a/rmutil/strings.h b/rmutil/strings.h index c447957..eaef71e 100644 --- a/rmutil/strings.h +++ b/rmutil/strings.h @@ -25,4 +25,14 @@ void RMUtil_StringToLower(RedisModuleString *s); /* Converts a redis string to uppercase in place without reallocating anything */ void RMUtil_StringToUpper(RedisModuleString *s); + +// If set, copy the strings using strdup rather than simply storing pointers. +#define RMUTIL_STRINGCONVERT_COPY 1 + +/** + * Convert one or more RedisModuleString objects into `const char*`. + * Both rs and ss are arrays, and should be of length. + * Options may be 0 or `RMUTIL_STRINGCONVERT_COPY` + */ +void RMUtil_StringConvert(RedisModuleString **rs, const char **ss, size_t n, int options); #endif diff --git a/rmutil/test_periodic.c b/rmutil/test_periodic.c index 88d6b0b..030a021 100644 --- a/rmutil/test_periodic.c +++ b/rmutil/test_periodic.c @@ -3,7 +3,7 @@ #include #include "periodic.h" #include "assert.h" -#include "test_util.h" +#include "test.h" void timerCb(RedisModuleCtx *ctx, void *p) { int *x = p; @@ -24,4 +24,4 @@ int testPeriodic() { return 0; } -TEST_MAIN({ TESTFUNC(testPeriodic); }); \ No newline at end of file +TEST_MAIN({ TESTFUNC(testPeriodic); }); diff --git a/rmutil/test_util.h b/rmutil/test_util.h index a15864a..5a6273d 100644 --- a/rmutil/test_util.h +++ b/rmutil/test_util.h @@ -1,69 +1,67 @@ -#ifndef __TESTUTIL_H__ -#define __TESTUTIL_H__ +#ifndef __TEST_UTIL_H__ +#define __TEST_UTIL_H__ -#include -#include +#include "util.h" +#include #include +#include -static int numTests = 0; -static int numAsserts = 0; -#define TESTFUNC(f) \ - printf(" Testing %s\t\t", __STRING(f)); \ - numTests++; \ - fflush(stdout); \ - if (f()) { \ - printf(" %s FAILED!\n", __STRING(f)); \ - exit(1); \ - } else \ - printf("[PASS]\n"); +#define RMUtil_Test(f) \ + if (argc < 2 || RMUtil_ArgExists(__STRING(f), argv, argc, 1)) { \ + int rc = f(ctx); \ + if (rc != REDISMODULE_OK) { \ + RedisModule_ReplyWithError(ctx, "Test " __STRING(f) " FAILED"); \ + return REDISMODULE_ERR;\ + }\ + } + + +#define RMUtil_Assert(expr) if (!(expr)) { fprintf (stderr, "Assertion '%s' Failed\n", __STRING(expr)); return REDISMODULE_ERR; } -#define ASSERTM(expr, ...) \ - if (!(expr)) { \ - fprintf(stderr, "%s:%d: Assertion '%s' Failed: " __VA_ARGS__ "\n", __FILE__, __LINE__, \ - __STRING(expr)); \ - return -1; \ - } \ - numAsserts++; +#define RMUtil_AssertReplyEquals(rep, cstr) RMUtil_Assert( \ + RMUtil_StringEquals(RedisModule_CreateStringFromCallReply(rep), RedisModule_CreateString(ctx, cstr, strlen(cstr))) \ + ) +# -#define ASSERT(expr) \ - if (!(expr)) { \ - fprintf(stderr, "%s:%d Assertion '%s' Failed\n", __FILE__, __LINE__, __STRING(expr)); \ - return -1; \ - } \ - numAsserts++; +/** +* Create an arg list to pass to a redis command handler manually, based on the format in fmt. +* The accepted format specifiers are: +* c - for null terminated c strings +* s - for RedisModuleString* objects +* l - for longs +* +* Example: RMUtil_MakeArgs(ctx, &argc, "clc", "hello", 1337, "world"); +* +* Returns an array of RedisModuleString pointers. The size of the array is store in argcp +*/ +RedisModuleString **RMUtil_MakeArgs(RedisModuleCtx *ctx, int *argcp, const char *fmt, ...) { + + va_list ap; + va_start(ap, fmt); + RedisModuleString **argv = calloc(strlen(fmt), sizeof(RedisModuleString*)); + int argc = 0; + const char *p = fmt; + while(*p) { + if (*p == 'c') { + char *cstr = va_arg(ap,char*); + argv[argc++] = RedisModule_CreateString(ctx, cstr, strlen(cstr)); + } else if (*p == 's') { + argv[argc++] = va_arg(ap,void*);; + } else if (*p == 'l') { + long ll = va_arg(ap,long long); + argv[argc++] = RedisModule_CreateStringFromLongLong(ctx, ll); + } else { + goto fmterr; + } + p++; + } + *argcp = argc; + + return argv; +fmterr: + free(argv); + return NULL; +} -#define ASSERT_STRING_EQ(s1, s2) ASSERT(!strcmp(s1, s2)); - -#define ASSERT_EQUAL(x, y, ...) \ - if (x != y) { \ - fprintf(stderr, "%s:%d: ", __FILE__, __LINE__); \ - fprintf(stderr, "%g != %g: " __VA_ARGS__ "\n", (double)x, (double)y); \ - return -1; \ - } \ - numAsserts++; - -#define FAIL(fmt, ...) \ - { \ - fprintf(stderr, "%s:%d: FAIL: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); \ - return -1; \ - } - -#define RETURN_TEST_SUCCESS return 0; -#define TEST_CASE(x, block) \ - int x { \ - block; \ - return 0 \ - } - -#define PRINT_TEST_SUMMARY printf("\nTotal: %d tests and %d assertions OK\n", numTests, numAsserts); - -#define TEST_MAIN(body) \ - int main(int argc, char **argv) { \ - printf("Starting Test '%s'...\n", argv[0]); \ - body; \ - PRINT_TEST_SUMMARY; \ - printf("\n--------------------\n\n"); \ - return 0; \ - } #endif \ No newline at end of file diff --git a/rmutil/test_vector.c b/rmutil/test_vector.c index e45a156..c5737b2 100644 --- a/rmutil/test_vector.c +++ b/rmutil/test_vector.c @@ -1,6 +1,6 @@ #include "vector.h" #include -#include "test_util.h" +#include "test.h" int testVector() { diff --git a/rmutil/util.c b/rmutil/util.c index b0b7856..8307e59 100644 --- a/rmutil/util.c +++ b/rmutil/util.c @@ -275,18 +275,3 @@ RedisModuleString **RMUtil_ParseVarArgs(RedisModuleString **argv, int argc, int *nargs = n; return argv + 1; } - -void RMUtil_DefaultAofRewrite(RedisModuleIO *aof, RedisModuleString *key, void *value) { - RedisModuleCallReply *rep = - RedisModule_Call(RedisModule_GetContextFromIO(aof), "DUMP", "%s", key); - if (rep != NULL && RedisModule_CallReplyType(rep) == REDISMODULE_REPLY_STRING) { - size_t n; - const char *s = RedisModule_CallReplyStringPtr(rep, &n); - RedisModule_EmitAOF(aof, "RESTORE", "%sb", key, s, n); - } else { - RedisModule_Log(RedisModule_GetContextFromIO(aof), "warning", "Failed to emit AOF"); - } - if (rep != NULL) { - RedisModule_FreeCallReply(rep); - } -} diff --git a/rmutil/util.h b/rmutil/util.h index 4dae477..a51c8ca 100644 --- a/rmutil/util.h +++ b/rmutil/util.h @@ -72,13 +72,6 @@ int rmutil_vparseArgs(RedisModuleString **argv, int argc, int offset, const char RedisModuleString **RMUtil_ParseVarArgs(RedisModuleString **argv, int argc, int offset, const char *keyword, size_t *nargs); -/** - * Default implementation of an AoF rewrite function that simply calls DUMP/RESTORE - * internally. To use this function, pass it as the .aof_rewrite value in - * RedisModuleTypeMethods - */ -void RMUtil_DefaultAofRewrite(RedisModuleIO *aof, RedisModuleString *key, void *value); - // A single key/value entry in a redis info map typedef struct { const char *key; diff --git a/rmutil/vector.h b/rmutil/vector.h index 0bff2c2..a3b606f 100644 --- a/rmutil/vector.h +++ b/rmutil/vector.h @@ -2,6 +2,7 @@ #define __VECTOR_H__ #include #include +#include /* * Generic resizable vector that can be used if you just want to store stuff @@ -9,10 +10,10 @@ * Works like C++ std::vector with an underlying resizable buffer */ typedef struct { - char *data; - size_t elemSize; - size_t cap; - size_t top; + char *data; + size_t elemSize; + size_t cap; + size_t top; } Vector;