find_bit.c 3.3 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.
 */
T
Thomas Gleixner 已提交
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
}
T
Thomas Gleixner 已提交
89
EXPORT_SYMBOL(find_first_bit);
90
#endif
91

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

Y
Yury Norov 已提交
100 101 102
	for (idx = 0; idx * BITS_PER_LONG < size; idx++) {
		if (addr[idx] != ~0UL)
			return min(idx * BITS_PER_LONG + ffz(addr[idx]), size);
103 104
	}

Y
Yury Norov 已提交
105
	return size;
106
}
T
Thomas Gleixner 已提交
107
EXPORT_SYMBOL(find_first_zero_bit);
108
#endif
109

110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
#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

130 131 132 133 134 135 136 137 138 139 140 141 142
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);