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