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