From 92e400c7f99d08b5d141682f6c6f8716d3245110 Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 7 Sep 2020 15:42:50 -0600 Subject: [PATCH] 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 --- AK/Bitmap.h | 129 +++++++++++++++++++++++++++++--------- AK/Tests/TestBitmap.cpp | 134 ++++++++++++++++++++++++++++++---------- 2 files changed, 203 insertions(+), 60 deletions(-) diff --git a/AK/Bitmap.h b/AK/Bitmap.h index 22bd892aba9..0b7a3a8766c 100644 --- a/AK/Bitmap.h +++ b/AK/Bitmap.h @@ -96,12 +96,6 @@ public: else m_data[index / 8] &= static_cast(~(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 - 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(start, len); + set_range(start, len); else - fill_range(start, len); + set_range(start, len); } void fill(bool value) @@ -230,31 +225,109 @@ public: __builtin_memset(m_data, value ? 0xff : 0x00, size_in_bytes()); } - Optional find_first_set() const + template + Optional 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 find_one_anywhere_set(size_t hint = 0) const + { + return find_one_anywhere(hint); + } + Optional find_one_anywhere_unset(size_t hint = 0) const + { + return find_one_anywhere(hint); + } + + template + Optional 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 find_first_set() const + { + return find_first(); + } Optional 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(); } // The function will return the next range of unset bits starting from the diff --git a/AK/Tests/TestBitmap.cpp b/AK/Tests/TestBitmap.cpp index 125e0c6643b..029e3737063 100644 --- a/AK/Tests/TestBitmap.cpp +++ b/AK/Tests/TestBitmap.cpp @@ -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)