diff --git a/src/libnm-client-impl/libnm.ver b/src/libnm-client-impl/libnm.ver index 42cb4e752..9a6c74be5 100644 --- a/src/libnm-client-impl/libnm.ver +++ b/src/libnm-client-impl/libnm.ver @@ -1884,6 +1884,14 @@ global: nm_client_wait_shutdown; nm_client_wait_shutdown_finish; nm_device_loopback_get_type; + nm_range_cmp; + nm_range_from_str; + nm_range_get_range; + nm_range_get_type; + nm_range_new; + nm_range_ref; + nm_range_to_str; + nm_range_unref; nm_setting_ip_config_get_dhcp_iaid; nm_setting_ip_config_get_dhcp_iaid; nm_setting_loopback_get_mtu; diff --git a/src/libnm-core-impl/nm-setting-private.h b/src/libnm-core-impl/nm-setting-private.h index 90d86e12f..6791d199e 100644 --- a/src/libnm-core-impl/nm-setting-private.h +++ b/src/libnm-core-impl/nm-setting-private.h @@ -302,6 +302,14 @@ typedef struct { /*****************************************************************************/ +struct _NMRange { + guint refcount; + guint64 start; + guint64 end; +}; + +/*****************************************************************************/ + #define NM_SETTING_PARAM_NONE 0 /* The property of the #NMSetting should be considered during comparisons that diff --git a/src/libnm-core-impl/nm-setting.c b/src/libnm-core-impl/nm-setting.c index a2416d88c..59e941dba 100644 --- a/src/libnm-core-impl/nm-setting.c +++ b/src/libnm-core-impl/nm-setting.c @@ -4123,6 +4123,233 @@ nm_setting_option_set_uint32(NMSetting *setting, const char *opt_name, guint32 v /*****************************************************************************/ +G_DEFINE_BOXED_TYPE(NMRange, nm_range, nm_range_ref, (GBoxedFreeFunc) nm_range_unref) + +static gboolean +NM_IS_RANGE(const NMRange *self) +{ + return self && self->refcount > 0; +} + +/** + * nm_range_new: + * @start: the first element of the range + * @end: the last element of the range, must be greater than or equal + * to @start. + * + * Creates a new #NMRange object for the given range. Setting @end + * equal to @start creates a single-element range. + * + * Returns: (transfer full): the new #NMRange object. + * + * Since: 1.42 + **/ +NMRange * +nm_range_new(guint64 start, guint64 end) +{ + NMRange *range; + + g_return_val_if_fail(start <= end, NULL); + + range = g_slice_new(NMRange); + *range = (NMRange){ + .refcount = 1, + .start = start, + .end = end, + }; + + return range; +} + +/** + * nm_range_ref: + * @range: the #NMRange + * + * Increases the reference count of the object. + * + * Returns: the input argument @range object. + * + * Since: 1.42 + **/ +NMRange * +nm_range_ref(const NMRange *range) +{ + g_return_val_if_fail(NM_IS_RANGE(range), NULL); + + nm_assert(range->refcount < G_MAXUINT); + + ((NMRange *) range)->refcount++; + return (NMRange *) range; +} + +/** + * nm_range_unref: + * @range: the #NMRange + * + * Decreases the reference count of the object. If the reference count + * reaches zero the object will be destroyed. + * + * Since: 1.42 + **/ +void +nm_range_unref(const NMRange *range) +{ + g_return_if_fail(NM_IS_RANGE(range)); + + nm_assert(range->refcount != 0); + + if (--((NMRange *) range)->refcount == 0) + g_slice_free(NMRange, (NMRange *) range); +} + +/** + * nm_range_cmp: + * @a: a #NMRange + * @b: another #NMRange + * + * Compare two ranges. + * + * Returns: zero if the two instances are equivalent or + * a non-zero integer otherwise. This defines a total ordering + * over the ranges. + * + * Since: 1.42 + **/ +int +nm_range_cmp(const NMRange *a, const NMRange *b) +{ + NM_CMP_SELF(a, b); + NM_CMP_FIELD(a, b, start); + NM_CMP_FIELD(a, b, end); + + return 0; +} + +/** + * nm_range_get_range: + * @range: the #NMRange + * @start: (out): location to store the start value + * @end: (out): location to store the end value + * + * Gets the start and end values for the range. + * + * Returns: %TRUE if the range contains more than one + * element, %FALSE otherwise. + * + * Since: 1.42 + **/ +gboolean +nm_range_get_range(const NMRange *range, guint64 *start, guint64 *end) +{ + /* with LTO and optimization, the compiler complains that the + * output variables are not initialized. In practice, the function + * only sets the output on success. But make the compiler happy. + */ + NM_SET_OUT(start, 0); + NM_SET_OUT(end, 0); + + g_return_val_if_fail(NM_IS_RANGE(range), 0); + + NM_SET_OUT(start, range->start); + NM_SET_OUT(end, range->end); + + return range->start != range->end; +} + +/** + * nm_range_to_str: + * @range: the %NMRange + * + * Convert a %NMRange to a string. + * + * Returns: (transfer full): a string representing the range. + * + * Since: 1.42 + */ +char * +nm_range_to_str(const NMRange *range) +{ + char buf[200]; + char *b = buf; + gsize l = sizeof(buf); + + g_return_val_if_fail(NM_IS_RANGE(range), NULL); + + nm_strbuf_append(&b, &l, "%" G_GUINT64_FORMAT, range->start); + if (range->start != range->end) + nm_strbuf_append(&b, &l, "-%" G_GUINT64_FORMAT, range->end); + + nm_assert(l > 0); + return nm_memdup_nul(buf, sizeof(buf) - l); +} + +/** + * nm_range_from_str: + * @str: the string representation of a range + * @error: (out) (allow-none): location to store the error on failure + * + * Parses the string representation of the range to create a %NMRange + * instance. + * + * Returns: (transfer full): the %NMRange or %NULL + * + * Since: 1.42 + */ +NMRange * +nm_range_from_str(const char *str, GError **error) +{ + gs_free char *str_free = NULL; + guint64 start; + guint64 end = 0; + char *c; + + g_return_val_if_fail(str, NULL); + g_return_val_if_fail(!error || !*error, NULL); + + c = strchr(str, '-'); + if (c) { + str = nm_strndup_a(300, str, c - str, &str_free); + c++; + } + + start = _nm_utils_ascii_str_to_uint64(str, 10, 0, G_MAXUINT64, 0); + if (errno != 0) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + "invalid range start '%s'", + str); + return NULL; + } + + if (c) { + end = _nm_utils_ascii_str_to_uint64(c, 10, 0, G_MAXUINT64, 0); + if (errno != 0) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + "invalid range end '%s'", + c); + return NULL; + } + if (end < start) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + "invalid range %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT + ", start must be less than or equal to end", + start, + end); + return NULL; + } + } else + end = start; + + return nm_range_new(start, end); +} + +/*****************************************************************************/ + static void get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { diff --git a/src/libnm-core-impl/tests/test-setting.c b/src/libnm-core-impl/tests/test-setting.c index b8267de48..40cdd4792 100644 --- a/src/libnm-core-impl/tests/test-setting.c +++ b/src/libnm-core-impl/tests/test-setting.c @@ -4165,6 +4165,88 @@ test_routing_rule(gconstpointer test_data) /*****************************************************************************/ +static void +test_ranges(void) +{ + GError *error = NULL; + NMRange *r1; + NMRange *r2; + guint64 start; + guint64 end; + char *str = NULL; + char *str2 = NULL; + + r1 = nm_range_from_str("99", &error); + nmtst_assert_success(r1, error); + nm_range_get_range(r1, &start, &end); + g_assert_cmpint(start, ==, 99); + g_assert_cmpint(end, ==, 99); + str = nm_range_to_str(r1); + g_assert_cmpstr(str, ==, "99"); + nm_clear_g_free(&str); + nm_range_unref(r1); + + r1 = nm_range_from_str("1000-2000", &error); + nmtst_assert_success(r1, error); + nm_range_get_range(r1, &start, &end); + g_assert_cmpint(start, ==, 1000); + g_assert_cmpint(end, ==, 2000); + str = nm_range_to_str(r1); + g_assert_cmpstr(str, ==, "1000-2000"); + nm_clear_g_free(&str); + nm_range_unref(r1); + + r1 = nm_range_from_str("0", &error); + nmtst_assert_success(r1, error); + nm_range_unref(r1); + + r1 = nm_range_from_str("-1", &error); + nmtst_assert_no_success(r1, error); + g_clear_error(&error); + + r1 = nm_range_from_str("foobar", &error); + nmtst_assert_no_success(r1, error); + g_clear_error(&error); + + r1 = nm_range_from_str("200-100", &error); + nmtst_assert_no_success(r1, error); + g_clear_error(&error); + + r1 = nm_range_from_str("100-200", &error); + nmtst_assert_success(r1, error); + r2 = nm_range_from_str("100-200", &error); + nmtst_assert_success(r2, error); + g_assert_cmpint(nm_range_cmp(r1, r2), ==, 0); + nm_range_unref(r1); + nm_range_unref(r2); + + r1 = nm_range_from_str("100-200", &error); + nmtst_assert_success(r1, error); + r2 = nm_range_from_str("1", &error); + nmtst_assert_success(r2, error); + g_assert_cmpint(nm_range_cmp(r1, r2), ==, 1); + nm_range_ref(r1); + nm_range_unref(r1); + nm_range_unref(r1); + nm_range_unref(r2); + + r1 = nm_range_new(G_MAXUINT64 - 1, G_MAXUINT64); + g_assert(r1); + str = nm_range_to_str(r1); + g_assert_cmpstr(str, ==, "18446744073709551614-18446744073709551615"); + r2 = nm_range_from_str(str, &error); + nmtst_assert_success(r2, error); + str2 = nm_range_to_str(r2); + g_assert_cmpstr(str, ==, str2); + g_assert_cmpint(nm_range_cmp(r1, r2), ==, 0); + nm_range_unref(r1); + nm_range_unref(r2); + nm_clear_g_free(&str); + nm_clear_g_free(&str2); +} + +/*****************************************************************************/ + static void test_parse_tc_handle(void) { @@ -5316,6 +5398,8 @@ main(int argc, char **argv) g_test_add_data_func("/libnm/settings/routing-rule/1", GINT_TO_POINTER(0), test_routing_rule); + g_test_add_func("/libnm/settings/ranges", test_ranges); + g_test_add_func("/libnm/parse-tc-handle", test_parse_tc_handle); g_test_add_func("/libnm/test_team_setting", test_team_setting); diff --git a/src/libnm-core-public/nm-setting.h b/src/libnm-core-public/nm-setting.h index ae898458a..737cf8831 100644 --- a/src/libnm-core-public/nm-setting.h +++ b/src/libnm-core-public/nm-setting.h @@ -260,6 +260,25 @@ const GVariantType *nm_setting_get_dbus_property_type(NMSetting *setting, /*****************************************************************************/ +typedef struct _NMRange NMRange; + +NM_AVAILABLE_IN_1_42 +GType nm_range_get_type(void); +NM_AVAILABLE_IN_1_42 +NMRange *nm_range_new(guint64 start, guint64 end); +NM_AVAILABLE_IN_1_42 +NMRange *nm_range_ref(const NMRange *range); +NM_AVAILABLE_IN_1_42 +void nm_range_unref(const NMRange *range); +NM_AVAILABLE_IN_1_42 +int nm_range_cmp(const NMRange *a, const NMRange *b); +NM_AVAILABLE_IN_1_42 +gboolean nm_range_get_range(const NMRange *range, guint64 *start, guint64 *end); +NM_AVAILABLE_IN_1_42 +char *nm_range_to_str(const NMRange *range); +NM_AVAILABLE_IN_1_42 +NMRange *nm_range_from_str(const char *str, GError **error); + G_END_DECLS #endif /* __NM_SETTING_H__ */