xref: /openbmc/qemu/qapi/string-output-visitor.c (revision f755dea7)
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 
13cbf21151SPeter Maydell #include "qemu/osdep.h"
14a020f980SPaolo Bonzini #include "qemu-common.h"
157b1b5d19SPaolo Bonzini #include "qapi/string-output-visitor.h"
167b1b5d19SPaolo Bonzini #include "qapi/visitor-impl.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 
70d7bea75dSEric Blake static StringOutputVisitor *to_sov(Visitor *v)
71d7bea75dSEric Blake {
72d7bea75dSEric Blake     return container_of(v, StringOutputVisitor, visitor);
73d7bea75dSEric Blake }
74d7bea75dSEric Blake 
75a020f980SPaolo Bonzini static void string_output_set(StringOutputVisitor *sov, char *string)
76a020f980SPaolo Bonzini {
7769e25563SHu Tao     if (sov->string) {
7869e25563SHu Tao         g_string_free(sov->string, true);
7969e25563SHu Tao     }
8069e25563SHu Tao     sov->string = g_string_new(string);
8169e25563SHu Tao     g_free(string);
8269e25563SHu Tao }
8369e25563SHu Tao 
8469e25563SHu Tao static void string_output_append(StringOutputVisitor *sov, int64_t a)
8569e25563SHu Tao {
8669e25563SHu Tao     Range *r = g_malloc0(sizeof(*r));
8769e25563SHu Tao     r->begin = a;
8869e25563SHu Tao     r->end = a + 1;
8969e25563SHu Tao     sov->ranges = g_list_insert_sorted_merged(sov->ranges, r, range_compare);
9069e25563SHu Tao }
9169e25563SHu Tao 
9269e25563SHu Tao static void string_output_append_range(StringOutputVisitor *sov,
9369e25563SHu Tao                                        int64_t s, int64_t e)
9469e25563SHu Tao {
9569e25563SHu Tao     Range *r = g_malloc0(sizeof(*r));
9669e25563SHu Tao     r->begin = s;
9769e25563SHu Tao     r->end = e + 1;
9869e25563SHu Tao     sov->ranges = g_list_insert_sorted_merged(sov->ranges, r, range_compare);
9969e25563SHu Tao }
10069e25563SHu Tao 
10169e25563SHu Tao static void format_string(StringOutputVisitor *sov, Range *r, bool next,
10269e25563SHu Tao                           bool human)
10369e25563SHu Tao {
10469e25563SHu Tao     if (r->end - r->begin > 1) {
10569e25563SHu Tao         if (human) {
106684531adSHu Tao             g_string_append_printf(sov->string, "0x%" PRIx64 "-0x%" PRIx64,
10769e25563SHu Tao                                    r->begin, r->end - 1);
10869e25563SHu Tao 
10969e25563SHu Tao         } else {
11069e25563SHu Tao             g_string_append_printf(sov->string, "%" PRId64 "-%" PRId64,
11169e25563SHu Tao                                    r->begin, r->end - 1);
11269e25563SHu Tao         }
11369e25563SHu Tao     } else {
11469e25563SHu Tao         if (human) {
11556fdfb61SMichael S. Tsirkin             g_string_append_printf(sov->string, "0x%" PRIx64, r->begin);
11669e25563SHu Tao         } else {
11769e25563SHu Tao             g_string_append_printf(sov->string, "%" PRId64, r->begin);
11869e25563SHu Tao         }
11969e25563SHu Tao     }
12069e25563SHu Tao     if (next) {
12169e25563SHu Tao         g_string_append(sov->string, ",");
12269e25563SHu Tao     }
123a020f980SPaolo Bonzini }
124a020f980SPaolo Bonzini 
1254c40314aSEric Blake static void print_type_int64(Visitor *v, int64_t *obj, const char *name,
126a020f980SPaolo Bonzini                              Error **errp)
127a020f980SPaolo Bonzini {
128d7bea75dSEric Blake     StringOutputVisitor *sov = to_sov(v);
12969e25563SHu Tao     GList *l;
13069e25563SHu Tao 
13169e25563SHu Tao     switch (sov->list_mode) {
13269e25563SHu Tao     case LM_NONE:
13369e25563SHu Tao         string_output_append(sov, *obj);
13469e25563SHu Tao         break;
13569e25563SHu Tao 
13669e25563SHu Tao     case LM_STARTED:
13769e25563SHu Tao         sov->range_start.s = *obj;
13869e25563SHu Tao         sov->range_end.s = *obj;
13969e25563SHu Tao         sov->list_mode = LM_IN_PROGRESS;
14069e25563SHu Tao         return;
14169e25563SHu Tao 
14269e25563SHu Tao     case LM_IN_PROGRESS:
14369e25563SHu Tao         if (sov->range_end.s + 1 == *obj) {
14469e25563SHu Tao             sov->range_end.s++;
14569e25563SHu Tao         } else {
14669e25563SHu Tao             if (sov->range_start.s == sov->range_end.s) {
14769e25563SHu Tao                 string_output_append(sov, sov->range_end.s);
14869e25563SHu Tao             } else {
14969e25563SHu Tao                 assert(sov->range_start.s < sov->range_end.s);
15069e25563SHu Tao                 string_output_append_range(sov, sov->range_start.s,
15169e25563SHu Tao                                            sov->range_end.s);
15269e25563SHu Tao             }
15369e25563SHu Tao 
15469e25563SHu Tao             sov->range_start.s = *obj;
15569e25563SHu Tao             sov->range_end.s = *obj;
15669e25563SHu Tao         }
15769e25563SHu Tao         return;
15869e25563SHu Tao 
15969e25563SHu Tao     case LM_END:
16069e25563SHu Tao         if (sov->range_end.s + 1 == *obj) {
16169e25563SHu Tao             sov->range_end.s++;
16269e25563SHu Tao             assert(sov->range_start.s < sov->range_end.s);
16369e25563SHu Tao             string_output_append_range(sov, sov->range_start.s,
16469e25563SHu Tao                                        sov->range_end.s);
16569e25563SHu Tao         } else {
16669e25563SHu Tao             if (sov->range_start.s == sov->range_end.s) {
16769e25563SHu Tao                 string_output_append(sov, sov->range_end.s);
16869e25563SHu Tao             } else {
16969e25563SHu Tao                 assert(sov->range_start.s < sov->range_end.s);
17069e25563SHu Tao 
17169e25563SHu Tao                 string_output_append_range(sov, sov->range_start.s,
17269e25563SHu Tao                                            sov->range_end.s);
17369e25563SHu Tao             }
17469e25563SHu Tao             string_output_append(sov, *obj);
17569e25563SHu Tao         }
17669e25563SHu Tao         break;
17769e25563SHu Tao 
17869e25563SHu Tao     default:
17969e25563SHu Tao         abort();
18069e25563SHu Tao     }
18169e25563SHu Tao 
18269e25563SHu Tao     l = sov->ranges;
18369e25563SHu Tao     while (l) {
18469e25563SHu Tao         Range *r = l->data;
18569e25563SHu Tao         format_string(sov, r, l->next != NULL, false);
18669e25563SHu Tao         l = l->next;
18769e25563SHu Tao     }
1880b7593e0SPaolo Bonzini 
1890b7593e0SPaolo Bonzini     if (sov->human) {
19069e25563SHu Tao         l = sov->ranges;
19169e25563SHu Tao         g_string_append(sov->string, " (");
19269e25563SHu Tao         while (l) {
19369e25563SHu Tao             Range *r = l->data;
19456fdfb61SMichael S. Tsirkin             format_string(sov, r, l->next != NULL, true);
19569e25563SHu Tao             l = l->next;
1960b7593e0SPaolo Bonzini         }
19769e25563SHu Tao         g_string_append(sov->string, ")");
19869e25563SHu Tao     }
1990b7593e0SPaolo Bonzini }
2000b7593e0SPaolo Bonzini 
201*f755dea7SEric Blake static void print_type_uint64(Visitor *v, uint64_t *obj, const char *name,
202*f755dea7SEric Blake                              Error **errp)
203*f755dea7SEric Blake {
204*f755dea7SEric Blake     /* FIXME: print_type_int64 mishandles values over INT64_MAX */
205*f755dea7SEric Blake     int64_t i = *obj;
206*f755dea7SEric Blake     print_type_int64(v, &i, name, errp);
207*f755dea7SEric Blake }
208*f755dea7SEric Blake 
2090b7593e0SPaolo Bonzini static void print_type_size(Visitor *v, uint64_t *obj, const char *name,
2100b7593e0SPaolo Bonzini                            Error **errp)
2110b7593e0SPaolo Bonzini {
212d7bea75dSEric Blake     StringOutputVisitor *sov = to_sov(v);
213e41b509dSPaolo Bonzini     static const char suffixes[] = { 'B', 'K', 'M', 'G', 'T', 'P', 'E' };
2140b7593e0SPaolo Bonzini     uint64_t div, val;
2150b7593e0SPaolo Bonzini     char *out;
2160b7593e0SPaolo Bonzini     int i;
2170b7593e0SPaolo Bonzini 
2180b7593e0SPaolo Bonzini     if (!sov->human) {
219e41b509dSPaolo Bonzini         out = g_strdup_printf("%"PRIu64, *obj);
2200b7593e0SPaolo Bonzini         string_output_set(sov, out);
2210b7593e0SPaolo Bonzini         return;
2220b7593e0SPaolo Bonzini     }
2230b7593e0SPaolo Bonzini 
2240b7593e0SPaolo Bonzini     val = *obj;
2250b7593e0SPaolo Bonzini 
226e41b509dSPaolo Bonzini     /* The exponent (returned in i) minus one gives us
227e41b509dSPaolo Bonzini      * floor(log2(val * 1024 / 1000).  The correction makes us
228e41b509dSPaolo Bonzini      * switch to the higher power when the integer part is >= 1000.
229e41b509dSPaolo Bonzini      */
230e41b509dSPaolo Bonzini     frexp(val / (1000.0 / 1024.0), &i);
231e41b509dSPaolo Bonzini     i = (i - 1) / 10;
232e41b509dSPaolo Bonzini     assert(i < ARRAY_SIZE(suffixes));
2330b7593e0SPaolo Bonzini     div = 1ULL << (i * 10);
2340b7593e0SPaolo Bonzini 
235e41b509dSPaolo Bonzini     out = g_strdup_printf("%"PRIu64" (%0.3g %c%s)", val,
236e41b509dSPaolo Bonzini                           (double)val/div, suffixes[i], i ? "iB" : "");
2370b7593e0SPaolo Bonzini     string_output_set(sov, out);
238a020f980SPaolo Bonzini }
239a020f980SPaolo Bonzini 
240a020f980SPaolo Bonzini static void print_type_bool(Visitor *v, bool *obj, const char *name,
241a020f980SPaolo Bonzini                             Error **errp)
242a020f980SPaolo Bonzini {
243d7bea75dSEric Blake     StringOutputVisitor *sov = to_sov(v);
244a020f980SPaolo Bonzini     string_output_set(sov, g_strdup(*obj ? "true" : "false"));
245a020f980SPaolo Bonzini }
246a020f980SPaolo Bonzini 
247a020f980SPaolo Bonzini static void print_type_str(Visitor *v, char **obj, const char *name,
248a020f980SPaolo Bonzini                            Error **errp)
249a020f980SPaolo Bonzini {
250d7bea75dSEric Blake     StringOutputVisitor *sov = to_sov(v);
2510b7593e0SPaolo Bonzini     char *out;
2520b7593e0SPaolo Bonzini 
2530b7593e0SPaolo Bonzini     if (sov->human) {
2540b7593e0SPaolo Bonzini         out = *obj ? g_strdup_printf("\"%s\"", *obj) : g_strdup("<null>");
2550b7593e0SPaolo Bonzini     } else {
2560b7593e0SPaolo Bonzini         out = g_strdup(*obj ? *obj : "");
2570b7593e0SPaolo Bonzini     }
2580b7593e0SPaolo Bonzini     string_output_set(sov, out);
259a020f980SPaolo Bonzini }
260a020f980SPaolo Bonzini 
261a020f980SPaolo Bonzini static void print_type_number(Visitor *v, double *obj, const char *name,
262a020f980SPaolo Bonzini                               Error **errp)
263a020f980SPaolo Bonzini {
264d7bea75dSEric Blake     StringOutputVisitor *sov = to_sov(v);
265173bbb75SMichael Roth     string_output_set(sov, g_strdup_printf("%f", *obj));
266a020f980SPaolo Bonzini }
267a020f980SPaolo Bonzini 
26869e25563SHu Tao static void
26969e25563SHu Tao start_list(Visitor *v, const char *name, Error **errp)
27069e25563SHu Tao {
271d7bea75dSEric Blake     StringOutputVisitor *sov = to_sov(v);
27269e25563SHu Tao 
27369e25563SHu Tao     /* we can't traverse a list in a list */
27469e25563SHu Tao     assert(sov->list_mode == LM_NONE);
27569e25563SHu Tao     sov->list_mode = LM_STARTED;
27669e25563SHu Tao     sov->head = true;
27769e25563SHu Tao }
27869e25563SHu Tao 
27969e25563SHu Tao static GenericList *
28069e25563SHu Tao next_list(Visitor *v, GenericList **list, Error **errp)
28169e25563SHu Tao {
282d7bea75dSEric Blake     StringOutputVisitor *sov = to_sov(v);
28369e25563SHu Tao     GenericList *ret = NULL;
28469e25563SHu Tao     if (*list) {
28569e25563SHu Tao         if (sov->head) {
28669e25563SHu Tao             ret = *list;
28769e25563SHu Tao         } else {
28869e25563SHu Tao             ret = (*list)->next;
28969e25563SHu Tao         }
29069e25563SHu Tao 
29169e25563SHu Tao         if (sov->head) {
29269e25563SHu Tao             if (ret && ret->next == NULL) {
29369e25563SHu Tao                 sov->list_mode = LM_NONE;
29469e25563SHu Tao             }
29569e25563SHu Tao             sov->head = false;
29669e25563SHu Tao         } else {
29769e25563SHu Tao             if (ret && ret->next == NULL) {
29869e25563SHu Tao                 sov->list_mode = LM_END;
29969e25563SHu Tao             }
30069e25563SHu Tao         }
30169e25563SHu Tao     }
30269e25563SHu Tao 
30369e25563SHu Tao     return ret;
30469e25563SHu Tao }
30569e25563SHu Tao 
30669e25563SHu Tao static void
30769e25563SHu Tao end_list(Visitor *v, Error **errp)
30869e25563SHu Tao {
309d7bea75dSEric Blake     StringOutputVisitor *sov = to_sov(v);
31069e25563SHu Tao 
31169e25563SHu Tao     assert(sov->list_mode == LM_STARTED ||
31269e25563SHu Tao            sov->list_mode == LM_END ||
31369e25563SHu Tao            sov->list_mode == LM_NONE ||
31469e25563SHu Tao            sov->list_mode == LM_IN_PROGRESS);
31569e25563SHu Tao     sov->list_mode = LM_NONE;
31669e25563SHu Tao     sov->head = true;
31769e25563SHu Tao 
31869e25563SHu Tao }
31969e25563SHu Tao 
320a020f980SPaolo Bonzini char *string_output_get_string(StringOutputVisitor *sov)
321a020f980SPaolo Bonzini {
32269e25563SHu Tao     char *string = g_string_free(sov->string, false);
323a020f980SPaolo Bonzini     sov->string = NULL;
324a020f980SPaolo Bonzini     return string;
325a020f980SPaolo Bonzini }
326a020f980SPaolo Bonzini 
327a020f980SPaolo Bonzini Visitor *string_output_get_visitor(StringOutputVisitor *sov)
328a020f980SPaolo Bonzini {
329a020f980SPaolo Bonzini     return &sov->visitor;
330a020f980SPaolo Bonzini }
331a020f980SPaolo Bonzini 
3320d156683SMichael S. Tsirkin static void free_range(void *range, void *dummy)
3330d156683SMichael S. Tsirkin {
3340d156683SMichael S. Tsirkin     g_free(range);
3350d156683SMichael S. Tsirkin }
3360d156683SMichael S. Tsirkin 
337a020f980SPaolo Bonzini void string_output_visitor_cleanup(StringOutputVisitor *sov)
338a020f980SPaolo Bonzini {
33969e25563SHu Tao     if (sov->string) {
34069e25563SHu Tao         g_string_free(sov->string, true);
34169e25563SHu Tao     }
34269e25563SHu Tao 
3430d156683SMichael S. Tsirkin     g_list_foreach(sov->ranges, free_range, NULL);
3440d156683SMichael S. Tsirkin     g_list_free(sov->ranges);
345a020f980SPaolo Bonzini     g_free(sov);
346a020f980SPaolo Bonzini }
347a020f980SPaolo Bonzini 
3480b7593e0SPaolo Bonzini StringOutputVisitor *string_output_visitor_new(bool human)
349a020f980SPaolo Bonzini {
350a020f980SPaolo Bonzini     StringOutputVisitor *v;
351a020f980SPaolo Bonzini 
352a020f980SPaolo Bonzini     v = g_malloc0(sizeof(*v));
353a020f980SPaolo Bonzini 
35469e25563SHu Tao     v->string = g_string_new(NULL);
3550b7593e0SPaolo Bonzini     v->human = human;
356a020f980SPaolo Bonzini     v->visitor.type_enum = output_type_enum;
3574c40314aSEric Blake     v->visitor.type_int64 = print_type_int64;
358*f755dea7SEric Blake     v->visitor.type_uint64 = print_type_uint64;
3590b7593e0SPaolo Bonzini     v->visitor.type_size = print_type_size;
360a020f980SPaolo Bonzini     v->visitor.type_bool = print_type_bool;
361a020f980SPaolo Bonzini     v->visitor.type_str = print_type_str;
362a020f980SPaolo Bonzini     v->visitor.type_number = print_type_number;
36369e25563SHu Tao     v->visitor.start_list = start_list;
36469e25563SHu Tao     v->visitor.next_list = next_list;
36569e25563SHu Tao     v->visitor.end_list = end_list;
366a020f980SPaolo Bonzini 
367a020f980SPaolo Bonzini     return v;
368a020f980SPaolo Bonzini }
369