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