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