diff --git a/src/modules/hellotype.c b/src/modules/hellotype.c deleted file mode 100644 index 4f2d1d730..000000000 --- a/src/modules/hellotype.c +++ /dev/null @@ -1,362 +0,0 @@ -/* This file implements a new module native data type called "HELLOTYPE". - * The data structure implemented is a very simple ordered linked list of - * 64 bit integers, in order to have something that is real world enough, but - * at the same time, extremely simple to understand, to show how the API - * works, how a new data type is created, and how to write basic methods - * for RDB loading, saving and AOF rewriting. - * - * ----------------------------------------------------------------------------- - * - * Copyright (c) 2016, Salvatore Sanfilippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "../redismodule.h" -#include -#include -#include -#include -#include - -static RedisModuleType *HelloType; - -/* ========================== Internal data structure ======================= - * This is just a linked list of 64 bit integers where elements are inserted - * in-place, so it's ordered. There is no pop/push operation but just insert - * because it is enough to show the implementation of new data types without - * making things complex. */ - -struct HelloTypeNode { - int64_t value; - struct HelloTypeNode *next; -}; - -struct HelloTypeObject { - struct HelloTypeNode *head; - size_t len; /* Number of elements added. */ -}; - -struct HelloTypeObject *createHelloTypeObject(void) { - struct HelloTypeObject *o; - o = RedisModule_Alloc(sizeof(*o)); - o->head = NULL; - o->len = 0; - return o; -} - -void HelloTypeInsert(struct HelloTypeObject *o, int64_t ele) { - struct HelloTypeNode *next = o->head, *newnode, *prev = NULL; - - while(next && next->value < ele) { - prev = next; - next = next->next; - } - newnode = RedisModule_Alloc(sizeof(*newnode)); - newnode->value = ele; - newnode->next = next; - if (prev) { - prev->next = newnode; - } else { - o->head = newnode; - } - o->len++; -} - -void HelloTypeReleaseObject(struct HelloTypeObject *o) { - struct HelloTypeNode *cur, *next; - cur = o->head; - while(cur) { - next = cur->next; - RedisModule_Free(cur); - cur = next; - } - RedisModule_Free(o); -} - -/* ========================= "hellotype" type commands ======================= */ - -/* HELLOTYPE.INSERT key value */ -int HelloTypeInsert_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - RedisModule_AutoMemory(ctx); /* Use automatic memory management. */ - - if (argc != 3) return RedisModule_WrongArity(ctx); - RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], - REDISMODULE_READ|REDISMODULE_WRITE); - int type = RedisModule_KeyType(key); - if (type != REDISMODULE_KEYTYPE_EMPTY && - RedisModule_ModuleTypeGetType(key) != HelloType) - { - return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); - } - - long long value; - if ((RedisModule_StringToLongLong(argv[2],&value) != REDISMODULE_OK)) { - return RedisModule_ReplyWithError(ctx,"ERR invalid value: must be a signed 64 bit integer"); - } - - /* Create an empty value object if the key is currently empty. */ - struct HelloTypeObject *hto; - if (type == REDISMODULE_KEYTYPE_EMPTY) { - hto = createHelloTypeObject(); - RedisModule_ModuleTypeSetValue(key,HelloType,hto); - } else { - hto = RedisModule_ModuleTypeGetValue(key); - } - - /* Insert the new element. */ - HelloTypeInsert(hto,value); - RedisModule_SignalKeyAsReady(ctx,argv[1]); - - RedisModule_ReplyWithLongLong(ctx,hto->len); - RedisModule_ReplicateVerbatim(ctx); - return REDISMODULE_OK; -} - -/* HELLOTYPE.RANGE key first count */ -int HelloTypeRange_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - RedisModule_AutoMemory(ctx); /* Use automatic memory management. */ - - if (argc != 4) return RedisModule_WrongArity(ctx); - RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], - REDISMODULE_READ|REDISMODULE_WRITE); - int type = RedisModule_KeyType(key); - if (type != REDISMODULE_KEYTYPE_EMPTY && - RedisModule_ModuleTypeGetType(key) != HelloType) - { - return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); - } - - long long first, count; - if (RedisModule_StringToLongLong(argv[2],&first) != REDISMODULE_OK || - RedisModule_StringToLongLong(argv[3],&count) != REDISMODULE_OK || - first < 0 || count < 0) - { - return RedisModule_ReplyWithError(ctx, - "ERR invalid first or count parameters"); - } - - struct HelloTypeObject *hto = RedisModule_ModuleTypeGetValue(key); - struct HelloTypeNode *node = hto ? hto->head : NULL; - RedisModule_ReplyWithArray(ctx,REDISMODULE_POSTPONED_ARRAY_LEN); - long long arraylen = 0; - while(node && count--) { - RedisModule_ReplyWithLongLong(ctx,node->value); - arraylen++; - node = node->next; - } - RedisModule_ReplySetArrayLength(ctx,arraylen); - return REDISMODULE_OK; -} - -/* HELLOTYPE.LEN key */ -int HelloTypeLen_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - RedisModule_AutoMemory(ctx); /* Use automatic memory management. */ - - if (argc != 2) return RedisModule_WrongArity(ctx); - RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], - REDISMODULE_READ|REDISMODULE_WRITE); - int type = RedisModule_KeyType(key); - if (type != REDISMODULE_KEYTYPE_EMPTY && - RedisModule_ModuleTypeGetType(key) != HelloType) - { - return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); - } - - struct HelloTypeObject *hto = RedisModule_ModuleTypeGetValue(key); - RedisModule_ReplyWithLongLong(ctx,hto ? hto->len : 0); - return REDISMODULE_OK; -} - -/* ====================== Example of a blocking command ==================== */ - -/* Reply callback for blocking command HELLOTYPE.BRANGE, this will get - * called when the key we blocked for is ready: we need to check if we - * can really serve the client, and reply OK or ERR accordingly. */ -int HelloBlock_Reply(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - RedisModuleString *keyname = RedisModule_GetBlockedClientReadyKey(ctx); - RedisModuleKey *key = RedisModule_OpenKey(ctx,keyname,REDISMODULE_READ); - int type = RedisModule_KeyType(key); - if (type != REDISMODULE_KEYTYPE_MODULE || - RedisModule_ModuleTypeGetType(key) != HelloType) - { - RedisModule_CloseKey(key); - return REDISMODULE_ERR; - } - - /* In case the key is able to serve our blocked client, let's directly - * use our original command implementation to make this example simpler. */ - RedisModule_CloseKey(key); - return HelloTypeRange_RedisCommand(ctx,argv,argc-1); -} - -/* Timeout callback for blocking command HELLOTYPE.BRANGE */ -int HelloBlock_Timeout(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - return RedisModule_ReplyWithSimpleString(ctx,"Request timedout"); -} - -/* Private data freeing callback for HELLOTYPE.BRANGE command. */ -void HelloBlock_FreeData(RedisModuleCtx *ctx, void *privdata) { - REDISMODULE_NOT_USED(ctx); - RedisModule_Free(privdata); -} - -/* HELLOTYPE.BRANGE key first count timeout -- This is a blocking verison of - * the RANGE operation, in order to show how to use the API - * RedisModule_BlockClientOnKeys(). */ -int HelloTypeBRange_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 5) return RedisModule_WrongArity(ctx); - RedisModule_AutoMemory(ctx); /* Use automatic memory management. */ - RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], - REDISMODULE_READ|REDISMODULE_WRITE); - int type = RedisModule_KeyType(key); - if (type != REDISMODULE_KEYTYPE_EMPTY && - RedisModule_ModuleTypeGetType(key) != HelloType) - { - return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); - } - - /* Parse the timeout before even trying to serve the client synchronously, - * so that we always fail ASAP on syntax errors. */ - long long timeout; - if (RedisModule_StringToLongLong(argv[4],&timeout) != REDISMODULE_OK) { - return RedisModule_ReplyWithError(ctx, - "ERR invalid timeout parameter"); - } - - /* Can we serve the reply synchronously? */ - if (type != REDISMODULE_KEYTYPE_EMPTY) { - return HelloTypeRange_RedisCommand(ctx,argv,argc-1); - } - - /* Otherwise let's block on the key. */ - void *privdata = RedisModule_Alloc(100); - RedisModule_BlockClientOnKeys(ctx,HelloBlock_Reply,HelloBlock_Timeout,HelloBlock_FreeData,timeout,argv+1,1,privdata); - return REDISMODULE_OK; -} - -/* ========================== "hellotype" type methods ======================= */ - -void *HelloTypeRdbLoad(RedisModuleIO *rdb, int encver) { - if (encver != 0) { - /* RedisModule_Log("warning","Can't load data with version %d", encver);*/ - return NULL; - } - uint64_t elements = RedisModule_LoadUnsigned(rdb); - struct HelloTypeObject *hto = createHelloTypeObject(); - while(elements--) { - int64_t ele = RedisModule_LoadSigned(rdb); - HelloTypeInsert(hto,ele); - } - return hto; -} - -void HelloTypeRdbSave(RedisModuleIO *rdb, void *value) { - struct HelloTypeObject *hto = value; - struct HelloTypeNode *node = hto->head; - RedisModule_SaveUnsigned(rdb,hto->len); - while(node) { - RedisModule_SaveSigned(rdb,node->value); - node = node->next; - } -} - -void HelloTypeAofRewrite(RedisModuleIO *aof, RedisModuleString *key, void *value) { - struct HelloTypeObject *hto = value; - struct HelloTypeNode *node = hto->head; - while(node) { - RedisModule_EmitAOF(aof,"HELLOTYPE.INSERT","sl",key,node->value); - node = node->next; - } -} - -/* The goal of this function is to return the amount of memory used by - * the HelloType value. */ -size_t HelloTypeMemUsage(const void *value) { - const struct HelloTypeObject *hto = value; - struct HelloTypeNode *node = hto->head; - return sizeof(*hto) + sizeof(*node)*hto->len; -} - -void HelloTypeFree(void *value) { - HelloTypeReleaseObject(value); -} - -void HelloTypeDigest(RedisModuleDigest *md, void *value) { - struct HelloTypeObject *hto = value; - struct HelloTypeNode *node = hto->head; - while(node) { - RedisModule_DigestAddLongLong(md,node->value); - node = node->next; - } - RedisModule_DigestEndSequence(md); -} - -/* This function must be present on each Redis module. It is used in order to - * register the commands into the Redis server. */ -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - if (RedisModule_Init(ctx,"hellotype",1,REDISMODULE_APIVER_1) - == REDISMODULE_ERR) return REDISMODULE_ERR; - - RedisModuleTypeMethods tm = { - .version = REDISMODULE_TYPE_METHOD_VERSION, - .rdb_load = HelloTypeRdbLoad, - .rdb_save = HelloTypeRdbSave, - .aof_rewrite = HelloTypeAofRewrite, - .mem_usage = HelloTypeMemUsage, - .free = HelloTypeFree, - .digest = HelloTypeDigest - }; - - HelloType = RedisModule_CreateDataType(ctx,"hellotype",0,&tm); - if (HelloType == NULL) return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"hellotype.insert", - HelloTypeInsert_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"hellotype.range", - HelloTypeRange_RedisCommand,"readonly",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"hellotype.len", - HelloTypeLen_RedisCommand,"readonly",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"hellotype.brange", - HelloTypeBRange_RedisCommand,"readonly",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - return REDISMODULE_OK; -}