On 03/10/2014 01:31 AM, Chunyan Liu wrote: > Add some qemu_opt functions to replace the same functionality of > QEMUOptionParameter handling. > > Signed-off-by: Dong Xu Wang <wdon...@linux.vnet.ibm.com> > Signed-off-by: Chunyan Liu <cy...@suse.com> > --- > include/qemu/option.h | 9 +++ > util/qemu-option.c | 188 > ++++++++++++++++++++++++++++++++++++++++++++++---- > 2 files changed, 184 insertions(+), 13 deletions(-) >
> +++ b/util/qemu-option.c > @@ -35,6 +35,7 @@ > > static const QemuOptDesc *find_desc_by_name(const QemuOptDesc *desc, > const char *name); > +static void qemu_opt_del(QemuOpt *opt); Again, any reason you can't hoist the function so that it occurs in topological order, rather than needing to add a forward declaration? > } > > +static size_t count_opts_list(QemuOptsList *list) > +{ > + QemuOptDesc *desc = NULL; > + size_t num_opts = 0; > + > + if (!list) { > + return 0; > + } > + > + desc = list->desc; > + while (desc && desc->name) { > + num_opts++; > + desc++; > + } This returns 0 for option lists where opts_accepts_any() is true. Is that going to bite us? Let's see... > +/* Create a new QemuOptsList with a desc of the merge of the first > + * and second. It will allocate space for one new QemuOptsList plus > + * enough space for QemuOptDesc in first and second QemuOptsList. > + * First argument's QemuOptDesc members take precedence over second's. > + * The result's name and implied_opt_name are not copied from them. > + * Both merge_lists should not be set. Both lists can be NULL. > + */ > +QemuOptsList *qemu_opts_append(QemuOptsList *dst, Naming this 'dst' is confusing, as it is not the destination. Rather, you are creating a new list, and the new list is the destination of the concatenation of first and second list arguments, where descriptions in the first list have priority over any duplicates in the second list. > + QemuOptsList *list) > +{ > + size_t num_opts, num_dst_opts; > + QemuOptsList *tmp; > + QemuOptDesc *desc; > + > + if (!dst && !list) { > + return NULL; > + } > + > + num_opts = count_opts_list(dst); > + num_opts += count_opts_list(list); > + tmp = g_malloc0(sizeof(QemuOptsList) + > + (num_opts + 1) * sizeof(QemuOptDesc)); ...Already I can see problems if either 'dst' or 'list' passes opts_accepts_any(). If exactly one has a desc array with no name, then that QemuOptsList accepts any option spelling, but the resulting list in 'tmp' will only accept the options in the other input. Probably worth asserting that neither input passes opts_accepts_any(). > + QTAILQ_INIT(&tmp->head); > + num_dst_opts = 0; This name is confusing - it is actually tracking how many descriptions you have copied into 'tmp', and NOT how many options are in 'dst'. > + > + /* copy dst->desc to new list */ > + if (dst) { > + desc = dst->desc; > + while (desc && desc->name) { > + tmp->desc[num_dst_opts++] = *desc; > + tmp->desc[num_dst_opts].name = NULL; Redundant assignment; your g_malloc0() above already guaranteed this. > + desc++; > + } > + } > + > + /* add list->desc to new list */ > + if (list) { > + desc = list->desc; > + while (desc && desc->name) { > + if (find_desc_by_name(tmp->desc, desc->name) == NULL) { So every call to qemu_opts_append() is O(n^2) behavior - I guess it's okay: as long as we are always passing short lists, we don't need it to scale very well. > + tmp->desc[num_dst_opts++] = *desc; > + tmp->desc[num_dst_opts].name = NULL; Again, redundant assignment to NULL. > + } > + desc++; > + } > + } > + > + return tmp; Okay, at the end of the day, 'tmp' is a single block of malloc'd storage, where descriptions are shared with pre-existing opt lists (probably worth documenting that the lifetime of the returned value of this function is no longer than the lifetime of the two lists you concatenated, and that a simple g_free() of the list will suffice - or point to qemu_opts_free() as the recommended cleanup method). > +} > + > /* > * Parses a parameter string (param) into an option list (dest). > * > @@ -574,6 +643,29 @@ const char *qemu_opt_get(QemuOpts *opts, const char > *name) > return opt ? opt->str : NULL; > } > > +char *qemu_opt_get_del(QemuOpts *opts, const char *name) Please, add a comment what this function does. Something like: If 'opts' already has a value for the key 'name', remove that key from the options list and return the value. Otherwise, if 'opts' has a default value for the key, return that default. Otherwise, return NULL. Also, I think qemu_opt_get_del and qemu_opts_append may be better as two separate commits, particularly if you are also enhancing the testsuite with each commit to prove the functionality works. Why are your later callers using a common helper function, but qemu_opt_get_del() repeating a lot of the body of qemu_opt_get()? Shouldn't qemu_opt_get() and qemu_opt_get_del() call into a common helper? > > -bool qemu_opt_get_bool(QemuOpts *opts, const char *name, bool defval) > +static bool _qemu_opt_get_bool(QemuOpts *opts, const char *name, Nothing else in the code base uses the '_qemu' namespace. You need to name this function something better. A comment for this method would be helpful, something like: Determine the boolean value for key 'name' from 'opts', defaulting to 'defval' if one has not already been added to the list and if the list does not already provide a default. Additionally, if 'del', remove the value from the list. > + bool defval, bool del) > { > QemuOpt *opt; > + bool ret = defval; > > if (opts == NULL) { > return defval; > @@ -599,19 +693,35 @@ bool qemu_opt_get_bool(QemuOpts *opts, const char > *name, bool defval) > if (opt == NULL) { > const QemuOptDesc *desc = find_desc_by_name(opts->list->desc, name); > if (desc && desc->def_value_str) { > - parse_option_bool(name, desc->def_value_str, &defval, > &error_abort); > + parse_option_bool(name, desc->def_value_str, &ret, &error_abort); > } > - return defval; > + return ret; This favors the QemuOptDesc default over the defval that the user passed in. I think that's an important detail to document that use of the def_value_str means the defval argument is never used. > } > if (opt->desc) { > assert(opt->desc->type == QEMU_OPT_BOOL); > } > - return opt->value.boolean; > + ret = opt->value.boolean; Same problem as in 4/25 - I don't think you can rely on opt->value.boolean being valid if you don't track the union discriminator, and right now, the union discriminator is tracked only via opt->desc. > + if (del) { > + qemu_opt_del(opt); > + } > + return ret; Couldn't this whole function be shortened to: char *tmp; if (del) { tmp = qemu_opt_get_del(opts, name); } else { tmp = qemu_opt_get(opts, name); } if (!tmp) { return defval; } parse_option_bool(name, tmp, &defval, &error_abort); g_free(tmp); return defval; or even shorter, if you combine qemu_opt_get{,_del} into calling a common helper function? > } > > -uint64_t qemu_opt_get_number(QemuOpts *opts, const char *name, uint64_t > defval) > +bool qemu_opt_get_bool(QemuOpts *opts, const char *name, bool defval) > +{ > + return _qemu_opt_get_bool(opts, name, defval, false); > +} > + > +bool qemu_opt_get_bool_del(QemuOpts *opts, const char *name, bool defval) > +{ > + return _qemu_opt_get_bool(opts, name, defval, true); > +} Other than the _qemu namespace, this is a good example of how a common helper function makes it easier to implement two variants. The _number and _size variants will need similar treatment as the _bool. > @@ -1302,3 +1443,24 @@ int qemu_opts_foreach(QemuOptsList *list, > qemu_opts_loopfunc func, void *opaque, > loc_pop(&loc); > return rc; > } > + > +/* free a QemuOptsList, can accept NULL as arguments */ > +void qemu_opts_free(QemuOptsList *list) > +{ > + if (!list) { > + return; > + } > + > + g_free(list); > +} g_free() is safe to call on NULL. Which means qemu_opts_free(foo) is a fancy way to spell g_free(foo). Do we need this function? > + > +void qemu_opts_print_help(QemuOptsList *list) > +{ > + int i; > + printf("Supported options:\n"); > + for (i = 0; list && list->desc[i].name; i++) { > + printf("%-16s %s\n", list->desc[i].name, > + list->desc[i].help ? > + list->desc[i].help : ""); This prints trailing spaces for any description that lacks help text. Not the end of the world, but I try to be cleaner than that. > + } > +} Unrelated to either of the other two categories of functions - maybe this patch should be split into three parts. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org
signature.asc
Description: OpenPGP digital signature