find_bit.c 4.5 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
static 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
#endif
L
Linus Torvalds 已提交
72

Y
Yury Norov 已提交
73
#ifndef find_next_bit
74
/*
Y
Yury Norov 已提交
75
 * Find the next set bit in a memory region.
76
 */
Y
Yury Norov 已提交
77 78 79
unsigned long find_next_bit(const unsigned long *addr, unsigned long size,
			    unsigned long offset)
{
80
	return _find_next_bit(addr, NULL, size, offset, 0UL, 0);
Y
Yury Norov 已提交
81 82 83 84 85
}
EXPORT_SYMBOL(find_next_bit);
#endif

#ifndef find_next_zero_bit
T
Thomas Gleixner 已提交
86 87
unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size,
				 unsigned long offset)
88
{
89
	return _find_next_bit(addr, NULL, size, offset, ~0UL, 0);
L
Linus Torvalds 已提交
90
}
T
Thomas Gleixner 已提交
91
EXPORT_SYMBOL(find_next_zero_bit);
92
#endif
93

94 95 96 97 98
#if !defined(find_next_and_bit)
unsigned long find_next_and_bit(const unsigned long *addr1,
		const unsigned long *addr2, unsigned long size,
		unsigned long offset)
{
99
	return _find_next_bit(addr1, addr2, size, offset, 0UL, 0);
100 101 102 103
}
EXPORT_SYMBOL(find_next_and_bit);
#endif

104
#ifndef find_first_bit
105 106 107
/*
 * Find the first set bit in a memory region.
 */
T
Thomas Gleixner 已提交
108
unsigned long find_first_bit(const unsigned long *addr, unsigned long size)
109
{
Y
Yury Norov 已提交
110
	unsigned long idx;
111

Y
Yury Norov 已提交
112 113 114
	for (idx = 0; idx * BITS_PER_LONG < size; idx++) {
		if (addr[idx])
			return min(idx * BITS_PER_LONG + __ffs(addr[idx]), size);
115 116
	}

Y
Yury Norov 已提交
117
	return size;
118
}
T
Thomas Gleixner 已提交
119
EXPORT_SYMBOL(find_first_bit);
120
#endif
121

122
#ifndef find_first_zero_bit
123 124 125
/*
 * Find the first cleared bit in a memory region.
 */
T
Thomas Gleixner 已提交
126
unsigned long find_first_zero_bit(const unsigned long *addr, unsigned long size)
127
{
Y
Yury Norov 已提交
128
	unsigned long idx;
129

Y
Yury Norov 已提交
130 131 132
	for (idx = 0; idx * BITS_PER_LONG < size; idx++) {
		if (addr[idx] != ~0UL)
			return min(idx * BITS_PER_LONG + ffz(addr[idx]), size);
133 134
	}

Y
Yury Norov 已提交
135
	return size;
136
}
T
Thomas Gleixner 已提交
137
EXPORT_SYMBOL(find_first_zero_bit);
138
#endif
139

140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
#ifndef find_last_bit
unsigned long find_last_bit(const unsigned long *addr, unsigned long size)
{
	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;
}
EXPORT_SYMBOL(find_last_bit);
#endif

160 161
#ifdef __BIG_ENDIAN

Y
Yury Norov 已提交
162 163 164 165
#ifndef find_next_zero_bit_le
unsigned long find_next_zero_bit_le(const void *addr, unsigned
		long size, unsigned long offset)
{
166
	return _find_next_bit(addr, NULL, size, offset, ~0UL, 1);
167
}
168
EXPORT_SYMBOL(find_next_zero_bit_le);
169
#endif
170

171
#ifndef find_next_bit_le
172
unsigned long find_next_bit_le(const void *addr, unsigned
173 174
		long size, unsigned long offset)
{
175
	return _find_next_bit(addr, NULL, size, offset, 0UL, 1);
176
}
177
EXPORT_SYMBOL(find_next_bit_le);
178
#endif
179

180
#endif /* __BIG_ENDIAN */
181 182 183 184 185 186 187 188 189 190 191 192 193 194

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);