diff --git a/utils/src/bitops.c b/utils/src/bitops.c new file mode 100644 index 00000000..148051f4 --- /dev/null +++ b/utils/src/bitops.c @@ -0,0 +1,60 @@ +#include +#include +#include + +#include "sparse.h" +#include "util.h" +#include "bitops.h" + +#if (__SIZEOF_LONG__ == 8) +typedef __le64 lelong; +#define lelong_to_cpu le64_to_cpu + +#elif (__SIZEOF_LONG__ == 4) +typedef __le32 lelong; +#define lelong_to_cpu le32_to_cpu + +#else +#error "no sizeof long define?" +#endif + +/* + * I'd have used ffsl(), but defining _GNU_SOURCE caused build errors + * in glibc. The gcc builtin has the added bonus of returning 0 for the + * least significant bit instead of 1. + */ +#define ctzl __builtin_ctzl + +int find_next_bit_le(void *addr, long size, int start) +{ + lelong * __packed longs = addr; + unsigned long off = 0; + unsigned long masked; + + /* skip past whole longs before start */ + if (start >= BITS_PER_LONG) { + longs += start / BITS_PER_LONG; + off = start & ~(BITS_PER_LONG - 1); + start -= off; + } + + /* mask off low bits if start isn't aligned */ + if (start) { + masked = lelong_to_cpu(*longs) & ~((1 << (start)) - 1); + if (masked) + return min(ctzl(masked), size); + + off += BITS_PER_LONG; + longs++; + } + + /* then search remaining longs */ + while (off < size) { + if (*longs) + return min(off + ctzl(lelong_to_cpu(*longs)), size); + longs++; + off += BITS_PER_LONG; + } + + return size; +} diff --git a/utils/src/bitops.h b/utils/src/bitops.h index abfa3755..7f2add29 100644 --- a/utils/src/bitops.h +++ b/utils/src/bitops.h @@ -75,4 +75,6 @@ static inline int test_and_clear_bit_le(int nr, void *addr) return ret; } +int find_next_bit_le(void *addr, long size, int start); + #endif