xref: /openbmc/qemu/qapi/string-output-visitor.c (revision 684531ad)
1a020f980SPaolo Bonzini /*
2a020f980SPaolo Bonzini  * String printing Visitor
3a020f980SPaolo Bonzini  *
4a020f980SPaolo Bonzini  * Copyright Red Hat, Inc. 2012
5a020f980SPaolo Bonzini  *
6a020f980SPaolo Bonzini  * Author: Paolo Bonzini <pbonzini@redhat.com>
7a020f980SPaolo Bonzini  *
8a020f980SPaolo Bonzini  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
9a020f980SPaolo Bonzini  * See the COPYING.LIB file in the top-level directory.
10a020f980SPaolo Bonzini  *
11a020f980SPaolo Bonzini  */
12a020f980SPaolo Bonzini 
13a020f980SPaolo Bonzini #include "qemu-common.h"
147b1b5d19SPaolo Bonzini #include "qapi/string-output-visitor.h"
157b1b5d19SPaolo Bonzini #include "qapi/visitor-impl.h"
167b1b5d19SPaolo Bonzini #include "qapi/qmp/qerror.h"
170b7593e0SPaolo Bonzini #include "qemu/host-utils.h"
18e41b509dSPaolo Bonzini #include <math.h>
1969e25563SHu Tao #include "qemu/range.h"
2069e25563SHu Tao 
2169e25563SHu Tao enum ListMode {
2269e25563SHu Tao     LM_NONE,             /* not traversing a list of repeated options */
2369e25563SHu Tao     LM_STARTED,          /* start_list() succeeded */
2469e25563SHu Tao 
2569e25563SHu Tao     LM_IN_PROGRESS,      /* next_list() has been called.
2669e25563SHu Tao                           *
2769e25563SHu Tao                           * Generating the next list link will consume the most
2869e25563SHu Tao                           * recently parsed QemuOpt instance of the repeated
2969e25563SHu Tao                           * option.
3069e25563SHu Tao                           *
3169e25563SHu Tao                           * Parsing a value into the list link will examine the
3269e25563SHu Tao                           * next QemuOpt instance of the repeated option, and
3369e25563SHu Tao                           * possibly enter LM_SIGNED_INTERVAL or
3469e25563SHu Tao                           * LM_UNSIGNED_INTERVAL.
3569e25563SHu Tao                           */
3669e25563SHu Tao 
3769e25563SHu Tao     LM_SIGNED_INTERVAL,  /* next_list() has been called.
3869e25563SHu Tao                           *
3969e25563SHu Tao                           * Generating the next list link will consume the most
4069e25563SHu Tao                           * recently stored element from the signed interval,
4169e25563SHu Tao                           * parsed from the most recent QemuOpt instance of the
4269e25563SHu Tao                           * repeated option. This may consume QemuOpt itself
4369e25563SHu Tao                           * and return to LM_IN_PROGRESS.
4469e25563SHu Tao                           *
4569e25563SHu Tao                           * Parsing a value into the list link will store the
4669e25563SHu Tao                           * next element of the signed interval.
4769e25563SHu Tao                           */
4869e25563SHu Tao 
4969e25563SHu Tao     LM_UNSIGNED_INTERVAL,/* Same as above, only for an unsigned interval. */
5069e25563SHu Tao 
5169e25563SHu Tao     LM_END
5269e25563SHu Tao };
5369e25563SHu Tao 
5469e25563SHu Tao typedef enum ListMode ListMode;
55a020f980SPaolo Bonzini 
56a020f980SPaolo Bonzini struct StringOutputVisitor
57a020f980SPaolo Bonzini {
58a020f980SPaolo Bonzini     Visitor visitor;
590b7593e0SPaolo Bonzini     bool human;
6069e25563SHu Tao     GString *string;
6169e25563SHu Tao     bool head;
6269e25563SHu Tao     ListMode list_mode;
6369e25563SHu Tao     union {
6469e25563SHu Tao         int64_t s;
6569e25563SHu Tao         uint64_t u;
6669e25563SHu Tao     } range_start, range_end;
6769e25563SHu Tao     GList *ranges;
68a020f980SPaolo Bonzini };
69a020f980SPaolo Bonzini 
70a020f980SPaolo Bonzini static void string_output_set(StringOutputVisitor *sov, char *string)
71a020f980SPaolo Bonzini {
7269e25563SHu Tao     if (sov->string) {
7369e25563SHu Tao         g_string_free(sov->string, true);
7469e25563SHu Tao     }
7569e25563SHu Tao     sov->string = g_string_new(string);
7669e25563SHu Tao     g_free(string);
7769e25563SHu Tao }
7869e25563SHu Tao 
7969e25563SHu Tao static void string_output_append(StringOutputVisitor *sov, int64_t a)
8069e25563SHu Tao {
8169e25563SHu Tao     Range *r = g_malloc0(sizeof(*r));
8269e25563SHu Tao     r->begin = a;
8369e25563SHu Tao     r->end = a + 1;
8469e25563SHu Tao     sov->ranges = g_list_insert_sorted_merged(sov->ranges, r, range_compare);
8569e25563SHu Tao }
8669e25563SHu Tao 
8769e25563SHu Tao static void string_output_append_range(StringOutputVisitor *sov,
8869e25563SHu Tao                                        int64_t s, int64_t e)
8969e25563SHu Tao {
9069e25563SHu Tao     Range *r = g_malloc0(sizeof(*r));
9169e25563SHu Tao     r->begin = s;
9269e25563SHu Tao     r->end = e + 1;
9369e25563SHu Tao     sov->ranges = g_list_insert_sorted_merged(sov->ranges, r, range_compare);
9469e25563SHu Tao }
9569e25563SHu Tao 
9669e25563SHu Tao static void format_string(StringOutputVisitor *sov, Range *r, bool next,
9769e25563SHu Tao                           bool human)
9869e25563SHu Tao {
9969e25563SHu Tao     if (r->end - r->begin > 1) {
10069e25563SHu Tao         if (human) {
101*684531adSHu Tao             g_string_append_printf(sov->string, "0x%" PRIx64 "-0x%" PRIx64,
10269e25563SHu Tao                                    r->begin, r->end - 1);
10369e25563SHu Tao 
10469e25563SHu Tao         } else {
10569e25563SHu Tao             g_string_append_printf(sov->string, "%" PRId64 "-%" PRId64,
10669e25563SHu Tao                                    r->begin, r->end - 1);
10769e25563SHu Tao         }
10869e25563SHu Tao     } else {
10969e25563SHu Tao         if (human) {
11056fdfb61SMichael S. Tsirkin             g_string_append_printf(sov->string, "0x%" PRIx64, r->begin);
11169e25563SHu Tao         } else {
11269e25563SHu Tao             g_string_append_printf(sov->string, "%" PRId64, r->begin);
11369e25563SHu Tao         }
11469e25563SHu Tao     }
11569e25563SHu Tao     if (next) {
11669e25563SHu Tao         g_string_append(sov->string, ",");
11769e25563SHu Tao     }
118a020f980SPaolo Bonzini }
119a020f980SPaolo Bonzini 
120a020f980SPaolo Bonzini static void print_type_int(Visitor *v, int64_t *obj, const char *name,
121a020f980SPaolo Bonzini                            Error **errp)
122a020f980SPaolo Bonzini {
123a020f980SPaolo Bonzini     StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
12469e25563SHu Tao     GList *l;
12569e25563SHu Tao 
12669e25563SHu Tao     switch (sov->list_mode) {
12769e25563SHu Tao     case LM_NONE:
12869e25563SHu Tao         string_output_append(sov, *obj);
12969e25563SHu Tao         break;
13069e25563SHu Tao 
13169e25563SHu Tao     case LM_STARTED:
13269e25563SHu Tao         sov->range_start.s = *obj;
13369e25563SHu Tao         sov->range_end.s = *obj;
13469e25563SHu Tao         sov->list_mode = LM_IN_PROGRESS;
13569e25563SHu Tao         return;
13669e25563SHu Tao 
13769e25563SHu Tao     case LM_IN_PROGRESS:
13869e25563SHu Tao         if (sov->range_end.s + 1 == *obj) {
13969e25563SHu Tao             sov->range_end.s++;
14069e25563SHu Tao         } else {
14169e25563SHu Tao             if (sov->range_start.s == sov->range_end.s) {
14269e25563SHu Tao                 string_output_append(sov, sov->range_end.s);
14369e25563SHu Tao             } else {
14469e25563SHu Tao                 assert(sov->range_start.s < sov->range_end.s);
14569e25563SHu Tao                 string_output_append_range(sov, sov->range_start.s,
14669e25563SHu Tao                                            sov->range_end.s);
14769e25563SHu Tao             }
14869e25563SHu Tao 
14969e25563SHu Tao             sov->range_start.s = *obj;
15069e25563SHu Tao             sov->range_end.s = *obj;
15169e25563SHu Tao         }
15269e25563SHu Tao         return;
15369e25563SHu Tao 
15469e25563SHu Tao     case LM_END:
15569e25563SHu Tao         if (sov->range_end.s + 1 == *obj) {
15669e25563SHu Tao             sov->range_end.s++;
15769e25563SHu Tao             assert(sov->range_start.s < sov->range_end.s);
15869e25563SHu Tao             string_output_append_range(sov, sov->range_start.s,
15969e25563SHu Tao                                        sov->range_end.s);
16069e25563SHu Tao         } else {
16169e25563SHu Tao             if (sov->range_start.s == sov->range_end.s) {
16269e25563SHu Tao                 string_output_append(sov, sov->range_end.s);
16369e25563SHu Tao             } else {
16469e25563SHu Tao                 assert(sov->range_start.s < sov->range_end.s);
16569e25563SHu Tao 
16669e25563SHu Tao                 string_output_append_range(sov, sov->range_start.s,
16769e25563SHu Tao                                            sov->range_end.s);
16869e25563SHu Tao             }
16969e25563SHu Tao             string_output_append(sov, *obj);
17069e25563SHu Tao         }
17169e25563SHu Tao         break;
17269e25563SHu Tao 
17369e25563SHu Tao     default:
17469e25563SHu Tao         abort();
17569e25563SHu Tao     }
17669e25563SHu Tao 
17769e25563SHu Tao     l = sov->ranges;
17869e25563SHu Tao     while (l) {
17969e25563SHu Tao         Range *r = l->data;
18069e25563SHu Tao         format_string(sov, r, l->next != NULL, false);
18169e25563SHu Tao         l = l->next;
18269e25563SHu Tao     }
1830b7593e0SPaolo Bonzini 
1840b7593e0SPaolo Bonzini     if (sov->human) {
18569e25563SHu Tao         l = sov->ranges;
18669e25563SHu Tao         g_string_append(sov->string, " (");
18769e25563SHu Tao         while (l) {
18869e25563SHu Tao             Range *r = l->data;
18956fdfb61SMichael S. Tsirkin             format_string(sov, r, l->next != NULL, true);
19069e25563SHu Tao             l = l->next;
1910b7593e0SPaolo Bonzini         }
19269e25563SHu Tao         g_string_append(sov->string, ")");
19369e25563SHu Tao     }
1940b7593e0SPaolo Bonzini }
1950b7593e0SPaolo Bonzini 
1960b7593e0SPaolo Bonzini static void print_type_size(Visitor *v, uint64_t *obj, const char *name,
1970b7593e0SPaolo Bonzini                            Error **errp)
1980b7593e0SPaolo Bonzini {
1990b7593e0SPaolo Bonzini     StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
200e41b509dSPaolo Bonzini     static const char suffixes[] = { 'B', 'K', 'M', 'G', 'T', 'P', 'E' };
2010b7593e0SPaolo Bonzini     uint64_t div, val;
2020b7593e0SPaolo Bonzini     char *out;
2030b7593e0SPaolo Bonzini     int i;
2040b7593e0SPaolo Bonzini 
2050b7593e0SPaolo Bonzini     if (!sov->human) {
206e41b509dSPaolo Bonzini         out = g_strdup_printf("%"PRIu64, *obj);
2070b7593e0SPaolo Bonzini         string_output_set(sov, out);
2080b7593e0SPaolo Bonzini         return;
2090b7593e0SPaolo Bonzini     }
2100b7593e0SPaolo Bonzini 
2110b7593e0SPaolo Bonzini     val = *obj;
2120b7593e0SPaolo Bonzini 
213e41b509dSPaolo Bonzini     /* The exponent (returned in i) minus one gives us
214e41b509dSPaolo Bonzini      * floor(log2(val * 1024 / 1000).  The correction makes us
215e41b509dSPaolo Bonzini      * switch to the higher power when the integer part is >= 1000.
216e41b509dSPaolo Bonzini      */
217e41b509dSPaolo Bonzini     frexp(val / (1000.0 / 1024.0), &i);
218e41b509dSPaolo Bonzini     i = (i - 1) / 10;
219e41b509dSPaolo Bonzini     assert(i < ARRAY_SIZE(suffixes));
2200b7593e0SPaolo Bonzini     div = 1ULL << (i * 10);
2210b7593e0SPaolo Bonzini 
222e41b509dSPaolo Bonzini     out = g_strdup_printf("%"PRIu64" (%0.3g %c%s)", val,
223e41b509dSPaolo Bonzini                           (double)val/div, suffixes[i], i ? "iB" : "");
2240b7593e0SPaolo Bonzini     string_output_set(sov, out);
225a020f980SPaolo Bonzini }
226a020f980SPaolo Bonzini 
227a020f980SPaolo Bonzini static void print_type_bool(Visitor *v, bool *obj, const char *name,
228a020f980SPaolo Bonzini                             Error **errp)
229a020f980SPaolo Bonzini {
230a020f980SPaolo Bonzini     StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
231a020f980SPaolo Bonzini     string_output_set(sov, g_strdup(*obj ? "true" : "false"));
232a020f980SPaolo Bonzini }
233a020f980SPaolo Bonzini 
234a020f980SPaolo Bonzini static void print_type_str(Visitor *v, char **obj, const char *name,
235a020f980SPaolo Bonzini                            Error **errp)
236a020f980SPaolo Bonzini {
237a020f980SPaolo Bonzini     StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
2380b7593e0SPaolo Bonzini     char *out;
2390b7593e0SPaolo Bonzini 
2400b7593e0SPaolo Bonzini     if (sov->human) {
2410b7593e0SPaolo Bonzini         out = *obj ? g_strdup_printf("\"%s\"", *obj) : g_strdup("<null>");
2420b7593e0SPaolo Bonzini     } else {
2430b7593e0SPaolo Bonzini         out = g_strdup(*obj ? *obj : "");
2440b7593e0SPaolo Bonzini     }
2450b7593e0SPaolo Bonzini     string_output_set(sov, out);
246a020f980SPaolo Bonzini }
247a020f980SPaolo Bonzini 
248a020f980SPaolo Bonzini static void print_type_number(Visitor *v, double *obj, const char *name,
249a020f980SPaolo Bonzini                               Error **errp)
250a020f980SPaolo Bonzini {
251a020f980SPaolo Bonzini     StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
252173bbb75SMichael Roth     string_output_set(sov, g_strdup_printf("%f", *obj));
253a020f980SPaolo Bonzini }
254a020f980SPaolo Bonzini 
25569e25563SHu Tao static void
25669e25563SHu Tao start_list(Visitor *v, const char *name, Error **errp)
25769e25563SHu Tao {
25869e25563SHu Tao     StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
25969e25563SHu Tao 
26069e25563SHu Tao     /* we can't traverse a list in a list */
26169e25563SHu Tao     assert(sov->list_mode == LM_NONE);
26269e25563SHu Tao     sov->list_mode = LM_STARTED;
26369e25563SHu Tao     sov->head = true;
26469e25563SHu Tao }
26569e25563SHu Tao 
26669e25563SHu Tao static GenericList *
26769e25563SHu Tao next_list(Visitor *v, GenericList **list, Error **errp)
26869e25563SHu Tao {
26969e25563SHu Tao     StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
27069e25563SHu Tao     GenericList *ret = NULL;
27169e25563SHu Tao     if (*list) {
27269e25563SHu Tao         if (sov->head) {
27369e25563SHu Tao             ret = *list;
27469e25563SHu Tao         } else {
27569e25563SHu Tao             ret = (*list)->next;
27669e25563SHu Tao         }
27769e25563SHu Tao 
27869e25563SHu Tao         if (sov->head) {
27969e25563SHu Tao             if (ret && ret->next == NULL) {
28069e25563SHu Tao                 sov->list_mode = LM_NONE;
28169e25563SHu Tao             }
28269e25563SHu Tao             sov->head = false;
28369e25563SHu Tao         } else {
28469e25563SHu Tao             if (ret && ret->next == NULL) {
28569e25563SHu Tao                 sov->list_mode = LM_END;
28669e25563SHu Tao             }
28769e25563SHu Tao         }
28869e25563SHu Tao     }
28969e25563SHu Tao 
29069e25563SHu Tao     return ret;
29169e25563SHu Tao }
29269e25563SHu Tao 
29369e25563SHu Tao static void
29469e25563SHu Tao end_list(Visitor *v, Error **errp)
29569e25563SHu Tao {
29669e25563SHu Tao     StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
29769e25563SHu Tao 
29869e25563SHu Tao     assert(sov->list_mode == LM_STARTED ||
29969e25563SHu Tao            sov->list_mode == LM_END ||
30069e25563SHu Tao            sov->list_mode == LM_NONE ||
30169e25563SHu Tao            sov->list_mode == LM_IN_PROGRESS);
30269e25563SHu Tao     sov->list_mode = LM_NONE;
30369e25563SHu Tao     sov->head = true;
30469e25563SHu Tao 
30569e25563SHu Tao }
30669e25563SHu Tao 
307a020f980SPaolo Bonzini char *string_output_get_string(StringOutputVisitor *sov)
308a020f980SPaolo Bonzini {
30969e25563SHu Tao     char *string = g_string_free(sov->string, false);
310a020f980SPaolo Bonzini     sov->string = NULL;
311a020f980SPaolo Bonzini     return string;
312a020f980SPaolo Bonzini }
313a020f980SPaolo Bonzini 
314a020f980SPaolo Bonzini Visitor *string_output_get_visitor(StringOutputVisitor *sov)
315a020f980SPaolo Bonzini {
316a020f980SPaolo Bonzini     return &sov->visitor;
317a020f980SPaolo Bonzini }
318a020f980SPaolo Bonzini 
3190d156683SMichael S. Tsirkin static void free_range(void *range, void *dummy)
3200d156683SMichael S. Tsirkin {
3210d156683SMichael S. Tsirkin     g_free(range);
3220d156683SMichael S. Tsirkin }
3230d156683SMichael S. Tsirkin 
324a020f980SPaolo Bonzini void string_output_visitor_cleanup(StringOutputVisitor *sov)
325a020f980SPaolo Bonzini {
32669e25563SHu Tao     if (sov->string) {
32769e25563SHu Tao         g_string_free(sov->string, true);
32869e25563SHu Tao     }
32969e25563SHu Tao 
3300d156683SMichael S. Tsirkin     g_list_foreach(sov->ranges, free_range, NULL);
3310d156683SMichael S. Tsirkin     g_list_free(sov->ranges);
332a020f980SPaolo Bonzini     g_free(sov);
333a020f980SPaolo Bonzini }
334a020f980SPaolo Bonzini 
3350b7593e0SPaolo Bonzini StringOutputVisitor *string_output_visitor_new(bool human)
336a020f980SPaolo Bonzini {
337a020f980SPaolo Bonzini     StringOutputVisitor *v;
338a020f980SPaolo Bonzini 
339a020f980SPaolo Bonzini     v = g_malloc0(sizeof(*v));
340a020f980SPaolo Bonzini 
34169e25563SHu Tao     v->string = g_string_new(NULL);
3420b7593e0SPaolo Bonzini     v->human = human;
343a020f980SPaolo Bonzini     v->visitor.type_enum = output_type_enum;
344a020f980SPaolo Bonzini     v->visitor.type_int = print_type_int;
3450b7593e0SPaolo Bonzini     v->visitor.type_size = print_type_size;
346a020f980SPaolo Bonzini     v->visitor.type_bool = print_type_bool;
347a020f980SPaolo Bonzini     v->visitor.type_str = print_type_str;
348a020f980SPaolo Bonzini     v->visitor.type_number = print_type_number;
34969e25563SHu Tao     v->visitor.start_list = start_list;
35069e25563SHu Tao     v->visitor.next_list = next_list;
35169e25563SHu Tao     v->visitor.end_list = end_list;
352a020f980SPaolo Bonzini 
353a020f980SPaolo Bonzini     return v;
354a020f980SPaolo Bonzini }
355