diff options
Diffstat (limited to 'examples/redis-unstable/src/call_reply.c')
| -rw-r--r-- | examples/redis-unstable/src/call_reply.c | 540 |
1 files changed, 540 insertions, 0 deletions
diff --git a/examples/redis-unstable/src/call_reply.c b/examples/redis-unstable/src/call_reply.c new file mode 100644 index 0000000..2a4f710 --- /dev/null +++ b/examples/redis-unstable/src/call_reply.c | |||
| @@ -0,0 +1,540 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2009-Present, Redis Ltd. | ||
| 3 | * All rights reserved. | ||
| 4 | * | ||
| 5 | * Licensed under your choice of (a) the Redis Source Available License 2.0 | ||
| 6 | * (RSALv2); or (b) the Server Side Public License v1 (SSPLv1); or (c) the | ||
| 7 | * GNU Affero General Public License v3 (AGPLv3). | ||
| 8 | */ | ||
| 9 | |||
| 10 | #include "server.h" | ||
| 11 | #include "call_reply.h" | ||
| 12 | |||
| 13 | #define REPLY_FLAG_ROOT (1<<0) | ||
| 14 | #define REPLY_FLAG_PARSED (1<<1) | ||
| 15 | #define REPLY_FLAG_RESP3 (1<<2) | ||
| 16 | |||
| 17 | /* -------------------------------------------------------- | ||
| 18 | * An opaque struct used to parse a RESP protocol reply and | ||
| 19 | * represent it. Used when parsing replies such as in RM_Call | ||
| 20 | * or Lua scripts. | ||
| 21 | * -------------------------------------------------------- */ | ||
| 22 | struct CallReply { | ||
| 23 | void *private_data; | ||
| 24 | sds original_proto; /* Available only for root reply. */ | ||
| 25 | const char *proto; | ||
| 26 | size_t proto_len; | ||
| 27 | int type; /* REPLY_... */ | ||
| 28 | int flags; /* REPLY_FLAG... */ | ||
| 29 | size_t len; /* Length of a string, or the number elements in an array. */ | ||
| 30 | union { | ||
| 31 | const char *str; /* String pointer for string and error replies. This | ||
| 32 | * does not need to be freed, always points inside | ||
| 33 | * a reply->proto buffer of the reply object or, in | ||
| 34 | * case of array elements, of parent reply objects. */ | ||
| 35 | struct { | ||
| 36 | const char *str; | ||
| 37 | const char *format; | ||
| 38 | } verbatim_str; /* Reply value for verbatim string */ | ||
| 39 | long long ll; /* Reply value for integer reply. */ | ||
| 40 | double d; /* Reply value for double reply. */ | ||
| 41 | struct CallReply *array; /* Array of sub-reply elements. used for set, array, map, and attribute */ | ||
| 42 | } val; | ||
| 43 | list *deferred_error_list; /* list of errors in sds form or NULL */ | ||
| 44 | struct CallReply *attribute; /* attribute reply, NULL if not exists */ | ||
| 45 | }; | ||
| 46 | |||
| 47 | static void callReplySetSharedData(CallReply *rep, int type, const char *proto, size_t proto_len, int extra_flags) { | ||
| 48 | rep->type = type; | ||
| 49 | rep->proto = proto; | ||
| 50 | rep->proto_len = proto_len; | ||
| 51 | rep->flags |= extra_flags; | ||
| 52 | } | ||
| 53 | |||
| 54 | static void callReplyNull(void *ctx, const char *proto, size_t proto_len) { | ||
| 55 | CallReply *rep = ctx; | ||
| 56 | callReplySetSharedData(rep, REDISMODULE_REPLY_NULL, proto, proto_len, REPLY_FLAG_RESP3); | ||
| 57 | } | ||
| 58 | |||
| 59 | static void callReplyNullBulkString(void *ctx, const char *proto, size_t proto_len) { | ||
| 60 | CallReply *rep = ctx; | ||
| 61 | callReplySetSharedData(rep, REDISMODULE_REPLY_NULL, proto, proto_len, 0); | ||
| 62 | } | ||
| 63 | |||
| 64 | static void callReplyNullArray(void *ctx, const char *proto, size_t proto_len) { | ||
| 65 | CallReply *rep = ctx; | ||
| 66 | callReplySetSharedData(rep, REDISMODULE_REPLY_NULL, proto, proto_len, 0); | ||
| 67 | } | ||
| 68 | |||
| 69 | static void callReplyBulkString(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len) { | ||
| 70 | CallReply *rep = ctx; | ||
| 71 | callReplySetSharedData(rep, REDISMODULE_REPLY_STRING, proto, proto_len, 0); | ||
| 72 | rep->len = len; | ||
| 73 | rep->val.str = str; | ||
| 74 | } | ||
| 75 | |||
| 76 | static void callReplyError(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len) { | ||
| 77 | CallReply *rep = ctx; | ||
| 78 | callReplySetSharedData(rep, REDISMODULE_REPLY_ERROR, proto, proto_len, 0); | ||
| 79 | rep->len = len; | ||
| 80 | rep->val.str = str; | ||
| 81 | } | ||
| 82 | |||
| 83 | static void callReplySimpleStr(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len) { | ||
| 84 | CallReply *rep = ctx; | ||
| 85 | callReplySetSharedData(rep, REDISMODULE_REPLY_STRING, proto, proto_len, 0); | ||
| 86 | rep->len = len; | ||
| 87 | rep->val.str = str; | ||
| 88 | } | ||
| 89 | |||
| 90 | static void callReplyLong(void *ctx, long long val, const char *proto, size_t proto_len) { | ||
| 91 | CallReply *rep = ctx; | ||
| 92 | callReplySetSharedData(rep, REDISMODULE_REPLY_INTEGER, proto, proto_len, 0); | ||
| 93 | rep->val.ll = val; | ||
| 94 | } | ||
| 95 | |||
| 96 | static void callReplyDouble(void *ctx, double val, const char *proto, size_t proto_len) { | ||
| 97 | CallReply *rep = ctx; | ||
| 98 | callReplySetSharedData(rep, REDISMODULE_REPLY_DOUBLE, proto, proto_len, REPLY_FLAG_RESP3); | ||
| 99 | rep->val.d = val; | ||
| 100 | } | ||
| 101 | |||
| 102 | static void callReplyVerbatimString(void *ctx, const char *format, const char *str, size_t len, const char *proto, size_t proto_len) { | ||
| 103 | CallReply *rep = ctx; | ||
| 104 | callReplySetSharedData(rep, REDISMODULE_REPLY_VERBATIM_STRING, proto, proto_len, REPLY_FLAG_RESP3); | ||
| 105 | rep->len = len; | ||
| 106 | rep->val.verbatim_str.str = str; | ||
| 107 | rep->val.verbatim_str.format = format; | ||
| 108 | } | ||
| 109 | |||
| 110 | static void callReplyBigNumber(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len) { | ||
| 111 | CallReply *rep = ctx; | ||
| 112 | callReplySetSharedData(rep, REDISMODULE_REPLY_BIG_NUMBER, proto, proto_len, REPLY_FLAG_RESP3); | ||
| 113 | rep->len = len; | ||
| 114 | rep->val.str = str; | ||
| 115 | } | ||
| 116 | |||
| 117 | static void callReplyBool(void *ctx, int val, const char *proto, size_t proto_len) { | ||
| 118 | CallReply *rep = ctx; | ||
| 119 | callReplySetSharedData(rep, REDISMODULE_REPLY_BOOL, proto, proto_len, REPLY_FLAG_RESP3); | ||
| 120 | rep->val.ll = val; | ||
| 121 | } | ||
| 122 | |||
| 123 | static void callReplyParseCollection(ReplyParser *parser, CallReply *rep, size_t len, const char *proto, size_t elements_per_entry) { | ||
| 124 | rep->len = len; | ||
| 125 | rep->val.array = zcalloc(elements_per_entry * len * sizeof(CallReply)); | ||
| 126 | for (size_t i = 0; i < len * elements_per_entry; i += elements_per_entry) { | ||
| 127 | for (size_t j = 0 ; j < elements_per_entry ; ++j) { | ||
| 128 | rep->val.array[i + j].private_data = rep->private_data; | ||
| 129 | parseReply(parser, rep->val.array + i + j); | ||
| 130 | rep->val.array[i + j].flags |= REPLY_FLAG_PARSED; | ||
| 131 | if (rep->val.array[i + j].flags & REPLY_FLAG_RESP3) { | ||
| 132 | /* If one of the sub-replies is RESP3, then the current reply is also RESP3. */ | ||
| 133 | rep->flags |= REPLY_FLAG_RESP3; | ||
| 134 | } | ||
| 135 | } | ||
| 136 | } | ||
| 137 | rep->proto = proto; | ||
| 138 | rep->proto_len = parser->curr_location - proto; | ||
| 139 | } | ||
| 140 | |||
| 141 | static void callReplyAttribute(ReplyParser *parser, void *ctx, size_t len, const char *proto) { | ||
| 142 | CallReply *rep = ctx; | ||
| 143 | rep->attribute = zcalloc(sizeof(CallReply)); | ||
| 144 | |||
| 145 | /* Continue parsing the attribute reply */ | ||
| 146 | rep->attribute->len = len; | ||
| 147 | rep->attribute->type = REDISMODULE_REPLY_ATTRIBUTE; | ||
| 148 | callReplyParseCollection(parser, rep->attribute, len, proto, 2); | ||
| 149 | rep->attribute->flags |= REPLY_FLAG_PARSED | REPLY_FLAG_RESP3; | ||
| 150 | rep->attribute->private_data = rep->private_data; | ||
| 151 | |||
| 152 | /* Continue parsing the reply */ | ||
| 153 | parseReply(parser, rep); | ||
| 154 | |||
| 155 | /* In this case we need to fix the proto address and len, it should start from the attribute */ | ||
| 156 | rep->proto = proto; | ||
| 157 | rep->proto_len = parser->curr_location - proto; | ||
| 158 | rep->flags |= REPLY_FLAG_RESP3; | ||
| 159 | } | ||
| 160 | |||
| 161 | static void callReplyArray(ReplyParser *parser, void *ctx, size_t len, const char *proto) { | ||
| 162 | CallReply *rep = ctx; | ||
| 163 | rep->type = REDISMODULE_REPLY_ARRAY; | ||
| 164 | callReplyParseCollection(parser, rep, len, proto, 1); | ||
| 165 | } | ||
| 166 | |||
| 167 | static void callReplySet(ReplyParser *parser, void *ctx, size_t len, const char *proto) { | ||
| 168 | CallReply *rep = ctx; | ||
| 169 | rep->type = REDISMODULE_REPLY_SET; | ||
| 170 | callReplyParseCollection(parser, rep, len, proto, 1); | ||
| 171 | rep->flags |= REPLY_FLAG_RESP3; | ||
| 172 | } | ||
| 173 | |||
| 174 | static void callReplyMap(ReplyParser *parser, void *ctx, size_t len, const char *proto) { | ||
| 175 | CallReply *rep = ctx; | ||
| 176 | rep->type = REDISMODULE_REPLY_MAP; | ||
| 177 | callReplyParseCollection(parser, rep, len, proto, 2); | ||
| 178 | rep->flags |= REPLY_FLAG_RESP3; | ||
| 179 | } | ||
| 180 | |||
| 181 | static void callReplyParseError(void *ctx) { | ||
| 182 | CallReply *rep = ctx; | ||
| 183 | rep->type = REDISMODULE_REPLY_UNKNOWN; | ||
| 184 | } | ||
| 185 | |||
| 186 | /* Recursively free the current call reply and its sub-replies. */ | ||
| 187 | static void freeCallReplyInternal(CallReply *rep) { | ||
| 188 | if (rep->type == REDISMODULE_REPLY_ARRAY || rep->type == REDISMODULE_REPLY_SET) { | ||
| 189 | for (size_t i = 0 ; i < rep->len ; ++i) { | ||
| 190 | freeCallReplyInternal(rep->val.array + i); | ||
| 191 | } | ||
| 192 | zfree(rep->val.array); | ||
| 193 | } | ||
| 194 | |||
| 195 | if (rep->type == REDISMODULE_REPLY_MAP || rep->type == REDISMODULE_REPLY_ATTRIBUTE) { | ||
| 196 | for (size_t i = 0 ; i < rep->len ; ++i) { | ||
| 197 | freeCallReplyInternal(rep->val.array + i * 2); | ||
| 198 | freeCallReplyInternal(rep->val.array + i * 2 + 1); | ||
| 199 | } | ||
| 200 | zfree(rep->val.array); | ||
| 201 | } | ||
| 202 | |||
| 203 | if (rep->attribute) { | ||
| 204 | freeCallReplyInternal(rep->attribute); | ||
| 205 | zfree(rep->attribute); | ||
| 206 | } | ||
| 207 | } | ||
| 208 | |||
| 209 | /* Free the given call reply and its children (in case of nested reply) recursively. | ||
| 210 | * If private data was set when the CallReply was created it will not be freed, as it's | ||
| 211 | * the caller's responsibility to free it before calling freeCallReply(). */ | ||
| 212 | void freeCallReply(CallReply *rep) { | ||
| 213 | if (!(rep->flags & REPLY_FLAG_ROOT)) { | ||
| 214 | return; | ||
| 215 | } | ||
| 216 | if (rep->flags & REPLY_FLAG_PARSED) { | ||
| 217 | if (rep->type == REDISMODULE_REPLY_PROMISE) { | ||
| 218 | zfree(rep); | ||
| 219 | return; | ||
| 220 | } | ||
| 221 | freeCallReplyInternal(rep); | ||
| 222 | } | ||
| 223 | sdsfree(rep->original_proto); | ||
| 224 | if (rep->deferred_error_list) | ||
| 225 | listRelease(rep->deferred_error_list); | ||
| 226 | zfree(rep); | ||
| 227 | } | ||
| 228 | |||
| 229 | CallReply *callReplyCreatePromise(void *private_data) { | ||
| 230 | CallReply *res = zmalloc(sizeof(*res)); | ||
| 231 | res->type = REDISMODULE_REPLY_PROMISE; | ||
| 232 | /* Mark the reply as parsed so there will be not attempt to parse | ||
| 233 | * it when calling reply API such as freeCallReply. | ||
| 234 | * Also mark the reply as root so freeCallReply will not ignore it. */ | ||
| 235 | res->flags |= REPLY_FLAG_PARSED | REPLY_FLAG_ROOT; | ||
| 236 | res->private_data = private_data; | ||
| 237 | return res; | ||
| 238 | } | ||
| 239 | |||
| 240 | static const ReplyParserCallbacks DefaultParserCallbacks = { | ||
| 241 | .null_callback = callReplyNull, | ||
| 242 | .bulk_string_callback = callReplyBulkString, | ||
| 243 | .null_bulk_string_callback = callReplyNullBulkString, | ||
| 244 | .null_array_callback = callReplyNullArray, | ||
| 245 | .error_callback = callReplyError, | ||
| 246 | .simple_str_callback = callReplySimpleStr, | ||
| 247 | .long_callback = callReplyLong, | ||
| 248 | .array_callback = callReplyArray, | ||
| 249 | .set_callback = callReplySet, | ||
| 250 | .map_callback = callReplyMap, | ||
| 251 | .double_callback = callReplyDouble, | ||
| 252 | .bool_callback = callReplyBool, | ||
| 253 | .big_number_callback = callReplyBigNumber, | ||
| 254 | .verbatim_string_callback = callReplyVerbatimString, | ||
| 255 | .attribute_callback = callReplyAttribute, | ||
| 256 | .error = callReplyParseError, | ||
| 257 | }; | ||
| 258 | |||
| 259 | /* Parse the buffer located in rep->original_proto and update the CallReply | ||
| 260 | * structure to represent its contents. */ | ||
| 261 | static void callReplyParse(CallReply *rep) { | ||
| 262 | if (rep->flags & REPLY_FLAG_PARSED) { | ||
| 263 | return; | ||
| 264 | } | ||
| 265 | |||
| 266 | ReplyParser parser = {.curr_location = rep->proto, .callbacks = DefaultParserCallbacks}; | ||
| 267 | |||
| 268 | parseReply(&parser, rep); | ||
| 269 | rep->flags |= REPLY_FLAG_PARSED; | ||
| 270 | } | ||
| 271 | |||
| 272 | /* Return the call reply type (REDISMODULE_REPLY_...). */ | ||
| 273 | int callReplyType(CallReply *rep) { | ||
| 274 | if (!rep) return REDISMODULE_REPLY_UNKNOWN; | ||
| 275 | callReplyParse(rep); | ||
| 276 | return rep->type; | ||
| 277 | } | ||
| 278 | |||
| 279 | /* Return reply string as buffer and len. Applicable to: | ||
| 280 | * - REDISMODULE_REPLY_STRING | ||
| 281 | * - REDISMODULE_REPLY_ERROR | ||
| 282 | * | ||
| 283 | * The return value is borrowed from CallReply, so it must not be freed | ||
| 284 | * explicitly or used after CallReply itself is freed. | ||
| 285 | * | ||
| 286 | * The returned value is not NULL terminated and its length is returned by | ||
| 287 | * reference through len, which must not be NULL. | ||
| 288 | */ | ||
| 289 | const char *callReplyGetString(CallReply *rep, size_t *len) { | ||
| 290 | callReplyParse(rep); | ||
| 291 | if (rep->type != REDISMODULE_REPLY_STRING && | ||
| 292 | rep->type != REDISMODULE_REPLY_ERROR) return NULL; | ||
| 293 | if (len) *len = rep->len; | ||
| 294 | return rep->val.str; | ||
| 295 | } | ||
| 296 | |||
| 297 | /* Return a long long reply value. Applicable to: | ||
| 298 | * - REDISMODULE_REPLY_INTEGER | ||
| 299 | */ | ||
| 300 | long long callReplyGetLongLong(CallReply *rep) { | ||
| 301 | callReplyParse(rep); | ||
| 302 | if (rep->type != REDISMODULE_REPLY_INTEGER) return LLONG_MIN; | ||
| 303 | return rep->val.ll; | ||
| 304 | } | ||
| 305 | |||
| 306 | /* Return a double reply value. Applicable to: | ||
| 307 | * - REDISMODULE_REPLY_DOUBLE | ||
| 308 | */ | ||
| 309 | double callReplyGetDouble(CallReply *rep) { | ||
| 310 | callReplyParse(rep); | ||
| 311 | if (rep->type != REDISMODULE_REPLY_DOUBLE) return LLONG_MIN; | ||
| 312 | return rep->val.d; | ||
| 313 | } | ||
| 314 | |||
| 315 | /* Return a reply Boolean value. Applicable to: | ||
| 316 | * - REDISMODULE_REPLY_BOOL | ||
| 317 | */ | ||
| 318 | int callReplyGetBool(CallReply *rep) { | ||
| 319 | callReplyParse(rep); | ||
| 320 | if (rep->type != REDISMODULE_REPLY_BOOL) return INT_MIN; | ||
| 321 | return rep->val.ll; | ||
| 322 | } | ||
| 323 | |||
| 324 | /* Return reply length. Applicable to: | ||
| 325 | * - REDISMODULE_REPLY_STRING | ||
| 326 | * - REDISMODULE_REPLY_ERROR | ||
| 327 | * - REDISMODULE_REPLY_ARRAY | ||
| 328 | * - REDISMODULE_REPLY_SET | ||
| 329 | * - REDISMODULE_REPLY_MAP | ||
| 330 | * - REDISMODULE_REPLY_ATTRIBUTE | ||
| 331 | */ | ||
| 332 | size_t callReplyGetLen(CallReply *rep) { | ||
| 333 | callReplyParse(rep); | ||
| 334 | switch(rep->type) { | ||
| 335 | case REDISMODULE_REPLY_STRING: | ||
| 336 | case REDISMODULE_REPLY_ERROR: | ||
| 337 | case REDISMODULE_REPLY_ARRAY: | ||
| 338 | case REDISMODULE_REPLY_SET: | ||
| 339 | case REDISMODULE_REPLY_MAP: | ||
| 340 | case REDISMODULE_REPLY_ATTRIBUTE: | ||
| 341 | return rep->len; | ||
| 342 | default: | ||
| 343 | return 0; | ||
| 344 | } | ||
| 345 | } | ||
| 346 | |||
| 347 | static CallReply *callReplyGetCollectionElement(CallReply *rep, size_t idx, int elements_per_entry) { | ||
| 348 | if (idx >= rep->len * elements_per_entry) return NULL; // real len is rep->len * elements_per_entry | ||
| 349 | return rep->val.array+idx; | ||
| 350 | } | ||
| 351 | |||
| 352 | /* Return a reply array element at a given index. Applicable to: | ||
| 353 | * - REDISMODULE_REPLY_ARRAY | ||
| 354 | * | ||
| 355 | * The return value is borrowed from CallReply, so it must not be freed | ||
| 356 | * explicitly or used after CallReply itself is freed. | ||
| 357 | */ | ||
| 358 | CallReply *callReplyGetArrayElement(CallReply *rep, size_t idx) { | ||
| 359 | callReplyParse(rep); | ||
| 360 | if (rep->type != REDISMODULE_REPLY_ARRAY) return NULL; | ||
| 361 | return callReplyGetCollectionElement(rep, idx, 1); | ||
| 362 | } | ||
| 363 | |||
| 364 | /* Return a reply set element at a given index. Applicable to: | ||
| 365 | * - REDISMODULE_REPLY_SET | ||
| 366 | * | ||
| 367 | * The return value is borrowed from CallReply, so it must not be freed | ||
| 368 | * explicitly or used after CallReply itself is freed. | ||
| 369 | */ | ||
| 370 | CallReply *callReplyGetSetElement(CallReply *rep, size_t idx) { | ||
| 371 | callReplyParse(rep); | ||
| 372 | if (rep->type != REDISMODULE_REPLY_SET) return NULL; | ||
| 373 | return callReplyGetCollectionElement(rep, idx, 1); | ||
| 374 | } | ||
| 375 | |||
| 376 | static int callReplyGetMapElementInternal(CallReply *rep, size_t idx, CallReply **key, CallReply **val, int type) { | ||
| 377 | callReplyParse(rep); | ||
| 378 | if (rep->type != type) return C_ERR; | ||
| 379 | if (idx >= rep->len) return C_ERR; | ||
| 380 | if (key) *key = callReplyGetCollectionElement(rep, idx * 2, 2); | ||
| 381 | if (val) *val = callReplyGetCollectionElement(rep, idx * 2 + 1, 2); | ||
| 382 | return C_OK; | ||
| 383 | } | ||
| 384 | |||
| 385 | /* Retrieve a map reply key and value at a given index. Applicable to: | ||
| 386 | * - REDISMODULE_REPLY_MAP | ||
| 387 | * | ||
| 388 | * The key and value are returned by reference through key and val, | ||
| 389 | * which may also be NULL if not needed. | ||
| 390 | * | ||
| 391 | * Returns C_OK on success or C_ERR if reply type mismatches, or if idx is out | ||
| 392 | * of range. | ||
| 393 | * | ||
| 394 | * The returned values are borrowed from CallReply, so they must not be freed | ||
| 395 | * explicitly or used after CallReply itself is freed. | ||
| 396 | */ | ||
| 397 | int callReplyGetMapElement(CallReply *rep, size_t idx, CallReply **key, CallReply **val) { | ||
| 398 | return callReplyGetMapElementInternal(rep, idx, key, val, REDISMODULE_REPLY_MAP); | ||
| 399 | } | ||
| 400 | |||
| 401 | /* Return reply attribute, or NULL if it does not exist. Applicable to all replies. | ||
| 402 | * | ||
| 403 | * The returned values are borrowed from CallReply, so they must not be freed | ||
| 404 | * explicitly or used after CallReply itself is freed. | ||
| 405 | */ | ||
| 406 | CallReply *callReplyGetAttribute(CallReply *rep) { | ||
| 407 | return rep->attribute; | ||
| 408 | } | ||
| 409 | |||
| 410 | /* Retrieve attribute reply key and value at a given index. Applicable to: | ||
| 411 | * - REDISMODULE_REPLY_ATTRIBUTE | ||
| 412 | * | ||
| 413 | * The key and value are returned by reference through key and val, | ||
| 414 | * which may also be NULL if not needed. | ||
| 415 | * | ||
| 416 | * Returns C_OK on success or C_ERR if reply type mismatches, or if idx is out | ||
| 417 | * of range. | ||
| 418 | * | ||
| 419 | * The returned values are borrowed from CallReply, so they must not be freed | ||
| 420 | * explicitly or used after CallReply itself is freed. | ||
| 421 | */ | ||
| 422 | int callReplyGetAttributeElement(CallReply *rep, size_t idx, CallReply **key, CallReply **val) { | ||
| 423 | return callReplyGetMapElementInternal(rep, idx, key, val, REDISMODULE_REPLY_MAP); | ||
| 424 | } | ||
| 425 | |||
| 426 | /* Return a big number reply value. Applicable to: | ||
| 427 | * - REDISMODULE_REPLY_BIG_NUMBER | ||
| 428 | * | ||
| 429 | * The returned values are borrowed from CallReply, so they must not be freed | ||
| 430 | * explicitly or used after CallReply itself is freed. | ||
| 431 | * | ||
| 432 | * The return value is guaranteed to be a big number, as described in the RESP3 | ||
| 433 | * protocol specifications. | ||
| 434 | * | ||
| 435 | * The returned value is not NULL terminated and its length is returned by | ||
| 436 | * reference through len, which must not be NULL. | ||
| 437 | */ | ||
| 438 | const char *callReplyGetBigNumber(CallReply *rep, size_t *len) { | ||
| 439 | callReplyParse(rep); | ||
| 440 | if (rep->type != REDISMODULE_REPLY_BIG_NUMBER) return NULL; | ||
| 441 | *len = rep->len; | ||
| 442 | return rep->val.str; | ||
| 443 | } | ||
| 444 | |||
| 445 | /* Return a verbatim string reply value. Applicable to: | ||
| 446 | * - REDISMODULE_REPLY_VERBATIM_STRING | ||
| 447 | * | ||
| 448 | * If format is non-NULL, the verbatim reply format is also returned by value. | ||
| 449 | * | ||
| 450 | * The optional output argument can be given to get a verbatim reply | ||
| 451 | * format, or can be set NULL if not needed. | ||
| 452 | * | ||
| 453 | * The return value is borrowed from CallReply, so it must not be freed | ||
| 454 | * explicitly or used after CallReply itself is freed. | ||
| 455 | * | ||
| 456 | * The returned value is not NULL terminated and its length is returned by | ||
| 457 | * reference through len, which must not be NULL. | ||
| 458 | */ | ||
| 459 | const char *callReplyGetVerbatim(CallReply *rep, size_t *len, const char **format){ | ||
| 460 | callReplyParse(rep); | ||
| 461 | if (rep->type != REDISMODULE_REPLY_VERBATIM_STRING) return NULL; | ||
| 462 | *len = rep->len; | ||
| 463 | if (format) *format = rep->val.verbatim_str.format; | ||
| 464 | return rep->val.verbatim_str.str; | ||
| 465 | } | ||
| 466 | |||
| 467 | /* Return the current reply blob. | ||
| 468 | * | ||
| 469 | * The return value is borrowed from CallReply, so it must not be freed | ||
| 470 | * explicitly or used after CallReply itself is freed. | ||
| 471 | */ | ||
| 472 | const char *callReplyGetProto(CallReply *rep, size_t *proto_len) { | ||
| 473 | *proto_len = rep->proto_len; | ||
| 474 | return rep->proto; | ||
| 475 | } | ||
| 476 | |||
| 477 | /* Return CallReply private data, as set by the caller on callReplyCreate(). | ||
| 478 | */ | ||
| 479 | void *callReplyGetPrivateData(CallReply *rep) { | ||
| 480 | return rep->private_data; | ||
| 481 | } | ||
| 482 | |||
| 483 | /* Return true if the reply or one of it sub-replies is RESP3 formatted. */ | ||
| 484 | int callReplyIsResp3(CallReply *rep) { | ||
| 485 | return rep->flags & REPLY_FLAG_RESP3; | ||
| 486 | } | ||
| 487 | |||
| 488 | /* Returns a list of errors in sds form, or NULL. */ | ||
| 489 | list *callReplyDeferredErrorList(CallReply *rep) { | ||
| 490 | return rep->deferred_error_list; | ||
| 491 | } | ||
| 492 | |||
| 493 | /* Create a new CallReply struct from the reply blob. | ||
| 494 | * | ||
| 495 | * The function will own the reply blob, so it must not be used or freed by | ||
| 496 | * the caller after passing it to this function. | ||
| 497 | * | ||
| 498 | * The reply blob will be freed when the returned CallReply struct is later | ||
| 499 | * freed using freeCallReply(). | ||
| 500 | * | ||
| 501 | * The deferred_error_list is an optional list of errors that are present | ||
| 502 | * in the reply blob, if given, this function will take ownership on it. | ||
| 503 | * | ||
| 504 | * The private_data is optional and can later be accessed using | ||
| 505 | * callReplyGetPrivateData(). | ||
| 506 | * | ||
| 507 | * NOTE: The parser used for parsing the reply and producing CallReply is | ||
| 508 | * designed to handle valid replies created by Redis itself. IT IS NOT | ||
| 509 | * DESIGNED TO HANDLE USER INPUT and using it to parse invalid replies is | ||
| 510 | * unsafe. | ||
| 511 | */ | ||
| 512 | CallReply *callReplyCreate(sds reply, list *deferred_error_list, void *private_data) { | ||
| 513 | CallReply *res = zmalloc(sizeof(*res)); | ||
| 514 | res->flags = REPLY_FLAG_ROOT; | ||
| 515 | res->original_proto = reply; | ||
| 516 | res->proto = reply; | ||
| 517 | res->proto_len = sdslen(reply); | ||
| 518 | res->private_data = private_data; | ||
| 519 | res->attribute = NULL; | ||
| 520 | res->deferred_error_list = deferred_error_list; | ||
| 521 | return res; | ||
| 522 | } | ||
| 523 | |||
| 524 | /* Create a new CallReply struct from the reply blob representing an error message. | ||
| 525 | * Automatically creating deferred_error_list and set a copy of the reply in it. | ||
| 526 | * Refer to callReplyCreate for detailed explanation. | ||
| 527 | * Reply string can come in one of two forms: | ||
| 528 | * 1. A protocol reply starting with "-CODE" and ending with "\r\n" | ||
| 529 | * 2. A plain string, in which case this function adds the protocol header and footer. */ | ||
| 530 | CallReply *callReplyCreateError(sds reply, void *private_data) { | ||
| 531 | sds err_buff = reply; | ||
| 532 | if (err_buff[0] != '-') { | ||
| 533 | err_buff = sdscatfmt(sdsempty(), "-ERR %S\r\n", reply); | ||
| 534 | sdsfree(reply); | ||
| 535 | } | ||
| 536 | list *deferred_error_list = listCreate(); | ||
| 537 | listSetFreeMethod(deferred_error_list, sdsfreegeneric); | ||
| 538 | listAddNodeTail(deferred_error_list, sdsnew(err_buff)); | ||
| 539 | return callReplyCreate(err_buff, deferred_error_list, private_data); | ||
| 540 | } | ||
