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