From c2ad81b2b5f1cdd6671d6aed7dcf962e61ebef1e Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Thu, 11 Jan 2024 11:37:56 +0100 Subject: [PATCH] add CuTest / update README --- Makefile | 4 +- README.org | 19 +-- include/bitops.h | 40 ++--- include/br.h | 1 + test/bitops-test.c | 364 ++++++++++++++++++++++++++------------------- 5 files changed, 246 insertions(+), 182 deletions(-) diff --git a/Makefile b/Makefile index e6e2f56..f238e3a 100644 --- a/Makefile +++ b/Makefile @@ -204,6 +204,8 @@ $(SLIB): $(OBJ) | $(LIBDIR) ##################################### testing .PHONY: cleanbin cleanbindir +CUTESTSRC := $(TESTDIR)/cutest/CuTest.c + cleanbin: $(call rmfiles,$(BIN),binary) @@ -211,7 +213,7 @@ cleanbindir: $(call rmdir,$(BINDIR),binaries) $(BINDIR)/%: $(TESTDIR)/%.c libs | $(BINDIR) - $(CC) $(CPPFLAGS) $(CFLAGS) $< $(LDFLAGS) $(LIBS) -o $@ + $(CC) $(CPPFLAGS) $(CFLAGS) $< $(CUTESTSRC) $(LDFLAGS) $(LIBS) -o $@ ##################################### pre-processed (.i) and assembler (.s) output %.i: %.c diff --git a/README.org b/README.org index 7b98a39..e039e1a 100644 --- a/README.org +++ b/README.org @@ -1,13 +1,17 @@ -* brlib - A small personal C library +#+title: brlib - A small personal C library +#+OPTIONS: toc:nil +#+OPTIONS: num:2 +#+startup: num + ** License -SPDX-License-Identifier: GPL-3.0-or-later +~SPDX-License-Identifier: GPL-3.0-or-later ~~ This work is, with exceptions below, Copyright (C) 2021-2024 Bruno Raoult ("br"), and licensed under the GNU General Public License v3.0 or later. Some rights reserved. See COPYING. -*** The licence exceptions are: -**** Cutest testing framework. +*The licence exceptions are:** +_Cutest testing framework.__ You can find the original work on [[https://sourceforge.net/projects/cutest/files/cutest/][sourceforge]]. @@ -16,17 +20,14 @@ See [[test/cutest/license.txt][license local copy]] or . ** Installation: -*** clone repository -**** user... +*** user... #+BEGIN_EXAMPLE $ git clone https://git.raoult.com:bruno/brlib.git -#+END_EXAMPLE or -#+BEGIN_EXAMPLE $ git clone https://github.com/braoult/brlib.git #+END_EXAMPLE -**** ... or developer +*** ...or developer #+BEGIN_EXAMPLE $ git clone git@git.raoult.com:bruno/brlib.git $ cd brlib diff --git a/include/bitops.h b/include/bitops.h index e3ffbfc..a71bbde 100644 --- a/include/bitops.h +++ b/include/bitops.h @@ -17,6 +17,10 @@ #include "bitops-emulated/generic-ctz.h" #include "bitops-emulated/generic-clz.h" +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + /* determine which native builtins are available */ #if __has_builtin(__builtin_popcount) @@ -41,10 +45,6 @@ */ void print_bitops_impl(void); -#ifndef __has_builtin -#define __has_builtin(x) 0 -#endif - /* no plan to support 32bits for now... * #if __WORDSIZE != 64 * #error "Only 64 bits word size supported." @@ -52,8 +52,8 @@ void print_bitops_impl(void); */ /** - * lsb, msb: least/most significant bit: 10101000 - * msb = 7 ^ ^ lsb = 3 + * lsb, msb: 0-indexed least/most significant bit: 10101000 + * msb = 7 ^ ^ lsb = 3 * */ #define lsb64(x) (ctz64(x)) @@ -63,7 +63,7 @@ void print_bitops_impl(void); /** * popcount32, popcout64 - count set bits: 10101000 -> 3 - * @num: unsigned 32 or 64 bits integer. + * @n: unsigned 32 or 64 bits integer. * */ #if defined(HAS_POPCOUNT) @@ -100,7 +100,7 @@ void print_bitops_impl(void); /** * ctz32, ctz64 - count trailing zeros: 00101000 -> 3 - * @num: unsigned 32 or 64 bits integer. + * @n: unsigned 32 or 64 bits integer. * * Not defined if no bit set, so check for non-zero before calling this. * This is similat the FFS (First Find Set), which has FFS(0) = 0. @@ -127,7 +127,7 @@ void print_bitops_impl(void); /** * clz32, clz64 - count leading zeros: 00101000 -> 2 * - * @num: unsigned 32 or 64 bits integer. + * @n: unsigned 32 or 64 bits integer. * * Not defined if no bit set, so check for non-zero before calling this. */ @@ -144,8 +144,8 @@ void print_bitops_impl(void); /** * ffs32, ffs64 - find first bit set, indexed from 0: 00101000 -> 4 - * ffz32, ffz64 - find first bit unset, indexed from 0: 00101000 -> 0 - * @num: unsigned 32 or 64 bits integer. + * ffz32, ffz64 - find first bit unset, indexed from 0: 00101010 -> 2 + * @n: unsigned 32 or 64 bits integer. * * ffs(n) is similar to ctz(n) + 1, but returns 0 if n == 0 (except * for ctz version, where ffs(0) is undefined). @@ -176,20 +176,18 @@ void print_bitops_impl(void); /** * fls32, fls64 - return one plus MSB index: 00101000 -> 6 - * @num: unsigned 32 or 64 bits integer. + * @n: unsigned 32 or 64 bits integer. * * Similar to nbits(n) - clz(n), but returns 0 if n == 0; */ #define fls32(n) ((n)? 32 - clz32(n): 0) #define fls64(n) ((n)? 64 - clz64(n): 0) -/* rolXX/rorXX are taken from kernel's are are: - * SPDX-License-Identifier: GPL-2.0 - */ -/** - * rol8, rol16, rol32, rol64 - rotate left +/* + * rol32, rot64 - rotate left * @num: unsigned 8, 16, 32 or 64 bits integer * @n: bits to roll + * See: https://stackoverflow.com/a/31488147/3079831 */ #define rol8(num, n) ((num << (n & 7)) | (num >> ((-n) & 7))) #define rol16(num, n) ((num << (n & 15)) | (num >> ((-n) & 15))) @@ -207,11 +205,13 @@ void print_bitops_impl(void); #define ror64(num, n) ((num >> (n & 63)) | (num << ((-n) & 63))) /** - * ilog2 - log base 2 + * ilog2_32, ilog2_64 - log base 2 * @n: unsigned 32 or 64 bits integer. + * + * Undefine value if n = 0. */ -#define ilog2_32(n) (fls32(n) - 1) -#define ilog2_64(n) (fls64(n) - 1) +#define ilog2_32(n) (msb32(n)) +#define ilog2_64(n) (msb64(n)) /** * is_pow2() - check if number is a power of two diff --git a/include/br.h b/include/br.h index f1ad5eb..36c1642 100644 --- a/include/br.h +++ b/include/br.h @@ -88,6 +88,7 @@ typedef signed char schar; */ #define __unused __attribute__((__unused__)) #define __used __attribute__((__used__)) +#define __const __attribute__((__const__)) /* see https://lkml.org/lkml/2018/3/20/845 for explanation of this monster */ diff --git a/test/bitops-test.c b/test/bitops-test.c index c83660f..10a08d7 100644 --- a/test/bitops-test.c +++ b/test/bitops-test.c @@ -16,181 +16,241 @@ #include "br.h" #include "bitops.h" -// #include "cutest/CuTest.h" +#include "cutest/CuTest.h" -static void test_popcount() +static const struct test32_1 { + u32 t32; /* input */ + int popc; + int ctz; + int clz; + int ffs; + int ffz; + int fls; + int ilog2; +} test32_1[] = { + { 0x00000000, 0, 32, 32, 0, 1, 0, 0 }, /* C undefined for some values */ + { 0xffffffff, 32, 0, 0, 1, 0, 32, 31 }, + { 0x00000001, 1, 0, 31, 1, 2, 1, 0 }, + { 0x80000000, 1, 31, 0, 32, 1, 32, 31 }, + { 0x71800718, 10, 3, 1, 4, 1, 31, 30 }, + { 0x07eeeef7, 22, 0, 5, 1, 4, 27, 26 }, +}; + +static const struct test64_1 { + u64 t64; /* input */ + int popc; + int ctz; + int clz; + int ffs; + int ffz; + int fls; + int ilog2; +} test64_1[] = { + { 0x0000000000000000, 0, 64, 64, 0, 1, 0, 0 }, /* undefined for some values */ + { 0xffffffffffffffff, 64, 0, 0, 1, 0, 64, 63 }, + { 0x0000000100000001, 2, 0, 31, 1, 2, 33, 32 }, + { 0x8000000000000000, 1, 63, 0, 64, 1, 64, 63 }, + { 0x7180071871800718, 20, 3, 1, 4, 1, 63, 62 }, + { 0x07eeeef707eeeef7, 44, 0, 5, 1, 4, 59, 58 }, +}; + +static void cutest_popcount(CuTest *tc) { - u32 t32[] = { 0x0, 0x88000101, 0xffffffff }; - u64 t64[] = { 0x0ll, 0x8880000000000101LL, 0xffffffffffffffffll }; - - for (uint i = 0; i < ARRAY_SIZE(t32); ++i) { - printf("popcount32 (%#x): ", t32[i]); -# ifdef ___popcount32_native - printf("native:%d ", __popcount32_native(t32[i])); -# else - printf("native:XXX "); -# endif - printf("emulated:%d ", __popcount_emulated(t32[i])); - printf("\n"); + for (uint i = 0; i < ARRAY_SIZE(test32_1); ++i) { + int res = popcount32(test32_1[i].t32); + CuAssertIntEquals(tc, test32_1[i].popc, res); } - printf("\n"); - for (uint i = 0; i < ARRAY_SIZE(t64); ++i) { - printf("popcount64 (%#lx): ", t64[i]); -# ifdef ___popcount64_native - printf("native:%d ", __popcount64_native(t64[i])); -# else - printf("native:XXX "); -# endif - printf("emulated:%d ", __popcount_emulated(t64[i])); - printf("\n"); + for (uint i = 0; i < ARRAY_SIZE(test64_1); ++i) { + int res = popcount64(test64_1[i].t64); + CuAssertIntEquals(tc, test64_1[i].popc, res); } - printf("\n"); } -static void test_ctz() +static void cutest_ctz(CuTest *tc) { - u32 t32[] = { - 0x88800101, - 0xffffffff, - 0x800, - 0x80000000, - 0x00800000 - }; - u64 t64[] = { - 0x8880000000000101LL, - 0xffffffffffffffffll, - 0x800ll, - 0x8000000000000000LL, - 0x0080000000000000LL}; - - for (uint i = 0; i < ARRAY_SIZE(t32); ++i) { - printf("ctz32 (%#x): ", t32[i]); -# ifdef __ctz32_native - printf("native:%d ", __ctz32_native(t32[i])); -# else - printf("native:XXX "); -# endif - printf("emulated1:%d ", __ctz32_emulated(t32[i])); - printf("emulated2:%d ", __ctz32_emulated2(t32[i])); - //printf("emulated3:%d ", __ctz32_emulated3(t32[i])); - //printf("emulated4:%d ", __ctz32_emulated4(t32[i])); - printf("\n"); + for (uint i = 0; i < ARRAY_SIZE(test32_1); ++i) { + int res = ctz32(test32_1[i].t32); + CuAssertIntEquals(tc, test32_1[i].ctz, res); } - printf("\n"); - for (uint i = 0; i < ARRAY_SIZE(t64); ++i) { - printf("ctz64 (%#lx): ", t64[i]); -# ifdef __ctz64_native - printf("native:%d ", __ctz64_native(t64[i])); -# else - printf("native:XXX "); -# endif - printf("emulated1:%d ", __ctz64_emulated(t64[i])); - printf("emulated2:%d ", __ctz64_emulated2(t64[i])); - //printf("emulated3:%d ", __ctz64_emulated3(t64[i])); - //printf("emulated4:%d ", __ctz64_emulated4(t64[i])); - printf("\n"); + for (uint i = 0; i < ARRAY_SIZE(test64_1); ++i) { + int res = ctz64(test64_1[i].t64); + //printf("t=%#llx r=%d e=%d\n", test64_1[i].t64, res, test64_1[i].ctz); + CuAssertIntEquals(tc, test64_1[i].ctz, res); } - printf("\n"); } -static void test_clz() +static void cutest_clz(CuTest *tc) { - u32 t32[] = { - 0x88800101, - 0xffffffff, - 0x800, - 0x80000000, - 0x00800000 - }; - u64 t64[] = { - 0x8880000000000101LL, - 0xffffffffffffffffll, - 0x800ll, - 0x8000000000000000LL, - 0x0080000000000000LL}; - - for (uint i = 0; i < ARRAY_SIZE(t32); ++i) { - printf("clz32 (%#x): ", t32[i]); -# ifdef __clz32_native - printf("native:%d ", __clz32_native(t32[i])); -# else - printf("native:XXX "); -# endif - printf("emulated1:%d ", __clz32_emulated(t32[i])); - //printf("emulated2:%d ", __clz32_emulated2(t32[i])); - //printf("emulated3:%d ", __ctz32_emulated3(t32[i])); - //printf("emulated4:%d ", __ctz32_emulated4(t32[i])); - printf("\n"); + for (uint i = 0; i < ARRAY_SIZE(test32_1); ++i) { + int res = clz32(test32_1[i].t32); + //printf("clz t=%#x r=%d e=%d\n", test32_1[i].t32, res, test32_1[i].clz); + CuAssertIntEquals(tc, test32_1[i].clz, res); } - printf("\n"); - for (uint i = 0; i < ARRAY_SIZE(t64); ++i) { - printf("clz64 (%#lx): ", t64[i]); -# ifdef __clz64_native - printf("native:%d ", __clz64_native(t64[i])); -# else - printf("native:XXX "); -# endif - printf("emulated1:%d ", __clz64_emulated(t64[i])); - //printf("emulated2:%d ", __ctz64_emulated2(t64[i])); - //printf("emulated3:%d ", __ctz64_emulated3(t64[i])); - //printf("emulated4:%d ", __ctz64_emulated4(t64[i])); - printf("\n"); + for (uint i = 0; i < ARRAY_SIZE(test64_1); ++i) { + int res = clz64(test64_1[i].t64); + //printf("clz t=%#llx r=%d e=%d\n", test64_1[i].t64, res, test64_1[i].clz); + CuAssertIntEquals(tc, test64_1[i].clz, res); } - printf("\n"); } -static void test_ffs() +static void cutest_ffs(CuTest *tc) { - u32 t32[] = { - 0x88800101, - 0xffffffff, - 0x800, - 0x80000000, - 0x00800000 - }; - u64 t64[] = { - 0x8880000000000101LL, - 0xffffffffffffffffll, - 0x800ll, - 0x8000000000000000LL, - 0x0080000000000000LL}; + for (uint i = 0; i < ARRAY_SIZE(test32_1); ++i) { + int res = ffs32(test32_1[i].t32); + CuAssertIntEquals(tc, test32_1[i].ffs, res); + } + for (uint i = 0; i < ARRAY_SIZE(test64_1); ++i) { + int res = ffs64(test64_1[i].t64); + //printf("ffs64 t=%#llx r=%d e=%d\n", test64_1[i].t64, res, test64_1[i].ffs); + CuAssertIntEquals(tc, test64_1[i].ffs, res); + } +} - for (uint i = 0; i < ARRAY_SIZE(t32); ++i) { - printf("ffs32 (%#x): ", t32[i]); -# ifdef __ffs32_native - printf("native:%d ", __ffs32_native(t32[i])); -# else - printf("native:XXX "); -# endif - printf("popcount:%d ", __ffs32_popcount(t32[i])); - printf("ctz:%d ", __ffs32_ctz(t32[i])); - printf("emulated:%d ", __ffs32_emulated(t32[i])); - //printf("emulated4:%d ", __ctz32_emulated4(t32[i])); - printf("\n"); +static void cutest_ffz(CuTest *tc) +{ + for (uint i = 0; i < ARRAY_SIZE(test32_1); ++i) { + int res = ffz32(test32_1[i].t32); + //printf("ffz32 t=%#x r=%d e=%d\n", test32_1[i].t32, res, test32_1[i].ffz); + CuAssertIntEquals(tc, test32_1[i].ffz, res); } - printf("\n"); - for (uint i = 0; i < ARRAY_SIZE(t64); ++i) { - printf("ffs64 (%#lx): ", t64[i]); -# ifdef __ffs64_native - printf("native:%d ", __ffs64_native(t64[i])); -# else - printf("native:XXX "); -# endif - printf("popcount:%d ", __ffs64_popcount(t64[i])); - printf("ctz:%d ", __ffs64_ctz(t64[i])); - printf("emulated:%d ", __ffs64_emulated(t64[i])); - //printf("emulated4:%d ", __ctz64_emulated4(t64[i])); - printf("\n"); + for (uint i = 0; i < ARRAY_SIZE(test64_1); ++i) { + int res = ffz64(test64_1[i].t64); + //printf("ffz64 t=%#llx r=%d e=%d\n", test64_1[i].t64, res, test64_1[i].ffz); + CuAssertIntEquals(tc, test64_1[i].ffz, res); } - printf("\n"); - printf("\n"); +} + +static void cutest_fls(CuTest *tc) +{ + for (uint i = 0; i < ARRAY_SIZE(test32_1); ++i) { + int res = fls32(test32_1[i].t32); + //printf("fls32 t=%#x r=%d e=%d\n", test32_1[i].t32, res, test32_1[i].fls); + CuAssertIntEquals(tc, test32_1[i].fls, res); + } + for (uint i = 0; i < ARRAY_SIZE(test64_1); ++i) { + int res = fls64(test64_1[i].t64); + //printf("fls64 t=%#llx r=%d e=%d\n", test64_1[i].t64, res, test64_1[i].fls); + CuAssertIntEquals(tc, test64_1[i].fls, res); + } +} + +static void cutest_ilog(CuTest *tc) +{ + for (uint i = 0; i < ARRAY_SIZE(test32_1); ++i) { + if (!test32_1[i].t32) { + printf("ilog2_32 t=%#x skipped\n", test32_1[i].t32); + continue; + } + int res = ilog2_32(test32_1[i].t32); + printf("ilog2_32_n t=%#x r=%d e=%d\n", test32_1[i].t32, + res, test32_1[i].ilog2); + CuAssertIntEquals(tc, test32_1[i].ilog2, res); + } + for (uint i = 0; i < ARRAY_SIZE(test64_1); ++i) { + if (!test64_1[i].t64) { + printf("ilog2_32 t=%#llx skipped\n", test64_1[i].t64); + continue; + } + int res = ilog2_64(test64_1[i].t64); + printf("ilog2_64_n t=%#llx r=%d e=%d\n", test64_1[i].t64, + res, test64_1[i].ilog2); + CuAssertIntEquals(tc, test64_1[i].ilog2, res); + } +} + +struct test32_2 { + u32 t32; /* input */ + u32 arg; + u32 rol; + u32 ror; +} test32_2[] = { + { 0x00000000, 0, 0, 0 }, + { 0x00000000, 1, 0, 0 }, + { 0x10000001, 2, 0x40000004, 0x44000000 }, + { 0x80000008, 3, 0x00000044, 0x10000001 }, + { 0x71800718, 8, 0x80071871, 0x18718007 }, + { 0x07eeeef7, 4, 0x7eeeef70, 0x707eeeef }, +}; + +struct test64_2 { + u64 t64; /* input */ + u32 arg; + u64 rol; + u64 ror; +} test64_2[] = { + { 0x0000000000000000, 0, 0, 0 }, + { 0x0000000000000000, 1, 0, 0 }, + { 0x1000000110000001, 2, 0x4000000440000004, 0x4400000044000000 }, + { 0x8000000880000008, 3, 0x0000004400000044, 0x1000000110000001 }, + { 0x7180071871800718, 8, 0x8007187180071871, 0x1871800718718007 }, + { 0x07eeeef707eeeef7, 4, 0x7eeeef707eeeef70, 0x707eeeef707eeeef }, +}; + +static void cutest_rol(CuTest *tc) +{ + for (uint i = 0; i < ARRAY_SIZE(test32_2); ++i) { + u32 res = rol32(test32_2[i].t32, test32_2[i].arg); + //printf("rol32 t=%#x a=%u r=%#08x e=%#08x\n", test32_2[i].t32, + // test32_2[i].arg, res, test32_2[i].rol); + CuAssertU32Equals(tc, test32_2[i].rol, res); + } + for (uint i = 0; i < ARRAY_SIZE(test64_2); ++i) { + u64 res = rol64(test64_2[i].t64, test64_2[i].arg); + //printf("rol64 t=%#llx a=%u r=%#08llx e=%#08llx\n", test64_2[i].t64, + // test64_2[i].arg, res, test64_2[i].rol); + CuAssertU64Equals(tc, test64_2[i].rol, res); + } +} + +static void cutest_ror(CuTest *tc) +{ + for (uint i = 0; i < ARRAY_SIZE(test32_2); ++i) { + u32 res = ror32(test32_2[i].t32, test32_2[i].arg); + //printf("ror32 t=%#x a=%u r=%#08x e=%#08x\n", test32_2[i].t32, + // test32_2[i].arg, res, test32_2[i].ror); + CuAssertU32Equals(tc, test32_2[i].ror, res); + } + for (uint i = 0; i < ARRAY_SIZE(test64_2); ++i) { + u64 res = ror64(test64_2[i].t64, test64_2[i].arg); + //printf("ror64 t=%#0llx a=%u r=%#08llx e=%#08llx\n", test64_2[i].t64, + // test64_2[i].arg, res, test64_2[i].ror); + CuAssertU64Equals(tc, test64_2[i].ror, res); + } +} + +#define MAXLOOPS 8 + +static CuSuite *bitops_GetSuite() +{ + CuSuite* suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, cutest_popcount); + SUITE_ADD_TEST(suite, cutest_ctz); + SUITE_ADD_TEST(suite, cutest_clz); + SUITE_ADD_TEST(suite, cutest_ffs); + SUITE_ADD_TEST(suite, cutest_ffz); + SUITE_ADD_TEST(suite, cutest_fls); + SUITE_ADD_TEST(suite, cutest_ilog); + + SUITE_ADD_TEST(suite, cutest_rol); + SUITE_ADD_TEST(suite, cutest_ror); + return suite; +} + +static void RunAllTests(void) +{ + CuString *output = CuStringNew(); + CuSuite* suite = CuSuiteNew(); + + CuSuiteAddSuite(suite, bitops_GetSuite()); + + CuSuiteRun(suite); + CuSuiteSummary(suite, output); + CuSuiteDetails(suite, output); + printf("%s\n", output->buffer); } int main() { - test_popcount(); - test_ctz(); - test_clz(); - test_ffs(); + RunAllTests(); exit(0); }