1 /* 2 * String printing Visitor 3 * 4 * Copyright Red Hat, Inc. 2012-2016 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/osdep.h" 14 #include "qemu-common.h" 15 #include "qapi/string-output-visitor.h" 16 #include "qapi/visitor-impl.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, /* next_list() ready to be called */ 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, /* next_list() called, about to see last element. */ 52 }; 53 54 typedef enum ListMode ListMode; 55 56 struct StringOutputVisitor 57 { 58 Visitor visitor; 59 bool human; 60 GString *string; 61 ListMode list_mode; 62 union { 63 int64_t s; 64 uint64_t u; 65 } range_start, range_end; 66 GList *ranges; 67 void *list; /* Only needed for sanity checking the caller */ 68 }; 69 70 static StringOutputVisitor *to_sov(Visitor *v) 71 { 72 return container_of(v, StringOutputVisitor, visitor); 73 } 74 75 static void string_output_set(StringOutputVisitor *sov, char *string) 76 { 77 if (sov->string) { 78 g_string_free(sov->string, true); 79 } 80 sov->string = g_string_new(string); 81 g_free(string); 82 } 83 84 static void string_output_append(StringOutputVisitor *sov, int64_t a) 85 { 86 Range *r = g_malloc0(sizeof(*r)); 87 88 range_set_bounds(r, a, a); 89 sov->ranges = range_list_insert(sov->ranges, r); 90 } 91 92 static void string_output_append_range(StringOutputVisitor *sov, 93 int64_t s, int64_t e) 94 { 95 Range *r = g_malloc0(sizeof(*r)); 96 97 range_set_bounds(r, s, e); 98 sov->ranges = range_list_insert(sov->ranges, r); 99 } 100 101 static void format_string(StringOutputVisitor *sov, Range *r, bool next, 102 bool human) 103 { 104 if (range_lob(r) != range_upb(r)) { 105 if (human) { 106 g_string_append_printf(sov->string, "0x%" PRIx64 "-0x%" PRIx64, 107 range_lob(r), range_upb(r)); 108 109 } else { 110 g_string_append_printf(sov->string, "%" PRId64 "-%" PRId64, 111 range_lob(r), range_upb(r)); 112 } 113 } else { 114 if (human) { 115 g_string_append_printf(sov->string, "0x%" PRIx64, range_lob(r)); 116 } else { 117 g_string_append_printf(sov->string, "%" PRId64, range_lob(r)); 118 } 119 } 120 if (next) { 121 g_string_append(sov->string, ","); 122 } 123 } 124 125 static void print_type_int64(Visitor *v, const char *name, int64_t *obj, 126 Error **errp) 127 { 128 StringOutputVisitor *sov = to_sov(v); 129 GList *l; 130 131 switch (sov->list_mode) { 132 case LM_NONE: 133 string_output_append(sov, *obj); 134 break; 135 136 case LM_STARTED: 137 sov->range_start.s = *obj; 138 sov->range_end.s = *obj; 139 sov->list_mode = LM_IN_PROGRESS; 140 return; 141 142 case LM_IN_PROGRESS: 143 if (sov->range_end.s + 1 == *obj) { 144 sov->range_end.s++; 145 } else { 146 if (sov->range_start.s == sov->range_end.s) { 147 string_output_append(sov, sov->range_end.s); 148 } else { 149 assert(sov->range_start.s < sov->range_end.s); 150 string_output_append_range(sov, sov->range_start.s, 151 sov->range_end.s); 152 } 153 154 sov->range_start.s = *obj; 155 sov->range_end.s = *obj; 156 } 157 return; 158 159 case LM_END: 160 if (sov->range_end.s + 1 == *obj) { 161 sov->range_end.s++; 162 assert(sov->range_start.s < sov->range_end.s); 163 string_output_append_range(sov, sov->range_start.s, 164 sov->range_end.s); 165 } else { 166 if (sov->range_start.s == sov->range_end.s) { 167 string_output_append(sov, sov->range_end.s); 168 } else { 169 assert(sov->range_start.s < sov->range_end.s); 170 171 string_output_append_range(sov, sov->range_start.s, 172 sov->range_end.s); 173 } 174 string_output_append(sov, *obj); 175 } 176 break; 177 178 default: 179 abort(); 180 } 181 182 l = sov->ranges; 183 while (l) { 184 Range *r = l->data; 185 format_string(sov, r, l->next != NULL, false); 186 l = l->next; 187 } 188 189 if (sov->human) { 190 l = sov->ranges; 191 g_string_append(sov->string, " ("); 192 while (l) { 193 Range *r = l->data; 194 format_string(sov, r, l->next != NULL, true); 195 l = l->next; 196 } 197 g_string_append(sov->string, ")"); 198 } 199 } 200 201 static void print_type_uint64(Visitor *v, const char *name, uint64_t *obj, 202 Error **errp) 203 { 204 /* FIXME: print_type_int64 mishandles values over INT64_MAX */ 205 int64_t i = *obj; 206 print_type_int64(v, name, &i, errp); 207 } 208 209 static void print_type_size(Visitor *v, const char *name, uint64_t *obj, 210 Error **errp) 211 { 212 StringOutputVisitor *sov = to_sov(v); 213 static const char suffixes[] = { 'B', 'K', 'M', 'G', 'T', 'P', 'E' }; 214 uint64_t div, val; 215 char *out; 216 int i; 217 218 if (!sov->human) { 219 out = g_strdup_printf("%"PRIu64, *obj); 220 string_output_set(sov, out); 221 return; 222 } 223 224 val = *obj; 225 226 /* The exponent (returned in i) minus one gives us 227 * floor(log2(val * 1024 / 1000). The correction makes us 228 * switch to the higher power when the integer part is >= 1000. 229 */ 230 frexp(val / (1000.0 / 1024.0), &i); 231 i = (i - 1) / 10; 232 assert(i < ARRAY_SIZE(suffixes)); 233 div = 1ULL << (i * 10); 234 235 out = g_strdup_printf("%"PRIu64" (%0.3g %c%s)", val, 236 (double)val/div, suffixes[i], i ? "iB" : ""); 237 string_output_set(sov, out); 238 } 239 240 static void print_type_bool(Visitor *v, const char *name, bool *obj, 241 Error **errp) 242 { 243 StringOutputVisitor *sov = to_sov(v); 244 string_output_set(sov, g_strdup(*obj ? "true" : "false")); 245 } 246 247 static void print_type_str(Visitor *v, const char *name, char **obj, 248 Error **errp) 249 { 250 StringOutputVisitor *sov = to_sov(v); 251 char *out; 252 253 if (sov->human) { 254 out = *obj ? g_strdup_printf("\"%s\"", *obj) : g_strdup("<null>"); 255 } else { 256 out = g_strdup(*obj ? *obj : ""); 257 } 258 string_output_set(sov, out); 259 } 260 261 static void print_type_number(Visitor *v, const char *name, double *obj, 262 Error **errp) 263 { 264 StringOutputVisitor *sov = to_sov(v); 265 string_output_set(sov, g_strdup_printf("%f", *obj)); 266 } 267 268 static void 269 start_list(Visitor *v, const char *name, GenericList **list, size_t size, 270 Error **errp) 271 { 272 StringOutputVisitor *sov = to_sov(v); 273 274 /* we can't traverse a list in a list */ 275 assert(sov->list_mode == LM_NONE); 276 /* We don't support visits without a list */ 277 assert(list); 278 sov->list = list; 279 /* List handling is only needed if there are at least two elements */ 280 if (*list && (*list)->next) { 281 sov->list_mode = LM_STARTED; 282 } 283 } 284 285 static GenericList *next_list(Visitor *v, GenericList *tail, size_t size) 286 { 287 StringOutputVisitor *sov = to_sov(v); 288 GenericList *ret = tail->next; 289 290 if (ret && !ret->next) { 291 sov->list_mode = LM_END; 292 } 293 return ret; 294 } 295 296 static void end_list(Visitor *v, void **obj) 297 { 298 StringOutputVisitor *sov = to_sov(v); 299 300 assert(sov->list == obj); 301 assert(sov->list_mode == LM_STARTED || 302 sov->list_mode == LM_END || 303 sov->list_mode == LM_NONE || 304 sov->list_mode == LM_IN_PROGRESS); 305 sov->list_mode = LM_NONE; 306 } 307 308 char *string_output_get_string(StringOutputVisitor *sov) 309 { 310 char *string = g_string_free(sov->string, false); 311 sov->string = NULL; 312 return string; 313 } 314 315 Visitor *string_output_get_visitor(StringOutputVisitor *sov) 316 { 317 return &sov->visitor; 318 } 319 320 static void free_range(void *range, void *dummy) 321 { 322 g_free(range); 323 } 324 325 static void string_output_free(Visitor *v) 326 { 327 StringOutputVisitor *sov = to_sov(v); 328 329 string_output_visitor_cleanup(sov); 330 } 331 332 void string_output_visitor_cleanup(StringOutputVisitor *sov) 333 { 334 if (sov->string) { 335 g_string_free(sov->string, true); 336 } 337 338 g_list_foreach(sov->ranges, free_range, NULL); 339 g_list_free(sov->ranges); 340 g_free(sov); 341 } 342 343 StringOutputVisitor *string_output_visitor_new(bool human) 344 { 345 StringOutputVisitor *v; 346 347 v = g_malloc0(sizeof(*v)); 348 349 v->string = g_string_new(NULL); 350 v->human = human; 351 v->visitor.type = VISITOR_OUTPUT; 352 v->visitor.type_int64 = print_type_int64; 353 v->visitor.type_uint64 = print_type_uint64; 354 v->visitor.type_size = print_type_size; 355 v->visitor.type_bool = print_type_bool; 356 v->visitor.type_str = print_type_str; 357 v->visitor.type_number = print_type_number; 358 v->visitor.start_list = start_list; 359 v->visitor.next_list = next_list; 360 v->visitor.end_list = end_list; 361 v->visitor.free = string_output_free; 362 363 return v; 364 } 365