1 /** @file
2     A general structure for extracting hierarchical data from the devices;
3     typically key-value pairs, but allows for more rich data as well.
4 
5     Copyright (C) 2015 by Erkki Seppälä <flux@modeemi.fi>
6 
7     This program is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11 */
12 
13 #include "data.h"
14 
15 #include "abuf.h"
16 #include "fatal.h"
17 
18 #include <stdarg.h>
19 #include <assert.h>
20 #include <string.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stdbool.h>
24 
25 // Macro to prevent unused variables (passed into a function)
26 // from generating a warning.
27 #define UNUSED(x) (void)(x)
28 
29 typedef void* (*array_elementwise_import_fn)(void*);
30 typedef void (*array_element_release_fn)(void*);
31 typedef void (*value_release_fn)(void*);
32 
33 typedef struct {
34     /* what is the element size when put inside an array? */
35     int array_element_size;
36 
37     /* is the element boxed (ie. behind a pointer) when inside an array?
38        if it's not boxed ("unboxed"), json dumping function needs to make
39        a copy of the value beforehand, because the dumping function only
40        deals with boxed values.
41      */
42     bool array_is_boxed;
43 
44     /* function for importing arrays. strings are specially handled (as they
45        are copied deeply), whereas other arrays are just copied shallowly
46        (but copied nevertheless) */
47     array_elementwise_import_fn array_elementwise_import;
48 
49     /* a function for releasing an element when put in an array; integers
50      * don't need to be released, while ie. strings and arrays do. */
51     array_element_release_fn array_element_release;
52 
53     /* a function for releasing a value. everything needs to be released. */
54     value_release_fn value_release;
55 } data_meta_type_t;
56 
57 static data_meta_type_t dmt[DATA_COUNT] = {
58     //  DATA_DATA
59     { .array_element_size       = sizeof(data_t*),
60       .array_is_boxed           = true,
61       .array_elementwise_import = NULL,
62       .array_element_release    = (array_element_release_fn) data_free,
63       .value_release            = (value_release_fn) data_free },
64 
65     //  DATA_INT
66     { .array_element_size       = sizeof(int),
67       .array_is_boxed           = false,
68       .array_elementwise_import = NULL,
69       .array_element_release    = NULL,
70       .value_release            = NULL },
71 
72     //  DATA_DOUBLE
73     { .array_element_size       = sizeof(double),
74       .array_is_boxed           = false,
75       .array_elementwise_import = NULL,
76       .array_element_release    = NULL,
77       .value_release            = NULL },
78 
79     //  DATA_STRING
80     { .array_element_size       = sizeof(char*),
81       .array_is_boxed           = true,
82       .array_elementwise_import = (array_elementwise_import_fn) strdup,
83       .array_element_release    = (array_element_release_fn) free,
84       .value_release            = (value_release_fn) free },
85 
86     //  DATA_ARRAY
87     { .array_element_size       = sizeof(data_array_t*),
88       .array_is_boxed           = true,
89       .array_elementwise_import = NULL,
90       .array_element_release    = (array_element_release_fn) data_array_free ,
91       .value_release            = (value_release_fn) data_array_free },
92 };
93 
import_values(void * dst,void * src,int num_values,data_type_t type)94 static bool import_values(void *dst, void *src, int num_values, data_type_t type)
95 {
96     int element_size = dmt[type].array_element_size;
97     array_elementwise_import_fn import = dmt[type].array_elementwise_import;
98     if (import) {
99         for (int i = 0; i < num_values; ++i) {
100             void *copy = import(*(void **)((char *)src + element_size * i));
101             if (!copy) {
102                 --i;
103                 while (i >= 0) {
104                     free(*(void **)((char *)dst + element_size * i));
105                     --i;
106                 }
107                 return false;
108             } else {
109                 *((char **)dst + i) = copy;
110             }
111         }
112     } else {
113         memcpy(dst, src, (size_t)element_size * num_values);
114     }
115     return true; // error is returned early
116 }
117 
118 /* data */
119 
data_array(int num_values,data_type_t type,void * values)120 data_array_t *data_array(int num_values, data_type_t type, void *values)
121 {
122     if (num_values < 0) {
123       return NULL;
124     }
125     data_array_t *array = calloc(1, sizeof(data_array_t));
126     if (!array) {
127         WARN_CALLOC("data_array()");
128         return NULL; // NOTE: returns NULL on alloc failure.
129     }
130 
131     int element_size = dmt[type].array_element_size;
132     if (num_values > 0) { // don't alloc empty arrays
133         array->values = calloc(num_values, element_size);
134         if (!array->values) {
135             WARN_CALLOC("data_array()");
136             goto alloc_error;
137         }
138         if (!import_values(array->values, values, num_values, type))
139             goto alloc_error;
140     }
141 
142     array->num_values = num_values;
143     array->type       = type;
144 
145     return array;
146 
147 alloc_error:
148     if (array)
149         free(array->values);
150     free(array);
151     return NULL;
152 }
153 
vdata_make(data_t * first,const char * key,const char * pretty_key,va_list ap)154 static data_t *vdata_make(data_t *first, const char *key, const char *pretty_key, va_list ap)
155 {
156     data_type_t type;
157     data_t *prev = first;
158     while (prev && prev->next)
159         prev = prev->next;
160     char *format = NULL;
161     int skip = 0; // skip the data item if this is set
162     type = va_arg(ap, data_type_t);
163     do {
164         data_t *current;
165         data_value_t value = {0};
166         // store explicit release function, CSA checker gets confused without this
167         value_release_fn value_release = NULL; // appease CSA checker
168 
169         switch (type) {
170         case DATA_COND:
171             skip |= !va_arg(ap, int);
172             type = va_arg(ap, data_type_t);
173             continue;
174         case DATA_FORMAT:
175             if (format) {
176                 fprintf(stderr, "vdata_make() format type used twice\n");
177                 goto alloc_error;
178             }
179             format = strdup(va_arg(ap, char *));
180             if (!format) {
181                 WARN_STRDUP("vdata_make()");
182                 goto alloc_error;
183             }
184             type = va_arg(ap, data_type_t);
185             continue;
186         case DATA_COUNT:
187             assert(0);
188             break;
189         case DATA_DATA:
190             value_release = (value_release_fn)data_free; // appease CSA checker
191             value.v_ptr = va_arg(ap, data_t *);
192             break;
193         case DATA_INT:
194             value.v_int = va_arg(ap, int);
195             break;
196         case DATA_DOUBLE:
197             value.v_dbl = va_arg(ap, double);
198             break;
199         case DATA_STRING:
200             value_release = (value_release_fn)free; // appease CSA checker
201             value.v_ptr = strdup(va_arg(ap, char *));
202             if (!value.v_ptr)
203                 WARN_STRDUP("vdata_make()");
204             break;
205         case DATA_ARRAY:
206             value_release = (value_release_fn)data_array_free; // appease CSA checker
207             value.v_ptr = va_arg(ap, data_array_t *);
208             break;
209         default:
210             fprintf(stderr, "vdata_make() bad data type (%d)\n", type);
211             goto alloc_error;
212         }
213 
214         if (skip) {
215             if (value_release) // could use dmt[type].value_release
216                 value_release(value.v_ptr);
217             free(format);
218             format = NULL;
219             skip = 0;
220         }
221         else {
222             current = calloc(1, sizeof(*current));
223             if (!current) {
224                 WARN_CALLOC("vdata_make()");
225                 if (value_release) // could use dmt[type].value_release
226                     value_release(value.v_ptr);
227                 goto alloc_error;
228             }
229             current->type   = type;
230             current->format = format;
231             format          = NULL; // consumed
232             current->value  = value;
233             current->next   = NULL;
234 
235             if (prev)
236                 prev->next = current;
237             prev = current;
238             if (!first)
239                 first = current;
240 
241             current->key = strdup(key);
242             if (!current->key) {
243                 WARN_STRDUP("vdata_make()");
244                 goto alloc_error;
245             }
246             current->pretty_key = strdup(pretty_key ? pretty_key : key);
247             if (!current->pretty_key) {
248                 WARN_STRDUP("vdata_make()");
249                 goto alloc_error;
250             }
251         }
252 
253         // next args
254         key = va_arg(ap, const char *);
255         if (key) {
256             pretty_key = va_arg(ap, const char *);
257             type = va_arg(ap, data_type_t);
258         }
259     } while (key);
260     va_end(ap);
261     if (format) {
262         fprintf(stderr, "vdata_make() format type without data\n");
263         goto alloc_error;
264     }
265 
266     return first;
267 
268 alloc_error:
269     free(format); // if not consumed
270     data_free(first);
271     return NULL;
272 }
273 
data_make(const char * key,const char * pretty_key,...)274 data_t *data_make(const char *key, const char *pretty_key, ...)
275 {
276     va_list ap;
277     va_start(ap, pretty_key);
278     data_t *result = vdata_make(NULL, key, pretty_key, ap);
279     va_end(ap);
280     return result;
281 }
282 
data_append(data_t * first,const char * key,const char * pretty_key,...)283 data_t *data_append(data_t *first, const char *key, const char *pretty_key, ...)
284 {
285     va_list ap;
286     va_start(ap, pretty_key);
287     data_t *result = vdata_make(first, key, pretty_key, ap);
288     va_end(ap);
289     return result;
290 }
291 
data_prepend(data_t * first,const char * key,const char * pretty_key,...)292 data_t *data_prepend(data_t *first, const char *key, const char *pretty_key, ...)
293 {
294     va_list ap;
295     va_start(ap, pretty_key);
296     data_t *result = vdata_make(NULL, key, pretty_key, ap);
297     va_end(ap);
298 
299     if (!result)
300         return first;
301 
302     data_t *prev = result;
303     while (prev->next)
304         prev = prev->next;
305     prev->next = first;
306 
307     return result;
308 }
309 
data_array_free(data_array_t * array)310 void data_array_free(data_array_t *array)
311 {
312     array_element_release_fn release = dmt[array->type].array_element_release;
313     if (release) {
314         int element_size = dmt[array->type].array_element_size;
315         for (int i = 0; i < array->num_values; ++i)
316             release(*(void **)((char *)array->values + element_size * i));
317     }
318     free(array->values);
319     free(array);
320 }
321 
data_retain(data_t * data)322 data_t *data_retain(data_t *data)
323 {
324     if (data)
325         ++data->retain;
326     return data;
327 }
328 
data_free(data_t * data)329 void data_free(data_t *data)
330 {
331     if (data && data->retain) {
332         --data->retain;
333         return;
334     }
335     while (data) {
336         data_t *prev_data = data;
337         if (dmt[data->type].value_release)
338             dmt[data->type].value_release(data->value.v_ptr);
339         free(data->format);
340         free(data->pretty_key);
341         free(data->key);
342         data = data->next;
343         free(prev_data);
344     }
345 }
346 
347 /* data output */
348 
data_output_print(data_output_t * output,data_t * data)349 void data_output_print(data_output_t *output, data_t *data)
350 {
351     if (!output)
352         return;
353     output->print_data(output, data, NULL);
354 
355     if (output->output_flush)
356         output->output_flush(output);
357 }
358 
data_output_start(struct data_output * output,char const * const * fields,int num_fields)359 void data_output_start(struct data_output *output, char const *const *fields, int num_fields)
360 {
361     if (!output || !output->output_start)
362         return;
363     output->output_start(output, fields, num_fields);
364 }
365 
data_output_free(data_output_t * output)366 void data_output_free(data_output_t *output)
367 {
368     if (!output)
369         return;
370     output->output_free(output);
371 }
372 
373 /* output helpers */
374 
print_value(data_output_t * output,data_type_t type,data_value_t value,char const * format)375 void print_value(data_output_t *output, data_type_t type, data_value_t value, char const *format)
376 {
377     switch (type) {
378     case DATA_FORMAT:
379     case DATA_COUNT:
380     case DATA_COND:
381         assert(0);
382         break;
383     case DATA_DATA:
384         output->print_data(output, value.v_ptr, format);
385         break;
386     case DATA_INT:
387         output->print_int(output, value.v_int, format);
388         break;
389     case DATA_DOUBLE:
390         output->print_double(output, value.v_dbl, format);
391         break;
392     case DATA_STRING:
393         output->print_string(output, value.v_ptr, format);
394         break;
395     case DATA_ARRAY:
396         output->print_array(output, value.v_ptr, format);
397         break;
398     }
399 }
400 
print_array_value(data_output_t * output,data_array_t * array,char const * format,int idx)401 void print_array_value(data_output_t *output, data_array_t *array, char const *format, int idx)
402 {
403     int element_size = dmt[array->type].array_element_size;
404     data_value_t value = {0};
405 
406     if (!dmt[array->type].array_is_boxed) {
407         memcpy(&value, (char *)array->values + element_size * idx, element_size);
408         print_value(output, array->type, value, format);
409     } else {
410         print_value(output, array->type, *(data_value_t*)((char *)array->values + element_size * idx), format);
411     }
412 }
413 
414 /* JSON string printer */
415 
416 typedef struct {
417     struct data_output output;
418     abuf_t msg;
419 } data_print_jsons_t;
420 
format_jsons_array(data_output_t * output,data_array_t * array,char const * format)421 static void format_jsons_array(data_output_t *output, data_array_t *array, char const *format)
422 {
423     data_print_jsons_t *jsons = (data_print_jsons_t *)output;
424 
425     abuf_cat(&jsons->msg, "[");
426     for (int c = 0; c < array->num_values; ++c) {
427         if (c)
428             abuf_cat(&jsons->msg, ",");
429         print_array_value(output, array, format, c);
430     }
431     abuf_cat(&jsons->msg, "]");
432 }
433 
format_jsons_object(data_output_t * output,data_t * data,char const * format)434 static void format_jsons_object(data_output_t *output, data_t *data, char const *format)
435 {
436     UNUSED(format);
437     data_print_jsons_t *jsons = (data_print_jsons_t *)output;
438 
439     bool separator = false;
440     abuf_cat(&jsons->msg, "{");
441     while (data) {
442         if (separator)
443             abuf_cat(&jsons->msg, ",");
444         output->print_string(output, data->key, NULL);
445         abuf_cat(&jsons->msg, ":");
446         print_value(output, data->type, data->value, data->format);
447         separator = true;
448         data      = data->next;
449     }
450     abuf_cat(&jsons->msg, "}");
451 }
452 
format_jsons_string(data_output_t * output,const char * str,char const * format)453 static void format_jsons_string(data_output_t *output, const char *str, char const *format)
454 {
455     UNUSED(format);
456     data_print_jsons_t *jsons = (data_print_jsons_t *)output;
457 
458     char *buf   = jsons->msg.tail;
459     size_t size = jsons->msg.left;
460 
461     size_t str_len = strlen(str);
462     if (size < str_len + 3) {
463         return;
464     }
465 
466     if (str[0] == '{' && str[str_len - 1] == '}') {
467         // Print embedded JSON object verbatim
468         abuf_cat(&jsons->msg, str);
469         return;
470     }
471 
472     *buf++ = '"';
473     size--;
474     for (; *str && size >= 3; ++str) {
475         if (*str == '\r') {
476             *buf++ = '\\';
477             size--;
478             *buf++ = 'r';
479             size--;
480             continue;
481         }
482         if (*str == '\n') {
483             *buf++ = '\\';
484             size--;
485             *buf++ = 'n';
486             size--;
487             continue;
488         }
489         if (*str == '\t') {
490             *buf++ = '\\';
491             size--;
492             *buf++ = 't';
493             size--;
494             continue;
495         }
496         if (*str == '"' || *str == '\\') {
497             *buf++ = '\\';
498             size--;
499         }
500         *buf++ = *str;
501         size--;
502     }
503     if (size >= 2) {
504         *buf++ = '"';
505         size--;
506     }
507     *buf = '\0';
508 
509     jsons->msg.tail = buf;
510     jsons->msg.left = size;
511 }
512 
format_jsons_double(data_output_t * output,double data,char const * format)513 static void format_jsons_double(data_output_t *output, double data, char const *format)
514 {
515     UNUSED(format);
516     data_print_jsons_t *jsons = (data_print_jsons_t *)output;
517     // use scientific notation for very big/small values
518     if (data > 1e7 || data < 1e-4) {
519         abuf_printf(&jsons->msg, "%g", data);
520     }
521     else {
522         abuf_printf(&jsons->msg, "%.5f", data);
523         // remove trailing zeros, always keep one digit after the decimal point
524         while (jsons->msg.left > 0 && *(jsons->msg.tail - 1) == '0' && *(jsons->msg.tail - 2) != '.') {
525             jsons->msg.tail--;
526             jsons->msg.left++;
527             *jsons->msg.tail = '\0';
528         }
529     }
530 }
531 
format_jsons_int(data_output_t * output,int data,char const * format)532 static void format_jsons_int(data_output_t *output, int data, char const *format)
533 {
534     UNUSED(format);
535     data_print_jsons_t *jsons = (data_print_jsons_t *)output;
536     abuf_printf(&jsons->msg, "%d", data);
537 }
538 
data_print_jsons(data_t * data,char * dst,size_t len)539 size_t data_print_jsons(data_t *data, char *dst, size_t len)
540 {
541     data_print_jsons_t jsons = {
542             .output = {
543                     .print_data   = format_jsons_object,
544                     .print_array  = format_jsons_array,
545                     .print_string = format_jsons_string,
546                     .print_double = format_jsons_double,
547                     .print_int    = format_jsons_int,
548             },
549     };
550 
551     abuf_init(&jsons.msg, dst, len);
552 
553     format_jsons_object(&jsons.output, data, NULL);
554 
555     return len - jsons.msg.left;
556 }
557