1 /* This module emulates a linked list for lazyfree testing of modules, which
2 is a simplified version of 'hellotype.c'
3 */
4 #include "redismodule.h"
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <ctype.h>
8 #include <string.h>
9 #include <stdint.h>
10
11 static RedisModuleType *LazyFreeLinkType;
12
13 struct LazyFreeLinkNode {
14 int64_t value;
15 struct LazyFreeLinkNode *next;
16 };
17
18 struct LazyFreeLinkObject {
19 struct LazyFreeLinkNode *head;
20 size_t len; /* Number of elements added. */
21 };
22
createLazyFreeLinkObject(void)23 struct LazyFreeLinkObject *createLazyFreeLinkObject(void) {
24 struct LazyFreeLinkObject *o;
25 o = RedisModule_Alloc(sizeof(*o));
26 o->head = NULL;
27 o->len = 0;
28 return o;
29 }
30
LazyFreeLinkInsert(struct LazyFreeLinkObject * o,int64_t ele)31 void LazyFreeLinkInsert(struct LazyFreeLinkObject *o, int64_t ele) {
32 struct LazyFreeLinkNode *next = o->head, *newnode, *prev = NULL;
33
34 while(next && next->value < ele) {
35 prev = next;
36 next = next->next;
37 }
38 newnode = RedisModule_Alloc(sizeof(*newnode));
39 newnode->value = ele;
40 newnode->next = next;
41 if (prev) {
42 prev->next = newnode;
43 } else {
44 o->head = newnode;
45 }
46 o->len++;
47 }
48
LazyFreeLinkReleaseObject(struct LazyFreeLinkObject * o)49 void LazyFreeLinkReleaseObject(struct LazyFreeLinkObject *o) {
50 struct LazyFreeLinkNode *cur, *next;
51 cur = o->head;
52 while(cur) {
53 next = cur->next;
54 RedisModule_Free(cur);
55 cur = next;
56 }
57 RedisModule_Free(o);
58 }
59
60 /* LAZYFREELINK.INSERT key value */
LazyFreeLinkInsert_RedisCommand(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)61 int LazyFreeLinkInsert_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
62 RedisModule_AutoMemory(ctx); /* Use automatic memory management. */
63
64 if (argc != 3) return RedisModule_WrongArity(ctx);
65 RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
66 REDISMODULE_READ|REDISMODULE_WRITE);
67 int type = RedisModule_KeyType(key);
68 if (type != REDISMODULE_KEYTYPE_EMPTY &&
69 RedisModule_ModuleTypeGetType(key) != LazyFreeLinkType)
70 {
71 return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
72 }
73
74 long long value;
75 if ((RedisModule_StringToLongLong(argv[2],&value) != REDISMODULE_OK)) {
76 return RedisModule_ReplyWithError(ctx,"ERR invalid value: must be a signed 64 bit integer");
77 }
78
79 struct LazyFreeLinkObject *hto;
80 if (type == REDISMODULE_KEYTYPE_EMPTY) {
81 hto = createLazyFreeLinkObject();
82 RedisModule_ModuleTypeSetValue(key,LazyFreeLinkType,hto);
83 } else {
84 hto = RedisModule_ModuleTypeGetValue(key);
85 }
86
87 LazyFreeLinkInsert(hto,value);
88 RedisModule_SignalKeyAsReady(ctx,argv[1]);
89
90 RedisModule_ReplyWithLongLong(ctx,hto->len);
91 RedisModule_ReplicateVerbatim(ctx);
92 return REDISMODULE_OK;
93 }
94
95 /* LAZYFREELINK.LEN key */
LazyFreeLinkLen_RedisCommand(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)96 int LazyFreeLinkLen_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
97 RedisModule_AutoMemory(ctx); /* Use automatic memory management. */
98
99 if (argc != 2) return RedisModule_WrongArity(ctx);
100 RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
101 REDISMODULE_READ);
102 int type = RedisModule_KeyType(key);
103 if (type != REDISMODULE_KEYTYPE_EMPTY &&
104 RedisModule_ModuleTypeGetType(key) != LazyFreeLinkType)
105 {
106 return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
107 }
108
109 struct LazyFreeLinkObject *hto = RedisModule_ModuleTypeGetValue(key);
110 RedisModule_ReplyWithLongLong(ctx,hto ? hto->len : 0);
111 return REDISMODULE_OK;
112 }
113
LazyFreeLinkRdbLoad(RedisModuleIO * rdb,int encver)114 void *LazyFreeLinkRdbLoad(RedisModuleIO *rdb, int encver) {
115 if (encver != 0) {
116 return NULL;
117 }
118 uint64_t elements = RedisModule_LoadUnsigned(rdb);
119 struct LazyFreeLinkObject *hto = createLazyFreeLinkObject();
120 while(elements--) {
121 int64_t ele = RedisModule_LoadSigned(rdb);
122 LazyFreeLinkInsert(hto,ele);
123 }
124 return hto;
125 }
126
LazyFreeLinkRdbSave(RedisModuleIO * rdb,void * value)127 void LazyFreeLinkRdbSave(RedisModuleIO *rdb, void *value) {
128 struct LazyFreeLinkObject *hto = value;
129 struct LazyFreeLinkNode *node = hto->head;
130 RedisModule_SaveUnsigned(rdb,hto->len);
131 while(node) {
132 RedisModule_SaveSigned(rdb,node->value);
133 node = node->next;
134 }
135 }
136
LazyFreeLinkAofRewrite(RedisModuleIO * aof,RedisModuleString * key,void * value)137 void LazyFreeLinkAofRewrite(RedisModuleIO *aof, RedisModuleString *key, void *value) {
138 struct LazyFreeLinkObject *hto = value;
139 struct LazyFreeLinkNode *node = hto->head;
140 while(node) {
141 RedisModule_EmitAOF(aof,"LAZYFREELINK.INSERT","sl",key,node->value);
142 node = node->next;
143 }
144 }
145
LazyFreeLinkFree(void * value)146 void LazyFreeLinkFree(void *value) {
147 LazyFreeLinkReleaseObject(value);
148 }
149
LazyFreeLinkFreeEffort(RedisModuleString * key,const void * value)150 size_t LazyFreeLinkFreeEffort(RedisModuleString *key, const void *value) {
151 REDISMODULE_NOT_USED(key);
152 const struct LazyFreeLinkObject *hto = value;
153 return hto->len;
154 }
155
LazyFreeLinkUnlink(RedisModuleString * key,const void * value)156 void LazyFreeLinkUnlink(RedisModuleString *key, const void *value) {
157 REDISMODULE_NOT_USED(key);
158 REDISMODULE_NOT_USED(value);
159 /* Here you can know which key and value is about to be freed. */
160 }
161
RedisModule_OnLoad(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)162 int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
163 REDISMODULE_NOT_USED(argv);
164 REDISMODULE_NOT_USED(argc);
165
166 if (RedisModule_Init(ctx,"lazyfreetest",1,REDISMODULE_APIVER_1)
167 == REDISMODULE_ERR) return REDISMODULE_ERR;
168
169 /* We only allow our module to be loaded when the redis core version is greater than the version of my module */
170 if (RedisModule_GetTypeMethodVersion() < REDISMODULE_TYPE_METHOD_VERSION) {
171 return REDISMODULE_ERR;
172 }
173
174 RedisModuleTypeMethods tm = {
175 .version = REDISMODULE_TYPE_METHOD_VERSION,
176 .rdb_load = LazyFreeLinkRdbLoad,
177 .rdb_save = LazyFreeLinkRdbSave,
178 .aof_rewrite = LazyFreeLinkAofRewrite,
179 .free = LazyFreeLinkFree,
180 .free_effort = LazyFreeLinkFreeEffort,
181 .unlink = LazyFreeLinkUnlink,
182 };
183
184 LazyFreeLinkType = RedisModule_CreateDataType(ctx,"test_lazy",0,&tm);
185 if (LazyFreeLinkType == NULL) return REDISMODULE_ERR;
186
187 if (RedisModule_CreateCommand(ctx,"lazyfreelink.insert",
188 LazyFreeLinkInsert_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
189 return REDISMODULE_ERR;
190
191 if (RedisModule_CreateCommand(ctx,"lazyfreelink.len",
192 LazyFreeLinkLen_RedisCommand,"readonly",1,1,1) == REDISMODULE_ERR)
193 return REDISMODULE_ERR;
194
195 return REDISMODULE_OK;
196 }
197