2 * A general structure for extracting hierarchical data from the devices;
3 * typically key-value pairs, but allows for more rich data as well.
5 * Copyright (C) 2015 by Erkki Seppälä <flux@modeemi.fi>
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.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
30 typedef void* (*array_elementwise_import_fn)(void*);
31 typedef void* (*array_element_release_fn)(void*);
32 typedef void* (*value_release_fn)(void*);
35 /* what is the element size when put inside an array? */
36 int array_element_size;
38 /* is the element boxed (ie. behind a pointer) when inside an array?
39 if it's not boxed ("unboxed"), json dumping function needs to make
40 a copy of the value beforehand, because the dumping function only
41 deals with boxed values.
45 /* function for importing arrays. strings are specially handled (as they
46 are copied deeply), whereas other arrays are just copied shallowly
47 (but copied nevertheles) */
48 array_elementwise_import_fn array_elementwise_import;
50 /* a function for releasing an element when put in an array; integers
51 * don't need to be released, while ie. strings and arrays do. */
52 array_element_release_fn array_element_release;
54 /* a function for releasing a value. everything needs to be released. */
55 value_release_fn value_release;
58 struct data_printer_context;
60 typedef struct data_printer {
61 void (*print_data)(struct data_printer_context *printer_ctx, data_t *data, char *format, FILE *file);
62 void (*print_array)(struct data_printer_context *printer_ctx, data_array_t *data, char *format, FILE *file);
63 void (*print_string)(struct data_printer_context *printer_ctx, const char *data, char *format, FILE *file);
64 void (*print_double)(struct data_printer_context *printer_ctx, double data, char *format, FILE *file);
65 void (*print_int)(struct data_printer_context *printer_ctx, int data, char *format, FILE *file);
68 typedef struct data_printer_context {
69 data_printer_t *printer;
71 } data_printer_context_t;
73 static data_meta_type_t dmt[DATA_COUNT] = {
75 { .array_element_size = sizeof(data_t*),
76 .array_is_boxed = true,
77 .array_elementwise_import = NULL,
78 .array_element_release = (array_element_release_fn) data_free,
79 .value_release = (value_release_fn) data_free },
82 { .array_element_size = sizeof(int),
83 .array_is_boxed = false,
84 .array_elementwise_import = NULL,
85 .array_element_release = NULL,
86 .value_release = (value_release_fn) free },
89 { .array_element_size = sizeof(double),
90 .array_is_boxed = false,
91 .array_elementwise_import = NULL,
92 .array_element_release = NULL,
93 .value_release = (value_release_fn) free },
96 { .array_element_size = sizeof(char*),
97 .array_is_boxed = true,
98 .array_elementwise_import = (array_elementwise_import_fn) strdup,
99 .array_element_release = (array_element_release_fn) free,
100 .value_release = (value_release_fn) free },
103 { .array_element_size = sizeof(data_array_t*),
104 .array_is_boxed = true,
105 .array_elementwise_import = NULL,
106 .array_element_release = (array_element_release_fn) data_array_free ,
107 .value_release = (value_release_fn) data_array_free },
110 static void print_json_data(data_printer_context_t *printer_ctx, data_t *data, char *format, FILE *file);
111 static void print_json_array(data_printer_context_t *printer_ctx, data_array_t *data, char *format, FILE *file);
112 static void print_json_string(data_printer_context_t *printer_ctx, const char *data, char *format, FILE *file);
113 static void print_json_double(data_printer_context_t *printer_ctx, double data, char *format, FILE *file);
114 static void print_json_int(data_printer_context_t *printer_ctx, int data, char *format, FILE *file);
116 static void print_kv_data(data_printer_context_t *printer_ctx, data_t *data, char *format, FILE *file);
117 static void print_kv_string(data_printer_context_t *printer_ctx, const char *data, char *format, FILE *file);
118 static void print_kv_double(data_printer_context_t *printer_ctx, double data, char *format, FILE *file);
119 static void print_kv_int(data_printer_context_t *printer_ctx, int data, char *format, FILE *file);
124 const char* separator;
127 static void print_csv_data(data_printer_context_t *printer_ctx, data_t *data, char *format, FILE *file);
128 static void print_csv_string(data_printer_context_t *printer_ctx, const char *data, char *format, FILE *file);
130 data_printer_t data_json_printer = {
131 .print_data = print_json_data,
132 .print_array = print_json_array,
133 .print_string = print_json_string,
134 .print_double = print_json_double,
135 .print_int = print_json_int
138 data_printer_t data_kv_printer = {
139 .print_data = print_kv_data,
140 .print_array = print_json_array,
141 .print_string = print_kv_string,
142 .print_double = print_kv_double,
143 .print_int = print_kv_int
146 data_printer_t data_csv_printer = {
147 .print_data = print_csv_data,
148 .print_array = print_json_array,
149 .print_string = print_csv_string,
150 .print_double = print_json_double,
151 .print_int = print_json_int
154 static _Bool import_values(void* dst, void* src, int num_values, data_type_t type) {
155 int element_size = dmt[type].array_element_size;
156 array_elementwise_import_fn import = dmt[type].array_elementwise_import;
158 for (int i = 0; i < num_values; ++i) {
159 void *copy = import(*(void**)(src + element_size * i));
163 free(*(void**)(dst + element_size * i));
168 *((char**) dst + i) = copy;
173 memcpy(dst, src, element_size * num_values);
175 return true; // error is returned early
178 data_array_t *data_array(int num_values, data_type_t type, void *values) {
179 data_array_t *array = calloc(1, sizeof(data_array_t));
181 int element_size = dmt[type].array_element_size;
182 array->values = calloc(num_values, element_size);
185 if (!import_values(array->values, values, num_values, type))
188 array->num_values = num_values;
200 data_t *data_make(const char *key, const char *pretty_key, ...) {
203 va_start(ap, pretty_key);
205 data_t *first = NULL;
207 char* format = false;
208 type = va_arg(ap, data_type_t);
215 format = strdup(va_arg(ap, char*));
218 type = va_arg(ap, data_type_t);
225 value = va_arg(ap, data_t*);
228 value = malloc(sizeof(int));
230 *(int*) value = va_arg(ap, int);
233 value = malloc(sizeof(double));
235 *(double*) value = va_arg(ap, double);
238 value = strdup(va_arg(ap, char*));
241 value = va_arg(ap, data_t*);
245 // also some null arguments are mapped to an alloc error;
246 // that's ok, because they originate (typically..) from
247 // an alloc error anyway
251 current = calloc(1, sizeof(*current));
255 prev->next = current;
257 current->key = strdup(key);
260 current->pretty_key = strdup(pretty_key ? pretty_key : key);
261 if (!current->pretty_key)
263 current->type = type;
264 current->format = format;
265 current->value = value;
266 current->next = NULL;
272 key = va_arg(ap, const char*);
274 pretty_key = va_arg(ap, const char*);
275 type = va_arg(ap, data_type_t);
289 void data_array_free(data_array_t *array) {
290 array_element_release_fn release = dmt[array->type].array_element_release;
292 int element_size = dmt[array->type].array_element_size;
293 for (int i = 0; i < array->num_values; ++i)
294 release(*(void**)(array->values + element_size * i));
300 void data_free(data_t *data) {
302 data_t *prev_data = data;
303 if (dmt[data->type].value_release)
304 dmt[data->type].value_release(data->value);
306 free(data->pretty_key);
313 void data_print(data_t* data, FILE *file, data_printer_t *printer, void *aux)
315 data_printer_context_t ctx = {
319 ctx.printer->print_data(&ctx, data, NULL, file);
326 static void print_value(data_printer_context_t *printer_ctx, FILE *file, data_type_t type, void *value, char *format) {
333 printer_ctx->printer->print_data(printer_ctx, value, format, file);
336 printer_ctx->printer->print_int(printer_ctx, *(int*) value, format, file);
339 printer_ctx->printer->print_double(printer_ctx, *(double*) value, format, file);
342 printer_ctx->printer->print_string(printer_ctx, value, format, file);
345 printer_ctx->printer->print_array(printer_ctx, value, format, file);
351 static void print_json_array(data_printer_context_t *printer_ctx, data_array_t *array, char *format, FILE *file) {
352 int element_size = dmt[array->type].array_element_size;
353 char buffer[element_size];
355 for (int c = 0; c < array->num_values; ++c) {
358 if (!dmt[array->type].array_is_boxed) {
359 memcpy(buffer, (void**)(array->values + element_size * c), element_size);
360 print_value(printer_ctx, file, array->type, buffer, format);
362 print_value(printer_ctx, file, array->type, *(void**)(array->values + element_size * c), format);
368 static void print_json_data(data_printer_context_t *printer_ctx, data_t *data, char *format, FILE *file)
370 _Bool separator = false;
375 printer_ctx->printer->print_string(printer_ctx, data->key, NULL, file);
376 fprintf(file, " : ");
377 print_value(printer_ctx, file, data->type, data->value, data->format);
384 static void print_json_string(data_printer_context_t *printer_ctx, const char *str, char *format, FILE *file) {
395 static void print_json_double(data_printer_context_t *printer_ctx, double data, char *format, FILE *file)
397 fprintf(file, "%.3f", data);
400 static void print_json_int(data_printer_context_t *printer_ctx, int data, char *format, FILE *file)
402 fprintf(file, "%d", data);
405 /* Key-Value printer */
406 static void print_kv_data(data_printer_context_t *printer_ctx, data_t *data, char *format, FILE *file)
408 _Bool separator = false;
409 _Bool was_labeled = false;
410 _Bool written_title = false;
412 _Bool labeled = data->pretty_key[0];
413 /* put a : between the first non-labeled and labeled */
415 if (labeled && !was_labeled && !written_title) {
417 written_title = true;
426 if (!strcmp(data->key, "time"))
427 /* fprintf(file, "") */ ;
428 else if (!strcmp(data->key, "model"))
429 fprintf(file, ":\t");
431 fprintf(file, "\t%s:\t", data->pretty_key);
434 print_value(printer_ctx, file, data->type, data->value, data->format);
436 was_labeled = labeled;
441 static void print_kv_double(data_printer_context_t *printer_ctx, double data, char *format, FILE *file)
443 fprintf(file, format ? format : "%.3f", data);
446 static void print_kv_int(data_printer_context_t *printer_ctx, int data, char *format, FILE *file)
448 fprintf(file, format ? format : "%d", data);
452 static void print_kv_string(data_printer_context_t *printer_ctx, const char *data, char *format, FILE *file)
454 fprintf(file, format ? format : "%s", data);
457 /* CSV printer; doesn't really support recursive data objects yes */
458 static void print_csv_data(data_printer_context_t *printer_ctx, data_t *data, char *format, FILE *file)
460 data_csv_aux_t *csv = printer_ctx->aux;
461 const char **fields = csv->fields;
464 if (csv->data_recursion)
467 ++csv->data_recursion;
468 for (i = 0; fields[i]; ++i) {
469 const char *key = fields[i];
470 data_t *found = NULL;
471 if (i) fprintf(file, "%s", csv->separator);
472 for (data_t *iter = data; !found && iter; iter = iter->next)
473 if (strcmp(iter->key, key) == 0)
477 print_value(printer_ctx, file, found->type, found->value, found->format);
479 --csv->data_recursion;
482 static void print_csv_string(data_printer_context_t *printer_ctx, const char *str, char *format, FILE *file)
484 data_csv_aux_t *csv = printer_ctx->aux;
486 if (strncmp(str, csv->separator, strlen(csv->separator)) == 0)
493 static int compare_strings(char** a, char** b)
495 return strcmp(*a, *b);
498 void *data_csv_init(const char **fields, int num_fields)
500 data_csv_aux_t *csv = calloc(1, sizeof(data_csv_aux_t));
503 const char **allowed = NULL;
504 int *use_count = NULL;
505 int num_unique_fields;
509 csv->separator = ",";
511 allowed = calloc(num_fields, sizeof(const char*));
512 memcpy(allowed, fields, sizeof(const char*) * num_fields);
514 qsort(allowed, num_fields, sizeof(char*), (void*) compare_strings);
516 // overwrite duplicates
519 while (j < num_fields) {
520 while (j > 0 && j < num_fields &&
521 strcmp(allowed[j - 1], allowed[j]) == 0)
524 if (j < num_fields) {
525 allowed[i] = allowed[j];
531 num_unique_fields = i;
533 csv->fields = calloc(num_unique_fields + 1, sizeof(const char**));
537 use_count = calloc(num_unique_fields, sizeof(*use_count));
541 for (i = 0; i < num_fields; ++i) {
542 const char **field = bsearch(&fields[i], allowed, num_unique_fields, sizeof(const char*),
543 (void*) compare_strings);
544 int *field_use_count = use_count + (field - allowed);
545 if (field && !*field_use_count) {
546 csv->fields[csv_fields] = fields[i];
551 csv->fields[csv_fields] = NULL;
555 // Output the CSV header
556 for (i = 0; csv->fields[i]; ++i) {
557 printf("%s%s", i > 0 ? csv->separator : "", csv->fields[i]);
565 if (csv) free(csv->fields);
570 void data_csv_free(void *aux)
572 data_csv_aux_t *csv = aux;