1 /* 2 * String printing Visitor 3 * 4 * Copyright Red Hat, Inc. 2012 5 * 6 * Author: Paolo Bonzini <pbonzini@redhat.com> 7 * 8 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. 9 * See the COPYING.LIB file in the top-level directory. 10 * 11 */ 12 13 #include "qemu-common.h" 14 #include "qapi/string-output-visitor.h" 15 #include "qapi/visitor-impl.h" 16 #include "qemu/host-utils.h" 17 #include <math.h> 18 #include "qemu/range.h" 19 20 enum ListMode { 21 LM_NONE, /* not traversing a list of repeated options */ 22 LM_STARTED, /* start_list() succeeded */ 23 24 LM_IN_PROGRESS, /* next_list() has been called. 25 * 26 * Generating the next list link will consume the most 27 * recently parsed QemuOpt instance of the repeated 28 * option. 29 * 30 * Parsing a value into the list link will examine the 31 * next QemuOpt instance of the repeated option, and 32 * possibly enter LM_SIGNED_INTERVAL or 33 * LM_UNSIGNED_INTERVAL. 34 */ 35 36 LM_SIGNED_INTERVAL, /* next_list() has been called. 37 * 38 * Generating the next list link will consume the most 39 * recently stored element from the signed interval, 40 * parsed from the most recent QemuOpt instance of the 41 * repeated option. This may consume QemuOpt itself 42 * and return to LM_IN_PROGRESS. 43 * 44 * Parsing a value into the list link will store the 45 * next element of the signed interval. 46 */ 47 48 LM_UNSIGNED_INTERVAL,/* Same as above, only for an unsigned interval. */ 49 50 LM_END 51 }; 52 53 typedef enum ListMode ListMode; 54 55 struct StringOutputVisitor 56 { 57 Visitor visitor; 58 bool human; 59 GString *string; 60 bool head; 61 ListMode list_mode; 62 union { 63 int64_t s; 64 uint64_t u; 65 } range_start, range_end; 66 GList *ranges; 67 }; 68 69 static void string_output_set(StringOutputVisitor *sov, char *string) 70 { 71 if (sov->string) { 72 g_string_free(sov->string, true); 73 } 74 sov->string = g_string_new(string); 75 g_free(string); 76 } 77 78 static void string_output_append(StringOutputVisitor *sov, int64_t a) 79 { 80 Range *r = g_malloc0(sizeof(*r)); 81 r->begin = a; 82 r->end = a + 1; 83 sov->ranges = g_list_insert_sorted_merged(sov->ranges, r, range_compare); 84 } 85 86 static void string_output_append_range(StringOutputVisitor *sov, 87 int64_t s, int64_t e) 88 { 89 Range *r = g_malloc0(sizeof(*r)); 90 r->begin = s; 91 r->end = e + 1; 92 sov->ranges = g_list_insert_sorted_merged(sov->ranges, r, range_compare); 93 } 94 95 static void format_string(StringOutputVisitor *sov, Range *r, bool next, 96 bool human) 97 { 98 if (r->end - r->begin > 1) { 99 if (human) { 100 g_string_append_printf(sov->string, "0x%" PRIx64 "-0x%" PRIx64, 101 r->begin, r->end - 1); 102 103 } else { 104 g_string_append_printf(sov->string, "%" PRId64 "-%" PRId64, 105 r->begin, r->end - 1); 106 } 107 } else { 108 if (human) { 109 g_string_append_printf(sov->string, "0x%" PRIx64, r->begin); 110 } else { 111 g_string_append_printf(sov->string, "%" PRId64, r->begin); 112 } 113 } 114 if (next) { 115 g_string_append(sov->string, ","); 116 } 117 } 118 119 static void print_type_int(Visitor *v, int64_t *obj, const char *name, 120 Error **errp) 121 { 122 StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v); 123 GList *l; 124 125 switch (sov->list_mode) { 126 case LM_NONE: 127 string_output_append(sov, *obj); 128 break; 129 130 case LM_STARTED: 131 sov->range_start.s = *obj; 132 sov->range_end.s = *obj; 133 sov->list_mode = LM_IN_PROGRESS; 134 return; 135 136 case LM_IN_PROGRESS: 137 if (sov->range_end.s + 1 == *obj) { 138 sov->range_end.s++; 139 } else { 140 if (sov->range_start.s == sov->range_end.s) { 141 string_output_append(sov, sov->range_end.s); 142 } else { 143 assert(sov->range_start.s < sov->range_end.s); 144 string_output_append_range(sov, sov->range_start.s, 145 sov->range_end.s); 146 } 147 148 sov->range_start.s = *obj; 149 sov->range_end.s = *obj; 150 } 151 return; 152 153 case LM_END: 154 if (sov->range_end.s + 1 == *obj) { 155 sov->range_end.s++; 156 assert(sov->range_start.s < sov->range_end.s); 157 string_output_append_range(sov, sov->range_start.s, 158 sov->range_end.s); 159 } else { 160 if (sov->range_start.s == sov->range_end.s) { 161 string_output_append(sov, sov->range_end.s); 162 } else { 163 assert(sov->range_start.s < sov->range_end.s); 164 165 string_output_append_range(sov, sov->range_start.s, 166 sov->range_end.s); 167 } 168 string_output_append(sov, *obj); 169 } 170 break; 171 172 default: 173 abort(); 174 } 175 176 l = sov->ranges; 177 while (l) { 178 Range *r = l->data; 179 format_string(sov, r, l->next != NULL, false); 180 l = l->next; 181 } 182 183 if (sov->human) { 184 l = sov->ranges; 185 g_string_append(sov->string, " ("); 186 while (l) { 187 Range *r = l->data; 188 format_string(sov, r, l->next != NULL, true); 189 l = l->next; 190 } 191 g_string_append(sov->string, ")"); 192 } 193 } 194 195 static void print_type_size(Visitor *v, uint64_t *obj, const char *name, 196 Error **errp) 197 { 198 StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v); 199 static const char suffixes[] = { 'B', 'K', 'M', 'G', 'T', 'P', 'E' }; 200 uint64_t div, val; 201 char *out; 202 int i; 203 204 if (!sov->human) { 205 out = g_strdup_printf("%"PRIu64, *obj); 206 string_output_set(sov, out); 207 return; 208 } 209 210 val = *obj; 211 212 /* The exponent (returned in i) minus one gives us 213 * floor(log2(val * 1024 / 1000). The correction makes us 214 * switch to the higher power when the integer part is >= 1000. 215 */ 216 frexp(val / (1000.0 / 1024.0), &i); 217 i = (i - 1) / 10; 218 assert(i < ARRAY_SIZE(suffixes)); 219 div = 1ULL << (i * 10); 220 221 out = g_strdup_printf("%"PRIu64" (%0.3g %c%s)", val, 222 (double)val/div, suffixes[i], i ? "iB" : ""); 223 string_output_set(sov, out); 224 } 225 226 static void print_type_bool(Visitor *v, bool *obj, const char *name, 227 Error **errp) 228 { 229 StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v); 230 string_output_set(sov, g_strdup(*obj ? "true" : "false")); 231 } 232 233 static void print_type_str(Visitor *v, char **obj, const char *name, 234 Error **errp) 235 { 236 StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v); 237 char *out; 238 239 if (sov->human) { 240 out = *obj ? g_strdup_printf("\"%s\"", *obj) : g_strdup("<null>"); 241 } else { 242 out = g_strdup(*obj ? *obj : ""); 243 } 244 string_output_set(sov, out); 245 } 246 247 static void print_type_number(Visitor *v, double *obj, const char *name, 248 Error **errp) 249 { 250 StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v); 251 string_output_set(sov, g_strdup_printf("%f", *obj)); 252 } 253 254 static void 255 start_list(Visitor *v, const char *name, Error **errp) 256 { 257 StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v); 258 259 /* we can't traverse a list in a list */ 260 assert(sov->list_mode == LM_NONE); 261 sov->list_mode = LM_STARTED; 262 sov->head = true; 263 } 264 265 static GenericList * 266 next_list(Visitor *v, GenericList **list, Error **errp) 267 { 268 StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v); 269 GenericList *ret = NULL; 270 if (*list) { 271 if (sov->head) { 272 ret = *list; 273 } else { 274 ret = (*list)->next; 275 } 276 277 if (sov->head) { 278 if (ret && ret->next == NULL) { 279 sov->list_mode = LM_NONE; 280 } 281 sov->head = false; 282 } else { 283 if (ret && ret->next == NULL) { 284 sov->list_mode = LM_END; 285 } 286 } 287 } 288 289 return ret; 290 } 291 292 static void 293 end_list(Visitor *v, Error **errp) 294 { 295 StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v); 296 297 assert(sov->list_mode == LM_STARTED || 298 sov->list_mode == LM_END || 299 sov->list_mode == LM_NONE || 300 sov->list_mode == LM_IN_PROGRESS); 301 sov->list_mode = LM_NONE; 302 sov->head = true; 303 304 } 305 306 char *string_output_get_string(StringOutputVisitor *sov) 307 { 308 char *string = g_string_free(sov->string, false); 309 sov->string = NULL; 310 return string; 311 } 312 313 Visitor *string_output_get_visitor(StringOutputVisitor *sov) 314 { 315 return &sov->visitor; 316 } 317 318 static void free_range(void *range, void *dummy) 319 { 320 g_free(range); 321 } 322 323 void string_output_visitor_cleanup(StringOutputVisitor *sov) 324 { 325 if (sov->string) { 326 g_string_free(sov->string, true); 327 } 328 329 g_list_foreach(sov->ranges, free_range, NULL); 330 g_list_free(sov->ranges); 331 g_free(sov); 332 } 333 334 StringOutputVisitor *string_output_visitor_new(bool human) 335 { 336 StringOutputVisitor *v; 337 338 v = g_malloc0(sizeof(*v)); 339 340 v->string = g_string_new(NULL); 341 v->human = human; 342 v->visitor.type_enum = output_type_enum; 343 v->visitor.type_int = print_type_int; 344 v->visitor.type_size = print_type_size; 345 v->visitor.type_bool = print_type_bool; 346 v->visitor.type_str = print_type_str; 347 v->visitor.type_number = print_type_number; 348 v->visitor.start_list = start_list; 349 v->visitor.next_list = next_list; 350 v->visitor.end_list = end_list; 351 352 return v; 353 } 354