├── .clang-format ├── .github ├── release-drafter-config.yml └── workflows │ └── release-drafter.yml ├── .gitignore ├── 4.0 └── redismodule.h ├── 5.0 ├── redis-module-sdk.h ├── redismodule.h └── redismodulex.h ├── 6.0 ├── redis-module-sdk.h ├── redismodule.h └── redismodulex.h ├── 6.2 └── redismodule.h ├── 7.0 └── redismodule.h ├── API.md ├── BLOCK.md ├── FUNCTIONS.md ├── LICENSE ├── Makefile ├── README.md ├── TYPES.md ├── example ├── Makefile ├── README.md └── module.c ├── redismodule-rlec.h ├── redismodule.h └── rmutil ├── Makefile ├── alloc.c ├── alloc.h ├── heap.c ├── heap.h ├── logging.h ├── periodic.c ├── periodic.h ├── priority_queue.c ├── priority_queue.h ├── sds.c ├── sds.h ├── sdsalloc.h ├── strings.c ├── strings.h ├── test.h ├── test_heap.c ├── test_periodic.c ├── test_priority_queue.c ├── test_util.h ├── test_vector.c ├── util.c ├── util.h ├── vector.c └── vector.h /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | AccessModifierOffset: -1 4 | AlignAfterOpenBracket: true 5 | AlignConsecutiveAssignments: false 6 | AlignEscapedNewlinesLeft: true 7 | AlignOperands: true 8 | AlignTrailingComments: true 9 | AllowAllParametersOfDeclarationOnNextLine: true 10 | AllowShortBlocksOnASingleLine: false 11 | AllowShortCaseLabelsOnASingleLine: false 12 | AllowShortFunctionsOnASingleLine: None 13 | AllowShortIfStatementsOnASingleLine: true 14 | AllowShortLoopsOnASingleLine: true 15 | AlwaysBreakAfterDefinitionReturnType: None 16 | AlwaysBreakBeforeMultilineStrings: true 17 | AlwaysBreakTemplateDeclarations: true 18 | BinPackArguments: true 19 | BinPackParameters: true 20 | BreakBeforeBinaryOperators: None 21 | BreakBeforeBraces: Attach 22 | BreakBeforeTernaryOperators: true 23 | BreakConstructorInitializersBeforeComma: false 24 | ColumnLimit: 100 25 | CommentPragmas: '^ IWYU pragma:' 26 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 27 | ConstructorInitializerIndentWidth: 4 28 | ContinuationIndentWidth: 4 29 | Cpp11BracedListStyle: true 30 | DerivePointerAlignment: true 31 | DisableFormat: false 32 | ExperimentalAutoDetectBinPacking: false 33 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 34 | IndentCaseLabels: true 35 | IndentWidth: 2 36 | IndentWrappedFunctionNames: false 37 | KeepEmptyLinesAtTheStartOfBlocks: true 38 | MacroBlockBegin: '' 39 | MacroBlockEnd: '' 40 | MaxEmptyLinesToKeep: 1 41 | NamespaceIndentation: None 42 | PenaltyBreakBeforeFirstCallParameter: 1 43 | PenaltyBreakComment: 300 44 | PenaltyBreakFirstLessLess: 120 45 | PenaltyBreakString: 1000 46 | PenaltyExcessCharacter: 1000000 47 | PenaltyReturnTypeOnItsOwnLine: 200 48 | PointerAlignment: Left 49 | SpaceAfterCStyleCast: false 50 | SpaceBeforeAssignmentOperators: true 51 | SpaceBeforeParens: ControlStatements 52 | SpaceInEmptyParentheses: false 53 | SpacesBeforeTrailingComments: 2 54 | SpacesInAngles: false 55 | SpacesInContainerLiterals: true 56 | SpacesInCStyleCastParentheses: false 57 | SpacesInParentheses: false 58 | SpacesInSquareBrackets: false 59 | Standard: Auto 60 | TabWidth: 4 61 | UseTab: Never 62 | SortIncludes: false 63 | ... 64 | 65 | -------------------------------------------------------------------------------- /.github/release-drafter-config.yml: -------------------------------------------------------------------------------- 1 | name-template: 'Version $NEXT_PATCH_VERSION' 2 | tag-template: 'v$NEXT_PATCH_VERSION' 3 | categories: 4 | - title: 'Features' 5 | labels: 6 | - 'feature' 7 | - 'enhancement' 8 | - title: 'Bug Fixes' 9 | labels: 10 | - 'fix' 11 | - 'bugfix' 12 | - 'bug' 13 | - title: 'Maintenance' 14 | label: 'chore' 15 | change-template: '- $TITLE (#$NUMBER)' 16 | exclude-labels: 17 | - 'skip-changelog' 18 | template: | 19 | ## Changes 20 | 21 | $CHANGES 22 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | # branches to consider in the event; optional, defaults to all 6 | branches: 7 | - master 8 | 9 | jobs: 10 | update_release_draft: 11 | runs-on: ubuntu-latest 12 | steps: 13 | # Drafts your next Release notes as Pull Requests are merged into "master" 14 | - uses: release-drafter/release-drafter@v5 15 | with: 16 | # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml 17 | config-name: release-drafter-config.yml 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | *.so 4 | *.db 5 | .vscode 6 | rmutil/test_vector 7 | -------------------------------------------------------------------------------- /4.0/redismodule.h: -------------------------------------------------------------------------------- 1 | #ifndef REDISMODULE_H 2 | #define REDISMODULE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | /* ---------------- Defines common between core and modules --------------- */ 9 | 10 | /* Error status return values. */ 11 | #define REDISMODULE_OK 0 12 | #define REDISMODULE_ERR 1 13 | 14 | /* API versions. */ 15 | #define REDISMODULE_APIVER_1 1 16 | 17 | /* API flags and constants */ 18 | #define REDISMODULE_READ (1<<0) 19 | #define REDISMODULE_WRITE (1<<1) 20 | 21 | #define REDISMODULE_LIST_HEAD 0 22 | #define REDISMODULE_LIST_TAIL 1 23 | 24 | /* Key types. */ 25 | #define REDISMODULE_KEYTYPE_EMPTY 0 26 | #define REDISMODULE_KEYTYPE_STRING 1 27 | #define REDISMODULE_KEYTYPE_LIST 2 28 | #define REDISMODULE_KEYTYPE_HASH 3 29 | #define REDISMODULE_KEYTYPE_SET 4 30 | #define REDISMODULE_KEYTYPE_ZSET 5 31 | #define REDISMODULE_KEYTYPE_MODULE 6 32 | 33 | /* Reply types. */ 34 | #define REDISMODULE_REPLY_UNKNOWN -1 35 | #define REDISMODULE_REPLY_STRING 0 36 | #define REDISMODULE_REPLY_ERROR 1 37 | #define REDISMODULE_REPLY_INTEGER 2 38 | #define REDISMODULE_REPLY_ARRAY 3 39 | #define REDISMODULE_REPLY_NULL 4 40 | 41 | /* Postponed array length. */ 42 | #define REDISMODULE_POSTPONED_ARRAY_LEN -1 43 | 44 | /* Expire */ 45 | #define REDISMODULE_NO_EXPIRE -1 46 | 47 | /* Sorted set API flags. */ 48 | #define REDISMODULE_ZADD_XX (1<<0) 49 | #define REDISMODULE_ZADD_NX (1<<1) 50 | #define REDISMODULE_ZADD_ADDED (1<<2) 51 | #define REDISMODULE_ZADD_UPDATED (1<<3) 52 | #define REDISMODULE_ZADD_NOP (1<<4) 53 | 54 | /* Hash API flags. */ 55 | #define REDISMODULE_HASH_NONE 0 56 | #define REDISMODULE_HASH_NX (1<<0) 57 | #define REDISMODULE_HASH_XX (1<<1) 58 | #define REDISMODULE_HASH_CFIELDS (1<<2) 59 | #define REDISMODULE_HASH_EXISTS (1<<3) 60 | 61 | /* Context Flags: Info about the current context returned by RM_GetContextFlags */ 62 | 63 | /* The command is running in the context of a Lua script */ 64 | #define REDISMODULE_CTX_FLAGS_LUA 0x0001 65 | /* The command is running inside a Redis transaction */ 66 | #define REDISMODULE_CTX_FLAGS_MULTI 0x0002 67 | /* The instance is a master */ 68 | #define REDISMODULE_CTX_FLAGS_MASTER 0x0004 69 | /* The instance is a slave */ 70 | #define REDISMODULE_CTX_FLAGS_SLAVE 0x0008 71 | /* The instance is read-only (usually meaning it's a slave as well) */ 72 | #define REDISMODULE_CTX_FLAGS_READONLY 0x0010 73 | /* The instance is running in cluster mode */ 74 | #define REDISMODULE_CTX_FLAGS_CLUSTER 0x0020 75 | /* The instance has AOF enabled */ 76 | #define REDISMODULE_CTX_FLAGS_AOF 0x0040 // 77 | /* The instance has RDB enabled */ 78 | #define REDISMODULE_CTX_FLAGS_RDB 0x0080 // 79 | /* The instance has Maxmemory set */ 80 | #define REDISMODULE_CTX_FLAGS_MAXMEMORY 0x0100 81 | /* Maxmemory is set and has an eviction policy that may delete keys */ 82 | #define REDISMODULE_CTX_FLAGS_EVICT 0x0200 83 | 84 | 85 | #define REDISMODULE_NOTIFY_GENERIC (1<<2) /* g */ 86 | #define REDISMODULE_NOTIFY_STRING (1<<3) /* $ */ 87 | #define REDISMODULE_NOTIFY_LIST (1<<4) /* l */ 88 | #define REDISMODULE_NOTIFY_SET (1<<5) /* s */ 89 | #define REDISMODULE_NOTIFY_HASH (1<<6) /* h */ 90 | #define REDISMODULE_NOTIFY_ZSET (1<<7) /* z */ 91 | #define REDISMODULE_NOTIFY_EXPIRED (1<<8) /* x */ 92 | #define REDISMODULE_NOTIFY_EVICTED (1<<9) /* e */ 93 | #define REDISMODULE_NOTIFY_ALL (REDISMODULE_NOTIFY_GENERIC | REDISMODULE_NOTIFY_STRING | REDISMODULE_NOTIFY_LIST | REDISMODULE_NOTIFY_SET | REDISMODULE_NOTIFY_HASH | REDISMODULE_NOTIFY_ZSET | REDISMODULE_NOTIFY_EXPIRED | REDISMODULE_NOTIFY_EVICTED) /* A */ 94 | 95 | 96 | /* A special pointer that we can use between the core and the module to signal 97 | * field deletion, and that is impossible to be a valid pointer. */ 98 | #define REDISMODULE_HASH_DELETE ((RedisModuleString*)(long)1) 99 | 100 | /* Error messages. */ 101 | #define REDISMODULE_ERRORMSG_WRONGTYPE "WRONGTYPE Operation against a key holding the wrong kind of value" 102 | 103 | #define REDISMODULE_POSITIVE_INFINITE (1.0/0.0) 104 | #define REDISMODULE_NEGATIVE_INFINITE (-1.0/0.0) 105 | 106 | #define REDISMODULE_NOT_USED(V) ((void) V) 107 | 108 | /* ------------------------- End of common defines ------------------------ */ 109 | 110 | #ifndef REDISMODULE_CORE 111 | 112 | typedef long long mstime_t; 113 | 114 | /* Incomplete structures for compiler checks but opaque access. */ 115 | typedef struct RedisModuleCtx RedisModuleCtx; 116 | typedef struct RedisModuleKey RedisModuleKey; 117 | typedef struct RedisModuleString RedisModuleString; 118 | typedef struct RedisModuleCallReply RedisModuleCallReply; 119 | typedef struct RedisModuleIO RedisModuleIO; 120 | typedef struct RedisModuleType RedisModuleType; 121 | typedef struct RedisModuleDigest RedisModuleDigest; 122 | typedef struct RedisModuleBlockedClient RedisModuleBlockedClient; 123 | 124 | typedef int (*RedisModuleCmdFunc) (RedisModuleCtx *ctx, RedisModuleString **argv, int argc); 125 | 126 | typedef int (*RedisModuleNotificationFunc) (RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key); 127 | typedef void *(*RedisModuleTypeLoadFunc)(RedisModuleIO *rdb, int encver); 128 | typedef void (*RedisModuleTypeSaveFunc)(RedisModuleIO *rdb, void *value); 129 | typedef void (*RedisModuleTypeRewriteFunc)(RedisModuleIO *aof, RedisModuleString *key, void *value); 130 | typedef size_t (*RedisModuleTypeMemUsageFunc)(const void *value); 131 | typedef void (*RedisModuleTypeDigestFunc)(RedisModuleDigest *digest, void *value); 132 | typedef void (*RedisModuleTypeFreeFunc)(void *value); 133 | 134 | #define REDISMODULE_TYPE_METHOD_VERSION 1 135 | typedef struct RedisModuleTypeMethods { 136 | uint64_t version; 137 | RedisModuleTypeLoadFunc rdb_load; 138 | RedisModuleTypeSaveFunc rdb_save; 139 | RedisModuleTypeRewriteFunc aof_rewrite; 140 | RedisModuleTypeMemUsageFunc mem_usage; 141 | RedisModuleTypeDigestFunc digest; 142 | RedisModuleTypeFreeFunc free; 143 | } RedisModuleTypeMethods; 144 | 145 | #define REDISMODULE_GET_API(name) \ 146 | RedisModule_GetApi("RedisModule_" #name, ((void **)&RedisModule_ ## name)) 147 | 148 | #define REDISMODULE_API_FUNC(x) (*x) 149 | 150 | 151 | void *REDISMODULE_API_FUNC(RedisModule_Alloc)(size_t bytes); 152 | void *REDISMODULE_API_FUNC(RedisModule_Realloc)(void *ptr, size_t bytes); 153 | void REDISMODULE_API_FUNC(RedisModule_Free)(void *ptr); 154 | void *REDISMODULE_API_FUNC(RedisModule_Calloc)(size_t nmemb, size_t size); 155 | char *REDISMODULE_API_FUNC(RedisModule_Strdup)(const char *str); 156 | int REDISMODULE_API_FUNC(RedisModule_GetApi)(const char *, void *); 157 | int REDISMODULE_API_FUNC(RedisModule_CreateCommand)(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep); 158 | void REDISMODULE_API_FUNC(RedisModule_SetModuleAttribs)(RedisModuleCtx *ctx, const char *name, int ver, int apiver); 159 | int REDISMODULE_API_FUNC(RedisModule_IsModuleNameBusy)(const char *name); 160 | int REDISMODULE_API_FUNC(RedisModule_WrongArity)(RedisModuleCtx *ctx); 161 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithLongLong)(RedisModuleCtx *ctx, long long ll); 162 | int REDISMODULE_API_FUNC(RedisModule_GetSelectedDb)(RedisModuleCtx *ctx); 163 | int REDISMODULE_API_FUNC(RedisModule_SelectDb)(RedisModuleCtx *ctx, int newid); 164 | void *REDISMODULE_API_FUNC(RedisModule_OpenKey)(RedisModuleCtx *ctx, RedisModuleString *keyname, int mode); 165 | void REDISMODULE_API_FUNC(RedisModule_CloseKey)(RedisModuleKey *kp); 166 | int REDISMODULE_API_FUNC(RedisModule_KeyType)(RedisModuleKey *kp); 167 | size_t REDISMODULE_API_FUNC(RedisModule_ValueLength)(RedisModuleKey *kp); 168 | int REDISMODULE_API_FUNC(RedisModule_ListPush)(RedisModuleKey *kp, int where, RedisModuleString *ele); 169 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_ListPop)(RedisModuleKey *key, int where); 170 | RedisModuleCallReply *REDISMODULE_API_FUNC(RedisModule_Call)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...); 171 | const char *REDISMODULE_API_FUNC(RedisModule_CallReplyProto)(RedisModuleCallReply *reply, size_t *len); 172 | void REDISMODULE_API_FUNC(RedisModule_FreeCallReply)(RedisModuleCallReply *reply); 173 | int REDISMODULE_API_FUNC(RedisModule_CallReplyType)(RedisModuleCallReply *reply); 174 | long long REDISMODULE_API_FUNC(RedisModule_CallReplyInteger)(RedisModuleCallReply *reply); 175 | size_t REDISMODULE_API_FUNC(RedisModule_CallReplyLength)(RedisModuleCallReply *reply); 176 | RedisModuleCallReply *REDISMODULE_API_FUNC(RedisModule_CallReplyArrayElement)(RedisModuleCallReply *reply, size_t idx); 177 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateString)(RedisModuleCtx *ctx, const char *ptr, size_t len); 178 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromLongLong)(RedisModuleCtx *ctx, long long ll); 179 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromString)(RedisModuleCtx *ctx, const RedisModuleString *str); 180 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringPrintf)(RedisModuleCtx *ctx, const char *fmt, ...); 181 | void REDISMODULE_API_FUNC(RedisModule_FreeString)(RedisModuleCtx *ctx, RedisModuleString *str); 182 | const char *REDISMODULE_API_FUNC(RedisModule_StringPtrLen)(const RedisModuleString *str, size_t *len); 183 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithError)(RedisModuleCtx *ctx, const char *err); 184 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithSimpleString)(RedisModuleCtx *ctx, const char *msg); 185 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithArray)(RedisModuleCtx *ctx, long len); 186 | void REDISMODULE_API_FUNC(RedisModule_ReplySetArrayLength)(RedisModuleCtx *ctx, long len); 187 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithStringBuffer)(RedisModuleCtx *ctx, const char *buf, size_t len); 188 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithString)(RedisModuleCtx *ctx, RedisModuleString *str); 189 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithNull)(RedisModuleCtx *ctx); 190 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithDouble)(RedisModuleCtx *ctx, double d); 191 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithCallReply)(RedisModuleCtx *ctx, RedisModuleCallReply *reply); 192 | int REDISMODULE_API_FUNC(RedisModule_StringToLongLong)(const RedisModuleString *str, long long *ll); 193 | int REDISMODULE_API_FUNC(RedisModule_StringToDouble)(const RedisModuleString *str, double *d); 194 | void REDISMODULE_API_FUNC(RedisModule_AutoMemory)(RedisModuleCtx *ctx); 195 | int REDISMODULE_API_FUNC(RedisModule_Replicate)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...); 196 | int REDISMODULE_API_FUNC(RedisModule_ReplicateVerbatim)(RedisModuleCtx *ctx); 197 | const char *REDISMODULE_API_FUNC(RedisModule_CallReplyStringPtr)(RedisModuleCallReply *reply, size_t *len); 198 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromCallReply)(RedisModuleCallReply *reply); 199 | int REDISMODULE_API_FUNC(RedisModule_DeleteKey)(RedisModuleKey *key); 200 | int REDISMODULE_API_FUNC(RedisModule_UnlinkKey)(RedisModuleKey *key); 201 | int REDISMODULE_API_FUNC(RedisModule_StringSet)(RedisModuleKey *key, RedisModuleString *str); 202 | char *REDISMODULE_API_FUNC(RedisModule_StringDMA)(RedisModuleKey *key, size_t *len, int mode); 203 | int REDISMODULE_API_FUNC(RedisModule_StringTruncate)(RedisModuleKey *key, size_t newlen); 204 | mstime_t REDISMODULE_API_FUNC(RedisModule_GetExpire)(RedisModuleKey *key); 205 | int REDISMODULE_API_FUNC(RedisModule_SetExpire)(RedisModuleKey *key, mstime_t expire); 206 | int REDISMODULE_API_FUNC(RedisModule_ZsetAdd)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr); 207 | int REDISMODULE_API_FUNC(RedisModule_ZsetIncrby)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr, double *newscore); 208 | int REDISMODULE_API_FUNC(RedisModule_ZsetScore)(RedisModuleKey *key, RedisModuleString *ele, double *score); 209 | int REDISMODULE_API_FUNC(RedisModule_ZsetRem)(RedisModuleKey *key, RedisModuleString *ele, int *deleted); 210 | void REDISMODULE_API_FUNC(RedisModule_ZsetRangeStop)(RedisModuleKey *key); 211 | int REDISMODULE_API_FUNC(RedisModule_ZsetFirstInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex); 212 | int REDISMODULE_API_FUNC(RedisModule_ZsetLastInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex); 213 | int REDISMODULE_API_FUNC(RedisModule_ZsetFirstInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max); 214 | int REDISMODULE_API_FUNC(RedisModule_ZsetLastInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max); 215 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_ZsetRangeCurrentElement)(RedisModuleKey *key, double *score); 216 | int REDISMODULE_API_FUNC(RedisModule_ZsetRangeNext)(RedisModuleKey *key); 217 | int REDISMODULE_API_FUNC(RedisModule_ZsetRangePrev)(RedisModuleKey *key); 218 | int REDISMODULE_API_FUNC(RedisModule_ZsetRangeEndReached)(RedisModuleKey *key); 219 | int REDISMODULE_API_FUNC(RedisModule_HashSet)(RedisModuleKey *key, int flags, ...); 220 | int REDISMODULE_API_FUNC(RedisModule_HashGet)(RedisModuleKey *key, int flags, ...); 221 | int REDISMODULE_API_FUNC(RedisModule_IsKeysPositionRequest)(RedisModuleCtx *ctx); 222 | void REDISMODULE_API_FUNC(RedisModule_KeyAtPos)(RedisModuleCtx *ctx, int pos); 223 | unsigned long long REDISMODULE_API_FUNC(RedisModule_GetClientId)(RedisModuleCtx *ctx); 224 | int REDISMODULE_API_FUNC(RedisModule_GetContextFlags)(RedisModuleCtx *ctx); 225 | void *REDISMODULE_API_FUNC(RedisModule_PoolAlloc)(RedisModuleCtx *ctx, size_t bytes); 226 | RedisModuleType *REDISMODULE_API_FUNC(RedisModule_CreateDataType)(RedisModuleCtx *ctx, const char *name, int encver, RedisModuleTypeMethods *typemethods); 227 | int REDISMODULE_API_FUNC(RedisModule_ModuleTypeSetValue)(RedisModuleKey *key, RedisModuleType *mt, void *value); 228 | RedisModuleType *REDISMODULE_API_FUNC(RedisModule_ModuleTypeGetType)(RedisModuleKey *key); 229 | void *REDISMODULE_API_FUNC(RedisModule_ModuleTypeGetValue)(RedisModuleKey *key); 230 | void REDISMODULE_API_FUNC(RedisModule_SaveUnsigned)(RedisModuleIO *io, uint64_t value); 231 | uint64_t REDISMODULE_API_FUNC(RedisModule_LoadUnsigned)(RedisModuleIO *io); 232 | void REDISMODULE_API_FUNC(RedisModule_SaveSigned)(RedisModuleIO *io, int64_t value); 233 | int64_t REDISMODULE_API_FUNC(RedisModule_LoadSigned)(RedisModuleIO *io); 234 | void REDISMODULE_API_FUNC(RedisModule_EmitAOF)(RedisModuleIO *io, const char *cmdname, const char *fmt, ...); 235 | void REDISMODULE_API_FUNC(RedisModule_SaveString)(RedisModuleIO *io, RedisModuleString *s); 236 | void REDISMODULE_API_FUNC(RedisModule_SaveStringBuffer)(RedisModuleIO *io, const char *str, size_t len); 237 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_LoadString)(RedisModuleIO *io); 238 | char *REDISMODULE_API_FUNC(RedisModule_LoadStringBuffer)(RedisModuleIO *io, size_t *lenptr); 239 | void REDISMODULE_API_FUNC(RedisModule_SaveDouble)(RedisModuleIO *io, double value); 240 | double REDISMODULE_API_FUNC(RedisModule_LoadDouble)(RedisModuleIO *io); 241 | void REDISMODULE_API_FUNC(RedisModule_SaveFloat)(RedisModuleIO *io, float value); 242 | float REDISMODULE_API_FUNC(RedisModule_LoadFloat)(RedisModuleIO *io); 243 | void REDISMODULE_API_FUNC(RedisModule_Log)(RedisModuleCtx *ctx, const char *level, const char *fmt, ...); 244 | void REDISMODULE_API_FUNC(RedisModule_LogIOError)(RedisModuleIO *io, const char *levelstr, const char *fmt, ...); 245 | int REDISMODULE_API_FUNC(RedisModule_StringAppendBuffer)(RedisModuleCtx *ctx, RedisModuleString *str, const char *buf, size_t len); 246 | void REDISMODULE_API_FUNC(RedisModule_RetainString)(RedisModuleCtx *ctx, RedisModuleString *str); 247 | int REDISMODULE_API_FUNC(RedisModule_StringCompare)(RedisModuleString *a, RedisModuleString *b); 248 | RedisModuleCtx *REDISMODULE_API_FUNC(RedisModule_GetContextFromIO)(RedisModuleIO *io); 249 | long long REDISMODULE_API_FUNC(RedisModule_Milliseconds)(void); 250 | void REDISMODULE_API_FUNC(RedisModule_DigestAddStringBuffer)(RedisModuleDigest *md, unsigned char *ele, size_t len); 251 | void REDISMODULE_API_FUNC(RedisModule_DigestAddLongLong)(RedisModuleDigest *md, long long ele); 252 | void REDISMODULE_API_FUNC(RedisModule_DigestEndSequence)(RedisModuleDigest *md); 253 | 254 | /* Experimental APIs */ 255 | #ifdef REDISMODULE_EXPERIMENTAL_API 256 | RedisModuleBlockedClient *REDISMODULE_API_FUNC(RedisModule_BlockClient)(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(void*), long long timeout_ms); 257 | int REDISMODULE_API_FUNC(RedisModule_UnblockClient)(RedisModuleBlockedClient *bc, void *privdata); 258 | int REDISMODULE_API_FUNC(RedisModule_IsBlockedReplyRequest)(RedisModuleCtx *ctx); 259 | int REDISMODULE_API_FUNC(RedisModule_IsBlockedTimeoutRequest)(RedisModuleCtx *ctx); 260 | void *REDISMODULE_API_FUNC(RedisModule_GetBlockedClientPrivateData)(RedisModuleCtx *ctx); 261 | int REDISMODULE_API_FUNC(RedisModule_AbortBlock)(RedisModuleBlockedClient *bc); 262 | RedisModuleCtx *REDISMODULE_API_FUNC(RedisModule_GetThreadSafeContext)(RedisModuleBlockedClient *bc); 263 | void REDISMODULE_API_FUNC(RedisModule_FreeThreadSafeContext)(RedisModuleCtx *ctx); 264 | void REDISMODULE_API_FUNC(RedisModule_ThreadSafeContextLock)(RedisModuleCtx *ctx); 265 | void REDISMODULE_API_FUNC(RedisModule_ThreadSafeContextUnlock)(RedisModuleCtx *ctx); 266 | int REDISMODULE_API_FUNC(RedisModule_SubscribeToKeyspaceEvents)(RedisModuleCtx *ctx, int types, RedisModuleNotificationFunc cb); 267 | 268 | #endif 269 | 270 | /* This is included inline inside each Redis module. */ 271 | static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) __attribute__((unused)); 272 | static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) { 273 | void *getapifuncptr = ((void**)ctx)[0]; 274 | RedisModule_GetApi = (int (*)(const char *, void *)) (unsigned long)getapifuncptr; 275 | REDISMODULE_GET_API(Alloc); 276 | REDISMODULE_GET_API(Calloc); 277 | REDISMODULE_GET_API(Free); 278 | REDISMODULE_GET_API(Realloc); 279 | REDISMODULE_GET_API(Strdup); 280 | REDISMODULE_GET_API(CreateCommand); 281 | REDISMODULE_GET_API(SetModuleAttribs); 282 | REDISMODULE_GET_API(IsModuleNameBusy); 283 | REDISMODULE_GET_API(WrongArity); 284 | REDISMODULE_GET_API(ReplyWithLongLong); 285 | REDISMODULE_GET_API(ReplyWithError); 286 | REDISMODULE_GET_API(ReplyWithSimpleString); 287 | REDISMODULE_GET_API(ReplyWithArray); 288 | REDISMODULE_GET_API(ReplySetArrayLength); 289 | REDISMODULE_GET_API(ReplyWithStringBuffer); 290 | REDISMODULE_GET_API(ReplyWithString); 291 | REDISMODULE_GET_API(ReplyWithNull); 292 | REDISMODULE_GET_API(ReplyWithCallReply); 293 | REDISMODULE_GET_API(ReplyWithDouble); 294 | REDISMODULE_GET_API(ReplySetArrayLength); 295 | REDISMODULE_GET_API(GetSelectedDb); 296 | REDISMODULE_GET_API(SelectDb); 297 | REDISMODULE_GET_API(OpenKey); 298 | REDISMODULE_GET_API(CloseKey); 299 | REDISMODULE_GET_API(KeyType); 300 | REDISMODULE_GET_API(ValueLength); 301 | REDISMODULE_GET_API(ListPush); 302 | REDISMODULE_GET_API(ListPop); 303 | REDISMODULE_GET_API(StringToLongLong); 304 | REDISMODULE_GET_API(StringToDouble); 305 | REDISMODULE_GET_API(Call); 306 | REDISMODULE_GET_API(CallReplyProto); 307 | REDISMODULE_GET_API(FreeCallReply); 308 | REDISMODULE_GET_API(CallReplyInteger); 309 | REDISMODULE_GET_API(CallReplyType); 310 | REDISMODULE_GET_API(CallReplyLength); 311 | REDISMODULE_GET_API(CallReplyArrayElement); 312 | REDISMODULE_GET_API(CallReplyStringPtr); 313 | REDISMODULE_GET_API(CreateStringFromCallReply); 314 | REDISMODULE_GET_API(CreateString); 315 | REDISMODULE_GET_API(CreateStringFromLongLong); 316 | REDISMODULE_GET_API(CreateStringFromString); 317 | REDISMODULE_GET_API(CreateStringPrintf); 318 | REDISMODULE_GET_API(FreeString); 319 | REDISMODULE_GET_API(StringPtrLen); 320 | REDISMODULE_GET_API(AutoMemory); 321 | REDISMODULE_GET_API(Replicate); 322 | REDISMODULE_GET_API(ReplicateVerbatim); 323 | REDISMODULE_GET_API(DeleteKey); 324 | REDISMODULE_GET_API(UnlinkKey); 325 | REDISMODULE_GET_API(StringSet); 326 | REDISMODULE_GET_API(StringDMA); 327 | REDISMODULE_GET_API(StringTruncate); 328 | REDISMODULE_GET_API(GetExpire); 329 | REDISMODULE_GET_API(SetExpire); 330 | REDISMODULE_GET_API(ZsetAdd); 331 | REDISMODULE_GET_API(ZsetIncrby); 332 | REDISMODULE_GET_API(ZsetScore); 333 | REDISMODULE_GET_API(ZsetRem); 334 | REDISMODULE_GET_API(ZsetRangeStop); 335 | REDISMODULE_GET_API(ZsetFirstInScoreRange); 336 | REDISMODULE_GET_API(ZsetLastInScoreRange); 337 | REDISMODULE_GET_API(ZsetFirstInLexRange); 338 | REDISMODULE_GET_API(ZsetLastInLexRange); 339 | REDISMODULE_GET_API(ZsetRangeCurrentElement); 340 | REDISMODULE_GET_API(ZsetRangeNext); 341 | REDISMODULE_GET_API(ZsetRangePrev); 342 | REDISMODULE_GET_API(ZsetRangeEndReached); 343 | REDISMODULE_GET_API(HashSet); 344 | REDISMODULE_GET_API(HashGet); 345 | REDISMODULE_GET_API(IsKeysPositionRequest); 346 | REDISMODULE_GET_API(KeyAtPos); 347 | REDISMODULE_GET_API(GetClientId); 348 | REDISMODULE_GET_API(GetContextFlags); 349 | REDISMODULE_GET_API(PoolAlloc); 350 | REDISMODULE_GET_API(CreateDataType); 351 | REDISMODULE_GET_API(ModuleTypeSetValue); 352 | REDISMODULE_GET_API(ModuleTypeGetType); 353 | REDISMODULE_GET_API(ModuleTypeGetValue); 354 | REDISMODULE_GET_API(SaveUnsigned); 355 | REDISMODULE_GET_API(LoadUnsigned); 356 | REDISMODULE_GET_API(SaveSigned); 357 | REDISMODULE_GET_API(LoadSigned); 358 | REDISMODULE_GET_API(SaveString); 359 | REDISMODULE_GET_API(SaveStringBuffer); 360 | REDISMODULE_GET_API(LoadString); 361 | REDISMODULE_GET_API(LoadStringBuffer); 362 | REDISMODULE_GET_API(SaveDouble); 363 | REDISMODULE_GET_API(LoadDouble); 364 | REDISMODULE_GET_API(SaveFloat); 365 | REDISMODULE_GET_API(LoadFloat); 366 | REDISMODULE_GET_API(EmitAOF); 367 | REDISMODULE_GET_API(Log); 368 | REDISMODULE_GET_API(LogIOError); 369 | REDISMODULE_GET_API(StringAppendBuffer); 370 | REDISMODULE_GET_API(RetainString); 371 | REDISMODULE_GET_API(StringCompare); 372 | REDISMODULE_GET_API(GetContextFromIO); 373 | REDISMODULE_GET_API(Milliseconds); 374 | REDISMODULE_GET_API(DigestAddStringBuffer); 375 | REDISMODULE_GET_API(DigestAddLongLong); 376 | REDISMODULE_GET_API(DigestEndSequence); 377 | 378 | #ifdef REDISMODULE_EXPERIMENTAL_API 379 | REDISMODULE_GET_API(GetThreadSafeContext); 380 | REDISMODULE_GET_API(FreeThreadSafeContext); 381 | REDISMODULE_GET_API(ThreadSafeContextLock); 382 | REDISMODULE_GET_API(ThreadSafeContextUnlock); 383 | REDISMODULE_GET_API(BlockClient); 384 | REDISMODULE_GET_API(UnblockClient); 385 | REDISMODULE_GET_API(IsBlockedReplyRequest); 386 | REDISMODULE_GET_API(IsBlockedTimeoutRequest); 387 | REDISMODULE_GET_API(GetBlockedClientPrivateData); 388 | REDISMODULE_GET_API(AbortBlock); 389 | REDISMODULE_GET_API(SubscribeToKeyspaceEvents); 390 | 391 | #endif 392 | 393 | if (RedisModule_IsModuleNameBusy && RedisModule_IsModuleNameBusy(name)) return REDISMODULE_ERR; 394 | RedisModule_SetModuleAttribs(ctx,name,ver,apiver); 395 | return REDISMODULE_OK; 396 | } 397 | 398 | #else 399 | 400 | /* Things only defined for the modules core, not exported to modules 401 | * including this file. */ 402 | #define RedisModuleString robj 403 | 404 | #endif /* REDISMODULE_CORE */ 405 | #endif /* REDISMOUDLE_H */ 406 | -------------------------------------------------------------------------------- /5.0/redis-module-sdk.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #define REDISMODULE_XAPI_ENTERPRISE(X) \ 5 | X(int, AvoidReplicaTraffic, ()) \ 6 | X(int, Fork, (RedisModuleForkDoneHandler cb, void *user_data)) \ 7 | X(int, ExitFromChild, (int retcode)) \ 8 | X(int, KillForkChild, (int child_pid)) 9 | 10 | #define REDISMODULE_XAPI_EXTENSIONS(X) REDISMODULE_XAPI_ENTERPRISE(X) 11 | 12 | #include "RedisModuleSDK/5.0/redismodulex.h" 13 | -------------------------------------------------------------------------------- /5.0/redismodule.h: -------------------------------------------------------------------------------- 1 | #ifndef REDISMODULE_H 2 | #define REDISMODULE_H 3 | 4 | // clang-format off 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | /* ---------------- Defines common between core and modules --------------- */ 15 | 16 | /* Error status return values. */ 17 | #define REDISMODULE_OK 0 18 | #define REDISMODULE_ERR 1 19 | 20 | /* API versions. */ 21 | #define REDISMODULE_APIVER_1 1 22 | 23 | /* API flags and constants */ 24 | #define REDISMODULE_READ (1<<0) 25 | #define REDISMODULE_WRITE (1<<1) 26 | 27 | #define REDISMODULE_LIST_HEAD 0 28 | #define REDISMODULE_LIST_TAIL 1 29 | 30 | /* Key types. */ 31 | #define REDISMODULE_KEYTYPE_EMPTY 0 32 | #define REDISMODULE_KEYTYPE_STRING 1 33 | #define REDISMODULE_KEYTYPE_LIST 2 34 | #define REDISMODULE_KEYTYPE_HASH 3 35 | #define REDISMODULE_KEYTYPE_SET 4 36 | #define REDISMODULE_KEYTYPE_ZSET 5 37 | #define REDISMODULE_KEYTYPE_MODULE 6 38 | 39 | /* Reply types. */ 40 | #define REDISMODULE_REPLY_UNKNOWN -1 41 | #define REDISMODULE_REPLY_STRING 0 42 | #define REDISMODULE_REPLY_ERROR 1 43 | #define REDISMODULE_REPLY_INTEGER 2 44 | #define REDISMODULE_REPLY_ARRAY 3 45 | #define REDISMODULE_REPLY_NULL 4 46 | 47 | /* Postponed array length. */ 48 | #define REDISMODULE_POSTPONED_ARRAY_LEN -1 49 | 50 | /* Expire */ 51 | #define REDISMODULE_NO_EXPIRE -1 52 | 53 | /* Sorted set API flags. */ 54 | #define REDISMODULE_ZADD_XX (1<<0) 55 | #define REDISMODULE_ZADD_NX (1<<1) 56 | #define REDISMODULE_ZADD_ADDED (1<<2) 57 | #define REDISMODULE_ZADD_UPDATED (1<<3) 58 | #define REDISMODULE_ZADD_NOP (1<<4) 59 | 60 | /* Hash API flags. */ 61 | #define REDISMODULE_HASH_NONE 0 62 | #define REDISMODULE_HASH_NX (1<<0) 63 | #define REDISMODULE_HASH_XX (1<<1) 64 | #define REDISMODULE_HASH_CFIELDS (1<<2) 65 | #define REDISMODULE_HASH_EXISTS (1<<3) 66 | 67 | /* Context Flags: Info about the current context returned by 68 | * RM_GetContextFlags(). */ 69 | 70 | /* The command is running in the context of a Lua script */ 71 | #define REDISMODULE_CTX_FLAGS_LUA (1<<0) 72 | /* The command is running inside a Redis transaction */ 73 | #define REDISMODULE_CTX_FLAGS_MULTI (1<<1) 74 | /* The instance is a master */ 75 | #define REDISMODULE_CTX_FLAGS_MASTER (1<<2) 76 | /* The instance is a slave */ 77 | #define REDISMODULE_CTX_FLAGS_SLAVE (1<<3) 78 | /* The instance is read-only (usually meaning it's a slave as well) */ 79 | #define REDISMODULE_CTX_FLAGS_READONLY (1<<4) 80 | /* The instance is running in cluster mode */ 81 | #define REDISMODULE_CTX_FLAGS_CLUSTER (1<<5) 82 | /* The instance has AOF enabled */ 83 | #define REDISMODULE_CTX_FLAGS_AOF (1<<6) 84 | /* The instance has RDB enabled */ 85 | #define REDISMODULE_CTX_FLAGS_RDB (1<<7) 86 | /* The instance has Maxmemory set */ 87 | #define REDISMODULE_CTX_FLAGS_MAXMEMORY (1<<8) 88 | /* Maxmemory is set and has an eviction policy that may delete keys */ 89 | #define REDISMODULE_CTX_FLAGS_EVICT (1<<9) 90 | /* Redis is out of memory according to the maxmemory flag. */ 91 | #define REDISMODULE_CTX_FLAGS_OOM (1<<10) 92 | /* Less than 25% of memory available according to maxmemory. */ 93 | #define REDISMODULE_CTX_FLAGS_OOM_WARNING (1<<11) 94 | /* The command was sent over the replication link. */ 95 | #define REDISMODULE_CTX_FLAGS_REPLICATED (1<<12) 96 | /* Redis is currently loading either from AOF or RDB. */ 97 | #define REDISMODULE_CTX_FLAGS_LOADING (1<<13) 98 | /* The replica has no link with its master, note that 99 | * there is the inverse flag as well: 100 | * 101 | * REDISMODULE_CTX_FLAGS_REPLICA_IS_ONLINE 102 | * 103 | * The two flags are exclusive, one or the other can be set. */ 104 | #define REDISMODULE_CTX_FLAGS_REPLICA_IS_STALE (1<<14) 105 | /* The replica is trying to connect with the master. 106 | * (REPL_STATE_CONNECT and REPL_STATE_CONNECTING states) */ 107 | #define REDISMODULE_CTX_FLAGS_REPLICA_IS_CONNECTING (1<<15) 108 | /* THe replica is receiving an RDB file from its master. */ 109 | #define REDISMODULE_CTX_FLAGS_REPLICA_IS_TRANSFERRING (1<<16) 110 | /* The replica is online, receiving updates from its master. */ 111 | #define REDISMODULE_CTX_FLAGS_REPLICA_IS_ONLINE (1<<17) 112 | /* There is currently some background process active. */ 113 | #define REDISMODULE_CTX_FLAGS_ACTIVE_CHILD (1<<18) 114 | 115 | #define REDISMODULE_NOTIFY_GENERIC (1<<2) /* g */ 116 | #define REDISMODULE_NOTIFY_STRING (1<<3) /* $ */ 117 | #define REDISMODULE_NOTIFY_LIST (1<<4) /* l */ 118 | #define REDISMODULE_NOTIFY_SET (1<<5) /* s */ 119 | #define REDISMODULE_NOTIFY_HASH (1<<6) /* h */ 120 | #define REDISMODULE_NOTIFY_ZSET (1<<7) /* z */ 121 | #define REDISMODULE_NOTIFY_EXPIRED (1<<8) /* x */ 122 | #define REDISMODULE_NOTIFY_EVICTED (1<<9) /* e */ 123 | #define REDISMODULE_NOTIFY_STREAM (1<<10) /* t */ 124 | #define REDISMODULE_NOTIFY_ALL (REDISMODULE_NOTIFY_GENERIC | REDISMODULE_NOTIFY_STRING | REDISMODULE_NOTIFY_LIST | REDISMODULE_NOTIFY_SET | REDISMODULE_NOTIFY_HASH | REDISMODULE_NOTIFY_ZSET | REDISMODULE_NOTIFY_EXPIRED | REDISMODULE_NOTIFY_EVICTED | REDISMODULE_NOTIFY_STREAM) /* A */ 125 | 126 | 127 | /* A special pointer that we can use between the core and the module to signal 128 | * field deletion, and that is impossible to be a valid pointer. */ 129 | #define REDISMODULE_HASH_DELETE ((RedisModuleString*)(long)1) 130 | 131 | /* Error messages. */ 132 | #define REDISMODULE_ERRORMSG_WRONGTYPE "WRONGTYPE Operation against a key holding the wrong kind of value" 133 | 134 | #define REDISMODULE_POSITIVE_INFINITE (1.0/0.0) 135 | #define REDISMODULE_NEGATIVE_INFINITE (-1.0/0.0) 136 | 137 | /* Cluster API defines. */ 138 | #define REDISMODULE_NODE_ID_LEN 40 139 | #define REDISMODULE_NODE_MYSELF (1<<0) 140 | #define REDISMODULE_NODE_MASTER (1<<1) 141 | #define REDISMODULE_NODE_SLAVE (1<<2) 142 | #define REDISMODULE_NODE_PFAIL (1<<3) 143 | #define REDISMODULE_NODE_FAIL (1<<4) 144 | #define REDISMODULE_NODE_NOFAILOVER (1<<5) 145 | 146 | #define REDISMODULE_CLUSTER_FLAG_NONE 0 147 | #define REDISMODULE_CLUSTER_FLAG_NO_FAILOVER (1<<1) 148 | #define REDISMODULE_CLUSTER_FLAG_NO_REDIRECTION (1<<2) 149 | 150 | #define REDISMODULE_NOT_USED(V) ((void) V) 151 | 152 | /* Bit flags for aux_save_triggers and the aux_load and aux_save callbacks */ 153 | #define REDISMODULE_AUX_BEFORE_RDB (1<<0) 154 | #define REDISMODULE_AUX_AFTER_RDB (1<<1) 155 | 156 | /* This type represents a timer handle, and is returned when a timer is 157 | * registered and used in order to invalidate a timer. It's just a 64 bit 158 | * number, because this is how each timer is represented inside the radix tree 159 | * of timers that are going to expire, sorted by expire time. */ 160 | typedef uint64_t RedisModuleTimerID; 161 | 162 | /* CommandFilter Flags */ 163 | 164 | /* Do filter RedisModule_Call() commands initiated by module itself. */ 165 | #define REDISMODULE_CMDFILTER_NOSELF (1<<0) 166 | 167 | /* ------------------------- End of common defines ------------------------ */ 168 | 169 | #ifndef REDISMODULE_CORE 170 | 171 | typedef long long mstime_t; 172 | 173 | /* Incomplete structures for compiler checks but opaque access. */ 174 | typedef struct RedisModuleCtx RedisModuleCtx; 175 | typedef struct RedisModuleKey RedisModuleKey; 176 | typedef struct RedisModuleString RedisModuleString; 177 | typedef struct RedisModuleCallReply RedisModuleCallReply; 178 | typedef struct RedisModuleIO RedisModuleIO; 179 | typedef struct RedisModuleType RedisModuleType; 180 | typedef struct RedisModuleDigest RedisModuleDigest; 181 | typedef struct RedisModuleBlockedClient RedisModuleBlockedClient; 182 | typedef struct RedisModuleClusterInfo RedisModuleClusterInfo; 183 | typedef struct RedisModuleDict RedisModuleDict; 184 | typedef struct RedisModuleDictIter RedisModuleDictIter; 185 | typedef struct RedisModuleCommandFilterCtx RedisModuleCommandFilterCtx; 186 | typedef struct RedisModuleCommandFilter RedisModuleCommandFilter; 187 | 188 | typedef int (*RedisModuleCmdFunc)(RedisModuleCtx *ctx, RedisModuleString **argv, int argc); 189 | typedef void (*RedisModuleDisconnectFunc)(RedisModuleCtx *ctx, RedisModuleBlockedClient *bc); 190 | typedef int (*RedisModuleNotificationFunc)(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key); 191 | typedef void *(*RedisModuleTypeLoadFunc)(RedisModuleIO *rdb, int encver); 192 | typedef void (*RedisModuleTypeSaveFunc)(RedisModuleIO *rdb, void *value); 193 | typedef int (*RedisModuleTypeAuxLoadFunc)(RedisModuleIO *rdb, int encver, int when); 194 | typedef void (*RedisModuleTypeAuxSaveFunc)(RedisModuleIO *rdb, int when); 195 | typedef void (*RedisModuleTypeRewriteFunc)(RedisModuleIO *aof, RedisModuleString *key, void *value); 196 | typedef size_t (*RedisModuleTypeMemUsageFunc)(const void *value); 197 | typedef void (*RedisModuleTypeDigestFunc)(RedisModuleDigest *digest, void *value); 198 | typedef void (*RedisModuleTypeFreeFunc)(void *value); 199 | typedef void (*RedisModuleClusterMessageReceiver)(RedisModuleCtx *ctx, const char *sender_id, uint8_t type, const unsigned char *payload, uint32_t len); 200 | typedef void (*RedisModuleTimerProc)(RedisModuleCtx *ctx, void *data); 201 | typedef void (*RedisModuleCommandFilterFunc) (RedisModuleCommandFilterCtx *filter); 202 | 203 | #define REDISMODULE_TYPE_METHOD_VERSION 2 204 | typedef struct RedisModuleTypeMethods { 205 | uint64_t version; 206 | RedisModuleTypeLoadFunc rdb_load; 207 | RedisModuleTypeSaveFunc rdb_save; 208 | RedisModuleTypeRewriteFunc aof_rewrite; 209 | RedisModuleTypeMemUsageFunc mem_usage; 210 | RedisModuleTypeDigestFunc digest; 211 | RedisModuleTypeFreeFunc free; 212 | RedisModuleTypeAuxLoadFunc aux_load; 213 | RedisModuleTypeAuxSaveFunc aux_save; 214 | int aux_save_triggers; 215 | } RedisModuleTypeMethods; 216 | 217 | #define REDISMODULE_GET_API(name) \ 218 | RedisModule_GetApi("RedisModule_" #name, ((void **)&RedisModule_ ## name)) 219 | 220 | #define REDISMODULE_API_FUNC(x) (*x) 221 | 222 | 223 | void *REDISMODULE_API_FUNC(RedisModule_Alloc)(size_t bytes); 224 | void *REDISMODULE_API_FUNC(RedisModule_Realloc)(void *ptr, size_t bytes); 225 | void REDISMODULE_API_FUNC(RedisModule_Free)(void *ptr); 226 | void *REDISMODULE_API_FUNC(RedisModule_Calloc)(size_t nmemb, size_t size); 227 | char *REDISMODULE_API_FUNC(RedisModule_Strdup)(const char *str); 228 | int REDISMODULE_API_FUNC(RedisModule_GetApi)(const char *, void *); 229 | int REDISMODULE_API_FUNC(RedisModule_CreateCommand)(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep); 230 | void REDISMODULE_API_FUNC(RedisModule_SetModuleAttribs)(RedisModuleCtx *ctx, const char *name, int ver, int apiver); 231 | int REDISMODULE_API_FUNC(RedisModule_IsModuleNameBusy)(const char *name); 232 | int REDISMODULE_API_FUNC(RedisModule_WrongArity)(RedisModuleCtx *ctx); 233 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithLongLong)(RedisModuleCtx *ctx, long long ll); 234 | int REDISMODULE_API_FUNC(RedisModule_GetSelectedDb)(RedisModuleCtx *ctx); 235 | int REDISMODULE_API_FUNC(RedisModule_SelectDb)(RedisModuleCtx *ctx, int newid); 236 | void *REDISMODULE_API_FUNC(RedisModule_OpenKey)(RedisModuleCtx *ctx, RedisModuleString *keyname, int mode); 237 | void REDISMODULE_API_FUNC(RedisModule_CloseKey)(RedisModuleKey *kp); 238 | int REDISMODULE_API_FUNC(RedisModule_KeyType)(RedisModuleKey *kp); 239 | size_t REDISMODULE_API_FUNC(RedisModule_ValueLength)(RedisModuleKey *kp); 240 | int REDISMODULE_API_FUNC(RedisModule_ListPush)(RedisModuleKey *kp, int where, RedisModuleString *ele); 241 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_ListPop)(RedisModuleKey *key, int where); 242 | RedisModuleCallReply *REDISMODULE_API_FUNC(RedisModule_Call)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...); 243 | const char *REDISMODULE_API_FUNC(RedisModule_CallReplyProto)(RedisModuleCallReply *reply, size_t *len); 244 | void REDISMODULE_API_FUNC(RedisModule_FreeCallReply)(RedisModuleCallReply *reply); 245 | int REDISMODULE_API_FUNC(RedisModule_CallReplyType)(RedisModuleCallReply *reply); 246 | long long REDISMODULE_API_FUNC(RedisModule_CallReplyInteger)(RedisModuleCallReply *reply); 247 | size_t REDISMODULE_API_FUNC(RedisModule_CallReplyLength)(RedisModuleCallReply *reply); 248 | RedisModuleCallReply *REDISMODULE_API_FUNC(RedisModule_CallReplyArrayElement)(RedisModuleCallReply *reply, size_t idx); 249 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateString)(RedisModuleCtx *ctx, const char *ptr, size_t len); 250 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromLongLong)(RedisModuleCtx *ctx, long long ll); 251 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromString)(RedisModuleCtx *ctx, const RedisModuleString *str); 252 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringPrintf)(RedisModuleCtx *ctx, const char *fmt, ...); 253 | void REDISMODULE_API_FUNC(RedisModule_FreeString)(RedisModuleCtx *ctx, RedisModuleString *str); 254 | const char *REDISMODULE_API_FUNC(RedisModule_StringPtrLen)(const RedisModuleString *str, size_t *len); 255 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithError)(RedisModuleCtx *ctx, const char *err); 256 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithSimpleString)(RedisModuleCtx *ctx, const char *msg); 257 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithArray)(RedisModuleCtx *ctx, long len); 258 | void REDISMODULE_API_FUNC(RedisModule_ReplySetArrayLength)(RedisModuleCtx *ctx, long len); 259 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithStringBuffer)(RedisModuleCtx *ctx, const char *buf, size_t len); 260 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithCString)(RedisModuleCtx *ctx, const char *buf); 261 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithString)(RedisModuleCtx *ctx, RedisModuleString *str); 262 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithNull)(RedisModuleCtx *ctx); 263 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithDouble)(RedisModuleCtx *ctx, double d); 264 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithCallReply)(RedisModuleCtx *ctx, RedisModuleCallReply *reply); 265 | int REDISMODULE_API_FUNC(RedisModule_StringToLongLong)(const RedisModuleString *str, long long *ll); 266 | int REDISMODULE_API_FUNC(RedisModule_StringToDouble)(const RedisModuleString *str, double *d); 267 | void REDISMODULE_API_FUNC(RedisModule_AutoMemory)(RedisModuleCtx *ctx); 268 | int REDISMODULE_API_FUNC(RedisModule_Replicate)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...); 269 | int REDISMODULE_API_FUNC(RedisModule_ReplicateVerbatim)(RedisModuleCtx *ctx); 270 | const char *REDISMODULE_API_FUNC(RedisModule_CallReplyStringPtr)(RedisModuleCallReply *reply, size_t *len); 271 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromCallReply)(RedisModuleCallReply *reply); 272 | int REDISMODULE_API_FUNC(RedisModule_DeleteKey)(RedisModuleKey *key); 273 | int REDISMODULE_API_FUNC(RedisModule_UnlinkKey)(RedisModuleKey *key); 274 | int REDISMODULE_API_FUNC(RedisModule_StringSet)(RedisModuleKey *key, RedisModuleString *str); 275 | char *REDISMODULE_API_FUNC(RedisModule_StringDMA)(RedisModuleKey *key, size_t *len, int mode); 276 | int REDISMODULE_API_FUNC(RedisModule_StringTruncate)(RedisModuleKey *key, size_t newlen); 277 | mstime_t REDISMODULE_API_FUNC(RedisModule_GetExpire)(RedisModuleKey *key); 278 | int REDISMODULE_API_FUNC(RedisModule_SetExpire)(RedisModuleKey *key, mstime_t expire); 279 | int REDISMODULE_API_FUNC(RedisModule_ZsetAdd)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr); 280 | int REDISMODULE_API_FUNC(RedisModule_ZsetIncrby)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr, double *newscore); 281 | int REDISMODULE_API_FUNC(RedisModule_ZsetScore)(RedisModuleKey *key, RedisModuleString *ele, double *score); 282 | int REDISMODULE_API_FUNC(RedisModule_ZsetRem)(RedisModuleKey *key, RedisModuleString *ele, int *deleted); 283 | void REDISMODULE_API_FUNC(RedisModule_ZsetRangeStop)(RedisModuleKey *key); 284 | int REDISMODULE_API_FUNC(RedisModule_ZsetFirstInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex); 285 | int REDISMODULE_API_FUNC(RedisModule_ZsetLastInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex); 286 | int REDISMODULE_API_FUNC(RedisModule_ZsetFirstInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max); 287 | int REDISMODULE_API_FUNC(RedisModule_ZsetLastInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max); 288 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_ZsetRangeCurrentElement)(RedisModuleKey *key, double *score); 289 | int REDISMODULE_API_FUNC(RedisModule_ZsetRangeNext)(RedisModuleKey *key); 290 | int REDISMODULE_API_FUNC(RedisModule_ZsetRangePrev)(RedisModuleKey *key); 291 | int REDISMODULE_API_FUNC(RedisModule_ZsetRangeEndReached)(RedisModuleKey *key); 292 | int REDISMODULE_API_FUNC(RedisModule_HashSet)(RedisModuleKey *key, int flags, ...); 293 | int REDISMODULE_API_FUNC(RedisModule_HashGet)(RedisModuleKey *key, int flags, ...); 294 | int REDISMODULE_API_FUNC(RedisModule_IsKeysPositionRequest)(RedisModuleCtx *ctx); 295 | void REDISMODULE_API_FUNC(RedisModule_KeyAtPos)(RedisModuleCtx *ctx, int pos); 296 | unsigned long long REDISMODULE_API_FUNC(RedisModule_GetClientId)(RedisModuleCtx *ctx); 297 | int REDISMODULE_API_FUNC(RedisModule_GetContextFlags)(RedisModuleCtx *ctx); 298 | void *REDISMODULE_API_FUNC(RedisModule_PoolAlloc)(RedisModuleCtx *ctx, size_t bytes); 299 | RedisModuleType *REDISMODULE_API_FUNC(RedisModule_CreateDataType)(RedisModuleCtx *ctx, const char *name, int encver, RedisModuleTypeMethods *typemethods); 300 | int REDISMODULE_API_FUNC(RedisModule_ModuleTypeSetValue)(RedisModuleKey *key, RedisModuleType *mt, void *value); 301 | RedisModuleType *REDISMODULE_API_FUNC(RedisModule_ModuleTypeGetType)(RedisModuleKey *key); 302 | void *REDISMODULE_API_FUNC(RedisModule_ModuleTypeGetValue)(RedisModuleKey *key); 303 | void REDISMODULE_API_FUNC(RedisModule_SaveUnsigned)(RedisModuleIO *io, uint64_t value); 304 | uint64_t REDISMODULE_API_FUNC(RedisModule_LoadUnsigned)(RedisModuleIO *io); 305 | void REDISMODULE_API_FUNC(RedisModule_SaveSigned)(RedisModuleIO *io, int64_t value); 306 | int64_t REDISMODULE_API_FUNC(RedisModule_LoadSigned)(RedisModuleIO *io); 307 | void REDISMODULE_API_FUNC(RedisModule_EmitAOF)(RedisModuleIO *io, const char *cmdname, const char *fmt, ...); 308 | void REDISMODULE_API_FUNC(RedisModule_SaveString)(RedisModuleIO *io, RedisModuleString *s); 309 | void REDISMODULE_API_FUNC(RedisModule_SaveStringBuffer)(RedisModuleIO *io, const char *str, size_t len); 310 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_LoadString)(RedisModuleIO *io); 311 | char *REDISMODULE_API_FUNC(RedisModule_LoadStringBuffer)(RedisModuleIO *io, size_t *lenptr); 312 | void REDISMODULE_API_FUNC(RedisModule_SaveDouble)(RedisModuleIO *io, double value); 313 | double REDISMODULE_API_FUNC(RedisModule_LoadDouble)(RedisModuleIO *io); 314 | void REDISMODULE_API_FUNC(RedisModule_SaveFloat)(RedisModuleIO *io, float value); 315 | float REDISMODULE_API_FUNC(RedisModule_LoadFloat)(RedisModuleIO *io); 316 | void REDISMODULE_API_FUNC(RedisModule_Log)(RedisModuleCtx *ctx, const char *level, const char *fmt, ...); 317 | void REDISMODULE_API_FUNC(RedisModule_LogIOError)(RedisModuleIO *io, const char *levelstr, const char *fmt, ...); 318 | int REDISMODULE_API_FUNC(RedisModule_StringAppendBuffer)(RedisModuleCtx *ctx, RedisModuleString *str, const char *buf, size_t len); 319 | void REDISMODULE_API_FUNC(RedisModule_RetainString)(RedisModuleCtx *ctx, RedisModuleString *str); 320 | int REDISMODULE_API_FUNC(RedisModule_StringCompare)(RedisModuleString *a, RedisModuleString *b); 321 | RedisModuleCtx *REDISMODULE_API_FUNC(RedisModule_GetContextFromIO)(RedisModuleIO *io); 322 | const RedisModuleString *REDISMODULE_API_FUNC(RedisModule_GetKeyNameFromIO)(RedisModuleIO *io); 323 | long long REDISMODULE_API_FUNC(RedisModule_Milliseconds)(void); 324 | void REDISMODULE_API_FUNC(RedisModule_DigestAddStringBuffer)(RedisModuleDigest *md, unsigned char *ele, size_t len); 325 | void REDISMODULE_API_FUNC(RedisModule_DigestAddLongLong)(RedisModuleDigest *md, long long ele); 326 | void REDISMODULE_API_FUNC(RedisModule_DigestEndSequence)(RedisModuleDigest *md); 327 | RedisModuleDict *REDISMODULE_API_FUNC(RedisModule_CreateDict)(RedisModuleCtx *ctx); 328 | void REDISMODULE_API_FUNC(RedisModule_FreeDict)(RedisModuleCtx *ctx, RedisModuleDict *d); 329 | uint64_t REDISMODULE_API_FUNC(RedisModule_DictSize)(RedisModuleDict *d); 330 | int REDISMODULE_API_FUNC(RedisModule_DictSetC)(RedisModuleDict *d, void *key, size_t keylen, void *ptr); 331 | int REDISMODULE_API_FUNC(RedisModule_DictReplaceC)(RedisModuleDict *d, void *key, size_t keylen, void *ptr); 332 | int REDISMODULE_API_FUNC(RedisModule_DictSet)(RedisModuleDict *d, RedisModuleString *key, void *ptr); 333 | int REDISMODULE_API_FUNC(RedisModule_DictReplace)(RedisModuleDict *d, RedisModuleString *key, void *ptr); 334 | void *REDISMODULE_API_FUNC(RedisModule_DictGetC)(RedisModuleDict *d, void *key, size_t keylen, int *nokey); 335 | void *REDISMODULE_API_FUNC(RedisModule_DictGet)(RedisModuleDict *d, RedisModuleString *key, int *nokey); 336 | int REDISMODULE_API_FUNC(RedisModule_DictDelC)(RedisModuleDict *d, void *key, size_t keylen, void *oldval); 337 | int REDISMODULE_API_FUNC(RedisModule_DictDel)(RedisModuleDict *d, RedisModuleString *key, void *oldval); 338 | RedisModuleDictIter *REDISMODULE_API_FUNC(RedisModule_DictIteratorStartC)(RedisModuleDict *d, const char *op, void *key, size_t keylen); 339 | RedisModuleDictIter *REDISMODULE_API_FUNC(RedisModule_DictIteratorStart)(RedisModuleDict *d, const char *op, RedisModuleString *key); 340 | void REDISMODULE_API_FUNC(RedisModule_DictIteratorStop)(RedisModuleDictIter *di); 341 | int REDISMODULE_API_FUNC(RedisModule_DictIteratorReseekC)(RedisModuleDictIter *di, const char *op, void *key, size_t keylen); 342 | int REDISMODULE_API_FUNC(RedisModule_DictIteratorReseek)(RedisModuleDictIter *di, const char *op, RedisModuleString *key); 343 | void *REDISMODULE_API_FUNC(RedisModule_DictNextC)(RedisModuleDictIter *di, size_t *keylen, void **dataptr); 344 | void *REDISMODULE_API_FUNC(RedisModule_DictPrevC)(RedisModuleDictIter *di, size_t *keylen, void **dataptr); 345 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_DictNext)(RedisModuleCtx *ctx, RedisModuleDictIter *di, void **dataptr); 346 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_DictPrev)(RedisModuleCtx *ctx, RedisModuleDictIter *di, void **dataptr); 347 | int REDISMODULE_API_FUNC(RedisModule_DictCompareC)(RedisModuleDictIter *di, const char *op, void *key, size_t keylen); 348 | int REDISMODULE_API_FUNC(RedisModule_DictCompare)(RedisModuleDictIter *di, const char *op, RedisModuleString *key); 349 | 350 | /* Experimental APIs */ 351 | #ifdef REDISMODULE_EXPERIMENTAL_API 352 | #define REDISMODULE_EXPERIMENTAL_API_VERSION 3 353 | RedisModuleBlockedClient *REDISMODULE_API_FUNC(RedisModule_BlockClient)(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), long long timeout_ms); 354 | int REDISMODULE_API_FUNC(RedisModule_UnblockClient)(RedisModuleBlockedClient *bc, void *privdata); 355 | int REDISMODULE_API_FUNC(RedisModule_IsBlockedReplyRequest)(RedisModuleCtx *ctx); 356 | int REDISMODULE_API_FUNC(RedisModule_IsBlockedTimeoutRequest)(RedisModuleCtx *ctx); 357 | void *REDISMODULE_API_FUNC(RedisModule_GetBlockedClientPrivateData)(RedisModuleCtx *ctx); 358 | RedisModuleBlockedClient *REDISMODULE_API_FUNC(RedisModule_GetBlockedClientHandle)(RedisModuleCtx *ctx); 359 | int REDISMODULE_API_FUNC(RedisModule_AbortBlock)(RedisModuleBlockedClient *bc); 360 | RedisModuleCtx *REDISMODULE_API_FUNC(RedisModule_GetThreadSafeContext)(RedisModuleBlockedClient *bc); 361 | void REDISMODULE_API_FUNC(RedisModule_FreeThreadSafeContext)(RedisModuleCtx *ctx); 362 | void REDISMODULE_API_FUNC(RedisModule_ThreadSafeContextLock)(RedisModuleCtx *ctx); 363 | void REDISMODULE_API_FUNC(RedisModule_ThreadSafeContextUnlock)(RedisModuleCtx *ctx); 364 | int REDISMODULE_API_FUNC(RedisModule_SubscribeToKeyspaceEvents)(RedisModuleCtx *ctx, int types, RedisModuleNotificationFunc cb); 365 | int REDISMODULE_API_FUNC(RedisModule_BlockedClientDisconnected)(RedisModuleCtx *ctx); 366 | void REDISMODULE_API_FUNC(RedisModule_RegisterClusterMessageReceiver)(RedisModuleCtx *ctx, uint8_t type, RedisModuleClusterMessageReceiver callback); 367 | int REDISMODULE_API_FUNC(RedisModule_SendClusterMessage)(RedisModuleCtx *ctx, char *target_id, uint8_t type, unsigned char *msg, uint32_t len); 368 | int REDISMODULE_API_FUNC(RedisModule_GetClusterNodeInfo)(RedisModuleCtx *ctx, const char *id, char *ip, char *master_id, int *port, int *flags); 369 | char **REDISMODULE_API_FUNC(RedisModule_GetClusterNodesList)(RedisModuleCtx *ctx, size_t *numnodes); 370 | void REDISMODULE_API_FUNC(RedisModule_FreeClusterNodesList)(char **ids); 371 | RedisModuleTimerID REDISMODULE_API_FUNC(RedisModule_CreateTimer)(RedisModuleCtx *ctx, mstime_t period, RedisModuleTimerProc callback, void *data); 372 | int REDISMODULE_API_FUNC(RedisModule_StopTimer)(RedisModuleCtx *ctx, RedisModuleTimerID id, void **data); 373 | int REDISMODULE_API_FUNC(RedisModule_GetTimerInfo)(RedisModuleCtx *ctx, RedisModuleTimerID id, uint64_t *remaining, void **data); 374 | const char *REDISMODULE_API_FUNC(RedisModule_GetMyClusterID)(void); 375 | size_t REDISMODULE_API_FUNC(RedisModule_GetClusterSize)(void); 376 | void REDISMODULE_API_FUNC(RedisModule_GetRandomBytes)(unsigned char *dst, size_t len); 377 | void REDISMODULE_API_FUNC(RedisModule_GetRandomHexChars)(char *dst, size_t len); 378 | void REDISMODULE_API_FUNC(RedisModule_SetDisconnectCallback)(RedisModuleBlockedClient *bc, RedisModuleDisconnectFunc callback); 379 | void REDISMODULE_API_FUNC(RedisModule_SetClusterFlags)(RedisModuleCtx *ctx, uint64_t flags); 380 | int REDISMODULE_API_FUNC(RedisModule_ExportSharedAPI)(RedisModuleCtx *ctx, const char *apiname, void *func); 381 | void *REDISMODULE_API_FUNC(RedisModule_GetSharedAPI)(RedisModuleCtx *ctx, const char *apiname); 382 | RedisModuleCommandFilter *REDISMODULE_API_FUNC(RedisModule_RegisterCommandFilter)(RedisModuleCtx *ctx, RedisModuleCommandFilterFunc cb, int flags); 383 | int REDISMODULE_API_FUNC(RedisModule_UnregisterCommandFilter)(RedisModuleCtx *ctx, RedisModuleCommandFilter *filter); 384 | int REDISMODULE_API_FUNC(RedisModule_CommandFilterArgsCount)(RedisModuleCommandFilterCtx *fctx); 385 | const RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CommandFilterArgGet)(RedisModuleCommandFilterCtx *fctx, int pos); 386 | int REDISMODULE_API_FUNC(RedisModule_CommandFilterArgInsert)(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg); 387 | int REDISMODULE_API_FUNC(RedisModule_CommandFilterArgReplace)(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg); 388 | int REDISMODULE_API_FUNC(RedisModule_CommandFilterArgDelete)(RedisModuleCommandFilterCtx *fctx, int pos); 389 | #endif 390 | 391 | /* This is included inline inside each Redis module. */ 392 | static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) __attribute__((unused)); 393 | static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) { 394 | void *getapifuncptr = ((void**)ctx)[0]; 395 | RedisModule_GetApi = (int (*)(const char *, void *)) (unsigned long)getapifuncptr; 396 | REDISMODULE_GET_API(Alloc); 397 | REDISMODULE_GET_API(Calloc); 398 | REDISMODULE_GET_API(Free); 399 | REDISMODULE_GET_API(Realloc); 400 | REDISMODULE_GET_API(Strdup); 401 | REDISMODULE_GET_API(CreateCommand); 402 | REDISMODULE_GET_API(SetModuleAttribs); 403 | REDISMODULE_GET_API(IsModuleNameBusy); 404 | REDISMODULE_GET_API(WrongArity); 405 | REDISMODULE_GET_API(ReplyWithLongLong); 406 | REDISMODULE_GET_API(ReplyWithError); 407 | REDISMODULE_GET_API(ReplyWithSimpleString); 408 | REDISMODULE_GET_API(ReplyWithArray); 409 | REDISMODULE_GET_API(ReplySetArrayLength); 410 | REDISMODULE_GET_API(ReplyWithStringBuffer); 411 | REDISMODULE_GET_API(ReplyWithCString); 412 | REDISMODULE_GET_API(ReplyWithString); 413 | REDISMODULE_GET_API(ReplyWithNull); 414 | REDISMODULE_GET_API(ReplyWithCallReply); 415 | REDISMODULE_GET_API(ReplyWithDouble); 416 | REDISMODULE_GET_API(ReplySetArrayLength); 417 | REDISMODULE_GET_API(GetSelectedDb); 418 | REDISMODULE_GET_API(SelectDb); 419 | REDISMODULE_GET_API(OpenKey); 420 | REDISMODULE_GET_API(CloseKey); 421 | REDISMODULE_GET_API(KeyType); 422 | REDISMODULE_GET_API(ValueLength); 423 | REDISMODULE_GET_API(ListPush); 424 | REDISMODULE_GET_API(ListPop); 425 | REDISMODULE_GET_API(StringToLongLong); 426 | REDISMODULE_GET_API(StringToDouble); 427 | REDISMODULE_GET_API(Call); 428 | REDISMODULE_GET_API(CallReplyProto); 429 | REDISMODULE_GET_API(FreeCallReply); 430 | REDISMODULE_GET_API(CallReplyInteger); 431 | REDISMODULE_GET_API(CallReplyType); 432 | REDISMODULE_GET_API(CallReplyLength); 433 | REDISMODULE_GET_API(CallReplyArrayElement); 434 | REDISMODULE_GET_API(CallReplyStringPtr); 435 | REDISMODULE_GET_API(CreateStringFromCallReply); 436 | REDISMODULE_GET_API(CreateString); 437 | REDISMODULE_GET_API(CreateStringFromLongLong); 438 | REDISMODULE_GET_API(CreateStringFromString); 439 | REDISMODULE_GET_API(CreateStringPrintf); 440 | REDISMODULE_GET_API(FreeString); 441 | REDISMODULE_GET_API(StringPtrLen); 442 | REDISMODULE_GET_API(AutoMemory); 443 | REDISMODULE_GET_API(Replicate); 444 | REDISMODULE_GET_API(ReplicateVerbatim); 445 | REDISMODULE_GET_API(DeleteKey); 446 | REDISMODULE_GET_API(UnlinkKey); 447 | REDISMODULE_GET_API(StringSet); 448 | REDISMODULE_GET_API(StringDMA); 449 | REDISMODULE_GET_API(StringTruncate); 450 | REDISMODULE_GET_API(GetExpire); 451 | REDISMODULE_GET_API(SetExpire); 452 | REDISMODULE_GET_API(ZsetAdd); 453 | REDISMODULE_GET_API(ZsetIncrby); 454 | REDISMODULE_GET_API(ZsetScore); 455 | REDISMODULE_GET_API(ZsetRem); 456 | REDISMODULE_GET_API(ZsetRangeStop); 457 | REDISMODULE_GET_API(ZsetFirstInScoreRange); 458 | REDISMODULE_GET_API(ZsetLastInScoreRange); 459 | REDISMODULE_GET_API(ZsetFirstInLexRange); 460 | REDISMODULE_GET_API(ZsetLastInLexRange); 461 | REDISMODULE_GET_API(ZsetRangeCurrentElement); 462 | REDISMODULE_GET_API(ZsetRangeNext); 463 | REDISMODULE_GET_API(ZsetRangePrev); 464 | REDISMODULE_GET_API(ZsetRangeEndReached); 465 | REDISMODULE_GET_API(HashSet); 466 | REDISMODULE_GET_API(HashGet); 467 | REDISMODULE_GET_API(IsKeysPositionRequest); 468 | REDISMODULE_GET_API(KeyAtPos); 469 | REDISMODULE_GET_API(GetClientId); 470 | REDISMODULE_GET_API(GetContextFlags); 471 | REDISMODULE_GET_API(PoolAlloc); 472 | REDISMODULE_GET_API(CreateDataType); 473 | REDISMODULE_GET_API(ModuleTypeSetValue); 474 | REDISMODULE_GET_API(ModuleTypeGetType); 475 | REDISMODULE_GET_API(ModuleTypeGetValue); 476 | REDISMODULE_GET_API(SaveUnsigned); 477 | REDISMODULE_GET_API(LoadUnsigned); 478 | REDISMODULE_GET_API(SaveSigned); 479 | REDISMODULE_GET_API(LoadSigned); 480 | REDISMODULE_GET_API(SaveString); 481 | REDISMODULE_GET_API(SaveStringBuffer); 482 | REDISMODULE_GET_API(LoadString); 483 | REDISMODULE_GET_API(LoadStringBuffer); 484 | REDISMODULE_GET_API(SaveDouble); 485 | REDISMODULE_GET_API(LoadDouble); 486 | REDISMODULE_GET_API(SaveFloat); 487 | REDISMODULE_GET_API(LoadFloat); 488 | REDISMODULE_GET_API(EmitAOF); 489 | REDISMODULE_GET_API(Log); 490 | REDISMODULE_GET_API(LogIOError); 491 | REDISMODULE_GET_API(StringAppendBuffer); 492 | REDISMODULE_GET_API(RetainString); 493 | REDISMODULE_GET_API(StringCompare); 494 | REDISMODULE_GET_API(GetContextFromIO); 495 | REDISMODULE_GET_API(GetKeyNameFromIO); 496 | REDISMODULE_GET_API(Milliseconds); 497 | REDISMODULE_GET_API(DigestAddStringBuffer); 498 | REDISMODULE_GET_API(DigestAddLongLong); 499 | REDISMODULE_GET_API(DigestEndSequence); 500 | REDISMODULE_GET_API(CreateDict); 501 | REDISMODULE_GET_API(FreeDict); 502 | REDISMODULE_GET_API(DictSize); 503 | REDISMODULE_GET_API(DictSetC); 504 | REDISMODULE_GET_API(DictReplaceC); 505 | REDISMODULE_GET_API(DictSet); 506 | REDISMODULE_GET_API(DictReplace); 507 | REDISMODULE_GET_API(DictGetC); 508 | REDISMODULE_GET_API(DictGet); 509 | REDISMODULE_GET_API(DictDelC); 510 | REDISMODULE_GET_API(DictDel); 511 | REDISMODULE_GET_API(DictIteratorStartC); 512 | REDISMODULE_GET_API(DictIteratorStart); 513 | REDISMODULE_GET_API(DictIteratorStop); 514 | REDISMODULE_GET_API(DictIteratorReseekC); 515 | REDISMODULE_GET_API(DictIteratorReseek); 516 | REDISMODULE_GET_API(DictNextC); 517 | REDISMODULE_GET_API(DictPrevC); 518 | REDISMODULE_GET_API(DictNext); 519 | REDISMODULE_GET_API(DictPrev); 520 | REDISMODULE_GET_API(DictCompare); 521 | REDISMODULE_GET_API(DictCompareC); 522 | 523 | #ifdef REDISMODULE_EXPERIMENTAL_API 524 | REDISMODULE_GET_API(GetThreadSafeContext); 525 | REDISMODULE_GET_API(FreeThreadSafeContext); 526 | REDISMODULE_GET_API(ThreadSafeContextLock); 527 | REDISMODULE_GET_API(ThreadSafeContextUnlock); 528 | REDISMODULE_GET_API(BlockClient); 529 | REDISMODULE_GET_API(UnblockClient); 530 | REDISMODULE_GET_API(IsBlockedReplyRequest); 531 | REDISMODULE_GET_API(IsBlockedTimeoutRequest); 532 | REDISMODULE_GET_API(GetBlockedClientPrivateData); 533 | REDISMODULE_GET_API(GetBlockedClientHandle); 534 | REDISMODULE_GET_API(AbortBlock); 535 | REDISMODULE_GET_API(SetDisconnectCallback); 536 | REDISMODULE_GET_API(SubscribeToKeyspaceEvents); 537 | REDISMODULE_GET_API(BlockedClientDisconnected); 538 | REDISMODULE_GET_API(RegisterClusterMessageReceiver); 539 | REDISMODULE_GET_API(SendClusterMessage); 540 | REDISMODULE_GET_API(GetClusterNodeInfo); 541 | REDISMODULE_GET_API(GetClusterNodesList); 542 | REDISMODULE_GET_API(FreeClusterNodesList); 543 | REDISMODULE_GET_API(CreateTimer); 544 | REDISMODULE_GET_API(StopTimer); 545 | REDISMODULE_GET_API(GetTimerInfo); 546 | REDISMODULE_GET_API(GetMyClusterID); 547 | REDISMODULE_GET_API(GetClusterSize); 548 | REDISMODULE_GET_API(GetRandomBytes); 549 | REDISMODULE_GET_API(GetRandomHexChars); 550 | REDISMODULE_GET_API(SetClusterFlags); 551 | REDISMODULE_GET_API(ExportSharedAPI); 552 | REDISMODULE_GET_API(GetSharedAPI); 553 | REDISMODULE_GET_API(RegisterCommandFilter); 554 | REDISMODULE_GET_API(UnregisterCommandFilter); 555 | REDISMODULE_GET_API(CommandFilterArgsCount); 556 | REDISMODULE_GET_API(CommandFilterArgGet); 557 | REDISMODULE_GET_API(CommandFilterArgInsert); 558 | REDISMODULE_GET_API(CommandFilterArgReplace); 559 | REDISMODULE_GET_API(CommandFilterArgDelete); 560 | #endif 561 | 562 | if (RedisModule_IsModuleNameBusy && RedisModule_IsModuleNameBusy(name)) return REDISMODULE_ERR; 563 | RedisModule_SetModuleAttribs(ctx,name,ver,apiver); 564 | return REDISMODULE_OK; 565 | } 566 | 567 | #else 568 | 569 | /* Things only defined for the modules core, not exported to modules 570 | * including this file. */ 571 | #define RedisModuleString robj 572 | 573 | #endif /* REDISMODULE_CORE */ 574 | 575 | #ifdef __cplusplus 576 | } 577 | #endif 578 | 579 | #endif /* REDISMOUDLE_H */ 580 | -------------------------------------------------------------------------------- /5.0/redismodulex.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef REDISMODULE_H 3 | #define REDISMODULE_H 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | /* ---------------- Defines common between core and modules --------------- */ 14 | 15 | /* Error status return values. */ 16 | #define REDISMODULE_OK 0 17 | #define REDISMODULE_ERR 1 18 | 19 | /* API versions. */ 20 | #define REDISMODULE_APIVER_1 1 21 | 22 | /* API flags and constants */ 23 | #define REDISMODULE_READ (1<<0) 24 | #define REDISMODULE_WRITE (1<<1) 25 | 26 | #define REDISMODULE_LIST_HEAD 0 27 | #define REDISMODULE_LIST_TAIL 1 28 | 29 | /* Key types. */ 30 | #define REDISMODULE_KEYTYPE_EMPTY 0 31 | #define REDISMODULE_KEYTYPE_STRING 1 32 | #define REDISMODULE_KEYTYPE_LIST 2 33 | #define REDISMODULE_KEYTYPE_HASH 3 34 | #define REDISMODULE_KEYTYPE_SET 4 35 | #define REDISMODULE_KEYTYPE_ZSET 5 36 | #define REDISMODULE_KEYTYPE_MODULE 6 37 | 38 | /* Reply types. */ 39 | #define REDISMODULE_REPLY_UNKNOWN (-1) 40 | #define REDISMODULE_REPLY_STRING 0 41 | #define REDISMODULE_REPLY_ERROR 1 42 | #define REDISMODULE_REPLY_INTEGER 2 43 | #define REDISMODULE_REPLY_ARRAY 3 44 | #define REDISMODULE_REPLY_NULL 4 45 | 46 | /* Postponed array length. */ 47 | #define REDISMODULE_POSTPONED_ARRAY_LEN -1 48 | 49 | /* Expire */ 50 | #define REDISMODULE_NO_EXPIRE -1 51 | 52 | /* Sorted set API flags. */ 53 | #define REDISMODULE_ZADD_XX (1<<0) 54 | #define REDISMODULE_ZADD_NX (1<<1) 55 | #define REDISMODULE_ZADD_ADDED (1<<2) 56 | #define REDISMODULE_ZADD_UPDATED (1<<3) 57 | #define REDISMODULE_ZADD_NOP (1<<4) 58 | 59 | /* Hash API flags. */ 60 | #define REDISMODULE_HASH_NONE 0 61 | #define REDISMODULE_HASH_NX (1<<0) 62 | #define REDISMODULE_HASH_XX (1<<1) 63 | #define REDISMODULE_HASH_CFIELDS (1<<2) 64 | #define REDISMODULE_HASH_EXISTS (1<<3) 65 | 66 | /* Context Flags: Info about the current context returned by 67 | * RM_GetContextFlags(). */ 68 | 69 | /* The command is running in the context of a Lua script */ 70 | #define REDISMODULE_CTX_FLAGS_LUA (1<<0) 71 | /* The command is running inside a Redis transaction */ 72 | #define REDISMODULE_CTX_FLAGS_MULTI (1<<1) 73 | /* The instance is a master */ 74 | #define REDISMODULE_CTX_FLAGS_MASTER (1<<2) 75 | /* The instance is a slave */ 76 | #define REDISMODULE_CTX_FLAGS_SLAVE (1<<3) 77 | /* The instance is read-only (usually meaning it's a slave as well) */ 78 | #define REDISMODULE_CTX_FLAGS_READONLY (1<<4) 79 | /* The instance is running in cluster mode */ 80 | #define REDISMODULE_CTX_FLAGS_CLUSTER (1<<5) 81 | /* The instance has AOF enabled */ 82 | #define REDISMODULE_CTX_FLAGS_AOF (1<<6) 83 | /* The instance has RDB enabled */ 84 | #define REDISMODULE_CTX_FLAGS_RDB (1<<7) 85 | /* The instance has Maxmemory set */ 86 | #define REDISMODULE_CTX_FLAGS_MAXMEMORY (1<<8) 87 | /* Maxmemory is set and has an eviction policy that may delete keys */ 88 | #define REDISMODULE_CTX_FLAGS_EVICT (1<<9) 89 | /* Redis is out of memory according to the maxmemory flag. */ 90 | #define REDISMODULE_CTX_FLAGS_OOM (1<<10) 91 | /* Less than 25% of memory available according to maxmemory. */ 92 | #define REDISMODULE_CTX_FLAGS_OOM_WARNING (1<<11) 93 | /* The command was sent over the replication link. */ 94 | #define REDISMODULE_CTX_FLAGS_REPLICATED (1<<12) 95 | /* Redis is currently loading either from AOF or RDB. */ 96 | #define REDISMODULE_CTX_FLAGS_LOADING (1<<13) 97 | /* The replica has no link with its master, note that 98 | * there is the inverse flag as well: 99 | * 100 | * REDISMODULE_CTX_FLAGS_REPLICA_IS_ONLINE 101 | * 102 | * The two flags are exclusive, one or the other can be set. */ 103 | #define REDISMODULE_CTX_FLAGS_REPLICA_IS_STALE (1<<14) 104 | /* The replica is trying to connect with the master. 105 | * (REPL_STATE_CONNECT and REPL_STATE_CONNECTING states) */ 106 | #define REDISMODULE_CTX_FLAGS_REPLICA_IS_CONNECTING (1<<15) 107 | /* THe replica is receiving an RDB file from its master. */ 108 | #define REDISMODULE_CTX_FLAGS_REPLICA_IS_TRANSFERRING (1<<16) 109 | /* The replica is online, receiving updates from its master. */ 110 | #define REDISMODULE_CTX_FLAGS_REPLICA_IS_ONLINE (1<<17) 111 | /* There is currently some background process active. */ 112 | #define REDISMODULE_CTX_FLAGS_ACTIVE_CHILD (1<<18) 113 | 114 | #define REDISMODULE_NOTIFY_GENERIC (1<<2) /* g */ 115 | #define REDISMODULE_NOTIFY_STRING (1<<3) /* $ */ 116 | #define REDISMODULE_NOTIFY_LIST (1<<4) /* l */ 117 | #define REDISMODULE_NOTIFY_SET (1<<5) /* s */ 118 | #define REDISMODULE_NOTIFY_HASH (1<<6) /* h */ 119 | #define REDISMODULE_NOTIFY_ZSET (1<<7) /* z */ 120 | #define REDISMODULE_NOTIFY_EXPIRED (1<<8) /* x */ 121 | #define REDISMODULE_NOTIFY_EVICTED (1<<9) /* e */ 122 | #define REDISMODULE_NOTIFY_STREAM (1<<10) /* t */ 123 | #define REDISMODULE_NOTIFY_ALL (REDISMODULE_NOTIFY_GENERIC | REDISMODULE_NOTIFY_STRING | REDISMODULE_NOTIFY_LIST | REDISMODULE_NOTIFY_SET | REDISMODULE_NOTIFY_HASH | REDISMODULE_NOTIFY_ZSET | REDISMODULE_NOTIFY_EXPIRED | REDISMODULE_NOTIFY_EVICTED | REDISMODULE_NOTIFY_STREAM) /* A */ 124 | 125 | 126 | /* A special pointer that we can use between the core and the module to signal 127 | * field deletion, and that is impossible to be a valid pointer. */ 128 | #define REDISMODULE_HASH_DELETE ((RedisModuleString*)(long)1) 129 | 130 | /* Error messages. */ 131 | #define REDISMODULE_ERRORMSG_WRONGTYPE "WRONGTYPE Operation against a key holding the wrong kind of value" 132 | 133 | #define REDISMODULE_POSITIVE_INFINITE (1.0/0.0) 134 | #define REDISMODULE_NEGATIVE_INFINITE (-1.0/0.0) 135 | 136 | /* Cluster API defines. */ 137 | #define REDISMODULE_NODE_ID_LEN 40 138 | #define REDISMODULE_NODE_MYSELF (1<<0) 139 | #define REDISMODULE_NODE_MASTER (1<<1) 140 | #define REDISMODULE_NODE_SLAVE (1<<2) 141 | #define REDISMODULE_NODE_PFAIL (1<<3) 142 | #define REDISMODULE_NODE_FAIL (1<<4) 143 | #define REDISMODULE_NODE_NOFAILOVER (1<<5) 144 | 145 | #define REDISMODULE_CLUSTER_FLAG_NONE 0 146 | #define REDISMODULE_CLUSTER_FLAG_NO_FAILOVER (1<<1) 147 | #define REDISMODULE_CLUSTER_FLAG_NO_REDIRECTION (1<<2) 148 | 149 | #define REDISMODULE_NOT_USED(V) ((void) V) 150 | 151 | /* Bit flags for aux_save_triggers and the aux_load and aux_save callbacks */ 152 | #define REDISMODULE_AUX_BEFORE_RDB (1<<0) 153 | #define REDISMODULE_AUX_AFTER_RDB (1<<1) 154 | 155 | /* This type represents a timer handle, and is returned when a timer is 156 | * registered and used in order to invalidate a timer. It's just a 64 bit 157 | * number, because this is how each timer is represented inside the radix tree 158 | * of timers that are going to expire, sorted by expire time. */ 159 | typedef uint64_t RedisModuleTimerID; 160 | 161 | /* CommandFilter Flags */ 162 | 163 | /* Do filter RedisModule_Call() commands initiated by module itself. */ 164 | #define REDISMODULE_CMDFILTER_NOSELF (1<<0) 165 | 166 | /* ------------------------- End of common defines ------------------------ */ 167 | 168 | #ifndef REDISMODULE_CORE 169 | 170 | typedef long long mstime_t; 171 | 172 | /* Incomplete structures for compiler checks but opaque access. */ 173 | typedef struct RedisModuleCtx RedisModuleCtx; 174 | typedef struct RedisModuleKey RedisModuleKey; 175 | typedef struct RedisModuleString RedisModuleString; 176 | typedef struct RedisModuleCallReply RedisModuleCallReply; 177 | typedef struct RedisModuleIO RedisModuleIO; 178 | typedef struct RedisModuleType RedisModuleType; 179 | typedef struct RedisModuleDigest RedisModuleDigest; 180 | typedef struct RedisModuleBlockedClient RedisModuleBlockedClient; 181 | typedef struct RedisModuleClusterInfo RedisModuleClusterInfo; 182 | typedef struct RedisModuleDict RedisModuleDict; 183 | typedef struct RedisModuleDictIter RedisModuleDictIter; 184 | typedef struct RedisModuleCommandFilterCtx RedisModuleCommandFilterCtx; 185 | typedef struct RedisModuleCommandFilter RedisModuleCommandFilter; 186 | 187 | typedef int (*RedisModuleCmdFunc)(RedisModuleCtx *ctx, RedisModuleString **argv, int argc); 188 | typedef void (*RedisModuleDisconnectFunc)(RedisModuleCtx *ctx, RedisModuleBlockedClient *bc); 189 | typedef int (*RedisModuleNotificationFunc)(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key); 190 | typedef void *(*RedisModuleTypeLoadFunc)(RedisModuleIO *rdb, int encver); 191 | typedef void (*RedisModuleTypeSaveFunc)(RedisModuleIO *rdb, void *value); 192 | typedef int (*RedisModuleTypeAuxLoadFunc)(RedisModuleIO *rdb, int encver, int when); 193 | typedef void (*RedisModuleTypeAuxSaveFunc)(RedisModuleIO *rdb, int when); 194 | typedef void (*RedisModuleTypeRewriteFunc)(RedisModuleIO *aof, RedisModuleString *key, void *value); 195 | typedef size_t (*RedisModuleTypeMemUsageFunc)(const void *value); 196 | typedef void (*RedisModuleTypeDigestFunc)(RedisModuleDigest *digest, void *value); 197 | typedef void (*RedisModuleTypeFreeFunc)(void *value); 198 | typedef void (*RedisModuleClusterMessageReceiver)(RedisModuleCtx *ctx, const char *sender_id, uint8_t type, const unsigned char *payload, uint32_t len); 199 | typedef void (*RedisModuleTimerProc)(RedisModuleCtx *ctx, void *data); 200 | typedef void (*RedisModuleCommandFilterFunc)(RedisModuleCommandFilterCtx *filter); 201 | 202 | #define REDISMODULE_TYPE_METHOD_VERSION 2 203 | typedef struct RedisModuleTypeMethods { 204 | uint64_t version; 205 | RedisModuleTypeLoadFunc rdb_load; 206 | RedisModuleTypeSaveFunc rdb_save; 207 | RedisModuleTypeRewriteFunc aof_rewrite; 208 | RedisModuleTypeMemUsageFunc mem_usage; 209 | RedisModuleTypeDigestFunc digest; 210 | RedisModuleTypeFreeFunc free; 211 | RedisModuleTypeAuxLoadFunc aux_load; 212 | RedisModuleTypeAuxSaveFunc aux_save; 213 | int aux_save_triggers; 214 | } RedisModuleTypeMethods; 215 | 216 | #define REDISMODULE_XAPI_STABLE(X) \ 217 | X(void *, Alloc, (size_t bytes)) \ 218 | X(void *, Realloc, (void *ptr, size_t bytes)) \ 219 | X(void,Free, (void *ptr)) \ 220 | X(void *, Calloc, (size_t nmemb, size_t size)) \ 221 | X(char *, Strdup, (const char *str)) \ 222 | X(int, GetApi, (const char *, void *)) \ 223 | X(int, CreateCommand, (RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep)) \ 224 | X(void, SetModuleAttribs, (RedisModuleCtx *ctx, const char *name, int ver, int apiver)) \ 225 | X(int, IsModuleNameBusy, (const char *name)) \ 226 | X(int, WrongArity, (RedisModuleCtx *ctx)) \ 227 | X(int, ReplyWithLongLong, (RedisModuleCtx *ctx, long long ll)) \ 228 | X(int, GetSelectedDb, (RedisModuleCtx *ctx)) \ 229 | X(int, SelectDb, (RedisModuleCtx *ctx, int newid)) \ 230 | X(RedisModuleKey *, OpenKey, (RedisModuleCtx *ctx, RedisModuleString *keyname, int mode)) \ 231 | X(void, CloseKey, (RedisModuleKey *kp)) \ 232 | X(int, KeyType, (RedisModuleKey *kp)) \ 233 | X(size_t, ValueLength, (RedisModuleKey *kp)) \ 234 | X(int, ListPush, (RedisModuleKey *kp, int where, RedisModuleString *ele)) \ 235 | X(RedisModuleString *, ListPop, (RedisModuleKey *key, int where)) \ 236 | X(RedisModuleCallReply *, Call, (RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...)) \ 237 | X(const char *, CallReplyProto, (RedisModuleCallReply *reply, size_t *len)) \ 238 | X(void, FreeCallReply, (RedisModuleCallReply *reply)) \ 239 | X(int, CallReplyType, (RedisModuleCallReply *reply)) \ 240 | X(long long, CallReplyInteger, (RedisModuleCallReply *reply)) \ 241 | X(size_t, CallReplyLength, (RedisModuleCallReply *reply)) \ 242 | X(RedisModuleCallReply *, CallReplyArrayElement, (RedisModuleCallReply *reply, size_t idx)) \ 243 | X(RedisModuleString *, CreateString, (RedisModuleCtx *ctx, const char *ptr, size_t len)) \ 244 | X(RedisModuleString *, CreateStringFromLongLong, (RedisModuleCtx *ctx, long long ll)) \ 245 | X(RedisModuleString *, CreateStringFromString, (RedisModuleCtx *ctx, const RedisModuleString *str)) \ 246 | X(RedisModuleString *, CreateStringPrintf, (RedisModuleCtx *ctx, const char *fmt, ...)) \ 247 | X(void, FreeString, (RedisModuleCtx *ctx, RedisModuleString *str)) \ 248 | X(const char *, StringPtrLen, (const RedisModuleString *str, size_t *len)) \ 249 | X(int, ReplyWithError, (RedisModuleCtx *ctx, const char *err)) \ 250 | X(int, ReplyWithSimpleString, (RedisModuleCtx *ctx, const char *msg)) \ 251 | X(int, ReplyWithArray, (RedisModuleCtx *ctx, long len)) \ 252 | X(void, ReplySetArrayLength, (RedisModuleCtx *ctx, long len)) \ 253 | X(int, ReplyWithStringBuffer, (RedisModuleCtx *ctx, const char *buf, size_t len)) \ 254 | X(int, ReplyWithCString, (RedisModuleCtx *ctx, const char *buf)) \ 255 | X(int, ReplyWithString, (RedisModuleCtx *ctx, RedisModuleString *str)) \ 256 | X(int, ReplyWithNull, (RedisModuleCtx *ctx)) \ 257 | X(int, ReplyWithDouble, (RedisModuleCtx *ctx, double d)) \ 258 | X(int, ReplyWithCallReply, (RedisModuleCtx *ctx, RedisModuleCallReply *reply)) \ 259 | X(int, StringToLongLong, (const RedisModuleString *str, long long *ll)) \ 260 | X(int, StringToDouble, (const RedisModuleString *str, double *d)) \ 261 | X(void, AutoMemory, (RedisModuleCtx *ctx)) \ 262 | X(int, Replicate, (RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...)) \ 263 | X(int, ReplicateVerbatim, (RedisModuleCtx *ctx)) \ 264 | X(const char *, CallReplyStringPtr, (RedisModuleCallReply *reply, size_t *len)) \ 265 | X(RedisModuleString *, CreateStringFromCallReply, (RedisModuleCallReply *reply)) \ 266 | X(int, DeleteKey, (RedisModuleKey *key)) \ 267 | X(int, UnlinkKey, (RedisModuleKey *key)) \ 268 | X(int, StringSet, (RedisModuleKey *key, RedisModuleString *str)) \ 269 | X(char *, StringDMA, (RedisModuleKey *key, size_t *len, int mode)) \ 270 | X(int, StringTruncate, (RedisModuleKey *key, size_t newlen)) \ 271 | X(mstime_t, GetExpire, (RedisModuleKey *key)) \ 272 | X(int, SetExpire, (RedisModuleKey *key, mstime_t expire)) \ 273 | X(int, ZsetAdd, (RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr)) \ 274 | X(int, ZsetIncrby, (RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr, double *newscore)) \ 275 | X(int, ZsetScore, (RedisModuleKey *key, RedisModuleString *ele, double *score)) \ 276 | X(int, ZsetRem, (RedisModuleKey *key, RedisModuleString *ele, int *deleted)) \ 277 | X(void ,ZsetRangeStop, (RedisModuleKey *key)) \ 278 | X(int, ZsetFirstInScoreRange, (RedisModuleKey *key, double min, double max, int minex, int maxex)) \ 279 | X(int, ZsetLastInScoreRange, (RedisModuleKey *key, double min, double max, int minex, int maxex)) \ 280 | X(int, ZsetFirstInLexRange, (RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max)) \ 281 | X(int, ZsetLastInLexRange, (RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max)) \ 282 | X(RedisModuleString *, ZsetRangeCurrentElement, (RedisModuleKey *key, double *score)) \ 283 | X(int, ZsetRangeNext, (RedisModuleKey *key)) \ 284 | X(int, ZsetRangePrev, (RedisModuleKey *key)) \ 285 | X(int, ZsetRangeEndReached, (RedisModuleKey *key)) \ 286 | X(int, HashSet, (RedisModuleKey *key, int flags, ...)) \ 287 | X(int, HashGet, (RedisModuleKey *key, int flags, ...)) \ 288 | X(int, IsKeysPositionRequest, (RedisModuleCtx *ctx)) \ 289 | X(void, KeyAtPos, (RedisModuleCtx *ctx, int pos)) \ 290 | X(unsigned long long, GetClientId, (RedisModuleCtx *ctx)) \ 291 | X(int, GetContextFlags, (RedisModuleCtx *ctx)) \ 292 | X(void *, PoolAlloc, (RedisModuleCtx *ctx, size_t bytes)) \ 293 | X(RedisModuleType *, CreateDataType, (RedisModuleCtx *ctx, const char *name, int encver, RedisModuleTypeMethods *typemethods)) \ 294 | X(int,ModuleTypeSetValue, (RedisModuleKey *key, RedisModuleType *mt, void *value)) \ 295 | X(RedisModuleType *, ModuleTypeGetType, (RedisModuleKey *key)) \ 296 | X(void *, ModuleTypeGetValue, (RedisModuleKey *key)) \ 297 | X(void, SaveUnsigned, (RedisModuleIO *io, uint64_t value)) \ 298 | X(uint64_t, LoadUnsigned, (RedisModuleIO *io)) \ 299 | X(void, SaveSigned, (RedisModuleIO *io, int64_t value)) \ 300 | X(int64_t, LoadSigned, (RedisModuleIO *io)) \ 301 | X(void, EmitAOF, (RedisModuleIO *io, const char *cmdname, const char *fmt, ...)) \ 302 | X(void, SaveString, (RedisModuleIO *io, RedisModuleString *s)) \ 303 | X(void, SaveStringBuffer, (RedisModuleIO *io, const char *str, size_t len)) \ 304 | X(RedisModuleString *, LoadString, (RedisModuleIO *io)) \ 305 | X(char *, LoadStringBuffer, (RedisModuleIO *io, size_t *lenptr)) \ 306 | X(void, SaveDouble, (RedisModuleIO *io, double value)) \ 307 | X(double, LoadDouble, (RedisModuleIO *io)) \ 308 | X(void, SaveFloat, (RedisModuleIO *io, float value)) \ 309 | X(float, LoadFloat, (RedisModuleIO *io)) \ 310 | X(void, Log, (RedisModuleCtx *ctx, const char *level, const char *fmt, ...)) \ 311 | X(void, LogIOError, (RedisModuleIO *io, const char *levelstr, const char *fmt, ...)) \ 312 | X(int, StringAppendBuffer, (RedisModuleCtx *ctx, RedisModuleString *str, const char *buf, size_t len)) \ 313 | X(void, RetainString, (RedisModuleCtx *ctx, RedisModuleString *str)) \ 314 | X(int, StringCompare, (RedisModuleString *a, RedisModuleString *b)) \ 315 | X(RedisModuleCtx *, GetContextFromIO, (RedisModuleIO *io)) \ 316 | X(const RedisModuleString *, GetKeyNameFromIO, (RedisModuleIO *io)) \ 317 | X(long long, Milliseconds, (void)) \ 318 | X(void, DigestAddStringBuffer, (RedisModuleDigest *md, unsigned char *ele, size_t len)) \ 319 | X(void, DigestAddLongLong, (RedisModuleDigest *md, long long ele)) \ 320 | X(void, DigestEndSequence, (RedisModuleDigest *md)) \ 321 | X(RedisModuleDict *, CreateDict, (RedisModuleCtx *ctx)) \ 322 | X(void, FreeDict, (RedisModuleCtx *ctx, RedisModuleDict *d)) \ 323 | X(uint64_t, DictSize, (RedisModuleDict *d)) \ 324 | X(int, DictSetC, (RedisModuleDict *d, void *key, size_t keylen, void *ptr)) \ 325 | X(int, DictReplaceC, (RedisModuleDict *d, void *key, size_t keylen, void *ptr)) \ 326 | X(int, DictSet, (RedisModuleDict *d, RedisModuleString *key, void *ptr)) \ 327 | X(int, DictReplace, (RedisModuleDict *d, RedisModuleString *key, void *ptr)) \ 328 | X(void *, DictGetC, (RedisModuleDict *d, void *key, size_t keylen, int *nokey)) \ 329 | X(void *, DictGet, (RedisModuleDict *d, RedisModuleString *key, int *nokey)) \ 330 | X(int, DictDelC, (RedisModuleDict *d, void *key, size_t keylen, void *oldval)) \ 331 | X(int, DictDel, (RedisModuleDict *d, RedisModuleString *key, void *oldval)) \ 332 | X(RedisModuleDictIter *, DictIteratorStartC, (RedisModuleDict *d, const char *op, void *key, size_t keylen)) \ 333 | X(RedisModuleDictIter *, DictIteratorStart, (RedisModuleDict *d, const char *op, RedisModuleString *key)) \ 334 | X(void, DictIteratorStop, (RedisModuleDictIter *di)) \ 335 | X(int, DictIteratorReseekC, (RedisModuleDictIter *di, const char *op, void *key, size_t keylen)) \ 336 | X(int, DictIteratorReseek, (RedisModuleDictIter *di, const char *op, RedisModuleString *key)) \ 337 | X(void *, DictNextC, (RedisModuleDictIter *di, size_t *keylen, void **dataptr)) \ 338 | X(void *, DictPrevC, (RedisModuleDictIter *di, size_t *keylen, void **dataptr)) \ 339 | X(RedisModuleString *, DictNext, (RedisModuleCtx *ctx, RedisModuleDictIter *di, void **dataptr)) \ 340 | X(RedisModuleString *, DictPrev, (RedisModuleCtx *ctx, RedisModuleDictIter *di, void **dataptr)) \ 341 | X(int, DictCompareC, (RedisModuleDictIter *di, const char *op, void *key, size_t keylen)) \ 342 | X(int, DictCompare, (RedisModuleDictIter *di, const char *op, RedisModuleString *key)) 343 | 344 | /* Experimental APIs */ 345 | 346 | #define REDISMODULE_EXPERIMENTAL_API_VERSION 3 347 | 348 | #define REDISMODULE_XAPI_EXPERIMENTAL(X) \ 349 | X(RedisModuleBlockedClient *, BlockClient, (RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), long long timeout_ms)) \ 350 | X(int, UnblockClient, (RedisModuleBlockedClient *bc, void *privdata)) \ 351 | X(int, IsBlockedReplyRequest, (RedisModuleCtx *ctx)) \ 352 | X(int, IsBlockedTimeoutRequest, (RedisModuleCtx *ctx)) \ 353 | X(void *, GetBlockedClientPrivateData, (RedisModuleCtx *ctx)) \ 354 | X(RedisModuleBlockedClient *, GetBlockedClientHandle, (RedisModuleCtx *ctx)) \ 355 | X(int, AbortBlock, (RedisModuleBlockedClient *bc)) \ 356 | X(RedisModuleCtx *, GetThreadSafeContext, (RedisModuleBlockedClient *bc)) \ 357 | X(void, FreeThreadSafeContext, (RedisModuleCtx *ctx)) \ 358 | X(void, ThreadSafeContextLock, (RedisModuleCtx *ctx)) \ 359 | X(void, ThreadSafeContextUnlock, (RedisModuleCtx *ctx)) \ 360 | X(int, SubscribeToKeyspaceEvents, (RedisModuleCtx *ctx, int types, RedisModuleNotificationFunc cb)) \ 361 | X(int, BlockedClientDisconnected, (RedisModuleCtx *ctx)) \ 362 | X(void, RegisterClusterMessageReceiver, (RedisModuleCtx *ctx, uint8_t type, RedisModuleClusterMessageReceiver callback)) \ 363 | X(int, SendClusterMessage, (RedisModuleCtx *ctx, char *target_id, uint8_t type, unsigned char *msg, uint32_t len)) \ 364 | X(int, GetClusterNodeInfo, (RedisModuleCtx *ctx, const char *id, char *ip, char *master_id, int *port, int *flags)) \ 365 | X(char **, GetClusterNodesList, (RedisModuleCtx *ctx, size_t *numnodes)) \ 366 | X(void, FreeClusterNodesList, (char **ids)) \ 367 | X(RedisModuleTimerID, CreateTimer, (RedisModuleCtx *ctx, mstime_t period, RedisModuleTimerProc callback, void *data)) \ 368 | X(int, StopTimer, (RedisModuleCtx *ctx, RedisModuleTimerID id, void **data)) \ 369 | X(int, GetTimerInfo, (RedisModuleCtx *ctx, RedisModuleTimerID id, uint64_t *remaining, void **data)) \ 370 | X(const char *, GetMyClusterID, (void)) \ 371 | X(size_t, GetClusterSize, (void)) \ 372 | X(void, GetRandomBytes, (unsigned char *dst, size_t len)) \ 373 | X(void, GetRandomHexChars, (char *dst, size_t len)) \ 374 | X(void, SetDisconnectCallback, (RedisModuleBlockedClient *bc, RedisModuleDisconnectFunc callback)) \ 375 | X(void, SetClusterFlags, (RedisModuleCtx *ctx, uint64_t flags)) \ 376 | X(int, ExportSharedAPI, (RedisModuleCtx *ctx, const char *apiname, void *func)) \ 377 | X(void *, GetSharedAPI, (RedisModuleCtx *ctx, const char *apiname)) \ 378 | X(RedisModuleCommandFilter *, RegisterCommandFilter, (RedisModuleCtx *ctx, RedisModuleCommandFilterFunc cb, int flags)) \ 379 | X(int, UnregisterCommandFilter, (RedisModuleCtx *ctx, RedisModuleCommandFilter *filter)) \ 380 | X(int, CommandFilterArgsCount, (RedisModuleCommandFilterCtx *fctx)) \ 381 | X(const RedisModuleString *, CommandFilterArgGet, (RedisModuleCommandFilterCtx *fctx, int pos)) \ 382 | X(int, CommandFilterArgInsert, (RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg)) \ 383 | X(int, CommandFilterArgReplace, (RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg)) \ 384 | X(int, CommandFilterArgDelete, (RedisModuleCommandFilterCtx *fctx, int pos)) 385 | 386 | #ifndef REDISMODULE_XAPI_EXTENSIONS 387 | #define REDISMODULE_XAPI_EXTENSIONS(X) 388 | #endif 389 | 390 | #ifdef REDISMODULE_EXPERIMENTAL_API 391 | #define REDISMODULE_XAPI(X) REDISMODULE_XAPI_STABLE(X) REDISMODULE_XAPI_EXPERIMENTAL(X) REDISMODULE_XAPI_EXTENSIONS(X) 392 | #else 393 | #define REDISMODULE_XAPI(X) REDISMODULE_XAPI_STABLE(X) REDISMODULE_XAPI_EXTENSIONS(X) 394 | #endif 395 | 396 | typedef int (*RedisModule_GetApiFunctionType)(const char *name, void *pp); 397 | 398 | #pragma push_macro("X") 399 | #define X(TYPE, NAME, ARGS) \ 400 | __attribute__ ((weak)) TYPE (*RedisModule_##NAME) ARGS = NULL; 401 | REDISMODULE_XAPI(X) 402 | #undef X 403 | #pragma pop_macro("X") 404 | 405 | /* This is included inline inside each Redis module. */ 406 | 407 | static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) __attribute__((unused)); 408 | 409 | static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) { 410 | RedisModule_GetApiFunctionType getapifuncptr = (RedisModule_GetApiFunctionType)((void **)ctx)[0]; 411 | 412 | #pragma push_macro("X") 413 | #define X(TYPE, NAME, ARGS) getapifuncptr("RedisModule_" #NAME, (void *)&RedisModule_##NAME); 414 | REDISMODULE_XAPI(X) 415 | #undef X 416 | #pragma pop_macro("X") 417 | 418 | if (RedisModule_IsModuleNameBusy && RedisModule_IsModuleNameBusy(name)) { 419 | return REDISMODULE_ERR; 420 | } 421 | RedisModule_SetModuleAttribs(ctx,name,ver,apiver); 422 | return REDISMODULE_OK; 423 | } 424 | 425 | #else 426 | 427 | /* Things only defined for the modules core, not exported to modules 428 | * including this file. */ 429 | #define RedisModuleString robj 430 | 431 | #endif /* REDISMODULE_CORE */ 432 | 433 | #ifdef __cplusplus 434 | } 435 | #endif 436 | 437 | #endif /* REDISMOUDLE_H */ 438 | -------------------------------------------------------------------------------- /6.0/redis-module-sdk.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #define REDISMODULE_XAPI_ENTERPRISE(X) 5 | 6 | #define REDISMODULE_XAPI_EXTENSIONS(X) REDISMODULE_XAPI_ENTERPRISE(X) 7 | 8 | #include "RedisModuleSDK/6.0/redismodulex.h" 9 | -------------------------------------------------------------------------------- /BLOCK.md: -------------------------------------------------------------------------------- 1 | Blocking commands in Redis modules 2 | === 3 | 4 | Redis has a few blocking commands among the built-in set of commands. 5 | One of the most used is `BLPOP` (or the symmetric `BRPOP`) which blocks 6 | waiting for elements arriving in a list. 7 | 8 | The interesting fact about blocking commands is that they do not block 9 | the whole server, but just the client calling them. Usually the reason to 10 | block is that we expect some external event to happen: this can be 11 | some change in the Redis data structures like in the `BLPOP` case, a 12 | long computation happening in a thread, to receive some data from the 13 | network, and so forth. 14 | 15 | Redis modules have the ability to implement blocking commands as well, 16 | this documentation shows how the API works and describes a few patterns 17 | that can be used in order to model blocking commands. 18 | 19 | How blocking and resuming works. 20 | --- 21 | 22 | _Note: You may want to check the `helloblock.c` example in the Redis source tree 23 | inside the `src/modules` directory, for a simple to understand example 24 | on how the blocking API is applied._ 25 | 26 | In Redis modules, commands are implemented by callback functions that 27 | are invoked by the Redis core when the specific command is called 28 | by the user. Normally the callback terminates its execution sending 29 | some reply to the client. Using the following function instead, the 30 | function implementing the module command may request that the client 31 | is put into the blocked state: 32 | 33 | RedisModuleBlockedClient *RedisModule_BlockClient(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(void*), long long timeout_ms); 34 | 35 | The function returns a `RedisModuleBlockedClient` object, which is later 36 | used in order to unblock the client. The arguments have the following 37 | meaning: 38 | 39 | * `ctx` is the command execution context as usually in the rest of the API. 40 | * `reply_callback` is the callback, having the same prototype of a normal command function, that is called when the client is unblocked in order to return a reply to the client. 41 | * `timeout_callback` is the callback, having the same prototype of a normal command function that is called when the client reached the `ms` timeout. 42 | * `free_privdata` is the callback that is called in order to free the private data. Private data is a pointer to some data that is passed between the API used to unblock the client, to the callback that will send the reply to the client. We'll see how this mechanism works later in this document. 43 | * `ms` is the timeout in milliseconds. When the timeout is reached, the timeout callback is called and the client is automatically aborted. 44 | 45 | Once a client is blocked, it can be unblocked with the following API: 46 | 47 | int RedisModule_UnblockClient(RedisModuleBlockedClient *bc, void *privdata); 48 | 49 | The function takes as argument the blocked client object returned by 50 | the previous call to `RedisModule_BlockClient()`, and unblock the client. 51 | Immediately before the client gets unblocked, the `reply_callback` function 52 | specified when the client was blocked is called: this function will 53 | have access to the `privdata` pointer used here. 54 | 55 | IMPORTANT: The above function is thread safe, and can be called from within 56 | a thread doing some work in order to implement the command that blocked 57 | the client. 58 | 59 | The `privdata` data will be freed automatically using the `free_privdata` 60 | callback when the client is unblocked. This is useful **since the reply 61 | callback may never be called** in case the client timeouts or disconnects 62 | from the server, so it's important that it's up to an external function 63 | to have the responsibility to free the data passed if needed. 64 | 65 | To better understand how the API works, we can imagine writing a command 66 | that blocks a client for one second, and then send as reply "Hello!". 67 | 68 | Note: arity checks and other non important things are not implemented 69 | int his command, in order to take the example simple. 70 | 71 | int Example_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, 72 | int argc) 73 | { 74 | RedisModuleBlockedClient *bc = 75 | RedisModule_BlockClient(ctx,reply_func,timeout_func,NULL,0); 76 | 77 | pthread_t tid; 78 | pthread_create(&tid,NULL,threadmain,bc); 79 | 80 | return REDISMODULE_OK; 81 | } 82 | 83 | void *threadmain(void *arg) { 84 | RedisModuleBlockedClient *bc = arg; 85 | 86 | sleep(1); /* Wait one second and unblock. */ 87 | RedisModule_UnblockClient(bc,NULL); 88 | } 89 | 90 | The above command blocks the client ASAP, spawining a thread that will 91 | wait a second and will unblock the client. Let's check the reply and 92 | timeout callbacks, which are in our case very similar, since they 93 | just reply the client with a different reply type. 94 | 95 | int reply_func(RedisModuleCtx *ctx, RedisModuleString **argv, 96 | int argc) 97 | { 98 | return RedisModule_ReplyWithSimpleString(ctx,"Hello!"); 99 | } 100 | 101 | int timeout_func(RedisModuleCtx *ctx, RedisModuleString **argv, 102 | int argc) 103 | { 104 | return RedisModule_ReplyWithNull(ctx); 105 | } 106 | 107 | The reply callback just sends the "Hello!" string to the client. 108 | The important bit here is that the reply callback is called when the 109 | client is unblocked from the thread. 110 | 111 | The timeout command returns `NULL`, as it often happens with actual 112 | Redis blocking commands timing out. 113 | 114 | Passing reply data when unblocking 115 | --- 116 | 117 | The above example is simple to understand but lacks an important 118 | real world aspect of an actual blocking command implementation: often 119 | the reply function will need to know what to reply to the client, 120 | and this information is often provided as the client is unblocked. 121 | 122 | We could modify the above example so that the thread generates a 123 | random number after waiting one second. You can think at it as an 124 | actually expansive operation of some kind. Then this random number 125 | can be passed to the reply function so that we return it to the command 126 | caller. In order to make this working, we modify the functions as follow: 127 | 128 | void *threadmain(void *arg) { 129 | RedisModuleBlockedClient *bc = arg; 130 | 131 | sleep(1); /* Wait one second and unblock. */ 132 | 133 | long *mynumber = RedisModule_Alloc(sizeof(long)); 134 | *mynumber = rand(); 135 | RedisModule_UnblockClient(bc,mynumber); 136 | } 137 | 138 | As you can see, now the unblocking call is passing some private data, 139 | that is the `mynumber` pointer, to the reply callback. In order to 140 | obtain this private data, the reply callback will use the following 141 | fnuction: 142 | 143 | void *RedisModule_GetBlockedClientPrivateData(RedisModuleCtx *ctx); 144 | 145 | So our reply callback is modified like that: 146 | 147 | int reply_func(RedisModuleCtx *ctx, RedisModuleString **argv, 148 | int argc) 149 | { 150 | long *mynumber = RedisModule_GetBlockedClientPrivateData(ctx); 151 | /* IMPORTANT: don't free mynumber here, but in the 152 | * free privdata callback. */ 153 | return RedisModule_ReplyWithLongLong(ctx,mynumber); 154 | } 155 | 156 | Note that we also need to pass a `free_privdata` function when blocking 157 | the client with `RedisModule_BlockClient()`, since the allocated 158 | long value must be freed. Our callback will look like the following: 159 | 160 | void free_privdata(void *privdata) { 161 | RedisModule_Free(privdata); 162 | } 163 | 164 | NOTE: It is important to stress that the private data is best freed in the 165 | `free_privdata` callback becaues the reply function may not be called 166 | if the client disconnects or timeout. 167 | 168 | Also note that the private data is also accessible from the timeout 169 | callback, always using the `GetBlockedClientPrivateData()` API. 170 | 171 | Aborting the blocking of a client 172 | --- 173 | 174 | One problem that sometimes arises is that we need to allocate resources 175 | in order to implement the non blocking command. So we block the client, 176 | then, for example, try to create a thread, but the thread creation function 177 | returns an error. What to do in such a condition in order to recover? We 178 | don't want to take the client blocked, nor we want to call `UnblockClient()` 179 | because this will trigger the reply callback to be called. 180 | 181 | In this case the best thing to do is to use the following function: 182 | 183 | int RedisModule_AbortBlock(RedisModuleBlockedClient *bc); 184 | 185 | Practically this is how to use it: 186 | 187 | int Example_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, 188 | int argc) 189 | { 190 | RedisModuleBlockedClient *bc = 191 | RedisModule_BlockClient(ctx,reply_func,timeout_func,NULL,0); 192 | 193 | pthread_t tid; 194 | if (pthread_create(&tid,NULL,threadmain,bc) != 0) { 195 | RedisModule_AbortBlock(bc); 196 | RedisModule_ReplyWithError(ctx,"Sorry can't create a thread"); 197 | } 198 | 199 | return REDISMODULE_OK; 200 | } 201 | 202 | The client will be unblocked but the reply callback will not be called. 203 | 204 | Implementing the command, reply and timeout callback using a single function 205 | --- 206 | 207 | The following functions can be used in order to implement the reply and 208 | callback with the same function that implements the primary command 209 | function: 210 | 211 | int RedisModule_IsBlockedReplyRequest(RedisModuleCtx *ctx); 212 | int RedisModule_IsBlockedTimeoutRequest(RedisModuleCtx *ctx); 213 | 214 | So I could rewrite the example command without using a separated 215 | reply and timeout callback: 216 | 217 | int Example_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, 218 | int argc) 219 | { 220 | if (RedisModule_IsBlockedReplyRequest(ctx)) { 221 | long *mynumber = RedisModule_GetBlockedClientPrivateData(ctx); 222 | return RedisModule_ReplyWithLongLong(ctx,mynumber); 223 | } else if (RedisModule_IsBlockedTimeoutRequest) { 224 | return RedisModule_ReplyWithNull(ctx); 225 | } 226 | 227 | RedisModuleBlockedClient *bc = 228 | RedisModule_BlockClient(ctx,reply_func,timeout_func,NULL,0); 229 | 230 | pthread_t tid; 231 | if (pthread_create(&tid,NULL,threadmain,bc) != 0) { 232 | RedisModule_AbortBlock(bc); 233 | RedisModule_ReplyWithError(ctx,"Sorry can't create a thread"); 234 | } 235 | 236 | return REDISMODULE_OK; 237 | } 238 | 239 | Functionally is the same but there are people that will prefer the less 240 | verbose implementation that concentrates most of the command logic in a 241 | single function. 242 | 243 | Working on copies of data inside a thread 244 | --- 245 | 246 | An interesting pattern in order to work with threads implementing the 247 | slow part of a command, is to work with a copy of the data, so that 248 | while some operation is performed in a key, the user continues to see 249 | the old version. However when the thread terminated its work, the 250 | representations are swapped and the new, processed version, is used. 251 | 252 | An example of this approach is the 253 | [Neural Redis module](https://github.com/antirez/neural-redis) 254 | where neural networks are trained in different threads while the 255 | user can still execute and inspect their older versions. 256 | 257 | Future work 258 | --- 259 | 260 | An API is work in progress right now in order to allow Redis modules APIs 261 | to be called in a safe way from threads, so that the threaded command 262 | can access the data space and do incremental operations. 263 | 264 | There is no ETA for this feature but it may appear in the course of the 265 | Redis 4.0 release at some point. 266 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Redis Labs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #set environment variable RM_INCLUDE_DIR to the location of redismodule.h 2 | ifndef RM_INCLUDE_DIR 3 | RM_INCLUDE_DIR=./ 4 | endif 5 | 6 | ifndef RMUTIL_LIBDIR 7 | RMUTIL_LIBDIR=rmutil 8 | endif 9 | 10 | ifndef SRC_DIR 11 | SRC_DIR=example 12 | endif 13 | 14 | 15 | all: module.so 16 | 17 | module.so: 18 | $(MAKE) -C ./$(SRC_DIR) 19 | cp ./$(SRC_DIR)/module.so . 20 | 21 | clean: FORCE 22 | rm -rf *.xo *.so *.o 23 | rm -rf ./$(SRC_DIR)/*.xo ./$(SRC_DIR)/*.so ./$(SRC_DIR)/*.o 24 | rm -rf ./$(RMUTIL_LIBDIR)/*.so ./$(RMUTIL_LIBDIR)/*.o ./$(RMUTIL_LIBDIR)/*.a 25 | 26 | run: 27 | redis-server --loadmodule ./module.so 28 | 29 | FORCE: 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RedisModulesSDK 2 | 3 | This little repo is here to help you write Redis modules a bit more easily. 4 | 5 | ## What it includes: 6 | 7 | ### 1. redismodule.h 8 | 9 | The only file you really need to start writing Redis modules. Either put this path into your module's include path, or copy it. 10 | 11 | Notice: This is an up-to-date copy of it from the Redis repo. 12 | 13 | ### 2. LibRMUtil 14 | 15 | A small library of utility functions and macros for module developers, including: 16 | 17 | * Easier argument parsing for your commands. 18 | * Testing utilities that allow you to wrap your module's tests as a redis command. 19 | * `RedisModuleString` utility functions (formatting, comparison, etc) 20 | * The entire `sds` string library, lifted from Redis itself. 21 | * A generic scalable Vector library. Not redis specific but we found it useful. 22 | * A few other helpful macros and functions. 23 | * `alloc.h`, an include file that allows modules implementing data types to implicitly replace the `malloc()` function family with the Redis special allocation wrappers. 24 | 25 | It can be found under the `rmutil` folder, and compiles into a static library you link your module against. 26 | 27 | ### 3. An example Module 28 | 29 | A minimal module implementing a few commands and demonstarting both the Redis Module API, and use of rmutils. 30 | 31 | You can treat it as a template for your module, and extend its code and makefile. 32 | 33 | **It includes 3 commands:** 34 | 35 | * `EXAMPLE.PARSE` - demonstrating rmutil's argument helpers. 36 | * `EXAMPLE.HGETSET` - an atomic HGET/HSET command, demonstrating the higher level Redis module API. 37 | * `EXAMPLE.TEST` - a unit test of the above commands, demonstrating use of the testing utilities of rmutils. 38 | 39 | ### 4. Documentation Files: 40 | 41 | 1. [API.md](API.md) - The official manual for writing Redis modules, copied from the Redis repo. 42 | Read this before starting, as it's more than an API reference. 43 | 44 | 2. [FUNCTIONS.md](FUNCTIONS.md) - Generated API reference documentation for both the Redis module API, and LibRMUtil. 45 | 46 | 3. [TYPES.md](TYPES.md) - Describes the API for creating new data structures inside Redis modules, 47 | copied from the Redis repo. 48 | 49 | 4. [BLOCK.md](BLOCK.md) - Describes the API for blocking a client while performing asynchronous tasks on a separate thread. 50 | 51 | 52 | # Quick Start Guide 53 | 54 | Here's what you need to do to build your first module: 55 | 56 | 0. Build Redis in a build supporting modules. 57 | 1. Build librmutil and the module by running `make`. (you can also build them seperatly by running `make` in their respective dirs) 58 | 2. Run redis loading the module: `/path/to/redis-server --loadmodule ./module.so` 59 | 60 | Now run `redis-cli` and try the commands: 61 | 62 | ``` 63 | 127.0.0.1:9979> EXAMPLE.HGETSET foo bar baz 64 | (nil) 65 | 127.0.0.1:9979> EXAMPLE.HGETSET foo bar vaz 66 | "baz" 67 | 127.0.0.1:9979> EXAMPLE.PARSE SUM 5 2 68 | (integer) 7 69 | 127.0.0.1:9979> EXAMPLE.PARSE PROD 5 2 70 | (integer) 10 71 | 127.0.0.1:9979> EXAMPLE.TEST 72 | PASS 73 | ``` 74 | 75 | Enjoy! 76 | 77 | -------------------------------------------------------------------------------- /TYPES.md: -------------------------------------------------------------------------------- 1 | Native types in Redis modules 2 | === 3 | 4 | Redis modules can access Redis built-in data structures both at high level, 5 | by calling Redis commands, and at low level, by manipulating the data structures 6 | directly. 7 | 8 | By using these capabilities in order to build new abstractions on top of existing 9 | Redis data structures, or by using strings DMA in order to encode modules 10 | data structures into Redis strings, it is possible to create modules that 11 | *feel like* they are exporting new data types. However, for more complex 12 | problems, this is not enough, and the implementation of new data structures 13 | inside the module is needed. 14 | 15 | We call the ability of Redis modules to implement new data structures that 16 | feel like native Redis ones **native types support**. This document describes 17 | the API exported by the Redis modules system in order to create new data 18 | structures and handle the serialization in RDB files, the rewriting process 19 | in AOF, the type reporting via the `TYPE` command, and so forth. 20 | 21 | Overview of native types 22 | --- 23 | 24 | A module exporting a native type is composed of the following main parts: 25 | 26 | * The implementation of some kind of new data structure and of commands operating on the new data structure. 27 | * A set of callbacks that handle: RDB saving, RDB loading, AOF rewriting, releasing of a value associated with a key, calculation of a value digest (hash) to be used with the `DEBUG DIGEST` command. 28 | * A 9 characters name that is unique to each module native data type. 29 | * An encoding version, used to persist into RDB files a module-specific data version, so that a module will be able to load older representations from RDB files. 30 | 31 | While to handle RDB loading, saving and AOF rewriting may look complex as a first glance, the modules API provide very high level function for handling all this, without requiring the user to handle read/write errors, so in practical terms, writing a new data structure for Redis is a simple task. 32 | 33 | A **very easy** to understand but complete example of native type implementation 34 | is available inside the Redis distribution in the `/modules/hellotype.c` file. 35 | The reader is encouraged to read the documentation by looking at this example 36 | implementation to see how things are applied in the practice. 37 | 38 | Registering a new data type 39 | === 40 | 41 | In order to register a new native type into the Redis core, the module needs 42 | to declare a global variable that will hold a reference to the data type. 43 | The API to register the data type will return a data type reference that will 44 | be stored in the global variable. 45 | 46 | static RedisModuleType *MyType; 47 | #define MYTYPE_ENCODING_VERSION 0 48 | 49 | int RedisModule_OnLoad(RedisModuleCtx *ctx) { 50 | RedisModuleTypeMethods tm = { 51 | .version = REDISMODULE_TYPE_METHOD_VERSION, 52 | .rdb_load = MyTypeRDBLoad, 53 | .rdb_save = MyTypeRDBSave, 54 | .aof_rewrite = MyTypeAOFRewrite, 55 | .free = MyTypeFree 56 | }; 57 | 58 | MyType = RedisModule_CreateDataType("MyType-AZ", 59 | MYTYPE_ENCODING_VERSION, &tm); 60 | if (MyType == NULL) return REDISMODULE_ERR; 61 | } 62 | 63 | As you can see from the example above, a single API call is needed in order to 64 | register the new type. However a number of function pointers are passed as 65 | arguments. Certain are optionals while some are mandatory. The above set 66 | of methods *must* be passed, while `.digest` and `.mem_usage` are optional 67 | and are currently not actually supported by the modules internals, so for 68 | now you can just ignore them. 69 | 70 | The `ctx` argument is the context that we receive in the `OnLoad` function. 71 | The type `name` is a 9 character name in the character set that includes 72 | from `A-Z`, `a-z`, `0-9`, plus the underscore `_` and minus `-` characters. 73 | 74 | Note that **this name must be unique** for each data type in the Redis 75 | ecosystem, so be creative, use both lower-case and upper case if it makes 76 | sense, and try to use the convention of mixing the type name with the name 77 | of the author of the module, to create a 9 character unique name. 78 | 79 | **NOTE:** It is very important that the name is exactly 9 chars or the 80 | registration of the type will fail. Read more to understand why. 81 | 82 | For example if I'm building a *b-tree* data structure and my name is *antirez* 83 | I'll call my type **btree1-az**. The name, converted to a 64 bit integer, 84 | is stored inside the RDB file when saving the type, and will be used when the 85 | RDB data is loaded in order to resolve what module can load the data. If Redis 86 | finds no matching module, the integer is converted back to a name in order to 87 | provide some clue to the user about what module is missing in order to load 88 | the data. 89 | 90 | The type name is also used as a reply for the `TYPE` command when called 91 | with a key holding the registered type. 92 | 93 | The `encver` argument is the encoding version used by the module to store data 94 | inside the RDB file. For example I can start with an encoding version of 0, 95 | but later when I release version 2.0 of my module, I can switch encoding to 96 | something better. The new module will register with an encoding version of 1, 97 | so when it saves new RDB files, the new version will be stored on disk. However 98 | when loading RDB files, the module `rdb_load` method will be called even if 99 | there is data found for a different encoding version (and the encoding version 100 | is passed as argument to `rdb_load`), so that the module can still load old 101 | RDB files. 102 | 103 | The last argument is a structure used in order to pass the type methods to the 104 | registration function: `rdb_load`, `rdb_save`, `aof_rewrite`, `digest` and 105 | `free` and `mem_usage` are all callbacks with the following prototypes and uses: 106 | 107 | typedef void *(*RedisModuleTypeLoadFunc)(RedisModuleIO *rdb, int encver); 108 | typedef void (*RedisModuleTypeSaveFunc)(RedisModuleIO *rdb, void *value); 109 | typedef void (*RedisModuleTypeRewriteFunc)(RedisModuleIO *aof, RedisModuleString *key, void *value); 110 | typedef size_t (*RedisModuleTypeMemUsageFunc)(void *value); 111 | typedef void (*RedisModuleTypeDigestFunc)(RedisModuleDigest *digest, void *value); 112 | typedef void (*RedisModuleTypeFreeFunc)(void *value); 113 | 114 | * `rdb_load` is called when loading data from the RDB file. It loads data in the same format as `rdb_save` produces. 115 | * `rdb_save` is called when saving data to the RDB file. 116 | * `aof_rewrite` is called when the AOF is being rewritten, and the module needs to tell Redis what is the sequence of commands to recreate the content of a given key. 117 | * `digest` is called when `DEBUG DIGEST` is executed and a key holding this module type is found. Currently this is not yet implemented so the function ca be left empty. 118 | * `mem_usage` is called when the `MEMORY` command ask for the total memory consumed by a specific key, and is used in order to get the amount of bytes used by the module value. 119 | * `free` is called when a key with the module native type is deleted via `DEL` or in any other mean, in order to let the module reclaim the memory associated with such a value. 120 | 121 | Ok, but *why* modules types require a 9 characters name? 122 | --- 123 | 124 | Oh, I understand you need to understand this, so here is a very specific 125 | explanation. 126 | 127 | When Redis persists to RDB files, modules specific data types require to 128 | be persisted as well. Now RDB files are sequences of key-value pairs 129 | like the following: 130 | 131 | [1 byte type] [key] [a type specific value] 132 | 133 | The 1 byte type identifies strings, lists, sets, and so forth. In the case 134 | of modules data, it is set to a special value of `module data`, but of 135 | course this is not enough, we need the information needed to link a specific 136 | value with a specific module type that is able to load and handle it. 137 | 138 | So when we save a `type specific value` about a module, we prefix it with 139 | a 64 bit integer. 64 bits is large enough to store the informations needed 140 | in order to lookup the module that can handle that specific type, but is 141 | short enough that we can prefix each module value we store inside the RDB 142 | without making the final RDB file too big. At the same time, this solution 143 | of prefixing the value with a 64 bit *signature* does not require to do 144 | strange things like defining in the RDB header a list of modules specific 145 | types. Everything is pretty simple. 146 | 147 | So, what you can store in 64 bits in order to identify a given module in 148 | a reliable way? Well if you build a character set of 64 symbols, you can 149 | easily store 9 characters of 6 bits, and you are left with 10 bits, that 150 | are used in order to store the *encoding version* of the type, so that 151 | the same type can evolve in the future and provide a different and more 152 | efficient or updated serialization format for RDB files. 153 | 154 | So the 64 bit prefix stored before each module value is like the following: 155 | 156 | 6|6|6|6|6|6|6|6|6|10 157 | 158 | The first 9 elements are 6-bits characters, the final 10 bits is the 159 | encoding version. 160 | 161 | When the RDB file is loaded back, it reads the 64 bit value, masks the final 162 | 10 bits, and searches for a matching module in the modules types cache. 163 | When a matching one is found, the method to load the RDB file value is called 164 | with the 10 bits encoding version as argument, so that the module knows 165 | what version of the data layout to load, if it can support multiple versions. 166 | 167 | Now the interesting thing about all this is that, if instead the module type 168 | cannot be resolved, since there is no loaded module having this signature, 169 | we can convert back the 64 bit value into a 9 characters name, and print 170 | an error to the user that includes the module type name! So that she or he 171 | immediately realizes what's wrong. 172 | 173 | Setting and getting keys 174 | --- 175 | 176 | After registering our new data type in the `RedisModule_OnLoad()` function, 177 | we also need to be able to set Redis keys having as value our native type. 178 | 179 | This normally happens in the context of commands that write data to a key. 180 | The native types API allow to set and get keys to module native data types, 181 | and to test if a given key is already associated to a value of a specific data 182 | type. 183 | 184 | The API uses the normal modules `RedisModule_OpenKey()` low level key access 185 | interface in order to deal with this. This is an eaxmple of setting a 186 | native type private data structure to a Redis key: 187 | 188 | RedisModuleKey *key = RedisModule_OpenKey(ctx,keyname,REDISMODULE_WRITE); 189 | struct some_private_struct *data = createMyDataStructure(); 190 | RedisModule_ModuleTypeSetValue(key,MyType,data); 191 | 192 | The function `RedisModule_ModuleTypeSetValue()` is used with a key handle open 193 | for writing, and gets three arguments: the key handle, the reference to the 194 | native type, as obtained during the type registration, and finally a `void*` 195 | pointer that contains the private data implementing the module native type. 196 | 197 | Note that Redis has no clues at all about what your data contains. It will 198 | just call the callbacks you provided during the method registration in order 199 | to perform operations on the type. 200 | 201 | Similarly we can retrieve the private data from a key using this function: 202 | 203 | struct some_private_struct *data; 204 | data = RedisModule_ModuleTypeGetValue(key); 205 | 206 | We can also test for a key to have our native type as value: 207 | 208 | if (RedisModule_ModuleTypeGetType(key) == MyType) { 209 | /* ... do something ... */ 210 | } 211 | 212 | However for the calls to do the right thing, we need to check if the key 213 | is empty, if it contains a value of the right kind, and so forth. So 214 | the idiomatic code to implement a command writing to our native type 215 | is along these lines: 216 | 217 | RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], 218 | REDISMODULE_READ|REDISMODULE_WRITE); 219 | int type = RedisModule_KeyType(key); 220 | if (type != REDISMODULE_KEYTYPE_EMPTY && 221 | RedisModule_ModuleTypeGetType(key) != MyType) 222 | { 223 | return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); 224 | } 225 | 226 | Then if we successfully verified the key is not of the wrong type, and 227 | we are going to write to it, we usually want to create a new data structure if 228 | the key is empty, or retrieve the reference to the value associated to the 229 | key if there is already one: 230 | 231 | /* Create an empty value object if the key is currently empty. */ 232 | struct some_private_struct *data; 233 | if (type == REDISMODULE_KEYTYPE_EMPTY) { 234 | data = createMyDataStructure(); 235 | RedisModule_ModuleTypeSetValue(key,MyTyke,data); 236 | } else { 237 | data = RedisModule_ModuleTypeGetValue(key); 238 | } 239 | /* Do something with 'data'... */ 240 | 241 | Free method 242 | --- 243 | 244 | As already mentioned, when Redis needs to free a key holding a native type 245 | value, it needs help from the module in order to release the memory. This 246 | is the reason why we pass a `free` callback during the type registration: 247 | 248 | typedef void (*RedisModuleTypeFreeFunc)(void *value); 249 | 250 | A trivial implementation of the free method can be something like this, 251 | assuming our data structure is composed of a single allocation: 252 | 253 | void MyTypeFreeCallback(void *value) { 254 | RedisModule_Free(value); 255 | } 256 | 257 | However a more real world one will call some function that performs a more 258 | complex memory reclaiming, by casting the void pointer to some structure 259 | and freeing all the resources composing the value. 260 | 261 | RDB load and save methods 262 | --- 263 | 264 | The RDB saving and loading callbacks need to create (and load back) a 265 | representation of the data type on disk. Redis offers an high level API 266 | that can automatically store inside the RDB file the following types: 267 | 268 | * Unsigned 64 bit integers. 269 | * Signed 64 bit integers. 270 | * Doubles. 271 | * Strings. 272 | 273 | It is up to the module to find a viable representation using the above base 274 | types. However note that while the integer and double values are stored 275 | and loaded in an architecture and *endianess* agnostic way, if you use 276 | the raw string saving API to, for example, save a structure on disk, you 277 | have to care those details yourself. 278 | 279 | This is the list of functions performing RDB saving and loading: 280 | 281 | void RedisModule_SaveUnsigned(RedisModuleIO *io, uint64_t value); 282 | uint64_t RedisModule_LoadUnsigned(RedisModuleIO *io); 283 | void RedisModule_SaveSigned(RedisModuleIO *io, int64_t value); 284 | int64_t RedisModule_LoadSigned(RedisModuleIO *io); 285 | void RedisModule_SaveString(RedisModuleIO *io, RedisModuleString *s); 286 | void RedisModule_SaveStringBuffer(RedisModuleIO *io, const char *str, size_t len); 287 | RedisModuleString *RedisModule_LoadString(RedisModuleIO *io); 288 | char *RedisModule_LoadStringBuffer(RedisModuleIO *io, size_t *lenptr); 289 | void RedisModule_SaveDouble(RedisModuleIO *io, double value); 290 | double RedisModule_LoadDouble(RedisModuleIO *io); 291 | 292 | The functions don't require any error checking from the module, that can 293 | always assume calls succeed. 294 | 295 | As an example, imagine I've a native type that implements an array of 296 | double values, with the following structure: 297 | 298 | struct double_array { 299 | size_t count; 300 | double *values; 301 | }; 302 | 303 | My `rdb_save` method may look like the following: 304 | 305 | void DoubleArrayRDBSave(RedisModuleIO *io, void *ptr) { 306 | struct dobule_array *da = ptr; 307 | RedisModule_SaveUnsigned(io,da->count); 308 | for (size_t j = 0; j < da->count; j++) 309 | RedisModule_SaveDouble(io,da->values[j]); 310 | } 311 | 312 | What we did was to store the number of elements followed by each double 313 | value. So when later we'll have to load the structure in the `rdb_load` 314 | method we'll do something like this: 315 | 316 | void *DoubleArrayRDBLoad(RedisModuleIO *io, int encver) { 317 | if (encver != DOUBLE_ARRAY_ENC_VER) { 318 | /* We should actually log an error here, or try to implement 319 | the ability to load older versions of our data structure. */ 320 | return NULL; 321 | } 322 | 323 | struct double_array *da; 324 | da = RedisModule_Alloc(sizeof(*da)); 325 | da->count = RedisModule_LoadUnsigned(io); 326 | da->values = RedisModule_Alloc(da->count * sizeof(double)); 327 | for (size_t j = 0; j < da->count; j++) 328 | da->values = RedisModule_LoadDouble(io); 329 | return da; 330 | } 331 | 332 | The load callback just reconstruct back the data structure from the data 333 | we stored in the RDB file. 334 | 335 | Note that while there is no error handling on the API that writes and reads 336 | from disk, still the load callback can return NULL on errors in case what 337 | it reads does not look correct. Redis will just panic in that case. 338 | 339 | AOF rewriting 340 | --- 341 | 342 | void RedisModule_EmitAOF(RedisModuleIO *io, const char *cmdname, const char *fmt, ...); 343 | 344 | Handling multiple encodings 345 | --- 346 | 347 | WORK IN PROGRESS 348 | 349 | Allocating memory 350 | --- 351 | 352 | Modules data types should try to use `RedisModule_Alloc()` functions family 353 | in order to allocate, reallocate and release heap memory used to implement the native data structures (see the other Redis Modules documentation for detailed information). 354 | 355 | This is not just useful in order for Redis to be able to account for the memory used by the module, but there are also more advantages: 356 | 357 | * Redis uses the `jemalloc` allcator, that often prevents fragmentation problems that could be caused by using the libc allocator. 358 | * When loading strings from the RDB file, the native types API is able to return strings allocated directly with `RedisModule_Alloc()`, so that the module can directly link this memory into the data structure representation, avoiding an useless copy of the data. 359 | 360 | Even if you are using external libraries implementing your data structures, the 361 | allocation functions provided by the module API is exactly compatible with 362 | `malloc()`, `realloc()`, `free()` and `strdup()`, so converting the libraries 363 | in order to use these functions should be trivial. 364 | 365 | In case you have an external library that uses libc `malloc()`, and you want 366 | to avoid replacing manually all the calls with the Redis Modules API calls, 367 | an approach could be to use simple macros in order to replace the libc calls 368 | with the Redis API calls. Something like this could work: 369 | 370 | #define malloc RedisModule_Alloc 371 | #define realloc RedisModule_Realloc 372 | #define free RedisModule_Free 373 | #define strdup RedisModule_Strdup 374 | 375 | However take in mind that mixing libc calls with Redis API calls will result 376 | into troubles and crashes, so if you replace calls using macros, you need to 377 | make sure that all the calls are correctly replaced, and that the code with 378 | the substituted calls will never, for example, attempt to call 379 | `RedisModule_Free()` with a pointer allocated using libc `malloc()`. 380 | -------------------------------------------------------------------------------- /example/Makefile: -------------------------------------------------------------------------------- 1 | #set environment variable RM_INCLUDE_DIR to the location of redismodule.h 2 | ifndef RM_INCLUDE_DIR 3 | RM_INCLUDE_DIR=../ 4 | endif 5 | 6 | ifndef RMUTIL_LIBDIR 7 | RMUTIL_LIBDIR=../rmutil 8 | endif 9 | 10 | # find the OS 11 | uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') 12 | 13 | # Compile flags for linux / osx 14 | ifeq ($(uname_S),Linux) 15 | SHOBJ_CFLAGS ?= -fno-common -g -ggdb 16 | SHOBJ_LDFLAGS ?= -shared -Bsymbolic 17 | else 18 | SHOBJ_CFLAGS ?= -dynamic -fno-common -g -ggdb 19 | SHOBJ_LDFLAGS ?= -bundle -undefined dynamic_lookup 20 | endif 21 | CFLAGS = -I$(RM_INCLUDE_DIR) -Wall -g -fPIC -lc -lm -std=gnu99 22 | CC=gcc 23 | 24 | all: rmutil module.so 25 | 26 | rmutil: FORCE 27 | $(MAKE) -C $(RMUTIL_LIBDIR) 28 | 29 | module.so: module.o 30 | $(LD) -o $@ module.o $(SHOBJ_LDFLAGS) $(LIBS) -L$(RMUTIL_LIBDIR) -lrmutil -lc 31 | 32 | clean: 33 | rm -rf *.xo *.so *.o 34 | 35 | FORCE: 36 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # An Example Redis Module 2 | 3 | This project is a simple redis module demonstrating basic API usage and `librmutil`. 4 | 5 | You can treat it as a basic module template. See the project's [README](../README.md) for more details. -------------------------------------------------------------------------------- /example/module.c: -------------------------------------------------------------------------------- 1 | #include "../redismodule.h" 2 | #include "../rmutil/util.h" 3 | #include "../rmutil/strings.h" 4 | #include "../rmutil/test_util.h" 5 | 6 | /* EXAMPLE.PARSE [SUM ] | [PROD ] 7 | * Demonstrates the automatic arg parsing utility. 8 | * If the command receives "SUM " it returns their sum 9 | * If it receives "PROD " it returns their product 10 | */ 11 | int ParseCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 12 | 13 | // we must have at least 4 args 14 | if (argc < 4) { 15 | return RedisModule_WrongArity(ctx); 16 | } 17 | 18 | // init auto memory for created strings 19 | RedisModule_AutoMemory(ctx); 20 | long long x, y; 21 | 22 | // If we got SUM - return the sum of 2 consecutive arguments 23 | if (RMUtil_ParseArgsAfter("SUM", argv, argc, "ll", &x, &y) == 24 | REDISMODULE_OK) { 25 | RedisModule_ReplyWithLongLong(ctx, x + y); 26 | return REDISMODULE_OK; 27 | } 28 | 29 | // If we got PROD - return the product of 2 consecutive arguments 30 | if (RMUtil_ParseArgsAfter("PROD", argv, argc, "ll", &x, &y) == 31 | REDISMODULE_OK) { 32 | RedisModule_ReplyWithLongLong(ctx, x * y); 33 | return REDISMODULE_OK; 34 | } 35 | 36 | // something is fishy... 37 | RedisModule_ReplyWithError(ctx, "Invalid arguments"); 38 | 39 | return REDISMODULE_ERR; 40 | } 41 | 42 | /* 43 | * example.HGETSET 44 | * Atomically set a value in a HASH key to and return its value before 45 | * the HSET. 46 | * 47 | * Basically atomic HGET + HSET 48 | */ 49 | int HGetSetCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 50 | 51 | // we need EXACTLY 4 arguments 52 | if (argc != 4) { 53 | return RedisModule_WrongArity(ctx); 54 | } 55 | RedisModule_AutoMemory(ctx); 56 | 57 | // open the key and make sure it's indeed a HASH and not empty 58 | RedisModuleKey *key = 59 | RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ | REDISMODULE_WRITE); 60 | if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_HASH && 61 | RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_EMPTY) { 62 | return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); 63 | } 64 | 65 | // get the current value of the hash element 66 | RedisModuleCallReply *rep = 67 | RedisModule_Call(ctx, "HGET", "ss", argv[1], argv[2]); 68 | RMUTIL_ASSERT_NOERROR(ctx, rep); 69 | 70 | // set the new value of the element 71 | RedisModuleCallReply *srep = 72 | RedisModule_Call(ctx, "HSET", "sss", argv[1], argv[2], argv[3]); 73 | RMUTIL_ASSERT_NOERROR(ctx, srep); 74 | 75 | // if the value was null before - we just return null 76 | if (RedisModule_CallReplyType(rep) == REDISMODULE_REPLY_NULL) { 77 | RedisModule_ReplyWithNull(ctx); 78 | return REDISMODULE_OK; 79 | } 80 | 81 | // forward the HGET reply to the client 82 | RedisModule_ReplyWithCallReply(ctx, rep); 83 | return REDISMODULE_OK; 84 | } 85 | 86 | // Test the the PARSE command 87 | int testParse(RedisModuleCtx *ctx) { 88 | 89 | RedisModuleCallReply *r = 90 | RedisModule_Call(ctx, "example.parse", "ccc", "SUM", "5", "2"); 91 | RMUtil_Assert(RedisModule_CallReplyType(r) == REDISMODULE_REPLY_INTEGER); 92 | RMUtil_AssertReplyEquals(r, "7"); 93 | 94 | r = RedisModule_Call(ctx, "example.parse", "ccc", "PROD", "5", "2"); 95 | RMUtil_Assert(RedisModule_CallReplyType(r) == REDISMODULE_REPLY_INTEGER); 96 | RMUtil_AssertReplyEquals(r, "10"); 97 | return 0; 98 | } 99 | 100 | // test the HGETSET command 101 | int testHgetSet(RedisModuleCtx *ctx) { 102 | RedisModuleCallReply *r = 103 | RedisModule_Call(ctx, "example.hgetset", "ccc", "foo", "bar", "baz"); 104 | RMUtil_Assert(RedisModule_CallReplyType(r) != REDISMODULE_REPLY_ERROR); 105 | 106 | r = RedisModule_Call(ctx, "example.hgetset", "ccc", "foo", "bar", "bag"); 107 | RMUtil_Assert(RedisModule_CallReplyType(r) == REDISMODULE_REPLY_STRING); 108 | RMUtil_AssertReplyEquals(r, "baz"); 109 | r = RedisModule_Call(ctx, "example.hgetset", "ccc", "foo", "bar", "bang"); 110 | RMUtil_AssertReplyEquals(r, "bag"); 111 | return 0; 112 | } 113 | 114 | // Unit test entry point for the module 115 | int TestModule(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 116 | RedisModule_AutoMemory(ctx); 117 | 118 | RMUtil_Test(testParse); 119 | RMUtil_Test(testHgetSet); 120 | 121 | RedisModule_ReplyWithSimpleString(ctx, "PASS"); 122 | return REDISMODULE_OK; 123 | } 124 | 125 | int RedisModule_OnLoad(RedisModuleCtx *ctx) { 126 | 127 | // Register the module itself 128 | if (RedisModule_Init(ctx, "example", 1, REDISMODULE_APIVER_1) == 129 | REDISMODULE_ERR) { 130 | return REDISMODULE_ERR; 131 | } 132 | 133 | // register example.parse - the default registration syntax 134 | if (RedisModule_CreateCommand(ctx, "example.parse", ParseCommand, "readonly", 135 | 1, 1, 1) == REDISMODULE_ERR) { 136 | return REDISMODULE_ERR; 137 | } 138 | 139 | // register example.hgetset - using the shortened utility registration macro 140 | RMUtil_RegisterWriteCmd(ctx, "example.hgetset", HGetSetCommand); 141 | 142 | // register the unit test 143 | RMUtil_RegisterWriteCmd(ctx, "example.test", TestModule); 144 | 145 | return REDISMODULE_OK; 146 | } 147 | -------------------------------------------------------------------------------- /redismodule-rlec.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | // clang-format off 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | //--------------------------------------------------------------------------------------------- 15 | // bigredis extensions 16 | 17 | #define REDISMODULE_METADATA_NOT_ON_SWAP 0x80 18 | 19 | /* Notification that a key's value is added to ram (from swap or otherwise). 20 | * swap_key_flags has 4 bits that the module can write / read. 21 | * when swap_key_metadata is NOT_ON_SWAP, it means the key is not loaded from swap. */ 22 | typedef void (*RedisModuleTypeKeyAddedToDbDictFunc)(RedisModuleCtx *ctx, RedisModuleString *key, void *value, int swap_key_metadata); 23 | 24 | /* Notification that a key's value is removed from ram (may still exist on swap). 25 | * when swap_key_metadata is NOT_ON_SWAP it means the key does not exist on swap. 26 | * return swap_key_metadata or NOT_ON_SWAP if key is to be deleted (and not to be written). */ 27 | typedef int (*RedisModuleTypeRemovingKeyFromDbDictFunc)(RedisModuleCtx *ctx, RedisModuleString *key, void *value, int swap_key_metadata, int writing_to_swap); 28 | 29 | /* return swap_key_metadata, 0 indicates nothing to write. when out_min_expire is -1 it indicates nothing to write. */ 30 | typedef int (*RedisModuleTypeGetKeyMetadataForRdbFunc)(RedisModuleCtx *ctx, RedisModuleString *key, void *value, long long *out_min_expire, long long *out_max_expire); 31 | 32 | #define REDISMODULE_TYPE_EXT_METHOD_VERSION 1 33 | typedef struct RedisModuleTypeExtMethods { 34 | uint64_t version; 35 | RedisModuleTypeKeyAddedToDbDictFunc key_added_to_db_dict; 36 | RedisModuleTypeRemovingKeyFromDbDictFunc removing_key_from_db_dict; 37 | RedisModuleTypeGetKeyMetadataForRdbFunc get_key_metadata_for_rdb; 38 | } RedisModuleTypeExtMethods; 39 | 40 | typedef void (*RedisModuleSwapPrefetchCB)(RedisModuleCtx *ctx, RedisModuleString *key, void* user_data); 41 | 42 | /* APIs */ 43 | 44 | REDISMODULE_API int (*RedisModule_SetDataTypeExtensions)(RedisModuleCtx *ctx, RedisModuleType *mt, RedisModuleTypeExtMethods *typemethods) REDISMODULE_ATTR; 45 | REDISMODULE_API int (*RedisModule_SwapPrefetchKey)(RedisModuleCtx *ctx, RedisModuleString *keyname, RedisModuleSwapPrefetchCB fn, void *user_data, int flags) REDISMODULE_ATTR; 46 | REDISMODULE_API int (*RedisModule_GetSwapKeyMetadata)(RedisModuleCtx *ctx, RedisModuleString *key) REDISMODULE_ATTR; 47 | REDISMODULE_API int (*RedisModule_SetSwapKeyMetadata)(RedisModuleCtx *ctx, RedisModuleString *key, int module_metadata) REDISMODULE_ATTR; 48 | REDISMODULE_API int (*RedisModule_IsKeyInRam)(RedisModuleCtx *ctx, RedisModuleString *key) REDISMODULE_ATTR; 49 | 50 | //--------------------------------------------------------------------------------------------- 51 | 52 | /* Keyspace changes notification classes. Every class is associated with a 53 | * character for configuration purposes. 54 | * NOTE: These have to be in sync with NOTIFY_* in server.h */ 55 | 56 | #define REDISMODULE_NOTIFY_TRIMMED (1<<30) /* trimmed by reshard trimming enterprise only event */ 57 | 58 | /* Server events definitions. 59 | * Those flags should not be used directly by the module, instead 60 | * the module should use RedisModuleEvent_* variables */ 61 | 62 | #define REDISMODULE_EVENT_SHARDING 1000 63 | 64 | static const RedisModuleEvent 65 | RedisModuleEvent_Sharding = { 66 | REDISMODULE_EVENT_SHARDING, 67 | 1 68 | }; 69 | 70 | /* Those are values that are used for the 'subevent' callback argument. */ 71 | 72 | #define REDISMODULE_SUBEVENT_SHARDING_SLOT_RANGE_CHANGED 0 73 | #define REDISMODULE_SUBEVENT_SHARDING_TRIMMING_STARTED 1 74 | #define REDISMODULE_SUBEVENT_SHARDING_TRIMMING_ENDED 2 75 | 76 | /* APIs */ 77 | 78 | REDISMODULE_API int (*RedisModule_ShardingGetKeySlot)(RedisModuleString *keyname) REDISMODULE_ATTR; 79 | REDISMODULE_API void (*RedisModule_ShardingGetSlotRange)(int *first_slot, int *last_slot) REDISMODULE_ATTR; 80 | 81 | //--------------------------------------------------------------------------------------------- 82 | 83 | #define REDISMODULE_RLEC_API_DEFS \ 84 | REDISMODULE_GET_API(ShardingGetKeySlot); \ 85 | REDISMODULE_GET_API(ShardingGetSlotRange); \ 86 | REDISMODULE_GET_API(SetDataTypeExtensions); \ 87 | REDISMODULE_GET_API(SwapPrefetchKey); \ 88 | REDISMODULE_GET_API(GetSwapKeyMetadata); \ 89 | REDISMODULE_GET_API(SetSwapKeyMetadata); \ 90 | REDISMODULE_GET_API(IsKeyInRam); \ 91 | /**/ 92 | 93 | //--------------------------------------------------------------------------------------------- 94 | 95 | #ifdef __cplusplus 96 | } 97 | #endif 98 | -------------------------------------------------------------------------------- /rmutil/Makefile: -------------------------------------------------------------------------------- 1 | # set environment variable RM_INCLUDE_DIR to the location of redismodule.h 2 | ifndef RM_INCLUDE_DIR 3 | RM_INCLUDE_DIR=../ 4 | endif 5 | 6 | CFLAGS ?= -g -fPIC -O3 -std=gnu99 -Wall -Wno-unused-function 7 | CFLAGS += -I$(RM_INCLUDE_DIR) 8 | CC=gcc 9 | 10 | OBJS=util.o strings.o sds.o vector.o alloc.o periodic.o 11 | 12 | all: librmutil.a 13 | 14 | clean: 15 | rm -rf *.o *.a 16 | 17 | librmutil.a: $(OBJS) 18 | ar rcs $@ $^ 19 | 20 | test_vector: test_vector.o vector.o 21 | $(CC) -Wall -o $@ $^ -lc -lpthread -O0 22 | @(sh -c ./$@) 23 | .PHONY: test_vector 24 | 25 | test_periodic: test_periodic.o periodic.o 26 | $(CC) -Wall -o $@ $^ -lc -lpthread -O0 27 | @(sh -c ./$@) 28 | .PHONY: test_periodic 29 | 30 | test: test_periodic test_vector 31 | .PHONY: test 32 | -------------------------------------------------------------------------------- /rmutil/alloc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "alloc.h" 5 | 6 | /* A patched implementation of strdup that will use our patched calloc */ 7 | char *rmalloc_strndup(const char *s, size_t n) { 8 | char *ret = calloc(n + 1, sizeof(char)); 9 | if (ret) 10 | memcpy(ret, s, n); 11 | return ret; 12 | } 13 | 14 | /* 15 | * Re-patching RedisModule_Alloc and friends to the original malloc functions 16 | * 17 | * This function should be called if you are working with malloc-patched code 18 | * outside of redis, usually for unit tests. Call it once when entering your unit 19 | * tests' main(). 20 | * 21 | * Since including "alloc.h" while defining REDIS_MODULE_TARGET 22 | * replaces all malloc functions in redis with the RM_Alloc family of functions, 23 | * when running that code outside of redis, your app will crash. This function 24 | * patches the RM_Alloc functions back to the original mallocs. */ 25 | void RMUTil_InitAlloc() { 26 | 27 | RedisModule_Alloc = malloc; 28 | RedisModule_Realloc = realloc; 29 | RedisModule_Calloc = calloc; 30 | RedisModule_Free = free; 31 | RedisModule_Strdup = strdup; 32 | } 33 | -------------------------------------------------------------------------------- /rmutil/alloc.h: -------------------------------------------------------------------------------- 1 | #ifndef __RMUTIL_ALLOC__ 2 | #define __RMUTIL_ALLOC__ 3 | 4 | /* Automatic Redis Module Allocation functions monkey-patching. 5 | * 6 | * Including this file while REDIS_MODULE_TARGET is defined, will explicitly 7 | * override malloc, calloc, realloc & free with RedisModule_Alloc, 8 | * RedisModule_Callc, etc implementations, that allow Redis better control and 9 | * reporting over allocations per module. 10 | * 11 | * You should include this file in all c files AS THE LAST INCLUDED FILE 12 | * 13 | * This only has effect when when compiling with the macro REDIS_MODULE_TARGET 14 | * defined. The idea is that for unit tests it will not be defined, but for the 15 | * module build target it will be. 16 | * 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | char *rmalloc_strndup(const char *s, size_t n); 23 | 24 | #ifdef REDIS_MODULE_TARGET /* Set this when compiling your code as a module */ 25 | 26 | #define malloc(size) RedisModule_Alloc(size) 27 | #define calloc(count, size) RedisModule_Calloc(count, size) 28 | #define realloc(ptr, size) RedisModule_Realloc(ptr, size) 29 | #define free(ptr) RedisModule_Free(ptr) 30 | 31 | #ifdef strdup 32 | #undef strdup 33 | #endif 34 | #define strdup(ptr) RedisModule_Strdup(ptr) 35 | 36 | /* More overriding */ 37 | // needed to avoid calling strndup->malloc 38 | #ifdef strndup 39 | #undef strndup 40 | #endif 41 | #define strndup(s, n) rmalloc_strndup(s, n) 42 | 43 | #else 44 | 45 | #endif /* REDIS_MODULE_TARGET */ 46 | /* This function should be called if you are working with malloc-patched code 47 | * outside of redis, usually for unit tests. Call it once when entering your unit 48 | * tests' main() */ 49 | void RMUTil_InitAlloc(); 50 | 51 | #endif /* __RMUTIL_ALLOC__ */ 52 | -------------------------------------------------------------------------------- /rmutil/heap.c: -------------------------------------------------------------------------------- 1 | #include "heap.h" 2 | 3 | /* Byte-wise swap two items of size SIZE. */ 4 | #define SWAP(a, b, size) \ 5 | do \ 6 | { \ 7 | register size_t __size = (size); \ 8 | register char *__a = (a), *__b = (b); \ 9 | do \ 10 | { \ 11 | char __tmp = *__a; \ 12 | *__a++ = *__b; \ 13 | *__b++ = __tmp; \ 14 | } while (--__size > 0); \ 15 | } while (0) 16 | 17 | inline char *__vector_GetPtr(Vector *v, size_t pos) { 18 | return v->data + (pos * v->elemSize); 19 | } 20 | 21 | void __sift_up(Vector *v, size_t first, size_t last, int (*cmp)(void *, void *)) { 22 | size_t len = last - first; 23 | if (len > 1) { 24 | len = (len - 2) / 2; 25 | size_t ptr = first + len; 26 | if (cmp(__vector_GetPtr(v, ptr), __vector_GetPtr(v, --last)) < 0) { 27 | char t[v->elemSize]; 28 | memcpy(t, __vector_GetPtr(v, last), v->elemSize); 29 | do { 30 | memcpy(__vector_GetPtr(v, last), __vector_GetPtr(v, ptr), v->elemSize); 31 | last = ptr; 32 | if (len == 0) 33 | break; 34 | len = (len - 1) / 2; 35 | ptr = first + len; 36 | } while (cmp(__vector_GetPtr(v, ptr), t) < 0); 37 | memcpy(__vector_GetPtr(v, last), t, v->elemSize); 38 | } 39 | } 40 | } 41 | 42 | void __sift_down(Vector *v, size_t first, size_t last, int (*cmp)(void *, void *), size_t start) { 43 | // left-child of __start is at 2 * __start + 1 44 | // right-child of __start is at 2 * __start + 2 45 | size_t len = last - first; 46 | size_t child = start - first; 47 | 48 | if (len < 2 || (len - 2) / 2 < child) 49 | return; 50 | 51 | child = 2 * child + 1; 52 | 53 | if ((child + 1) < len && cmp(__vector_GetPtr(v, first + child), __vector_GetPtr(v, first + child + 1)) < 0) { 54 | // right-child exists and is greater than left-child 55 | ++child; 56 | } 57 | 58 | // check if we are in heap-order 59 | if (cmp(__vector_GetPtr(v, first + child), __vector_GetPtr(v, start)) < 0) 60 | // we are, __start is larger than it's largest child 61 | return; 62 | 63 | char top[v->elemSize]; 64 | memcpy(top, __vector_GetPtr(v, start), v->elemSize); 65 | do { 66 | // we are not in heap-order, swap the parent with it's largest child 67 | memcpy(__vector_GetPtr(v, start), __vector_GetPtr(v, first + child), v->elemSize); 68 | start = first + child; 69 | 70 | if ((len - 2) / 2 < child) 71 | break; 72 | 73 | // recompute the child based off of the updated parent 74 | child = 2 * child + 1; 75 | 76 | if ((child + 1) < len && cmp(__vector_GetPtr(v, first + child), __vector_GetPtr(v, first + child + 1)) < 0) { 77 | // right-child exists and is greater than left-child 78 | ++child; 79 | } 80 | 81 | // check if we are in heap-order 82 | } while (cmp(__vector_GetPtr(v, first + child), top) >= 0); 83 | memcpy(__vector_GetPtr(v, start), top, v->elemSize); 84 | } 85 | 86 | 87 | void Make_Heap(Vector *v, size_t first, size_t last, int (*cmp)(void *, void *)) { 88 | if (last - first > 1) { 89 | // start from the first parent, there is no need to consider children 90 | for (int start = (last - first - 2) / 2; start >= 0; --start) { 91 | __sift_down(v, first, last, cmp, first + start); 92 | } 93 | } 94 | } 95 | 96 | 97 | inline void Heap_Push(Vector *v, size_t first, size_t last, int (*cmp)(void *, void *)) { 98 | __sift_up(v, first, last, cmp); 99 | } 100 | 101 | 102 | inline void Heap_Pop(Vector *v, size_t first, size_t last, int (*cmp)(void *, void *)) { 103 | if (last - first > 1) { 104 | SWAP(__vector_GetPtr(v, first), __vector_GetPtr(v, --last), v->elemSize); 105 | __sift_down(v, first, last, cmp, first); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /rmutil/heap.h: -------------------------------------------------------------------------------- 1 | #ifndef __HEAP_H__ 2 | #define __HEAP_H__ 3 | 4 | #include "vector.h" 5 | 6 | 7 | /* Make heap from range 8 | * Rearranges the elements in the range [first,last) in such a way that they form a heap. 9 | * A heap is a way to organize the elements of a range that allows for fast retrieval of the element with the highest 10 | * value at any moment (with pop_heap), even repeatedly, while allowing for fast insertion of new elements (with 11 | * push_heap). 12 | * The element with the highest value is always pointed by first. The order of the other elements depends on the 13 | * particular implementation, but it is consistent throughout all heap-related functions of this header. 14 | * The elements are compared using cmp. 15 | */ 16 | void Make_Heap(Vector *v, size_t first, size_t last, int (*cmp)(void *, void *)); 17 | 18 | 19 | /* Push element into heap range 20 | * Given a heap in the range [first,last-1), this function extends the range considered a heap to [first,last) by 21 | * placing the value in (last-1) into its corresponding location within it. 22 | * A range can be organized into a heap by calling make_heap. After that, its heap properties are preserved if elements 23 | * are added and removed from it using push_heap and pop_heap, respectively. 24 | */ 25 | void Heap_Push(Vector *v, size_t first, size_t last, int (*cmp)(void *, void *)); 26 | 27 | 28 | /* Pop element from heap range 29 | * Rearranges the elements in the heap range [first,last) in such a way that the part considered a heap is shortened 30 | * by one: The element with the highest value is moved to (last-1). 31 | * While the element with the highest value is moved from first to (last-1) (which now is out of the heap), the other 32 | * elements are reorganized in such a way that the range [first,last-1) preserves the properties of a heap. 33 | * A range can be organized into a heap by calling make_heap. After that, its heap properties are preserved if elements 34 | * are added and removed from it using push_heap and pop_heap, respectively. 35 | */ 36 | void Heap_Pop(Vector *v, size_t first, size_t last, int (*cmp)(void *, void *)); 37 | 38 | #endif //__HEAP_H__ 39 | -------------------------------------------------------------------------------- /rmutil/logging.h: -------------------------------------------------------------------------------- 1 | #ifndef __RMUTIL_LOGGING_H__ 2 | #define __RMUTIL_LOGGING_H__ 3 | 4 | /* Convenience macros for redis logging */ 5 | 6 | #define RM_LOG_DEBUG(ctx, ...) RedisModule_Log(ctx, "debug", __VA_ARGS__) 7 | #define RM_LOG_VERBOSE(ctx, ...) RedisModule_Log(ctx, "verbose", __VA_ARGS__) 8 | #define RM_LOG_NOTICE(ctx, ...) RedisModule_Log(ctx, "notice", __VA_ARGS__) 9 | #define RM_LOG_WARNING(ctx, ...) RedisModule_Log(ctx, "warning", __VA_ARGS__) 10 | 11 | #endif -------------------------------------------------------------------------------- /rmutil/periodic.c: -------------------------------------------------------------------------------- 1 | #define REDISMODULE_EXPERIMENTAL_API 2 | #include "periodic.h" 3 | #include 4 | #include 5 | #include 6 | 7 | typedef struct RMUtilTimer { 8 | RMutilTimerFunc cb; 9 | RMUtilTimerTerminationFunc onTerm; 10 | void *privdata; 11 | struct timespec interval; 12 | pthread_t thread; 13 | pthread_mutex_t lock; 14 | pthread_cond_t cond; 15 | } RMUtilTimer; 16 | 17 | static struct timespec timespecAdd(struct timespec *a, struct timespec *b) { 18 | struct timespec ret; 19 | ret.tv_sec = a->tv_sec + b->tv_sec; 20 | 21 | long long ns = a->tv_nsec + b->tv_nsec; 22 | ret.tv_sec += ns / 1000000000; 23 | ret.tv_nsec = ns % 1000000000; 24 | return ret; 25 | } 26 | 27 | static void *rmutilTimer_Loop(void *ctx) { 28 | RMUtilTimer *tm = ctx; 29 | 30 | int rc = ETIMEDOUT; 31 | struct timespec ts; 32 | 33 | pthread_mutex_lock(&tm->lock); 34 | while (rc != 0) { 35 | clock_gettime(CLOCK_REALTIME, &ts); 36 | struct timespec timeout = timespecAdd(&ts, &tm->interval); 37 | if ((rc = pthread_cond_timedwait(&tm->cond, &tm->lock, &timeout)) == ETIMEDOUT) { 38 | 39 | // Create a thread safe context if we're running inside redis 40 | RedisModuleCtx *rctx = NULL; 41 | if (RedisModule_GetThreadSafeContext) rctx = RedisModule_GetThreadSafeContext(NULL); 42 | 43 | // call our callback... 44 | tm->cb(rctx, tm->privdata); 45 | 46 | // If needed - free the thread safe context. 47 | // It's up to the user to decide whether automemory is active there 48 | if (rctx) RedisModule_FreeThreadSafeContext(rctx); 49 | } 50 | if (rc == EINVAL) { 51 | perror("Error waiting for condition"); 52 | break; 53 | } 54 | } 55 | 56 | // call the termination callback if needed 57 | if (tm->onTerm != NULL) { 58 | tm->onTerm(tm->privdata); 59 | } 60 | 61 | // free resources associated with the timer 62 | pthread_cond_destroy(&tm->cond); 63 | free(tm); 64 | 65 | return NULL; 66 | } 67 | 68 | /* set a new frequency for the timer. This will take effect AFTER the next trigger */ 69 | void RMUtilTimer_SetInterval(struct RMUtilTimer *t, struct timespec newInterval) { 70 | t->interval = newInterval; 71 | } 72 | 73 | RMUtilTimer *RMUtil_NewPeriodicTimer(RMutilTimerFunc cb, RMUtilTimerTerminationFunc onTerm, 74 | void *privdata, struct timespec interval) { 75 | RMUtilTimer *ret = malloc(sizeof(*ret)); 76 | *ret = (RMUtilTimer){ 77 | .privdata = privdata, .interval = interval, .cb = cb, .onTerm = onTerm, 78 | }; 79 | pthread_cond_init(&ret->cond, NULL); 80 | pthread_mutex_init(&ret->lock, NULL); 81 | 82 | pthread_create(&ret->thread, NULL, rmutilTimer_Loop, ret); 83 | return ret; 84 | } 85 | 86 | int RMUtilTimer_Terminate(struct RMUtilTimer *t) { 87 | return pthread_cond_signal(&t->cond); 88 | } 89 | -------------------------------------------------------------------------------- /rmutil/periodic.h: -------------------------------------------------------------------------------- 1 | #ifndef RMUTIL_PERIODIC_H_ 2 | #define RMUTIL_PERIODIC_H_ 3 | #include 4 | #include 5 | 6 | /** periodic.h - Utility periodic timer running a task repeatedly every given time interval */ 7 | 8 | /* RMUtilTimer - opaque context for the timer */ 9 | struct RMUtilTimer; 10 | 11 | /* RMutilTimerFunc - callback type for timer tasks. The ctx is a thread-safe redis module context 12 | * that should be locked/unlocked by the callback when running stuff against redis. privdata is 13 | * pre-existing private data */ 14 | typedef void (*RMutilTimerFunc)(RedisModuleCtx *ctx, void *privdata); 15 | 16 | typedef void (*RMUtilTimerTerminationFunc)(void *privdata); 17 | 18 | /* Create and start a new periodic timer. Each timer has its own thread and can only be run and 19 | * stopped once. The timer runs `cb` every `interval` with `privdata` passed to the callback. */ 20 | struct RMUtilTimer *RMUtil_NewPeriodicTimer(RMutilTimerFunc cb, RMUtilTimerTerminationFunc onTerm, 21 | void *privdata, struct timespec interval); 22 | 23 | /* set a new frequency for the timer. This will take effect AFTER the next trigger */ 24 | void RMUtilTimer_SetInterval(struct RMUtilTimer *t, struct timespec newInterval); 25 | 26 | /* Stop the timer loop, call the termination callbck to free up any resources linked to the timer, 27 | * and free the timer after stopping. 28 | * 29 | * This function doesn't wait for the thread to terminate, as it may cause a race condition if the 30 | * timer's callback is waiting for the redis global lock. 31 | * Instead you should make sure any resources are freed by the callback after the thread loop is 32 | * finished. 33 | * 34 | * The timer is freed automatically, so the callback doesn't need to do anything about it. 35 | * The callback gets the timer's associated privdata as its argument. 36 | * 37 | * If no callback is specified we do not free up privdata. If privdata is NULL we still call the 38 | * callback, as it may log stuff or free global resources. 39 | */ 40 | int RMUtilTimer_Terminate(struct RMUtilTimer *t); 41 | 42 | /* DEPRECATED - do not use this function (well now you can't), use terminate instead 43 | Free the timer context. The caller should be responsible for freeing the private data at this 44 | * point */ 45 | // void RMUtilTimer_Free(struct RMUtilTimer *t); 46 | #endif -------------------------------------------------------------------------------- /rmutil/priority_queue.c: -------------------------------------------------------------------------------- 1 | #include "priority_queue.h" 2 | #include "heap.h" 3 | 4 | PriorityQueue *__newPriorityQueueSize(size_t elemSize, size_t cap, int (*cmp)(void *, void *)) { 5 | PriorityQueue *pq = malloc(sizeof(PriorityQueue)); 6 | pq->v = __newVectorSize(elemSize, cap); 7 | pq->cmp = cmp; 8 | return pq; 9 | } 10 | 11 | inline size_t Priority_Queue_Size(PriorityQueue *pq) { 12 | return Vector_Size(pq->v); 13 | } 14 | 15 | inline int Priority_Queue_Top(PriorityQueue *pq, void *ptr) { 16 | return Vector_Get(pq->v, 0, ptr); 17 | } 18 | 19 | inline size_t __priority_Queue_PushPtr(PriorityQueue *pq, void *elem) { 20 | size_t top = __vector_PushPtr(pq->v, elem); 21 | Heap_Push(pq->v, 0, top, pq->cmp); 22 | return top; 23 | } 24 | 25 | inline void Priority_Queue_Pop(PriorityQueue *pq) { 26 | if (pq->v->top == 0) { 27 | return; 28 | } 29 | Heap_Pop(pq->v, 0, pq->v->top, pq->cmp); 30 | pq->v->top--; 31 | } 32 | 33 | void Priority_Queue_Free(PriorityQueue *pq) { 34 | Vector_Free(pq->v); 35 | free(pq); 36 | } 37 | -------------------------------------------------------------------------------- /rmutil/priority_queue.h: -------------------------------------------------------------------------------- 1 | #ifndef __PRIORITY_QUEUE_H__ 2 | #define __PRIORITY_QUEUE_H__ 3 | 4 | #include "vector.h" 5 | 6 | /* Priority queue 7 | * Priority queues are designed such that its first element is always the greatest of the elements it contains. 8 | * This context is similar to a heap, where elements can be inserted at any moment, and only the max heap element can be 9 | * retrieved (the one at the top in the priority queue). 10 | * Priority queues are implemented as Vectors. Elements are popped from the "back" of Vector, which is known as the top 11 | * of the priority queue. 12 | */ 13 | typedef struct { 14 | Vector *v; 15 | 16 | int (*cmp)(void *, void *); 17 | } PriorityQueue; 18 | 19 | /* Construct priority queue 20 | * Constructs a priority_queue container adaptor object. 21 | */ 22 | PriorityQueue *__newPriorityQueueSize(size_t elemSize, size_t cap, int (*cmp)(void *, void *)); 23 | 24 | #define NewPriorityQueue(type, cap, cmp) __newPriorityQueueSize(sizeof(type), cap, cmp) 25 | 26 | /* Return size 27 | * Returns the number of elements in the priority_queue. 28 | */ 29 | size_t Priority_Queue_Size(PriorityQueue *pq); 30 | 31 | /* Access top element 32 | * Copy the top element in the priority_queue to ptr. 33 | * The top element is the element that compares higher in the priority_queue. 34 | */ 35 | int Priority_Queue_Top(PriorityQueue *pq, void *ptr); 36 | 37 | /* Insert element 38 | * Inserts a new element in the priority_queue. 39 | */ 40 | size_t __priority_Queue_PushPtr(PriorityQueue *pq, void *elem); 41 | 42 | #define Priority_Queue_Push(pq, elem) __priority_Queue_PushPtr(pq, &(typeof(elem)){elem}) 43 | 44 | /* Remove top element 45 | * Removes the element on top of the priority_queue, effectively reducing its size by one. The element removed is the 46 | * one with the highest value. 47 | * The value of this element can be retrieved before being popped by calling Priority_Queue_Top. 48 | */ 49 | void Priority_Queue_Pop(PriorityQueue *pq); 50 | 51 | /* free the priority queue and the underlying data. Does not release its elements if 52 | * they are pointers */ 53 | void Priority_Queue_Free(PriorityQueue *pq); 54 | 55 | #endif //__PRIORITY_QUEUE_H__ 56 | -------------------------------------------------------------------------------- /rmutil/sds.h: -------------------------------------------------------------------------------- 1 | /* SDSLib 2.0 -- A C dynamic strings library 2 | * 3 | * Copyright (c) 2006-2015, Salvatore Sanfilippo 4 | * Copyright (c) 2015, Oran Agra 5 | * Copyright (c) 2015, Redis Labs, Inc 6 | * All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions are met: 10 | * 11 | * * Redistributions of source code must retain the above copyright notice, 12 | * this list of conditions and the following disclaimer. 13 | * * Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * * Neither the name of Redis nor the names of its contributors may be used 17 | * to endorse or promote products derived from this software without 18 | * specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | * POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | #ifndef __SDS_H 34 | #define __SDS_H 35 | 36 | #define SDS_MAX_PREALLOC (1024*1024) 37 | 38 | #include 39 | #include 40 | #include 41 | 42 | typedef char *sds; 43 | 44 | /* Note: sdshdr5 is never used, we just access the flags byte directly. 45 | * However is here to document the layout of type 5 SDS strings. */ 46 | struct __attribute__ ((__packed__)) sdshdr5 { 47 | unsigned char flags; /* 3 lsb of type, and 5 msb of string length */ 48 | char buf[]; 49 | }; 50 | struct __attribute__ ((__packed__)) sdshdr8 { 51 | uint8_t len; /* used */ 52 | uint8_t alloc; /* excluding the header and null terminator */ 53 | unsigned char flags; /* 3 lsb of type, 5 unused bits */ 54 | char buf[]; 55 | }; 56 | struct __attribute__ ((__packed__)) sdshdr16 { 57 | uint16_t len; /* used */ 58 | uint16_t alloc; /* excluding the header and null terminator */ 59 | unsigned char flags; /* 3 lsb of type, 5 unused bits */ 60 | char buf[]; 61 | }; 62 | struct __attribute__ ((__packed__)) sdshdr32 { 63 | uint32_t len; /* used */ 64 | uint32_t alloc; /* excluding the header and null terminator */ 65 | unsigned char flags; /* 3 lsb of type, 5 unused bits */ 66 | char buf[]; 67 | }; 68 | struct __attribute__ ((__packed__)) sdshdr64 { 69 | uint64_t len; /* used */ 70 | uint64_t alloc; /* excluding the header and null terminator */ 71 | unsigned char flags; /* 3 lsb of type, 5 unused bits */ 72 | char buf[]; 73 | }; 74 | 75 | #define SDS_TYPE_5 0 76 | #define SDS_TYPE_8 1 77 | #define SDS_TYPE_16 2 78 | #define SDS_TYPE_32 3 79 | #define SDS_TYPE_64 4 80 | #define SDS_TYPE_MASK 7 81 | #define SDS_TYPE_BITS 3 82 | #define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (void*)((s)-(sizeof(struct sdshdr##T))); 83 | #define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)))) 84 | #define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS) 85 | 86 | static inline size_t sdslen(const sds s) { 87 | unsigned char flags = s[-1]; 88 | switch(flags&SDS_TYPE_MASK) { 89 | case SDS_TYPE_5: 90 | return SDS_TYPE_5_LEN(flags); 91 | case SDS_TYPE_8: 92 | return SDS_HDR(8,s)->len; 93 | case SDS_TYPE_16: 94 | return SDS_HDR(16,s)->len; 95 | case SDS_TYPE_32: 96 | return SDS_HDR(32,s)->len; 97 | case SDS_TYPE_64: 98 | return SDS_HDR(64,s)->len; 99 | } 100 | return 0; 101 | } 102 | 103 | static inline size_t sdsavail(const sds s) { 104 | unsigned char flags = s[-1]; 105 | switch(flags&SDS_TYPE_MASK) { 106 | case SDS_TYPE_5: { 107 | return 0; 108 | } 109 | case SDS_TYPE_8: { 110 | SDS_HDR_VAR(8,s); 111 | return sh->alloc - sh->len; 112 | } 113 | case SDS_TYPE_16: { 114 | SDS_HDR_VAR(16,s); 115 | return sh->alloc - sh->len; 116 | } 117 | case SDS_TYPE_32: { 118 | SDS_HDR_VAR(32,s); 119 | return sh->alloc - sh->len; 120 | } 121 | case SDS_TYPE_64: { 122 | SDS_HDR_VAR(64,s); 123 | return sh->alloc - sh->len; 124 | } 125 | } 126 | return 0; 127 | } 128 | 129 | static inline void sdssetlen(sds s, size_t newlen) { 130 | unsigned char flags = s[-1]; 131 | switch(flags&SDS_TYPE_MASK) { 132 | case SDS_TYPE_5: 133 | { 134 | unsigned char *fp = ((unsigned char*)s)-1; 135 | *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); 136 | } 137 | break; 138 | case SDS_TYPE_8: 139 | SDS_HDR(8,s)->len = newlen; 140 | break; 141 | case SDS_TYPE_16: 142 | SDS_HDR(16,s)->len = newlen; 143 | break; 144 | case SDS_TYPE_32: 145 | SDS_HDR(32,s)->len = newlen; 146 | break; 147 | case SDS_TYPE_64: 148 | SDS_HDR(64,s)->len = newlen; 149 | break; 150 | } 151 | } 152 | 153 | static inline void sdsinclen(sds s, size_t inc) { 154 | unsigned char flags = s[-1]; 155 | switch(flags&SDS_TYPE_MASK) { 156 | case SDS_TYPE_5: 157 | { 158 | unsigned char *fp = ((unsigned char*)s)-1; 159 | unsigned char newlen = SDS_TYPE_5_LEN(flags)+inc; 160 | *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); 161 | } 162 | break; 163 | case SDS_TYPE_8: 164 | SDS_HDR(8,s)->len += inc; 165 | break; 166 | case SDS_TYPE_16: 167 | SDS_HDR(16,s)->len += inc; 168 | break; 169 | case SDS_TYPE_32: 170 | SDS_HDR(32,s)->len += inc; 171 | break; 172 | case SDS_TYPE_64: 173 | SDS_HDR(64,s)->len += inc; 174 | break; 175 | } 176 | } 177 | 178 | /* sdsalloc() = sdsavail() + sdslen() */ 179 | static inline size_t sdsalloc(const sds s) { 180 | unsigned char flags = s[-1]; 181 | switch(flags&SDS_TYPE_MASK) { 182 | case SDS_TYPE_5: 183 | return SDS_TYPE_5_LEN(flags); 184 | case SDS_TYPE_8: 185 | return SDS_HDR(8,s)->alloc; 186 | case SDS_TYPE_16: 187 | return SDS_HDR(16,s)->alloc; 188 | case SDS_TYPE_32: 189 | return SDS_HDR(32,s)->alloc; 190 | case SDS_TYPE_64: 191 | return SDS_HDR(64,s)->alloc; 192 | } 193 | return 0; 194 | } 195 | 196 | static inline void sdssetalloc(sds s, size_t newlen) { 197 | unsigned char flags = s[-1]; 198 | switch(flags&SDS_TYPE_MASK) { 199 | case SDS_TYPE_5: 200 | /* Nothing to do, this type has no total allocation info. */ 201 | break; 202 | case SDS_TYPE_8: 203 | SDS_HDR(8,s)->alloc = newlen; 204 | break; 205 | case SDS_TYPE_16: 206 | SDS_HDR(16,s)->alloc = newlen; 207 | break; 208 | case SDS_TYPE_32: 209 | SDS_HDR(32,s)->alloc = newlen; 210 | break; 211 | case SDS_TYPE_64: 212 | SDS_HDR(64,s)->alloc = newlen; 213 | break; 214 | } 215 | } 216 | 217 | sds sdsnewlen(const void *init, size_t initlen); 218 | sds sdsnew(const char *init); 219 | sds sdsempty(void); 220 | sds sdsdup(const sds s); 221 | void sdsfree(sds s); 222 | sds sdsgrowzero(sds s, size_t len); 223 | sds sdscatlen(sds s, const void *t, size_t len); 224 | sds sdscat(sds s, const char *t); 225 | sds sdscatsds(sds s, const sds t); 226 | sds sdscpylen(sds s, const char *t, size_t len); 227 | sds sdscpy(sds s, const char *t); 228 | 229 | sds sdscatvprintf(sds s, const char *fmt, va_list ap); 230 | #ifdef __GNUC__ 231 | sds sdscatprintf(sds s, const char *fmt, ...) 232 | __attribute__((format(printf, 2, 3))); 233 | #else 234 | sds sdscatprintf(sds s, const char *fmt, ...); 235 | #endif 236 | 237 | sds sdscatfmt(sds s, char const *fmt, ...); 238 | sds sdstrim(sds s, const char *cset); 239 | void sdsrange(sds s, int start, int end); 240 | void sdsupdatelen(sds s); 241 | void sdsclear(sds s); 242 | int sdscmp(const sds s1, const sds s2); 243 | sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count); 244 | void sdsfreesplitres(sds *tokens, int count); 245 | void sdstolower(sds s); 246 | void sdstoupper(sds s); 247 | sds sdsfromlonglong(long long value); 248 | sds sdscatrepr(sds s, const char *p, size_t len); 249 | sds *sdssplitargs(const char *line, int *argc); 250 | sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen); 251 | sds sdsjoin(char **argv, int argc, char *sep); 252 | sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen); 253 | 254 | /* Low level functions exposed to the user API */ 255 | sds sdsMakeRoomFor(sds s, size_t addlen); 256 | void sdsIncrLen(sds s, int incr); 257 | sds sdsRemoveFreeSpace(sds s); 258 | size_t sdsAllocSize(sds s); 259 | void *sdsAllocPtr(sds s); 260 | 261 | /* Export the allocator used by SDS to the program using SDS. 262 | * Sometimes the program SDS is linked to, may use a different set of 263 | * allocators, but may want to allocate or free things that SDS will 264 | * respectively free or allocate. */ 265 | void *sds_malloc(size_t size); 266 | void *sds_realloc(void *ptr, size_t size); 267 | void sds_free(void *ptr); 268 | 269 | #ifdef REDIS_TEST 270 | int sdsTest(int argc, char *argv[]); 271 | #endif 272 | 273 | #endif 274 | -------------------------------------------------------------------------------- /rmutil/sdsalloc.h: -------------------------------------------------------------------------------- 1 | /* SDSLib 2.0 -- A C dynamic strings library 2 | * 3 | * Copyright (c) 2006-2015, Salvatore Sanfilippo 4 | * Copyright (c) 2015, Redis Labs, Inc 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * * Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * * Neither the name of Redis nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /* SDS allocator selection. 33 | * 34 | * This file is used in order to change the SDS allocator at compile time. 35 | * Just define the following defines to what you want to use. Also add 36 | * the include of your alternate allocator if needed (not needed in order 37 | * to use the default libc allocator). */ 38 | 39 | #if defined(__MACH__) 40 | #include 41 | #else 42 | #include 43 | #endif 44 | //#include "zmalloc.h" 45 | #define s_malloc malloc 46 | #define s_realloc realloc 47 | #define s_free free 48 | -------------------------------------------------------------------------------- /rmutil/strings.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "strings.h" 5 | #include "alloc.h" 6 | 7 | #include "sds.h" 8 | 9 | // RedisModuleString *RMUtil_CreateFormattedString(RedisModuleCtx *ctx, const char *fmt, ...) { 10 | // sds s = sdsempty(); 11 | 12 | // va_list ap; 13 | // va_start(ap, fmt); 14 | // s = sdscatvprintf(s, fmt, ap); 15 | // va_end(ap); 16 | 17 | // RedisModuleString *ret = RedisModule_CreateString(ctx, (const char *)s, sdslen(s)); 18 | // sdsfree(s); 19 | // return ret; 20 | // } 21 | 22 | int RMUtil_StringEquals(RedisModuleString *s1, RedisModuleString *s2) { 23 | 24 | const char *c1, *c2; 25 | size_t l1, l2; 26 | c1 = RedisModule_StringPtrLen(s1, &l1); 27 | c2 = RedisModule_StringPtrLen(s2, &l2); 28 | if (l1 != l2) return 0; 29 | 30 | return strncmp(c1, c2, l1) == 0; 31 | } 32 | 33 | int RMUtil_StringEqualsC(RedisModuleString *s1, const char *s2) { 34 | 35 | const char *c1; 36 | size_t l1, l2 = strlen(s2); 37 | c1 = RedisModule_StringPtrLen(s1, &l1); 38 | if (l1 != l2) return 0; 39 | 40 | return strncmp(c1, s2, l1) == 0; 41 | } 42 | int RMUtil_StringEqualsCaseC(RedisModuleString *s1, const char *s2) { 43 | 44 | const char *c1; 45 | size_t l1, l2 = strlen(s2); 46 | c1 = RedisModule_StringPtrLen(s1, &l1); 47 | if (l1 != l2) return 0; 48 | 49 | return strncasecmp(c1, s2, l1) == 0; 50 | } 51 | 52 | void RMUtil_StringToLower(RedisModuleString *s) { 53 | 54 | size_t l; 55 | char *c = (char *)RedisModule_StringPtrLen(s, &l); 56 | size_t i; 57 | for (i = 0; i < l; i++) { 58 | *c = tolower(*c); 59 | ++c; 60 | } 61 | } 62 | 63 | void RMUtil_StringToUpper(RedisModuleString *s) { 64 | size_t l; 65 | char *c = (char *)RedisModule_StringPtrLen(s, &l); 66 | size_t i; 67 | for (i = 0; i < l; i++) { 68 | *c = toupper(*c); 69 | ++c; 70 | } 71 | } 72 | 73 | void RMUtil_StringConvert(RedisModuleString **rs, const char **ss, size_t n, int options) { 74 | for (size_t ii = 0; ii < n; ++ii) { 75 | const char *p = RedisModule_StringPtrLen(rs[ii], NULL); 76 | if (options & RMUTIL_STRINGCONVERT_COPY) { 77 | p = strdup(p); 78 | } 79 | ss[ii] = p; 80 | } 81 | } -------------------------------------------------------------------------------- /rmutil/strings.h: -------------------------------------------------------------------------------- 1 | #ifndef __RMUTIL_STRINGS_H__ 2 | #define __RMUTIL_STRINGS_H__ 3 | 4 | #include 5 | 6 | /* 7 | * Create a new RedisModuleString object from a printf-style format and arguments. 8 | * Note that RedisModuleString objects CANNOT be used as formatting arguments. 9 | */ 10 | // DEPRECATED since it was added to the RedisModule API. Replaced with a macro below 11 | // RedisModuleString *RMUtil_CreateFormattedString(RedisModuleCtx *ctx, const char *fmt, ...); 12 | #define RMUtil_CreateFormattedString RedisModule_CreateStringPrintf 13 | 14 | /* Return 1 if the two strings are equal. Case *sensitive* */ 15 | int RMUtil_StringEquals(RedisModuleString *s1, RedisModuleString *s2); 16 | 17 | /* Return 1 if the string is equal to a C NULL terminated string. Case *sensitive* */ 18 | int RMUtil_StringEqualsC(RedisModuleString *s1, const char *s2); 19 | 20 | /* Return 1 if the string is equal to a C NULL terminated string. Case *insensitive* */ 21 | int RMUtil_StringEqualsCaseC(RedisModuleString *s1, const char *s2); 22 | 23 | /* Converts a redis string to lowercase in place without reallocating anything */ 24 | void RMUtil_StringToLower(RedisModuleString *s); 25 | 26 | /* Converts a redis string to uppercase in place without reallocating anything */ 27 | void RMUtil_StringToUpper(RedisModuleString *s); 28 | 29 | // If set, copy the strings using strdup rather than simply storing pointers. 30 | #define RMUTIL_STRINGCONVERT_COPY 1 31 | 32 | /** 33 | * Convert one or more RedisModuleString objects into `const char*`. 34 | * Both rs and ss are arrays, and should be of length. 35 | * Options may be 0 or `RMUTIL_STRINGCONVERT_COPY` 36 | */ 37 | void RMUtil_StringConvert(RedisModuleString **rs, const char **ss, size_t n, int options); 38 | #endif 39 | -------------------------------------------------------------------------------- /rmutil/test.h: -------------------------------------------------------------------------------- 1 | #ifndef __TESTUTIL_H__ 2 | #define __TESTUTIL_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | static int numTests = 0; 9 | static int numAsserts = 0; 10 | 11 | #define TESTFUNC(f) \ 12 | printf(" Testing %s\t\t", __STRING(f)); \ 13 | numTests++; \ 14 | fflush(stdout); \ 15 | if (f()) { \ 16 | printf(" %s FAILED!\n", __STRING(f)); \ 17 | exit(1); \ 18 | } else \ 19 | printf("[PASS]\n"); 20 | 21 | #define ASSERTM(expr, ...) \ 22 | if (!(expr)) { \ 23 | fprintf(stderr, "%s:%d: Assertion '%s' Failed: " __VA_ARGS__ "\n", __FILE__, __LINE__, \ 24 | __STRING(expr)); \ 25 | return -1; \ 26 | } \ 27 | numAsserts++; 28 | 29 | #define ASSERT(expr) \ 30 | if (!(expr)) { \ 31 | fprintf(stderr, "%s:%d Assertion '%s' Failed\n", __FILE__, __LINE__, __STRING(expr)); \ 32 | return -1; \ 33 | } \ 34 | numAsserts++; 35 | 36 | #define ASSERT_STRING_EQ(s1, s2) ASSERT(!strcmp(s1, s2)); 37 | 38 | #define ASSERT_EQUAL(x, y, ...) \ 39 | if (x != y) { \ 40 | fprintf(stderr, "%s:%d: ", __FILE__, __LINE__); \ 41 | fprintf(stderr, "%g != %g: " __VA_ARGS__ "\n", (double)x, (double)y); \ 42 | return -1; \ 43 | } \ 44 | numAsserts++; 45 | 46 | #define FAIL(fmt, ...) \ 47 | { \ 48 | fprintf(stderr, "%s:%d: FAIL: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); \ 49 | return -1; \ 50 | } 51 | 52 | #define RETURN_TEST_SUCCESS return 0; 53 | #define TEST_CASE(x, block) \ 54 | int x { \ 55 | block; \ 56 | return 0 \ 57 | } 58 | 59 | #define PRINT_TEST_SUMMARY printf("\nTotal: %d tests and %d assertions OK\n", numTests, numAsserts); 60 | 61 | #define TEST_MAIN(body) \ 62 | int main(int argc, char **argv) { \ 63 | printf("Starting Test '%s'...\n", argv[0]); \ 64 | body; \ 65 | PRINT_TEST_SUMMARY; \ 66 | printf("\n--------------------\n\n"); \ 67 | return 0; \ 68 | } 69 | #endif -------------------------------------------------------------------------------- /rmutil/test_heap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "heap.h" 3 | #include "assert.h" 4 | 5 | int cmp(void *a, void *b) { 6 | int *__a = (int *) a; 7 | int *__b = (int *) b; 8 | return *__a - *__b; 9 | } 10 | 11 | int main(int argc, char **argv) { 12 | int myints[] = {10, 20, 30, 5, 15}; 13 | Vector *v = NewVector(int, 5); 14 | for (int i = 0; i < 5; i++) { 15 | Vector_Push(v, myints[i]); 16 | } 17 | 18 | Make_Heap(v, 0, v->top, cmp); 19 | 20 | int n; 21 | Vector_Get(v, 0, &n); 22 | assert(30 == n); 23 | 24 | Heap_Pop(v, 0, v->top, cmp); 25 | v->top = 4; 26 | Vector_Get(v, 0, &n); 27 | assert(20 == n); 28 | 29 | Vector_Push(v, 99); 30 | Heap_Push(v, 0, v->top, cmp); 31 | Vector_Get(v, 0, &n); 32 | assert(99 == n); 33 | 34 | Vector_Free(v); 35 | printf("PASS!\n"); 36 | return 0; 37 | } 38 | 39 | -------------------------------------------------------------------------------- /rmutil/test_periodic.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "periodic.h" 5 | #include "assert.h" 6 | #include "test.h" 7 | 8 | void timerCb(RedisModuleCtx *ctx, void *p) { 9 | int *x = p; 10 | (*x)++; 11 | } 12 | 13 | int testPeriodic() { 14 | int x = 0; 15 | struct RMUtilTimer *tm = RMUtil_NewPeriodicTimer( 16 | timerCb, NULL, &x, (struct timespec){.tv_sec = 0, .tv_nsec = 10000000}); 17 | 18 | sleep(1); 19 | 20 | ASSERT_EQUAL(0, RMUtilTimer_Terminate(tm)); 21 | ASSERT(x > 0); 22 | ASSERT(x <= 100); 23 | return 0; 24 | } 25 | 26 | TEST_MAIN({ TESTFUNC(testPeriodic); }); 27 | -------------------------------------------------------------------------------- /rmutil/test_priority_queue.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "assert.h" 3 | #include "priority_queue.h" 4 | 5 | int cmp(void* i1, void* i2) { 6 | int *__i1 = (int*) i1; 7 | int *__i2 = (int*) i2; 8 | return *__i1 - *__i2; 9 | } 10 | 11 | int main(int argc, char **argv) { 12 | PriorityQueue *pq = NewPriorityQueue(int, 10, cmp); 13 | assert(0 == Priority_Queue_Size(pq)); 14 | 15 | for (int i = 0; i < 5; i++) { 16 | Priority_Queue_Push(pq, i); 17 | } 18 | assert(5 == Priority_Queue_Size(pq)); 19 | 20 | Priority_Queue_Pop(pq); 21 | assert(4 == Priority_Queue_Size(pq)); 22 | 23 | Priority_Queue_Push(pq, 10); 24 | Priority_Queue_Push(pq, 20); 25 | Priority_Queue_Push(pq, 15); 26 | int n; 27 | Priority_Queue_Top(pq, &n); 28 | assert(20 == n); 29 | 30 | Priority_Queue_Pop(pq); 31 | Priority_Queue_Top(pq, &n); 32 | assert(15 == n); 33 | 34 | Priority_Queue_Free(pq); 35 | printf("PASS!\n"); 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /rmutil/test_util.h: -------------------------------------------------------------------------------- 1 | #ifndef __TEST_UTIL_H__ 2 | #define __TEST_UTIL_H__ 3 | 4 | #include "util.h" 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | #define RMUtil_Test(f) \ 11 | if (argc < 2 || RMUtil_ArgExists(__STRING(f), argv, argc, 1)) { \ 12 | int rc = f(ctx); \ 13 | if (rc != REDISMODULE_OK) { \ 14 | RedisModule_ReplyWithError(ctx, "Test " __STRING(f) " FAILED"); \ 15 | return REDISMODULE_ERR;\ 16 | }\ 17 | } 18 | 19 | 20 | #define RMUtil_Assert(expr) if (!(expr)) { fprintf (stderr, "Assertion '%s' Failed\n", __STRING(expr)); return REDISMODULE_ERR; } 21 | 22 | #define RMUtil_AssertNullReply(rep) RMUtil_Assert( \ 23 | RedisModule_CallReplyType(rep) == REDISMODULE_REPLY_NULL || RedisModule_CreateStringFromCallReply(rep) == NULL) 24 | 25 | #define RMUtil_AssertReplyEquals(rep, cstr) RMUtil_Assert( \ 26 | RMUtil_StringEquals(RedisModule_CreateStringFromCallReply(rep), RedisModule_CreateString(ctx, cstr, strlen(cstr))) \ 27 | ) 28 | # 29 | 30 | /** 31 | * Create an arg list to pass to a redis command handler manually, based on the format in fmt. 32 | * The accepted format specifiers are: 33 | * c - for null terminated c strings 34 | * s - for RedisModuleString* objects 35 | * l - for longs 36 | * 37 | * Example: RMUtil_MakeArgs(ctx, &argc, "clc", "hello", 1337, "world"); 38 | * 39 | * Returns an array of RedisModuleString pointers. The size of the array is store in argcp 40 | */ 41 | RedisModuleString **RMUtil_MakeArgs(RedisModuleCtx *ctx, int *argcp, const char *fmt, ...) { 42 | 43 | va_list ap; 44 | va_start(ap, fmt); 45 | RedisModuleString **argv = calloc(strlen(fmt), sizeof(RedisModuleString*)); 46 | int argc = 0; 47 | const char *p = fmt; 48 | while(*p) { 49 | if (*p == 'c') { 50 | char *cstr = va_arg(ap,char*); 51 | argv[argc++] = RedisModule_CreateString(ctx, cstr, strlen(cstr)); 52 | } else if (*p == 's') { 53 | argv[argc++] = va_arg(ap,void*);; 54 | } else if (*p == 'l') { 55 | long ll = va_arg(ap,long long); 56 | argv[argc++] = RedisModule_CreateStringFromLongLong(ctx, ll); 57 | } else { 58 | goto fmterr; 59 | } 60 | p++; 61 | } 62 | *argcp = argc; 63 | 64 | return argv; 65 | fmterr: 66 | free(argv); 67 | return NULL; 68 | } 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /rmutil/test_vector.c: -------------------------------------------------------------------------------- 1 | #include "vector.h" 2 | #include 3 | #include "test.h" 4 | 5 | int testVector() { 6 | 7 | Vector *v = NewVector(int, 1); 8 | ASSERT(v != NULL); 9 | // Vector_Put(v, 0, 1); 10 | // Vector_Put(v, 1, 3); 11 | for (int i = 0; i < 10; i++) { 12 | Vector_Push(v, i); 13 | } 14 | ASSERT_EQUAL(10, Vector_Size(v)); 15 | ASSERT_EQUAL(16, Vector_Cap(v)); 16 | 17 | for (int i = 0; i < Vector_Size(v); i++) { 18 | int n; 19 | int rc = Vector_Get(v, i, &n); 20 | ASSERT_EQUAL(1, rc); 21 | // printf("%d %d\n", rc, n); 22 | 23 | ASSERT_EQUAL(n, i); 24 | } 25 | 26 | Vector_Free(v); 27 | 28 | v = NewVector(char *, 0); 29 | int N = 4; 30 | char *strings[4] = {"hello", "world", "foo", "bar"}; 31 | 32 | for (int i = 0; i < N; i++) { 33 | Vector_Push(v, strings[i]); 34 | } 35 | ASSERT_EQUAL(N, Vector_Size(v)); 36 | ASSERT(Vector_Cap(v) >= N); 37 | 38 | for (int i = 0; i < Vector_Size(v); i++) { 39 | char *x; 40 | int rc = Vector_Get(v, i, &x); 41 | ASSERT_EQUAL(1, rc); 42 | ASSERT_STRING_EQ(x, strings[i]); 43 | } 44 | 45 | int rc = Vector_Get(v, 100, NULL); 46 | ASSERT_EQUAL(0, rc); 47 | 48 | Vector_Free(v); 49 | 50 | return 0; 51 | // Vector_Push(v, "hello"); 52 | // Vector_Push(v, "world"); 53 | // char *x = NULL; 54 | // int rc = Vector_Getx(v, 0, &x); 55 | // printf("rc: %d got %s\n", rc, x); 56 | } 57 | 58 | TEST_MAIN({ TESTFUNC(testVector); }); 59 | -------------------------------------------------------------------------------- /rmutil/util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #define REDISMODULE_EXPERIMENTAL_API 10 | #include 11 | #include "util.h" 12 | 13 | /** 14 | Check if an argument exists in an argument list (argv,argc), starting at offset. 15 | @return 0 if it doesn't exist, otherwise the offset it exists in 16 | */ 17 | int RMUtil_ArgExists(const char *arg, RedisModuleString **argv, int argc, int offset) { 18 | 19 | size_t larg = strlen(arg); 20 | for (; offset < argc; offset++) { 21 | size_t l; 22 | const char *carg = RedisModule_StringPtrLen(argv[offset], &l); 23 | if (l != larg) continue; 24 | if (carg != NULL && strncasecmp(carg, arg, larg) == 0) { 25 | return offset; 26 | } 27 | } 28 | return 0; 29 | } 30 | 31 | /** 32 | Check if an argument exists in an argument list (argv,argc) 33 | @return -1 if it doesn't exist, otherwise the offset it exists in 34 | */ 35 | int RMUtil_ArgIndex(const char *arg, RedisModuleString **argv, int argc) { 36 | 37 | size_t larg = strlen(arg); 38 | for (int offset = 0; offset < argc; offset++) { 39 | size_t l; 40 | const char *carg = RedisModule_StringPtrLen(argv[offset], &l); 41 | if (l != larg) continue; 42 | if (carg != NULL && strncasecmp(carg, arg, larg) == 0) { 43 | return offset; 44 | } 45 | } 46 | return -1; 47 | } 48 | 49 | RMUtilInfo *RMUtil_GetRedisInfo(RedisModuleCtx *ctx) { 50 | 51 | RedisModuleCallReply *r = RedisModule_Call(ctx, "INFO", "c", "all"); 52 | if (r == NULL || RedisModule_CallReplyType(r) == REDISMODULE_REPLY_ERROR) { 53 | return NULL; 54 | } 55 | 56 | int cap = 100; // rough estimate of info lines 57 | RMUtilInfo *info = malloc(sizeof(RMUtilInfo)); 58 | info->entries = calloc(cap, sizeof(RMUtilInfoEntry)); 59 | 60 | int i = 0; 61 | size_t sz; 62 | char *text = (char *)RedisModule_CallReplyStringPtr(r, &sz); 63 | 64 | char *line = text; 65 | while (line && line < text + sz) { 66 | char *line = strsep(&text, "\r\n"); 67 | if (line == NULL) break; 68 | 69 | if (!(*line >= 'a' && *line <= 'z')) { // skip non entry lines 70 | continue; 71 | } 72 | 73 | char *key = strsep(&line, ":"); 74 | info->entries[i].key = strdup(key); 75 | info->entries[i].val = strdup(line); 76 | i++; 77 | if (i >= cap) { 78 | cap *= 2; 79 | info->entries = realloc(info->entries, cap * sizeof(RMUtilInfoEntry)); 80 | } 81 | } 82 | info->numEntries = i; 83 | RedisModule_FreeCallReply(r); 84 | return info; 85 | } 86 | void RMUtilRedisInfo_Free(RMUtilInfo *info) { 87 | for (int i = 0; i < info->numEntries; i++) { 88 | free(info->entries[i].key); 89 | free(info->entries[i].val); 90 | } 91 | free(info->entries); 92 | free(info); 93 | } 94 | 95 | int RMUtilInfo_GetInt(RMUtilInfo *info, const char *key, long long *val) { 96 | 97 | const char *p = NULL; 98 | if (!RMUtilInfo_GetString(info, key, &p)) { 99 | return 0; 100 | } 101 | 102 | *val = strtoll(p, NULL, 10); 103 | if ((errno == ERANGE && (*val == LONG_MAX || *val == LONG_MIN)) || (errno != 0 && *val == 0)) { 104 | *val = -1; 105 | return 0; 106 | } 107 | 108 | return 1; 109 | } 110 | 111 | int RMUtilInfo_GetString(RMUtilInfo *info, const char *key, const char **str) { 112 | int i; 113 | for (i = 0; i < info->numEntries; i++) { 114 | if (!strcmp(key, info->entries[i].key)) { 115 | *str = info->entries[i].val; 116 | return 1; 117 | } 118 | } 119 | return 0; 120 | } 121 | 122 | int RMUtilInfo_GetDouble(RMUtilInfo *info, const char *key, double *d) { 123 | const char *p = NULL; 124 | if (!RMUtilInfo_GetString(info, key, &p)) { 125 | printf("not found %s\n", key); 126 | return 0; 127 | } 128 | 129 | *d = strtod(p, NULL); 130 | if ((errno == ERANGE && (*d == HUGE_VAL || *d == -HUGE_VAL)) || (errno != 0 && *d == 0)) { 131 | return 0; 132 | } 133 | 134 | return 1; 135 | } 136 | 137 | /* 138 | c -- pointer to a Null terminated C string pointer. 139 | b -- pointer to a C buffer, followed by pointer to a size_t for its length 140 | s -- pointer to a RedisModuleString 141 | l -- pointer to Long long integer. 142 | d -- pointer to a Double 143 | * -- do not parse this argument at all 144 | */ 145 | int RMUtil_ParseArgs(RedisModuleString **argv, int argc, int offset, const char *fmt, ...) { 146 | va_list ap; 147 | va_start(ap, fmt); 148 | int rc = rmutil_vparseArgs(argv, argc, offset, fmt, ap); 149 | va_end(ap); 150 | return rc; 151 | } 152 | 153 | // Internal function that parses arguments based on the format described above 154 | int rmutil_vparseArgs(RedisModuleString **argv, int argc, int offset, const char *fmt, va_list ap) { 155 | 156 | int i = offset; 157 | char *c = (char *)fmt; 158 | while (*c && i < argc) { 159 | 160 | // read c string 161 | if (*c == 'c') { 162 | char **p = va_arg(ap, char **); 163 | *p = (char *)RedisModule_StringPtrLen(argv[i], NULL); 164 | } else if (*c == 'b') { 165 | char **p = va_arg(ap, char **); 166 | size_t *len = va_arg(ap, size_t *); 167 | *p = (char *)RedisModule_StringPtrLen(argv[i], len); 168 | } else if (*c == 's') { // read redis string 169 | 170 | RedisModuleString **s = va_arg(ap, void *); 171 | *s = argv[i]; 172 | 173 | } else if (*c == 'l') { // read long 174 | long long *l = va_arg(ap, long long *); 175 | 176 | if (RedisModule_StringToLongLong(argv[i], l) != REDISMODULE_OK) { 177 | return REDISMODULE_ERR; 178 | } 179 | } else if (*c == 'd') { // read double 180 | double *d = va_arg(ap, double *); 181 | if (RedisModule_StringToDouble(argv[i], d) != REDISMODULE_OK) { 182 | return REDISMODULE_ERR; 183 | } 184 | } else if (*c == '*') { // skip current arg 185 | // do nothing 186 | } else { 187 | return REDISMODULE_ERR; // WAT? 188 | } 189 | c++; 190 | i++; 191 | } 192 | // if the format is longer than argc, retun an error 193 | if (*c != 0) { 194 | return REDISMODULE_ERR; 195 | } 196 | return REDISMODULE_OK; 197 | } 198 | 199 | int RMUtil_ParseArgsAfter(const char *token, RedisModuleString **argv, int argc, const char *fmt, 200 | ...) { 201 | 202 | int pos = RMUtil_ArgIndex(token, argv, argc); 203 | if (pos < 0) { 204 | return REDISMODULE_ERR; 205 | } 206 | 207 | va_list ap; 208 | va_start(ap, fmt); 209 | int rc = rmutil_vparseArgs(argv, argc, pos + 1, fmt, ap); 210 | va_end(ap); 211 | return rc; 212 | } 213 | 214 | RedisModuleCallReply *RedisModule_CallReplyArrayElementByPath(RedisModuleCallReply *rep, 215 | const char *path) { 216 | if (rep == NULL) return NULL; 217 | 218 | RedisModuleCallReply *ele = rep; 219 | const char *s = path; 220 | char *e; 221 | long idx; 222 | do { 223 | errno = 0; 224 | idx = strtol(s, &e, 10); 225 | 226 | if ((errno == ERANGE && (idx == LONG_MAX || idx == LONG_MIN)) || (errno != 0 && idx == 0) || 227 | (REDISMODULE_REPLY_ARRAY != RedisModule_CallReplyType(ele)) || (s == e)) { 228 | ele = NULL; 229 | break; 230 | } 231 | s = e; 232 | ele = RedisModule_CallReplyArrayElement(ele, idx - 1); 233 | 234 | } while ((ele != NULL) && (*e != '\0')); 235 | 236 | return ele; 237 | } 238 | 239 | int RedisModule_TryGetValue(RedisModuleKey *key, const RedisModuleType *type, void **out) { 240 | if (key == NULL) { 241 | return RMUTIL_VALUE_MISSING; 242 | } 243 | int keytype = RedisModule_KeyType(key); 244 | if (keytype == REDISMODULE_KEYTYPE_EMPTY) { 245 | return RMUTIL_VALUE_EMPTY; 246 | } else if (keytype == REDISMODULE_KEYTYPE_MODULE && RedisModule_ModuleTypeGetType(key) == type) { 247 | *out = RedisModule_ModuleTypeGetValue(key); 248 | return RMUTIL_VALUE_OK; 249 | } else { 250 | return RMUTIL_VALUE_MISMATCH; 251 | } 252 | } 253 | 254 | RedisModuleString **RMUtil_ParseVarArgs(RedisModuleString **argv, int argc, int offset, 255 | const char *keyword, size_t *nargs) { 256 | if (offset > argc) { 257 | return NULL; 258 | } 259 | 260 | argv += offset; 261 | argc -= offset; 262 | 263 | int ix = RMUtil_ArgIndex(keyword, argv, argc); 264 | if (ix < 0) { 265 | return NULL; 266 | } else if (ix >= argc - 1) { 267 | *nargs = RMUTIL_VARARGS_BADARG; 268 | return argv; 269 | } 270 | 271 | argv += (ix + 1); 272 | argc -= (ix + 1); 273 | 274 | long long n = 0; 275 | RMUtil_ParseArgs(argv, argc, 0, "l", &n); 276 | if (n > argc - 1 || n < 0) { 277 | *nargs = RMUTIL_VARARGS_BADARG; 278 | return argv; 279 | } 280 | 281 | *nargs = n; 282 | return argv + 1; 283 | } 284 | 285 | void RMUtil_DefaultAofRewrite(RedisModuleIO *aof, RedisModuleString *key, void *value) { 286 | RedisModuleCtx *ctx = RedisModule_GetThreadSafeContext(NULL); 287 | RedisModuleCallReply *rep = RedisModule_Call(ctx, "DUMP", "s", key); 288 | if (rep != NULL && RedisModule_CallReplyType(rep) == REDISMODULE_REPLY_STRING) { 289 | size_t n; 290 | const char *s = RedisModule_CallReplyStringPtr(rep, &n); 291 | RedisModule_EmitAOF(aof, "RESTORE", "slb", key, 0, s, n); 292 | } else { 293 | RedisModule_Log(RedisModule_GetContextFromIO(aof), "warning", "Failed to emit AOF"); 294 | } 295 | if (rep != NULL) { 296 | RedisModule_FreeCallReply(rep); 297 | } 298 | RedisModule_FreeThreadSafeContext(ctx); 299 | } -------------------------------------------------------------------------------- /rmutil/util.h: -------------------------------------------------------------------------------- 1 | #ifndef __UTIL_H__ 2 | #define __UTIL_H__ 3 | 4 | #include 5 | #include 6 | 7 | /// make sure the response is not NULL or an error, and if it is sends the error to the client and 8 | /// exit the current function 9 | #define RMUTIL_ASSERT_NOERROR(ctx, r) \ 10 | if (r == NULL) { \ 11 | return RedisModule_ReplyWithError(ctx, "ERR reply is NULL"); \ 12 | } else if (RedisModule_CallReplyType(r) == REDISMODULE_REPLY_ERROR) { \ 13 | RedisModule_ReplyWithCallReply(ctx, r); \ 14 | return REDISMODULE_ERR; \ 15 | } 16 | 17 | #define __rmutil_register_cmd(ctx, cmd, f, mode) \ 18 | if (RedisModule_CreateCommand(ctx, cmd, f, mode, 1, 1, 1) == REDISMODULE_ERR) \ 19 | return REDISMODULE_ERR; 20 | 21 | #define RMUtil_RegisterReadCmd(ctx, cmd, f) __rmutil_register_cmd(ctx, cmd, f, "readonly") 22 | 23 | #define RMUtil_RegisterWriteCmd(ctx, cmd, f) __rmutil_register_cmd(ctx, cmd, f, "write") 24 | 25 | #define RMUtil_RegisterWriteDenyOOMCmd(ctx, cmd, f) __rmutil_register_cmd(ctx, cmd, f, "write deny-oom") 26 | 27 | /* RedisModule utilities. */ 28 | 29 | /** DEPRECATED: Return the offset of an arg if it exists in the arg list, or 0 if it's not there */ 30 | int RMUtil_ArgExists(const char *arg, RedisModuleString **argv, int argc, int offset); 31 | 32 | /* Same as argExists but returns -1 if not found. Use this, RMUtil_ArgExists is kept for backwards 33 | compatibility. */ 34 | int RMUtil_ArgIndex(const char *arg, RedisModuleString **argv, int argc); 35 | 36 | /** 37 | Automatically conver the arg list to corresponding variable pointers according to a given format. 38 | You pass it the command arg list and count, the starting offset, a parsing format, and pointers to 39 | the variables. 40 | The format is a string consisting of the following identifiers: 41 | 42 | c -- pointer to a Null terminated C string pointer. 43 | s -- pointer to a RedisModuleString 44 | l -- pointer to Long long integer. 45 | d -- pointer to a Double 46 | * -- do not parse this argument at all 47 | 48 | Example: If I want to parse args[1], args[2] as a long long and double, I do: 49 | double d; 50 | long long l; 51 | RMUtil_ParseArgs(argv, argc, 1, "ld", &l, &d); 52 | */ 53 | int RMUtil_ParseArgs(RedisModuleString **argv, int argc, int offset, const char *fmt, ...); 54 | 55 | /** 56 | Same as RMUtil_ParseArgs, but only parses the arguments after `token`, if it was found. 57 | This is useful for optional stuff like [LIMIT [offset] [limit]] 58 | */ 59 | int RMUtil_ParseArgsAfter(const char *token, RedisModuleString **argv, int argc, const char *fmt, 60 | ...); 61 | 62 | int rmutil_vparseArgs(RedisModuleString **argv, int argc, int offset, const char *fmt, va_list ap); 63 | 64 | #define RMUTIL_VARARGS_BADARG ((size_t)-1) 65 | /** 66 | * Parse arguments in the form of KEYWORD {len} {arg} .. {arg}_len. 67 | * If keyword is present, returns the position within `argv` containing the arguments. 68 | * Returns NULL if the keyword is not found. 69 | * If a parse error has occurred, `nargs` is set to RMUTIL_VARARGS_BADARG, but 70 | * the return value is not NULL. 71 | */ 72 | RedisModuleString **RMUtil_ParseVarArgs(RedisModuleString **argv, int argc, int offset, 73 | const char *keyword, size_t *nargs); 74 | 75 | /** 76 | * Default implementation of an AoF rewrite function that simply calls DUMP/RESTORE 77 | * internally. To use this function, pass it as the .aof_rewrite value in 78 | * RedisModuleTypeMethods 79 | */ 80 | void RMUtil_DefaultAofRewrite(RedisModuleIO *aof, RedisModuleString *key, void *value); 81 | 82 | // A single key/value entry in a redis info map 83 | typedef struct { 84 | char *key; 85 | char *val; 86 | } RMUtilInfoEntry; 87 | 88 | // Representation of INFO command response, as a list of k/v pairs 89 | typedef struct { 90 | RMUtilInfoEntry *entries; 91 | int numEntries; 92 | } RMUtilInfo; 93 | 94 | /** 95 | * Get redis INFO result and parse it as RMUtilInfo. 96 | * Returns NULL if something goes wrong. 97 | * The resulting object needs to be freed with RMUtilRedisInfo_Free 98 | */ 99 | RMUtilInfo *RMUtil_GetRedisInfo(RedisModuleCtx *ctx); 100 | 101 | /** 102 | * Free an RMUtilInfo object and its entries 103 | */ 104 | void RMUtilRedisInfo_Free(RMUtilInfo *info); 105 | 106 | /** 107 | * Get an integer value from an info object. Returns 1 if the value was found and 108 | * is an integer, 0 otherwise. the value is placed in 'val' 109 | */ 110 | int RMUtilInfo_GetInt(RMUtilInfo *info, const char *key, long long *val); 111 | 112 | /** 113 | * Get a string value from an info object. The value is placed in str. 114 | * Returns 1 if the key was found, 0 if not 115 | */ 116 | int RMUtilInfo_GetString(RMUtilInfo *info, const char *key, const char **str); 117 | 118 | /** 119 | * Get a double value from an info object. Returns 1 if the value was found and is 120 | * a correctly formatted double, 0 otherwise. the value is placed in 'd' 121 | */ 122 | int RMUtilInfo_GetDouble(RMUtilInfo *info, const char *key, double *d); 123 | 124 | /* 125 | * Returns a call reply array's element given by a space-delimited path. E.g., 126 | * the path "1 2 3" will return the 3rd element from the 2 element of the 1st 127 | * element from an array (or NULL if not found) 128 | */ 129 | RedisModuleCallReply *RedisModule_CallReplyArrayElementByPath(RedisModuleCallReply *rep, 130 | const char *path); 131 | 132 | /** 133 | * Extract the module type from an opened key. 134 | */ 135 | typedef enum { 136 | RMUTIL_VALUE_OK = 0, 137 | RMUTIL_VALUE_MISSING, 138 | RMUTIL_VALUE_EMPTY, 139 | RMUTIL_VALUE_MISMATCH 140 | } RMUtil_TryGetValueStatus; 141 | 142 | /** 143 | * Tries to extract the module-specific type from the value. 144 | * @param key an opened key (may be null) 145 | * @param type the pointer to the type to match to 146 | * @param[out] out if the value is present, will be set to it. 147 | * @return a value in the @ref RMUtil_TryGetValueStatus enum. 148 | */ 149 | int RedisModule_TryGetValue(RedisModuleKey *key, const RedisModuleType *type, void **out); 150 | 151 | #endif 152 | -------------------------------------------------------------------------------- /rmutil/vector.c: -------------------------------------------------------------------------------- 1 | #include "vector.h" 2 | #include 3 | 4 | inline int __vector_PushPtr(Vector *v, void *elem) { 5 | if (v->top == v->cap) { 6 | Vector_Resize(v, v->cap ? v->cap * 2 : 1); 7 | } 8 | 9 | __vector_PutPtr(v, v->top, elem); 10 | return v->top; 11 | } 12 | 13 | inline int Vector_Get(Vector *v, size_t pos, void *ptr) { 14 | // return 0 if pos is out of bounds 15 | if (pos >= v->top) { 16 | return 0; 17 | } 18 | 19 | memcpy(ptr, v->data + (pos * v->elemSize), v->elemSize); 20 | return 1; 21 | } 22 | 23 | /* Get the element at the end of the vector, decreasing the size by one */ 24 | inline int Vector_Pop(Vector *v, void *ptr) { 25 | if (v->top > 0) { 26 | if (ptr != NULL) { 27 | Vector_Get(v, v->top - 1, ptr); 28 | } 29 | v->top--; 30 | return 1; 31 | } 32 | return 0; 33 | } 34 | 35 | inline int __vector_PutPtr(Vector *v, size_t pos, void *elem) { 36 | // resize if pos is out of bounds 37 | if (pos >= v->cap) { 38 | Vector_Resize(v, pos + 1); 39 | } 40 | 41 | if (elem) { 42 | memcpy(v->data + pos * v->elemSize, elem, v->elemSize); 43 | } else { 44 | memset(v->data + pos * v->elemSize, 0, v->elemSize); 45 | } 46 | // move the end offset to pos if we grew 47 | if (pos >= v->top) { 48 | v->top = pos + 1; 49 | } 50 | return 1; 51 | } 52 | 53 | int Vector_Resize(Vector *v, size_t newcap) { 54 | int oldcap = v->cap; 55 | v->cap = newcap; 56 | 57 | v->data = realloc(v->data, v->cap * v->elemSize); 58 | 59 | // If we grew: 60 | // put all zeros at the newly realloc'd part of the vector 61 | if (newcap > oldcap) { 62 | int offset = oldcap * v->elemSize; 63 | memset(v->data + offset, 0, v->cap * v->elemSize - offset); 64 | } 65 | return v->cap; 66 | } 67 | 68 | Vector *__newVectorSize(size_t elemSize, size_t cap) { 69 | Vector *vec = malloc(sizeof(Vector)); 70 | vec->data = calloc(cap, elemSize); 71 | vec->top = 0; 72 | vec->elemSize = elemSize; 73 | vec->cap = cap; 74 | 75 | return vec; 76 | } 77 | 78 | void Vector_Free(Vector *v) { 79 | free(v->data); 80 | free(v); 81 | } 82 | 83 | 84 | /* return the used size of the vector, regardless of capacity */ 85 | inline int Vector_Size(Vector *v) { return v->top; } 86 | 87 | /* return the actual capacity */ 88 | inline int Vector_Cap(Vector *v) { return v->cap; } 89 | -------------------------------------------------------------------------------- /rmutil/vector.h: -------------------------------------------------------------------------------- 1 | #ifndef __VECTOR_H__ 2 | #define __VECTOR_H__ 3 | #include 4 | #include 5 | #include 6 | 7 | /* 8 | * Generic resizable vector that can be used if you just want to store stuff 9 | * temporarily. 10 | * Works like C++ std::vector with an underlying resizable buffer 11 | */ 12 | typedef struct { 13 | char *data; 14 | size_t elemSize; 15 | size_t cap; 16 | size_t top; 17 | 18 | } Vector; 19 | 20 | /* Create a new vector with element size. This should generally be used 21 | * internall by the NewVector macro */ 22 | Vector *__newVectorSize(size_t elemSize, size_t cap); 23 | 24 | // Put a pointer in the vector. To be used internall by the library 25 | int __vector_PutPtr(Vector *v, size_t pos, void *elem); 26 | 27 | /* 28 | * Create a new vector for a given type and a given capacity. 29 | * e.g. NewVector(int, 0) - empty vector of ints 30 | */ 31 | #define NewVector(type, cap) __newVectorSize(sizeof(type), cap) 32 | 33 | /* 34 | * get the element at index pos. The value is copied in to ptr. If pos is outside 35 | * the vector capacity, we return 0 36 | * otherwise 1 37 | */ 38 | int Vector_Get(Vector *v, size_t pos, void *ptr); 39 | 40 | /* Get the element at the end of the vector, decreasing the size by one */ 41 | int Vector_Pop(Vector *v, void *ptr); 42 | 43 | //#define Vector_Getx(v, pos, ptr) pos < v->cap ? 1 : 0; *ptr = 44 | //*(typeof(ptr))(v->data + v->elemSize*pos) 45 | 46 | /* 47 | * Put an element at pos. 48 | * Note: If pos is outside the vector capacity, we resize it accordingly 49 | */ 50 | #define Vector_Put(v, pos, elem) __vector_PutPtr(v, pos, elem ? &(typeof(elem)){elem} : NULL) 51 | 52 | /* Push an element at the end of v, resizing it if needed. This macro wraps 53 | * __vector_PushPtr */ 54 | #define Vector_Push(v, elem) __vector_PushPtr(v, elem ? &(typeof(elem)){elem} : NULL) 55 | 56 | int __vector_PushPtr(Vector *v, void *elem); 57 | 58 | /* resize capacity of v */ 59 | int Vector_Resize(Vector *v, size_t newcap); 60 | 61 | /* return the used size of the vector, regardless of capacity */ 62 | int Vector_Size(Vector *v); 63 | 64 | /* return the actual capacity */ 65 | int Vector_Cap(Vector *v); 66 | 67 | /* free the vector and the underlying data. Does not release its elements if 68 | * they are pointers*/ 69 | void Vector_Free(Vector *v); 70 | 71 | int __vecotr_PutPtr(Vector *v, size_t pos, void *elem); 72 | 73 | #endif --------------------------------------------------------------------------------