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