All of lore.kernel.org
 help / color / mirror / Atom feed
From: Markus Armbruster <armbru@redhat.com>
To: "Daniel P. Berrange" <berrange@redhat.com>
Cc: qemu-devel@nongnu.org, "Max Reitz" <mreitz@redhat.com>,
	"Marc-André Lureau" <marcandre.lureau@gmail.com>,
	"Paolo Bonzini" <pbonzini@redhat.com>,
	"Andreas Färber" <afaerber@suse.de>
Subject: Re: [Qemu-devel] [PATCH v8 1/7] qdict: implement a qdict_crumple method for un-flattening a dict
Date: Fri, 15 Jul 2016 17:06:08 +0200	[thread overview]
Message-ID: <87shvbc55r.fsf@dusky.pond.sub.org> (raw)
In-Reply-To: <1468505019-18154-2-git-send-email-berrange@redhat.com> (Daniel P. Berrange's message of "Thu, 14 Jul 2016 15:03:33 +0100")

"Daniel P. Berrange" <berrange@redhat.com> writes:

> The qdict_flatten() method will take a dict whose elements are
> further nested dicts/lists and flatten them by concatenating
> keys.
>
> The qdict_crumple() method aims to do the reverse, taking a flat
> qdict, and turning it into a set of nested dicts/lists. It will
> apply nesting based on the key name, with a '.' indicating a
> new level in the hierarchy. If the keys in the nested structure
> are all numeric, it will create a list, otherwise it will create
> a dict.
>
> If the keys are a mixture of numeric and non-numeric, or the
> numeric keys are not in strictly ascending order, an error will
> be reported.
>
> As an example, a flat dict containing
>
>  {
>    'foo.0.bar': 'one',
>    'foo.0.wizz': '1',
>    'foo.1.bar': 'two',
>    'foo.1.wizz': '2'
>  }
>
> will get turned into a dict with one element 'foo' whose
> value is a list. The list elements will each in turn be
> dicts.
>
>  {
>    'foo': [
>      { 'bar': 'one', 'wizz': '1' },
>      { 'bar': 'two', 'wizz': '2' }
>    ],
>  }
>
> If the key is intended to contain a literal '.', then it must
> be escaped as '..'. ie a flat dict
>
>   {
>      'foo..bar': 'wizz',
>      'bar.foo..bar': 'eek',
>      'bar.hello': 'world'
>   }
>
> Will end up as
>
>   {
>      'foo.bar': 'wizz',
>      'bar': {
>         'foo.bar': 'eek',
>         'hello': 'world'
>      }
>   }
>
> The intent of this function is that it allows a set of QemuOpts
> to be turned into a nested data structure that mirrors the nesting
> used when the same object is defined over QMP.
>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> ---
>  include/qapi/qmp/qdict.h |   1 +
>  qobject/qdict.c          | 283 +++++++++++++++++++++++++++++++++++++++++++++++
>  tests/check-qdict.c      | 241 ++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 525 insertions(+)
>
> diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h
> index 71b8eb0..8a3ac13 100644
> --- a/include/qapi/qmp/qdict.h
> +++ b/include/qapi/qmp/qdict.h
> @@ -73,6 +73,7 @@ void qdict_flatten(QDict *qdict);
>  void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start);
>  void qdict_array_split(QDict *src, QList **dst);
>  int qdict_array_entries(QDict *src, const char *subqdict);
> +QObject *qdict_crumple(QDict *src, bool recursive, Error **errp);
>  
>  void qdict_join(QDict *dest, QDict *src, bool overwrite);
>  
> diff --git a/qobject/qdict.c b/qobject/qdict.c
> index 60f158c..fd2d0e3 100644
> --- a/qobject/qdict.c
> +++ b/qobject/qdict.c
> @@ -17,6 +17,7 @@
>  #include "qapi/qmp/qbool.h"
>  #include "qapi/qmp/qstring.h"
>  #include "qapi/qmp/qobject.h"
> +#include "qapi/error.h"
>  #include "qemu/queue.h"
>  #include "qemu-common.h"
>  #include "qemu/cutils.h"
> @@ -683,6 +684,288 @@ void qdict_array_split(QDict *src, QList **dst)
>      }
>  }
>  
> +
> +/**
> + * 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.
> + *
> + * eg
> + *    '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)) {
> +
> +        if (qemu_strtoll(ent->key, NULL, 10, &val) == 0) {
> +            if (is_list == -1) {
> +                is_list = 1;
> +            } else if (!is_list) {
> +                error_setg(errp,
> +                           "Cannot crumple a dictionary with a mix of list "
> +                           "and non-list keys");
> +                return -1;
> +            }
> +            len++;
> +            if (val > max) {
> +                max = val;
> +            }
> +        } else {
> +            if (is_list == -1) {
> +                is_list = 0;
> +            } else if (is_list) {
> +                error_setg(errp,
> +                           "Cannot crumple a dictionary with a mix of list "
> +                           "and non-list keys");
> +                return -1;
> +            }
> +        }
> +    }
> +
> +    if (is_list == -1) {
> +        assert(!qdict_size(maybe_list));
> +        is_list = 0;
> +    }
> +
> +    if (len != (max + 1)) {
> +        error_setg(errp, "List indexes are not contigous, "

contiguous

> +                   "saw %zd elements but %zd largest index",
> +                   len, max);
> +        return -1;
> +    }
> +
> +    return is_list ? 1 : 0;

Why not simply return is_list?

> +}
[...]

  reply	other threads:[~2016-07-15 15:06 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-07-14 14:03 [Qemu-devel] [PATCH v8 0/7] Provide a QOM-based authorization API Daniel P. Berrange
2016-07-14 14:03 ` [Qemu-devel] [PATCH v8 1/7] qdict: implement a qdict_crumple method for un-flattening a dict Daniel P. Berrange
2016-07-15 15:06   ` Markus Armbruster [this message]
2016-07-14 14:03 ` [Qemu-devel] [PATCH v8 2/7] option: make parse_option_bool/number non-static Daniel P. Berrange
2016-07-15 15:13   ` Markus Armbruster
2016-07-14 14:03 ` [Qemu-devel] [PATCH v8 3/7] qapi: add a QmpInputVisitor that does string conversion Daniel P. Berrange
2016-07-15 15:36   ` Markus Armbruster
2016-07-15 16:27     ` Eric Blake
2016-07-14 14:03 ` [Qemu-devel] [PATCH v8 4/7] qom: support arbitrary non-scalar properties with -object Daniel P. Berrange
2016-07-14 14:03 ` [Qemu-devel] [PATCH v8 5/7] util: add QAuthZ object as an authorization base class Daniel P. Berrange
2016-07-14 14:03 ` [Qemu-devel] [PATCH v8 6/7] util: add QAuthZSimple object type for a simple access control list Daniel P. Berrange
2016-07-14 14:03 ` [Qemu-devel] [PATCH v8 7/7] acl: delete existing ACL implementation Daniel P. Berrange

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87shvbc55r.fsf@dusky.pond.sub.org \
    --to=armbru@redhat.com \
    --cc=afaerber@suse.de \
    --cc=berrange@redhat.com \
    --cc=marcandre.lureau@gmail.com \
    --cc=mreitz@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.