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