Add bitops.c for find_next_bit_le()

The upcoming buddy changes are going to need a find_next_bit_le().

Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
Zach Brown
2016-11-04 13:52:36 -07:00
parent 871db60fb2
commit 40b9f19ec4
2 changed files with 62 additions and 0 deletions

60
utils/src/bitops.c Normal file
View File

@@ -0,0 +1,60 @@
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#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;
}

View File

@@ -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