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/cutils.h"
  15#include "qapi/string-output-visitor.h"
  16#include "qapi/visitor-impl.h"
  17#include <math.h>
  18#include "qemu/range.h"
  19
  20enum ListMode {
  21    LM_NONE,             /* not traversing a list of repeated options */
  22    LM_STARTED,          /* next_list() ready to be called */
  23
  24    LM_IN_PROGRESS,      /* next_list() has been called.
  25                          *
  26                          * Generating the next list link will consume the most
  27                          * recently parsed QemuOpt instance of the repeated
  28                          * option.
  29                          *
  30                          * Parsing a value into the list link will examine the
  31                          * next QemuOpt instance of the repeated option, and
  32                          * possibly enter LM_SIGNED_INTERVAL or
  33                          * LM_UNSIGNED_INTERVAL.
  34                          */
  35
  36    LM_SIGNED_INTERVAL,  /* next_list() has been called.
  37                          *
  38                          * Generating the next list link will consume the most
  39                          * recently stored element from the signed interval,
  40                          * parsed from the most recent QemuOpt instance of the
  41                          * repeated option. This may consume QemuOpt itself
  42                          * and return to LM_IN_PROGRESS.
  43                          *
  44                          * Parsing a value into the list link will store the
  45                          * next element of the signed interval.
  46                          */
  47
  48    LM_UNSIGNED_INTERVAL,/* Same as above, only for an unsigned interval. */
  49
  50    LM_END,              /* next_list() called, about to see last element. */
  51};
  52
  53typedef enum ListMode ListMode;
  54
  55struct StringOutputVisitor
  56{
  57    Visitor visitor;
  58    bool human;
  59    GString *string;
  60    char **result;
  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
  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
  88    range_set_bounds(r, a, a);
  89    sov->ranges = range_list_insert(sov->ranges, r);
  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
  97    range_set_bounds(r, s, e);
  98    sov->ranges = range_list_insert(sov->ranges, r);
  99}
 100
 101static 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
 125static bool 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 true;
 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 true;
 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    return true;
 201}
 202
 203static bool print_type_uint64(Visitor *v, const char *name, uint64_t *obj,
 204                             Error **errp)
 205{
 206    /* FIXME: print_type_int64 mishandles values over INT64_MAX */
 207    int64_t i = *obj;
 208    return print_type_int64(v, name, &i, errp);
 209}
 210
 211static bool print_type_size(Visitor *v, const char *name, uint64_t *obj,
 212                            Error **errp)
 213{
 214    StringOutputVisitor *sov = to_sov(v);
 215    uint64_t val;
 216    char *out, *psize;
 217
 218    if (!sov->human) {
 219        out = g_strdup_printf("%"PRIu64, *obj);
 220        string_output_set(sov, out);
 221        return true;
 222    }
 223
 224    val = *obj;
 225    psize = size_to_str(val);
 226    out = g_strdup_printf("%"PRIu64" (%s)", val, psize);
 227    string_output_set(sov, out);
 228
 229    g_free(psize);
 230    return true;
 231}
 232
 233static bool print_type_bool(Visitor *v, const char *name, bool *obj,
 234                            Error **errp)
 235{
 236    StringOutputVisitor *sov = to_sov(v);
 237    string_output_set(sov, g_strdup(*obj ? "true" : "false"));
 238    return true;
 239}
 240
 241static bool print_type_str(Visitor *v, const char *name, char **obj,
 242                           Error **errp)
 243{
 244    StringOutputVisitor *sov = to_sov(v);
 245    char *out;
 246
 247    if (sov->human) {
 248        out = *obj ? g_strdup_printf("\"%s\"", *obj) : g_strdup("<null>");
 249    } else {
 250        out = g_strdup(*obj ? *obj : "");
 251    }
 252    string_output_set(sov, out);
 253    return true;
 254}
 255
 256static bool print_type_number(Visitor *v, const char *name, double *obj,
 257                              Error **errp)
 258{
 259    StringOutputVisitor *sov = to_sov(v);
 260    string_output_set(sov, g_strdup_printf("%.17g", *obj));
 261    return true;
 262}
 263
 264static bool print_type_null(Visitor *v, const char *name, QNull **obj,
 265                            Error **errp)
 266{
 267    StringOutputVisitor *sov = to_sov(v);
 268    char *out;
 269
 270    if (sov->human) {
 271        out = g_strdup("<null>");
 272    } else {
 273        out = g_strdup("");
 274    }
 275    string_output_set(sov, out);
 276    return true;
 277}
 278
 279static bool
 280start_list(Visitor *v, const char *name, GenericList **list, size_t size,
 281           Error **errp)
 282{
 283    StringOutputVisitor *sov = to_sov(v);
 284
 285    /* we can't traverse a list in a list */
 286    assert(sov->list_mode == LM_NONE);
 287    /* We don't support visits without a list */
 288    assert(list);
 289    sov->list = list;
 290    /* List handling is only needed if there are at least two elements */
 291    if (*list && (*list)->next) {
 292        sov->list_mode = LM_STARTED;
 293    }
 294    return true;
 295}
 296
 297static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
 298{
 299    StringOutputVisitor *sov = to_sov(v);
 300    GenericList *ret = tail->next;
 301
 302    if (ret && !ret->next) {
 303        sov->list_mode = LM_END;
 304    }
 305    return ret;
 306}
 307
 308static void end_list(Visitor *v, void **obj)
 309{
 310    StringOutputVisitor *sov = to_sov(v);
 311
 312    assert(sov->list == obj);
 313    assert(sov->list_mode == LM_STARTED ||
 314           sov->list_mode == LM_END ||
 315           sov->list_mode == LM_NONE ||
 316           sov->list_mode == LM_IN_PROGRESS);
 317    sov->list_mode = LM_NONE;
 318}
 319
 320static void string_output_complete(Visitor *v, void *opaque)
 321{
 322    StringOutputVisitor *sov = to_sov(v);
 323
 324    assert(opaque == sov->result);
 325    *sov->result = g_string_free(sov->string, false);
 326    sov->string = NULL;
 327}
 328
 329static void free_range(void *range, void *dummy)
 330{
 331    g_free(range);
 332}
 333
 334static void string_output_free(Visitor *v)
 335{
 336    StringOutputVisitor *sov = to_sov(v);
 337
 338    if (sov->string) {
 339        g_string_free(sov->string, true);
 340    }
 341
 342    g_list_foreach(sov->ranges, free_range, NULL);
 343    g_list_free(sov->ranges);
 344    g_free(sov);
 345}
 346
 347Visitor *string_output_visitor_new(bool human, char **result)
 348{
 349    StringOutputVisitor *v;
 350
 351    v = g_malloc0(sizeof(*v));
 352
 353    v->string = g_string_new(NULL);
 354    v->human = human;
 355    v->result = result;
 356    *result = NULL;
 357
 358    v->visitor.type = VISITOR_OUTPUT;
 359    v->visitor.type_int64 = print_type_int64;
 360    v->visitor.type_uint64 = print_type_uint64;
 361    v->visitor.type_size = print_type_size;
 362    v->visitor.type_bool = print_type_bool;
 363    v->visitor.type_str = print_type_str;
 364    v->visitor.type_number = print_type_number;
 365    v->visitor.type_null = print_type_null;
 366    v->visitor.start_list = start_list;
 367    v->visitor.next_list = next_list;
 368    v->visitor.end_list = end_list;
 369    v->visitor.complete = string_output_complete;
 370    v->visitor.free = string_output_free;
 371
 372    return &v->visitor;
 373}
 374