Import QEMU upstream snapshot d2e570c

Upstream: https://gitlab.com/qemu-project/qemu.git

Upstream-Commit: d2e570cc0f97b936902a5b1b86b73c0f5998b475
This commit is contained in:
2026-08-31 02:15:30 +02:00
commit cf256aa081
11315 changed files with 3598369 additions and 0 deletions
+738
View File
@@ -0,0 +1,738 @@
/*
* Special QDict functions used by the block layer
*
* Copyright (c) 2013-2018 Red Hat, Inc.
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING.LIB file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "block/qdict.h"
#include "qobject/qbool.h"
#include "qobject/qlist.h"
#include "qobject/qnum.h"
#include "qobject/qstring.h"
#include "qapi/qobject-input-visitor.h"
#include "qemu/cutils.h"
#include "qapi/error.h"
/**
* qdict_copy_default(): If no entry mapped by 'key' exists in 'dst' yet, the
* value of 'key' in 'src' is copied there (and the refcount increased
* accordingly).
*/
void qdict_copy_default(QDict *dst, QDict *src, const char *key)
{
QObject *val;
if (qdict_haskey(dst, key)) {
return;
}
val = qdict_get(src, key);
if (val) {
qdict_put_obj(dst, key, qobject_ref(val));
}
}
/**
* qdict_set_default_str(): If no entry mapped by 'key' exists in 'dst' yet, a
* new QString initialised by 'val' is put there.
*/
void qdict_set_default_str(QDict *dst, const char *key, const char *val)
{
if (qdict_haskey(dst, key)) {
return;
}
qdict_put_str(dst, key, val);
}
static void qdict_flatten_qdict(QDict *qdict, QDict *target,
const char *prefix);
static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix)
{
QObject *value;
const QListEntry *entry;
QDict *dict_val;
QList *list_val;
char *new_key;
int i;
/* This function is never called with prefix == NULL, i.e., it is always
* called from within qdict_flatten_q(list|dict)(). Therefore, it does not
* need to remove list entries during the iteration (the whole list will be
* deleted eventually anyway from qdict_flatten_qdict()). */
assert(prefix);
entry = qlist_first(qlist);
for (i = 0; entry; entry = qlist_next(entry), i++) {
value = qlist_entry_obj(entry);
dict_val = qobject_to(QDict, value);
list_val = qobject_to(QList, value);
new_key = g_strdup_printf("%s.%i", prefix, i);
/*
* Flatten non-empty QDict and QList recursively into @target,
* copy other objects to @target
*/
if (dict_val && qdict_size(dict_val)) {
qdict_flatten_qdict(dict_val, target, new_key);
} else if (list_val && !qlist_empty(list_val)) {
qdict_flatten_qlist(list_val, target, new_key);
} else {
qdict_put_obj(target, new_key, qobject_ref(value));
}
g_free(new_key);
}
}
static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix)
{
QObject *value;
const QDictEntry *entry, *next;
QDict *dict_val;
QList *list_val;
char *key, *new_key;
entry = qdict_first(qdict);
while (entry != NULL) {
next = qdict_next(qdict, entry);
value = qdict_entry_value(entry);
dict_val = qobject_to(QDict, value);
list_val = qobject_to(QList, value);
if (prefix) {
key = new_key = g_strdup_printf("%s.%s", prefix, entry->key);
} else {
key = entry->key;
new_key = NULL;
}
/*
* Flatten non-empty QDict and QList recursively into @target,
* copy other objects to @target.
* On the root level (if @qdict == @target), remove flattened
* nested QDicts and QLists from @qdict.
*
* (Note that we do not need to remove entries from nested
* dicts or lists. Their reference count is decremented on
* the root level, so there are no leaks. In fact, if they
* have a reference count greater than one, we are probably
* well advised not to modify them altogether.)
*/
if (dict_val && qdict_size(dict_val)) {
qdict_flatten_qdict(dict_val, target, key);
if (target == qdict) {
qdict_del(qdict, entry->key);
}
} else if (list_val && !qlist_empty(list_val)) {
qdict_flatten_qlist(list_val, target, key);
if (target == qdict) {
qdict_del(qdict, entry->key);
}
} else if (target != qdict) {
qdict_put_obj(target, key, qobject_ref(value));
}
g_free(new_key);
entry = next;
}
}
/**
* qdict_flatten(): For each nested non-empty QDict with key x, all
* fields with key y are moved to this QDict and their key is renamed
* to "x.y". For each nested non-empty QList with key x, the field at
* index y is moved to this QDict with the key "x.y" (i.e., the
* reverse of what qdict_array_split() does).
* This operation is applied recursively for nested QDicts and QLists.
*/
void qdict_flatten(QDict *qdict)
{
qdict_flatten_qdict(qdict, qdict, NULL);
}
/* extract all the src QDict entries starting by start into dst.
* If dst is NULL then the entries are simply removed from src. */
void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start)
{
const QDictEntry *entry, *next;
const char *p;
if (dst) {
*dst = qdict_new();
}
entry = qdict_first(src);
while (entry != NULL) {
next = qdict_next(src, entry);
if (strstart(entry->key, start, &p)) {
if (dst) {
qdict_put_obj(*dst, p, qobject_ref(entry->value));
}
qdict_del(src, entry->key);
}
entry = next;
}
}
static int qdict_count_prefixed_entries(const QDict *src, const char *start)
{
const QDictEntry *entry;
int count = 0;
for (entry = qdict_first(src); entry; entry = qdict_next(src, entry)) {
if (strstart(entry->key, start, NULL)) {
if (count == INT_MAX) {
return -ERANGE;
}
count++;
}
}
return count;
}
/**
* qdict_array_split(): This function moves array-like elements of a QDict into
* a new QList. Every entry in the original QDict with a key "%u" or one
* prefixed "%u.", where %u designates an unsigned integer starting at 0 and
* incrementally counting up, will be moved to a new QDict at index %u in the
* output QList with the key prefix removed, if that prefix is "%u.". If the
* whole key is just "%u", the whole QObject will be moved unchanged without
* creating a new QDict. The function terminates when there is no entry in the
* QDict with a prefix directly (incrementally) following the last one; it also
* returns if there are both entries with "%u" and "%u." for the same index %u.
* Example: {"0.a": 42, "0.b": 23, "1.x": 0, "4.y": 1, "o.o": 7, "2": 66}
* (or {"1.x": 0, "4.y": 1, "0.a": 42, "o.o": 7, "0.b": 23, "2": 66})
* => [{"a": 42, "b": 23}, {"x": 0}, 66]
* and {"4.y": 1, "o.o": 7} (remainder of the old QDict)
*/
void qdict_array_split(QDict *src, QList **dst)
{
unsigned i;
*dst = qlist_new();
for (i = 0; i < UINT_MAX; i++) {
QObject *subqobj;
bool is_subqdict;
QDict *subqdict;
char indexstr[32], prefix[32];
size_t snprintf_ret;
snprintf_ret = snprintf(indexstr, 32, "%u", i);
assert(snprintf_ret < 32);
subqobj = qdict_get(src, indexstr);
snprintf_ret = snprintf(prefix, 32, "%u.", i);
assert(snprintf_ret < 32);
/* Overflow is the same as positive non-zero results */
is_subqdict = qdict_count_prefixed_entries(src, prefix);
/*
* There may be either a single subordinate object (named
* "%u") or multiple objects (each with a key prefixed "%u."),
* but not both.
*/
if (!subqobj == !is_subqdict) {
break;
}
if (is_subqdict) {
qdict_extract_subqdict(src, &subqdict, prefix);
assert(qdict_size(subqdict) > 0);
qlist_append_obj(*dst, QOBJECT(subqdict));
} else {
qobject_ref(subqobj);
qdict_del(src, indexstr);
qlist_append_obj(*dst, subqobj);
}
}
}
/**
* qdict_split_flat_key:
* @key: the key string to split
* @prefix: non-NULL pointer to hold extracted prefix
* @suffix: non-NULL pointer to remaining suffix
*
* Given a flattened key such as 'foo.0.bar', split it into two parts
* at the first '.' separator. Allows double dot ('..') to escape the
* normal separator.
*
* e.g.
* 'foo.0.bar' -> prefix='foo' and suffix='0.bar'
* 'foo..0.bar' -> prefix='foo.0' and suffix='bar'
*
* The '..' sequence will be unescaped in the returned 'prefix'
* string. The 'suffix' string will be left in escaped format, so it
* can be fed back into the qdict_split_flat_key() key as the input
* later.
*
* The caller is responsible for freeing the string returned in @prefix
* using g_free().
*/
static void qdict_split_flat_key(const char *key, char **prefix,
const char **suffix)
{
const char *separator;
size_t i, j;
/* Find first '.' separator, but if there is a pair '..'
* that acts as an escape, so skip over '..' */
separator = NULL;
do {
if (separator) {
separator += 2;
} else {
separator = key;
}
separator = strchr(separator, '.');
} while (separator && separator[1] == '.');
if (separator) {
*prefix = g_strndup(key, separator - key);
*suffix = separator + 1;
} else {
*prefix = g_strdup(key);
*suffix = NULL;
}
/* Unescape the '..' sequence into '.' */
for (i = 0, j = 0; (*prefix)[i] != '\0'; i++, j++) {
if ((*prefix)[i] == '.') {
assert((*prefix)[i + 1] == '.');
i++;
}
(*prefix)[j] = (*prefix)[i];
}
(*prefix)[j] = '\0';
}
/**
* qdict_is_list:
* @maybe_list: dict to check if keys represent list elements.
*
* Determine whether all keys in @maybe_list are valid list elements.
* If @maybe_list is non-zero in length and all the keys look like
* valid list indexes, this will return 1. If @maybe_list is zero
* length or all keys are non-numeric then it will return 0 to indicate
* it is a normal qdict. If there is a mix of numeric and non-numeric
* keys, or the list indexes are non-contiguous, an error is reported.
*
* Returns: 1 if a valid list, 0 if a dict, -1 on error
*/
static int qdict_is_list(QDict *maybe_list, Error **errp)
{
const QDictEntry *ent;
ssize_t len = 0;
ssize_t max = -1;
int is_list = -1;
int64_t val;
for (ent = qdict_first(maybe_list); ent != NULL;
ent = qdict_next(maybe_list, ent)) {
int is_index = !qemu_strtoi64(ent->key, NULL, 10, &val);
if (is_list == -1) {
is_list = is_index;
}
if (is_index != is_list) {
error_setg(errp, "Cannot mix list and non-list keys");
return -1;
}
if (is_index) {
len++;
if (val > max) {
max = val;
}
}
}
if (is_list == -1) {
assert(!qdict_size(maybe_list));
is_list = 0;
}
/* NB this isn't a perfect check - e.g. it won't catch
* a list containing '1', '+1', '01', '3', but that
* does not matter - we've still proved that the
* input is a list. It is up the caller to do a
* stricter check if desired */
if (len != (max + 1)) {
error_setg(errp, "List indices are not contiguous, "
"saw %zd elements but %zd largest index",
len, max);
return -1;
}
return is_list;
}
/**
* qdict_crumple:
* @src: the original flat dictionary (only scalar values) to crumple
*
* Takes a flat dictionary whose keys use '.' separator to indicate
* nesting, and values are scalars, empty dictionaries or empty lists,
* and crumples it into a nested structure.
*
* To include a literal '.' in a key name, it must be escaped as '..'
*
* For example, an input of:
*
* { 'foo.0.bar': 'one', 'foo.0.wizz': '1',
* 'foo.1.bar': 'two', 'foo.1.wizz': '2' }
*
* will result in an output of:
*
* {
* 'foo': [
* { 'bar': 'one', 'wizz': '1' },
* { 'bar': 'two', 'wizz': '2' }
* ],
* }
*
* The following scenarios in the input dict will result in an
* error being returned:
*
* - Any values in @src are non-scalar types
* - If keys in @src imply that a particular level is both a
* list and a dict. e.g., "foo.0.bar" and "foo.eek.bar".
* - If keys in @src imply that a particular level is a list,
* but the indices are non-contiguous. e.g. "foo.0.bar" and
* "foo.2.bar" without any "foo.1.bar" present.
* - If keys in @src represent list indexes, but are not in
* the "%zu" format. e.g. "foo.+0.bar"
*
* Returns: either a QDict or QList for the nested data structure, or NULL
* on error
*/
QObject *qdict_crumple(const QDict *src, Error **errp)
{
const QDictEntry *ent;
QDict *two_level, *multi_level = NULL, *child_dict;
QDict *dict_val;
QList *list_val;
QObject *dst = NULL, *child;
size_t i;
char *prefix = NULL;
const char *suffix = NULL;
int is_list;
two_level = qdict_new();
/* Step 1: split our totally flat dict into a two level dict */
for (ent = qdict_first(src); ent != NULL; ent = qdict_next(src, ent)) {
dict_val = qobject_to(QDict, ent->value);
list_val = qobject_to(QList, ent->value);
if ((dict_val && qdict_size(dict_val))
|| (list_val && !qlist_empty(list_val))) {
error_setg(errp, "Value %s is not flat", ent->key);
goto error;
}
qdict_split_flat_key(ent->key, &prefix, &suffix);
child = qdict_get(two_level, prefix);
child_dict = qobject_to(QDict, child);
if (child) {
/*
* If @child_dict, then all previous keys with this prefix
* had a suffix. If @suffix, this one has one as well,
* and we're good, else there's a clash.
*/
if (!child_dict || !suffix) {
error_setg(errp, "Cannot mix scalar and non-scalar keys");
goto error;
}
}
if (suffix) {
if (!child_dict) {
child_dict = qdict_new();
qdict_put(two_level, prefix, child_dict);
}
qdict_put_obj(child_dict, suffix, qobject_ref(ent->value));
} else {
qdict_put_obj(two_level, prefix, qobject_ref(ent->value));
}
g_free(prefix);
prefix = NULL;
}
/* Step 2: optionally process the two level dict recursively
* into a multi-level dict */
multi_level = qdict_new();
for (ent = qdict_first(two_level); ent != NULL;
ent = qdict_next(two_level, ent)) {
dict_val = qobject_to(QDict, ent->value);
if (dict_val && qdict_size(dict_val)) {
child = qdict_crumple(dict_val, errp);
if (!child) {
goto error;
}
qdict_put_obj(multi_level, ent->key, child);
} else {
qdict_put_obj(multi_level, ent->key, qobject_ref(ent->value));
}
}
qobject_unref(two_level);
two_level = NULL;
/* Step 3: detect if we need to turn our dict into list */
is_list = qdict_is_list(multi_level, errp);
if (is_list < 0) {
goto error;
}
if (is_list) {
dst = QOBJECT(qlist_new());
for (i = 0; i < qdict_size(multi_level); i++) {
char *key = g_strdup_printf("%zu", i);
child = qdict_get(multi_level, key);
g_free(key);
if (!child) {
error_setg(errp, "Missing list index %zu", i);
goto error;
}
qlist_append_obj(qobject_to(QList, dst), qobject_ref(child));
}
qobject_unref(multi_level);
multi_level = NULL;
} else {
dst = QOBJECT(multi_level);
}
return dst;
error:
g_free(prefix);
qobject_unref(multi_level);
qobject_unref(two_level);
qobject_unref(dst);
return NULL;
}
/**
* qdict_crumple_for_keyval_qiv:
* @src: the flat dictionary (only scalar values) to crumple
* @errp: location to store error
*
* Like qdict_crumple(), but additionally transforms scalar values so
* the result can be passed to qobject_input_visitor_new_keyval().
*
* The block subsystem uses this function to prepare its flat QDict
* with possibly confused scalar types for a visit. It should not be
* used for anything else, and it should go away once the block
* subsystem has been cleaned up.
*/
static QObject *qdict_crumple_for_keyval_qiv(QDict *src, Error **errp)
{
QDict *tmp = NULL;
char *buf;
const char *s;
const QDictEntry *ent;
QObject *dst;
for (ent = qdict_first(src); ent; ent = qdict_next(src, ent)) {
buf = NULL;
switch (qobject_type(ent->value)) {
case QTYPE_QNULL:
case QTYPE_QSTRING:
continue;
case QTYPE_QNUM:
s = buf = qnum_to_string(qobject_to(QNum, ent->value));
break;
case QTYPE_QDICT:
case QTYPE_QLIST:
/* @src isn't flat; qdict_crumple() will fail */
continue;
case QTYPE_QBOOL:
s = qbool_get_bool(qobject_to(QBool, ent->value))
? "on" : "off";
break;
default:
abort();
}
if (!tmp) {
tmp = qdict_clone_shallow(src);
}
qdict_put_str(tmp, ent->key, s);
g_free(buf);
}
dst = qdict_crumple(tmp ?: src, errp);
qobject_unref(tmp);
return dst;
}
/**
* qdict_array_entries(): Returns the number of direct array entries if the
* sub-QDict of src specified by the prefix in subqdict (or src itself for
* prefix == "") is valid as an array, i.e. the length of the created list if
* the sub-QDict would become empty after calling qdict_array_split() on it. If
* the array is not valid, -EINVAL is returned.
*/
int qdict_array_entries(QDict *src, const char *subqdict)
{
const QDictEntry *entry;
unsigned i;
unsigned entries = 0;
size_t subqdict_len = strlen(subqdict);
assert(!subqdict_len || subqdict[subqdict_len - 1] == '.');
/* qdict_array_split() loops until UINT_MAX, but as we want to return
* negative errors, we only have a signed return value here. Any additional
* entries will lead to -EINVAL. */
for (i = 0; i < INT_MAX; i++) {
QObject *subqobj;
int subqdict_entries;
char *prefix = g_strdup_printf("%s%u.", subqdict, i);
subqdict_entries = qdict_count_prefixed_entries(src, prefix);
/* Remove ending "." */
prefix[strlen(prefix) - 1] = 0;
subqobj = qdict_get(src, prefix);
g_free(prefix);
if (subqdict_entries < 0) {
return subqdict_entries;
}
/* There may be either a single subordinate object (named "%u") or
* multiple objects (each with a key prefixed "%u."), but not both. */
if (subqobj && subqdict_entries) {
return -EINVAL;
} else if (!subqobj && !subqdict_entries) {
break;
}
entries += subqdict_entries ? subqdict_entries : 1;
}
/* Consider everything handled that isn't part of the given sub-QDict */
for (entry = qdict_first(src); entry; entry = qdict_next(src, entry)) {
if (!strstart(qdict_entry_key(entry), subqdict, NULL)) {
entries++;
}
}
/* Anything left in the sub-QDict that wasn't handled? */
if (qdict_size(src) != entries) {
return -EINVAL;
}
return i;
}
/**
* qdict_join(): Absorb the src QDict into the dest QDict, that is, move all
* elements from src to dest.
*
* If an element from src has a key already present in dest, it will not be
* moved unless overwrite is true.
*
* If overwrite is true, the conflicting values in dest will be discarded and
* replaced by the corresponding values from src.
*
* Therefore, with overwrite being true, the src QDict will always be empty when
* this function returns. If overwrite is false, the src QDict will be empty
* iff there were no conflicts.
*/
void qdict_join(QDict *dest, QDict *src, bool overwrite)
{
const QDictEntry *entry, *next;
entry = qdict_first(src);
while (entry) {
next = qdict_next(src, entry);
if (overwrite || !qdict_haskey(dest, entry->key)) {
qdict_put_obj(dest, entry->key, qobject_ref(entry->value));
qdict_del(src, entry->key);
}
entry = next;
}
}
/**
* qdict_rename_keys(): Rename keys in qdict according to the replacements
* specified in the array renames. The array must be terminated by an entry
* with from = NULL.
*
* The renames are performed individually in the order of the array, so entries
* may be renamed multiple times and may or may not conflict depending on the
* order of the renames array.
*
* Returns true for success, false in error cases.
*/
bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp)
{
QObject *qobj;
while (renames->from) {
if (qdict_haskey(qdict, renames->from)) {
if (qdict_haskey(qdict, renames->to)) {
error_setg(errp, "'%s' and its alias '%s' can't be used at the "
"same time", renames->to, renames->from);
return false;
}
qobj = qdict_get(qdict, renames->from);
qdict_put_obj(qdict, renames->to, qobject_ref(qobj));
qdict_del(qdict, renames->from);
}
renames++;
}
return true;
}
/*
* Create a QObject input visitor for flat @qdict with possibly
* confused scalar types.
*
* The block subsystem uses this function to visit its flat QDict with
* possibly confused scalar types. It should not be used for anything
* else, and it should go away once the block subsystem has been
* cleaned up.
*/
Visitor *qobject_input_visitor_new_flat_confused(QDict *qdict,
Error **errp)
{
QObject *crumpled;
Visitor *v;
crumpled = qdict_crumple_for_keyval_qiv(qdict, errp);
if (!crumpled) {
return NULL;
}
v = qobject_input_visitor_new_keyval(crumpled);
qobject_unref(crumpled);
return v;
}
+368
View File
@@ -0,0 +1,368 @@
/*
* JSON lexer
*
* Copyright IBM, Corp. 2009
*
* Authors:
* Anthony Liguori <[email protected]>
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING.LIB file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "json-parser-int.h"
#define MAX_TOKEN_SIZE (64ULL << 20)
/*
* From RFC 8259 "The JavaScript Object Notation (JSON) Data
* Interchange Format", with [comments in brackets]:
*
* The set of tokens includes six structural characters, strings,
* numbers, and three literal names.
*
* These are the six structural characters:
*
* begin-array = ws %x5B ws ; [ left square bracket
* begin-object = ws %x7B ws ; { left curly bracket
* end-array = ws %x5D ws ; ] right square bracket
* end-object = ws %x7D ws ; } right curly bracket
* name-separator = ws %x3A ws ; : colon
* value-separator = ws %x2C ws ; , comma
*
* Insignificant whitespace is allowed before or after any of the six
* structural characters.
* [This lexer accepts it before or after any token, which is actually
* the same, as the grammar always has structural characters between
* other tokens.]
*
* ws = *(
* %x20 / ; Space
* %x09 / ; Horizontal tab
* %x0A / ; Line feed or New line
* %x0D ) ; Carriage return
*
* [...] three literal names:
* false null true
* [This lexer accepts [a-z]+, and leaves rejecting unknown literal
* names to the parser.]
*
* [Numbers:]
*
* number = [ minus ] int [ frac ] [ exp ]
* decimal-point = %x2E ; .
* digit1-9 = %x31-39 ; 1-9
* e = %x65 / %x45 ; e E
* exp = e [ minus / plus ] 1*DIGIT
* frac = decimal-point 1*DIGIT
* int = zero / ( digit1-9 *DIGIT )
* minus = %x2D ; -
* plus = %x2B ; +
* zero = %x30 ; 0
*
* [Strings:]
* string = quotation-mark *char quotation-mark
*
* char = unescaped /
* escape (
* %x22 / ; " quotation mark U+0022
* %x5C / ; \ reverse solidus U+005C
* %x2F / ; / solidus U+002F
* %x62 / ; b backspace U+0008
* %x66 / ; f form feed U+000C
* %x6E / ; n line feed U+000A
* %x72 / ; r carriage return U+000D
* %x74 / ; t tab U+0009
* %x75 4HEXDIG ) ; uXXXX U+XXXX
* escape = %x5C ; \
* quotation-mark = %x22 ; "
* unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
* [This lexer accepts any non-control character after escape, and
* leaves rejecting invalid ones to the parser.]
*
*
* Extensions over RFC 8259:
* - Extra escape sequence in strings:
* 0x27 (apostrophe) is recognized after escape, too
* - Single-quoted strings:
* Like double-quoted strings, except they're delimited by %x27
* (apostrophe) instead of %x22 (quotation mark), and can't contain
* unescaped apostrophe, but can contain unescaped quotation mark.
* - Interpolation, if enabled:
* The lexer accepts %[A-Za-z0-9]*, and leaves rejecting invalid
* ones to the parser.
*
* Note:
* - Input must be encoded in modified UTF-8.
* - Decoding and validating is left to the parser.
*/
enum json_lexer_state {
IN_RECOVERY = 1,
IN_DQ_STRING_ESCAPE,
IN_DQ_STRING,
IN_SQ_STRING_ESCAPE,
IN_SQ_STRING,
IN_ZERO,
IN_EXP_DIGITS,
IN_EXP_SIGN,
IN_EXP_E,
IN_MANTISSA,
IN_MANTISSA_DIGITS,
IN_DIGITS,
IN_SIGN,
IN_KEYWORD,
IN_INTERP,
IN_START,
IN_START_INTERP, /* must be IN_START + 1 */
};
QEMU_BUILD_BUG_ON(JSON_ERROR != 0);
QEMU_BUILD_BUG_ON(IN_RECOVERY != JSON_ERROR + 1);
QEMU_BUILD_BUG_ON((int)JSON_MIN <= (int)IN_START_INTERP);
QEMU_BUILD_BUG_ON(JSON_MAX >= 0x80);
QEMU_BUILD_BUG_ON(IN_START_INTERP != IN_START + 1);
#define LOOKAHEAD 0x80
#define TERMINAL(state) [0 ... 0xFF] = ((state) | LOOKAHEAD)
static const uint8_t json_lexer[][256] = {
/* Relies on default initialization to IN_ERROR! */
/* error recovery */
[IN_RECOVERY] = {
/*
* Skip characters until a structural character, an ASCII
* control character other than '\t', or impossible UTF-8
* bytes '\xFE', '\xFF'. Structural characters and line
* endings are promising resynchronization points. Clients
* may use the others to force the JSON parser into known-good
* state; see docs/interop/qmp-spec.rst.
*/
[0 ... 0x1F] = IN_START | LOOKAHEAD,
[0x20 ... 0xFD] = IN_RECOVERY,
[0xFE ... 0xFF] = IN_START | LOOKAHEAD,
['\t'] = IN_RECOVERY,
['['] = IN_START | LOOKAHEAD,
[']'] = IN_START | LOOKAHEAD,
['{'] = IN_START | LOOKAHEAD,
['}'] = IN_START | LOOKAHEAD,
[':'] = IN_START | LOOKAHEAD,
[','] = IN_START | LOOKAHEAD,
},
/* double quote string */
[IN_DQ_STRING_ESCAPE] = {
[0x20 ... 0xFD] = IN_DQ_STRING,
},
[IN_DQ_STRING] = {
[0x20 ... 0xFD] = IN_DQ_STRING,
['\\'] = IN_DQ_STRING_ESCAPE,
['"'] = JSON_STRING,
},
/* single quote string */
[IN_SQ_STRING_ESCAPE] = {
[0x20 ... 0xFD] = IN_SQ_STRING,
},
[IN_SQ_STRING] = {
[0x20 ... 0xFD] = IN_SQ_STRING,
['\\'] = IN_SQ_STRING_ESCAPE,
['\''] = JSON_STRING,
},
/* Zero */
[IN_ZERO] = {
TERMINAL(JSON_INTEGER),
['0' ... '9'] = JSON_ERROR,
['.'] = IN_MANTISSA,
},
/* Float */
[IN_EXP_DIGITS] = {
TERMINAL(JSON_FLOAT),
['0' ... '9'] = IN_EXP_DIGITS,
},
[IN_EXP_SIGN] = {
['0' ... '9'] = IN_EXP_DIGITS,
},
[IN_EXP_E] = {
['-'] = IN_EXP_SIGN,
['+'] = IN_EXP_SIGN,
['0' ... '9'] = IN_EXP_DIGITS,
},
[IN_MANTISSA_DIGITS] = {
TERMINAL(JSON_FLOAT),
['0' ... '9'] = IN_MANTISSA_DIGITS,
['e'] = IN_EXP_E,
['E'] = IN_EXP_E,
},
[IN_MANTISSA] = {
['0' ... '9'] = IN_MANTISSA_DIGITS,
},
/* Number */
[IN_DIGITS] = {
TERMINAL(JSON_INTEGER),
['0' ... '9'] = IN_DIGITS,
['e'] = IN_EXP_E,
['E'] = IN_EXP_E,
['.'] = IN_MANTISSA,
},
[IN_SIGN] = {
['0'] = IN_ZERO,
['1' ... '9'] = IN_DIGITS,
},
/* keywords */
[IN_KEYWORD] = {
TERMINAL(JSON_KEYWORD),
['a' ... 'z'] = IN_KEYWORD,
},
/* interpolation */
[IN_INTERP] = {
TERMINAL(JSON_INTERP),
['A' ... 'Z'] = IN_INTERP,
['a' ... 'z'] = IN_INTERP,
['0' ... '9'] = IN_INTERP,
},
/*
* Two start states:
* - IN_START recognizes JSON tokens with our string extensions
* - IN_START_INTERP additionally recognizes interpolation.
*/
[IN_START ... IN_START_INTERP] = {
['"'] = IN_DQ_STRING,
['\''] = IN_SQ_STRING,
['0'] = IN_ZERO,
['1' ... '9'] = IN_DIGITS,
['-'] = IN_SIGN,
['{'] = JSON_LCURLY,
['}'] = JSON_RCURLY,
['['] = JSON_LSQUARE,
[']'] = JSON_RSQUARE,
[','] = JSON_COMMA,
[':'] = JSON_COLON,
['a' ... 'z'] = IN_KEYWORD,
[' '] = IN_START,
['\t'] = IN_START,
['\r'] = IN_START,
['\n'] = IN_START,
},
[IN_START_INTERP]['%'] = IN_INTERP,
};
static inline uint8_t next_state(JSONLexer *lexer, char ch, bool flush,
bool *char_consumed)
{
uint8_t next;
assert(lexer->state < ARRAY_SIZE(json_lexer));
next = json_lexer[lexer->state][(uint8_t)ch];
*char_consumed = !flush && !(next & LOOKAHEAD);
return next & ~LOOKAHEAD;
}
void json_lexer_init(JSONLexer *lexer, bool enable_interpolation)
{
lexer->start_state = lexer->state = enable_interpolation
? IN_START_INTERP : IN_START;
lexer->token = g_string_sized_new(3);
lexer->cur_x = lexer->cur_y = 1;
lexer->x = lexer->y = 1;
}
static void json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush)
{
int new_state;
bool char_consumed = false;
lexer->cur_x++;
if (ch == '\n') {
lexer->cur_x = 1;
lexer->cur_y++;
}
while (flush ? lexer->state != lexer->start_state : !char_consumed) {
new_state = next_state(lexer, ch, flush, &char_consumed);
if (char_consumed) {
assert(!flush);
g_string_append_c(lexer->token, ch);
}
switch (new_state) {
case JSON_LCURLY:
case JSON_RCURLY:
case JSON_LSQUARE:
case JSON_RSQUARE:
case JSON_COLON:
case JSON_COMMA:
case JSON_INTERP:
case JSON_INTEGER:
case JSON_FLOAT:
case JSON_KEYWORD:
case JSON_STRING:
json_message_process_token(lexer, lexer->token, new_state,
lexer->x, lexer->y);
/* fall through */
case IN_START:
g_string_truncate(lexer->token, 0);
new_state = lexer->start_state;
lexer->x = lexer->cur_x;
lexer->y = lexer->cur_y;
break;
case JSON_ERROR:
json_message_process_token(lexer, lexer->token, JSON_ERROR,
lexer->x, lexer->y);
new_state = IN_RECOVERY;
/* fall through */
case IN_RECOVERY:
g_string_truncate(lexer->token, 0);
break;
default:
break;
}
lexer->state = new_state;
}
/* Do not let a single token grow to an arbitrarily large size,
* this is a security consideration.
*/
if (lexer->token->len > MAX_TOKEN_SIZE) {
json_message_process_token(lexer, lexer->token, lexer->state,
lexer->x, lexer->y);
g_string_truncate(lexer->token, 0);
lexer->state = lexer->start_state;
}
}
void json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size)
{
size_t i;
for (i = 0; i < size; i++) {
json_lexer_feed_char(lexer, buffer[i], false);
}
}
void json_lexer_flush(JSONLexer *lexer)
{
json_lexer_feed_char(lexer, 0, true);
assert(lexer->state == lexer->start_state);
json_message_process_token(lexer, lexer->token, JSON_END_OF_INPUT,
lexer->x, lexer->y);
}
void json_lexer_destroy(JSONLexer *lexer)
{
g_string_free(lexer->token, true);
}
+61
View File
@@ -0,0 +1,61 @@
/*
* JSON Parser
*
* Copyright IBM, Corp. 2009
*
* Authors:
* Anthony Liguori <[email protected]>
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING.LIB file in the top-level directory.
*
*/
#ifndef JSON_PARSER_INT_H
#define JSON_PARSER_INT_H
#include "qobject/json-parser.h"
typedef enum json_token_type {
JSON_ERROR = 0, /* must be zero, see json_lexer[] */
/* Gap for lexer states */
JSON_LCURLY = 100,
JSON_MIN = JSON_LCURLY,
JSON_RCURLY,
JSON_LSQUARE,
JSON_RSQUARE,
JSON_COLON,
JSON_COMMA,
JSON_INTEGER,
JSON_FLOAT,
JSON_KEYWORD,
JSON_STRING,
JSON_INTERP,
JSON_END_OF_INPUT,
JSON_MAX = JSON_END_OF_INPUT
} JSONTokenType;
typedef struct JSONToken {
JSONTokenType type;
int x;
int y;
char *str;
} JSONToken;
/* json-lexer.c */
void json_lexer_init(JSONLexer *lexer, bool enable_interpolation);
void json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size);
void json_lexer_flush(JSONLexer *lexer);
void json_lexer_destroy(JSONLexer *lexer);
/* json-streamer.c */
void json_message_process_token(JSONLexer *lexer, GString *input,
JSONTokenType type, int x, int y);
/* json-parser.c */
void json_parser_init(JSONParserContext *ctxt, va_list *ap);
void json_parser_reset(JSONParserContext *ctxt);
QObject *json_parser_feed(JSONParserContext *ctxt, const JSONToken *token, Error **errp);
void json_parser_destroy(JSONParserContext *ctxt);
#endif
+671
View File
@@ -0,0 +1,671 @@
/*
* JSON Parser
*
* Copyright IBM, Corp. 2009
*
* Authors:
* Anthony Liguori <[email protected]>
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING.LIB file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "qemu/ctype.h"
#include "qemu/cutils.h"
#include "qemu/unicode.h"
#include "qapi/error.h"
#include "qobject/qbool.h"
#include "qobject/qdict.h"
#include "qobject/qlist.h"
#include "qobject/qnull.h"
#include "qobject/qnum.h"
#include "qobject/qstring.h"
#include "json-parser-int.h"
/*
* The JSON parser is a push parser, returning a completed top-level
* object, an error, or NULL (if the object is incomplete and no error
* happened) after every token. Therefore it has an explicit
* representation of its parser stack; each stack entry consists of a
* parser state and a QObject:
* - a QList, for an array that is being added to
* - a QDict, for a dictionary that is being added to
* - a QString, for the key of the next pair that will be added to a QDict
*
* The stack represents an arbitrary nesting of arrays and dictionaries
* (whose next key has been parsed); it can also have a dictionary whose
* next key has not been parsed, but that can only happen at the top level.
* Because of this, the stack contents are always of the form
* "(QList | QDict QString)* QDict?".
*
* An empty stack represents the beginning of the parsing process, with
* start state BEFORE_VALUE.
*/
typedef enum JSONParserState {
AFTER_LCURLY,
AFTER_LSQUARE,
BEFORE_KEY,
BEFORE_VALUE,
END_OF_KEY,
END_OF_VALUE,
} JSONParserState;
typedef struct JSONParserStackEntry {
/*
* State when the container is completed or, for the top of the stack,
* entry state for the next token.
*/
JSONParserState state;
/*
* A QString with the last parsed key, or a QList/QDict for the current
* container.
*/
QObject *partial;
} JSONParserStackEntry;
/*
* This is the JSON grammar that's parsed, with the state transition and
* action at each point of the grammar. While this is not a formal
* description, "-> action" represents the pseudocode of the action
* and "-> STATE" sets the top stack entry's state to STATE.
*
* The state alone is enough to tell you what to parse; the state plus
* the type of the top of stack tells you which action to take.
*
* // The initial state is BEFORE_VALUE.
* input := value -> END_OF_VALUE -> return parsed value
* (input | END_OF_INPUT)
*
* // entered on BEFORE_VALUE; after any of these rules are processed, the
* // parser has completed a QObject and is in the END_OF_VALUE state.
* //
* // When the parser reaches the END_OF_VALUE state, it examines the
* // top of the stack to see if it's coming from "input" (stack empty),
* // "array_items" (TOS is a QList) or "dict_pairs" (TOS is a QString; the
* // item below will be a QDict). It then proceeds with the corresponding
* // actions, which will be one of:
* // - return parsed value
* // - add value to QList
* // - pop QString with the key, add key/value to the QDict
* value := literal -> END_OF_VALUE
* | '[' -> push empty QList -> AFTER_LSQUARE
* after_lsquare -> END_OF_VALUE
* | '{' -> push empty QDict -> AFTER_LCURLY
* after_lcurly -> END_OF_VALUE
*
* // non-recursive values, entered on BEFORE_VALUE
* literal := INTEGER -> END_OF_VALUE
* | FLOAT -> END_OF_VALUE
* | KEYWORD -> END_OF_VALUE
* | STRING -> END_OF_VALUE
* | INTERP -> END_OF_VALUE
*
* // entered on AFTER_LSQUARE
* after_lsquare := ']' -> pop completed QList -> END_OF_VALUE
* | ϵ -> BEFORE_VALUE
* array_items -> END_OF_VALUE
*
* // entered on BEFORE_VALUE, with TOS being a QList
* array_items := value -> add value to QList -> END_OF_VALUE
* (']' -> pop completed QList -> END_OF_VALUE
* | ',' -> BEFORE_VALUE
* array_items) -> END_OF_VALUE
*
* // entered on AFTER_LCURLY
* after_lcurly := '}' -> pop completed QDict -> END_OF_VALUE
* | ϵ -> BEFORE_KEY
* dict_pairs -> END_OF_VALUE
*
* // entered on BEFORE_KEY, with TOS being a QDict
* dict_pairs := (STRING | INTERP) -> push QString -> END_OF_KEY
* ':' -> BEFORE_VALUE
* value -> pop QString + add pair to QDict -> END_OF_VALUE
* ('}' -> pop completed QDict -> END_OF_VALUE
* | ',' -> BEFORE_KEY
* dict_pairs) -> END_OF_VALUE
*
* Parse errors ignore the token. json_parser_reset() can be
* called to restart parsing from scratch, with an empty stack.
*/
#define BUG_ON(cond) assert(!(cond))
static inline JSONParserStackEntry *current_entry(JSONParserContext *ctxt)
{
return g_queue_peek_tail(ctxt->stack);
}
static void push_entry(JSONParserContext *ctxt, QObject *partial,
JSONParserState state)
{
JSONParserStackEntry *entry = g_new(JSONParserStackEntry, 1);
entry->partial = partial;
entry->state = state;
g_queue_push_tail(ctxt->stack, entry);
}
/* Drop the top entry and return the new top entry. */
static JSONParserStackEntry *pop_entry(JSONParserContext *ctxt)
{
JSONParserStackEntry *entry = g_queue_pop_tail(ctxt->stack);
g_free(entry);
return current_entry(ctxt);
}
/**
* Error handler
*/
static void G_GNUC_PRINTF(3, 4) parse_error(JSONParserContext *ctxt,
const JSONToken *token,
const char *msg, ...)
{
va_list ap;
char message[1024];
if (ctxt->err) {
return;
}
va_start(ap, msg);
vsnprintf(message, sizeof(message), msg, ap);
va_end(ap);
error_setg(&ctxt->err, "%d:%d: JSON parse error, %s",
token->y, token->x, message);
}
static int cvt4hex(const char *s)
{
int cp, i;
cp = 0;
for (i = 0; i < 4; i++) {
if (!qemu_isxdigit(s[i])) {
return -1;
}
cp <<= 4;
if (s[i] >= '0' && s[i] <= '9') {
cp |= s[i] - '0';
} else if (s[i] >= 'a' && s[i] <= 'f') {
cp |= 10 + s[i] - 'a';
} else if (s[i] >= 'A' && s[i] <= 'F') {
cp |= 10 + s[i] - 'A';
} else {
return -1;
}
}
return cp;
}
/**
* parse_string(): Parse a JSON string
*
* From RFC 8259 "The JavaScript Object Notation (JSON) Data
* Interchange Format":
*
* char = unescaped /
* escape (
* %x22 / ; " quotation mark U+0022
* %x5C / ; \ reverse solidus U+005C
* %x2F / ; / solidus U+002F
* %x62 / ; b backspace U+0008
* %x66 / ; f form feed U+000C
* %x6E / ; n line feed U+000A
* %x72 / ; r carriage return U+000D
* %x74 / ; t tab U+0009
* %x75 4HEXDIG ) ; uXXXX U+XXXX
* escape = %x5C ; \
* quotation-mark = %x22 ; "
* unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
*
* Extensions over RFC 8259:
* - Extra escape sequence in strings:
* 0x27 (apostrophe) is recognized after escape, too
* - Single-quoted strings:
* Like double-quoted strings, except they're delimited by %x27
* (apostrophe) instead of %x22 (quotation mark), and can't contain
* unescaped apostrophe, but can contain unescaped quotation mark.
*
* Note:
* - Encoding is modified UTF-8.
* - Invalid Unicode characters are rejected.
* - Control characters \x00..\x1F are rejected by the lexer.
*/
static QString *parse_string(JSONParserContext *ctxt, const JSONToken *token)
{
const char *ptr = token->str;
GString *str;
char quote;
const char *beg;
int cp, trailing;
char *end;
ssize_t len;
char utf8_buf[5];
assert(*ptr == '"' || *ptr == '\'');
quote = *ptr++;
str = g_string_new(NULL);
while (*ptr != quote) {
assert(*ptr);
switch (*ptr) {
case '\\':
beg = ptr++;
switch (*ptr++) {
case '"':
g_string_append_c(str, '"');
break;
case '\'':
g_string_append_c(str, '\'');
break;
case '\\':
g_string_append_c(str, '\\');
break;
case '/':
g_string_append_c(str, '/');
break;
case 'b':
g_string_append_c(str, '\b');
break;
case 'f':
g_string_append_c(str, '\f');
break;
case 'n':
g_string_append_c(str, '\n');
break;
case 'r':
g_string_append_c(str, '\r');
break;
case 't':
g_string_append_c(str, '\t');
break;
case 'u':
cp = cvt4hex(ptr);
ptr += 4;
/* handle surrogate pairs */
if (cp >= 0xD800 && cp <= 0xDBFF
&& ptr[0] == '\\' && ptr[1] == 'u') {
/* leading surrogate followed by \u */
cp = 0x10000 + ((cp & 0x3FF) << 10);
trailing = cvt4hex(ptr + 2);
if (trailing >= 0xDC00 && trailing <= 0xDFFF) {
/* followed by trailing surrogate */
cp |= trailing & 0x3FF;
ptr += 6;
} else {
cp = -1; /* invalid */
}
}
if (mod_utf8_encode(utf8_buf, sizeof(utf8_buf), cp) < 0) {
parse_error(ctxt, token,
"%.*s is not a valid Unicode character",
(int)(ptr - beg), beg);
goto out;
}
g_string_append(str, utf8_buf);
break;
default:
parse_error(ctxt, token, "invalid escape sequence in string");
goto out;
}
break;
case '%':
if (ctxt->ap) {
if (ptr[1] != '%') {
parse_error(ctxt, token, "can't interpolate into string");
goto out;
}
ptr++;
}
/* fall through */
default:
cp = mod_utf8_codepoint(ptr, 6, &end);
if (cp < 0) {
parse_error(ctxt, token, "invalid UTF-8 sequence in string");
goto out;
}
ptr = end;
len = mod_utf8_encode(utf8_buf, sizeof(utf8_buf), cp);
assert(len >= 0);
g_string_append(str, utf8_buf);
}
}
return qstring_from_gstring(str);
out:
g_string_free(str, true);
return NULL;
}
/* Terminals */
static QObject *parse_keyword(JSONParserContext *ctxt, const JSONToken *token)
{
assert(token && token->type == JSON_KEYWORD);
if (!strcmp(token->str, "true")) {
return QOBJECT(qbool_from_bool(true));
} else if (!strcmp(token->str, "false")) {
return QOBJECT(qbool_from_bool(false));
} else if (!strcmp(token->str, "null")) {
return QOBJECT(qnull());
}
parse_error(ctxt, token, "invalid keyword '%s'", token->str);
return NULL;
}
static QObject *parse_interpolation(JSONParserContext *ctxt,
const JSONToken *token)
{
assert(token && token->type == JSON_INTERP);
if (!strcmp(token->str, "%p")) {
return va_arg(*ctxt->ap, QObject *);
} else if (!strcmp(token->str, "%i")) {
return QOBJECT(qbool_from_bool(va_arg(*ctxt->ap, int)));
} else if (!strcmp(token->str, "%d")) {
return QOBJECT(qnum_from_int(va_arg(*ctxt->ap, int)));
} else if (!strcmp(token->str, "%ld")) {
return QOBJECT(qnum_from_int(va_arg(*ctxt->ap, long)));
} else if (!strcmp(token->str, "%lld")) {
return QOBJECT(qnum_from_int(va_arg(*ctxt->ap, long long)));
} else if (!strcmp(token->str, "%" PRId64)) {
return QOBJECT(qnum_from_int(va_arg(*ctxt->ap, int64_t)));
} else if (!strcmp(token->str, "%u")) {
return QOBJECT(qnum_from_uint(va_arg(*ctxt->ap, unsigned int)));
} else if (!strcmp(token->str, "%lu")) {
return QOBJECT(qnum_from_uint(va_arg(*ctxt->ap, unsigned long)));
} else if (!strcmp(token->str, "%llu")) {
return QOBJECT(qnum_from_uint(va_arg(*ctxt->ap, unsigned long long)));
} else if (!strcmp(token->str, "%" PRIu64)) {
return QOBJECT(qnum_from_uint(va_arg(*ctxt->ap, uint64_t)));
} else if (!strcmp(token->str, "%s")) {
return QOBJECT(qstring_from_str(va_arg(*ctxt->ap, const char *)));
} else if (!strcmp(token->str, "%f")) {
return QOBJECT(qnum_from_double(va_arg(*ctxt->ap, double)));
}
parse_error(ctxt, token, "invalid interpolation '%s'", token->str);
return NULL;
}
static QObject *parse_literal(JSONParserContext *ctxt, const JSONToken *token)
{
assert(token);
switch (token->type) {
case JSON_STRING:
return QOBJECT(parse_string(ctxt, token));
case JSON_INTEGER: {
/*
* Represent JSON_INTEGER as QNUM_I64 if possible, else as
* QNUM_U64, else as QNUM_DOUBLE. Note that qemu_strtoi64()
* and qemu_strtou64() fail with ERANGE when it's not
* possible.
*
* qnum_get_int() will then work for any signed 64-bit
* JSON_INTEGER, qnum_get_uint() for any unsigned 64-bit
* integer, and qnum_get_double() both for any JSON_INTEGER
* and any JSON_FLOAT (with precision loss for integers beyond
* 53 bits)
*/
int ret;
int64_t value;
uint64_t uvalue;
ret = qemu_strtoi64(token->str, NULL, 10, &value);
if (!ret) {
return QOBJECT(qnum_from_int(value));
}
assert(ret == -ERANGE);
if (token->str[0] != '-') {
ret = qemu_strtou64(token->str, NULL, 10, &uvalue);
if (!ret) {
return QOBJECT(qnum_from_uint(uvalue));
}
assert(ret == -ERANGE);
}
}
/* fall through to JSON_FLOAT */
case JSON_FLOAT:
/* FIXME dependent on locale; a pervasive issue in QEMU */
/* FIXME our lexer matches RFC 8259 in forbidding Inf or NaN,
* but those might be useful extensions beyond JSON */
return QOBJECT(qnum_from_double(strtod(token->str, NULL)));
default:
abort();
}
}
/* Parsing state machine */
static QObject *parse_begin_value(JSONParserContext *ctxt,
const JSONToken *token)
{
switch (token->type) {
case JSON_LCURLY:
push_entry(ctxt, QOBJECT(qdict_new()), AFTER_LCURLY);
return NULL;
case JSON_LSQUARE:
push_entry(ctxt, QOBJECT(qlist_new()), AFTER_LSQUARE);
return NULL;
case JSON_INTERP:
return parse_interpolation(ctxt, token);
case JSON_INTEGER:
case JSON_FLOAT:
case JSON_STRING:
return parse_literal(ctxt, token);
case JSON_KEYWORD:
return parse_keyword(ctxt, token);
default:
parse_error(ctxt, token, "expecting value");
return NULL;
}
}
static QObject *parse_token(JSONParserContext *ctxt, const JSONToken *token)
{
JSONParserStackEntry *entry;
JSONParserState state;
QString *key;
QObject *key_obj = NULL, *value = NULL;
entry = current_entry(ctxt);
state = entry ? entry->state : BEFORE_VALUE;
switch (state) {
case AFTER_LCURLY:
/* Grab '}' for empty object or fall through to BEFORE_KEY */
assert(qobject_type(entry->partial) == QTYPE_QDICT);
if (token->type == JSON_RCURLY) {
value = entry->partial;
entry = pop_entry(ctxt);
break;
}
entry->state = BEFORE_KEY;
/* fall through */
case BEFORE_KEY:
/* Expecting object key */
assert(qobject_type(entry->partial) == QTYPE_QDICT);
if (token->type != JSON_STRING && token->type != JSON_INTERP) {
parse_error(ctxt, token, "expecting key");
return NULL;
}
key_obj = parse_begin_value(ctxt, token);
if (!key_obj) {
/* Parse error already reported */
} else if (qobject_type(key_obj) != QTYPE_QSTRING) {
/* An interpolation was valid syntactically but not %s */
parse_error(ctxt, token, "key is not a string in object");
} else {
/* Store key in a special entry on the stack */
push_entry(ctxt, key_obj, END_OF_KEY);
}
return NULL;
case END_OF_KEY:
/* Expecting ':' after key */
assert(qobject_type(entry->partial) == QTYPE_QSTRING);
if (token->type == JSON_COLON) {
entry->state = BEFORE_VALUE;
} else {
parse_error(ctxt, token, "expecting ':'");
}
return NULL;
case AFTER_LSQUARE:
/* Grab ']' for empty array or fall through to BEFORE_VALUE */
assert(qobject_type(entry->partial) == QTYPE_QLIST);
if (token->type == JSON_RSQUARE) {
value = entry->partial;
entry = pop_entry(ctxt);
break;
}
entry->state = BEFORE_VALUE;
/* fall through */
case BEFORE_VALUE:
/* Expecting value */
assert(!entry || qobject_type(entry->partial) != QTYPE_QDICT);
value = parse_begin_value(ctxt, token);
if (!value) {
/* Error or '['/'{' */
return NULL;
}
/* Return value or insert it into a container */
break;
case END_OF_VALUE:
/* Grab ',' or ']' for array; ',' or '}' for object */
if (qobject_to(QList, entry->partial)) {
/* Array */
if (token->type != JSON_RSQUARE) {
if (token->type == JSON_COMMA) {
entry->state = BEFORE_VALUE;
} else {
parse_error(ctxt, token, "expected ',' or ']'");
}
return NULL;
}
} else if (qobject_to(QDict, entry->partial)) {
/* Object */
if (token->type != JSON_RCURLY) {
if (token->type == JSON_COMMA) {
entry->state = BEFORE_KEY;
} else {
parse_error(ctxt, token, "expected ',' or '}'");
}
return NULL;
}
} else {
g_assert_not_reached();
}
/* Got ']' or '}'; return full value or insert into parent container */
value = entry->partial;
entry = pop_entry(ctxt);
break;
}
assert(value);
if (entry == NULL) {
/* Parse stack now empty, the top-level value is complete. */
return value;
}
/*
* Parse stack is not empty and entry->partial is the top of stack.
* It's a QString with the key (and a QDict is below it) if we're
* parsing an object, or a QList if we're parsing an array.
*/
key = qobject_to(QString, entry->partial);
if (key) {
const char *key_str;
QDict *dict;
/* Pop off key, and store (key, value) in QDict. */
entry = pop_entry(ctxt);
dict = qobject_to(QDict, entry->partial);
assert(dict);
key_str = qstring_get_str(key);
if (qdict_haskey(dict, key_str)) {
parse_error(ctxt, token, "duplicate key");
qobject_unref(value);
return NULL;
}
qdict_put_obj(dict, key_str, value);
qobject_unref(key);
} else {
/* Array, just store value in the QList. */
qlist_append_obj(qobject_to(QList, entry->partial), value);
}
entry->state = END_OF_VALUE;
return NULL;
}
void json_parser_reset(JSONParserContext *ctxt)
{
JSONParserStackEntry *entry;
ctxt->err = NULL;
while ((entry = g_queue_pop_tail(ctxt->stack)) != NULL) {
qobject_unref(entry->partial);
g_free(entry);
}
}
void json_parser_init(JSONParserContext *ctxt, va_list *ap)
{
ctxt->stack = g_queue_new();
ctxt->ap = ap;
json_parser_reset(ctxt);
}
void json_parser_destroy(JSONParserContext *ctxt)
{
json_parser_reset(ctxt);
g_queue_free(ctxt->stack);
ctxt->stack = NULL;
}
/*
* Advance the parser based on the token that is passed.
* Return the finished top-level value if the token completes it, else
* NULL.
* Once an error is returned, the function must not be called again
* without first resetting the parser.
*/
QObject *json_parser_feed(JSONParserContext *ctxt, const JSONToken *token,
Error **errp)
{
QObject *result = NULL;
assert(!ctxt->err);
switch (token->type) {
case JSON_ERROR:
parse_error(ctxt, token, "stray '%s'", token->str);
break;
case JSON_END_OF_INPUT:
/* Check for premature end of input */
if (!g_queue_is_empty(ctxt->stack)) {
parse_error(ctxt, token, "premature end of input");
}
break;
default:
result = parse_token(ctxt, token);
break;
}
error_propagate(errp, ctxt->err);
return result;
}
+143
View File
@@ -0,0 +1,143 @@
/*
* JSON parser - callback interface and error recovery
*
* Copyright IBM, Corp. 2009
*
* Authors:
* Anthony Liguori <[email protected]>
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING.LIB file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "json-parser-int.h"
#define MAX_TOKEN_SIZE (64ULL << 20)
#define MAX_TOKEN_COUNT (2ULL << 20)
#define MAX_NESTING (1 << 10)
void json_message_process_token(JSONLexer *lexer, GString *input,
JSONTokenType type, int x, int y)
{
JSONMessageParser *parser = container_of(lexer, JSONMessageParser, lexer);
Error *err = NULL;
parser->token_size += input->len;
parser->token_count++;
/* Detect message boundaries for error recovery purposes. */
switch (type) {
case JSON_LCURLY:
parser->brace_count++;
break;
case JSON_RCURLY:
if (!parser->brace_count) {
goto end_error_recovery;
}
parser->brace_count--;
break;
case JSON_LSQUARE:
parser->bracket_count++;
break;
case JSON_RSQUARE:
if (!parser->bracket_count) {
goto end_error_recovery;
}
parser->bracket_count--;
break;
case JSON_ERROR:
end_error_recovery:
/*
* We come here due to receiving either JSON_ERROR or a
* JSON_R{CURLY,SQUARE}) that is known to be unbalanced.
* If in error recovery, end it immediately. If not in
* error recovery, json_parser_feed() will raise an error
* but error recovery won't be entered at all.
*/
parser->brace_count = 0;
parser->bracket_count = 0;
break;
default:
break;
}
if (parser->error) {
/* error recovery, eat tokens until parentheses balance */
} else {
/*
* Safety consideration, we limit total memory allocated per object
* and the maximum nesting depth that a message can force.
*/
if (parser->token_size >= MAX_TOKEN_SIZE) {
error_setg(&err, "JSON token size limit exceeded");
} else if (parser->token_count > MAX_TOKEN_COUNT) {
error_setg(&err, "JSON token count limit exceeded");
} else if (parser->bracket_count + parser->brace_count > MAX_NESTING) {
error_setg(&err, "JSON nesting depth limit exceeded");
} else {
JSONToken token = (JSONToken) {
.type = type,
.x = x,
.y = y,
.str = input->str
};
QObject *json = json_parser_feed(&parser->parser, &token, &err);
if (json) {
parser->emit(parser->opaque, json, NULL);
}
}
if (err) {
parser->emit(parser->opaque, NULL, err);
/* start recovery */
parser->error = true;
}
}
if ((parser->brace_count == 0 && parser->bracket_count == 0)
|| type == JSON_END_OF_INPUT) {
json_parser_reset(&parser->parser);
parser->error = false;
parser->brace_count = 0;
parser->bracket_count = 0;
parser->token_count = 0;
parser->token_size = 0;
}
}
void json_message_parser_init(JSONMessageParser *parser,
void (*emit)(void *opaque, QObject *json,
Error *err),
void *opaque, va_list *ap)
{
parser->emit = emit;
parser->opaque = opaque;
parser->error = false;
parser->brace_count = 0;
parser->bracket_count = 0;
parser->token_count = 0;
parser->token_size = 0;
json_parser_init(&parser->parser, ap);
json_lexer_init(&parser->lexer, !!ap);
}
void json_message_parser_feed(JSONMessageParser *parser,
const char *buffer, size_t size)
{
json_lexer_feed(&parser->lexer, buffer, size);
}
void json_message_parser_flush(JSONMessageParser *parser)
{
json_lexer_flush(&parser->lexer);
}
void json_message_parser_destroy(JSONMessageParser *parser)
{
json_lexer_destroy(&parser->lexer);
json_parser_destroy(&parser->parser);
}
+250
View File
@@ -0,0 +1,250 @@
/*
* JSON Writer
*
* Copyright IBM, Corp. 2009
* Copyright (c) 2010-2020 Red Hat Inc.
*
* Authors:
* Anthony Liguori <[email protected]>
* Markus Armbruster <[email protected]>
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING.LIB file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "qobject/json-writer.h"
#include "qemu/unicode.h"
struct JSONWriter {
bool pretty;
bool need_comma;
GString *contents;
GByteArray *container_is_array;
};
/* Should cover most QMP responses without reallocation */
#define JSON_WRITER_INITIAL_SIZE 4096
JSONWriter *json_writer_new(bool pretty)
{
JSONWriter *writer = g_new(JSONWriter, 1);
writer->pretty = pretty;
writer->need_comma = false;
writer->contents = g_string_sized_new(JSON_WRITER_INITIAL_SIZE);
writer->container_is_array = g_byte_array_new();
return writer;
}
const char *json_writer_get(JSONWriter *writer)
{
g_assert(!writer->container_is_array->len);
return writer->contents->str;
}
GString *json_writer_get_and_free(JSONWriter *writer)
{
GString *contents = writer->contents;
writer->contents = NULL;
g_byte_array_free(writer->container_is_array, true);
g_free(writer);
return contents;
}
void json_writer_free(JSONWriter *writer)
{
if (writer) {
g_string_free(json_writer_get_and_free(writer), true);
}
}
static void enter_container(JSONWriter *writer, bool is_array)
{
unsigned depth = writer->container_is_array->len;
g_byte_array_set_size(writer->container_is_array, depth + 1);
writer->container_is_array->data[depth] = is_array;
writer->need_comma = false;
}
static void leave_container(JSONWriter *writer, bool is_array)
{
unsigned depth = writer->container_is_array->len;
assert(depth);
assert(writer->container_is_array->data[depth - 1] == is_array);
g_byte_array_set_size(writer->container_is_array, depth - 1);
writer->need_comma = true;
}
static bool in_object(JSONWriter *writer)
{
unsigned depth = writer->container_is_array->len;
return depth && !writer->container_is_array->data[depth - 1];
}
static void pretty_newline(JSONWriter *writer)
{
if (writer->pretty) {
g_string_append_printf(writer->contents, "\n%*s",
writer->container_is_array->len * 4, "");
}
}
static void pretty_newline_or_space(JSONWriter *writer)
{
if (writer->pretty) {
g_string_append_printf(writer->contents, "\n%*s",
writer->container_is_array->len * 4, "");
} else {
g_string_append_c(writer->contents, ' ');
}
}
static void quoted_str(JSONWriter *writer, const char *str)
{
const char *ptr;
char *end;
int cp;
g_string_append_c(writer->contents, '"');
for (ptr = str; *ptr; ptr = end) {
cp = mod_utf8_codepoint(ptr, 6, &end);
switch (cp) {
case '\"':
g_string_append(writer->contents, "\\\"");
break;
case '\\':
g_string_append(writer->contents, "\\\\");
break;
case '\b':
g_string_append(writer->contents, "\\b");
break;
case '\f':
g_string_append(writer->contents, "\\f");
break;
case '\n':
g_string_append(writer->contents, "\\n");
break;
case '\r':
g_string_append(writer->contents, "\\r");
break;
case '\t':
g_string_append(writer->contents, "\\t");
break;
default:
if (cp < 0) {
cp = 0xFFFD; /* replacement character */
}
if (cp > 0xFFFF) {
/* beyond BMP; need a surrogate pair */
g_string_append_printf(writer->contents, "\\u%04X\\u%04X",
0xD800 + ((cp - 0x10000) >> 10),
0xDC00 + ((cp - 0x10000) & 0x3FF));
} else if (cp < 0x20 || cp >= 0x7F) {
g_string_append_printf(writer->contents, "\\u%04X", cp);
} else {
g_string_append_c(writer->contents, cp);
}
}
};
g_string_append_c(writer->contents, '"');
}
static void maybe_comma_name(JSONWriter *writer, const char *name)
{
if (writer->need_comma) {
g_string_append_c(writer->contents, ',');
pretty_newline_or_space(writer);
} else {
if (writer->contents->len) {
pretty_newline(writer);
}
writer->need_comma = true;
}
if (in_object(writer)) {
quoted_str(writer, name);
g_string_append(writer->contents, ": ");
}
}
void json_writer_start_object(JSONWriter *writer, const char *name)
{
maybe_comma_name(writer, name);
g_string_append_c(writer->contents, '{');
enter_container(writer, false);
}
void json_writer_end_object(JSONWriter *writer)
{
leave_container(writer, false);
pretty_newline(writer);
g_string_append_c(writer->contents, '}');
}
void json_writer_start_array(JSONWriter *writer, const char *name)
{
maybe_comma_name(writer, name);
g_string_append_c(writer->contents, '[');
enter_container(writer, true);
}
void json_writer_end_array(JSONWriter *writer)
{
leave_container(writer, true);
pretty_newline(writer);
g_string_append_c(writer->contents, ']');
}
void json_writer_bool(JSONWriter *writer, const char *name, bool val)
{
maybe_comma_name(writer, name);
g_string_append(writer->contents, val ? "true" : "false");
}
void json_writer_null(JSONWriter *writer, const char *name)
{
maybe_comma_name(writer, name);
g_string_append(writer->contents, "null");
}
void json_writer_int64(JSONWriter *writer, const char *name, int64_t val)
{
maybe_comma_name(writer, name);
g_string_append_printf(writer->contents, "%" PRId64, val);
}
void json_writer_uint64(JSONWriter *writer, const char *name, uint64_t val)
{
maybe_comma_name(writer, name);
g_string_append_printf(writer->contents, "%" PRIu64, val);
}
void json_writer_double(JSONWriter *writer, const char *name, double val)
{
maybe_comma_name(writer, name);
/*
* FIXME: g_string_append_printf() is locale dependent; but JSON
* requires numbers to be formatted as if in the C locale.
* Dependence on C locale is a pervasive issue in QEMU.
*/
/*
* FIXME: This risks printing Inf or NaN, which are not valid
* JSON values.
*/
g_string_append_printf(writer->contents, "%.17g", val);
}
void json_writer_str(JSONWriter *writer, const char *name, const char *str)
{
maybe_comma_name(writer, name);
quoted_str(writer, str);
}
+4
View File
@@ -0,0 +1,4 @@
util_ss.add(files('qnull.c', 'qnum.c', 'qstring.c', 'qdict.c',
'qlist.c', 'qbool.c', 'qlit.c', 'qjson.c', 'qobject.c',
'json-writer.c', 'json-lexer.c', 'json-streamer.c', 'json-parser.c',
'block-qdict.c'))
+63
View File
@@ -0,0 +1,63 @@
/*
* QBool Module
*
* Copyright IBM, Corp. 2009
*
* Authors:
* Anthony Liguori <[email protected]>
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING.LIB file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "qobject/qbool.h"
#include "qobject-internal.h"
/**
* qbool_from_bool(): Create a new QBool from a bool
*
* Return strong reference.
*/
QBool *qbool_from_bool(bool value)
{
QBool *qb;
qb = g_malloc(sizeof(*qb));
qobject_init(QOBJECT(qb), QTYPE_QBOOL);
qb->value = value;
return qb;
}
/**
* qbool_get_bool(): Get the stored bool
*/
bool qbool_get_bool(const QBool *qb)
{
return qb->value;
}
/**
* qbool_is_equal(): Test whether the two QBools are equal
*/
bool qbool_is_equal(const QObject *x, const QObject *y)
{
return qobject_to(QBool, x)->value == qobject_to(QBool, y)->value;
}
/**
* qbool_destroy_obj(): Free all memory allocated by a
* QBool object
*/
void qbool_destroy_obj(QObject *obj)
{
assert(obj != NULL);
g_free(qobject_to(QBool, obj));
}
void qbool_unref(QBool *q)
{
qobject_unref(q);
}
+462
View File
@@ -0,0 +1,462 @@
/*
* QDict Module
*
* Copyright (C) 2009 Red Hat Inc.
*
* Authors:
* Luiz Capitulino <[email protected]>
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING.LIB file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qobject/qnum.h"
#include "qobject/qdict.h"
#include "qobject/qbool.h"
#include "qobject/qnull.h"
#include "qobject/qstring.h"
#include "qobject-internal.h"
/**
* qdict_new(): Create a new QDict
*
* Return strong reference.
*/
QDict *qdict_new(void)
{
QDict *qdict;
qdict = g_malloc0(sizeof(*qdict));
qobject_init(QOBJECT(qdict), QTYPE_QDICT);
return qdict;
}
/**
* tdb_hash(): based on the hash algorithm from gdbm, via tdb
* (from module-init-tools)
*/
static unsigned int tdb_hash(const char *name)
{
unsigned value; /* Used to compute the hash value. */
unsigned i; /* Used to cycle through random values. */
/* Set the initial value from the key size. */
for (value = 0x238F13AF * strlen(name), i = 0; name[i]; i++) {
value = (value + (((const unsigned char *)name)[i] << (i * 5 % 24)));
}
return (1103515243 * value + 12345);
}
/**
* alloc_entry(): allocate a new QDictEntry
*/
static QDictEntry *alloc_entry(const char *key, QObject *value)
{
QDictEntry *entry;
entry = g_malloc0(sizeof(*entry));
entry->key = g_strdup(key);
entry->value = value;
return entry;
}
/**
* qdict_entry_value(): Return qdict entry value
*
* Return weak reference.
*/
QObject *qdict_entry_value(const QDictEntry *entry)
{
return entry->value;
}
/**
* qdict_entry_key(): Return qdict entry key
*
* Return a *pointer* to the string, it has to be duplicated before being
* stored.
*/
const char *qdict_entry_key(const QDictEntry *entry)
{
return entry->key;
}
/**
* qdict_find(): List lookup function
*/
static QDictEntry *qdict_find(const QDict *qdict,
const char *key, unsigned int bucket)
{
QDictEntry *entry;
QLIST_FOREACH(entry, &qdict->table[bucket], next)
if (!strcmp(entry->key, key)) {
return entry;
}
return NULL;
}
/**
* qdict_put_obj(): Put a new QObject into the dictionary
*
* Insert the pair 'key:value' into 'qdict', if 'key' already exists
* its 'value' will be replaced.
*
* This is done by freeing the reference to the stored QObject and
* storing the new one in the same entry.
*
* NOTE: ownership of 'value' is transferred to the QDict
*/
void qdict_put_obj(QDict *qdict, const char *key, QObject *value)
{
unsigned int bucket;
QDictEntry *entry;
bucket = tdb_hash(key) % QDICT_BUCKET_MAX;
entry = qdict_find(qdict, key, bucket);
if (entry) {
/* replace key's value */
qobject_unref(entry->value);
entry->value = value;
} else {
/* allocate a new entry */
entry = alloc_entry(key, value);
QLIST_INSERT_HEAD(&qdict->table[bucket], entry, next);
qdict->size++;
}
}
void qdict_put_int(QDict *qdict, const char *key, int64_t value)
{
qdict_put(qdict, key, qnum_from_int(value));
}
void qdict_put_bool(QDict *qdict, const char *key, bool value)
{
qdict_put(qdict, key, qbool_from_bool(value));
}
void qdict_put_str(QDict *qdict, const char *key, const char *value)
{
qdict_put(qdict, key, qstring_from_str(value));
}
void qdict_put_null(QDict *qdict, const char *key)
{
qdict_put(qdict, key, qnull());
}
/**
* qdict_get(): Lookup for a given 'key'
*
* Return a weak reference to the QObject associated with 'key' if
* 'key' is present in the dictionary, NULL otherwise.
*/
QObject *qdict_get(const QDict *qdict, const char *key)
{
QDictEntry *entry;
entry = qdict_find(qdict, key, tdb_hash(key) % QDICT_BUCKET_MAX);
return (entry == NULL ? NULL : entry->value);
}
/**
* qdict_haskey(): Check if 'key' exists
*
* Return 1 if 'key' exists in the dict, 0 otherwise
*/
int qdict_haskey(const QDict *qdict, const char *key)
{
unsigned int bucket = tdb_hash(key) % QDICT_BUCKET_MAX;
return (qdict_find(qdict, key, bucket) == NULL ? 0 : 1);
}
/**
* qdict_size(): Return the size of the dictionary
*/
size_t qdict_size(const QDict *qdict)
{
return qdict->size;
}
/**
* qdict_get_double(): Get an number mapped by 'key'
*
* This function assumes that 'key' exists and it stores a QNum.
*
* Return number mapped by 'key'.
*/
double qdict_get_double(const QDict *qdict, const char *key)
{
return qnum_get_double(qobject_to(QNum, qdict_get(qdict, key)));
}
/**
* qdict_get_int(): Get an integer mapped by 'key'
*
* This function assumes that 'key' exists and it stores a
* QNum representable as int.
*
* Return integer mapped by 'key'.
*/
int64_t qdict_get_int(const QDict *qdict, const char *key)
{
return qnum_get_int(qobject_to(QNum, qdict_get(qdict, key)));
}
/**
* qdict_get_uint(): Get an unsigned integer mapped by 'key'
*
* This function assumes that 'key' exists and it stores a
* QNum representable as uint.
*
* Return unsigned integer mapped by 'key'.
*/
uint64_t qdict_get_uint(const QDict *qdict, const char *key)
{
return qnum_get_uint(qobject_to(QNum, qdict_get(qdict, key)));
}
/**
* qdict_get_bool(): Get a bool mapped by 'key'
*
* This function assumes that 'key' exists and it stores a
* QBool object.
*
* Return bool mapped by 'key'.
*/
bool qdict_get_bool(const QDict *qdict, const char *key)
{
return qbool_get_bool(qobject_to(QBool, qdict_get(qdict, key)));
}
/**
* qdict_get_qlist(): If @qdict maps @key to a QList, return it, else NULL.
*/
QList *qdict_get_qlist(const QDict *qdict, const char *key)
{
return qobject_to(QList, qdict_get(qdict, key));
}
/**
* qdict_get_qdict(): If @qdict maps @key to a QDict, return it, else NULL.
*/
QDict *qdict_get_qdict(const QDict *qdict, const char *key)
{
return qobject_to(QDict, qdict_get(qdict, key));
}
/**
* qdict_get_str(): Get a pointer to the stored string mapped
* by 'key'
*
* This function assumes that 'key' exists and it stores a
* QString object.
*
* Return pointer to the string mapped by 'key'.
*/
const char *qdict_get_str(const QDict *qdict, const char *key)
{
return qstring_get_str(qobject_to(QString, qdict_get(qdict, key)));
}
/**
* qdict_get_try_int(): Try to get integer mapped by 'key'
*
* Return integer mapped by 'key', if it is not present in the
* dictionary or if the stored object is not a QNum representing an
* integer, 'def_value' will be returned.
*/
int64_t qdict_get_try_int(const QDict *qdict, const char *key,
int64_t def_value)
{
QNum *qnum = qobject_to(QNum, qdict_get(qdict, key));
int64_t val;
if (!qnum || !qnum_get_try_int(qnum, &val)) {
return def_value;
}
return val;
}
/**
* qdict_get_try_bool(): Try to get a bool mapped by 'key'
*
* Return bool mapped by 'key', if it is not present in the
* dictionary or if the stored object is not of QBool type
* 'def_value' will be returned.
*/
bool qdict_get_try_bool(const QDict *qdict, const char *key, bool def_value)
{
QBool *qbool = qobject_to(QBool, qdict_get(qdict, key));
return qbool ? qbool_get_bool(qbool) : def_value;
}
/**
* qdict_get_try_str(): Try to get a pointer to the stored string
* mapped by 'key'
*
* Return a pointer to the string mapped by 'key', if it is not present
* in the dictionary or if the stored object is not of QString type
* NULL will be returned.
*/
const char *qdict_get_try_str(const QDict *qdict, const char *key)
{
QString *qstr = qobject_to(QString, qdict_get(qdict, key));
return qstr ? qstring_get_str(qstr) : NULL;
}
static QDictEntry *qdict_next_entry(const QDict *qdict, int first_bucket)
{
int i;
for (i = first_bucket; i < QDICT_BUCKET_MAX; i++) {
if (!QLIST_EMPTY(&qdict->table[i])) {
return QLIST_FIRST(&qdict->table[i]);
}
}
return NULL;
}
/**
* qdict_first(): Return first qdict entry for iteration.
*/
const QDictEntry *qdict_first(const QDict *qdict)
{
return qdict_next_entry(qdict, 0);
}
/**
* qdict_next(): Return next qdict entry in an iteration.
*/
const QDictEntry *qdict_next(const QDict *qdict, const QDictEntry *entry)
{
QDictEntry *ret;
ret = QLIST_NEXT(entry, next);
if (!ret) {
unsigned int bucket = tdb_hash(entry->key) % QDICT_BUCKET_MAX;
ret = qdict_next_entry(qdict, bucket + 1);
}
return ret;
}
/**
* qdict_clone_shallow(): Clones a given QDict. Its entries are not copied, but
* another reference is added.
*/
QDict *qdict_clone_shallow(const QDict *src)
{
QDict *dest;
QDictEntry *entry;
int i;
dest = qdict_new();
for (i = 0; i < QDICT_BUCKET_MAX; i++) {
QLIST_FOREACH(entry, &src->table[i], next) {
qdict_put_obj(dest, entry->key, qobject_ref(entry->value));
}
}
return dest;
}
/**
* qentry_destroy(): Free all the memory allocated by a QDictEntry
*/
static void qentry_destroy(QDictEntry *e)
{
assert(e != NULL);
assert(e->key != NULL);
assert(e->value != NULL);
qobject_unref(e->value);
g_free(e->key);
g_free(e);
}
/**
* qdict_del(): Delete a 'key:value' pair from the dictionary
*
* This will destroy all data allocated by this entry.
*/
void qdict_del(QDict *qdict, const char *key)
{
QDictEntry *entry;
entry = qdict_find(qdict, key, tdb_hash(key) % QDICT_BUCKET_MAX);
if (entry) {
QLIST_REMOVE(entry, next);
qentry_destroy(entry);
qdict->size--;
}
}
/**
* qdict_is_equal(): Test whether the two QDicts are equal
*
* Here, equality means whether they contain the same keys and whether
* the respective values are in turn equal (i.e. invoking
* qobject_is_equal() on them yields true).
*/
bool qdict_is_equal(const QObject *x, const QObject *y)
{
const QDict *dict_x = qobject_to(QDict, x);
const QDict *dict_y = qobject_to(QDict, y);
const QDictEntry *e;
if (qdict_size(dict_x) != qdict_size(dict_y)) {
return false;
}
for (e = qdict_first(dict_x); e; e = qdict_next(dict_x, e)) {
const QObject *obj_x = qdict_entry_value(e);
const QObject *obj_y = qdict_get(dict_y, qdict_entry_key(e));
if (!qobject_is_equal(obj_x, obj_y)) {
return false;
}
}
return true;
}
/**
* qdict_destroy_obj(): Free all the memory allocated by a QDict
*/
void qdict_destroy_obj(QObject *obj)
{
int i;
QDict *qdict;
assert(obj != NULL);
qdict = qobject_to(QDict, obj);
for (i = 0; i < QDICT_BUCKET_MAX; i++) {
QDictEntry *entry = QLIST_FIRST(&qdict->table[i]);
while (entry) {
QDictEntry *tmp = QLIST_NEXT(entry, next);
QLIST_REMOVE(entry, next);
qentry_destroy(entry);
entry = tmp;
}
}
g_free(qdict);
}
void qdict_unref(QDict *q)
{
qobject_unref(q);
}
+232
View File
@@ -0,0 +1,232 @@
/*
* QObject JSON integration
*
* Copyright IBM, Corp. 2009
*
* Authors:
* Anthony Liguori <[email protected]>
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING.LIB file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qobject/json-parser.h"
#include "qobject/json-writer.h"
#include "qobject/qjson.h"
#include "qobject/qbool.h"
#include "qobject/qdict.h"
#include "qobject/qlist.h"
#include "qobject/qnum.h"
#include "qobject/qstring.h"
typedef struct JSONParsingState {
JSONMessageParser parser;
QObject *result;
Error *err;
} JSONParsingState;
static void consume_json(void *opaque, QObject *json, Error *err)
{
JSONParsingState *s = opaque;
assert(!json != !err);
assert(!s->result || !s->err);
if (s->result) {
qobject_unref(s->result);
s->result = NULL;
error_setg(&s->err, "Expecting at most one JSON value");
}
if (s->err) {
qobject_unref(json);
error_free(err);
return;
}
s->result = json;
s->err = err;
}
/*
* Parse @string as JSON value.
* If @ap is non-null, interpolate %-escapes.
* Takes ownership of %p arguments.
* On success, return the JSON value.
* On failure, store an error through @errp and return NULL.
* Ownership of %p arguments becomes indeterminate then. To avoid
* leaks, callers passing %p must terminate on error, e.g. by passing
* &error_abort.
*/
static QObject *qobject_from_jsonv(const char *string, va_list *ap,
Error **errp)
{
JSONParsingState state = {};
json_message_parser_init(&state.parser, consume_json, &state, ap);
json_message_parser_feed(&state.parser, string, strlen(string));
json_message_parser_flush(&state.parser);
json_message_parser_destroy(&state.parser);
if (!state.result && !state.err) {
error_setg(&state.err, "Expecting a JSON value");
}
error_propagate(errp, state.err);
return state.result;
}
QObject *qobject_from_json(const char *string, Error **errp)
{
return qobject_from_jsonv(string, NULL, errp);
}
/*
* Parse @string as JSON value with %-escapes interpolated.
* Abort on error. Do not use with untrusted @string.
* Return the resulting QObject. It is never null.
*/
QObject *qobject_from_vjsonf_nofail(const char *string, va_list ap)
{
va_list ap_copy;
QObject *obj;
/* va_copy() is needed when va_list is an array type */
va_copy(ap_copy, ap);
obj = qobject_from_jsonv(string, &ap_copy, &error_abort);
va_end(ap_copy);
assert(obj);
return obj;
}
/*
* Parse @string as JSON value with %-escapes interpolated.
* Abort on error. Do not use with untrusted @string.
* Return the resulting QObject. It is never null.
*/
QObject *qobject_from_jsonf_nofail(const char *string, ...)
{
QObject *obj;
va_list ap;
va_start(ap, string);
obj = qobject_from_vjsonf_nofail(string, ap);
va_end(ap);
return obj;
}
/*
* Parse @string as JSON object with %-escapes interpolated.
* Abort on error. Do not use with untrusted @string.
* Return the resulting QDict. It is never null.
*/
QDict *qdict_from_vjsonf_nofail(const char *string, va_list ap)
{
QDict *qdict;
qdict = qobject_to(QDict, qobject_from_vjsonf_nofail(string, ap));
assert(qdict);
return qdict;
}
/*
* Parse @string as JSON object with %-escapes interpolated.
* Abort on error. Do not use with untrusted @string.
* Return the resulting QDict. It is never null.
*/
QDict *qdict_from_jsonf_nofail(const char *string, ...)
{
QDict *qdict;
va_list ap;
va_start(ap, string);
qdict = qdict_from_vjsonf_nofail(string, ap);
va_end(ap);
return qdict;
}
static void to_json(JSONWriter *writer, const char *name,
const QObject *obj)
{
switch (qobject_type(obj)) {
case QTYPE_QNULL:
json_writer_null(writer, name);
break;
case QTYPE_QNUM: {
QNum *val = qobject_to(QNum, obj);
switch (val->kind) {
case QNUM_I64:
json_writer_int64(writer, name, val->u.i64);
break;
case QNUM_U64:
json_writer_uint64(writer, name, val->u.u64);
break;
case QNUM_DOUBLE:
json_writer_double(writer, name, val->u.dbl);
break;
default:
abort();
}
break;
}
case QTYPE_QSTRING: {
QString *val = qobject_to(QString, obj);
json_writer_str(writer, name, qstring_get_str(val));
break;
}
case QTYPE_QDICT: {
QDict *val = qobject_to(QDict, obj);
const QDictEntry *entry;
json_writer_start_object(writer, name);
for (entry = qdict_first(val);
entry;
entry = qdict_next(val, entry)) {
to_json(writer, qdict_entry_key(entry), qdict_entry_value(entry));
}
json_writer_end_object(writer);
break;
}
case QTYPE_QLIST: {
QList *val = qobject_to(QList, obj);
QListEntry *entry;
json_writer_start_array(writer, name);
QLIST_FOREACH_ENTRY(val, entry) {
to_json(writer, NULL, qlist_entry_obj(entry));
}
json_writer_end_array(writer);
break;
}
case QTYPE_QBOOL: {
QBool *val = qobject_to(QBool, obj);
json_writer_bool(writer, name, qbool_get_bool(val));
break;
}
default:
abort();
}
}
GString *qobject_to_json_pretty(const QObject *obj, bool pretty)
{
JSONWriter *writer = json_writer_new(pretty);
to_json(writer, NULL, obj);
return json_writer_get_and_free(writer);
}
GString *qobject_to_json(const QObject *obj)
{
return qobject_to_json_pretty(obj, false);
}
+189
View File
@@ -0,0 +1,189 @@
/*
* QList Module
*
* Copyright (C) 2009 Red Hat Inc.
*
* Authors:
* Luiz Capitulino <[email protected]>
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING.LIB file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qobject/qbool.h"
#include "qobject/qlist.h"
#include "qobject/qnull.h"
#include "qobject/qnum.h"
#include "qobject/qstring.h"
#include "qemu/queue.h"
#include "qobject-internal.h"
/**
* qlist_new(): Create a new QList
*
* Return strong reference.
*/
QList *qlist_new(void)
{
QList *qlist;
qlist = g_malloc(sizeof(*qlist));
qobject_init(QOBJECT(qlist), QTYPE_QLIST);
QTAILQ_INIT(&qlist->head);
return qlist;
}
QList *qlist_copy(QList *src)
{
QList *dst = qlist_new();
QListEntry *entry;
QObject *elt;
QLIST_FOREACH_ENTRY(src, entry) {
elt = qlist_entry_obj(entry);
qobject_ref(elt);
qlist_append_obj(dst, elt);
}
return dst;
}
/**
* qlist_append_obj(): Append an QObject into QList
*
* NOTE: ownership of 'value' is transferred to the QList
*/
void qlist_append_obj(QList *qlist, QObject *value)
{
QListEntry *entry;
entry = g_malloc(sizeof(*entry));
entry->value = value;
QTAILQ_INSERT_TAIL(&qlist->head, entry, next);
}
void qlist_append_int(QList *qlist, int64_t value)
{
qlist_append(qlist, qnum_from_int(value));
}
void qlist_append_bool(QList *qlist, bool value)
{
qlist_append(qlist, qbool_from_bool(value));
}
void qlist_append_str(QList *qlist, const char *value)
{
qlist_append(qlist, qstring_from_str(value));
}
void qlist_append_null(QList *qlist)
{
qlist_append(qlist, qnull());
}
QObject *qlist_pop(QList *qlist)
{
QListEntry *entry;
QObject *ret;
if (qlist == NULL || QTAILQ_EMPTY(&qlist->head)) {
return NULL;
}
entry = QTAILQ_FIRST(&qlist->head);
QTAILQ_REMOVE(&qlist->head, entry, next);
ret = entry->value;
g_free(entry);
return ret;
}
QObject *qlist_peek(QList *qlist)
{
QListEntry *entry;
if (qlist == NULL || QTAILQ_EMPTY(&qlist->head)) {
return NULL;
}
entry = QTAILQ_FIRST(&qlist->head);
return entry->value;
}
int qlist_empty(const QList *qlist)
{
return QTAILQ_EMPTY(&qlist->head);
}
size_t qlist_size(const QList *qlist)
{
size_t count = 0;
QListEntry *entry;
QLIST_FOREACH_ENTRY(qlist, entry) {
count++;
}
return count;
}
/**
* qlist_is_equal(): Test whether the two QLists are equal
*
* In order to be considered equal, the respective two objects at each
* index of the two lists have to compare equal (regarding
* qobject_is_equal()), and both lists have to have the same number of
* elements.
* That means both lists have to contain equal objects in equal order.
*/
bool qlist_is_equal(const QObject *x, const QObject *y)
{
const QList *list_x = qobject_to(QList, x);
const QList *list_y = qobject_to(QList, y);
const QListEntry *entry_x, *entry_y;
entry_x = qlist_first(list_x);
entry_y = qlist_first(list_y);
while (entry_x && entry_y) {
if (!qobject_is_equal(qlist_entry_obj(entry_x),
qlist_entry_obj(entry_y)))
{
return false;
}
entry_x = qlist_next(entry_x);
entry_y = qlist_next(entry_y);
}
return !entry_x && !entry_y;
}
/**
* qlist_destroy_obj(): Free all the memory allocated by a QList
*/
void qlist_destroy_obj(QObject *obj)
{
QList *qlist;
QListEntry *entry, *next_entry;
assert(obj != NULL);
qlist = qobject_to(QList, obj);
QTAILQ_FOREACH_SAFE(entry, &qlist->head, next, next_entry) {
QTAILQ_REMOVE(&qlist->head, entry, next);
qobject_unref(entry->value);
g_free(entry);
}
g_free(qlist);
}
void qlist_unref(QList *q)
{
qobject_unref(q);
}
+125
View File
@@ -0,0 +1,125 @@
/*
* QLit literal qobject
*
* Copyright IBM, Corp. 2009
* Copyright (c) 2013, 2015, 2017 Red Hat Inc.
*
* Authors:
* Anthony Liguori <[email protected]>
* Markus Armbruster <[email protected]>
* Marc-André Lureau <[email protected]>
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING.LIB file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qobject/qlit.h"
#include "qobject/qbool.h"
#include "qobject/qlist.h"
#include "qobject/qnum.h"
#include "qobject/qdict.h"
#include "qobject/qstring.h"
#include "qobject/qnull.h"
static bool qlit_equal_qdict(const QLitObject *lhs, const QDict *qdict)
{
int i;
for (i = 0; lhs->value.qdict[i].key; i++) {
QObject *obj = qdict_get(qdict, lhs->value.qdict[i].key);
if (!qlit_equal_qobject(&lhs->value.qdict[i].value, obj)) {
return false;
}
}
/* Note: the literal qdict must not contain duplicates, this is
* considered a programming error and it isn't checked here. */
if (qdict_size(qdict) != i) {
return false;
}
return true;
}
static bool qlit_equal_qlist(const QLitObject *lhs, const QList *qlist)
{
QListEntry *e;
int i = 0;
QLIST_FOREACH_ENTRY(qlist, e) {
QObject *obj = qlist_entry_obj(e);
if (!qlit_equal_qobject(&lhs->value.qlist[i], obj)) {
return false;
}
i++;
}
return !e && lhs->value.qlist[i].type == QTYPE_NONE;
}
bool qlit_equal_qobject(const QLitObject *lhs, const QObject *rhs)
{
if (!rhs || lhs->type != qobject_type(rhs)) {
return false;
}
switch (lhs->type) {
case QTYPE_QBOOL:
return lhs->value.qbool == qbool_get_bool(qobject_to(QBool, rhs));
case QTYPE_QNUM:
return lhs->value.qnum == qnum_get_int(qobject_to(QNum, rhs));
case QTYPE_QSTRING:
return (strcmp(lhs->value.qstr,
qstring_get_str(qobject_to(QString, rhs))) == 0);
case QTYPE_QDICT:
return qlit_equal_qdict(lhs, qobject_to(QDict, rhs));
case QTYPE_QLIST:
return qlit_equal_qlist(lhs, qobject_to(QList, rhs));
case QTYPE_QNULL:
return true;
default:
break;
}
return false;
}
QObject *qobject_from_qlit(const QLitObject *qlit)
{
switch (qlit->type) {
case QTYPE_QNULL:
return QOBJECT(qnull());
case QTYPE_QNUM:
return QOBJECT(qnum_from_int(qlit->value.qnum));
case QTYPE_QSTRING:
return QOBJECT(qstring_from_str(qlit->value.qstr));
case QTYPE_QDICT: {
QDict *qdict = qdict_new();
QLitDictEntry *e;
for (e = qlit->value.qdict; e->key; e++) {
qdict_put_obj(qdict, e->key, qobject_from_qlit(&e->value));
}
return QOBJECT(qdict);
}
case QTYPE_QLIST: {
QList *qlist = qlist_new();
QLitObject *e;
for (e = qlit->value.qlist; e->type != QTYPE_NONE; e++) {
qlist_append_obj(qlist, qobject_from_qlit(e));
}
return QOBJECT(qlist);
}
case QTYPE_QBOOL:
return QOBJECT(qbool_from_bool(qlit->value.qbool));
default:
g_assert_not_reached();
}
return NULL;
}
+36
View File
@@ -0,0 +1,36 @@
/*
* QNull
*
* Copyright (C) 2015 Red Hat, Inc.
*
* Authors:
* Markus Armbruster <[email protected]>
*
* This work is licensed under the terms of the GNU LGPL, version 2.1
* or later. See the COPYING.LIB file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qobject/qnull.h"
#include "qobject-internal.h"
QNull qnull_ = {
.base = {
.type = QTYPE_QNULL,
.refcnt = 1,
},
};
/**
* qnull_is_equal(): Always return true because any two QNull objects
* are equal.
*/
bool qnull_is_equal(const QObject *x, const QObject *y)
{
return true;
}
void qnull_unref(QNull *q)
{
qobject_unref(q);
}
+242
View File
@@ -0,0 +1,242 @@
/*
* QNum Module
*
* Copyright (C) 2009 Red Hat Inc.
*
* Authors:
* Luiz Capitulino <[email protected]>
* Anthony Liguori <[email protected]>
* Marc-André Lureau <[email protected]>
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING.LIB file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qobject/qnum.h"
#include "qobject-internal.h"
/**
* qnum_from_int(): Create a new QNum from an int64_t
*
* Return strong reference.
*/
QNum *qnum_from_int(int64_t value)
{
QNum *qn = g_new(QNum, 1);
qobject_init(QOBJECT(qn), QTYPE_QNUM);
qn->kind = QNUM_I64;
qn->u.i64 = value;
return qn;
}
/**
* qnum_from_uint(): Create a new QNum from an uint64_t
*
* Return strong reference.
*/
QNum *qnum_from_uint(uint64_t value)
{
QNum *qn = g_new(QNum, 1);
qobject_init(QOBJECT(qn), QTYPE_QNUM);
qn->kind = QNUM_U64;
qn->u.u64 = value;
return qn;
}
/**
* qnum_from_double(): Create a new QNum from a double
*
* Return strong reference.
*/
QNum *qnum_from_double(double value)
{
QNum *qn = g_new(QNum, 1);
qobject_init(QOBJECT(qn), QTYPE_QNUM);
qn->kind = QNUM_DOUBLE;
qn->u.dbl = value;
return qn;
}
/**
* qnum_get_try_int(): Get an integer representation of the number
*
* Return true on success.
*/
bool qnum_get_try_int(const QNum *qn, int64_t *val)
{
switch (qn->kind) {
case QNUM_I64:
*val = qn->u.i64;
return true;
case QNUM_U64:
if (qn->u.u64 > INT64_MAX) {
return false;
}
*val = qn->u.u64;
return true;
case QNUM_DOUBLE:
return false;
}
g_assert_not_reached();
}
/**
* qnum_get_int(): Get an integer representation of the number
*
* assert() on failure.
*/
int64_t qnum_get_int(const QNum *qn)
{
int64_t val;
bool success = qnum_get_try_int(qn, &val);
assert(success);
return val;
}
/**
* qnum_get_uint(): Get an unsigned integer from the number
*
* Return true on success.
*/
bool qnum_get_try_uint(const QNum *qn, uint64_t *val)
{
switch (qn->kind) {
case QNUM_I64:
if (qn->u.i64 < 0) {
return false;
}
*val = qn->u.i64;
return true;
case QNUM_U64:
*val = qn->u.u64;
return true;
case QNUM_DOUBLE:
return false;
}
g_assert_not_reached();
}
/**
* qnum_get_uint(): Get an unsigned integer from the number
*
* assert() on failure.
*/
uint64_t qnum_get_uint(const QNum *qn)
{
uint64_t val;
bool success = qnum_get_try_uint(qn, &val);
assert(success);
return val;
}
/**
* qnum_get_double(): Get a float representation of the number
*
* qnum_get_double() loses precision for integers beyond 53 bits.
*/
double qnum_get_double(QNum *qn)
{
switch (qn->kind) {
case QNUM_I64:
return qn->u.i64;
case QNUM_U64:
return qn->u.u64;
case QNUM_DOUBLE:
return qn->u.dbl;
}
g_assert_not_reached();
}
char *qnum_to_string(QNum *qn)
{
switch (qn->kind) {
case QNUM_I64:
return g_strdup_printf("%" PRId64, qn->u.i64);
case QNUM_U64:
return g_strdup_printf("%" PRIu64, qn->u.u64);
case QNUM_DOUBLE:
/* 17 digits suffice for IEEE double */
return g_strdup_printf("%.17g", qn->u.dbl);
}
g_assert_not_reached();
}
/**
* qnum_is_equal(): Test whether the two QNums are equal
*
* Negative integers are never considered equal to unsigned integers,
* but positive integers in the range [0, INT64_MAX] are considered
* equal independently of whether the QNum's kind is i64 or u64.
*
* Doubles are never considered equal to integers.
*/
bool qnum_is_equal(const QObject *x, const QObject *y)
{
QNum *num_x = qobject_to(QNum, x);
QNum *num_y = qobject_to(QNum, y);
switch (num_x->kind) {
case QNUM_I64:
switch (num_y->kind) {
case QNUM_I64:
/* Comparison in native int64_t type */
return num_x->u.i64 == num_y->u.i64;
case QNUM_U64:
/* Implicit conversion of x to uin64_t, so we have to
* check its sign before */
return num_x->u.i64 >= 0 && num_x->u.i64 == num_y->u.u64;
case QNUM_DOUBLE:
return false;
}
abort();
case QNUM_U64:
switch (num_y->kind) {
case QNUM_I64:
return qnum_is_equal(y, x);
case QNUM_U64:
/* Comparison in native uint64_t type */
return num_x->u.u64 == num_y->u.u64;
case QNUM_DOUBLE:
return false;
}
abort();
case QNUM_DOUBLE:
switch (num_y->kind) {
case QNUM_I64:
case QNUM_U64:
return false;
case QNUM_DOUBLE:
/* Comparison in native double type */
return num_x->u.dbl == num_y->u.dbl;
}
abort();
}
abort();
}
/**
* qnum_destroy_obj(): Free all memory allocated by a
* QNum object
*/
void qnum_destroy_obj(QObject *obj)
{
assert(obj != NULL);
g_free(qobject_to(QNum, obj));
}
void qnum_unref(QNum *q)
{
qobject_unref(q);
}
+39
View File
@@ -0,0 +1,39 @@
/*
* QObject internals
*
* Copyright (C) 2015 Red Hat, Inc.
*
* This work is licensed under the terms of the GNU LGPL, version 2.1
* or later. See the COPYING.LIB file in the top-level directory.
*/
#ifndef QOBJECT_INTERNAL_H
#define QOBJECT_INTERNAL_H
#include "qobject/qobject.h"
static inline void qobject_init(QObject *obj, QType type)
{
assert(QTYPE_NONE < type && type < QTYPE__MAX);
obj->base.refcnt = 1;
obj->base.type = type;
}
void qbool_destroy_obj(QObject *obj);
bool qbool_is_equal(const QObject *x, const QObject *y);
void qdict_destroy_obj(QObject *obj);
bool qdict_is_equal(const QObject *x, const QObject *y);
void qlist_destroy_obj(QObject *obj);
bool qlist_is_equal(const QObject *x, const QObject *y);
bool qnull_is_equal(const QObject *x, const QObject *y);
void qnum_destroy_obj(QObject *obj);
bool qnum_is_equal(const QObject *x, const QObject *y);
void qstring_destroy_obj(QObject *obj);
bool qstring_is_equal(const QObject *x, const QObject *y);
#endif
+72
View File
@@ -0,0 +1,72 @@
/*
* QObject
*
* Copyright (C) 2015 Red Hat, Inc.
*
* This work is licensed under the terms of the GNU LGPL, version 2.1
* or later. See the COPYING.LIB file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qobject/qbool.h"
#include "qobject/qnull.h"
#include "qobject/qnum.h"
#include "qobject/qdict.h"
#include "qobject/qlist.h"
#include "qobject/qstring.h"
#include "qobject-internal.h"
QEMU_BUILD_BUG_MSG(
offsetof(QNull, base) != 0 ||
offsetof(QNum, base) != 0 ||
offsetof(QString, base) != 0 ||
offsetof(QDict, base) != 0 ||
offsetof(QList, base) != 0 ||
offsetof(QBool, base) != 0,
"base qobject must be at offset 0");
static void (*qdestroy[QTYPE__MAX])(QObject *) = {
[QTYPE_NONE] = NULL, /* No such object exists */
[QTYPE_QNULL] = NULL, /* qnull_ is indestructible */
[QTYPE_QNUM] = qnum_destroy_obj,
[QTYPE_QSTRING] = qstring_destroy_obj,
[QTYPE_QDICT] = qdict_destroy_obj,
[QTYPE_QLIST] = qlist_destroy_obj,
[QTYPE_QBOOL] = qbool_destroy_obj,
};
void qobject_destroy(QObject *obj)
{
assert(!obj->base.refcnt);
assert(QTYPE_QNULL < obj->base.type && obj->base.type < QTYPE__MAX);
qdestroy[obj->base.type](obj);
}
static bool (*qis_equal[QTYPE__MAX])(const QObject *, const QObject *) = {
[QTYPE_NONE] = NULL, /* No such object exists */
[QTYPE_QNULL] = qnull_is_equal,
[QTYPE_QNUM] = qnum_is_equal,
[QTYPE_QSTRING] = qstring_is_equal,
[QTYPE_QDICT] = qdict_is_equal,
[QTYPE_QLIST] = qlist_is_equal,
[QTYPE_QBOOL] = qbool_is_equal,
};
bool qobject_is_equal(const QObject *x, const QObject *y)
{
/* We cannot test x == y because an object does not need to be
* equal to itself (e.g. NaN floats are not). */
if (!x && !y) {
return true;
}
if (!x || !y || x->base.type != y->base.type) {
return false;
}
assert(QTYPE_NONE < x->base.type && x->base.type < QTYPE__MAX);
return qis_equal[x->base.type](x, y);
}
+107
View File
@@ -0,0 +1,107 @@
/*
* QString Module
*
* Copyright (C) 2009 Red Hat Inc.
*
* Authors:
* Luiz Capitulino <[email protected]>
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING.LIB file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qobject/qstring.h"
#include "qobject-internal.h"
/**
* qstring_new(): Create a new empty QString
*
* Return strong reference.
*/
QString *qstring_new(void)
{
return qstring_from_str("");
}
/**
* qstring_from_substr(): Create a new QString from a C string substring
*
* Return string reference
*/
QString *qstring_from_substr(const char *str, size_t start, size_t end)
{
QString *qstring;
assert(start <= end);
qstring = g_malloc(sizeof(*qstring));
qobject_init(QOBJECT(qstring), QTYPE_QSTRING);
qstring->string = g_strndup(str + start, end - start);
return qstring;
}
/**
* qstring_from_str(): Create a new QString from a regular C string
*
* Return strong reference.
*/
QString *qstring_from_str(const char *str)
{
return qstring_from_substr(str, 0, strlen(str));
}
/**
* qstring_from_gstring(): Convert a GString to a QString
*
* Return strong reference.
*/
QString *qstring_from_gstring(GString *gstr)
{
QString *qstring;
qstring = g_malloc(sizeof(*qstring));
qobject_init(QOBJECT(qstring), QTYPE_QSTRING);
qstring->string = g_string_free(gstr, false);
return qstring;
}
/**
* qstring_get_str(): Return a pointer to the stored string
*
* NOTE: Should be used with caution, if the object is deallocated
* this pointer becomes invalid.
*/
const char *qstring_get_str(const QString *qstring)
{
return qstring->string;
}
/**
* qstring_is_equal(): Test whether the two QStrings are equal
*/
bool qstring_is_equal(const QObject *x, const QObject *y)
{
return !strcmp(qobject_to(QString, x)->string,
qobject_to(QString, y)->string);
}
/**
* qstring_destroy_obj(): Free all memory allocated by a QString
* object
*/
void qstring_destroy_obj(QObject *obj)
{
QString *qs;
assert(obj != NULL);
qs = qobject_to(QString, obj);
g_free((char *)qs->string);
g_free(qs);
}
void qstring_unref(QString *q)
{
qobject_unref(q);
}