qemu/qapi/string-output-visitor.c
<<
>>
Prefs
   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
  21enum 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
  54typedef enum ListMode ListMode;
  55
  56struct 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
  70static StringOutputVisitor *to_sov(Visitor *v)
  71{
  72    return container_of(v, StringOutputVisitor, visitor);
  73}
  74
  75static 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
  84static void string_output_append(StringOutputVisitor *sov, int64_t a)
  85{
  86    Range *r = g_malloc0(sizeof(*r));
  87    r->begin = a;
  88    r->end = a + 1;
  89    sov->ranges = g_list_insert_sorted_merged(sov->ranges, r, range_compare);
  90}
  91
  92static void string_output_append_range(StringOutputVisitor *sov,
  93                                       int64_t s, int64_t e)
  94{
  95    Range *r = g_malloc0(sizeof(*r));
  96    r->begin = s;
  97    r->end = e + 1;
  98    sov->ranges = g_list_insert_sorted_merged(sov->ranges, r, range_compare);
  99}
 100
 101static void format_string(StringOutputVisitor *sov, Range *r, bool next,
 102                          bool human)
 103{
 104    if (r->end - r->begin > 1) {
 105        if (human) {
 106            g_string_append_printf(sov->string, "0x%" PRIx64 "-0x%" PRIx64,
 107                                   r->begin, r->end - 1);
 108
 109        } else {
 110            g_string_append_printf(sov->string, "%" PRId64 "-%" PRId64,
 111                                   r->begin, r->end - 1);
 112        }
 113    } else {
 114        if (human) {
 115            g_string_append_printf(sov->string, "0x%" PRIx64, r->begin);
 116        } else {
 117            g_string_append_printf(sov->string, "%" PRId64, r->begin);
 118        }
 119    }
 120    if (next) {
 121        g_string_append(sov->string, ",");
 122    }
 123}
 124
 125static 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
 201static 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
 209static 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
 240static 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
 247static 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
 261static 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
 268static void
 269start_list(Visitor *v, const char *name, 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    sov->list_mode = LM_STARTED;
 276    sov->head = true;
 277}
 278
 279static GenericList *next_list(Visitor *v, GenericList **list, size_t size)
 280{
 281    StringOutputVisitor *sov = to_sov(v);
 282    GenericList *ret = NULL;
 283    if (*list) {
 284        if (sov->head) {
 285            ret = *list;
 286        } else {
 287            ret = (*list)->next;
 288        }
 289
 290        if (sov->head) {
 291            if (ret && ret->next == NULL) {
 292                sov->list_mode = LM_NONE;
 293            }
 294            sov->head = false;
 295        } else {
 296            if (ret && ret->next == NULL) {
 297                sov->list_mode = LM_END;
 298            }
 299        }
 300    }
 301
 302    return ret;
 303}
 304
 305static void end_list(Visitor *v)
 306{
 307    StringOutputVisitor *sov = to_sov(v);
 308
 309    assert(sov->list_mode == LM_STARTED ||
 310           sov->list_mode == LM_END ||
 311           sov->list_mode == LM_NONE ||
 312           sov->list_mode == LM_IN_PROGRESS);
 313    sov->list_mode = LM_NONE;
 314    sov->head = true;
 315
 316}
 317
 318char *string_output_get_string(StringOutputVisitor *sov)
 319{
 320    char *string = g_string_free(sov->string, false);
 321    sov->string = NULL;
 322    return string;
 323}
 324
 325Visitor *string_output_get_visitor(StringOutputVisitor *sov)
 326{
 327    return &sov->visitor;
 328}
 329
 330static void free_range(void *range, void *dummy)
 331{
 332    g_free(range);
 333}
 334
 335void string_output_visitor_cleanup(StringOutputVisitor *sov)
 336{
 337    if (sov->string) {
 338        g_string_free(sov->string, true);
 339    }
 340
 341    g_list_foreach(sov->ranges, free_range, NULL);
 342    g_list_free(sov->ranges);
 343    g_free(sov);
 344}
 345
 346StringOutputVisitor *string_output_visitor_new(bool human)
 347{
 348    StringOutputVisitor *v;
 349
 350    v = g_malloc0(sizeof(*v));
 351
 352    v->string = g_string_new(NULL);
 353    v->human = human;
 354    v->visitor.type_enum = output_type_enum;
 355    v->visitor.type_int64 = print_type_int64;
 356    v->visitor.type_uint64 = print_type_uint64;
 357    v->visitor.type_size = print_type_size;
 358    v->visitor.type_bool = print_type_bool;
 359    v->visitor.type_str = print_type_str;
 360    v->visitor.type_number = print_type_number;
 361    v->visitor.start_list = start_list;
 362    v->visitor.next_list = next_list;
 363    v->visitor.end_list = end_list;
 364
 365    return v;
 366}
 367