find_bit.c 5.1 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>
Y
Yury Norov 已提交
18
#include <linux/kernel.h>
L
Linus Torvalds 已提交
19

20 21
#if !defined(find_next_bit) || !defined(find_next_zero_bit) || \
		!defined(find_next_and_bit)
22

23
/*
24 25 26 27 28
 * 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.
29
 */
30 31 32
static inline unsigned long _find_next_bit(const unsigned long *addr1,
		const unsigned long *addr2, unsigned long nbits,
		unsigned long start, unsigned long invert)
L
Linus Torvalds 已提交
33 34 35
{
	unsigned long tmp;

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

39 40 41 42
	tmp = addr1[start / BITS_PER_LONG];
	if (addr2)
		tmp &= addr2[start / BITS_PER_LONG];
	tmp ^= invert;
Y
Yury Norov 已提交
43 44 45 46 47 48 49 50 51 52

	/* Handle 1st word. */
	tmp &= BITMAP_FIRST_WORD_MASK(start);
	start = round_down(start, BITS_PER_LONG);

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

53 54 55 56
		tmp = addr1[start / BITS_PER_LONG];
		if (addr2)
			tmp &= addr2[start / BITS_PER_LONG];
		tmp ^= invert;
L
Linus Torvalds 已提交
57 58
	}

Y
Yury Norov 已提交
59
	return min(start + __ffs(tmp), nbits);
60
}
61
#endif
L
Linus Torvalds 已提交
62

Y
Yury Norov 已提交
63
#ifndef find_next_bit
64
/*
Y
Yury Norov 已提交
65
 * Find the next set bit in a memory region.
66
 */
Y
Yury Norov 已提交
67 68 69
unsigned long find_next_bit(const unsigned long *addr, unsigned long size,
			    unsigned long offset)
{
70
	return _find_next_bit(addr, NULL, size, offset, 0UL);
Y
Yury Norov 已提交
71 72 73 74 75
}
EXPORT_SYMBOL(find_next_bit);
#endif

#ifndef find_next_zero_bit
T
Thomas Gleixner 已提交
76 77
unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size,
				 unsigned long offset)
78
{
79
	return _find_next_bit(addr, NULL, size, offset, ~0UL);
L
Linus Torvalds 已提交
80
}
T
Thomas Gleixner 已提交
81
EXPORT_SYMBOL(find_next_zero_bit);
82
#endif
83

84 85 86 87 88 89 90 91 92 93
#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)
{
	return _find_next_bit(addr1, addr2, size, offset, 0UL);
}
EXPORT_SYMBOL(find_next_and_bit);
#endif

94
#ifndef find_first_bit
95 96 97
/*
 * Find the first set bit in a memory region.
 */
T
Thomas Gleixner 已提交
98
unsigned long find_first_bit(const unsigned long *addr, unsigned long size)
99
{
Y
Yury Norov 已提交
100
	unsigned long idx;
101

Y
Yury Norov 已提交
102 103 104
	for (idx = 0; idx * BITS_PER_LONG < size; idx++) {
		if (addr[idx])
			return min(idx * BITS_PER_LONG + __ffs(addr[idx]), size);
105 106
	}

Y
Yury Norov 已提交
107
	return size;
108
}
T
Thomas Gleixner 已提交
109
EXPORT_SYMBOL(find_first_bit);
110
#endif
111

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

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

Y
Yury Norov 已提交
125
	return size;
126
}
T
Thomas Gleixner 已提交
127
EXPORT_SYMBOL(find_first_zero_bit);
128
#endif
129

130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
#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

150 151
#ifdef __BIG_ENDIAN

Y
Yury Norov 已提交
152
#if !defined(find_next_bit_le) || !defined(find_next_zero_bit_le)
153 154 155
static inline unsigned long _find_next_bit_le(const unsigned long *addr1,
		const unsigned long *addr2, unsigned long nbits,
		unsigned long start, unsigned long invert)
156 157 158
{
	unsigned long tmp;

159
	if (unlikely(start >= nbits))
Y
Yury Norov 已提交
160 161
		return nbits;

162 163 164 165
	tmp = addr1[start / BITS_PER_LONG];
	if (addr2)
		tmp &= addr2[start / BITS_PER_LONG];
	tmp ^= invert;
Y
Yury Norov 已提交
166 167

	/* Handle 1st word. */
168
	tmp &= swab(BITMAP_FIRST_WORD_MASK(start));
Y
Yury Norov 已提交
169
	start = round_down(start, BITS_PER_LONG);
170

Y
Yury Norov 已提交
171 172 173 174 175
	while (!tmp) {
		start += BITS_PER_LONG;
		if (start >= nbits)
			return nbits;

176 177 178 179
		tmp = addr1[start / BITS_PER_LONG];
		if (addr2)
			tmp &= addr2[start / BITS_PER_LONG];
		tmp ^= invert;
180 181
	}

182
	return min(start + __ffs(swab(tmp)), nbits);
Y
Yury Norov 已提交
183 184 185 186 187 188 189
}
#endif

#ifndef find_next_zero_bit_le
unsigned long find_next_zero_bit_le(const void *addr, unsigned
		long size, unsigned long offset)
{
190
	return _find_next_bit_le(addr, NULL, size, offset, ~0UL);
191
}
192
EXPORT_SYMBOL(find_next_zero_bit_le);
193
#endif
194

195
#ifndef find_next_bit_le
196
unsigned long find_next_bit_le(const void *addr, unsigned
197 198
		long size, unsigned long offset)
{
199
	return _find_next_bit_le(addr, NULL, size, offset, 0UL);
200
}
201
EXPORT_SYMBOL(find_next_bit_le);
202
#endif
203

204
#endif /* __BIG_ENDIAN */
205 206 207 208 209 210 211 212 213 214 215 216 217 218

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