Coverage Report

Created: 2026-02-16 07:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/AK/BitmapView.h
Line
Count
Source
1
/*
2
 * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#pragma once
8
9
#include <AK/Array.h>
10
#include <AK/BuiltinWrappers.h>
11
#include <AK/Optional.h>
12
#include <AK/StdLibExtras.h>
13
#include <AK/Types.h>
14
15
namespace AK {
16
17
static constexpr Array bitmask_first_byte = { 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80 };
18
static constexpr Array bitmask_last_byte = { 0x00, 0x1, 0x3, 0x7, 0xF, 0x1F, 0x3F, 0x7F };
19
20
class BitmapView {
21
public:
22
    BitmapView() = default;
23
24
    BitmapView(u8* data, size_t size)
25
0
        : m_data(data)
26
0
        , m_size(size)
27
0
    {
28
0
    }
29
30
0
    [[nodiscard]] size_t size() const { return m_size; }
31
0
    [[nodiscard]] size_t size_in_bytes() const { return ceil_div(m_size, static_cast<size_t>(8)); }
32
    [[nodiscard]] bool get(size_t index) const
33
0
    {
34
0
        VERIFY(index < m_size);
35
0
        return 0 != (m_data[index / 8] & (1u << (index % 8)));
36
0
    }
Unexecuted instantiation: AK::BitmapView::get(unsigned long) const
Unexecuted instantiation: AK::BitmapView::get(unsigned long) const
37
38
    [[nodiscard]] size_t count_slow(bool value) const
39
0
    {
40
0
        return count_in_range(0, m_size, value);
41
0
    }
42
43
    [[nodiscard]] size_t count_in_range(size_t start, size_t len, bool value) const
44
0
    {
45
0
        VERIFY(start < m_size);
46
0
        VERIFY(start + len <= m_size);
47
0
        if (len == 0)
48
0
            return 0;
49
0
50
0
        size_t count;
51
0
        u8 const* first = &m_data[start / 8];
52
0
        u8 const* last = &m_data[(start + len) / 8];
53
0
        u8 byte = *first;
54
0
        byte &= bitmask_first_byte[start % 8];
55
0
        if (first == last) {
56
0
            byte &= bitmask_last_byte[(start + len) % 8];
57
0
            count = popcount(byte);
58
0
        } else {
59
0
            count = popcount(byte);
60
0
            // Don't access *last if it's out of bounds
61
0
            if (last < &m_data[size_in_bytes()]) {
62
0
                byte = *last;
63
0
                byte &= bitmask_last_byte[(start + len) % 8];
64
0
                count += popcount(byte);
65
0
            }
66
0
            if (++first < last) {
67
0
                size_t const* ptr_large = reinterpret_cast<size_t const*>((reinterpret_cast<FlatPtr>(first) + sizeof(size_t) - 1) & ~(sizeof(size_t) - 1));
68
0
                if (reinterpret_cast<u8 const*>(ptr_large) > last)
69
0
                    ptr_large = reinterpret_cast<size_t const*>(last);
70
0
                while (first < reinterpret_cast<u8 const*>(ptr_large)) {
71
0
                    count += popcount(*first);
72
0
                    first++;
73
0
                }
74
0
                size_t const* last_large = reinterpret_cast<size_t const*>(reinterpret_cast<FlatPtr>(last) & ~(sizeof(size_t) - 1));
75
0
                while (ptr_large < last_large) {
76
0
                    count += popcount(*ptr_large);
77
0
                    ptr_large++;
78
0
                }
79
0
                for (first = reinterpret_cast<u8 const*>(ptr_large); first < last; first++)
80
0
                    count += popcount(*first);
81
0
            }
82
0
        }
83
0
84
0
        if (!value)
85
0
            count = len - count;
86
0
        return count;
87
0
    }
88
89
0
    [[nodiscard]] bool is_null() const { return m_data == nullptr; }
90
91
0
    [[nodiscard]] u8 const* data() const { return m_data; }
92
93
    template<bool VALUE>
94
    Optional<size_t> find_one_anywhere(size_t hint = 0) const
95
0
    {
96
0
        VERIFY(hint < m_size);
97
0
        u8 const* end = &m_data[size_in_bytes()];
98
0
99
0
        for (;;) {
100
0
            // We will use hint as what it is: a hint. Because we try to
101
0
            // scan over entire 32 bit words, we may start searching before
102
0
            // the hint!
103
0
            size_t const* ptr_large = reinterpret_cast<size_t const*>(reinterpret_cast<FlatPtr>(&m_data[hint / 8]) & ~(sizeof(size_t) - 1));
104
0
            if (reinterpret_cast<u8 const*>(ptr_large) < &m_data[0]) {
105
0
                ptr_large++;
106
0
107
0
                // m_data isn't aligned, check first bytes
108
0
                size_t start_ptr_large = reinterpret_cast<u8 const*>(ptr_large) - &m_data[0];
109
0
                size_t i = 0;
110
0
                u8 byte = VALUE ? 0x00 : 0xff;
111
0
                while (i < start_ptr_large && m_data[i] == byte)
112
0
                    i++;
113
0
                if (i < start_ptr_large) {
114
0
                    byte = m_data[i];
115
0
                    if constexpr (!VALUE)
116
0
                        byte = ~byte;
117
0
                    VERIFY(byte != 0);
118
0
                    return i * 8 + bit_scan_forward(byte) - 1;
119
0
                }
120
0
            }
121
0
122
0
            size_t val_large = VALUE ? 0x0 : NumericLimits<size_t>::max();
123
0
            size_t const* end_large = reinterpret_cast<size_t const*>(reinterpret_cast<FlatPtr>(end) & ~(sizeof(size_t) - 1));
124
0
            while (ptr_large < end_large && *ptr_large == val_large)
125
0
                ptr_large++;
126
0
127
0
            if (ptr_large == end_large) {
128
0
                // We didn't find anything, check the remaining few bytes (if any)
129
0
                u8 byte = VALUE ? 0x00 : 0xff;
130
0
                size_t i = reinterpret_cast<u8 const*>(ptr_large) - &m_data[0];
131
0
                size_t byte_count = size_in_bytes();
132
0
                VERIFY(i <= byte_count);
133
0
                while (i < byte_count && m_data[i] == byte)
134
0
                    i++;
135
0
                if (i == byte_count) {
136
0
                    if (hint <= 8)
137
0
                        return {}; // We already checked from the beginning
138
0
139
0
                    // Try scanning before the hint
140
0
                    end = reinterpret_cast<u8 const*>(reinterpret_cast<FlatPtr>(&m_data[hint / 8]) & ~(sizeof(size_t) - 1));
141
0
                    hint = 0;
142
0
                    continue;
143
0
                }
144
0
                byte = m_data[i];
145
0
                if constexpr (!VALUE)
146
0
                    byte = ~byte;
147
0
                VERIFY(byte != 0);
148
0
                return i * 8 + bit_scan_forward(byte) - 1;
149
0
            }
150
0
151
0
            // NOTE: We don't really care about byte ordering. We found *one*
152
0
            // free bit, just calculate the position and return it
153
0
            val_large = *ptr_large;
154
0
            if constexpr (!VALUE)
155
0
                val_large = ~val_large;
156
0
            VERIFY(val_large != 0);
157
0
            return (reinterpret_cast<u8 const*>(ptr_large) - &m_data[0]) * 8 + bit_scan_forward(val_large) - 1;
158
0
        }
159
0
    }
Unexecuted instantiation: AK::Optional<unsigned long> AK::BitmapView::find_one_anywhere<true>(unsigned long) const
Unexecuted instantiation: AK::Optional<unsigned long> AK::BitmapView::find_one_anywhere<false>(unsigned long) const
160
161
    Optional<size_t> find_one_anywhere_set(size_t hint = 0) const
162
0
    {
163
0
        return find_one_anywhere<true>(hint);
164
0
    }
165
166
    Optional<size_t> find_one_anywhere_unset(size_t hint = 0) const
167
0
    {
168
0
        return find_one_anywhere<false>(hint);
169
0
    }
170
171
    template<bool VALUE>
172
    Optional<size_t> find_first() const
173
0
    {
174
0
        size_t byte_count = size_in_bytes();
175
0
        size_t i = 0;
176
0
177
0
        u8 byte = VALUE ? 0x00 : 0xff;
178
0
        while (i < byte_count && m_data[i] == byte)
179
0
            i++;
180
0
        if (i == byte_count)
181
0
            return {};
182
0
183
0
        byte = m_data[i];
184
0
        if constexpr (!VALUE)
185
0
            byte = ~byte;
186
0
        VERIFY(byte != 0);
187
0
        return i * 8 + bit_scan_forward(byte) - 1;
188
0
    }
Unexecuted instantiation: AK::Optional<unsigned long> AK::BitmapView::find_first<true>() const
Unexecuted instantiation: AK::Optional<unsigned long> AK::BitmapView::find_first<false>() const
189
190
0
    Optional<size_t> find_first_set() const { return find_first<true>(); }
191
0
    Optional<size_t> find_first_unset() const { return find_first<false>(); }
192
193
    // The function will return the next range of unset bits starting from the
194
    // @from value.
195
    // @from: the position from which the search starts. The var will be
196
    //        changed and new value is the offset of the found block.
197
    // @min_length: minimum size of the range which will be returned.
198
    // @max_length: maximum size of the range which will be returned.
199
    //              This is used to increase performance, since the range of
200
    //              unset bits can be long, and we don't need the while range,
201
    //              so we can stop when we've reached @max_length.
202
    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
203
0
    {
204
0
        if (min_length > max_length) {
205
0
            return {};
206
0
        }
207
0
208
0
        size_t bit_size = 8 * sizeof(size_t);
209
0
210
0
        size_t* bitmap = reinterpret_cast<size_t*>(m_data);
211
0
212
0
        // Calculating the start offset.
213
0
        size_t start_bucket_index = from / bit_size;
214
0
        size_t start_bucket_bit = from % bit_size;
215
0
216
0
        size_t* start_of_free_chunks = &from;
217
0
        size_t free_chunks = 0;
218
0
219
0
        for (size_t bucket_index = start_bucket_index; bucket_index < m_size / bit_size; ++bucket_index) {
220
0
            if (bitmap[bucket_index] == NumericLimits<size_t>::max()) {
221
0
                // Skip over completely full bucket of size bit_size.
222
0
                if (free_chunks >= min_length) {
223
0
                    return min(free_chunks, max_length);
224
0
                }
225
0
                free_chunks = 0;
226
0
                start_bucket_bit = 0;
227
0
                continue;
228
0
            }
229
0
            if (bitmap[bucket_index] == 0x0) {
230
0
                // Skip over completely empty bucket of size bit_size.
231
0
                if (free_chunks == 0) {
232
0
                    *start_of_free_chunks = bucket_index * bit_size;
233
0
                }
234
0
                free_chunks += bit_size;
235
0
                if (free_chunks >= max_length) {
236
0
                    return max_length;
237
0
                }
238
0
                start_bucket_bit = 0;
239
0
                continue;
240
0
            }
241
0
242
0
            size_t bucket = bitmap[bucket_index];
243
0
            u8 viewed_bits = start_bucket_bit;
244
0
            u32 trailing_zeroes = 0;
245
0
246
0
            bucket >>= viewed_bits;
247
0
            start_bucket_bit = 0;
248
0
249
0
            while (viewed_bits < bit_size) {
250
0
                if (bucket == 0) {
251
0
                    if (free_chunks == 0) {
252
0
                        *start_of_free_chunks = bucket_index * bit_size + viewed_bits;
253
0
                    }
254
0
                    free_chunks += bit_size - viewed_bits;
255
0
                    viewed_bits = bit_size;
256
0
                } else {
257
0
                    trailing_zeroes = count_trailing_zeroes(bucket);
258
0
                    bucket >>= trailing_zeroes;
259
0
260
0
                    if (free_chunks == 0) {
261
0
                        *start_of_free_chunks = bucket_index * bit_size + viewed_bits;
262
0
                    }
263
0
                    free_chunks += trailing_zeroes;
264
0
                    viewed_bits += trailing_zeroes;
265
0
266
0
                    if (free_chunks >= min_length) {
267
0
                        return min(free_chunks, max_length);
268
0
                    }
269
0
270
0
                    // Deleting trailing ones.
271
0
                    u32 trailing_ones = count_trailing_zeroes(~bucket);
272
0
                    bucket >>= trailing_ones;
273
0
                    viewed_bits += trailing_ones;
274
0
                    free_chunks = 0;
275
0
                }
276
0
            }
277
0
        }
278
0
279
0
        if (free_chunks < min_length) {
280
0
            size_t first_trailing_bit = (m_size / bit_size) * bit_size;
281
0
            size_t trailing_bits = size() % bit_size;
282
0
            for (size_t i = 0; i < trailing_bits; ++i) {
283
0
                if (!get(first_trailing_bit + i)) {
284
0
                    if (free_chunks == 0)
285
0
                        *start_of_free_chunks = first_trailing_bit + i;
286
0
                    if (++free_chunks >= min_length)
287
0
                        return min(free_chunks, max_length);
288
0
                } else {
289
0
                    free_chunks = 0;
290
0
                }
291
0
            }
292
0
            return {};
293
0
        }
294
0
295
0
        return min(free_chunks, max_length);
296
0
    }
297
298
    Optional<size_t> find_longest_range_of_unset_bits(size_t max_length, size_t& found_range_size) const
299
0
    {
300
0
        size_t start = 0;
301
0
        size_t max_region_start = 0;
302
0
        size_t max_region_size = 0;
303
0
304
0
        while (true) {
305
0
            // Look for the next block which is bigger than currunt.
306
0
            auto length_of_found_range = find_next_range_of_unset_bits(start, max_region_size + 1, max_length);
307
0
            if (length_of_found_range.has_value()) {
308
0
                max_region_start = start;
309
0
                max_region_size = length_of_found_range.value();
310
0
                start += max_region_size;
311
0
            } else {
312
0
                // No ranges which are bigger than current were found.
313
0
                break;
314
0
            }
315
0
        }
316
0
317
0
        found_range_size = max_region_size;
318
0
        if (max_region_size != 0) {
319
0
            return max_region_start;
320
0
        }
321
0
        return {};
322
0
    }
323
324
    Optional<size_t> find_first_fit(size_t minimum_length) const
325
0
    {
326
0
        size_t start = 0;
327
0
        auto length_of_found_range = find_next_range_of_unset_bits(start, minimum_length, minimum_length);
328
0
        if (length_of_found_range.has_value()) {
329
0
            return start;
330
0
        }
331
0
        return {};
332
0
    }
333
334
    Optional<size_t> find_best_fit(size_t minimum_length) const
335
0
    {
336
0
        size_t start = 0;
337
0
        size_t best_region_start = 0;
338
0
        size_t best_region_size = max_size;
339
0
        bool found = false;
340
0
341
0
        while (true) {
342
0
            // Look for the next block which is bigger than requested length.
343
0
            auto length_of_found_range = find_next_range_of_unset_bits(start, minimum_length, best_region_size);
344
0
            if (length_of_found_range.has_value()) {
345
0
                if (best_region_size > length_of_found_range.value() || !found) {
346
0
                    best_region_start = start;
347
0
                    best_region_size = length_of_found_range.value();
348
0
                    found = true;
349
0
                }
350
0
                start += length_of_found_range.value();
351
0
            } else {
352
0
                // There are no ranges which can fit requested length.
353
0
                break;
354
0
            }
355
0
        }
356
0
357
0
        if (found) {
358
0
            return best_region_start;
359
0
        }
360
0
        return {};
361
0
    }
362
363
    static constexpr size_t max_size = 0xffffffff;
364
365
protected:
366
    u8* m_data { nullptr };
367
    size_t m_size { 0 };
368
};
369
370
}
371
372
#if USING_AK_GLOBALLY
373
using AK::BitmapView;
374
#endif