libnm: add NMRange
The next commit is going to introduce a new object in libnm to represent a range of ovs-port VLANs. A "range of integers" object seems something that can be used for other purposes in the future, so instead of adding an object specific for this case (e.g. NMOvsPortVlanRange), introduce a generic NMRange object that generically represents a range of non-negative integers.
This commit is contained in:
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
{
|
||||
|
@@ -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);
|
||||
|
@@ -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__ */
|
||||
|
Reference in New Issue
Block a user