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 "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 bool 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 true;
 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 true;
 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    return true;
 202}
 203
 204static bool print_type_uint64(Visitor *v, const char *name, uint64_t *obj,
 205                             Error **errp)
 206{
 207    /* FIXME: print_type_int64 mishandles values over INT64_MAX */
 208    int64_t i = *obj;
 209    return print_type_int64(v, name, &i, errp);
 210}
 211
 212static bool print_type_size(Visitor *v, const char *name, uint64_t *obj,
 213                            Error **errp)
 214{
 215    StringOutputVisitor *sov = to_sov(v);
 216    uint64_t val;
 217    char *out, *psize;
 218
 219    if (!sov->human) {
 220        out = g_strdup_printf("%"PRIu64, *obj);
 221        string_output_set(sov, out);
 222        return true;
 223    }
 224
 225    val = *obj;
 226    psize = size_to_str(val);
 227    out = g_strdup_printf("%"PRIu64" (%s)", val, psize);
 228    string_output_set(sov, out);
 229
 230    g_free(psize);
 231    return true;
 232}
 233
 234static bool print_type_bool(Visitor *v, const char *name, bool *obj,
 235                            Error **errp)
 236{
 237    StringOutputVisitor *sov = to_sov(v);
 238    string_output_set(sov, g_strdup(*obj ? "true" : "false"));
 239    return true;
 240}
 241
 242static bool print_type_str(Visitor *v, const char *name, char **obj,
 243                           Error **errp)
 244{
 245    StringOutputVisitor *sov = to_sov(v);
 246    char *out;
 247
 248    if (sov->human) {
 249        out = *obj ? g_strdup_printf("\"%s\"", *obj) : g_strdup("<null>");
 250    } else {
 251        out = g_strdup(*obj ? *obj : "");
 252    }
 253    string_output_set(sov, out);
 254    return true;
 255}
 256
 257static bool print_type_number(Visitor *v, const char *name, double *obj,
 258                              Error **errp)
 259{
 260    StringOutputVisitor *sov = to_sov(v);
 261    string_output_set(sov, g_strdup_printf("%.17g", *obj));
 262    return true;
 263}
 264
 265static bool print_type_null(Visitor *v, const char *name, QNull **obj,
 266                            Error **errp)
 267{
 268    StringOutputVisitor *sov = to_sov(v);
 269    char *out;
 270
 271    if (sov->human) {
 272        out = g_strdup("<null>");
 273    } else {
 274        out = g_strdup("");
 275    }
 276    string_output_set(sov, out);
 277    return true;
 278}
 279
 280static bool
 281start_list(Visitor *v, const char *name, GenericList **list, size_t size,
 282           Error **errp)
 283{
 284    StringOutputVisitor *sov = to_sov(v);
 285
 286    /* we can't traverse a list in a list */
 287    assert(sov->list_mode == LM_NONE);
 288    /* We don't support visits without a list */
 289    assert(list);
 290    sov->list = list;
 291    /* List handling is only needed if there are at least two elements */
 292    if (*list && (*list)->next) {
 293        sov->list_mode = LM_STARTED;
 294    }
 295    return true;
 296}
 297
 298static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
 299{
 300    StringOutputVisitor *sov = to_sov(v);
 301    GenericList *ret = tail->next;
 302
 303    if (ret && !ret->next) {
 304        sov->list_mode = LM_END;
 305    }
 306    return ret;
 307}
 308
 309static void end_list(Visitor *v, void **obj)
 310{
 311    StringOutputVisitor *sov = to_sov(v);
 312
 313    assert(sov->list == obj);
 314    assert(sov->list_mode == LM_STARTED ||
 315           sov->list_mode == LM_END ||
 316           sov->list_mode == LM_NONE ||
 317           sov->list_mode == LM_IN_PROGRESS);
 318    sov->list_mode = LM_NONE;
 319}
 320
 321static void string_output_complete(Visitor *v, void *opaque)
 322{
 323    StringOutputVisitor *sov = to_sov(v);
 324
 325    assert(opaque == sov->result);
 326    *sov->result = g_string_free(sov->string, false);
 327    sov->string = NULL;
 328}
 329
 330static void free_range(void *range, void *dummy)
 331{
 332    g_free(range);
 333}
 334
 335static void string_output_free(Visitor *v)
 336{
 337    StringOutputVisitor *sov = to_sov(v);
 338
 339    if (sov->string) {
 340        g_string_free(sov->string, true);
 341    }
 342
 343    g_list_foreach(sov->ranges, free_range, NULL);
 344    g_list_free(sov->ranges);
 345    g_free(sov);
 346}
 347
 348Visitor *string_output_visitor_new(bool human, char **result)
 349{
 350    StringOutputVisitor *v;
 351
 352    v = g_malloc0(sizeof(*v));
 353
 354    v->string = g_string_new(NULL);
 355    v->human = human;
 356    v->result = result;
 357    *result = NULL;
 358
 359    v->visitor.type = VISITOR_OUTPUT;
 360    v->visitor.type_int64 = print_type_int64;
 361    v->visitor.type_uint64 = print_type_uint64;
 362    v->visitor.type_size = print_type_size;
 363    v->visitor.type_bool = print_type_bool;
 364    v->visitor.type_str = print_type_str;
 365    v->visitor.type_number = print_type_number;
 366    v->visitor.type_null = print_type_null;
 367    v->visitor.start_list = start_list;
 368    v->visitor.next_list = next_list;
 369    v->visitor.end_list = end_list;
 370    v->visitor.complete = string_output_complete;
 371    v->visitor.free = string_output_free;
 372
 373    return &v->visitor;
 374}
 375