bitops: introduce CONFIG_GENERIC_FIND_BIT_LE
[linux-2.6-block.git] / lib / find_next_bit.c
index bda0d71a25144c110df24920b6320470e9c213a8..b0a8767282bf674fe980f17b5d192103e5ffc4f5 100644 (file)
 
 #define BITOP_WORD(nr)         ((nr) / BITS_PER_LONG)
 
-/**
- * find_next_bit - find the next set bit in a memory region
- * @addr: The address to base the search on
- * @offset: The bitnumber to start searching at
- * @size: The maximum size to search
+#ifdef CONFIG_GENERIC_FIND_NEXT_BIT
+/*
+ * Find the next set bit in a memory region.
  */
 unsigned long find_next_bit(const unsigned long *addr, unsigned long size,
-               unsigned long offset)
+                           unsigned long offset)
 {
        const unsigned long *p = addr + BITOP_WORD(offset);
        unsigned long result = offset & ~(BITS_PER_LONG-1);
@@ -60,7 +58,6 @@ found_first:
 found_middle:
        return result + __ffs(tmp);
 }
-
 EXPORT_SYMBOL(find_next_bit);
 
 /*
@@ -68,7 +65,7 @@ EXPORT_SYMBOL(find_next_bit);
  * Linus' asm-alpha/bitops.h.
  */
 unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size,
-               unsigned long offset)
+                                unsigned long offset)
 {
        const unsigned long *p = addr + BITOP_WORD(offset);
        unsigned long result = offset & ~(BITS_PER_LONG-1);
@@ -105,10 +102,65 @@ found_first:
 found_middle:
        return result + ffz(tmp);
 }
-
 EXPORT_SYMBOL(find_next_zero_bit);
+#endif /* CONFIG_GENERIC_FIND_NEXT_BIT */
+
+#ifdef CONFIG_GENERIC_FIND_FIRST_BIT
+/*
+ * Find the first set bit in a memory region.
+ */
+unsigned long find_first_bit(const unsigned long *addr, unsigned long size)
+{
+       const unsigned long *p = addr;
+       unsigned long result = 0;
+       unsigned long tmp;
+
+       while (size & ~(BITS_PER_LONG-1)) {
+               if ((tmp = *(p++)))
+                       goto found;
+               result += BITS_PER_LONG;
+               size -= BITS_PER_LONG;
+       }
+       if (!size)
+               return result;
+
+       tmp = (*p) & (~0UL >> (BITS_PER_LONG - size));
+       if (tmp == 0UL)         /* Are any bits set? */
+               return result + size;   /* Nope. */
+found:
+       return result + __ffs(tmp);
+}
+EXPORT_SYMBOL(find_first_bit);
+
+/*
+ * Find the first cleared bit in a memory region.
+ */
+unsigned long find_first_zero_bit(const unsigned long *addr, unsigned long size)
+{
+       const unsigned long *p = addr;
+       unsigned long result = 0;
+       unsigned long tmp;
+
+       while (size & ~(BITS_PER_LONG-1)) {
+               if (~(tmp = *(p++)))
+                       goto found;
+               result += BITS_PER_LONG;
+               size -= BITS_PER_LONG;
+       }
+       if (!size)
+               return result;
+
+       tmp = (*p) | (~0UL << size);
+       if (tmp == ~0UL)        /* Are any bits zero? */
+               return result + size;   /* Nope. */
+found:
+       return result + ffz(tmp);
+}
+EXPORT_SYMBOL(find_first_zero_bit);
+#endif /* CONFIG_GENERIC_FIND_FIRST_BIT */
 
 #ifdef __BIG_ENDIAN
+#ifdef CONFIG_GENERIC_FIND_BIT_LE
 
 /* include/linux/byteorder does not support "unsigned long" type */
 static inline unsigned long ext2_swabp(const unsigned long * x)
@@ -134,15 +186,16 @@ static inline unsigned long ext2_swab(const unsigned long y)
 #endif
 }
 
-unsigned long generic_find_next_zero_le_bit(const unsigned long *addr, unsigned
+unsigned long find_next_zero_bit_le(const void *addr, unsigned
                long size, unsigned long offset)
 {
-       const unsigned long *p = addr + BITOP_WORD(offset);
+       const unsigned long *p = addr;
        unsigned long result = offset & ~(BITS_PER_LONG - 1);
        unsigned long tmp;
 
        if (offset >= size)
                return size;
+       p += BITOP_WORD(offset);
        size -= result;
        offset &= (BITS_PER_LONG - 1UL);
        if (offset) {
@@ -175,7 +228,52 @@ found_middle:
 found_middle_swap:
        return result + ffz(ext2_swab(tmp));
 }
+EXPORT_SYMBOL(find_next_zero_bit_le);
+
+unsigned long find_next_bit_le(const void *addr, unsigned
+               long size, unsigned long offset)
+{
+       const unsigned long *p = addr;
+       unsigned long result = offset & ~(BITS_PER_LONG - 1);
+       unsigned long tmp;
 
-EXPORT_SYMBOL(generic_find_next_zero_le_bit);
+       if (offset >= size)
+               return size;
+       p += BITOP_WORD(offset);
+       size -= result;
+       offset &= (BITS_PER_LONG - 1UL);
+       if (offset) {
+               tmp = ext2_swabp(p++);
+               tmp &= (~0UL << offset);
+               if (size < BITS_PER_LONG)
+                       goto found_first;
+               if (tmp)
+                       goto found_middle;
+               size -= BITS_PER_LONG;
+               result += BITS_PER_LONG;
+       }
+
+       while (size & ~(BITS_PER_LONG - 1)) {
+               tmp = *(p++);
+               if (tmp)
+                       goto found_middle_swap;
+               result += BITS_PER_LONG;
+               size -= BITS_PER_LONG;
+       }
+       if (!size)
+               return result;
+       tmp = ext2_swabp(p);
+found_first:
+       tmp &= (~0UL >> (BITS_PER_LONG - size));
+       if (tmp == 0UL)         /* Are any bits set? */
+               return result + size; /* Nope. */
+found_middle:
+       return result + __ffs(tmp);
+
+found_middle_swap:
+       return result + __ffs(ext2_swab(tmp));
+}
+EXPORT_SYMBOL(find_next_bit_le);
 
+#endif /* CONFIG_GENERIC_FIND_BIT_LE */
 #endif /* __BIG_ENDIAN */