1 #include <pthread.h>
2
3 #include "value.h"
4 #include "util/mempool.h"
5 #include "module.h"
6 #include "query_error.h"
7 #include "rmutil/rm_assert.h"
8
9 ///////////////////////////////////////////////////////////////
10 // Variant Values - will be used in documents as well
11 ///////////////////////////////////////////////////////////////
RSValue_NumToString(double dd,char * buf)12 static size_t RSValue_NumToString(double dd, char *buf) {
13 long long ll = dd;
14 if (ll == dd) {
15 return sprintf(buf, "%lld", ll);
16 } else {
17 return sprintf(buf, "%.12g", dd);
18 }
19 }
20
21 typedef struct {
22 mempool_t *values;
23 mempool_t *fieldmaps;
24 } mempoolThreadPool;
25
mempoolThreadPoolDtor(void * p)26 static void mempoolThreadPoolDtor(void *p) {
27 mempoolThreadPool *tp = p;
28 if (tp->values) {
29 mempool_destroy(tp->values);
30 }
31 if (tp->fieldmaps) {
32 mempool_destroy(tp->fieldmaps);
33 }
34 rm_free(tp);
35 }
36
37 pthread_key_t mempoolKey_g;
38
_valueAlloc()39 static void *_valueAlloc() {
40 return rm_malloc(sizeof(RSValue));
41 }
42
_valueFree(void * p)43 static void _valueFree(void *p) {
44 rm_free(p);
45 }
46
initKey()47 static void __attribute__((constructor)) initKey() {
48 pthread_key_create(&mempoolKey_g, mempoolThreadPoolDtor);
49 }
50
getPoolInfo()51 static inline mempoolThreadPool *getPoolInfo() {
52 mempoolThreadPool *tp = pthread_getspecific(mempoolKey_g);
53 if (tp == NULL) {
54 tp = rm_calloc(1, sizeof(*tp));
55 mempool_options opts = {
56 .isGlobal = 0, .initialCap = 0, .maxCap = 1000, .alloc = _valueAlloc, .free = _valueFree};
57 tp->values = mempool_new(&opts);
58 pthread_setspecific(mempoolKey_g, tp);
59 }
60 return tp;
61 }
62
RS_NewValue(RSValueType t)63 RSValue *RS_NewValue(RSValueType t) {
64 RSValue *v = mempool_get(getPoolInfo()->values);
65 v->t = t;
66 v->refcount = 1;
67 v->allocated = 1;
68 return v;
69 }
70
RSValue_Clear(RSValue * v)71 void RSValue_Clear(RSValue *v) {
72 switch (v->t) {
73 case RSValue_String:
74 // free strings by allocation strategy
75 switch (v->strval.stype) {
76 case RSString_Malloc:
77 rm_free(v->strval.str);
78 break;
79 case RSString_RMAlloc:
80 rm_free(v->strval.str);
81 break;
82 case RSString_SDS:
83 sdsfree(v->strval.str);
84 break;
85 case RSString_Const:
86 case RSString_Volatile:
87 break;
88 }
89 break;
90 case RSValue_Array:
91 for (uint32_t i = 0; i < v->arrval.len; i++) {
92 RSValue_Decref(v->arrval.vals[i]);
93 }
94 if (!v->arrval.staticarray) {
95 rm_free(v->arrval.vals);
96 }
97 break;
98 case RSValue_Reference:
99 RSValue_Decref(v->ref);
100 break;
101 case RSValue_OwnRstring:
102 RedisModule_FreeString(RSDummyContext, v->rstrval);
103 break;
104 case RSValue_Null:
105 return; // prevent changing global RS_NULL to RSValue_Undef
106 default: // no free
107 break;
108 }
109
110 v->ref = NULL;
111 v->t = RSValue_Undef;
112 }
113
114 /* Free a value's internal value. It only does anything in the case of a string, and doesn't free
115 * the actual value object */
RSValue_Free(RSValue * v)116 void RSValue_Free(RSValue *v) {
117 RSValue_Clear(v);
118 if (v->allocated) {
119 mempool_release(getPoolInfo()->values, v);
120 }
121 }
122
RS_Value(RSValueType t)123 RSValue RS_Value(RSValueType t) {
124 RSValue v = (RSValue){
125 .t = t,
126 .refcount = 1,
127 .allocated = 0,
128 };
129 return v;
130 }
131
RSValue_SetNumber(RSValue * v,double n)132 inline void RSValue_SetNumber(RSValue *v, double n) {
133 v->t = RSValue_Number;
134 v->numval = n;
135 }
136
RSValue_SetString(RSValue * v,char * str,size_t len)137 inline void RSValue_SetString(RSValue *v, char *str, size_t len) {
138 v->t = RSValue_String;
139 v->strval.len = len;
140 v->strval.str = str;
141 v->strval.stype = RSString_Malloc;
142 }
143
RS_NewCopiedString(const char * s,size_t n)144 RSValue *RS_NewCopiedString(const char *s, size_t n) {
145 RSValue *v = RS_NewValue(RSValue_String);
146 char *cp = rm_malloc(n + 1);
147 cp[n] = 0;
148 memcpy(cp, s, n);
149 RSValue_SetString(v, cp, n);
150 return v;
151 }
152
RSValue_SetSDS(RSValue * v,sds s)153 inline void RSValue_SetSDS(RSValue *v, sds s) {
154 v->t = RSValue_String;
155 v->strval.len = sdslen(s);
156 v->strval.str = s;
157 v->strval.stype = RSString_SDS;
158 }
RSValue_SetConstString(RSValue * v,const char * str,size_t len)159 inline void RSValue_SetConstString(RSValue *v, const char *str, size_t len) {
160 v->t = RSValue_String;
161 v->strval.len = len;
162 v->strval.str = (char *)str;
163 v->strval.stype = RSString_Const;
164 }
165
166 /* Wrap a string with length into a value object. Doesn't duplicate the string. Use strdup if
167 * the value needs to be detached */
RS_StringVal(char * str,uint32_t len)168 inline RSValue *RS_StringVal(char *str, uint32_t len) {
169 RS_LOG_ASSERT(len <= (UINT32_MAX >> 4), "string length exceeds limit");
170 RSValue *v = RS_NewValue(RSValue_String);
171 v->strval.str = str;
172 v->strval.len = len;
173 v->strval.stype = RSString_Malloc;
174 return v;
175 }
176
177 /* Same as RS_StringVal but with explicit string type */
RS_StringValT(char * str,uint32_t len,RSStringType t)178 inline RSValue *RS_StringValT(char *str, uint32_t len, RSStringType t) {
179 RSValue *v = RS_NewValue(RSValue_String);
180 v->strval.str = str;
181 v->strval.len = len;
182 v->strval.stype = t;
183 return v;
184 }
185
RS_StringValFmt(const char * fmt,...)186 RSValue *RS_StringValFmt(const char *fmt, ...) {
187 char *buf;
188 va_list ap;
189 va_start(ap, fmt);
190 rm_vasprintf(&buf, fmt, ap);
191 va_end(ap);
192 return RS_StringVal(buf, strlen(buf));
193 }
194
195 /* Wrap a redis string value */
RS_RedisStringVal(RedisModuleString * str)196 RSValue *RS_RedisStringVal(RedisModuleString *str) {
197 RSValue *v = RS_NewValue(RSValue_RedisString);
198 v->rstrval = str;
199 return v;
200 }
201
RS_OwnRedisStringVal(RedisModuleString * str)202 RSValue *RS_OwnRedisStringVal(RedisModuleString *str) {
203 RSValue *r = RS_RedisStringVal(str);
204 RSValue_MakeRStringOwner(r);
205 return r;
206 }
207
RS_StealRedisStringVal(RedisModuleString * str)208 RSValue *RS_StealRedisStringVal(RedisModuleString *str) {
209 RSValue *ret = RS_RedisStringVal(str);
210 ret->rstrval = str;
211 ret->t = RSValue_OwnRstring;
212 return ret;
213 }
214
RSValue_MakeRStringOwner(RSValue * v)215 void RSValue_MakeRStringOwner(RSValue *v) {
216 RS_LOG_ASSERT(v->t == RSValue_RedisString, "RSvalue type should be string");
217 v->t = RSValue_OwnRstring;
218 RedisModule_RetainString(RSDummyContext, v->rstrval);
219 }
220
221 /* Convert a value to a string value. If the value is already a string value it gets
222 * shallow-copied (no string buffer gets copied) */
RSValue_ToString(RSValue * dst,RSValue * v)223 void RSValue_ToString(RSValue *dst, RSValue *v) {
224 switch (v->t) {
225 case RSValue_String:
226 RSValue_MakeReference(dst, v);
227 break;
228 case RSValue_RedisString:
229 case RSValue_OwnRstring: {
230 size_t sz;
231 const char *str = RedisModule_StringPtrLen(v->rstrval, &sz);
232 RSValue_SetConstString(dst, str, sz);
233 break;
234 }
235 case RSValue_Number: {
236 char tmpbuf[128] = {0};
237 RSValue_NumToString(v->numval, tmpbuf);
238 char *buf = rm_strdup(tmpbuf);
239 RSValue_SetString(dst, buf, strlen(buf));
240 break;
241 }
242 case RSValue_Reference:
243 return RSValue_ToString(dst, v->ref);
244
245 case RSValue_Null:
246 default:
247 return RSValue_SetConstString(dst, "", 0);
248 }
249 }
250
RSValue_ParseNumber(const char * p,size_t l)251 RSValue *RSValue_ParseNumber(const char *p, size_t l) {
252
253 char *e;
254 errno = 0;
255 double d = strtod(p, &e);
256 if ((errno == ERANGE && (d == HUGE_VAL || d == -HUGE_VAL)) || (errno != 0 && d == 0) ||
257 *e != '\0') {
258 return NULL;
259 }
260 return RS_NumVal(d);
261 }
262
263 /* Convert a value to a number, either returning the actual numeric values or by parsing a string
264 into a number. Return 1 if the value is a number or a numeric string and can be converted, or 0 if
265 not. If possible, we put the actual value into teh double pointer */
RSValue_ToNumber(const RSValue * v,double * d)266 int RSValue_ToNumber(const RSValue *v, double *d) {
267 if (RSValue_IsNull(v)) return 0;
268 v = RSValue_Dereference(v);
269
270 const char *p = NULL;
271 size_t l = 0;
272 switch (v->t) {
273 // for numerics - just set the value and return
274 case RSValue_Number:
275 *d = v->numval;
276 return 1;
277
278 case RSValue_String:
279 // C strings - take the ptr and len
280 p = v->strval.str;
281 l = v->strval.len;
282 break;
283 case RSValue_RedisString:
284 case RSValue_OwnRstring:
285 // Redis strings - take the number and len
286 p = RedisModule_StringPtrLen(v->rstrval, &l);
287 break;
288
289 case RSValue_Null:
290 case RSValue_Array:
291 case RSValue_Undef:
292 default:
293 return 0;
294 }
295 // If we have a string - try to parse it
296 if (p) {
297 char *e;
298 errno = 0;
299 *d = strtod(p, &e);
300 if ((errno == ERANGE && (*d == HUGE_VAL || *d == -HUGE_VAL)) || (errno != 0 && *d == 0) ||
301 *e != '\0') {
302 return 0;
303 }
304
305 return 1;
306 }
307
308 return 0;
309 }
310
311 /**
312 * Returns the value as a simple opaque buffer
313 inline const void *RSValue_ToBuffer(RSValue *value, size_t *outlen) {
314 value = RSValue_Dereference(value);
315
316 switch (value->t) {
317 case RSValue_Number:
318 *outlen = sizeof(value->numval);
319 return &value->numval;
320 case RSValue_String:
321 *outlen = value->strval.len;
322 return value->strval.str;
323 case RSValue_RedisString:
324 case RSValue_OwnRstring:
325 return RedisModule_StringPtrLen(value->rstrval, outlen);
326 case RSValue_Array:
327 case RSValue_Null:
328 default:
329 *outlen = 0;
330 return "";
331 }
332 }
333 */
334
335 // Gets the string pointer and length from the value
RSValue_StringPtrLen(const RSValue * value,size_t * lenp)336 const char *RSValue_StringPtrLen(const RSValue *value, size_t *lenp) {
337 value = RSValue_Dereference(value);
338
339 switch (value->t) {
340 case RSValue_String:
341 if (lenp) {
342 *lenp = value->strval.len;
343 }
344 return value->strval.str;
345 case RSValue_RedisString:
346 case RSValue_OwnRstring:
347 return RedisModule_StringPtrLen(value->rstrval, lenp);
348 default:
349 return NULL;
350 }
351 }
352
353 // Combines PtrLen with ToString to convert any RSValue into a string buffer.
354 // Returns NULL if buf is required, but is too small
RSValue_ConvertStringPtrLen(const RSValue * value,size_t * lenp,char * buf,size_t buflen)355 const char *RSValue_ConvertStringPtrLen(const RSValue *value, size_t *lenp, char *buf,
356 size_t buflen) {
357 value = RSValue_Dereference(value);
358
359 if (RSValue_IsString(value)) {
360 return RSValue_StringPtrLen(value, lenp);
361 } else if (value->t == RSValue_Number) {
362 size_t n = snprintf(buf, buflen, "%f", value->numval);
363 if (n >= buflen) {
364 *lenp = 0;
365 return "";
366 }
367 *lenp = n;
368 return buf;
369 } else {
370 // Array, Null, other types
371 *lenp = 0;
372 return "";
373 }
374 }
375
376 /* Wrap a number into a value object */
RS_NumVal(double n)377 RSValue *RS_NumVal(double n) {
378 RSValue *v = RS_NewValue(RSValue_Number);
379 v->numval = n;
380 return v;
381 }
382
RS_Int64Val(int64_t dd)383 RSValue *RS_Int64Val(int64_t dd) {
384 RSValue *v = RS_NewValue(RSValue_Number);
385 v->numval = dd;
386 return v;
387 }
388
RSValue_NewArrayEx(RSValue ** vals,size_t n,int options)389 RSValue *RSValue_NewArrayEx(RSValue **vals, size_t n, int options) {
390 RSValue *arr = RS_NewValue(RSValue_Array);
391 RSValue **list;
392 if (options & RSVAL_ARRAY_ALLOC) {
393 list = vals;
394 } else {
395 list = rm_malloc(sizeof(*list) * n);
396 }
397
398 arr->arrval.vals = list;
399
400 if (options & RSVAL_ARRAY_STATIC) {
401 arr->arrval.staticarray = 1;
402 } else {
403 arr->arrval.staticarray = 0;
404 }
405
406 if (!vals) {
407 arr->arrval.len = 0;
408 } else {
409 arr->arrval.len = n;
410 for (size_t ii = 0; ii < n; ++ii) {
411 RSValue *v = vals[ii];
412 list[ii] = v;
413 if (!v) {
414 continue;
415 }
416 if (!(options & RSVAL_ARRAY_NOINCREF)) {
417 RSValue_IncrRef(v);
418 }
419 }
420 }
421
422 return arr;
423 }
424
RS_VStringArray(uint32_t sz,...)425 RSValue *RS_VStringArray(uint32_t sz, ...) {
426 RSValue **arr = rm_calloc(sz, sizeof(*arr));
427 va_list ap;
428 va_start(ap, sz);
429 for (uint32_t i = 0; i < sz; i++) {
430 char *p = va_arg(ap, char *);
431 arr[i] = RS_StringValC(p);
432 }
433 va_end(ap);
434 return RSValue_NewArrayEx(arr, sz, RSVAL_ARRAY_NOINCREF | RSVAL_ARRAY_ALLOC);
435 }
436
437 /* Wrap an array of NULL terminated C strings into an RSValue array */
RS_StringArray(char ** strs,uint32_t sz)438 RSValue *RS_StringArray(char **strs, uint32_t sz) {
439 RSValue **arr = rm_calloc(sz, sizeof(RSValue *));
440
441 for (uint32_t i = 0; i < sz; i++) {
442 arr[i] = RS_StringValC(strs[i]);
443 }
444 return RSValue_NewArrayEx(arr, sz, RSVAL_ARRAY_NOINCREF | RSVAL_ARRAY_ALLOC);
445 }
446
RS_StringArrayT(char ** strs,uint32_t sz,RSStringType st)447 RSValue *RS_StringArrayT(char **strs, uint32_t sz, RSStringType st) {
448 RSValue **arr = rm_calloc(sz, sizeof(RSValue *));
449
450 for (uint32_t i = 0; i < sz; i++) {
451 arr[i] = RS_StringValT(strs[i], strlen(strs[i]), st);
452 }
453 return RSValue_NewArrayEx(arr, sz, RSVAL_ARRAY_NOINCREF | RSVAL_ARRAY_ALLOC);
454 }
455
456 RSValue RS_NULL = {.t = RSValue_Null, .refcount = 1, .allocated = 0};
457 /* Create a new NULL RSValue */
RS_NullVal()458 inline RSValue *RS_NullVal() {
459 return &RS_NULL;
460 }
461
cmp_strings(const char * s1,const char * s2,size_t l1,size_t l2)462 static inline int cmp_strings(const char *s1, const char *s2, size_t l1, size_t l2) {
463 int cmp = strncmp(s1, s2, MIN(l1, l2));
464 if (l1 == l2) {
465 // if the strings are the same length, just return the result of strcmp
466 return cmp;
467 } else { // if the lengths arent identical
468 // if the strings are identical but the lengths aren't, return the longer string
469 if (cmp == 0) {
470 return l1 > l2 ? 1 : -1;
471 } else { // the strings are lexically different, just return that
472 return cmp;
473 }
474 }
475 }
476
cmp_numbers(const RSValue * v1,const RSValue * v2)477 static inline int cmp_numbers(const RSValue *v1, const RSValue *v2) {
478 return v1->numval > v2->numval ? 1 : (v1->numval < v2->numval ? -1 : 0);
479 }
480
convert_to_number(const RSValue * v,RSValue * vn,QueryError * qerr)481 static inline int convert_to_number(const RSValue *v, RSValue *vn, QueryError *qerr) {
482 double d;
483 if (!RSValue_ToNumber(v, &d)) {
484 if (!qerr) return 0;
485
486 const char *s = RSValue_StringPtrLen(v, NULL);
487 QueryError_SetErrorFmt(qerr, QUERY_ENOTNUMERIC, "Error converting string '%s' to number", s);
488 return 0;
489 }
490
491 RSValue_SetNumber(vn, d);
492 return 1;
493 }
494
RSValue_CmpNC(const RSValue * v1,const RSValue * v2)495 static int RSValue_CmpNC(const RSValue *v1, const RSValue *v2) {
496 switch (v1->t) {
497 case RSValue_Number:
498 return cmp_numbers(v1, v2);
499 case RSValue_String:
500 return cmp_strings(v1->strval.str, v2->strval.str, v1->strval.len, v2->strval.len);
501 case RSValue_RedisString:
502 case RSValue_OwnRstring: {
503 size_t l1, l2;
504 const char *s1 = RedisModule_StringPtrLen(v1->rstrval, &l1);
505 const char *s2 = RedisModule_StringPtrLen(v2->rstrval, &l2);
506 return cmp_strings(s1, s2, l1, l2);
507 }
508 case RSValue_Null:
509 return 0;
510 case RSValue_Array: // can't compare arrays ATM
511 default:
512 return 0;
513 }
514 }
515
RSValue_Cmp(const RSValue * v1,const RSValue * v2,QueryError * qerr)516 int RSValue_Cmp(const RSValue *v1, const RSValue *v2, QueryError *qerr) {
517 RS_LOG_ASSERT(v1 && v2, "missing RSvalue");
518 v1 = RSValue_Dereference(v1);
519 v2 = RSValue_Dereference(v2);
520
521 if (v1->t == v2->t) {
522 return RSValue_CmpNC(v1, v2);
523 }
524
525 // if one of the values is null, the other wins
526 if (v1->t == RSValue_Null) {
527 return -1;
528 } else if (v2->t == RSValue_Null) {
529 return 1;
530 }
531
532 // if either of the arguments is a number, convert the other one to a number
533 // if, however, error handling is not available, fallback to string comparison
534 do {
535 if (v1->t == RSValue_Number) {
536 RSValue v2n;
537 if (!convert_to_number(v2, &v2n, qerr)) {
538 // if it is possible to indicate an error, return
539 if (qerr) return 0;
540 // otherwise, fallback to string comparison
541 break;
542 }
543 return cmp_numbers(v1, &v2n);
544 } else if (v2->t == RSValue_Number) {
545 RSValue v1n;
546 if (!convert_to_number(v1, &v1n, qerr)) {
547 // if it is possible to indicate an error, return
548 if (qerr) return 0;
549 // otherwise, fallback to string comparison
550 break;
551 }
552 // otherwise, fallback to string comparison
553 return cmp_numbers(&v1n, v2);
554 }
555 } while (0);
556
557 // cast to strings and compare as strings
558 char buf1[100], buf2[100];
559
560 size_t l1, l2;
561 const char *s1 = RSValue_ConvertStringPtrLen(v1, &l1, buf1, sizeof(buf1));
562 const char *s2 = RSValue_ConvertStringPtrLen(v2, &l2, buf2, sizeof(buf2));
563 return cmp_strings(s1, s2, l1, l2);
564 }
565
RSValue_Equal(const RSValue * v1,const RSValue * v2,QueryError * qerr)566 int RSValue_Equal(const RSValue *v1, const RSValue *v2, QueryError *qerr) {
567 RS_LOG_ASSERT(v1 && v2, "missing RSvalue");
568 v1 = RSValue_Dereference(v1);
569 v2 = RSValue_Dereference(v2);
570
571 if (v1->t == v2->t) {
572 return RSValue_CmpNC(v1, v2) == 0;
573 }
574
575 if (v1->t == RSValue_Null || v2->t == RSValue_Null) {
576 return 0;
577 }
578
579 // if either of the arguments is a number, convert the other one to a number
580 RSValue vn;
581 if (v1->t == RSValue_Number) {
582 if (!convert_to_number(v2, &vn, NULL)) return 0;
583 return cmp_numbers(v1, &vn) == 0;
584 } else if (v2->t == RSValue_Number) {
585 if (!convert_to_number(v1, &vn, NULL)) return 0;
586 return cmp_numbers(&vn, v2) == 0;
587 }
588
589 // cast to strings and compare as strings
590 char buf1[100], buf2[100];
591
592 size_t l1, l2;
593 const char *s1 = RSValue_ConvertStringPtrLen(v1, &l1, buf1, sizeof(buf1));
594 const char *s2 = RSValue_ConvertStringPtrLen(v2, &l2, buf2, sizeof(buf2));
595 return cmp_strings(s1, s2, l1, l2) == 0;
596 }
597
598 /* Based on the value type, serialize the value into redis client response */
RSValue_SendReply(RedisModuleCtx * ctx,const RSValue * v,int isTyped)599 int RSValue_SendReply(RedisModuleCtx *ctx, const RSValue *v, int isTyped) {
600 v = RSValue_Dereference(v);
601
602 switch (v->t) {
603 case RSValue_String:
604 return RedisModule_ReplyWithStringBuffer(ctx, v->strval.str, v->strval.len);
605 case RSValue_RedisString:
606 case RSValue_OwnRstring:
607 return RedisModule_ReplyWithString(ctx, v->rstrval);
608 case RSValue_Number: {
609 char buf[128] = {0};
610 RSValue_NumToString(v->numval, buf);
611
612 if (isTyped) {
613 return RedisModule_ReplyWithError(ctx, buf);
614 } else {
615 return RedisModule_ReplyWithStringBuffer(ctx, buf, strlen(buf));
616 }
617 }
618 case RSValue_Null:
619 return RedisModule_ReplyWithNull(ctx);
620 case RSValue_Array:
621 RedisModule_ReplyWithArray(ctx, v->arrval.len);
622 for (uint32_t i = 0; i < v->arrval.len; i++) {
623 RSValue_SendReply(ctx, v->arrval.vals[i], isTyped);
624 }
625 return REDISMODULE_OK;
626 default:
627 RedisModule_ReplyWithNull(ctx);
628 }
629 return REDISMODULE_OK;
630 }
631
RSValue_Print(const RSValue * v)632 void RSValue_Print(const RSValue *v) {
633 FILE *fp = stderr;
634 if (!v) {
635 fprintf(fp, "nil");
636 }
637 switch (v->t) {
638 case RSValue_String:
639 fprintf(fp, "\"%.*s\"", v->strval.len, v->strval.str);
640 break;
641 case RSValue_RedisString:
642 case RSValue_OwnRstring:
643 fprintf(fp, "\"%s\"", RedisModule_StringPtrLen(v->rstrval, NULL));
644 break;
645 case RSValue_Number: {
646 char tmp[128] = {0};
647 RSValue_NumToString(v->numval, tmp);
648 fprintf(fp, "%s", tmp);
649 break;
650 }
651 case RSValue_Null:
652 fprintf(fp, "NULL");
653 break;
654 case RSValue_Undef:
655 fprintf(fp, "<Undefined>");
656 case RSValue_Array:
657 fprintf(fp, "[");
658 for (uint32_t i = 0; i < v->arrval.len; i++) {
659 RSValue_Print(v->arrval.vals[i]);
660 printf(", ");
661 }
662 fprintf(fp, "]");
663 break;
664 case RSValue_Reference:
665 RSValue_Print(v->ref);
666 break;
667 }
668 }
669
670 /*
671 * - s: will be parsed as a string
672 * - l: Will be parsed as a long integer
673 * - d: Will be parsed as a double
674 * - !: will be skipped
675 * - ?: means evrything after is optional
676 */
677
RSValue_ArrayAssign(RSValue ** args,int argc,const char * fmt,...)678 int RSValue_ArrayAssign(RSValue **args, int argc, const char *fmt, ...) {
679
680 va_list ap;
681 va_start(ap, fmt);
682 const char *p = fmt;
683 size_t i = 0;
684 int optional = 0;
685 while (i < argc && *p) {
686 switch (*p) {
687 case 's': {
688 char **ptr = va_arg(ap, char **);
689 if (!RSValue_IsString(args[i])) {
690 goto err;
691 }
692 *ptr = (char *)RSValue_StringPtrLen(args[i], NULL);
693 break;
694 }
695 case 'l': {
696 long long *lp = va_arg(ap, long long *);
697 double d;
698 if (!RSValue_ToNumber(args[i], &d)) {
699 goto err;
700 }
701 *lp = (long long)d;
702 break;
703 }
704 case 'd': {
705 double *dp = va_arg(ap, double *);
706 if (!RSValue_ToNumber(args[i], dp)) {
707 goto err;
708 }
709 break;
710 }
711 case '!':
712 // do nothing...
713 break;
714 case '?':
715 optional = 1;
716 // reduce i because it will be incremented soon
717 i -= 1;
718 break;
719 default:
720 goto err;
721 }
722 ++i;
723 ++p;
724 }
725 // if we have stuff left to read in the format but we haven't gotten to the optional part -fail
726 if (*p && !optional && i < argc) {
727 goto err;
728 }
729 // if we don't have anything left to read from the format but we haven't gotten to the array's
730 // end, fail
731 if (*p == 0 && i < argc) {
732 goto err;
733 }
734
735 va_end(ap);
736 return 1;
737 err:
738 va_end(ap);
739 return 0;
740 }
741
RSValue_TypeName(RSValueType t)742 const char *RSValue_TypeName(RSValueType t) {
743 switch (t) {
744 case RSValue_Array:
745 return "array";
746 case RSValue_Number:
747 return "number";
748 case RSValue_String:
749 return "string";
750 case RSValue_Null:
751 return "(null)";
752 case RSValue_OwnRstring:
753 case RSValue_RedisString:
754 return "redis-string";
755 case RSValue_Reference:
756 return "reference";
757 default:
758 return "!!UNKNOWN TYPE!!";
759 }
760 }
761