summaryrefslogtreecommitdiffstats
path: root/src/modules/helloworld.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 17:31:02 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 17:31:02 +0000
commitbb12c1fd00eb51118749bbbc69c5596835fcbd3b (patch)
tree88038a98bd31c1b765f3390767a2ec12e37c79ec /src/modules/helloworld.c
parentInitial commit. (diff)
downloadredis-upstream.tar.xz
redis-upstream.zip
Adding upstream version 5:7.0.15.upstream/5%7.0.15upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/modules/helloworld.c')
-rw-r--r--src/modules/helloworld.c621
1 files changed, 621 insertions, 0 deletions
diff --git a/src/modules/helloworld.c b/src/modules/helloworld.c
new file mode 100644
index 0000000..e517963
--- /dev/null
+++ b/src/modules/helloworld.c
@@ -0,0 +1,621 @@
+/* Helloworld module -- A few examples of the Redis Modules API in the form
+ * of commands showing how to accomplish common tasks.
+ *
+ * This module does not do anything useful, if not for a few commands. The
+ * examples are designed in order to show the API.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (c) 2016, Salvatore Sanfilippo <antirez at gmail dot com>
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+/* HELLO.SIMPLE is among the simplest commands you can implement.
+ * It just returns the currently selected DB id, a functionality which is
+ * missing in Redis. The command uses two important API calls: one to
+ * fetch the currently selected DB, the other in order to send the client
+ * an integer reply as response. */
+int HelloSimple_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
+ REDISMODULE_NOT_USED(argv);
+ REDISMODULE_NOT_USED(argc);
+ RedisModule_ReplyWithLongLong(ctx,RedisModule_GetSelectedDb(ctx));
+ return REDISMODULE_OK;
+}
+
+/* HELLO.PUSH.NATIVE re-implements RPUSH, and shows the low level modules API
+ * where you can "open" keys, make low level operations, create new keys by
+ * pushing elements into non-existing keys, and so forth.
+ *
+ * You'll find this command to be roughly as fast as the actual RPUSH
+ * command. */
+int HelloPushNative_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+ if (argc != 3) return RedisModule_WrongArity(ctx);
+
+ RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
+ REDISMODULE_READ|REDISMODULE_WRITE);
+
+ RedisModule_ListPush(key,REDISMODULE_LIST_TAIL,argv[2]);
+ size_t newlen = RedisModule_ValueLength(key);
+ RedisModule_CloseKey(key);
+ RedisModule_ReplyWithLongLong(ctx,newlen);
+ return REDISMODULE_OK;
+}
+
+/* HELLO.PUSH.CALL implements RPUSH using an higher level approach, calling
+ * a Redis command instead of working with the key in a low level way. This
+ * approach is useful when you need to call Redis commands that are not
+ * available as low level APIs, or when you don't need the maximum speed
+ * possible but instead prefer implementation simplicity. */
+int HelloPushCall_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+ if (argc != 3) return RedisModule_WrongArity(ctx);
+
+ RedisModuleCallReply *reply;
+
+ reply = RedisModule_Call(ctx,"RPUSH","ss",argv[1],argv[2]);
+ long long len = RedisModule_CallReplyInteger(reply);
+ RedisModule_FreeCallReply(reply);
+ RedisModule_ReplyWithLongLong(ctx,len);
+ return REDISMODULE_OK;
+}
+
+/* HELLO.PUSH.CALL2
+ * This is exactly as HELLO.PUSH.CALL, but shows how we can reply to the
+ * client using directly a reply object that Call() returned. */
+int HelloPushCall2_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+ if (argc != 3) return RedisModule_WrongArity(ctx);
+
+ RedisModuleCallReply *reply;
+
+ reply = RedisModule_Call(ctx,"RPUSH","ss",argv[1],argv[2]);
+ RedisModule_ReplyWithCallReply(ctx,reply);
+ RedisModule_FreeCallReply(reply);
+ return REDISMODULE_OK;
+}
+
+/* HELLO.LIST.SUM.LEN returns the total length of all the items inside
+ * a Redis list, by using the high level Call() API.
+ * This command is an example of the array reply access. */
+int HelloListSumLen_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+ if (argc != 2) return RedisModule_WrongArity(ctx);
+
+ RedisModuleCallReply *reply;
+
+ reply = RedisModule_Call(ctx,"LRANGE","sll",argv[1],(long long)0,(long long)-1);
+ size_t strlen = 0;
+ size_t items = RedisModule_CallReplyLength(reply);
+ size_t j;
+ for (j = 0; j < items; j++) {
+ RedisModuleCallReply *ele = RedisModule_CallReplyArrayElement(reply,j);
+ strlen += RedisModule_CallReplyLength(ele);
+ }
+ RedisModule_FreeCallReply(reply);
+ RedisModule_ReplyWithLongLong(ctx,strlen);
+ return REDISMODULE_OK;
+}
+
+/* HELLO.LIST.SPLICE srclist dstlist count
+ * Moves 'count' elements from the tail of 'srclist' to the head of
+ * 'dstlist'. If less than count elements are available, it moves as much
+ * elements as possible. */
+int HelloListSplice_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
+ if (argc != 4) return RedisModule_WrongArity(ctx);
+
+ RedisModuleKey *srckey = RedisModule_OpenKey(ctx,argv[1],
+ REDISMODULE_READ|REDISMODULE_WRITE);
+ RedisModuleKey *dstkey = RedisModule_OpenKey(ctx,argv[2],
+ REDISMODULE_READ|REDISMODULE_WRITE);
+
+ /* Src and dst key must be empty or lists. */
+ if ((RedisModule_KeyType(srckey) != REDISMODULE_KEYTYPE_LIST &&
+ RedisModule_KeyType(srckey) != REDISMODULE_KEYTYPE_EMPTY) ||
+ (RedisModule_KeyType(dstkey) != REDISMODULE_KEYTYPE_LIST &&
+ RedisModule_KeyType(dstkey) != REDISMODULE_KEYTYPE_EMPTY))
+ {
+ RedisModule_CloseKey(srckey);
+ RedisModule_CloseKey(dstkey);
+ return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
+ }
+
+ long long count;
+ if ((RedisModule_StringToLongLong(argv[3],&count) != REDISMODULE_OK) ||
+ (count < 0)) {
+ RedisModule_CloseKey(srckey);
+ RedisModule_CloseKey(dstkey);
+ return RedisModule_ReplyWithError(ctx,"ERR invalid count");
+ }
+
+ while(count-- > 0) {
+ RedisModuleString *ele;
+
+ ele = RedisModule_ListPop(srckey,REDISMODULE_LIST_TAIL);
+ if (ele == NULL) break;
+ RedisModule_ListPush(dstkey,REDISMODULE_LIST_HEAD,ele);
+ RedisModule_FreeString(ctx,ele);
+ }
+
+ size_t len = RedisModule_ValueLength(srckey);
+ RedisModule_CloseKey(srckey);
+ RedisModule_CloseKey(dstkey);
+ RedisModule_ReplyWithLongLong(ctx,len);
+ return REDISMODULE_OK;
+}
+
+/* Like the HELLO.LIST.SPLICE above, but uses automatic memory management
+ * in order to avoid freeing stuff. */
+int HelloListSpliceAuto_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
+ if (argc != 4) return RedisModule_WrongArity(ctx);
+
+ RedisModule_AutoMemory(ctx);
+
+ RedisModuleKey *srckey = RedisModule_OpenKey(ctx,argv[1],
+ REDISMODULE_READ|REDISMODULE_WRITE);
+ RedisModuleKey *dstkey = RedisModule_OpenKey(ctx,argv[2],
+ REDISMODULE_READ|REDISMODULE_WRITE);
+
+ /* Src and dst key must be empty or lists. */
+ if ((RedisModule_KeyType(srckey) != REDISMODULE_KEYTYPE_LIST &&
+ RedisModule_KeyType(srckey) != REDISMODULE_KEYTYPE_EMPTY) ||
+ (RedisModule_KeyType(dstkey) != REDISMODULE_KEYTYPE_LIST &&
+ RedisModule_KeyType(dstkey) != REDISMODULE_KEYTYPE_EMPTY))
+ {
+ return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
+ }
+
+ long long count;
+ if ((RedisModule_StringToLongLong(argv[3],&count) != REDISMODULE_OK) ||
+ (count < 0))
+ {
+ return RedisModule_ReplyWithError(ctx,"ERR invalid count");
+ }
+
+ while(count-- > 0) {
+ RedisModuleString *ele;
+
+ ele = RedisModule_ListPop(srckey,REDISMODULE_LIST_TAIL);
+ if (ele == NULL) break;
+ RedisModule_ListPush(dstkey,REDISMODULE_LIST_HEAD,ele);
+ }
+
+ size_t len = RedisModule_ValueLength(srckey);
+ RedisModule_ReplyWithLongLong(ctx,len);
+ return REDISMODULE_OK;
+}
+
+/* HELLO.RAND.ARRAY <count>
+ * Shows how to generate arrays as commands replies.
+ * It just outputs <count> random numbers. */
+int HelloRandArray_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
+ if (argc != 2) return RedisModule_WrongArity(ctx);
+ long long count;
+ if (RedisModule_StringToLongLong(argv[1],&count) != REDISMODULE_OK ||
+ count < 0)
+ return RedisModule_ReplyWithError(ctx,"ERR invalid count");
+
+ /* To reply with an array, we call RedisModule_ReplyWithArray() followed
+ * by other "count" calls to other reply functions in order to generate
+ * the elements of the array. */
+ RedisModule_ReplyWithArray(ctx,count);
+ while(count--) RedisModule_ReplyWithLongLong(ctx,rand());
+ return REDISMODULE_OK;
+}
+
+/* This is a simple command to test replication. Because of the "!" modified
+ * in the RedisModule_Call() call, the two INCRs get replicated.
+ * Also note how the ECHO is replicated in an unexpected position (check
+ * comments the function implementation). */
+int HelloRepl1_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+ REDISMODULE_NOT_USED(argv);
+ REDISMODULE_NOT_USED(argc);
+ RedisModule_AutoMemory(ctx);
+
+ /* This will be replicated *after* the two INCR statements, since
+ * the Call() replication has precedence, so the actual replication
+ * stream will be:
+ *
+ * MULTI
+ * INCR foo
+ * INCR bar
+ * ECHO c foo
+ * EXEC
+ */
+ RedisModule_Replicate(ctx,"ECHO","c","foo");
+
+ /* Using the "!" modifier we replicate the command if it
+ * modified the dataset in some way. */
+ RedisModule_Call(ctx,"INCR","c!","foo");
+ RedisModule_Call(ctx,"INCR","c!","bar");
+
+ RedisModule_ReplyWithLongLong(ctx,0);
+
+ return REDISMODULE_OK;
+}
+
+/* Another command to show replication. In this case, we call
+ * RedisModule_ReplicateVerbatim() to mean we want just the command to be
+ * propagated to slaves / AOF exactly as it was called by the user.
+ *
+ * This command also shows how to work with string objects.
+ * It takes a list, and increments all the elements (that must have
+ * a numerical value) by 1, returning the sum of all the elements
+ * as reply.
+ *
+ * Usage: HELLO.REPL2 <list-key> */
+int HelloRepl2_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
+ if (argc != 2) return RedisModule_WrongArity(ctx);
+
+ RedisModule_AutoMemory(ctx); /* Use automatic memory management. */
+ RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
+ REDISMODULE_READ|REDISMODULE_WRITE);
+
+ if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_LIST)
+ return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
+
+ size_t listlen = RedisModule_ValueLength(key);
+ long long sum = 0;
+
+ /* Rotate and increment. */
+ while(listlen--) {
+ RedisModuleString *ele = RedisModule_ListPop(key,REDISMODULE_LIST_TAIL);
+ long long val;
+ if (RedisModule_StringToLongLong(ele,&val) != REDISMODULE_OK) val = 0;
+ val++;
+ sum += val;
+ RedisModuleString *newele = RedisModule_CreateStringFromLongLong(ctx,val);
+ RedisModule_ListPush(key,REDISMODULE_LIST_HEAD,newele);
+ }
+ RedisModule_ReplyWithLongLong(ctx,sum);
+ RedisModule_ReplicateVerbatim(ctx);
+ return REDISMODULE_OK;
+}
+
+/* This is an example of strings DMA access. Given a key containing a string
+ * it toggles the case of each character from lower to upper case or the
+ * other way around.
+ *
+ * No automatic memory management is used in this example (for the sake
+ * of variety).
+ *
+ * HELLO.TOGGLE.CASE key */
+int HelloToggleCase_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
+ if (argc != 2) return RedisModule_WrongArity(ctx);
+
+ RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
+ REDISMODULE_READ|REDISMODULE_WRITE);
+
+ int keytype = RedisModule_KeyType(key);
+ if (keytype != REDISMODULE_KEYTYPE_STRING &&
+ keytype != REDISMODULE_KEYTYPE_EMPTY)
+ {
+ RedisModule_CloseKey(key);
+ return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
+ }
+
+ if (keytype == REDISMODULE_KEYTYPE_STRING) {
+ size_t len, j;
+ char *s = RedisModule_StringDMA(key,&len,REDISMODULE_WRITE);
+ for (j = 0; j < len; j++) {
+ if (isupper(s[j])) {
+ s[j] = tolower(s[j]);
+ } else {
+ s[j] = toupper(s[j]);
+ }
+ }
+ }
+
+ RedisModule_CloseKey(key);
+ RedisModule_ReplyWithSimpleString(ctx,"OK");
+ RedisModule_ReplicateVerbatim(ctx);
+ return REDISMODULE_OK;
+}
+
+/* HELLO.MORE.EXPIRE key milliseconds.
+ *
+ * If the key has already an associated TTL, extends it by "milliseconds"
+ * milliseconds. Otherwise no operation is performed. */
+int HelloMoreExpire_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
+ RedisModule_AutoMemory(ctx); /* Use automatic memory management. */
+ if (argc != 3) return RedisModule_WrongArity(ctx);
+
+ mstime_t addms, expire;
+
+ if (RedisModule_StringToLongLong(argv[2],&addms) != REDISMODULE_OK)
+ return RedisModule_ReplyWithError(ctx,"ERR invalid expire time");
+
+ RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
+ REDISMODULE_READ|REDISMODULE_WRITE);
+ expire = RedisModule_GetExpire(key);
+ if (expire != REDISMODULE_NO_EXPIRE) {
+ expire += addms;
+ RedisModule_SetExpire(key,expire);
+ }
+ return RedisModule_ReplyWithSimpleString(ctx,"OK");
+}
+
+/* HELLO.ZSUMRANGE key startscore endscore
+ * Return the sum of all the scores elements between startscore and endscore.
+ *
+ * The computation is performed two times, one time from start to end and
+ * another time backward. The two scores, returned as a two element array,
+ * should match.*/
+int HelloZsumRange_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
+ double score_start, score_end;
+ if (argc != 4) return RedisModule_WrongArity(ctx);
+
+ if (RedisModule_StringToDouble(argv[2],&score_start) != REDISMODULE_OK ||
+ RedisModule_StringToDouble(argv[3],&score_end) != REDISMODULE_OK)
+ {
+ return RedisModule_ReplyWithError(ctx,"ERR invalid range");
+ }
+
+ RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
+ REDISMODULE_READ|REDISMODULE_WRITE);
+ if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_ZSET) {
+ return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
+ }
+
+ double scoresum_a = 0;
+ double scoresum_b = 0;
+
+ RedisModule_ZsetFirstInScoreRange(key,score_start,score_end,0,0);
+ while(!RedisModule_ZsetRangeEndReached(key)) {
+ double score;
+ RedisModuleString *ele = RedisModule_ZsetRangeCurrentElement(key,&score);
+ RedisModule_FreeString(ctx,ele);
+ scoresum_a += score;
+ RedisModule_ZsetRangeNext(key);
+ }
+ RedisModule_ZsetRangeStop(key);
+
+ RedisModule_ZsetLastInScoreRange(key,score_start,score_end,0,0);
+ while(!RedisModule_ZsetRangeEndReached(key)) {
+ double score;
+ RedisModuleString *ele = RedisModule_ZsetRangeCurrentElement(key,&score);
+ RedisModule_FreeString(ctx,ele);
+ scoresum_b += score;
+ RedisModule_ZsetRangePrev(key);
+ }
+
+ RedisModule_ZsetRangeStop(key);
+
+ RedisModule_CloseKey(key);
+
+ RedisModule_ReplyWithArray(ctx,2);
+ RedisModule_ReplyWithDouble(ctx,scoresum_a);
+ RedisModule_ReplyWithDouble(ctx,scoresum_b);
+ return REDISMODULE_OK;
+}
+
+/* HELLO.LEXRANGE key min_lex max_lex min_age max_age
+ * This command expects a sorted set stored at key in the following form:
+ * - All the elements have score 0.
+ * - Elements are pairs of "<name>:<age>", for example "Anna:52".
+ * The command will return all the sorted set items that are lexicographically
+ * between the specified range (using the same format as ZRANGEBYLEX)
+ * and having an age between min_age and max_age. */
+int HelloLexRange_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
+ RedisModule_AutoMemory(ctx); /* Use automatic memory management. */
+
+ if (argc != 6) return RedisModule_WrongArity(ctx);
+
+ RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
+ REDISMODULE_READ|REDISMODULE_WRITE);
+ if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_ZSET) {
+ return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
+ }
+
+ if (RedisModule_ZsetFirstInLexRange(key,argv[2],argv[3]) != REDISMODULE_OK) {
+ return RedisModule_ReplyWithError(ctx,"invalid range");
+ }
+
+ int arraylen = 0;
+ RedisModule_ReplyWithArray(ctx,REDISMODULE_POSTPONED_LEN);
+ while(!RedisModule_ZsetRangeEndReached(key)) {
+ double score;
+ RedisModuleString *ele = RedisModule_ZsetRangeCurrentElement(key,&score);
+ RedisModule_ReplyWithString(ctx,ele);
+ RedisModule_FreeString(ctx,ele);
+ RedisModule_ZsetRangeNext(key);
+ arraylen++;
+ }
+ RedisModule_ZsetRangeStop(key);
+ RedisModule_ReplySetArrayLength(ctx,arraylen);
+ RedisModule_CloseKey(key);
+ return REDISMODULE_OK;
+}
+
+/* HELLO.HCOPY key srcfield dstfield
+ * This is just an example command that sets the hash field dstfield to the
+ * same value of srcfield. If srcfield does not exist no operation is
+ * performed.
+ *
+ * The command returns 1 if the copy is performed (srcfield exists) otherwise
+ * 0 is returned. */
+int HelloHCopy_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_HASH &&
+ type != REDISMODULE_KEYTYPE_EMPTY)
+ {
+ return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
+ }
+
+ /* Get the old field value. */
+ RedisModuleString *oldval;
+ RedisModule_HashGet(key,REDISMODULE_HASH_NONE,argv[2],&oldval,NULL);
+ if (oldval) {
+ RedisModule_HashSet(key,REDISMODULE_HASH_NONE,argv[3],oldval,NULL);
+ }
+ RedisModule_ReplyWithLongLong(ctx,oldval != NULL);
+ return REDISMODULE_OK;
+}
+
+/* HELLO.LEFTPAD str len ch
+ * This is an implementation of the infamous LEFTPAD function, that
+ * was at the center of an issue with the npm modules system in March 2016.
+ *
+ * LEFTPAD is a good example of using a Redis Modules API called
+ * "pool allocator", that was a famous way to allocate memory in yet another
+ * open source project, the Apache web server.
+ *
+ * The concept is very simple: there is memory that is useful to allocate
+ * only in the context of serving a request, and must be freed anyway when
+ * the callback implementing the command returns. So in that case the module
+ * does not need to retain a reference to these allocations, it is just
+ * required to free the memory before returning. When this is the case the
+ * module can call RedisModule_PoolAlloc() instead, that works like malloc()
+ * but will automatically free the memory when the module callback returns.
+ *
+ * Note that PoolAlloc() does not necessarily require AutoMemory to be
+ * active. */
+int HelloLeftPad_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
+ RedisModule_AutoMemory(ctx); /* Use automatic memory management. */
+ long long padlen;
+
+ if (argc != 4) return RedisModule_WrongArity(ctx);
+
+ if ((RedisModule_StringToLongLong(argv[2],&padlen) != REDISMODULE_OK) ||
+ (padlen< 0)) {
+ return RedisModule_ReplyWithError(ctx,"ERR invalid padding length");
+ }
+ size_t strlen, chlen;
+ const char *str = RedisModule_StringPtrLen(argv[1], &strlen);
+ const char *ch = RedisModule_StringPtrLen(argv[3], &chlen);
+
+ /* If the string is already larger than the target len, just return
+ * the string itself. */
+ if (strlen >= (size_t)padlen)
+ return RedisModule_ReplyWithString(ctx,argv[1]);
+
+ /* Padding must be a single character in this simple implementation. */
+ if (chlen != 1)
+ return RedisModule_ReplyWithError(ctx,
+ "ERR padding must be a single char");
+
+ /* Here we use our pool allocator, for our throw-away allocation. */
+ padlen -= strlen;
+ char *buf = RedisModule_PoolAlloc(ctx,padlen+strlen);
+ for (long long j = 0; j < padlen; j++) buf[j] = *ch;
+ memcpy(buf+padlen,str,strlen);
+
+ RedisModule_ReplyWithStringBuffer(ctx,buf,padlen+strlen);
+ return REDISMODULE_OK;
+}
+
+/* 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) {
+ if (RedisModule_Init(ctx,"helloworld",1,REDISMODULE_APIVER_1)
+ == REDISMODULE_ERR) return REDISMODULE_ERR;
+
+ /* Log the list of parameters passing loading the module. */
+ for (int j = 0; j < argc; j++) {
+ const char *s = RedisModule_StringPtrLen(argv[j],NULL);
+ printf("Module loaded with ARGV[%d] = %s\n", j, s);
+ }
+
+ if (RedisModule_CreateCommand(ctx,"hello.simple",
+ HelloSimple_RedisCommand,"readonly",0,0,0) == REDISMODULE_ERR)
+ return REDISMODULE_ERR;
+
+ if (RedisModule_CreateCommand(ctx,"hello.push.native",
+ HelloPushNative_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+ return REDISMODULE_ERR;
+
+ if (RedisModule_CreateCommand(ctx,"hello.push.call",
+ HelloPushCall_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+ return REDISMODULE_ERR;
+
+ if (RedisModule_CreateCommand(ctx,"hello.push.call2",
+ HelloPushCall2_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+ return REDISMODULE_ERR;
+
+ if (RedisModule_CreateCommand(ctx,"hello.list.sum.len",
+ HelloListSumLen_RedisCommand,"readonly",1,1,1) == REDISMODULE_ERR)
+ return REDISMODULE_ERR;
+
+ if (RedisModule_CreateCommand(ctx,"hello.list.splice",
+ HelloListSplice_RedisCommand,"write deny-oom",1,2,1) == REDISMODULE_ERR)
+ return REDISMODULE_ERR;
+
+ if (RedisModule_CreateCommand(ctx,"hello.list.splice.auto",
+ HelloListSpliceAuto_RedisCommand,
+ "write deny-oom",1,2,1) == REDISMODULE_ERR)
+ return REDISMODULE_ERR;
+
+ if (RedisModule_CreateCommand(ctx,"hello.rand.array",
+ HelloRandArray_RedisCommand,"readonly",0,0,0) == REDISMODULE_ERR)
+ return REDISMODULE_ERR;
+
+ if (RedisModule_CreateCommand(ctx,"hello.repl1",
+ HelloRepl1_RedisCommand,"write",0,0,0) == REDISMODULE_ERR)
+ return REDISMODULE_ERR;
+
+ if (RedisModule_CreateCommand(ctx,"hello.repl2",
+ HelloRepl2_RedisCommand,"write",1,1,1) == REDISMODULE_ERR)
+ return REDISMODULE_ERR;
+
+ if (RedisModule_CreateCommand(ctx,"hello.toggle.case",
+ HelloToggleCase_RedisCommand,"write",1,1,1) == REDISMODULE_ERR)
+ return REDISMODULE_ERR;
+
+ if (RedisModule_CreateCommand(ctx,"hello.more.expire",
+ HelloMoreExpire_RedisCommand,"write",1,1,1) == REDISMODULE_ERR)
+ return REDISMODULE_ERR;
+
+ if (RedisModule_CreateCommand(ctx,"hello.zsumrange",
+ HelloZsumRange_RedisCommand,"readonly",1,1,1) == REDISMODULE_ERR)
+ return REDISMODULE_ERR;
+
+ if (RedisModule_CreateCommand(ctx,"hello.lexrange",
+ HelloLexRange_RedisCommand,"readonly",1,1,1) == REDISMODULE_ERR)
+ return REDISMODULE_ERR;
+
+ if (RedisModule_CreateCommand(ctx,"hello.hcopy",
+ HelloHCopy_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+ return REDISMODULE_ERR;
+
+ if (RedisModule_CreateCommand(ctx,"hello.leftpad",
+ HelloLeftPad_RedisCommand,"",1,1,1) == REDISMODULE_ERR)
+ return REDISMODULE_ERR;
+
+ return REDISMODULE_OK;
+}