1 // Copyright 2009 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9
10 #include "net/disk_cache/blockfile/bitmap.h"
11
12 #include <algorithm>
13 #include <bit>
14
15 #include "base/check_op.h"
16
17 namespace {
18 // Returns the index of the first bit set to |value| from |word|. This code
19 // assumes that we'll be able to find that bit.
FindLSBNonEmpty(uint32_t word,bool value)20 int FindLSBNonEmpty(uint32_t word, bool value) {
21 // If we are looking for 0, negate |word| and look for 1.
22 if (!value)
23 word = ~word;
24
25 return std::countr_zero(word);
26 }
27
28 } // namespace
29
30 namespace disk_cache {
31
32 Bitmap::Bitmap() = default;
33
Bitmap(int num_bits,bool clear_bits)34 Bitmap::Bitmap(int num_bits, bool clear_bits)
35 : num_bits_(num_bits),
36 array_size_(RequiredArraySize(num_bits)),
37 allocated_map_(std::make_unique<uint32_t[]>(array_size_)) {
38 map_ = allocated_map_.get();
39
40 // Initialize all of the bits.
41 if (clear_bits)
42 Clear();
43 }
44
Bitmap(uint32_t * map,int num_bits,int num_words)45 Bitmap::Bitmap(uint32_t* map, int num_bits, int num_words)
46 : num_bits_(num_bits),
47 // If size is larger than necessary, trim because array_size_ is used
48 // as a bound by various methods.
49 array_size_(std::min(RequiredArraySize(num_bits), num_words)),
50 map_(map) {}
51
52 Bitmap::~Bitmap() = default;
53
Resize(int num_bits,bool clear_bits)54 void Bitmap::Resize(int num_bits, bool clear_bits) {
55 DCHECK(allocated_map_ || !map_);
56 const int old_maxsize = num_bits_;
57 const int old_array_size = array_size_;
58 array_size_ = RequiredArraySize(num_bits);
59
60 if (array_size_ != old_array_size) {
61 auto new_map = std::make_unique<uint32_t[]>(array_size_);
62 // Always clear the unused bits in the last word.
63 new_map[array_size_ - 1] = 0;
64 std::copy(map_, map_ + std::min(array_size_, old_array_size),
65 new_map.get());
66 map_ = new_map.get();
67 allocated_map_ = std::move(new_map);
68 }
69
70 num_bits_ = num_bits;
71 if (old_maxsize < num_bits_ && clear_bits) {
72 SetRange(old_maxsize, num_bits_, false);
73 }
74 }
75
Set(int index,bool value)76 void Bitmap::Set(int index, bool value) {
77 DCHECK_LT(index, num_bits_);
78 DCHECK_GE(index, 0);
79 const int i = index & (kIntBits - 1);
80 const int j = index / kIntBits;
81 if (value)
82 map_[j] |= (1 << i);
83 else
84 map_[j] &= ~(1 << i);
85 }
86
Get(int index) const87 bool Bitmap::Get(int index) const {
88 DCHECK_LT(index, num_bits_);
89 DCHECK_GE(index, 0);
90 const int i = index & (kIntBits-1);
91 const int j = index / kIntBits;
92 return ((map_[j] & (1 << i)) != 0);
93 }
94
Toggle(int index)95 void Bitmap::Toggle(int index) {
96 DCHECK_LT(index, num_bits_);
97 DCHECK_GE(index, 0);
98 const int i = index & (kIntBits - 1);
99 const int j = index / kIntBits;
100 map_[j] ^= (1 << i);
101 }
102
SetMapElement(int array_index,uint32_t value)103 void Bitmap::SetMapElement(int array_index, uint32_t value) {
104 DCHECK_LT(array_index, array_size_);
105 DCHECK_GE(array_index, 0);
106 map_[array_index] = value;
107 }
108
GetMapElement(int array_index) const109 uint32_t Bitmap::GetMapElement(int array_index) const {
110 DCHECK_LT(array_index, array_size_);
111 DCHECK_GE(array_index, 0);
112 return map_[array_index];
113 }
114
SetMap(const uint32_t * map,int size)115 void Bitmap::SetMap(const uint32_t* map, int size) {
116 std::copy(map, map + std::min(size, array_size_), map_);
117 }
118
SetRange(int begin,int end,bool value)119 void Bitmap::SetRange(int begin, int end, bool value) {
120 DCHECK_LE(begin, end);
121 int start_offset = begin & (kIntBits - 1);
122 if (start_offset) {
123 // Set the bits in the first word.
124 int len = std::min(end - begin, kIntBits - start_offset);
125 SetWordBits(begin, len, value);
126 begin += len;
127 }
128
129 if (begin == end)
130 return;
131
132 // Now set the bits in the last word.
133 int end_offset = end & (kIntBits - 1);
134 end -= end_offset;
135 SetWordBits(end, end_offset, value);
136
137 // Set all the words in the middle.
138 memset(map_ + (begin / kIntBits), (value ? 0xFF : 0x00),
139 ((end / kIntBits) - (begin / kIntBits)) * sizeof(*map_));
140 }
141
142 // Return true if any bit between begin inclusive and end exclusive
143 // is set. 0 <= begin <= end <= bits() is required.
TestRange(int begin,int end,bool value) const144 bool Bitmap::TestRange(int begin, int end, bool value) const {
145 DCHECK_LT(begin, num_bits_);
146 DCHECK_LE(end, num_bits_);
147 DCHECK_LE(begin, end);
148 DCHECK_GE(begin, 0);
149 DCHECK_GE(end, 0);
150
151 // Return false immediately if the range is empty.
152 if (begin >= end || end <= 0)
153 return false;
154
155 // Calculate the indices of the words containing the first and last bits,
156 // along with the positions of the bits within those words.
157 int word = begin / kIntBits;
158 int offset = begin & (kIntBits - 1);
159 int last_word = (end - 1) / kIntBits;
160 int last_offset = (end - 1) & (kIntBits - 1);
161
162 // If we are looking for zeros, negate the data from the map.
163 uint32_t this_word = map_[word];
164 if (!value)
165 this_word = ~this_word;
166
167 // If the range spans multiple words, discard the extraneous bits of the
168 // first word by shifting to the right, and then test the remaining bits.
169 if (word < last_word) {
170 if (this_word >> offset)
171 return true;
172 offset = 0;
173
174 word++;
175 // Test each of the "middle" words that lies completely within the range.
176 while (word < last_word) {
177 this_word = map_[word++];
178 if (!value)
179 this_word = ~this_word;
180 if (this_word)
181 return true;
182 }
183 }
184
185 // Test the portion of the last word that lies within the range. (This logic
186 // also handles the case where the entire range lies within a single word.)
187 const uint32_t mask = ((2 << (last_offset - offset)) - 1) << offset;
188
189 this_word = map_[last_word];
190 if (!value)
191 this_word = ~this_word;
192
193 return (this_word & mask) != 0;
194 }
195
FindNextBit(int * index,int limit,bool value) const196 bool Bitmap::FindNextBit(int* index, int limit, bool value) const {
197 DCHECK_LT(*index, num_bits_);
198 DCHECK_LE(limit, num_bits_);
199 DCHECK_LE(*index, limit);
200 DCHECK_GE(*index, 0);
201 DCHECK_GE(limit, 0);
202
203 const int bit_index = *index;
204 if (bit_index >= limit || limit <= 0)
205 return false;
206
207 // From now on limit != 0, since if it was we would have returned false.
208 int word_index = bit_index >> kLogIntBits;
209 uint32_t one_word = map_[word_index];
210
211 // Simple optimization where we can immediately return true if the first
212 // bit is set. This helps for cases where many bits are set, and doesn't
213 // hurt too much if not.
214 if (Get(bit_index) == value)
215 return true;
216
217 const int first_bit_offset = bit_index & (kIntBits - 1);
218
219 // First word is special - we need to mask off leading bits.
220 uint32_t mask = 0xFFFFFFFF << first_bit_offset;
221 if (value) {
222 one_word &= mask;
223 } else {
224 one_word |= ~mask;
225 }
226
227 uint32_t empty_value = value ? 0 : 0xFFFFFFFF;
228
229 // Loop through all but the last word. Note that 'limit' is one
230 // past the last bit we want to check, and we don't want to read
231 // past the end of "words". E.g. if num_bits_ == 32 only words[0] is
232 // valid, so we want to avoid reading words[1] when limit == 32.
233 const int last_word_index = (limit - 1) >> kLogIntBits;
234 while (word_index < last_word_index) {
235 if (one_word != empty_value) {
236 *index = (word_index << kLogIntBits) + FindLSBNonEmpty(one_word, value);
237 return true;
238 }
239 one_word = map_[++word_index];
240 }
241
242 // Last word is special - we may need to mask off trailing bits. Note that
243 // 'limit' is one past the last bit we want to check, and if limit is a
244 // multiple of 32 we want to check all bits in this word.
245 const int last_bit_offset = (limit - 1) & (kIntBits - 1);
246 mask = 0xFFFFFFFE << last_bit_offset;
247 if (value) {
248 one_word &= ~mask;
249 } else {
250 one_word |= mask;
251 }
252 if (one_word != empty_value) {
253 *index = (word_index << kLogIntBits) + FindLSBNonEmpty(one_word, value);
254 return true;
255 }
256 return false;
257 }
258
FindBits(int * index,int limit,bool value) const259 int Bitmap::FindBits(int* index, int limit, bool value) const {
260 DCHECK_LT(*index, num_bits_);
261 DCHECK_LE(limit, num_bits_);
262 DCHECK_LE(*index, limit);
263 DCHECK_GE(*index, 0);
264 DCHECK_GE(limit, 0);
265
266 if (!FindNextBit(index, limit, value))
267 return false;
268
269 // Now see how many bits have the same value.
270 int end = *index;
271 if (!FindNextBit(&end, limit, !value))
272 return limit - *index;
273
274 return end - *index;
275 }
276
SetWordBits(int start,int len,bool value)277 void Bitmap::SetWordBits(int start, int len, bool value) {
278 DCHECK_LT(len, kIntBits);
279 DCHECK_GE(len, 0);
280 if (!len)
281 return;
282
283 int word = start / kIntBits;
284 int offset = start % kIntBits;
285
286 uint32_t to_add = 0xffffffff << len;
287 to_add = (~to_add) << offset;
288 if (value) {
289 map_[word] |= to_add;
290 } else {
291 map_[word] &= ~to_add;
292 }
293 }
294
295 } // namespace disk_cache
296