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,          /* 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
  54typedef enum ListMode ListMode;
  55
  56struct 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
  71static StringOutputVisitor *to_sov(Visitor *v)
  72{
  73    return container_of(v, StringOutputVisitor, visitor);
  74}
  75
  76static 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
  85static 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
  93static 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
 102static 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
 126static 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
 202static 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
 210static void print_type_size(Visitor *v, const char *name, uint64_t *obj,
 211                            Error **errp)
 212{
 213    StringOutputVisitor *sov = to_sov(v);
 214    uint64_t val;
 215    char *out, *psize;
 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    psize = size_to_str(val);
 225    out = g_strdup_printf("%"PRIu64" (%s)", val, psize);
 226    string_output_set(sov, out);
 227
 228    g_free(psize);
 229}
 230
 231static void print_type_bool(Visitor *v, const char *name, bool *obj,
 232                            Error **errp)
 233{
 234    StringOutputVisitor *sov = to_sov(v);
 235    string_output_set(sov, g_strdup(*obj ? "true" : "false"));
 236}
 237
 238static void print_type_str(Visitor *v, const char *name, char **obj,
 239                           Error **errp)
 240{
 241    StringOutputVisitor *sov = to_sov(v);
 242    char *out;
 243
 244    if (sov->human) {
 245        out = *obj ? g_strdup_printf("\"%s\"", *obj) : g_strdup("<null>");
 246    } else {
 247        out = g_strdup(*obj ? *obj : "");
 248    }
 249    string_output_set(sov, out);
 250}
 251
 252static void print_type_number(Visitor *v, const char *name, double *obj,
 253                              Error **errp)
 254{
 255    StringOutputVisitor *sov = to_sov(v);
 256    string_output_set(sov, g_strdup_printf("%f", *obj));
 257}
 258
 259static void print_type_null(Visitor *v, const char *name, QNull **obj,
 260                            Error **errp)
 261{
 262    StringOutputVisitor *sov = to_sov(v);
 263    char *out;
 264
 265    if (sov->human) {
 266        out = g_strdup("<null>");
 267    } else {
 268        out = g_strdup("");
 269    }
 270    string_output_set(sov, out);
 271}
 272
 273static void
 274start_list(Visitor *v, const char *name, GenericList **list, size_t size,
 275           Error **errp)
 276{
 277    StringOutputVisitor *sov = to_sov(v);
 278
 279    /* we can't traverse a list in a list */
 280    assert(sov->list_mode == LM_NONE);
 281    /* We don't support visits without a list */
 282    assert(list);
 283    sov->list = list;
 284    /* List handling is only needed if there are at least two elements */
 285    if (*list && (*list)->next) {
 286        sov->list_mode = LM_STARTED;
 287    }
 288}
 289
 290static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
 291{
 292    StringOutputVisitor *sov = to_sov(v);
 293    GenericList *ret = tail->next;
 294
 295    if (ret && !ret->next) {
 296        sov->list_mode = LM_END;
 297    }
 298    return ret;
 299}
 300
 301static void end_list(Visitor *v, void **obj)
 302{
 303    StringOutputVisitor *sov = to_sov(v);
 304
 305    assert(sov->list == obj);
 306    assert(sov->list_mode == LM_STARTED ||
 307           sov->list_mode == LM_END ||
 308           sov->list_mode == LM_NONE ||
 309           sov->list_mode == LM_IN_PROGRESS);
 310    sov->list_mode = LM_NONE;
 311}
 312
 313static void string_output_complete(Visitor *v, void *opaque)
 314{
 315    StringOutputVisitor *sov = to_sov(v);
 316
 317    assert(opaque == sov->result);
 318    *sov->result = g_string_free(sov->string, false);
 319    sov->string = NULL;
 320}
 321
 322static void free_range(void *range, void *dummy)
 323{
 324    g_free(range);
 325}
 326
 327static void string_output_free(Visitor *v)
 328{
 329    StringOutputVisitor *sov = to_sov(v);
 330
 331    if (sov->string) {
 332        g_string_free(sov->string, true);
 333    }
 334
 335    g_list_foreach(sov->ranges, free_range, NULL);
 336    g_list_free(sov->ranges);
 337    g_free(sov);
 338}
 339
 340Visitor *string_output_visitor_new(bool human, char **result)
 341{
 342    StringOutputVisitor *v;
 343
 344    v = g_malloc0(sizeof(*v));
 345
 346    v->string = g_string_new(NULL);
 347    v->human = human;
 348    v->result = result;
 349    *result = NULL;
 350
 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.type_null = print_type_null;
 359    v->visitor.start_list = start_list;
 360    v->visitor.next_list = next_list;
 361    v->visitor.end_list = end_list;
 362    v->visitor.complete = string_output_complete;
 363    v->visitor.free = string_output_free;
 364
 365    return &v->visitor;
 366}
 367