• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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