[libvirt] [PATCH 5/9] util: json: Split out code to create json value objects
John Ferlan
jferlan at redhat.com
Tue Oct 14 10:46:15 UTC 2014
On 10/14/2014 03:29 AM, Peter Krempa wrote:
> Our qemu monitor code has a converter from key-value pairs to a json
> value object. I want to re-use the code later and having it part of the
> monitor command generator is inflexible. Split it out into a separate
> helper.
> ---
> src/libvirt_private.syms | 2 +
> src/qemu/qemu_monitor_json.c | 161 +--------------------------------
> src/util/virjson.c | 211 +++++++++++++++++++++++++++++++++++++++++++
> src/util/virjson.h | 5 +
> 4 files changed, 220 insertions(+), 159 deletions(-)
>
> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
> index d6265ac..314b2b8 100644
> --- a/src/libvirt_private.syms
> +++ b/src/libvirt_private.syms
> @@ -1503,6 +1503,8 @@ virJSONValueObjectAppendNumberLong;
> virJSONValueObjectAppendNumberUint;
> virJSONValueObjectAppendNumberUlong;
> virJSONValueObjectAppendString;
> +virJSONValueObjectCreate;
> +virJSONValueObjectCreateVArgs;
> virJSONValueObjectGet;
> virJSONValueObjectGetBoolean;
> virJSONValueObjectGetKey;
> diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
> index 7f23aae..2967193 100644
> --- a/src/qemu/qemu_monitor_json.c
> +++ b/src/qemu/qemu_monitor_json.c
> @@ -431,7 +431,6 @@ qemuMonitorJSONMakeCommandRaw(bool wrap, const char *cmdname, ...)
> virJSONValuePtr obj;
> virJSONValuePtr jargs = NULL;
> va_list args;
> - char *key;
>
> va_start(args, cmdname);
>
> @@ -442,164 +441,8 @@ qemuMonitorJSONMakeCommandRaw(bool wrap, const char *cmdname, ...)
> cmdname) < 0)
> goto error;
>
> - while ((key = va_arg(args, char *)) != NULL) {
> - int ret;
> - char type;
> -
> - if (strlen(key) < 3) {
> - virReportError(VIR_ERR_INTERNAL_ERROR,
> - _("argument key '%s' is too short, missing type prefix"),
> - key);
> - goto error;
> - }
> -
> - /* Keys look like s:name the first letter is a type code:
> - * Explanation of type codes:
> - * s: string value, must be non-null
> - * S: string value, omitted if null
> - *
> - * i: signed integer value
> - * j: signed integer value, error if negative
> - * z: signed integer value, omitted if zero
> - * y: signed integer value, omitted if zero, error if negative
> - *
> - * I: signed long integer value
> - * J: signed long integer value, error if negative
> - * Z: signed long integer value, omitted if zero
> - * Y: signed long integer value, omitted if zero, error if negative
> - *
> - * u: unsigned integer value
> - * p: unsigned integer value, omitted if zero
> - *
> - * U: unsigned long integer value (see below for quirks)
> - * P: unsigned long integer value, omitted if zero
> - *
> - * b: boolean value
> - * B: boolean value, omitted if false
> - *
> - * d: double precision floating point number
> - * n: json null value
> - * a: json array
> - */
> - type = key[0];
> - key += 2;
> -
> - if (!jargs &&
> - !(jargs = virJSONValueNewObject()))
> - goto error;
> -
> - /* This doesn't support maps, but no command uses those. */
> - switch (type) {
> - case 'S':
> - case 's': {
> - char *val = va_arg(args, char *);
> - if (!val) {
> - if (type == 'S')
> - continue;
> -
> - virReportError(VIR_ERR_INTERNAL_ERROR,
> - _("argument key '%s' must not have null value"),
> - key);
> - goto error;
> - }
> - ret = virJSONValueObjectAppendString(jargs, key, val);
> - } break;
> -
> - case 'z':
> - case 'y':
> - case 'j':
> - case 'i': {
> - int val = va_arg(args, int);
> -
> - if (val < 0 && (type == 'j' || type == 'y')) {
> - virReportError(VIR_ERR_INTERNAL_ERROR,
> - _("argument key '%s' must not be negative"),
> - key);
> - goto error;
> - }
> -
> - if (!val && (type == 'z' || type == 'y'))
> - continue;
> -
> - ret = virJSONValueObjectAppendNumberInt(jargs, key, val);
> - } break;
> -
> - case 'p':
> - case 'u': {
> - unsigned int val = va_arg(args, unsigned int);
> -
> - if (!val && type == 'p')
> - continue;
> -
> - ret = virJSONValueObjectAppendNumberUint(jargs, key, val);
> - } break;
> -
> - case 'Z':
> - case 'Y':
> - case 'J':
> - case 'I': {
> - long long val = va_arg(args, long long);
> -
> - if (val < 0 && (type == 'J' || type == 'Y')) {
> - virReportError(VIR_ERR_INTERNAL_ERROR,
> - _("argument key '%s' must not be negative"),
> - key);
> - goto error;
> - }
> -
> - if (!val && (type == 'Z' || type == 'Y'))
> - continue;
> -
> - ret = virJSONValueObjectAppendNumberLong(jargs, key, val);
> - } break;
> -
> - case 'P':
> - case 'U': {
> - /* qemu silently truncates numbers larger than LLONG_MAX,
> - * so passing the full range of unsigned 64 bit integers
> - * is not safe here. Pass them as signed 64 bit integers
> - * instead.
> - */
> - long long val = va_arg(args, long long);
> -
> - if (!val && type == 'P')
> - continue;
> -
> - ret = virJSONValueObjectAppendNumberLong(jargs, key, val);
> - } break;
> -
> - case 'd': {
> - double val = va_arg(args, double);
> - ret = virJSONValueObjectAppendNumberDouble(jargs, key, val);
> - } break;
> -
> - case 'B':
> - case 'b': {
> - int val = va_arg(args, int);
> -
> - if (!val && type == 'B')
> - continue;
> -
> - ret = virJSONValueObjectAppendBoolean(jargs, key, val);
> - } break;
> -
> - case 'n': {
> - ret = virJSONValueObjectAppendNull(jargs, key);
> - } break;
> -
> - case 'a': {
> - virJSONValuePtr val = va_arg(args, virJSONValuePtr);
> - ret = virJSONValueObjectAppend(jargs, key, val);
> - } break;
> -
> - default:
> - virReportError(VIR_ERR_INTERNAL_ERROR,
> - _("unsupported data type '%c' for arg '%s'"), type, key - 2);
> - goto error;
> - }
> - if (ret < 0)
> - goto error;
> - }
> + if (virJSONValueObjectCreateVArgs(&jargs, args) < 0)
> + goto error;
>
> if (jargs &&
> virJSONValueObjectAppend(obj, wrap ? "data" : "arguments", jargs) < 0)
> diff --git a/src/util/virjson.c b/src/util/virjson.c
> index ec83b2f..0dfeaed 100644
> --- a/src/util/virjson.c
> +++ b/src/util/virjson.c
> @@ -63,6 +63,217 @@ struct _virJSONParser {
> };
>
>
> +/**
> + * virJSONValueObjectCreateVArgs:
> + * @obj: returns the created JSON object
> + * @...: a key-value argument pairs, terminated by NULL
> + *
> + * Creates a JSON value object filled with key-value pairs supplied as variable
> + * argument list.
> + *
> + * Keys look like s:name the first letter is a type code:
> + * Explanation of type codes:
> + * s: string value, must be non-null
> + * S: string value, omitted if null
> + *
> + * i: signed integer value
> + * j: signed integer value, error if negative
> + * z: signed integer value, omitted if zero
> + * y: signed integer value, omitted if zero, error if negative
> + *
> + * I: signed long integer value
> + * J: signed long integer value, error if negative
> + * Z: signed long integer value, omitted if zero
> + * Y: signed long integer value, omitted if zero, error if negative
> + *
> + * u: unsigned integer value
> + * p: unsigned integer value, omitted if zero
> + *
> + * U: unsigned long integer value (see below for quirks)
> + * P: unsigned long integer value, omitted if zero
> + *
> + * b: boolean value
> + * B: boolean value, omitted if false
> + *
> + * d: double precision floating point number
> + * n: json null value
> + * a: json array
> + *
> + * The value corresponds to the selected type.
> + *
> + * Returns -1 on error. 1 on success, if at least one key:pair was valid 0
> + * in case of no error but nothing was filled (@obj will be NULL).
> + */
> +int
> +virJSONValueObjectCreateVArgs(virJSONValuePtr *obj, va_list args)
> +{
> + virJSONValuePtr jargs = NULL;
> + char type;
> + char *key;
> + int ret = -1;
> + int rc = -2; /* -2 is a sentinel to check if at least one entry was added */
> +
> + *obj = NULL;
> +
> + if (!(jargs = virJSONValueNewObject()))
> + goto cleanup;
> +
> + while ((key = va_arg(args, char *)) != NULL) {
> +
> + if (strlen(key) < 3) {
> + virReportError(VIR_ERR_INTERNAL_ERROR,
> + _("argument key '%s' is too short, missing type prefix"),
> + key);
> + goto cleanup;
> + }
> +
> + type = key[0];
> + key += 2;
> +
> + /* This doesn't support maps, but no command uses those. */
> + switch (type) {
> + case 'S':
> + case 's': {
> + char *val = va_arg(args, char *);
> + if (!val) {
> + if (type == 'S')
> + continue;
> +
> + virReportError(VIR_ERR_INTERNAL_ERROR,
> + _("argument key '%s' must not have null value"),
> + key);
> + goto cleanup;
> + }
> + rc = virJSONValueObjectAppendString(jargs, key, val);
> + } break;
> +
> + case 'z':
> + case 'y':
> + case 'j':
> + case 'i': {
> + int val = va_arg(args, int);
> +
> + if (val < 0 && (type == 'j' || type == 'y')) {
> + virReportError(VIR_ERR_INTERNAL_ERROR,
> + _("argument key '%s' must not be negative"),
> + key);
> + goto cleanup;
> + }
> +
> + if (!val && (type == 'z' || type == 'y'))
> + continue;
> +
> + rc = virJSONValueObjectAppendNumberInt(jargs, key, val);
> + } break;
> +
> + case 'p':
> + case 'u': {
> + unsigned int val = va_arg(args, unsigned int);
> +
> + if (!val && type == 'p')
> + continue;
> +
> + rc = virJSONValueObjectAppendNumberUint(jargs, key, val);
> + } break;
> +
> + case 'Z':
> + case 'Y':
> + case 'J':
> + case 'I': {
> + long long val = va_arg(args, long long);
> +
> + if (val < 0 && (type == 'J' || type == 'Y')) {
> + virReportError(VIR_ERR_INTERNAL_ERROR,
> + _("argument key '%s' must not be negative"),
> + key);
> + goto cleanup;
> + }
> +
> + if (!val && (type == 'Z' || type == 'Y'))
> + continue;
> +
> + rc = virJSONValueObjectAppendNumberLong(jargs, key, val);
> + } break;
> +
> + case 'P':
> + case 'U': {
> + /* qemu silently truncates numbers larger than LLONG_MAX,
> + * so passing the full range of unsigned 64 bit integers
> + * is not safe here. Pass them as signed 64 bit integers
> + * instead.
> + */
> + long long val = va_arg(args, long long);
> +
> + if (!val && type == 'P')
> + continue;
> +
> + rc = virJSONValueObjectAppendNumberLong(jargs, key, val);
> + } break;
> +
> + case 'd': {
> + double val = va_arg(args, double);
> + rc = virJSONValueObjectAppendNumberDouble(jargs, key, val);
> + } break;
> +
> + case 'B':
> + case 'b': {
> + int val = va_arg(args, int);
> +
> + if (!val && type == 'B')
> + continue;
> +
> + rc = virJSONValueObjectAppendBoolean(jargs, key, val);
> + } break;
> +
> + case 'n': {
> + rc = virJSONValueObjectAppendNull(jargs, key);
> + } break;
> +
> + case 'a': {
> + virJSONValuePtr val = va_arg(args, virJSONValuePtr);
> + rc = virJSONValueObjectAppend(jargs, key, val);
> + } break;
> +
> + default:
> + virReportError(VIR_ERR_INTERNAL_ERROR,
> + _("unsupported data type '%c' for arg '%s'"), type, key - 2);
> + goto cleanup;
> + }
> +
> + if (rc < 0)
> + goto cleanup;
> + }
> +
> + /* verify that we added at least one key-value pair */
> + if (rc == -2) {
I didn't check any of the virJSAONValue* functions called, but if any
return -2 for whatever reason "sometime" in the future, then this logic
is flawed.
However, at this point you should have an array size/count right? And if
> 0, then we added something (eg, npairs count should be incremented)
thus virJSONValueObjectKeysNumber() should return > 0
> + ret = 0;
> + goto cleanup;
> + }
> +
> + *obj = jargs;
> + jargs = NULL;
> + ret = 1;
> +
> + cleanup:
> + virJSONValueFree(jargs);
> + return ret;
> +}
> +
> +
> +int
> +virJSONValueObjectCreate(virJSONValuePtr *obj, ...)
> +{
> + va_list args;
> + int ret;
> +
> + va_start(args, obj);
> + ret = virJSONValueObjectCreateVArgs(obj, args);
> + va_end(args);
> +
> + return ret;
> +}
> +
> +
> void
> virJSONValueFree(virJSONValuePtr value)
> {
> diff --git a/src/util/virjson.h b/src/util/virjson.h
> index 9487729..be603ae 100644
> --- a/src/util/virjson.h
> +++ b/src/util/virjson.h
> @@ -26,6 +26,8 @@
>
> # include "internal.h"
>
> +# include <stdarg.h>
> +
>
> typedef enum {
> VIR_JSON_TYPE_OBJECT,
> @@ -79,6 +81,9 @@ struct _virJSONValue {
>
> void virJSONValueFree(virJSONValuePtr value);
>
> +int virJSONValueObjectCreate(virJSONValuePtr *obj, ...);
> +int virJSONValueObjectCreateVArgs(virJSONValuePtr *obj, va_list args);
Add an ATTRIBUTE_NONNULL(1) for this definition, since you deref in
function.
ACK with adjustments
John
> +
> virJSONValuePtr virJSONValueNewString(const char *data);
> virJSONValuePtr virJSONValueNewStringLen(const char *data, size_t length);
> virJSONValuePtr virJSONValueNewNumberInt(int data);
>
More information about the libvir-list
mailing list