1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
|
// SPDX-FileCopyrightText: 2024 Redict Contributors
// SPDX-FileCopyrightText: 2024 Salvatore Sanfilippo <antirez at gmail dot com>
//
// SPDX-License-Identifier: BSD-3-Clause
// SPDX-License-Identifier: LGPL-3.0-only
#include "redictmodule.h"
#include <assert.h>
#include <errno.h>
#include <strings.h>
/* LIST.GETALL key [REVERSE] */
int list_getall(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
if (argc < 2 || argc > 3) return RedictModule_WrongArity(ctx);
int reverse = (argc == 3 &&
!strcasecmp(RedictModule_StringPtrLen(argv[2], NULL),
"REVERSE"));
RedictModule_AutoMemory(ctx);
RedictModuleKey *key = RedictModule_OpenKey(ctx, argv[1], REDICTMODULE_READ);
if (RedictModule_KeyType(key) != REDICTMODULE_KEYTYPE_LIST) {
return RedictModule_ReplyWithError(ctx, REDICTMODULE_ERRORMSG_WRONGTYPE);
}
long n = RedictModule_ValueLength(key);
RedictModule_ReplyWithArray(ctx, n);
if (!reverse) {
for (long i = 0; i < n; i++) {
RedictModuleString *elem = RedictModule_ListGet(key, i);
RedictModule_ReplyWithString(ctx, elem);
RedictModule_FreeString(ctx, elem);
}
} else {
for (long i = -1; i >= -n; i--) {
RedictModuleString *elem = RedictModule_ListGet(key, i);
RedictModule_ReplyWithString(ctx, elem);
RedictModule_FreeString(ctx, elem);
}
}
/* Test error condition: index out of bounds */
assert(RedictModule_ListGet(key, n) == NULL);
assert(errno == EDOM); /* no more elements in list */
/* RedictModule_CloseKey(key); //implicit, done by auto memory */
return REDICTMODULE_OK;
}
/* LIST.EDIT key [REVERSE] cmdstr [value ..]
*
* cmdstr is a string of the following characters:
*
* k -- keep
* d -- delete
* i -- insert value from args
* r -- replace with value from args
*
* The number of occurrences of "i" and "r" in cmdstr) should correspond to the
* number of args after cmdstr.
*
* Reply with a RESP3 Map, containing the number of edits (inserts, replaces, deletes)
* performed, as well as the last index and the entry it points to.
*/
int list_edit(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
if (argc < 3) return RedictModule_WrongArity(ctx);
RedictModule_AutoMemory(ctx);
int argpos = 1; /* the next arg */
/* key */
int keymode = REDICTMODULE_READ | REDICTMODULE_WRITE;
RedictModuleKey *key = RedictModule_OpenKey(ctx, argv[argpos++], keymode);
if (RedictModule_KeyType(key) != REDICTMODULE_KEYTYPE_LIST) {
return RedictModule_ReplyWithError(ctx, REDICTMODULE_ERRORMSG_WRONGTYPE);
}
/* REVERSE */
int reverse = 0;
if (argc >= 4 &&
!strcasecmp(RedictModule_StringPtrLen(argv[argpos], NULL), "REVERSE")) {
reverse = 1;
argpos++;
}
/* cmdstr */
size_t cmdstr_len;
const char *cmdstr = RedictModule_StringPtrLen(argv[argpos++], &cmdstr_len);
/* validate cmdstr vs. argc */
long num_req_args = 0;
long min_list_length = 0;
for (size_t cmdpos = 0; cmdpos < cmdstr_len; cmdpos++) {
char c = cmdstr[cmdpos];
if (c == 'i' || c == 'r') num_req_args++;
if (c == 'd' || c == 'r' || c == 'k') min_list_length++;
}
if (argc < argpos + num_req_args) {
return RedictModule_ReplyWithError(ctx, "ERR too few args");
}
if ((long)RedictModule_ValueLength(key) < min_list_length) {
return RedictModule_ReplyWithError(ctx, "ERR list too short");
}
/* Iterate over the chars in cmdstr (edit instructions) */
long long num_inserts = 0, num_deletes = 0, num_replaces = 0;
long index = reverse ? -1 : 0;
RedictModuleString *value;
for (size_t cmdpos = 0; cmdpos < cmdstr_len; cmdpos++) {
switch (cmdstr[cmdpos]) {
case 'i': /* insert */
value = argv[argpos++];
assert(RedictModule_ListInsert(key, index, value) == REDICTMODULE_OK);
index += reverse ? -1 : 1;
num_inserts++;
break;
case 'd': /* delete */
assert(RedictModule_ListDelete(key, index) == REDICTMODULE_OK);
num_deletes++;
break;
case 'r': /* replace */
value = argv[argpos++];
assert(RedictModule_ListSet(key, index, value) == REDICTMODULE_OK);
index += reverse ? -1 : 1;
num_replaces++;
break;
case 'k': /* keep */
index += reverse ? -1 : 1;
break;
}
}
RedictModuleString *v = RedictModule_ListGet(key, index);
RedictModule_ReplyWithMap(ctx, v ? 5 : 4);
RedictModule_ReplyWithCString(ctx, "i");
RedictModule_ReplyWithLongLong(ctx, num_inserts);
RedictModule_ReplyWithCString(ctx, "d");
RedictModule_ReplyWithLongLong(ctx, num_deletes);
RedictModule_ReplyWithCString(ctx, "r");
RedictModule_ReplyWithLongLong(ctx, num_replaces);
RedictModule_ReplyWithCString(ctx, "index");
RedictModule_ReplyWithLongLong(ctx, index);
if (v) {
RedictModule_ReplyWithCString(ctx, "entry");
RedictModule_ReplyWithString(ctx, v);
RedictModule_FreeString(ctx, v);
}
RedictModule_CloseKey(key);
return REDICTMODULE_OK;
}
/* Reply based on errno as set by the List API functions. */
static int replyByErrno(RedictModuleCtx *ctx) {
switch (errno) {
case EDOM:
return RedictModule_ReplyWithError(ctx, "ERR index out of bounds");
case ENOTSUP:
return RedictModule_ReplyWithError(ctx, REDICTMODULE_ERRORMSG_WRONGTYPE);
default: assert(0); /* Can't happen */
}
}
/* LIST.GET key index */
int list_get(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
if (argc != 3) return RedictModule_WrongArity(ctx);
long long index;
if (RedictModule_StringToLongLong(argv[2], &index) != REDICTMODULE_OK) {
return RedictModule_ReplyWithError(ctx, "ERR index must be a number");
}
RedictModuleKey *key = RedictModule_OpenKey(ctx, argv[1], REDICTMODULE_READ);
RedictModuleString *value = RedictModule_ListGet(key, index);
if (value) {
RedictModule_ReplyWithString(ctx, value);
RedictModule_FreeString(ctx, value);
} else {
replyByErrno(ctx);
}
RedictModule_CloseKey(key);
return REDICTMODULE_OK;
}
/* LIST.SET key index value */
int list_set(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
if (argc != 4) return RedictModule_WrongArity(ctx);
long long index;
if (RedictModule_StringToLongLong(argv[2], &index) != REDICTMODULE_OK) {
RedictModule_ReplyWithError(ctx, "ERR index must be a number");
return REDICTMODULE_OK;
}
RedictModuleKey *key = RedictModule_OpenKey(ctx, argv[1], REDICTMODULE_WRITE);
if (RedictModule_ListSet(key, index, argv[3]) == REDICTMODULE_OK) {
RedictModule_ReplyWithSimpleString(ctx, "OK");
} else {
replyByErrno(ctx);
}
RedictModule_CloseKey(key);
return REDICTMODULE_OK;
}
/* LIST.INSERT key index value
*
* If index is negative, value is inserted after, otherwise before the element
* at index.
*/
int list_insert(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
if (argc != 4) return RedictModule_WrongArity(ctx);
long long index;
if (RedictModule_StringToLongLong(argv[2], &index) != REDICTMODULE_OK) {
RedictModule_ReplyWithError(ctx, "ERR index must be a number");
return REDICTMODULE_OK;
}
RedictModuleKey *key = RedictModule_OpenKey(ctx, argv[1], REDICTMODULE_WRITE);
if (RedictModule_ListInsert(key, index, argv[3]) == REDICTMODULE_OK) {
RedictModule_ReplyWithSimpleString(ctx, "OK");
} else {
replyByErrno(ctx);
}
RedictModule_CloseKey(key);
return REDICTMODULE_OK;
}
/* LIST.DELETE key index */
int list_delete(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
if (argc != 3) return RedictModule_WrongArity(ctx);
long long index;
if (RedictModule_StringToLongLong(argv[2], &index) != REDICTMODULE_OK) {
RedictModule_ReplyWithError(ctx, "ERR index must be a number");
return REDICTMODULE_OK;
}
RedictModuleKey *key = RedictModule_OpenKey(ctx, argv[1], REDICTMODULE_WRITE);
if (RedictModule_ListDelete(key, index) == REDICTMODULE_OK) {
RedictModule_ReplyWithSimpleString(ctx, "OK");
} else {
replyByErrno(ctx);
}
RedictModule_CloseKey(key);
return REDICTMODULE_OK;
}
int RedictModule_OnLoad(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
REDICTMODULE_NOT_USED(argv);
REDICTMODULE_NOT_USED(argc);
if (RedictModule_Init(ctx, "list", 1, REDICTMODULE_APIVER_1) == REDICTMODULE_OK &&
RedictModule_CreateCommand(ctx, "list.getall", list_getall, "",
1, 1, 1) == REDICTMODULE_OK &&
RedictModule_CreateCommand(ctx, "list.edit", list_edit, "write",
1, 1, 1) == REDICTMODULE_OK &&
RedictModule_CreateCommand(ctx, "list.get", list_get, "write",
1, 1, 1) == REDICTMODULE_OK &&
RedictModule_CreateCommand(ctx, "list.set", list_set, "write",
1, 1, 1) == REDICTMODULE_OK &&
RedictModule_CreateCommand(ctx, "list.insert", list_insert, "write",
1, 1, 1) == REDICTMODULE_OK &&
RedictModule_CreateCommand(ctx, "list.delete", list_delete, "write",
1, 1, 1) == REDICTMODULE_OK) {
return REDICTMODULE_OK;
} else {
return REDICTMODULE_ERR;
}
}
|