// SPDX-FileCopyrightText: 2024 Redict Contributors // SPDX-FileCopyrightText: 2024 Salvatore Sanfilippo // // SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: LGPL-3.0-only /* This module emulates a linked list for lazyfree testing of modules, which is a simplified version of 'hellotype.c' */ #include "redictmodule.h" #include #include #include #include #include static RedictModuleType *LazyFreeLinkType; struct LazyFreeLinkNode { int64_t value; struct LazyFreeLinkNode *next; }; struct LazyFreeLinkObject { struct LazyFreeLinkNode *head; size_t len; /* Number of elements added. */ }; struct LazyFreeLinkObject *createLazyFreeLinkObject(void) { struct LazyFreeLinkObject *o; o = RedictModule_Alloc(sizeof(*o)); o->head = NULL; o->len = 0; return o; } void LazyFreeLinkInsert(struct LazyFreeLinkObject *o, int64_t ele) { struct LazyFreeLinkNode *next = o->head, *newnode, *prev = NULL; while(next && next->value < ele) { prev = next; next = next->next; } newnode = RedictModule_Alloc(sizeof(*newnode)); newnode->value = ele; newnode->next = next; if (prev) { prev->next = newnode; } else { o->head = newnode; } o->len++; } void LazyFreeLinkReleaseObject(struct LazyFreeLinkObject *o) { struct LazyFreeLinkNode *cur, *next; cur = o->head; while(cur) { next = cur->next; RedictModule_Free(cur); cur = next; } RedictModule_Free(o); } /* LAZYFREELINK.INSERT key value */ int LazyFreeLinkInsert_RedisCommand(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) { RedictModule_AutoMemory(ctx); /* Use automatic memory management. */ if (argc != 3) return RedictModule_WrongArity(ctx); RedictModuleKey *key = RedictModule_OpenKey(ctx,argv[1], REDICTMODULE_READ|REDICTMODULE_WRITE); int type = RedictModule_KeyType(key); if (type != REDICTMODULE_KEYTYPE_EMPTY && RedictModule_ModuleTypeGetType(key) != LazyFreeLinkType) { return RedictModule_ReplyWithError(ctx,REDICTMODULE_ERRORMSG_WRONGTYPE); } long long value; if ((RedictModule_StringToLongLong(argv[2],&value) != REDICTMODULE_OK)) { return RedictModule_ReplyWithError(ctx,"ERR invalid value: must be a signed 64 bit integer"); } struct LazyFreeLinkObject *hto; if (type == REDICTMODULE_KEYTYPE_EMPTY) { hto = createLazyFreeLinkObject(); RedictModule_ModuleTypeSetValue(key,LazyFreeLinkType,hto); } else { hto = RedictModule_ModuleTypeGetValue(key); } LazyFreeLinkInsert(hto,value); RedictModule_SignalKeyAsReady(ctx,argv[1]); RedictModule_ReplyWithLongLong(ctx,hto->len); RedictModule_ReplicateVerbatim(ctx); return REDICTMODULE_OK; } /* LAZYFREELINK.LEN key */ int LazyFreeLinkLen_RedisCommand(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) { RedictModule_AutoMemory(ctx); /* Use automatic memory management. */ if (argc != 2) return RedictModule_WrongArity(ctx); RedictModuleKey *key = RedictModule_OpenKey(ctx,argv[1], REDICTMODULE_READ); int type = RedictModule_KeyType(key); if (type != REDICTMODULE_KEYTYPE_EMPTY && RedictModule_ModuleTypeGetType(key) != LazyFreeLinkType) { return RedictModule_ReplyWithError(ctx,REDICTMODULE_ERRORMSG_WRONGTYPE); } struct LazyFreeLinkObject *hto = RedictModule_ModuleTypeGetValue(key); RedictModule_ReplyWithLongLong(ctx,hto ? hto->len : 0); return REDICTMODULE_OK; } void *LazyFreeLinkRdbLoad(RedictModuleIO *rdb, int encver) { if (encver != 0) { return NULL; } uint64_t elements = RedictModule_LoadUnsigned(rdb); struct LazyFreeLinkObject *hto = createLazyFreeLinkObject(); while(elements--) { int64_t ele = RedictModule_LoadSigned(rdb); LazyFreeLinkInsert(hto,ele); } return hto; } void LazyFreeLinkRdbSave(RedictModuleIO *rdb, void *value) { struct LazyFreeLinkObject *hto = value; struct LazyFreeLinkNode *node = hto->head; RedictModule_SaveUnsigned(rdb,hto->len); while(node) { RedictModule_SaveSigned(rdb,node->value); node = node->next; } } void LazyFreeLinkAofRewrite(RedictModuleIO *aof, RedictModuleString *key, void *value) { struct LazyFreeLinkObject *hto = value; struct LazyFreeLinkNode *node = hto->head; while(node) { RedictModule_EmitAOF(aof,"LAZYFREELINK.INSERT","sl",key,node->value); node = node->next; } } void LazyFreeLinkFree(void *value) { LazyFreeLinkReleaseObject(value); } size_t LazyFreeLinkFreeEffort(RedictModuleString *key, const void *value) { REDICTMODULE_NOT_USED(key); const struct LazyFreeLinkObject *hto = value; return hto->len; } void LazyFreeLinkUnlink(RedictModuleString *key, const void *value) { REDICTMODULE_NOT_USED(key); REDICTMODULE_NOT_USED(value); /* Here you can know which key and value is about to be freed. */ } int RedictModule_OnLoad(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) { REDICTMODULE_NOT_USED(argv); REDICTMODULE_NOT_USED(argc); if (RedictModule_Init(ctx,"lazyfreetest",1,REDICTMODULE_APIVER_1) == REDICTMODULE_ERR) return REDICTMODULE_ERR; /* We only allow our module to be loaded when the redis core version is greater than the version of my module */ if (RedictModule_GetTypeMethodVersion() < REDICTMODULE_TYPE_METHOD_VERSION) { return REDICTMODULE_ERR; } RedictModuleTypeMethods tm = { .version = REDICTMODULE_TYPE_METHOD_VERSION, .rdb_load = LazyFreeLinkRdbLoad, .rdb_save = LazyFreeLinkRdbSave, .aof_rewrite = LazyFreeLinkAofRewrite, .free = LazyFreeLinkFree, .free_effort = LazyFreeLinkFreeEffort, .unlink = LazyFreeLinkUnlink, }; LazyFreeLinkType = RedictModule_CreateDataType(ctx,"test_lazy",0,&tm); if (LazyFreeLinkType == NULL) return REDICTMODULE_ERR; if (RedictModule_CreateCommand(ctx,"lazyfreelink.insert", LazyFreeLinkInsert_RedisCommand,"write deny-oom",1,1,1) == REDICTMODULE_ERR) return REDICTMODULE_ERR; if (RedictModule_CreateCommand(ctx,"lazyfreelink.len", LazyFreeLinkLen_RedisCommand,"readonly",1,1,1) == REDICTMODULE_ERR) return REDICTMODULE_ERR; return REDICTMODULE_OK; }