1 /* Helloworld module -- A few examples of the Redis Modules API in the form 2 * of commands showing how to accomplish common tasks. 3 * 4 * This module does not do anything useful, if not for a few commands. The 5 * examples are designed in order to show the API. 6 * 7 * ----------------------------------------------------------------------------- 8 * 9 * Copyright (c) 2016, Salvatore Sanfilippo <antirez at gmail dot com> 10 * All rights reserved. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions are met: 14 * 15 * * Redistributions of source code must retain the above copyright notice, 16 * this list of conditions and the following disclaimer. 17 * * Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * * Neither the name of Redis nor the names of its contributors may be used 21 * to endorse or promote products derived from this software without 22 * specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 * POSSIBILITY OF SUCH DAMAGE. 35 */ 36 37 #include "../redismodule.h" 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <ctype.h> 41 #include <string.h> 42 43 /* HELLO.SIMPLE is among the simplest commands you can implement. 44 * It just returns the currently selected DB id, a functionality which is 45 * missing in Redis. The command uses two important API calls: one to 46 * fetch the currently selected DB, the other in order to send the client 47 * an integer reply as response. */ 48 int HelloSimple_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 49 REDISMODULE_NOT_USED(argv); 50 REDISMODULE_NOT_USED(argc); 51 RedisModule_ReplyWithLongLong(ctx,RedisModule_GetSelectedDb(ctx)); 52 return REDISMODULE_OK; 53 } 54 55 /* HELLO.PUSH.NATIVE re-implements RPUSH, and shows the low level modules API 56 * where you can "open" keys, make low level operations, create new keys by 57 * pushing elements into non-existing keys, and so forth. 58 * 59 * You'll find this command to be roughly as fast as the actual RPUSH 60 * command. */ 61 int HelloPushNative_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) 62 { 63 if (argc != 3) return RedisModule_WrongArity(ctx); 64 65 RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], 66 REDISMODULE_READ|REDISMODULE_WRITE); 67 68 RedisModule_ListPush(key,REDISMODULE_LIST_TAIL,argv[2]); 69 size_t newlen = RedisModule_ValueLength(key); 70 RedisModule_CloseKey(key); 71 RedisModule_ReplyWithLongLong(ctx,newlen); 72 return REDISMODULE_OK; 73 } 74 75 /* HELLO.PUSH.CALL implements RPUSH using an higher level approach, calling 76 * a Redis command instead of working with the key in a low level way. This 77 * approach is useful when you need to call Redis commands that are not 78 * available as low level APIs, or when you don't need the maximum speed 79 * possible but instead prefer implementation simplicity. */ 80 int HelloPushCall_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) 81 { 82 if (argc != 3) return RedisModule_WrongArity(ctx); 83 84 RedisModuleCallReply *reply; 85 86 reply = RedisModule_Call(ctx,"RPUSH","ss",argv[1],argv[2]); 87 long long len = RedisModule_CallReplyInteger(reply); 88 RedisModule_FreeCallReply(reply); 89 RedisModule_ReplyWithLongLong(ctx,len); 90 return REDISMODULE_OK; 91 } 92 93 /* HELLO.PUSH.CALL2 94 * This is exaxctly as HELLO.PUSH.CALL, but shows how we can reply to the 95 * client using directly a reply object that Call() returned. */ 96 int HelloPushCall2_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) 97 { 98 if (argc != 3) return RedisModule_WrongArity(ctx); 99 100 RedisModuleCallReply *reply; 101 102 reply = RedisModule_Call(ctx,"RPUSH","ss",argv[1],argv[2]); 103 RedisModule_ReplyWithCallReply(ctx,reply); 104 RedisModule_FreeCallReply(reply); 105 return REDISMODULE_OK; 106 } 107 108 /* HELLO.LIST.SUM.LEN returns the total length of all the items inside 109 * a Redis list, by using the high level Call() API. 110 * This command is an example of the array reply access. */ 111 int HelloListSumLen_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) 112 { 113 if (argc != 2) return RedisModule_WrongArity(ctx); 114 115 RedisModuleCallReply *reply; 116 117 reply = RedisModule_Call(ctx,"LRANGE","sll",argv[1],(long long)0,(long long)-1); 118 size_t strlen = 0; 119 size_t items = RedisModule_CallReplyLength(reply); 120 size_t j; 121 for (j = 0; j < items; j++) { 122 RedisModuleCallReply *ele = RedisModule_CallReplyArrayElement(reply,j); 123 strlen += RedisModule_CallReplyLength(ele); 124 } 125 RedisModule_FreeCallReply(reply); 126 RedisModule_ReplyWithLongLong(ctx,strlen); 127 return REDISMODULE_OK; 128 } 129 130 /* HELLO.LIST.SPLICE srclist dstlist count 131 * Moves 'count' elements from the tail of 'srclist' to the head of 132 * 'dstlist'. If less than count elements are available, it moves as much 133 * elements as possible. */ 134 int HelloListSplice_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 135 if (argc != 4) return RedisModule_WrongArity(ctx); 136 137 RedisModuleKey *srckey = RedisModule_OpenKey(ctx,argv[1], 138 REDISMODULE_READ|REDISMODULE_WRITE); 139 RedisModuleKey *dstkey = RedisModule_OpenKey(ctx,argv[2], 140 REDISMODULE_READ|REDISMODULE_WRITE); 141 142 /* Src and dst key must be empty or lists. */ 143 if ((RedisModule_KeyType(srckey) != REDISMODULE_KEYTYPE_LIST && 144 RedisModule_KeyType(srckey) != REDISMODULE_KEYTYPE_EMPTY) || 145 (RedisModule_KeyType(dstkey) != REDISMODULE_KEYTYPE_LIST && 146 RedisModule_KeyType(dstkey) != REDISMODULE_KEYTYPE_EMPTY)) 147 { 148 RedisModule_CloseKey(srckey); 149 RedisModule_CloseKey(dstkey); 150 return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); 151 } 152 153 long long count; 154 if ((RedisModule_StringToLongLong(argv[3],&count) != REDISMODULE_OK) || 155 (count < 0)) { 156 RedisModule_CloseKey(srckey); 157 RedisModule_CloseKey(dstkey); 158 return RedisModule_ReplyWithError(ctx,"ERR invalid count"); 159 } 160 161 while(count-- > 0) { 162 RedisModuleString *ele; 163 164 ele = RedisModule_ListPop(srckey,REDISMODULE_LIST_TAIL); 165 if (ele == NULL) break; 166 RedisModule_ListPush(dstkey,REDISMODULE_LIST_HEAD,ele); 167 RedisModule_FreeString(ctx,ele); 168 } 169 170 size_t len = RedisModule_ValueLength(srckey); 171 RedisModule_CloseKey(srckey); 172 RedisModule_CloseKey(dstkey); 173 RedisModule_ReplyWithLongLong(ctx,len); 174 return REDISMODULE_OK; 175 } 176 177 /* Like the HELLO.LIST.SPLICE above, but uses automatic memory management 178 * in order to avoid freeing stuff. */ 179 int HelloListSpliceAuto_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 180 if (argc != 4) return RedisModule_WrongArity(ctx); 181 182 RedisModule_AutoMemory(ctx); 183 184 RedisModuleKey *srckey = RedisModule_OpenKey(ctx,argv[1], 185 REDISMODULE_READ|REDISMODULE_WRITE); 186 RedisModuleKey *dstkey = RedisModule_OpenKey(ctx,argv[2], 187 REDISMODULE_READ|REDISMODULE_WRITE); 188 189 /* Src and dst key must be empty or lists. */ 190 if ((RedisModule_KeyType(srckey) != REDISMODULE_KEYTYPE_LIST && 191 RedisModule_KeyType(srckey) != REDISMODULE_KEYTYPE_EMPTY) || 192 (RedisModule_KeyType(dstkey) != REDISMODULE_KEYTYPE_LIST && 193 RedisModule_KeyType(dstkey) != REDISMODULE_KEYTYPE_EMPTY)) 194 { 195 return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); 196 } 197 198 long long count; 199 if ((RedisModule_StringToLongLong(argv[3],&count) != REDISMODULE_OK) || 200 (count < 0)) 201 { 202 return RedisModule_ReplyWithError(ctx,"ERR invalid count"); 203 } 204 205 while(count-- > 0) { 206 RedisModuleString *ele; 207 208 ele = RedisModule_ListPop(srckey,REDISMODULE_LIST_TAIL); 209 if (ele == NULL) break; 210 RedisModule_ListPush(dstkey,REDISMODULE_LIST_HEAD,ele); 211 } 212 213 size_t len = RedisModule_ValueLength(srckey); 214 RedisModule_ReplyWithLongLong(ctx,len); 215 return REDISMODULE_OK; 216 } 217 218 /* HELLO.RAND.ARRAY <count> 219 * Shows how to generate arrays as commands replies. 220 * It just outputs <count> random numbers. */ 221 int HelloRandArray_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 222 if (argc != 2) return RedisModule_WrongArity(ctx); 223 long long count; 224 if (RedisModule_StringToLongLong(argv[1],&count) != REDISMODULE_OK || 225 count < 0) 226 return RedisModule_ReplyWithError(ctx,"ERR invalid count"); 227 228 /* To reply with an array, we call RedisModule_ReplyWithArray() followed 229 * by other "count" calls to other reply functions in order to generate 230 * the elements of the array. */ 231 RedisModule_ReplyWithArray(ctx,count); 232 while(count--) RedisModule_ReplyWithLongLong(ctx,rand()); 233 return REDISMODULE_OK; 234 } 235 236 /* This is a simple command to test replication. Because of the "!" modified 237 * in the RedisModule_Call() call, the two INCRs get replicated. 238 * Also note how the ECHO is replicated in an unexpected position (check 239 * comments the function implementation). */ 240 int HelloRepl1_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) 241 { 242 REDISMODULE_NOT_USED(argv); 243 REDISMODULE_NOT_USED(argc); 244 RedisModule_AutoMemory(ctx); 245 246 /* This will be replicated *after* the two INCR statements, since 247 * the Call() replication has precedence, so the actual replication 248 * stream will be: 249 * 250 * MULTI 251 * INCR foo 252 * INCR bar 253 * ECHO c foo 254 * EXEC 255 */ 256 RedisModule_Replicate(ctx,"ECHO","c","foo"); 257 258 /* Using the "!" modifier we replicate the command if it 259 * modified the dataset in some way. */ 260 RedisModule_Call(ctx,"INCR","c!","foo"); 261 RedisModule_Call(ctx,"INCR","c!","bar"); 262 263 RedisModule_ReplyWithLongLong(ctx,0); 264 265 return REDISMODULE_OK; 266 } 267 268 /* Another command to show replication. In this case, we call 269 * RedisModule_ReplicateVerbatim() to mean we want just the command to be 270 * propagated to slaves / AOF exactly as it was called by the user. 271 * 272 * This command also shows how to work with string objects. 273 * It takes a list, and increments all the elements (that must have 274 * a numerical value) by 1, returning the sum of all the elements 275 * as reply. 276 * 277 * Usage: HELLO.REPL2 <list-key> */ 278 int HelloRepl2_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 279 if (argc != 2) return RedisModule_WrongArity(ctx); 280 281 RedisModule_AutoMemory(ctx); /* Use automatic memory management. */ 282 RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], 283 REDISMODULE_READ|REDISMODULE_WRITE); 284 285 if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_LIST) 286 return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); 287 288 size_t listlen = RedisModule_ValueLength(key); 289 long long sum = 0; 290 291 /* Rotate and increment. */ 292 while(listlen--) { 293 RedisModuleString *ele = RedisModule_ListPop(key,REDISMODULE_LIST_TAIL); 294 long long val; 295 if (RedisModule_StringToLongLong(ele,&val) != REDISMODULE_OK) val = 0; 296 val++; 297 sum += val; 298 RedisModuleString *newele = RedisModule_CreateStringFromLongLong(ctx,val); 299 RedisModule_ListPush(key,REDISMODULE_LIST_HEAD,newele); 300 } 301 RedisModule_ReplyWithLongLong(ctx,sum); 302 RedisModule_ReplicateVerbatim(ctx); 303 return REDISMODULE_OK; 304 } 305 306 /* This is an example of strings DMA access. Given a key containing a string 307 * it toggles the case of each character from lower to upper case or the 308 * other way around. 309 * 310 * No automatic memory management is used in this example (for the sake 311 * of variety). 312 * 313 * HELLO.TOGGLE.CASE key */ 314 int HelloToggleCase_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 315 if (argc != 2) return RedisModule_WrongArity(ctx); 316 317 RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], 318 REDISMODULE_READ|REDISMODULE_WRITE); 319 320 int keytype = RedisModule_KeyType(key); 321 if (keytype != REDISMODULE_KEYTYPE_STRING && 322 keytype != REDISMODULE_KEYTYPE_EMPTY) 323 { 324 RedisModule_CloseKey(key); 325 return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); 326 } 327 328 if (keytype == REDISMODULE_KEYTYPE_STRING) { 329 size_t len, j; 330 char *s = RedisModule_StringDMA(key,&len,REDISMODULE_WRITE); 331 for (j = 0; j < len; j++) { 332 if (isupper(s[j])) { 333 s[j] = tolower(s[j]); 334 } else { 335 s[j] = toupper(s[j]); 336 } 337 } 338 } 339 340 RedisModule_CloseKey(key); 341 RedisModule_ReplyWithSimpleString(ctx,"OK"); 342 RedisModule_ReplicateVerbatim(ctx); 343 return REDISMODULE_OK; 344 } 345 346 /* HELLO.MORE.EXPIRE key milliseconds. 347 * 348 * If they key has already an associated TTL, extends it by "milliseconds" 349 * milliseconds. Otherwise no operation is performed. */ 350 int HelloMoreExpire_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 351 RedisModule_AutoMemory(ctx); /* Use automatic memory management. */ 352 if (argc != 3) return RedisModule_WrongArity(ctx); 353 354 mstime_t addms, expire; 355 356 if (RedisModule_StringToLongLong(argv[2],&addms) != REDISMODULE_OK) 357 return RedisModule_ReplyWithError(ctx,"ERR invalid expire time"); 358 359 RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], 360 REDISMODULE_READ|REDISMODULE_WRITE); 361 expire = RedisModule_GetExpire(key); 362 if (expire != REDISMODULE_NO_EXPIRE) { 363 expire += addms; 364 RedisModule_SetExpire(key,expire); 365 } 366 return RedisModule_ReplyWithSimpleString(ctx,"OK"); 367 } 368 369 /* HELLO.ZSUMRANGE key startscore endscore 370 * Return the sum of all the scores elements between startscore and endscore. 371 * 372 * The computation is performed two times, one time from start to end and 373 * another time backward. The two scores, returned as a two element array, 374 * should match.*/ 375 int HelloZsumRange_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 376 double score_start, score_end; 377 if (argc != 4) return RedisModule_WrongArity(ctx); 378 379 if (RedisModule_StringToDouble(argv[2],&score_start) != REDISMODULE_OK || 380 RedisModule_StringToDouble(argv[3],&score_end) != REDISMODULE_OK) 381 { 382 return RedisModule_ReplyWithError(ctx,"ERR invalid range"); 383 } 384 385 RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], 386 REDISMODULE_READ|REDISMODULE_WRITE); 387 if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_ZSET) { 388 return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); 389 } 390 391 double scoresum_a = 0; 392 double scoresum_b = 0; 393 394 RedisModule_ZsetFirstInScoreRange(key,score_start,score_end,0,0); 395 while(!RedisModule_ZsetRangeEndReached(key)) { 396 double score; 397 RedisModuleString *ele = RedisModule_ZsetRangeCurrentElement(key,&score); 398 RedisModule_FreeString(ctx,ele); 399 scoresum_a += score; 400 RedisModule_ZsetRangeNext(key); 401 } 402 RedisModule_ZsetRangeStop(key); 403 404 RedisModule_ZsetLastInScoreRange(key,score_start,score_end,0,0); 405 while(!RedisModule_ZsetRangeEndReached(key)) { 406 double score; 407 RedisModuleString *ele = RedisModule_ZsetRangeCurrentElement(key,&score); 408 RedisModule_FreeString(ctx,ele); 409 scoresum_b += score; 410 RedisModule_ZsetRangePrev(key); 411 } 412 413 RedisModule_ZsetRangeStop(key); 414 415 RedisModule_CloseKey(key); 416 417 RedisModule_ReplyWithArray(ctx,2); 418 RedisModule_ReplyWithDouble(ctx,scoresum_a); 419 RedisModule_ReplyWithDouble(ctx,scoresum_b); 420 return REDISMODULE_OK; 421 } 422 423 /* HELLO.LEXRANGE key min_lex max_lex min_age max_age 424 * This command expects a sorted set stored at key in the following form: 425 * - All the elements have score 0. 426 * - Elements are pairs of "<name>:<age>", for example "Anna:52". 427 * The command will return all the sorted set items that are lexicographically 428 * between the specified range (using the same format as ZRANGEBYLEX) 429 * and having an age between min_age and max_age. */ 430 int HelloLexRange_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 431 RedisModule_AutoMemory(ctx); /* Use automatic memory management. */ 432 433 if (argc != 6) return RedisModule_WrongArity(ctx); 434 435 RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], 436 REDISMODULE_READ|REDISMODULE_WRITE); 437 if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_ZSET) { 438 return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); 439 } 440 441 if (RedisModule_ZsetFirstInLexRange(key,argv[2],argv[3]) != REDISMODULE_OK) { 442 return RedisModule_ReplyWithError(ctx,"invalid range"); 443 } 444 445 int arraylen = 0; 446 RedisModule_ReplyWithArray(ctx,REDISMODULE_POSTPONED_ARRAY_LEN); 447 while(!RedisModule_ZsetRangeEndReached(key)) { 448 double score; 449 RedisModuleString *ele = RedisModule_ZsetRangeCurrentElement(key,&score); 450 RedisModule_ReplyWithString(ctx,ele); 451 RedisModule_FreeString(ctx,ele); 452 RedisModule_ZsetRangeNext(key); 453 arraylen++; 454 } 455 RedisModule_ZsetRangeStop(key); 456 RedisModule_ReplySetArrayLength(ctx,arraylen); 457 RedisModule_CloseKey(key); 458 return REDISMODULE_OK; 459 } 460 461 /* HELLO.HCOPY key srcfield dstfield 462 * This is just an example command that sets the hash field dstfield to the 463 * same value of srcfield. If srcfield does not exist no operation is 464 * performed. 465 * 466 * The command returns 1 if the copy is performed (srcfield exists) otherwise 467 * 0 is returned. */ 468 int HelloHCopy_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 469 RedisModule_AutoMemory(ctx); /* Use automatic memory management. */ 470 471 if (argc != 4) return RedisModule_WrongArity(ctx); 472 RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], 473 REDISMODULE_READ|REDISMODULE_WRITE); 474 int type = RedisModule_KeyType(key); 475 if (type != REDISMODULE_KEYTYPE_HASH && 476 type != REDISMODULE_KEYTYPE_EMPTY) 477 { 478 return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); 479 } 480 481 /* Get the old field value. */ 482 RedisModuleString *oldval; 483 RedisModule_HashGet(key,REDISMODULE_HASH_NONE,argv[2],&oldval,NULL); 484 if (oldval) { 485 RedisModule_HashSet(key,REDISMODULE_HASH_NONE,argv[3],oldval,NULL); 486 } 487 RedisModule_ReplyWithLongLong(ctx,oldval != NULL); 488 return REDISMODULE_OK; 489 } 490 491 /* HELLO.LEFTPAD str len ch 492 * This is an implementation of the infamous LEFTPAD function, that 493 * was at the center of an issue with the npm modules system in March 2016. 494 * 495 * LEFTPAD is a good example of using a Redis Modules API called 496 * "pool allocator", that was a famous way to allocate memory in yet another 497 * open source project, the Apache web server. 498 * 499 * The concept is very simple: there is memory that is useful to allocate 500 * only in the context of serving a request, and must be freed anyway when 501 * the callback implementing the command returns. So in that case the module 502 * does not need to retain a reference to these allocations, it is just 503 * required to free the memory before returning. When this is the case the 504 * module can call RedisModule_PoolAlloc() instead, that works like malloc() 505 * but will automatically free the memory when the module callback returns. 506 * 507 * Note that PoolAlloc() does not necessarily require AutoMemory to be 508 * active. */ 509 int HelloLeftPad_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 510 RedisModule_AutoMemory(ctx); /* Use automatic memory management. */ 511 long long padlen; 512 513 if (argc != 4) return RedisModule_WrongArity(ctx); 514 515 if ((RedisModule_StringToLongLong(argv[2],&padlen) != REDISMODULE_OK) || 516 (padlen< 0)) { 517 return RedisModule_ReplyWithError(ctx,"ERR invalid padding length"); 518 } 519 size_t strlen, chlen; 520 const char *str = RedisModule_StringPtrLen(argv[1], &strlen); 521 const char *ch = RedisModule_StringPtrLen(argv[3], &chlen); 522 523 /* If the string is already larger than the target len, just return 524 * the string itself. */ 525 if (strlen >= (size_t)padlen) 526 return RedisModule_ReplyWithString(ctx,argv[1]); 527 528 /* Padding must be a single character in this simple implementation. */ 529 if (chlen != 1) 530 return RedisModule_ReplyWithError(ctx, 531 "ERR padding must be a single char"); 532 533 /* Here we use our pool allocator, for our throw-away allocation. */ 534 padlen -= strlen; 535 char *buf = RedisModule_PoolAlloc(ctx,padlen+strlen); 536 for (long long j = 0; j < padlen; j++) buf[j] = *ch; 537 memcpy(buf+padlen,str,strlen); 538 539 RedisModule_ReplyWithStringBuffer(ctx,buf,padlen+strlen); 540 return REDISMODULE_OK; 541 } 542 543 /* This function must be present on each Redis module. It is used in order to 544 * register the commands into the Redis server. */ 545 int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 546 if (RedisModule_Init(ctx,"helloworld",1,REDISMODULE_APIVER_1) 547 == REDISMODULE_ERR) return REDISMODULE_ERR; 548 549 /* Log the list of parameters passing loading the module. */ 550 for (int j = 0; j < argc; j++) { 551 const char *s = RedisModule_StringPtrLen(argv[j],NULL); 552 printf("Module loaded with ARGV[%d] = %s\n", j, s); 553 } 554 555 if (RedisModule_CreateCommand(ctx,"hello.simple", 556 HelloSimple_RedisCommand,"readonly",0,0,0) == REDISMODULE_ERR) 557 return REDISMODULE_ERR; 558 559 if (RedisModule_CreateCommand(ctx,"hello.push.native", 560 HelloPushNative_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR) 561 return REDISMODULE_ERR; 562 563 if (RedisModule_CreateCommand(ctx,"hello.push.call", 564 HelloPushCall_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR) 565 return REDISMODULE_ERR; 566 567 if (RedisModule_CreateCommand(ctx,"hello.push.call2", 568 HelloPushCall2_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR) 569 return REDISMODULE_ERR; 570 571 if (RedisModule_CreateCommand(ctx,"hello.list.sum.len", 572 HelloListSumLen_RedisCommand,"readonly",1,1,1) == REDISMODULE_ERR) 573 return REDISMODULE_ERR; 574 575 if (RedisModule_CreateCommand(ctx,"hello.list.splice", 576 HelloListSplice_RedisCommand,"write deny-oom",1,2,1) == REDISMODULE_ERR) 577 return REDISMODULE_ERR; 578 579 if (RedisModule_CreateCommand(ctx,"hello.list.splice.auto", 580 HelloListSpliceAuto_RedisCommand, 581 "write deny-oom",1,2,1) == REDISMODULE_ERR) 582 return REDISMODULE_ERR; 583 584 if (RedisModule_CreateCommand(ctx,"hello.rand.array", 585 HelloRandArray_RedisCommand,"readonly",0,0,0) == REDISMODULE_ERR) 586 return REDISMODULE_ERR; 587 588 if (RedisModule_CreateCommand(ctx,"hello.repl1", 589 HelloRepl1_RedisCommand,"write",0,0,0) == REDISMODULE_ERR) 590 return REDISMODULE_ERR; 591 592 if (RedisModule_CreateCommand(ctx,"hello.repl2", 593 HelloRepl2_RedisCommand,"write",1,1,1) == REDISMODULE_ERR) 594 return REDISMODULE_ERR; 595 596 if (RedisModule_CreateCommand(ctx,"hello.toggle.case", 597 HelloToggleCase_RedisCommand,"write",1,1,1) == REDISMODULE_ERR) 598 return REDISMODULE_ERR; 599 600 if (RedisModule_CreateCommand(ctx,"hello.more.expire", 601 HelloMoreExpire_RedisCommand,"write",1,1,1) == REDISMODULE_ERR) 602 return REDISMODULE_ERR; 603 604 if (RedisModule_CreateCommand(ctx,"hello.zsumrange", 605 HelloZsumRange_RedisCommand,"readonly",1,1,1) == REDISMODULE_ERR) 606 return REDISMODULE_ERR; 607 608 if (RedisModule_CreateCommand(ctx,"hello.lexrange", 609 HelloLexRange_RedisCommand,"readonly",1,1,1) == REDISMODULE_ERR) 610 return REDISMODULE_ERR; 611 612 if (RedisModule_CreateCommand(ctx,"hello.hcopy", 613 HelloHCopy_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR) 614 return REDISMODULE_ERR; 615 616 if (RedisModule_CreateCommand(ctx,"hello.leftpad", 617 HelloLeftPad_RedisCommand,"",1,1,1) == REDISMODULE_ERR) 618 return REDISMODULE_ERR; 619 620 return REDISMODULE_OK; 621 } 622