X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/0706f7c85b3c0783f92d44b551f362884db0f4bd..fcf73f66a67f5e58c18216f8c8651e38cf4d90af:/qobject/qdict.c diff --git a/qobject/qdict.c b/qobject/qdict.c index a3924f24bd..97e881b3a4 100644 --- a/qobject/qdict.c +++ b/qobject/qdict.c @@ -46,9 +46,9 @@ QDict *qdict_new(void) */ QDict *qobject_to_qdict(const QObject *obj) { - if (qobject_type(obj) != QTYPE_QDICT) + if (!obj || qobject_type(obj) != QTYPE_QDICT) { return NULL; - + } return container_of(obj, QDict, base); } @@ -229,8 +229,7 @@ double qdict_get_double(const QDict *qdict, const char *key) */ int64_t qdict_get_int(const QDict *qdict, const char *key) { - QObject *obj = qdict_get_obj(qdict, key, QTYPE_QINT); - return qint_get_int(qobject_to_qint(obj)); + return qint_get_int(qobject_to_qint(qdict_get(qdict, key))); } /** @@ -241,10 +240,9 @@ int64_t qdict_get_int(const QDict *qdict, const char *key) * * Return bool mapped by 'key'. */ -int qdict_get_bool(const QDict *qdict, const char *key) +bool qdict_get_bool(const QDict *qdict, const char *key) { - QObject *obj = qdict_get_obj(qdict, key, QTYPE_QBOOL); - return qbool_get_int(qobject_to_qbool(obj)); + return qbool_get_bool(qobject_to_qbool(qdict_get(qdict, key))); } /** @@ -270,7 +268,7 @@ QList *qdict_get_qlist(const QDict *qdict, const char *key) */ QDict *qdict_get_qdict(const QDict *qdict, const char *key) { - return qobject_to_qdict(qdict_get_obj(qdict, key, QTYPE_QDICT)); + return qobject_to_qdict(qdict_get(qdict, key)); } /** @@ -298,13 +296,9 @@ const char *qdict_get_str(const QDict *qdict, const char *key) int64_t qdict_get_try_int(const QDict *qdict, const char *key, int64_t def_value) { - QObject *obj; + QInt *qint = qobject_to_qint(qdict_get(qdict, key)); - obj = qdict_get(qdict, key); - if (!obj || qobject_type(obj) != QTYPE_QINT) - return def_value; - - return qint_get_int(qobject_to_qint(obj)); + return qint ? qint_get_int(qint) : def_value; } /** @@ -314,15 +308,11 @@ int64_t qdict_get_try_int(const QDict *qdict, const char *key, * dictionary or if the stored object is not of QBool type * 'def_value' will be returned. */ -int qdict_get_try_bool(const QDict *qdict, const char *key, int def_value) +bool qdict_get_try_bool(const QDict *qdict, const char *key, bool def_value) { - QObject *obj; + QBool *qbool = qobject_to_qbool(qdict_get(qdict, key)); - obj = qdict_get(qdict, key); - if (!obj || qobject_type(obj) != QTYPE_QBOOL) - return def_value; - - return qbool_get_int(qobject_to_qbool(obj)); + return qbool ? qbool_get_bool(qbool) : def_value; } /** @@ -477,6 +467,39 @@ static void qdict_destroy_obj(QObject *obj) g_free(qdict); } +/** + * 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) { + qobject_incref(val); + qdict_put_obj(dst, key, 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(dst, key, qstring_from_str(val)); +} + static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix); @@ -597,18 +620,37 @@ void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start) } } +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 of QDicts. Every entry in the original QDict with a key prefixed - * "%u.", where %u designates an unsigned integer starting at 0 and + * 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. The function terminates when there - * is no entry in the QDict with a prefix directly (incrementally) following the - * last one. - * Example: {"0.a": 42, "0.b": 23, "1.x": 0, "3.y": 1, "o.o": 7} - * (or {"1.x": 0, "3.y": 1, "0.a": 42, "o.o": 7, "0.b": 23}) - * => [{"a": 42, "b": 23}, {"x": 0}] - * and {"3.y": 1, "o.o": 7} (remainder of the old QDict) + * 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) { @@ -617,19 +659,134 @@ void qdict_array_split(QDict *src, QList **dst) *dst = qlist_new(); for (i = 0; i < UINT_MAX; i++) { + QObject *subqobj; + bool is_subqdict; QDict *subqdict; - char prefix[32]; + 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); - qdict_extract_subqdict(src, &subqdict, prefix); - if (!qdict_size(subqdict)) { - QDECREF(subqdict); + /* 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; } - qlist_append_obj(*dst, QOBJECT(subqdict)); + if (is_subqdict) { + qdict_extract_subqdict(src, &subqdict, prefix); + assert(qdict_size(subqdict) > 0); + } else { + qobject_incref(subqobj); + qdict_del(src, indexstr); + } + + qlist_append_obj(*dst, subqobj ?: QOBJECT(subqdict)); + } +} + +/** + * 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; + size_t slen = 32 + subqdict_len; + char indexstr[slen], prefix[slen]; + size_t snprintf_ret; + + snprintf_ret = snprintf(indexstr, slen, "%s%u", subqdict, i); + assert(snprintf_ret < slen); + + subqobj = qdict_get(src, indexstr); + + snprintf_ret = snprintf(prefix, slen, "%s%u.", subqdict, i); + assert(snprintf_ret < slen); + + subqdict_entries = qdict_count_prefixed_entries(src, 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)) { + qobject_incref(entry->value); + qdict_put_obj(dest, entry->key, entry->value); + qdict_del(src, entry->key); + } + + entry = next; } }