mirror of
https://github.com/mpv-player/mpv
synced 2025-01-09 01:36:25 +01:00
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.
This commit is contained in:
parent
0c1dd8a8f5
commit
7aae399239
83
m_config.c
83
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)
|
||||
static void substruct_write_ptr(void *ptr, void *val)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
17
m_option.h
17
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 */
|
||||
|
Loading…
Reference in New Issue
Block a user