From b32175d625aec96f67c01c26ffd92c5765b86e28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dag-Erling=20Sm=C3=B8rgrav?= Date: Sat, 17 Nov 2018 16:37:22 +0100 Subject: [PATCH] Fix our ffs() / fls() and add unit tests. --- include/cryb/bitwise.h | 54 ++++++++++----- t/.gitignore | 1 + t/Makefile.am | 3 +- t/t_ffs_fls.c | 153 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 192 insertions(+), 19 deletions(-) create mode 100644 t/t_ffs_fls.c diff --git a/include/cryb/bitwise.h b/include/cryb/bitwise.h index c7f9107..6b12304 100644 --- a/include/cryb/bitwise.h +++ b/include/cryb/bitwise.h @@ -79,9 +79,12 @@ static inline int cryb_ffs(int n) { #elif HAVE___BUILTIN_CTZ return (n ? __builtin_ctz(n) : 0); #else - int i = 8 * sizeof n - 1; - for (i > 0) - if (n & (1 << --i)) + unsigned int m; + unsigned int i; + if (n == 0) + return (0); + for (i = m = 1; i <= sizeof n * 8; ++i, m <<= 1) + if ((unsigned int)n & m) break; return (i); #endif @@ -93,9 +96,12 @@ static inline int cryb_ffsl(long int n) { #elif HAVE___BUILTIN_CLZ return (n ? __builtin_ctz(n) : 0); #else - int i = 8 * sizeof n - 1; - for (i > 0) - if (n & (1 << --i)) + unsigned long int m; + unsigned int i; + if (n == 0) + return (0); + for (i = m = 1; i <= sizeof n * 8; ++i, m <<= 1) + if ((unsigned long int)n & m) break; return (i); #endif @@ -107,9 +113,12 @@ static inline int cryb_ffsll(long long int n) { #elif HAVE___BUILTIN_CLZ return (n ? __builtin_ctz(n) : 0); #else - int i = 8 * sizeof n - 1; - for (i > 0) - if (n & (1 << --i)) + unsigned long long int m; + unsigned int i; + if (n == 0) + return (0); + for (i = m = 1; i <= sizeof n * 8; ++i, m <<= 1) + if ((unsigned long long int)n & m) break; return (i); #endif @@ -121,9 +130,12 @@ static inline int cryb_fls(int n) { #elif HAVE___BUILTIN_CLZ return (n ? (8 * sizeof n) - __builtin_clz(n) : 0); #else - int i = 8 * sizeof n - 1; - for (i > 0) - if (n & (1 << --i)) + unsigned int m; + unsigned int i; + if (n == 0) + return (0); + for (i = sizeof n * 8, m = 1U << (i - 1); i > 0; --i, m >>= 1) + if ((unsigned int)n & m) break; return (i); #endif @@ -135,9 +147,12 @@ static inline int cryb_flsl(long int n) { #elif HAVE___BUILTIN_CLZ return (n ? (8 * sizeof n) - __builtin_clzl(n) : 0); #else - int i = 8 * sizeof n - 1; - for (i > 0) - if (n & (1 << --i)) + unsigned long int m; + unsigned int i; + if (n == 0) + return (0); + for (i = sizeof n * 8, m = 1UL << (i - 1); i > 0; --i, m >>= 1) + if ((unsigned long int)n & m) break; return (i); #endif @@ -149,9 +164,12 @@ static inline int cryb_flsll(long long int n) { #elif HAVE___BUILTIN_CLZ return (n ? (8 * sizeof n) - __builtin_clzll(n) : 0); #else - int i = 8 * sizeof n - 1; - for (i > 0) - if (n & (1 << --i)) + unsigned long long int m; + unsigned int i; + if (n == 0) + return (0); + for (i = sizeof n * 8, m = 1ULL << (i - 1); i > 0; --i, m >>= 1) + if ((unsigned long long int)n & m) break; return (i); #endif diff --git a/t/.gitignore b/t/.gitignore index 6b872c5..fe24f7d 100644 --- a/t/.gitignore +++ b/t/.gitignore @@ -10,6 +10,7 @@ /t_digest /t_enc /t_endian +/t_ffs_fls /t_fletcher /t_hash /t_hmac_sha1 diff --git a/t/Makefile.am b/t/Makefile.am index feebfa6..bc7e5e8 100644 --- a/t/Makefile.am +++ b/t/Makefile.am @@ -105,7 +105,8 @@ t_strchrnul_LDADD = $(libt) $(libcore) t_strlcat_LDADD = $(libt) $(libcore) t_strlcmp_LDADD = $(libt) $(libcore) t_strlcpy_LDADD = $(libt) $(libcore) -TESTS += t_rol_ror +TESTS += t_ffs_fls t_rol_ror +t_ffs_fls_LDADD = $(libt) $(libcore) t_rol_ror_LDADD = $(libt) $(libcore) TESTS += t_string t_wstring EXTRA_DIST += t__string.c diff --git a/t/t_ffs_fls.c b/t/t_ffs_fls.c new file mode 100644 index 0000000..9a6d545 --- /dev/null +++ b/t/t_ffs_fls.c @@ -0,0 +1,153 @@ +/*- + * Copyright (c) 2014-2018 Dag-Erling Smørgrav + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cryb/impl.h" + +#include +#include +#include + +/* test our own code, not the compiler's */ +#undef HAVE___BUILTIN_CLZ +#undef HAVE___BUILTIN_CTZ +#undef HAVE___BUILTIN_FFS +#undef HAVE___BUILTIN_FFSL +#undef HAVE___BUILTIN_FFSLL +#undef HAVE___BUILTIN_FLS +#undef HAVE___BUILTIN_FLSL +#undef HAVE___BUILTIN_FLSLL + +#include + +#include + +static int +t_ffs(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) +{ + int u; + int n, ret; + + ret = t_compare_i(0, cryb_ffs(0)); + for (u = 1, n = 1; u != 0; u <<= 1, n++) { + t_printv("ffs(0x%08x) == %d\n", u, cryb_ffs(u)); + ret &= t_compare_i(n, cryb_ffs(u)); + } + return (ret); +} + +static int +t_ffsl(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) +{ + long int u; + int n, ret; + + ret = t_compare_i(0, cryb_ffs(0)); + for (u = 1, n = 1; u != 0; u <<= 1, n++) + ret &= t_compare_i(n, cryb_ffsl(u)); + return (ret); +} + +static int +t_ffsll(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) +{ + long long int u; + int n, ret; + + ret = t_compare_i(0, cryb_ffs(0)); + for (u = 1, n = 1; u != 0; u <<= 1, n++) + ret &= t_compare_i(n, cryb_ffsll(u)); + return (ret); +} + +static int +t_fls(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) +{ + unsigned int u; + int n, ret; + + ret = t_compare_i(0, cryb_fls(0)); + for (u = 1, n = 1; u != 0; u <<= 1, n++) { + t_printv("fls(0x%08x) == %d\n", u, cryb_fls(u)); + ret &= t_compare_i(n, cryb_fls(u)); + } + return (ret); +} + +static int +t_flsl(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) +{ + unsigned long int u; + int n, ret; + + ret = t_compare_i(0, cryb_flsl(0)); + for (u = 1, n = 1; u != 0; u <<= 1, n++) { + t_printv("flsl(0x%08lx) == %d\n", u, cryb_flsl(u)); + ret &= t_compare_i(n, cryb_flsl(u)); + } + return (ret); +} + +static int +t_flsll(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) +{ + unsigned long long int u; + int n, ret; + + ret = t_compare_i(0, cryb_flsll(0)); + for (u = 1, n = 1; u != 0; u <<= 1, n++) { + t_printv("flsll(0x%016llx) == %d\n", u, cryb_flsll(u)); + ret &= t_compare_i(n, cryb_flsll(u)); + } + return (ret); +} + + +/*************************************************************************** + * Boilerplate + */ + +static int +t_prepare(int argc CRYB_UNUSED, char *argv[] CRYB_UNUSED) +{ + + t_add_test(t_ffs, 0, "ffs"); + t_add_test(t_ffsl, 0, "ffsl"); + t_add_test(t_ffsll, 0, "ffsll"); + t_add_test(t_fls, 0, "fls"); + t_add_test(t_flsl, 0, "flsl"); + t_add_test(t_flsll, 0, "flsll"); + return (0); +} + +int +main(int argc, char *argv[]) +{ + + t_main(t_prepare, NULL, argc, argv); +}