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