summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2015-05-12 09:24:05 +0200
committerThomas Haller <thaller@redhat.com>2015-06-17 11:41:43 +0200
commit0a3c1f57741677e21ec9efb9c67bfac81c31d208 (patch)
tree3035ab63e870fca5792433e131c340f9fbd637e2
parent56b07b1a3f26da472ae93ffb94a5928390057ef8 (diff)
downloadNetworkManager-0a3c1f57741677e21ec9efb9c67bfac81c31d208.tar.gz
utils: add nm_utils_is_power_of_two() macro
-rw-r--r--include/nm-macros-internal.h17
-rw-r--r--libnm-core/tests/test-general.c98
2 files changed, 115 insertions, 0 deletions
diff --git a/include/nm-macros-internal.h b/include/nm-macros-internal.h
index 9be6b0f321..0cb85cea74 100644
--- a/include/nm-macros-internal.h
+++ b/include/nm-macros-internal.h
@@ -181,6 +181,23 @@ nm_clear_g_source (guint *id)
/*****************************************************************************/
+/* Determine whether @x is a power of two (@x being an integer type).
+ * For the special cases @x equals zero or one, it also returns true.
+ * For negative @x, always returns FALSE. That only applies, is the data
+ * type of @x is signed. */
+#define nm_utils_is_power_of_two(x) ({ \
+ const typeof(x) __x = (x); \
+ \
+ ((__x & (__x - 1)) == 0) && \
+ /* Check if the value is negative. In that case, return FALSE.
+ * The first expression is a compile time constant, depending on whether
+ * the type is signed. The second expression is a clumsy way for (__x >= 0),
+ * which causes a compiler warning for unsigned types. */ \
+ ( ( ((typeof(__x)) -1) > ((typeof(__x)) 0) ) || (__x > 0) || (__x == 0) ); \
+ })
+
+/*****************************************************************************/
+
/* check if @flags has exactly one flag (@check) set. You should call this
* only with @check being a compile time constant and a power of two. */
#define NM_FLAGS_HAS(flags, check) \
diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c
index 1aca515a88..106b67606e 100644
--- a/libnm-core/tests/test-general.c
+++ b/libnm-core/tests/test-general.c
@@ -4358,6 +4358,103 @@ test_nm_utils_dns_option_find_idx (void)
/******************************************************************************/
+enum TEST_IS_POWER_OF_TWP_ENUM_SIGNED {
+ _DUMMY_1 = -1,
+};
+
+enum TEST_IS_POWER_OF_TWP_ENUM_UNSIGNED {
+ _DUMMY_2,
+};
+
+enum TEST_IS_POWER_OF_TWP_ENUM_SIGNED_64 {
+ _DUMMY_3 = (1LL << 40),
+};
+
+enum TEST_IS_POWER_OF_TWP_ENUM_UNSIGNED_64 {
+ _DUMMY_4a = -1,
+ _DUMMY_4b = (1LL << 40),
+};
+
+#define test_nm_utils_is_power_of_two_do(type, x, expect) \
+ G_STMT_START { \
+ typeof (x) x1 = (x); \
+ type x2 = (type) x1; \
+ \
+ if (((typeof (x1)) x2) == x1 && (x2 > 0 || x2 == 0)) { \
+ /* x2 equals @x, and is positive. Compare to @expect */ \
+ g_assert_cmpint (expect, ==, nm_utils_is_power_of_two (x2)); \
+ } else if (!(x2 > 0) && !(x2 == 0)) { \
+ /* a (signed) negative value is always FALSE. */ \
+ g_assert_cmpint (FALSE, ==, nm_utils_is_power_of_two (x2));\
+ } \
+ g_assert_cmpint (expect, ==, nm_utils_is_power_of_two (x1)); \
+ } G_STMT_END
+
+static void
+test_nm_utils_is_power_of_two ()
+{
+ guint64 xyes, xno;
+ gint i, j;
+ GRand *rand = nmtst_get_rand ();
+ int numbits;
+
+ for (i = -1; i < 64; i++) {
+
+ /* find a (positive) x which is a power of two. */
+ if (i == -1)
+ xyes = 0;
+ else {
+ xyes = (1LL << i);
+ g_assert (xyes != 0);
+ }
+
+ xno = xyes;
+ if (xyes != 0) {
+again:
+ /* Find another @xno, that is not a power of two. Do that,
+ * by randomly setting bits. */
+ numbits = g_rand_int_range (rand, 1, 65);
+ while (xno != ~((guint64) 0) && numbits > 0) {
+ guint64 v = (1LL << g_rand_int_range (rand, 0, 65));
+
+ if ((xno | v) != xno) {
+ xno |= v;
+ --numbits;
+ }
+ }
+ if (xno == xyes)
+ goto again;
+ }
+
+ for (j = 0; j < 2; j++) {
+ gboolean expect = j == 0;
+ guint64 x = expect ? xyes : xno;
+
+ if (!expect && xno == 0)
+ continue;
+
+ /* check if @x is as @expect, when casted to a certain data type. */
+ test_nm_utils_is_power_of_two_do (gint8, x, expect);
+ test_nm_utils_is_power_of_two_do (guint8, x, expect);
+ test_nm_utils_is_power_of_two_do (gint16, x, expect);
+ test_nm_utils_is_power_of_two_do (guint16, x, expect);
+ test_nm_utils_is_power_of_two_do (gint32, x, expect);
+ test_nm_utils_is_power_of_two_do (guint32, x, expect);
+ test_nm_utils_is_power_of_two_do (gint64, x, expect);
+ test_nm_utils_is_power_of_two_do (guint64, x, expect);
+ test_nm_utils_is_power_of_two_do (char, x, expect);
+ test_nm_utils_is_power_of_two_do (unsigned char, x, expect);
+ test_nm_utils_is_power_of_two_do (signed char, x, expect);
+ test_nm_utils_is_power_of_two_do (enum TEST_IS_POWER_OF_TWP_ENUM_SIGNED, x, expect);
+ test_nm_utils_is_power_of_two_do (enum TEST_IS_POWER_OF_TWP_ENUM_UNSIGNED, x, expect);
+ test_nm_utils_is_power_of_two_do (enum TEST_IS_POWER_OF_TWP_ENUM_SIGNED_64, x, expect);
+ test_nm_utils_is_power_of_two_do (enum TEST_IS_POWER_OF_TWP_ENUM_UNSIGNED_64, x, expect);
+ }
+ }
+}
+
+/******************************************************************************/
+
NMTST_DEFINE ();
int main (int argc, char **argv)
@@ -4459,6 +4556,7 @@ int main (int argc, char **argv)
g_test_add_func ("/core/general/_nm_utils_uuid_generate_from_strings", test_nm_utils_uuid_generate_from_strings);
g_test_add_func ("/core/general/_nm_utils_ascii_str_to_int64", test_nm_utils_ascii_str_to_int64);
+ g_test_add_func ("/core/general/nm_utils_is_power_of_two", test_nm_utils_is_power_of_two);
g_test_add_func ("/core/general/_nm_utils_dns_option_validate", test_nm_utils_dns_option_validate);
g_test_add_func ("/core/general/_nm_utils_dns_option_find_idx", test_nm_utils_dns_option_find_idx);