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