qemu/qobject/qnum.c
<<
>>
Prefs
   1/*
   2 * QNum Module
   3 *
   4 * Copyright (C) 2009 Red Hat Inc.
   5 *
   6 * Authors:
   7 *  Luiz Capitulino <lcapitulino@redhat.com>
   8 *  Anthony Liguori <aliguori@us.ibm.com>
   9 *  Marc-André Lureau <marcandre.lureau@redhat.com>
  10 *
  11 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
  12 * See the COPYING.LIB file in the top-level directory.
  13 */
  14
  15#include "qemu/osdep.h"
  16#include "qapi/qmp/qnum.h"
  17
  18/**
  19 * qnum_from_int(): Create a new QNum from an int64_t
  20 *
  21 * Return strong reference.
  22 */
  23QNum *qnum_from_int(int64_t value)
  24{
  25    QNum *qn = g_new(QNum, 1);
  26
  27    qobject_init(QOBJECT(qn), QTYPE_QNUM);
  28    qn->kind = QNUM_I64;
  29    qn->u.i64 = value;
  30
  31    return qn;
  32}
  33
  34/**
  35 * qnum_from_uint(): Create a new QNum from an uint64_t
  36 *
  37 * Return strong reference.
  38 */
  39QNum *qnum_from_uint(uint64_t value)
  40{
  41    QNum *qn = g_new(QNum, 1);
  42
  43    qobject_init(QOBJECT(qn), QTYPE_QNUM);
  44    qn->kind = QNUM_U64;
  45    qn->u.u64 = value;
  46
  47    return qn;
  48}
  49
  50/**
  51 * qnum_from_double(): Create a new QNum from a double
  52 *
  53 * Return strong reference.
  54 */
  55QNum *qnum_from_double(double value)
  56{
  57    QNum *qn = g_new(QNum, 1);
  58
  59    qobject_init(QOBJECT(qn), QTYPE_QNUM);
  60    qn->kind = QNUM_DOUBLE;
  61    qn->u.dbl = value;
  62
  63    return qn;
  64}
  65
  66/**
  67 * qnum_get_try_int(): Get an integer representation of the number
  68 *
  69 * Return true on success.
  70 */
  71bool qnum_get_try_int(const QNum *qn, int64_t *val)
  72{
  73    switch (qn->kind) {
  74    case QNUM_I64:
  75        *val = qn->u.i64;
  76        return true;
  77    case QNUM_U64:
  78        if (qn->u.u64 > INT64_MAX) {
  79            return false;
  80        }
  81        *val = qn->u.u64;
  82        return true;
  83    case QNUM_DOUBLE:
  84        return false;
  85    }
  86
  87    assert(0);
  88    return false;
  89}
  90
  91/**
  92 * qnum_get_int(): Get an integer representation of the number
  93 *
  94 * assert() on failure.
  95 */
  96int64_t qnum_get_int(const QNum *qn)
  97{
  98    int64_t val;
  99    bool success = qnum_get_try_int(qn, &val);
 100    assert(success);
 101    return val;
 102}
 103
 104/**
 105 * qnum_get_uint(): Get an unsigned integer from the number
 106 *
 107 * Return true on success.
 108 */
 109bool qnum_get_try_uint(const QNum *qn, uint64_t *val)
 110{
 111    switch (qn->kind) {
 112    case QNUM_I64:
 113        if (qn->u.i64 < 0) {
 114            return false;
 115        }
 116        *val = qn->u.i64;
 117        return true;
 118    case QNUM_U64:
 119        *val = qn->u.u64;
 120        return true;
 121    case QNUM_DOUBLE:
 122        return false;
 123    }
 124
 125    assert(0);
 126    return false;
 127}
 128
 129/**
 130 * qnum_get_uint(): Get an unsigned integer from the number
 131 *
 132 * assert() on failure.
 133 */
 134uint64_t qnum_get_uint(const QNum *qn)
 135{
 136    uint64_t val;
 137    bool success = qnum_get_try_uint(qn, &val);
 138    assert(success);
 139    return val;
 140}
 141
 142/**
 143 * qnum_get_double(): Get a float representation of the number
 144 *
 145 * qnum_get_double() loses precision for integers beyond 53 bits.
 146 */
 147double qnum_get_double(QNum *qn)
 148{
 149    switch (qn->kind) {
 150    case QNUM_I64:
 151        return qn->u.i64;
 152    case QNUM_U64:
 153        return qn->u.u64;
 154    case QNUM_DOUBLE:
 155        return qn->u.dbl;
 156    }
 157
 158    assert(0);
 159    return 0.0;
 160}
 161
 162char *qnum_to_string(QNum *qn)
 163{
 164    char *buffer;
 165    int len;
 166
 167    switch (qn->kind) {
 168    case QNUM_I64:
 169        return g_strdup_printf("%" PRId64, qn->u.i64);
 170    case QNUM_U64:
 171        return g_strdup_printf("%" PRIu64, qn->u.u64);
 172    case QNUM_DOUBLE:
 173        /* FIXME: snprintf() is locale dependent; but JSON requires
 174         * numbers to be formatted as if in the C locale. Dependence
 175         * on C locale is a pervasive issue in QEMU. */
 176        /* FIXME: This risks printing Inf or NaN, which are not valid
 177         * JSON values. */
 178        /* FIXME: the default precision of 6 for %f often causes
 179         * rounding errors; we should be using DBL_DECIMAL_DIG (17),
 180         * and only rounding to a shorter number if the result would
 181         * still produce the same floating point value.  */
 182        buffer = g_strdup_printf("%f" , qn->u.dbl);
 183        len = strlen(buffer);
 184        while (len > 0 && buffer[len - 1] == '0') {
 185            len--;
 186        }
 187
 188        if (len && buffer[len - 1] == '.') {
 189            buffer[len - 1] = 0;
 190        } else {
 191            buffer[len] = 0;
 192        }
 193
 194        return buffer;
 195    }
 196
 197    assert(0);
 198    return NULL;
 199}
 200
 201/**
 202 * qnum_is_equal(): Test whether the two QNums are equal
 203 *
 204 * Negative integers are never considered equal to unsigned integers,
 205 * but positive integers in the range [0, INT64_MAX] are considered
 206 * equal independently of whether the QNum's kind is i64 or u64.
 207 *
 208 * Doubles are never considered equal to integers.
 209 */
 210bool qnum_is_equal(const QObject *x, const QObject *y)
 211{
 212    QNum *num_x = qobject_to(QNum, x);
 213    QNum *num_y = qobject_to(QNum, y);
 214
 215    switch (num_x->kind) {
 216    case QNUM_I64:
 217        switch (num_y->kind) {
 218        case QNUM_I64:
 219            /* Comparison in native int64_t type */
 220            return num_x->u.i64 == num_y->u.i64;
 221        case QNUM_U64:
 222            /* Implicit conversion of x to uin64_t, so we have to
 223             * check its sign before */
 224            return num_x->u.i64 >= 0 && num_x->u.i64 == num_y->u.u64;
 225        case QNUM_DOUBLE:
 226            return false;
 227        }
 228        abort();
 229    case QNUM_U64:
 230        switch (num_y->kind) {
 231        case QNUM_I64:
 232            return qnum_is_equal(y, x);
 233        case QNUM_U64:
 234            /* Comparison in native uint64_t type */
 235            return num_x->u.u64 == num_y->u.u64;
 236        case QNUM_DOUBLE:
 237            return false;
 238        }
 239        abort();
 240    case QNUM_DOUBLE:
 241        switch (num_y->kind) {
 242        case QNUM_I64:
 243        case QNUM_U64:
 244            return false;
 245        case QNUM_DOUBLE:
 246            /* Comparison in native double type */
 247            return num_x->u.dbl == num_y->u.dbl;
 248        }
 249        abort();
 250    }
 251
 252    abort();
 253}
 254
 255/**
 256 * qnum_destroy_obj(): Free all memory allocated by a
 257 * QNum object
 258 */
 259void qnum_destroy_obj(QObject *obj)
 260{
 261    assert(obj != NULL);
 262    g_free(qobject_to(QNum, obj));
 263}
 264