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:
Beniamino Galvani
2022-11-18 17:07:23 +01:00
parent 29bec94ee8
commit 041e38b151
5 changed files with 346 additions and 0 deletions

View File

@@ -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;

View File

@@ -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

View File

@@ -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)
{

View File

@@ -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);

View File

@@ -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__ */