list.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. #include "redismodule.h"
  2. #include <assert.h>
  3. #include <errno.h>
  4. #include <strings.h>
  5. /* LIST.GETALL key [REVERSE] */
  6. int list_getall(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
  7. if (argc < 2 || argc > 3) return RedisModule_WrongArity(ctx);
  8. int reverse = (argc == 3 &&
  9. !strcasecmp(RedisModule_StringPtrLen(argv[2], NULL),
  10. "REVERSE"));
  11. RedisModule_AutoMemory(ctx);
  12. RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ);
  13. if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_LIST) {
  14. return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);
  15. }
  16. long n = RedisModule_ValueLength(key);
  17. RedisModule_ReplyWithArray(ctx, n);
  18. if (!reverse) {
  19. for (long i = 0; i < n; i++) {
  20. RedisModuleString *elem = RedisModule_ListGet(key, i);
  21. RedisModule_ReplyWithString(ctx, elem);
  22. RedisModule_FreeString(ctx, elem);
  23. }
  24. } else {
  25. for (long i = -1; i >= -n; i--) {
  26. RedisModuleString *elem = RedisModule_ListGet(key, i);
  27. RedisModule_ReplyWithString(ctx, elem);
  28. RedisModule_FreeString(ctx, elem);
  29. }
  30. }
  31. /* Test error condition: index out of bounds */
  32. assert(RedisModule_ListGet(key, n) == NULL);
  33. assert(errno == EDOM); /* no more elements in list */
  34. /* RedisModule_CloseKey(key); //implicit, done by auto memory */
  35. return REDISMODULE_OK;
  36. }
  37. /* LIST.EDIT key [REVERSE] cmdstr [value ..]
  38. *
  39. * cmdstr is a string of the following characters:
  40. *
  41. * k -- keep
  42. * d -- delete
  43. * i -- insert value from args
  44. * r -- replace with value from args
  45. *
  46. * The number of occurrences of "i" and "r" in cmdstr) should correspond to the
  47. * number of args after cmdstr.
  48. *
  49. * The reply is the number of edits (inserts + replaces + deletes) performed.
  50. */
  51. int list_edit(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
  52. if (argc < 3) return RedisModule_WrongArity(ctx);
  53. RedisModule_AutoMemory(ctx);
  54. int argpos = 1; /* the next arg */
  55. /* key */
  56. int keymode = REDISMODULE_READ | REDISMODULE_WRITE;
  57. RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[argpos++], keymode);
  58. if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_LIST) {
  59. return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);
  60. }
  61. /* REVERSE */
  62. int reverse = 0;
  63. if (argc >= 4 &&
  64. !strcasecmp(RedisModule_StringPtrLen(argv[argpos], NULL), "REVERSE")) {
  65. reverse = 1;
  66. argpos++;
  67. }
  68. /* cmdstr */
  69. size_t cmdstr_len;
  70. const char *cmdstr = RedisModule_StringPtrLen(argv[argpos++], &cmdstr_len);
  71. /* validate cmdstr vs. argc */
  72. long num_req_args = 0;
  73. long min_list_length = 0;
  74. for (size_t cmdpos = 0; cmdpos < cmdstr_len; cmdpos++) {
  75. char c = cmdstr[cmdpos];
  76. if (c == 'i' || c == 'r') num_req_args++;
  77. if (c == 'd' || c == 'r' || c == 'k') min_list_length++;
  78. }
  79. if (argc < argpos + num_req_args) {
  80. return RedisModule_ReplyWithError(ctx, "ERR too few args");
  81. }
  82. if ((long)RedisModule_ValueLength(key) < min_list_length) {
  83. return RedisModule_ReplyWithError(ctx, "ERR list too short");
  84. }
  85. /* Iterate over the chars in cmdstr (edit instructions) */
  86. long long num_edits = 0;
  87. long index = reverse ? -1 : 0;
  88. RedisModuleString *value;
  89. for (size_t cmdpos = 0; cmdpos < cmdstr_len; cmdpos++) {
  90. switch (cmdstr[cmdpos]) {
  91. case 'i': /* insert */
  92. value = argv[argpos++];
  93. assert(RedisModule_ListInsert(key, index, value) == REDISMODULE_OK);
  94. index += reverse ? -1 : 1;
  95. num_edits++;
  96. break;
  97. case 'd': /* delete */
  98. assert(RedisModule_ListDelete(key, index) == REDISMODULE_OK);
  99. num_edits++;
  100. break;
  101. case 'r': /* replace */
  102. value = argv[argpos++];
  103. assert(RedisModule_ListSet(key, index, value) == REDISMODULE_OK);
  104. index += reverse ? -1 : 1;
  105. num_edits++;
  106. break;
  107. case 'k': /* keep */
  108. index += reverse ? -1 : 1;
  109. break;
  110. }
  111. }
  112. RedisModule_ReplyWithLongLong(ctx, num_edits);
  113. RedisModule_CloseKey(key);
  114. return REDISMODULE_OK;
  115. }
  116. /* Reply based on errno as set by the List API functions. */
  117. static int replyByErrno(RedisModuleCtx *ctx) {
  118. switch (errno) {
  119. case EDOM:
  120. return RedisModule_ReplyWithError(ctx, "ERR index out of bounds");
  121. case ENOTSUP:
  122. return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);
  123. default: assert(0); /* Can't happen */
  124. }
  125. }
  126. /* LIST.GET key index */
  127. int list_get(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
  128. if (argc != 3) return RedisModule_WrongArity(ctx);
  129. long long index;
  130. if (RedisModule_StringToLongLong(argv[2], &index) != REDISMODULE_OK) {
  131. return RedisModule_ReplyWithError(ctx, "ERR index must be a number");
  132. }
  133. RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ);
  134. RedisModuleString *value = RedisModule_ListGet(key, index);
  135. if (value) {
  136. RedisModule_ReplyWithString(ctx, value);
  137. RedisModule_FreeString(ctx, value);
  138. } else {
  139. replyByErrno(ctx);
  140. }
  141. RedisModule_CloseKey(key);
  142. return REDISMODULE_OK;
  143. }
  144. /* LIST.SET key index value */
  145. int list_set(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
  146. if (argc != 4) return RedisModule_WrongArity(ctx);
  147. long long index;
  148. if (RedisModule_StringToLongLong(argv[2], &index) != REDISMODULE_OK) {
  149. RedisModule_ReplyWithError(ctx, "ERR index must be a number");
  150. return REDISMODULE_OK;
  151. }
  152. RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE);
  153. if (RedisModule_ListSet(key, index, argv[3]) == REDISMODULE_OK) {
  154. RedisModule_ReplyWithSimpleString(ctx, "OK");
  155. } else {
  156. replyByErrno(ctx);
  157. }
  158. RedisModule_CloseKey(key);
  159. return REDISMODULE_OK;
  160. }
  161. /* LIST.INSERT key index value
  162. *
  163. * If index is negative, value is inserted after, otherwise before the element
  164. * at index.
  165. */
  166. int list_insert(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
  167. if (argc != 4) return RedisModule_WrongArity(ctx);
  168. long long index;
  169. if (RedisModule_StringToLongLong(argv[2], &index) != REDISMODULE_OK) {
  170. RedisModule_ReplyWithError(ctx, "ERR index must be a number");
  171. return REDISMODULE_OK;
  172. }
  173. RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE);
  174. if (RedisModule_ListInsert(key, index, argv[3]) == REDISMODULE_OK) {
  175. RedisModule_ReplyWithSimpleString(ctx, "OK");
  176. } else {
  177. replyByErrno(ctx);
  178. }
  179. RedisModule_CloseKey(key);
  180. return REDISMODULE_OK;
  181. }
  182. /* LIST.DELETE key index */
  183. int list_delete(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
  184. if (argc != 3) return RedisModule_WrongArity(ctx);
  185. long long index;
  186. if (RedisModule_StringToLongLong(argv[2], &index) != REDISMODULE_OK) {
  187. RedisModule_ReplyWithError(ctx, "ERR index must be a number");
  188. return REDISMODULE_OK;
  189. }
  190. RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE);
  191. if (RedisModule_ListDelete(key, index) == REDISMODULE_OK) {
  192. RedisModule_ReplyWithSimpleString(ctx, "OK");
  193. } else {
  194. replyByErrno(ctx);
  195. }
  196. RedisModule_CloseKey(key);
  197. return REDISMODULE_OK;
  198. }
  199. int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
  200. REDISMODULE_NOT_USED(argv);
  201. REDISMODULE_NOT_USED(argc);
  202. if (RedisModule_Init(ctx, "list", 1, REDISMODULE_APIVER_1) == REDISMODULE_OK &&
  203. RedisModule_CreateCommand(ctx, "list.getall", list_getall, "",
  204. 1, 1, 1) == REDISMODULE_OK &&
  205. RedisModule_CreateCommand(ctx, "list.edit", list_edit, "",
  206. 1, 1, 1) == REDISMODULE_OK &&
  207. RedisModule_CreateCommand(ctx, "list.get", list_get, "",
  208. 1, 1, 1) == REDISMODULE_OK &&
  209. RedisModule_CreateCommand(ctx, "list.set", list_set, "",
  210. 1, 1, 1) == REDISMODULE_OK &&
  211. RedisModule_CreateCommand(ctx, "list.insert", list_insert, "",
  212. 1, 1, 1) == REDISMODULE_OK &&
  213. RedisModule_CreateCommand(ctx, "list.delete", list_delete, "",
  214. 1, 1, 1) == REDISMODULE_OK) {
  215. return REDISMODULE_OK;
  216. } else {
  217. return REDISMODULE_ERR;
  218. }
  219. }