find_bit.c 3.7 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/* bit search implementation
L
Linus Torvalds 已提交
3 4 5 6
 *
 * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
 * Written by David Howells (dhowells@redhat.com)
 *
7 8 9 10
 * Copyright (C) 2008 IBM Corporation
 * 'find_last_bit' is written by Rusty Russell <rusty@rustcorp.com.au>
 * (Inspired by David Howell's find_next_bit implementation)
 *
Y
Yury Norov 已提交
11 12
 * Rewritten by Yury Norov <yury.norov@gmail.com> to decrease
 * size and improve performance, 2015.
L
Linus Torvalds 已提交
13 14 15
 */

#include <linux/bitops.h>
16
#include <linux/bitmap.h>
17
#include <linux/export.h>
18
#include <linux/math.h>
19
#include <linux/minmax.h>
20
#include <linux/swab.h>
L
Linus Torvalds 已提交
21

22 23 24
#if !defined(find_next_bit) || !defined(find_next_zero_bit) ||			\
	!defined(find_next_bit_le) || !defined(find_next_zero_bit_le) ||	\
	!defined(find_next_and_bit)
25
/*
26 27 28 29 30
 * This is a common helper function for find_next_bit, find_next_zero_bit, and
 * find_next_and_bit. The differences are:
 *  - The "invert" argument, which is XORed with each fetched word before
 *    searching it for one bits.
 *  - The optional "addr2", which is anded with "addr1" if present.
31
 */
32
unsigned long _find_next_bit(const unsigned long *addr1,
33
		const unsigned long *addr2, unsigned long nbits,
34
		unsigned long start, unsigned long invert, unsigned long le)
L
Linus Torvalds 已提交
35
{
36
	unsigned long tmp, mask;
L
Linus Torvalds 已提交
37

38
	if (unlikely(start >= nbits))
Y
Yury Norov 已提交
39 40
		return nbits;

41 42 43 44
	tmp = addr1[start / BITS_PER_LONG];
	if (addr2)
		tmp &= addr2[start / BITS_PER_LONG];
	tmp ^= invert;
Y
Yury Norov 已提交
45 46

	/* Handle 1st word. */
47 48 49 50 51 52
	mask = BITMAP_FIRST_WORD_MASK(start);
	if (le)
		mask = swab(mask);

	tmp &= mask;

Y
Yury Norov 已提交
53 54 55 56 57 58 59
	start = round_down(start, BITS_PER_LONG);

	while (!tmp) {
		start += BITS_PER_LONG;
		if (start >= nbits)
			return nbits;

60 61 62 63
		tmp = addr1[start / BITS_PER_LONG];
		if (addr2)
			tmp &= addr2[start / BITS_PER_LONG];
		tmp ^= invert;
L
Linus Torvalds 已提交
64 65
	}

66 67 68
	if (le)
		tmp = swab(tmp);

Y
Yury Norov 已提交
69
	return min(start + __ffs(tmp), nbits);
70
}
71
EXPORT_SYMBOL(_find_next_bit);
72 73
#endif

74
#ifndef find_first_bit
75 76 77
/*
 * Find the first set bit in a memory region.
 */
78
unsigned long _find_first_bit(const unsigned long *addr, unsigned long size)
79
{
Y
Yury Norov 已提交
80
	unsigned long idx;
81

Y
Yury Norov 已提交
82 83 84
	for (idx = 0; idx * BITS_PER_LONG < size; idx++) {
		if (addr[idx])
			return min(idx * BITS_PER_LONG + __ffs(addr[idx]), size);
85 86
	}

Y
Yury Norov 已提交
87
	return size;
88
}
89
EXPORT_SYMBOL(_find_first_bit);
90
#endif
91

Y
Yury Norov 已提交
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
#ifndef find_first_and_bit
/*
 * Find the first set bit in two memory regions.
 */
unsigned long _find_first_and_bit(const unsigned long *addr1,
				  const unsigned long *addr2,
				  unsigned long size)
{
	unsigned long idx, val;

	for (idx = 0; idx * BITS_PER_LONG < size; idx++) {
		val = addr1[idx] & addr2[idx];
		if (val)
			return min(idx * BITS_PER_LONG + __ffs(val), size);
	}

	return size;
}
EXPORT_SYMBOL(_find_first_and_bit);
#endif

113
#ifndef find_first_zero_bit
114 115 116
/*
 * Find the first cleared bit in a memory region.
 */
117
unsigned long _find_first_zero_bit(const unsigned long *addr, unsigned long size)
118
{
Y
Yury Norov 已提交
119
	unsigned long idx;
120

Y
Yury Norov 已提交
121 122 123
	for (idx = 0; idx * BITS_PER_LONG < size; idx++) {
		if (addr[idx] != ~0UL)
			return min(idx * BITS_PER_LONG + ffz(addr[idx]), size);
124 125
	}

Y
Yury Norov 已提交
126
	return size;
127
}
128
EXPORT_SYMBOL(_find_first_zero_bit);
129
#endif
130

131
#ifndef find_last_bit
132
unsigned long _find_last_bit(const unsigned long *addr, unsigned long size)
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
{
	if (size) {
		unsigned long val = BITMAP_LAST_WORD_MASK(size);
		unsigned long idx = (size-1) / BITS_PER_LONG;

		do {
			val &= addr[idx];
			if (val)
				return idx * BITS_PER_LONG + __fls(val);

			val = ~0ul;
		} while (idx--);
	}
	return size;
}
148
EXPORT_SYMBOL(_find_last_bit);
149 150
#endif

151 152 153 154 155 156 157 158 159 160 161 162 163
unsigned long find_next_clump8(unsigned long *clump, const unsigned long *addr,
			       unsigned long size, unsigned long offset)
{
	offset = find_next_bit(addr, size, offset);
	if (offset == size)
		return size;

	offset = round_down(offset, 8);
	*clump = bitmap_get_value8(addr, offset);

	return offset;
}
EXPORT_SYMBOL(find_next_clump8);