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