123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372 |
- /*
- * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
- #pragma once
- #include <AK/Array.h>
- #include <AK/BuiltinWrappers.h>
- #include <AK/Optional.h>
- #include <AK/StdLibExtras.h>
- #include <AK/Types.h>
- namespace AK {
- static constexpr Array bitmask_first_byte = { 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80 };
- static constexpr Array bitmask_last_byte = { 0x00, 0x1, 0x3, 0x7, 0xF, 0x1F, 0x3F, 0x7F };
- class BitmapView {
- public:
- BitmapView() = default;
- BitmapView(u8* data, size_t size)
- : m_data(data)
- , m_size(size)
- {
- }
- [[nodiscard]] size_t size() const { return m_size; }
- [[nodiscard]] size_t size_in_bytes() const { return ceil_div(m_size, static_cast<size_t>(8)); }
- [[nodiscard]] bool get(size_t index) const
- {
- VERIFY(index < m_size);
- return 0 != (m_data[index / 8] & (1u << (index % 8)));
- }
- [[nodiscard]] size_t count_slow(bool value) const
- {
- return count_in_range(0, m_size, value);
- }
- [[nodiscard]] size_t count_in_range(size_t start, size_t len, bool value) const
- {
- VERIFY(start < m_size);
- VERIFY(start + len <= m_size);
- if (len == 0)
- return 0;
- size_t count;
- const u8* first = &m_data[start / 8];
- const u8* last = &m_data[(start + len) / 8];
- u8 byte = *first;
- byte &= bitmask_first_byte[start % 8];
- if (first == last) {
- byte &= bitmask_last_byte[(start + len) % 8];
- count = popcount(byte);
- } else {
- count = popcount(byte);
- // Don't access *last if it's out of bounds
- if (last < &m_data[size_in_bytes()]) {
- byte = *last;
- byte &= bitmask_last_byte[(start + len) % 8];
- count += popcount(byte);
- }
- if (++first < last) {
- const size_t* ptr_large = (const size_t*)(((FlatPtr)first + sizeof(size_t) - 1) & ~(sizeof(size_t) - 1));
- if ((const u8*)ptr_large > last)
- ptr_large = (const size_t*)last;
- while (first < (const u8*)ptr_large) {
- count += popcount(*first);
- first++;
- }
- const size_t* last_large = (const size_t*)((FlatPtr)last & ~(sizeof(size_t) - 1));
- while (ptr_large < last_large) {
- count += popcount(*ptr_large);
- ptr_large++;
- }
- for (first = (const u8*)ptr_large; first < last; first++)
- count += popcount(*first);
- }
- }
- if (!value)
- count = len - count;
- return count;
- }
- [[nodiscard]] bool is_null() const { return m_data == nullptr; }
- [[nodiscard]] const u8* data() const { return m_data; }
- template<bool VALUE>
- Optional<size_t> find_one_anywhere(size_t hint = 0) const
- {
- VERIFY(hint < m_size);
- const u8* end = &m_data[m_size / 8];
- 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 size_t* ptr_large = (const size_t*)((FlatPtr)&m_data[hint / 8] & ~(sizeof(size_t) - 1));
- if ((const u8*)ptr_large < &m_data[0]) {
- ptr_large++;
- // m_data isn't aligned, check first bytes
- size_t start_ptr_large = (const u8*)ptr_large - &m_data[0];
- size_t i = 0;
- u8 byte = VALUE ? 0x00 : 0xff;
- while (i < start_ptr_large && m_data[i] == byte)
- i++;
- if (i < start_ptr_large) {
- byte = m_data[i];
- if constexpr (!VALUE)
- byte = ~byte;
- VERIFY(byte != 0);
- return i * 8 + bit_scan_forward(byte) - 1;
- }
- }
- size_t val_large = VALUE ? 0x0 : NumericLimits<size_t>::max();
- const size_t* end_large = (const size_t*)((FlatPtr)end & ~(sizeof(size_t) - 1));
- while (ptr_large < end_large && *ptr_large == val_large)
- ptr_large++;
- if (ptr_large == end_large) {
- // We didn't find anything, check the remaining few bytes (if any)
- u8 byte = VALUE ? 0x00 : 0xff;
- size_t i = (const u8*)ptr_large - &m_data[0];
- size_t byte_count = m_size / 8;
- VERIFY(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(size_t) - 1));
- hint = 0;
- continue;
- }
- byte = m_data[i];
- if constexpr (!VALUE)
- byte = ~byte;
- VERIFY(byte != 0);
- return i * 8 + bit_scan_forward(byte) - 1;
- }
- // NOTE: We don't really care about byte ordering. We found *one*
- // free bit, just calculate the position and return it
- val_large = *ptr_large;
- if constexpr (!VALUE)
- val_large = ~val_large;
- VERIFY(val_large != 0);
- return ((const u8*)ptr_large - &m_data[0]) * 8 + bit_scan_forward(val_large) - 1;
- }
- }
- 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;
- VERIFY(byte != 0);
- return i * 8 + bit_scan_forward(byte) - 1;
- }
- Optional<size_t> find_first_set() const { return find_first<true>(); }
- Optional<size_t> find_first_unset() const { return find_first<false>(); }
- // The function will return the next range of unset bits starting from the
- // @from value.
- // @from: the position from which the search starts. The var will be
- // changed and new value is the offset of the found block.
- // @min_length: minimum size of the range which will be returned.
- // @max_length: maximum size of the range which will be returned.
- // This is used to increase performance, since the range of
- // unset bits can be long, and we don't need the while range,
- // so we can stop when we've reached @max_length.
- inline Optional<size_t> find_next_range_of_unset_bits(size_t& from, size_t min_length = 1, size_t max_length = max_size) const
- {
- if (min_length > max_length) {
- return {};
- }
- size_t bit_size = 8 * sizeof(size_t);
- size_t* bitmap = (size_t*)m_data;
- // Calculating the start offset.
- size_t start_bucket_index = from / bit_size;
- size_t start_bucket_bit = from % bit_size;
- size_t* start_of_free_chunks = &from;
- size_t free_chunks = 0;
- for (size_t bucket_index = start_bucket_index; bucket_index < m_size / bit_size; ++bucket_index) {
- if (bitmap[bucket_index] == NumericLimits<size_t>::max()) {
- // Skip over completely full bucket of size bit_size.
- if (free_chunks >= min_length) {
- return min(free_chunks, max_length);
- }
- free_chunks = 0;
- start_bucket_bit = 0;
- continue;
- }
- if (bitmap[bucket_index] == 0x0) {
- // Skip over completely empty bucket of size bit_size.
- if (free_chunks == 0) {
- *start_of_free_chunks = bucket_index * bit_size;
- }
- free_chunks += bit_size;
- if (free_chunks >= max_length) {
- return max_length;
- }
- start_bucket_bit = 0;
- continue;
- }
- size_t bucket = bitmap[bucket_index];
- u8 viewed_bits = start_bucket_bit;
- u32 trailing_zeroes = 0;
- bucket >>= viewed_bits;
- start_bucket_bit = 0;
- while (viewed_bits < bit_size) {
- if (bucket == 0) {
- if (free_chunks == 0) {
- *start_of_free_chunks = bucket_index * bit_size + viewed_bits;
- }
- free_chunks += bit_size - viewed_bits;
- viewed_bits = bit_size;
- } else {
- trailing_zeroes = count_trailing_zeroes(bucket);
- bucket >>= trailing_zeroes;
- if (free_chunks == 0) {
- *start_of_free_chunks = bucket_index * bit_size + viewed_bits;
- }
- free_chunks += trailing_zeroes;
- viewed_bits += trailing_zeroes;
- if (free_chunks >= min_length) {
- return min(free_chunks, max_length);
- }
- // Deleting trailing ones.
- u32 trailing_ones = count_trailing_zeroes(~bucket);
- bucket >>= trailing_ones;
- viewed_bits += trailing_ones;
- free_chunks = 0;
- }
- }
- }
- if (free_chunks < min_length) {
- size_t first_trailing_bit = (m_size / bit_size) * bit_size;
- size_t trailing_bits = size() % bit_size;
- for (size_t i = 0; i < trailing_bits; ++i) {
- if (!get(first_trailing_bit + i)) {
- if (free_chunks == 0)
- *start_of_free_chunks = first_trailing_bit + i;
- if (++free_chunks >= min_length)
- return min(free_chunks, max_length);
- } else {
- free_chunks = 0;
- }
- }
- return {};
- }
- return min(free_chunks, max_length);
- }
- Optional<size_t> find_longest_range_of_unset_bits(size_t max_length, size_t& found_range_size) const
- {
- size_t start = 0;
- size_t max_region_start = 0;
- size_t max_region_size = 0;
- while (true) {
- // Look for the next block which is bigger than currunt.
- auto length_of_found_range = find_next_range_of_unset_bits(start, max_region_size + 1, max_length);
- if (length_of_found_range.has_value()) {
- max_region_start = start;
- max_region_size = length_of_found_range.value();
- start += max_region_size;
- } else {
- // No ranges which are bigger than current were found.
- break;
- }
- }
- found_range_size = max_region_size;
- if (max_region_size != 0) {
- return max_region_start;
- }
- return {};
- }
- Optional<size_t> find_first_fit(size_t minimum_length) const
- {
- size_t start = 0;
- auto length_of_found_range = find_next_range_of_unset_bits(start, minimum_length, minimum_length);
- if (length_of_found_range.has_value()) {
- return start;
- }
- return {};
- }
- Optional<size_t> find_best_fit(size_t minimum_length) const
- {
- size_t start = 0;
- size_t best_region_start = 0;
- size_t best_region_size = max_size;
- bool found = false;
- while (true) {
- // Look for the next block which is bigger than requested length.
- auto length_of_found_range = find_next_range_of_unset_bits(start, minimum_length, best_region_size);
- if (length_of_found_range.has_value()) {
- if (best_region_size > length_of_found_range.value() || !found) {
- best_region_start = start;
- best_region_size = length_of_found_range.value();
- found = true;
- }
- start += length_of_found_range.value();
- } else {
- // There are no ranges which can fit requested length.
- break;
- }
- }
- if (found) {
- return best_region_start;
- }
- return {};
- }
- static constexpr size_t max_size = 0xffffffff;
- protected:
- u8* m_data { nullptr };
- size_t m_size { 0 };
- };
- }
- using AK::BitmapView;
|