Augustin Cavalier b973a1b377 kernel/util: Add fls to BitUtils.
Implemented using __builtin_clz where available, otherwise using
an algorithm derived from "Bit Twiddling Hacks" which is similar
to the one ramfs uses. GCC and Clang seem to unroll the loop on
x86 at least (but it doesn't matter there as the builtin exists,
implemented using the "bsr" instruction.)
2024-09-02 14:16:05 -04:00

141 lines
3.1 KiB
C++

/*
* Copyright 2013 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef KERNEL_UTIL_BITUTIL_H
#define KERNEL_UTIL_BITUTIL_H
#include <string.h>
#include <SupportDefs.h>
// http://graphics.stanford.edu/~seander/bithacks.html
static inline uint32
next_power_of_2(uint32 v)
{
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return v;
}
// http://graphics.stanford.edu/~seander/bithacks.html
static inline uint32
count_set_bits(uint32 v)
{
v = v - ((v >> 1) & 0x55555555);
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
return (((v + (v >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24;
}
static inline uint32
fls(uint32 value)
{
if (value == 0)
return 0;
#if __has_builtin(__builtin_clz)
return ((sizeof(value) * 8) - __builtin_clz(value));
#else
// https://graphics.stanford.edu/~seander/bithacks.html#IntegerLog
static const uint32 masks[] = {
0xaaaaaaaa,
0xcccccccc,
0xf0f0f0f0,
0xff00ff00,
0xffff0000
};
uint32 result = (value & masks[0]) != 0;
for (int i = 4; i > 0; i--)
result |= ((value & masks[i]) != 0) << i;
return result + 1;
#endif
}
static inline uint32
log2(uint32 v)
{
static const int MultiplyDeBruijnBitPosition[32] = {
0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
};
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
return MultiplyDeBruijnBitPosition[(uint32)(v * 0x07C4ACDDU) >> 27];
}
template<typename T>
void
bitmap_shift(T* bits, size_t bitCount, ssize_t shift)
{
if (shift == 0)
return;
const size_t bitsPerElement = sizeof(T) * 8;
const size_t elementsCount = (bitCount + bitsPerElement - 1) / bitsPerElement;
const size_t absoluteShift = (shift > 0) ? shift : -shift;
const size_t nElements = absoluteShift / bitsPerElement;
const size_t nBits = absoluteShift % bitsPerElement;
if (nElements != 0) {
if (shift > 0) {
// "Left" shift.
memmove(&bits[nElements], bits, sizeof(T) * (elementsCount - nElements));
memset(bits, 0, sizeof(T) * nElements);
} else if (shift < 0) {
// "Right" shift.
memmove(bits, &bits[nElements], sizeof(T) * (elementsCount - nElements));
memset(&bits[elementsCount - nElements], 0, sizeof(T) * nElements);
}
}
// If the shift was by a multiple of the element size, nothing more to do.
if (nBits == 0)
return;
// One set of bits comes from the "current" element and are shifted in the
// direction of the shift; the other set comes from the next-processed
// element and are shifted in the opposite direction.
if (shift > 0) {
// "Left" shift.
for (ssize_t i = elementsCount - 1; i >= 0; i--) {
T low = 0;
if (i != 0)
low = bits[i - 1] >> (bitsPerElement - nBits);
const T high = bits[i] << nBits;
bits[i] = low | high;
}
} else if (shift < 0) {
// "Right" shift.
for (size_t i = 0; i < elementsCount; i++) {
const T low = bits[i] >> nBits;
T high = 0;
if (i != (elementsCount - 1))
high = bits[i + 1] << (bitsPerElement - nBits);
bits[i] = low | high;
}
}
}
#endif // KERNEL_UTIL_BITUTIL_H