From 7aae399239d02e48de9060dded1d0232310d2141 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 6 Aug 2012 17:45:17 +0200 Subject: m_config: support auto-allocated sub-structs Given your option struct has a field that is a pointer to another struct, this commit allows you to declare options that write into that other struct. The code in m_config will dereference the pointer field on its own if such an option is accessed. If the field is NULL on initialization of the containing m_config, the struct is automatically allocated. OPT_SUBSTRUCT() can be used to declare such a field. struct m_sub_options is used to describe the pointed-to struct, and includes size and defaults if the struct has to be allocated by m_config. --- m_config.c | 83 ++++++++++++++++++++++++++++++++++---------------------------- m_config.h | 6 +++++ m_option.c | 6 +++++ m_option.h | 17 +++++++++++++ 4 files changed, 75 insertions(+), 37 deletions(-) diff --git a/m_config.c b/m_config.c index ab410646e1..56886e08eb 100644 --- a/m_config.c +++ b/m_config.c @@ -139,26 +139,18 @@ static int list_options(struct m_option *opt, char *name, char *param) return M_OPT_EXIT; } -static void *optstruct_ptr(const struct m_config *config, - const struct m_option *opt) +// The memcpys are supposed to work around the struct aliasing violation, +// that would result if we just dereferenced a void** (where the void** is +// actually casted from struct some_type* ). +static void *substruct_read_ptr(void *ptr) { - return m_option_get_ptr(opt, config->optstruct); + void *res; + memcpy(&res, ptr, sizeof(void*)); + return res; } - -static void optstruct_get(const struct m_config *config, - const struct m_option *opt, - void *dst) -{ - if (opt->type->copy) - opt->type->copy(opt, dst, optstruct_ptr(config, opt)); -} - -static void optstruct_set(const struct m_config *config, - const struct m_option *opt, - const void *src) +static void substruct_write_ptr(void *ptr, void *val) { - if (opt->type->copy) - opt->type->copy(opt, optstruct_ptr(config, opt), src); + memcpy(ptr, &val, sizeof(void*)); } static void m_config_add_option(struct m_config *config, @@ -172,9 +164,7 @@ static int config_destroy(void *p) if (copt->flags & M_CFG_OPT_ALIAS) continue; if (copt->opt->type->flags & M_OPT_TYPE_DYNAMIC) { - void *ptr = m_option_get_ptr(copt->opt, config->optstruct); - if (ptr) - m_option_free(copt->opt, ptr); + m_option_free(copt->opt, copt->data); } if (copt->global_backup) m_option_free(copt->opt, copt->global_backup); @@ -240,7 +230,7 @@ static void ensure_backup(struct m_config *config, struct m_config_option *co) if (co->global_backup) return; co->global_backup = talloc_zero_size(co, co->opt->type->size); - optstruct_get(config, co->opt, co->global_backup); + m_option_copy(co->opt, co->global_backup, co->data); } void m_config_enter_file_local(struct m_config *config) @@ -255,7 +245,7 @@ void m_config_leave_file_local(struct m_config *config) config->file_local_mode = false; for (struct m_config_option *co = config->opts; co; co = co->next) { if (co->global_backup) { - optstruct_set(config, co->opt, co->global_backup); + m_option_copy(co->opt, co->data, co->global_backup); m_option_free(co->opt, co->global_backup); talloc_free(co->global_backup); co->global_backup = NULL; @@ -284,6 +274,11 @@ static void m_config_add_option(struct m_config *config, co = talloc_zero(config, struct m_config_option); co->opt = arg; + void *optstruct = config->optstruct; + if (parent && (parent->opt->type->flags & M_OPT_TYPE_USE_SUBSTRUCT)) + optstruct = substruct_read_ptr(parent->data); + co->data = arg->new ? (char *)optstruct + arg->offset : arg->p; + if (parent) { // Merge case: pretend it has no parent (note that we still must follow // the "real" parent for accessing struct fields) @@ -303,15 +298,22 @@ static void m_config_add_option(struct m_config *config, // Option with children -> add them if (arg->type->flags & M_OPT_TYPE_HAS_CHILD) { - const struct m_option *sub = arg->p; - add_options(config, co, sub); + if (arg->type->flags & M_OPT_TYPE_USE_SUBSTRUCT) { + const struct m_sub_options *subopts = arg->priv; + if (!substruct_read_ptr(co->data)) { + void *subdata = m_config_alloc_struct(config, subopts); + substruct_write_ptr(co->data, subdata); + } + add_options(config, co, subopts->opts); + } else { + const struct m_option *sub = arg->p; + add_options(config, co, sub); + } } else { - struct m_config_option *i; // Check if there is already an option pointing to this address - if (arg->p || arg->new && arg->offset >= 0) { - for (i = config->opts; i; i = i->next) { - if (arg->new ? (i->opt->new && i->opt->offset == arg->offset) - : (!i->opt->new && i->opt->p == arg->p)) { + if (co->data) { + for (struct m_config_option *i = config->opts; i; i = i->next) { + if (co->data == i->data) { // So we don't save the same vars more than 1 time co->flags |= M_CFG_OPT_ALIAS; break; @@ -324,18 +326,17 @@ static void m_config_add_option(struct m_config *config, if (arg->defval) { // Target data in optstruct is supposed to be cleared (consider // m_option freeing previously set dynamic data). - optstruct_set(config, arg, arg->defval); + m_option_copy(arg, co->data, arg->defval); } else if (arg->type->flags & M_OPT_TYPE_DYNAMIC) { // Initialize dynamically managed fields from static data (like // string options): copy the option into temporary memory, // clear the original option (to void m_option freeing the // static data), copy it back. - void *init_data = optstruct_ptr(config, arg); - if (init_data) { + if (co->data) { void *temp = talloc_zero_size(NULL, arg->type->size); - m_option_copy(arg, temp, init_data); - memset(init_data, 0, arg->type->size); - optstruct_set(config, arg, temp); + m_option_copy(arg, temp, co->data); + memset(co->data, 0, arg->type->size); + m_option_copy(arg, co->data, temp); m_option_free(arg, temp); talloc_free(temp); } @@ -431,8 +432,7 @@ static int m_config_parse_option(struct m_config *config, void *optstruct, if (set) ensure_backup(config, co); - void *dst = set ? m_option_get_ptr(co->opt, optstruct) : NULL; - return m_option_parse(co->opt, name, param, dst); + return m_option_parse(co->opt, name, param, set ? co->data : NULL); } static int parse_subopts(struct m_config *config, void *optstruct, char *name, @@ -630,3 +630,12 @@ void m_config_set_profile(struct m_config *config, struct m_profile *p) config->profile_depth--; config->mode = prev_mode; } + +void *m_config_alloc_struct(void *talloc_parent, + const struct m_sub_options *subopts) +{ + void *substruct = talloc_zero_size(talloc_parent, subopts->size); + if (subopts->defaults) + memcpy(substruct, subopts->defaults, subopts->size); + return substruct; +} diff --git a/m_config.h b/m_config.h index beeab80a53..80897ce0e4 100644 --- a/m_config.h +++ b/m_config.h @@ -30,6 +30,7 @@ typedef struct m_profile m_profile_t; struct m_option; struct m_option_type; +struct m_sub_options; // Config option struct m_config_option { @@ -38,6 +39,8 @@ struct m_config_option { char *name; // Option description. const struct m_option *opt; + // Raw value of the option. + void *data; // Raw value of the backup of the global value (or NULL). void *global_backup; // See \ref ConfigOptionFlags. @@ -198,4 +201,7 @@ int m_config_set_profile_option(struct m_config *config, struct m_profile *p, */ void m_config_set_profile(struct m_config *config, struct m_profile *p); +void *m_config_alloc_struct(void *talloc_parent, + const struct m_sub_options *subopts); + #endif /* MPLAYER_M_CONFIG_H */ diff --git a/m_option.c b/m_option.c index 35b5efc98d..22abee1496 100644 --- a/m_option.c +++ b/m_option.c @@ -907,6 +907,12 @@ const m_option_type_t m_option_type_subconfig = { .parse = parse_subconf, }; +const m_option_type_t m_option_type_subconfig_struct = { + .name = "Subconfig", + .flags = M_OPT_TYPE_HAS_CHILD | M_OPT_TYPE_USE_SUBSTRUCT, + .parse = parse_subconf, +}; + #include "libmpcodecs/img_format.h" /* FIXME: snyc with img_format.h */ diff --git a/m_option.h b/m_option.h index e0321d1de1..ab83027a85 100644 --- a/m_option.h +++ b/m_option.h @@ -52,6 +52,7 @@ extern const m_option_type_t m_option_type_print; extern const m_option_type_t m_option_type_print_func; extern const m_option_type_t m_option_type_print_func_param; extern const m_option_type_t m_option_type_subconfig; +extern const m_option_type_t m_option_type_subconfig_struct; extern const m_option_type_t m_option_type_imgfmt; extern const m_option_type_t m_option_type_afmt; @@ -148,6 +149,12 @@ struct m_opt_choice_alternatives { int value; }; +// m_option.priv points to this if M_OPT_TYPE_USE_SUBSTRUCT is used +struct m_sub_options { + const struct m_option *opts; + size_t size; + const void *defaults; +}; // FIXME: backward compatibility #define CONF_TYPE_FLAG (&m_option_type_flag) @@ -338,6 +345,10 @@ struct m_option { // takes no parameter. #define M_OPT_TYPE_OLD_SYNTAX_NO_PARAM (1 << 3) +// modify M_OPT_TYPE_HAS_CHILD so that m_option::p points to +// struct m_sub_options, instead of a direct m_option array. +#define M_OPT_TYPE_USE_SUBSTRUCT (1 << 4) + ///////////////////////////// Parser flags ///////////////////////////////// // On success parsers return the number of arguments consumed: 0 or 1. @@ -461,6 +472,12 @@ static inline void m_option_free(const m_option_t *opt, void *dst) #define OPT_CHOICE_(optname, varname, flags, choices, ...) OPT_GENERAL(optname, varname, flags, .priv = (void *)&(const struct m_opt_choice_alternatives[]){OPT_HELPER_REMOVEPAREN choices, {NULL}}, __VA_ARGS__) #define OPT_TIME(...) OPT_GENERAL(__VA_ARGS__, .type = &m_option_type_time) +// subconf must have the type struct m_sub_options. +// flagv should be M_OPT_MERGE or M_OPT_FLATTEN. +// varname refers to the field, that must be a pointer to a field described by +// the subconf struct. +#define OPT_SUBSTRUCT(varname, subconf, flagv) OPT_GENERAL("-", varname, flagv, .type = &m_option_type_subconfig_struct, .priv = (void*)&subconf) + #define OPT_BASE_STRUCT struct MPOpts #endif /* MPLAYER_M_OPTION_H */ -- cgit v1.2.3