/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 |