mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-21 23:20:20 +00:00
AK: Add Bitmap::find_one_anywhere and optimize Bitmap::find_first
Leverage constexpr and __builtin_ffs for Bitmap::find_first. Also add a variant Bitmap::find_one_anywhere that can start scanning at a provided hint. Also, merge Bitmap::fill_range into the already existing Bitmap::set_range
This commit is contained in:
parent
42153221a5
commit
92e400c7f9
Notes:
sideshowbarker
2024-07-19 02:49:24 +09:00
Author: https://github.com/tomuta Commit: https://github.com/SerenityOS/serenity/commit/92e400c7f99 Pull-request: https://github.com/SerenityOS/serenity/pull/3436
2 changed files with 203 additions and 60 deletions
129
AK/Bitmap.h
129
AK/Bitmap.h
|
@ -96,12 +96,6 @@ public:
|
|||
else
|
||||
m_data[index / 8] &= static_cast<u8>(~(1u << (index % 8)));
|
||||
}
|
||||
void set_range(size_t start, size_t len, bool value)
|
||||
{
|
||||
for (size_t index = start; index < start + len; ++index) {
|
||||
set(index, value);
|
||||
}
|
||||
}
|
||||
|
||||
size_t count_slow(bool value) const
|
||||
{
|
||||
|
@ -159,6 +153,7 @@ public:
|
|||
|
||||
void grow(size_t size, bool default_value)
|
||||
{
|
||||
ASSERT(m_owned);
|
||||
ASSERT(size > m_size);
|
||||
|
||||
auto previous_size_bytes = size_in_bytes();
|
||||
|
@ -179,7 +174,7 @@ public:
|
|||
}
|
||||
|
||||
template<bool VALUE>
|
||||
void fill_range(size_t start, size_t len)
|
||||
void set_range(size_t start, size_t len)
|
||||
{
|
||||
ASSERT(start < m_size);
|
||||
ASSERT(start + len <= m_size);
|
||||
|
@ -217,12 +212,12 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void fill_range(size_t start, size_t len, bool value)
|
||||
void set_range(size_t start, size_t len, bool value)
|
||||
{
|
||||
if (value)
|
||||
fill_range<true>(start, len);
|
||||
set_range<true>(start, len);
|
||||
else
|
||||
fill_range<false>(start, len);
|
||||
set_range<false>(start, len);
|
||||
}
|
||||
|
||||
void fill(bool value)
|
||||
|
@ -230,31 +225,109 @@ public:
|
|||
__builtin_memset(m_data, value ? 0xff : 0x00, size_in_bytes());
|
||||
}
|
||||
|
||||
Optional<size_t> find_first_set() const
|
||||
template<bool VALUE>
|
||||
Optional<size_t> find_one_anywhere(size_t hint = 0) const
|
||||
{
|
||||
size_t i = 0;
|
||||
while (i < m_size / 8 && m_data[i] == 0x00)
|
||||
i++;
|
||||
ASSERT(hint < m_size);
|
||||
const u8* end = &m_data[m_size / 8];
|
||||
|
||||
for (size_t j = i * 8; j < m_size; j++) {
|
||||
if (get(j))
|
||||
return j;
|
||||
for (;;) {
|
||||
// We will use hint as what it is: a hint. Because we try to
|
||||
// scan over entire 32 bit words, we may start searching before
|
||||
// the hint!
|
||||
const u32* ptr32 = (const u32*)((FlatPtr)&m_data[hint / 8] & ~(sizeof(u32) - 1));
|
||||
if ((const u8*)ptr32 < &m_data[0]) {
|
||||
ptr32++;
|
||||
|
||||
// m_data isn't aligned, check first bytes
|
||||
size_t start_ptr32 = (const u8*)ptr32 - &m_data[0];
|
||||
size_t i = 0;
|
||||
u8 byte = VALUE ? 0x00 : 0xff;
|
||||
while (i < start_ptr32 && m_data[i] == byte)
|
||||
i++;
|
||||
if (i < start_ptr32) {
|
||||
byte = m_data[i];
|
||||
if constexpr (!VALUE)
|
||||
byte = ~byte;
|
||||
ASSERT(byte != 0);
|
||||
return i * 8 + __builtin_ffs(byte) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
u32 val32 = VALUE ? 0x0 : 0xffffffff;
|
||||
const u32* end32 = (const u32*)((FlatPtr)end & ~(sizeof(u32) - 1));
|
||||
while (ptr32 < end32 && *ptr32 == val32)
|
||||
ptr32++;
|
||||
|
||||
if (ptr32 == end32) {
|
||||
// We didn't find anything, check the remaining few bytes (if any)
|
||||
u8 byte = VALUE ? 0x00 : 0xff;
|
||||
size_t i = (const u8*)ptr32 - &m_data[0];
|
||||
size_t byte_count = m_size / 8;
|
||||
ASSERT(i <= byte_count);
|
||||
while (i < byte_count && m_data[i] == byte)
|
||||
i++;
|
||||
if (i == byte_count) {
|
||||
if (hint <= 8)
|
||||
return {}; // We already checked from the beginning
|
||||
|
||||
// Try scanning before the hint
|
||||
end = (const u8*)((FlatPtr)&m_data[hint / 8] & ~(sizeof(u32) - 1));
|
||||
hint = 0;
|
||||
continue;
|
||||
}
|
||||
byte = m_data[i];
|
||||
if constexpr (!VALUE)
|
||||
byte = ~byte;
|
||||
ASSERT(byte != 0);
|
||||
return i * 8 + __builtin_ffs(byte) - 1;
|
||||
}
|
||||
|
||||
// NOTE: We don't really care about byte ordering. We found *one*
|
||||
// free bit, just calculate the position and return it
|
||||
val32 = *ptr32;
|
||||
if constexpr (!VALUE)
|
||||
val32 = ~val32;
|
||||
ASSERT(val32 != 0);
|
||||
return ((const u8*)ptr32 - &m_data[0]) * 8 + __builtin_ffsl(val32) - 1;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Optional<size_t> find_one_anywhere_set(size_t hint = 0) const
|
||||
{
|
||||
return find_one_anywhere<true>(hint);
|
||||
}
|
||||
Optional<size_t> find_one_anywhere_unset(size_t hint = 0) const
|
||||
{
|
||||
return find_one_anywhere<false>(hint);
|
||||
}
|
||||
|
||||
template<bool VALUE>
|
||||
Optional<size_t> find_first() const
|
||||
{
|
||||
size_t byte_count = m_size / 8;
|
||||
size_t i = 0;
|
||||
|
||||
u8 byte = VALUE ? 0x00 : 0xff;
|
||||
while (i < byte_count && m_data[i] == byte)
|
||||
i++;
|
||||
if (i == byte_count)
|
||||
return {};
|
||||
|
||||
byte = m_data[i];
|
||||
if constexpr (!VALUE)
|
||||
byte = ~byte;
|
||||
ASSERT(byte != 0);
|
||||
return i * 8 + __builtin_ffs(byte) - 1;
|
||||
}
|
||||
|
||||
Optional<size_t> find_first_set() const
|
||||
{
|
||||
return find_first<true>();
|
||||
}
|
||||
Optional<size_t> find_first_unset() const
|
||||
{
|
||||
size_t i = 0;
|
||||
while (i < m_size / 8 && m_data[i] == 0xff)
|
||||
i++;
|
||||
|
||||
for (size_t j = i * 8; j < m_size; j++)
|
||||
if (!get(j))
|
||||
return j;
|
||||
|
||||
return {};
|
||||
return find_first<false>();
|
||||
}
|
||||
|
||||
// The function will return the next range of unset bits starting from the
|
||||
|
|
|
@ -48,6 +48,78 @@ TEST_CASE(find_first_unset)
|
|||
EXPECT_EQ(bitmap.find_first_unset().value(), 51u);
|
||||
}
|
||||
|
||||
TEST_CASE(find_one_anywhere_set)
|
||||
{
|
||||
{
|
||||
Bitmap bitmap(168, false);
|
||||
bitmap.set(34, true);
|
||||
bitmap.set(97, true);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_set(0).value(), 34u);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_set(31).value(), 34u);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_set(32).value(), 34u);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_set(34).value(), 34u);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_set(36).value(), 34u);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_set(63).value(), 34u);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_set(64).value(), 97u);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_set(96).value(), 97u);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_set(96).value(), 97u);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_set(97).value(), 97u);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_set(127).value(), 97u);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_set(128).value(), 34u);
|
||||
}
|
||||
{
|
||||
Bitmap bitmap(128 + 24, false);
|
||||
bitmap.set(34, true);
|
||||
bitmap.set(126, true);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_set(0).value(), 34u);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_set(63).value(), 34u);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_set(64).value(), 126u);
|
||||
}
|
||||
{
|
||||
Bitmap bitmap(32, false);
|
||||
bitmap.set(12, true);
|
||||
bitmap.set(24, true);
|
||||
auto got = bitmap.find_one_anywhere_set(0).value();
|
||||
EXPECT(got == 12 || got == 24);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(find_one_anywhere_unset)
|
||||
{
|
||||
{
|
||||
Bitmap bitmap(168, true);
|
||||
bitmap.set(34, false);
|
||||
bitmap.set(97, false);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_unset(0).value(), 34u);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_unset(31).value(), 34u);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_unset(32).value(), 34u);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_unset(34).value(), 34u);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_unset(36).value(), 34u);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_unset(63).value(), 34u);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_unset(64).value(), 97u);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_unset(96).value(), 97u);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_unset(96).value(), 97u);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_unset(97).value(), 97u);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_unset(127).value(), 97u);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_unset(128).value(), 34u);
|
||||
}
|
||||
{
|
||||
Bitmap bitmap(128 + 24, true);
|
||||
bitmap.set(34, false);
|
||||
bitmap.set(126, false);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_unset(0).value(), 34u);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_unset(63).value(), 34u);
|
||||
EXPECT_EQ(bitmap.find_one_anywhere_unset(64).value(), 126u);
|
||||
}
|
||||
{
|
||||
Bitmap bitmap(32, true);
|
||||
bitmap.set(12, false);
|
||||
bitmap.set(24, false);
|
||||
auto got = bitmap.find_one_anywhere_unset(0).value();
|
||||
EXPECT(got == 12 || got == 24);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(find_first_range)
|
||||
{
|
||||
Bitmap bitmap(128, true);
|
||||
|
@ -65,20 +137,36 @@ TEST_CASE(find_first_range)
|
|||
|
||||
TEST_CASE(set_range)
|
||||
{
|
||||
Bitmap bitmap(128, false);
|
||||
bitmap.set_range(41, 10, true);
|
||||
EXPECT_EQ(bitmap.get(40), false);
|
||||
EXPECT_EQ(bitmap.get(41), true);
|
||||
EXPECT_EQ(bitmap.get(42), true);
|
||||
EXPECT_EQ(bitmap.get(43), true);
|
||||
EXPECT_EQ(bitmap.get(44), true);
|
||||
EXPECT_EQ(bitmap.get(45), true);
|
||||
EXPECT_EQ(bitmap.get(46), true);
|
||||
EXPECT_EQ(bitmap.get(47), true);
|
||||
EXPECT_EQ(bitmap.get(48), true);
|
||||
EXPECT_EQ(bitmap.get(49), true);
|
||||
EXPECT_EQ(bitmap.get(50), true);
|
||||
EXPECT_EQ(bitmap.get(51), false);
|
||||
{
|
||||
Bitmap bitmap(128, false);
|
||||
bitmap.set_range(41, 10, true);
|
||||
EXPECT_EQ(bitmap.get(40), false);
|
||||
EXPECT_EQ(bitmap.get(41), true);
|
||||
EXPECT_EQ(bitmap.get(42), true);
|
||||
EXPECT_EQ(bitmap.get(43), true);
|
||||
EXPECT_EQ(bitmap.get(44), true);
|
||||
EXPECT_EQ(bitmap.get(45), true);
|
||||
EXPECT_EQ(bitmap.get(46), true);
|
||||
EXPECT_EQ(bitmap.get(47), true);
|
||||
EXPECT_EQ(bitmap.get(48), true);
|
||||
EXPECT_EQ(bitmap.get(49), true);
|
||||
EXPECT_EQ(bitmap.get(50), true);
|
||||
EXPECT_EQ(bitmap.get(51), false);
|
||||
}
|
||||
{
|
||||
Bitmap bitmap(288, false);
|
||||
bitmap.set_range(48, 32, true);
|
||||
bitmap.set_range(94, 39, true);
|
||||
bitmap.set_range(190, 71, true);
|
||||
bitmap.set_range(190 + 71 - 7, 21, false); // slighly overlapping clear
|
||||
for (size_t i = 0; i < bitmap.size(); i++) {
|
||||
bool should_be_set = (i >= 48 && i < 48 + 32)
|
||||
|| (i >= 94 && i < 94 + 39)
|
||||
|| ((i >= 190 && i < 190 + 71) && !(i >= 190 + 71 - 7 && i < 190 + 71 - 7 + 21));
|
||||
EXPECT_EQ(bitmap.get(i), should_be_set);
|
||||
}
|
||||
EXPECT_EQ(bitmap.count_slow(true), 32u + 39u + 71u - 7u);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(find_first_fit)
|
||||
|
@ -181,22 +269,4 @@ TEST_CASE(count_in_range)
|
|||
test_with_value(false);
|
||||
}
|
||||
|
||||
TEST_CASE(fill_range)
|
||||
{
|
||||
Bitmap bitmap(288, false);
|
||||
bitmap.fill_range(48, 32, true);
|
||||
bitmap.fill_range(94, 39, true);
|
||||
bitmap.fill_range(190, 71, true);
|
||||
bitmap.fill_range(190 + 71 - 7, 21, false); // slighly overlapping clear
|
||||
|
||||
for (size_t i = 0; i < bitmap.size(); i++) {
|
||||
bool should_be_set = (i >= 48 && i < 48 + 32)
|
||||
|| (i >= 94 && i < 94 + 39)
|
||||
|| ((i >= 190 && i < 190 + 71) && !(i >= 190 + 71 - 7 && i < 190 + 71 - 7 + 21));
|
||||
EXPECT_EQ(bitmap.get(i), should_be_set);
|
||||
}
|
||||
|
||||
EXPECT_EQ(bitmap.count_slow(true), 32u + 39u + 71u - 7u);
|
||||
}
|
||||
|
||||
TEST_MAIN(Bitmap)
|
||||
|
|
Loading…
Reference in a new issue