diff --git a/options/ansi/include/string.h b/options/ansi/include/string.h index d8bce017..224c576d 100644 --- a/options/ansi/include/string.h +++ b/options/ansi/include/string.h @@ -57,6 +57,8 @@ char *stpcpy(char *__restrict, const char *__restrict); // GNU extensions. int strverscmp(const char *l0, const char *r0); +int ffsl(long i); +int ffsll(long long i); #ifdef __cplusplus } diff --git a/options/posix/generic/strings-stubs.cpp b/options/posix/generic/strings-stubs.cpp index 59a5354a..137c1c1d 100644 --- a/options/posix/generic/strings-stubs.cpp +++ b/options/posix/generic/strings-stubs.cpp @@ -13,9 +13,61 @@ char *rindex(const char *s, int c) { return strrchr(s, c); } -int ffs(int) { - __ensure(!"Not implemented"); - __builtin_unreachable(); +namespace { + + template + int ffs_generic(T i) { + //Non-portably assume a byte has 8 bits; fine in all plausible cases. + for(int b = 0; b < sizeof(T) * 8;) + if(i & (static_cast(0x1) << b++)) + return b; + + return 0; + } + +} + +#ifdef __has_builtin +# if __has_builtin(__builtin_ffs) +# define __mlibc_ffs __builtin_ffs +# endif +# if __has_builtin(__builtin_ffsl) +# define __mlibc_ffsl __builtin_ffsl +# endif +# if __has_builtin(__builtin_ffsll) +# define __mlibc_ffsll __builtin_ffsll +# endif +#endif + +int ffs(int i) { +#ifdef __mlibc_ffs + return __mlibc_ffs(i); +#else + return ffs_generic(i); +#endif +} + +/* + Both ffsl() and ffsll() are glibc extensions + defined in string.h. They are however implemented + here because of similarity in logic and + shared code. +*/ + +int ffsl(long i) { +#ifdef __mlibc_ffsl + return __mlibc_ffsl(i); +#else + return ffs_generic(i); +#endif +} + +int ffsll(long long i) { +#ifdef __mlibc_ffsll + return __mlibc_ffsll(i); +#else + return ffs_generic(i); +#endif } int strcasecmp(const char *a, const char *b) { diff --git a/tests/glibc/ffsl-ffsll.c b/tests/glibc/ffsl-ffsll.c new file mode 100644 index 00000000..5bea030d --- /dev/null +++ b/tests/glibc/ffsl-ffsll.c @@ -0,0 +1,20 @@ +#include +#include + +int main(void){ + long test1 = 1L << (sizeof(long) * 8 - 1); + long long test2 = 1LL << (sizeof(long long) * 8 - 1); + + assert(ffsl(test1) == sizeof(long) * 8); + assert(ffsll(test2) == sizeof(long long) * 8); + assert(ffsl(0) == 0); + assert(ffsll(0) == 0); + assert(ffsl(test1) == ffsll(test1)); + if(sizeof(long) < sizeof(long long)){ + assert(ffsl(test1) < ffsll(test2)); + } else { + assert(ffsl(test2) == ffsll(test2)); + } + + return 0; +} diff --git a/tests/meson.build b/tests/meson.build index f59e6f1a..ca217fd1 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -39,7 +39,8 @@ posix_test_cases = [ 'system', # This test should be in the ANSI tests, but it depends on sys/wait.h 'sigsuspend', 'sigaltstack', - 'realpath' + 'realpath', + 'ffs' ] posix_fail_test_cases = [ @@ -47,7 +48,8 @@ posix_fail_test_cases = [ ] glibc_test_cases = [ - 'getopt' + 'getopt', + 'ffsl-ffsll' ] test_sources = [ diff --git a/tests/posix/ffs.c b/tests/posix/ffs.c new file mode 100644 index 00000000..23f89bc2 --- /dev/null +++ b/tests/posix/ffs.c @@ -0,0 +1,10 @@ +#include +#include + +int main(void){ + assert(ffs(0x8000) == 16); + assert(ffs(0x0) == 0); + assert(ffs(0x8000000000000000) == 0); + + return 0; +}