diff --git a/src/libnm-core-impl/nm-setting-private.h b/src/libnm-core-impl/nm-setting-private.h index f76c719342..39e5992126 100644 --- a/src/libnm-core-impl/nm-setting-private.h +++ b/src/libnm-core-impl/nm-setting-private.h @@ -332,6 +332,10 @@ gboolean _nm_setting_property_is_regular_secret_flags(NMSetting * setting, /*****************************************************************************/ +const NMSettInfoProperty * +_nm_sett_info_property_lookup_by_param_spec(const NMSettInfoSetting *sett_info, + const GParamSpec * param_spec); + static inline GArray * _nm_sett_info_property_override_create_array_sized(guint reserved_size) { diff --git a/src/libnm-core-impl/nm-setting.c b/src/libnm-core-impl/nm-setting.c index 4c51bc08f0..eef2a30d5c 100644 --- a/src/libnm-core-impl/nm-setting.c +++ b/src/libnm-core-impl/nm-setting.c @@ -263,6 +263,16 @@ _property_infos_sort(const NMSettInfoProperty *property_infos, return arr; } +static int +_property_lookup_by_param_spec_sort(gconstpointer p_a, gconstpointer p_b, gpointer user_data) +{ + const NMSettInfoPropertLookupByParamSpec *a = p_a; + const NMSettInfoPropertLookupByParamSpec *b = p_b; + + NM_CMP_DIRECT(a->param_spec_as_uint, b->param_spec_as_uint); + return 0; +} + void _nm_setting_class_commit(NMSettingClass * setting_class, NMMetaSettingType meta_type, @@ -271,10 +281,11 @@ _nm_setting_class_commit(NMSettingClass * setting_class, gint16 private_offset) { NMSettInfoSetting *sett_info; - gs_free GParamSpec **property_specs = NULL; - guint n_property_specs; - guint override_len; - guint i; + gs_free GParamSpec ** property_specs = NULL; + guint n_property_specs; + NMSettInfoPropertLookupByParamSpec *lookup_by_iter; + guint override_len; + guint i; nm_assert(NM_IS_SETTING_CLASS(setting_class)); nm_assert(!setting_class->setting_info); @@ -424,6 +435,33 @@ has_property_type: sett_info->property_infos_len, setting_class); + nm_assert(sett_info->property_infos_len < G_MAXUINT16); + sett_info->property_lookup_by_param_spec_len = 0; + for (i = 0; i < sett_info->property_infos_len; i++) { + if (sett_info->property_infos[i].param_spec) { + sett_info->property_lookup_by_param_spec_len++; + } + } + sett_info->property_lookup_by_param_spec = + g_new(NMSettInfoPropertLookupByParamSpec, sett_info->property_lookup_by_param_spec_len); + lookup_by_iter = + (NMSettInfoPropertLookupByParamSpec *) sett_info->property_lookup_by_param_spec; + for (i = 0; i < sett_info->property_infos_len; i++) { + const NMSettInfoProperty *property_info = &sett_info->property_infos[i]; + + if (property_info->param_spec) { + *(lookup_by_iter++) = (NMSettInfoPropertLookupByParamSpec){ + .param_spec_as_uint = (uintptr_t) ((gpointer) property_info->param_spec), + .property_info = property_info, + }; + } + } + g_qsort_with_data(sett_info->property_lookup_by_param_spec, + sett_info->property_lookup_by_param_spec_len, + sizeof(NMSettInfoPropertLookupByParamSpec), + _property_lookup_by_param_spec_sort, + NULL); + g_array_free(properties_override, TRUE); } @@ -473,6 +511,47 @@ _nm_setting_class_get_sett_info(NMSettingClass *setting_class) return sett_info; } +const NMSettInfoProperty * +_nm_sett_info_property_lookup_by_param_spec(const NMSettInfoSetting *sett_info, + const GParamSpec * param_spec) +{ + NMSettInfoPropertLookupByParamSpec needle; + int imin; + int imax; + int imid; + int cmp; + + nm_assert(sett_info); + nm_assert(param_spec); + + /* ensure that "int" is large enough to contain the index variables. */ + G_STATIC_ASSERT_EXPR(sizeof(int) > sizeof(sett_info->property_lookup_by_param_spec_len)); + + if (sett_info->property_lookup_by_param_spec_len == 0) + return NULL; + + needle.param_spec_as_uint = (uintptr_t) ((gpointer) param_spec); + + imin = 0; + imax = sett_info->property_lookup_by_param_spec_len - 1; + while (imin <= imax) { + imid = imin + (imax - imin) / 2; + + cmp = _property_lookup_by_param_spec_sort(&sett_info->property_lookup_by_param_spec[imid], + &needle, + NULL); + if (cmp == 0) + return sett_info->property_lookup_by_param_spec[imid].property_info; + + if (cmp < 0) + imin = imid + 1; + else + imax = imid - 1; + } + + return NULL; +} + /*****************************************************************************/ void diff --git a/src/libnm-core-impl/tests/test-setting.c b/src/libnm-core-impl/tests/test-setting.c index d00a9796b5..944b3cc5b5 100644 --- a/src/libnm-core-impl/tests/test-setting.c +++ b/src/libnm-core-impl/tests/test-setting.c @@ -4388,6 +4388,9 @@ test_setting_metadata(void) guint prop_idx; gs_free GParamSpec **property_specs = NULL; guint n_property_specs; + guint n_param_spec; + guint i; + guint j; g_assert(sis); @@ -4418,6 +4421,8 @@ test_setting_metadata(void) h_properties = g_hash_table_new(nm_str_hash, g_str_equal); + n_param_spec = 0; + for (prop_idx = 0; prop_idx < sis->property_infos_len; prop_idx++) { const NMSettInfoProperty *sip = &sis->property_infos[prop_idx]; GArray * property_types_data; @@ -4426,6 +4431,9 @@ test_setting_metadata(void) g_assert(sip->name); + if (sip->param_spec) + n_param_spec++; + if (prop_idx > 0) g_assert_cmpint(strcmp(sis->property_infos[prop_idx - 1].name, sip->name), <, 0); @@ -4587,6 +4595,44 @@ check_done:; g_assert_cmpstr(sis->property_infos[0].name, ==, NM_SETTING_NAME); } else g_assert_cmpint(meta_type, !=, NM_META_SETTING_TYPE_ETHTOOL); + + g_assert_cmpint(n_param_spec, >, 0); + g_assert_cmpint(n_param_spec, ==, sis->property_lookup_by_param_spec_len); + g_assert(sis->property_lookup_by_param_spec); + for (i = 0; i < sis->property_lookup_by_param_spec_len; i++) { + const NMSettInfoPropertLookupByParamSpec *p = &sis->property_lookup_by_param_spec[i]; + guint n_found; + + if (i > 0) { + g_assert_cmpint(sis->property_lookup_by_param_spec[i - 1].param_spec_as_uint, + <, + p->param_spec_as_uint); + } + g_assert(p->property_info); + g_assert(p->property_info >= sis->property_infos); + g_assert(p->property_info < &sis->property_infos[sis->property_infos_len]); + g_assert(p->property_info + == &sis->property_infos[p->property_info - sis->property_infos]); + + g_assert(p->property_info->param_spec); + g_assert(p->param_spec_as_uint + == ((uintptr_t) ((gpointer) p->property_info->param_spec))); + + g_assert(_nm_sett_info_property_lookup_by_param_spec(sis, p->property_info->param_spec) + == p->property_info); + + n_found = 0; + for (j = 0; j < sis->property_infos_len; j++) { + const NMSettInfoProperty *pip2 = &sis->property_infos[j]; + + if (pip2->param_spec + && p->param_spec_as_uint == ((uintptr_t) ((gpointer) pip2->param_spec))) { + g_assert(pip2 == p->property_info); + n_found++; + } + } + g_assert(n_found == 1); + } } { diff --git a/src/libnm-core-intern/nm-core-internal.h b/src/libnm-core-intern/nm-core-internal.h index 2933c73c29..6735a9e5b4 100644 --- a/src/libnm-core-intern/nm-core-internal.h +++ b/src/libnm-core-intern/nm-core-internal.h @@ -745,6 +745,15 @@ struct _NMSettInfoProperty { } to_dbus_data; }; +typedef struct { + /* we want to do binary search by "GParamSpec *", but unrelated pointers + * are not directly comparable in C. No problem, we convert them to + * uintptr_t for the search, that is guaranteed to work. */ + uintptr_t param_spec_as_uint; + + const NMSettInfoProperty *property_info; +} NMSettInfoPropertLookupByParamSpec; + typedef struct { const GVariantType *(*get_variant_type)(const struct _NMSettInfoSetting *sett_info, const char * name, @@ -784,8 +793,12 @@ struct _NMSettInfoSetting { */ const NMSettInfoProperty *const *property_infos_sorted; + const NMSettInfoPropertLookupByParamSpec *property_lookup_by_param_spec; + guint property_infos_len; + guint16 property_lookup_by_param_spec_len; + /* the offset in bytes to get the private data from the @self pointer. */ gint16 private_offset;