linux/lib/find_bit.c
<<
>>
Prefs
   1/* bit search implementation
   2 *
   3 * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
   4 * Written by David Howells (dhowells@redhat.com)
   5 *
   6 * Copyright (C) 2008 IBM Corporation
   7 * 'find_last_bit' is written by Rusty Russell <rusty@rustcorp.com.au>
   8 * (Inspired by David Howell's find_next_bit implementation)
   9 *
  10 * Rewritten by Yury Norov <yury.norov@gmail.com> to decrease
  11 * size and improve performance, 2015.
  12 *
  13 * This program is free software; you can redistribute it and/or
  14 * modify it under the terms of the GNU General Public License
  15 * as published by the Free Software Foundation; either version
  16 * 2 of the License, or (at your option) any later version.
  17 */
  18
  19#include <linux/bitops.h>
  20#include <linux/bitmap.h>
  21#include <linux/export.h>
  22#include <linux/kernel.h>
  23
  24#if !defined(find_next_bit) || !defined(find_next_zero_bit)
  25
  26/*
  27 * This is a common helper function for find_next_bit and
  28 * find_next_zero_bit.  The difference is the "invert" argument, which
  29 * is XORed with each fetched word before searching it for one bits.
  30 */
  31static unsigned long _find_next_bit(const unsigned long *addr,
  32                unsigned long nbits, unsigned long start, unsigned long invert)
  33{
  34        unsigned long tmp;
  35
  36        if (!nbits || start >= nbits)
  37                return nbits;
  38
  39        tmp = addr[start / BITS_PER_LONG] ^ invert;
  40
  41        /* Handle 1st word. */
  42        tmp &= BITMAP_FIRST_WORD_MASK(start);
  43        start = round_down(start, BITS_PER_LONG);
  44
  45        while (!tmp) {
  46                start += BITS_PER_LONG;
  47                if (start >= nbits)
  48                        return nbits;
  49
  50                tmp = addr[start / BITS_PER_LONG] ^ invert;
  51        }
  52
  53        return min(start + __ffs(tmp), nbits);
  54}
  55#endif
  56
  57#ifndef find_next_bit
  58/*
  59 * Find the next set bit in a memory region.
  60 */
  61unsigned long find_next_bit(const unsigned long *addr, unsigned long size,
  62                            unsigned long offset)
  63{
  64        return _find_next_bit(addr, size, offset, 0UL);
  65}
  66EXPORT_SYMBOL(find_next_bit);
  67#endif
  68
  69#ifndef find_next_zero_bit
  70unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size,
  71                                 unsigned long offset)
  72{
  73        return _find_next_bit(addr, size, offset, ~0UL);
  74}
  75EXPORT_SYMBOL(find_next_zero_bit);
  76#endif
  77
  78#ifndef find_first_bit
  79/*
  80 * Find the first set bit in a memory region.
  81 */
  82unsigned long find_first_bit(const unsigned long *addr, unsigned long size)
  83{
  84        unsigned long idx;
  85
  86        for (idx = 0; idx * BITS_PER_LONG < size; idx++) {
  87                if (addr[idx])
  88                        return min(idx * BITS_PER_LONG + __ffs(addr[idx]), size);
  89        }
  90
  91        return size;
  92}
  93EXPORT_SYMBOL(find_first_bit);
  94#endif
  95
  96#ifndef find_first_zero_bit
  97/*
  98 * Find the first cleared bit in a memory region.
  99 */
 100unsigned long find_first_zero_bit(const unsigned long *addr, unsigned long size)
 101{
 102        unsigned long idx;
 103
 104        for (idx = 0; idx * BITS_PER_LONG < size; idx++) {
 105                if (addr[idx] != ~0UL)
 106                        return min(idx * BITS_PER_LONG + ffz(addr[idx]), size);
 107        }
 108
 109        return size;
 110}
 111EXPORT_SYMBOL(find_first_zero_bit);
 112#endif
 113
 114#ifndef find_last_bit
 115unsigned long find_last_bit(const unsigned long *addr, unsigned long size)
 116{
 117        if (size) {
 118                unsigned long val = BITMAP_LAST_WORD_MASK(size);
 119                unsigned long idx = (size-1) / BITS_PER_LONG;
 120
 121                do {
 122                        val &= addr[idx];
 123                        if (val)
 124                                return idx * BITS_PER_LONG + __fls(val);
 125
 126                        val = ~0ul;
 127                } while (idx--);
 128        }
 129        return size;
 130}
 131EXPORT_SYMBOL(find_last_bit);
 132#endif
 133
 134#ifdef __BIG_ENDIAN
 135
 136/* include/linux/byteorder does not support "unsigned long" type */
 137static inline unsigned long ext2_swab(const unsigned long y)
 138{
 139#if BITS_PER_LONG == 64
 140        return (unsigned long) __swab64((u64) y);
 141#elif BITS_PER_LONG == 32
 142        return (unsigned long) __swab32((u32) y);
 143#else
 144#error BITS_PER_LONG not defined
 145#endif
 146}
 147
 148#if !defined(find_next_bit_le) || !defined(find_next_zero_bit_le)
 149static unsigned long _find_next_bit_le(const unsigned long *addr,
 150                unsigned long nbits, unsigned long start, unsigned long invert)
 151{
 152        unsigned long tmp;
 153
 154        if (!nbits || start >= nbits)
 155                return nbits;
 156
 157        tmp = addr[start / BITS_PER_LONG] ^ invert;
 158
 159        /* Handle 1st word. */
 160        tmp &= ext2_swab(BITMAP_FIRST_WORD_MASK(start));
 161        start = round_down(start, BITS_PER_LONG);
 162
 163        while (!tmp) {
 164                start += BITS_PER_LONG;
 165                if (start >= nbits)
 166                        return nbits;
 167
 168                tmp = addr[start / BITS_PER_LONG] ^ invert;
 169        }
 170
 171        return min(start + __ffs(ext2_swab(tmp)), nbits);
 172}
 173#endif
 174
 175#ifndef find_next_zero_bit_le
 176unsigned long find_next_zero_bit_le(const void *addr, unsigned
 177                long size, unsigned long offset)
 178{
 179        return _find_next_bit_le(addr, size, offset, ~0UL);
 180}
 181EXPORT_SYMBOL(find_next_zero_bit_le);
 182#endif
 183
 184#ifndef find_next_bit_le
 185unsigned long find_next_bit_le(const void *addr, unsigned
 186                long size, unsigned long offset)
 187{
 188        return _find_next_bit_le(addr, size, offset, 0UL);
 189}
 190EXPORT_SYMBOL(find_next_bit_le);
 191#endif
 192
 193#endif /* __BIG_ENDIAN */
 194