summaryrefslogtreecommitdiff
path: root/chromium/net/disk_cache/bitmap.cc
blob: 6d469dfe3dc2d43e003047433fde8a5ec27fafc4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/disk_cache/bitmap.h"

#include <algorithm>

#include "base/logging.h"

namespace {

// Returns the number of trailing zeros.
int FindLSBSetNonZero(uint32 word) {
  // Get the LSB, put it on the exponent of a 32 bit float and remove the
  // mantisa and the bias. This code requires IEEE 32 bit float compliance.
  float f = static_cast<float>(word & -static_cast<int>(word));

  // We use a union to go around strict-aliasing complains.
  union {
    float ieee_float;
    uint32 as_uint;
  } x;

  x.ieee_float = f;
  return (x.as_uint >> 23) - 0x7f;
}

// Returns the index of the first bit set to |value| from |word|. This code
// assumes that we'll be able to find that bit.
int FindLSBNonEmpty(uint32 word, bool value) {
  // If we are looking for 0, negate |word| and look for 1.
  if (!value)
    word = ~word;

  return FindLSBSetNonZero(word);
}

}  // namespace

namespace disk_cache {

Bitmap::Bitmap(int num_bits, bool clear_bits)
    : num_bits_(num_bits),
      array_size_(RequiredArraySize(num_bits)),
      alloc_(true) {
  map_ = new uint32[array_size_];

  // Initialize all of the bits.
  if (clear_bits)
    Clear();
}

Bitmap::Bitmap(uint32* map, int num_bits, int num_words)
    : map_(map),
      num_bits_(num_bits),
      // If size is larger than necessary, trim because array_size_ is used
      // as a bound by various methods.
      array_size_(std::min(RequiredArraySize(num_bits), num_words)),
      alloc_(false) {
}

Bitmap::~Bitmap() {
  if (alloc_)
    delete [] map_;
}

void Bitmap::Resize(int num_bits, bool clear_bits) {
  DCHECK(alloc_ || !map_);
  const int old_maxsize = num_bits_;
  const int old_array_size = array_size_;
  array_size_ = RequiredArraySize(num_bits);

  if (array_size_ != old_array_size) {
    uint32* new_map = new uint32[array_size_];
    // Always clear the unused bits in the last word.
    new_map[array_size_ - 1] = 0;
    memcpy(new_map, map_,
           sizeof(*map_) * std::min(array_size_, old_array_size));
    if (alloc_)
      delete[] map_;  // No need to check for NULL.
    map_ = new_map;
    alloc_ = true;
  }

  num_bits_ = num_bits;
  if (old_maxsize < num_bits_ && clear_bits) {
    SetRange(old_maxsize, num_bits_, false);
  }
}

void Bitmap::Set(int index, bool value) {
  DCHECK_LT(index, num_bits_);
  DCHECK_GE(index, 0);
  const int i = index & (kIntBits - 1);
  const int j = index / kIntBits;
  if (value)
    map_[j] |= (1 << i);
  else
    map_[j] &= ~(1 << i);
}

bool Bitmap::Get(int index) const {
  DCHECK_LT(index, num_bits_);
  DCHECK_GE(index, 0);
  const int i = index & (kIntBits-1);
  const int j = index / kIntBits;
  return ((map_[j] & (1 << i)) != 0);
}

void Bitmap::Toggle(int index) {
  DCHECK_LT(index, num_bits_);
  DCHECK_GE(index, 0);
  const int i = index & (kIntBits - 1);
  const int j = index / kIntBits;
  map_[j] ^= (1 << i);
}

void Bitmap::SetMapElement(int array_index, uint32 value) {
  DCHECK_LT(array_index, array_size_);
  DCHECK_GE(array_index, 0);
  map_[array_index] = value;
}

uint32 Bitmap::GetMapElement(int array_index) const {
  DCHECK_LT(array_index, array_size_);
  DCHECK_GE(array_index, 0);
  return map_[array_index];
}

void Bitmap::SetMap(const uint32* map, int size) {
  memcpy(map_, map, std::min(size, array_size_) * sizeof(*map_));
}

void Bitmap::SetRange(int begin, int end, bool value) {
  DCHECK_LE(begin, end);
  int start_offset = begin & (kIntBits - 1);
  if (start_offset) {
    // Set the bits in the first word.
    int len = std::min(end - begin, kIntBits - start_offset);
    SetWordBits(begin, len, value);
    begin += len;
  }

  if (begin == end)
    return;

  // Now set the bits in the last word.
  int end_offset = end & (kIntBits - 1);
  end -= end_offset;
  SetWordBits(end, end_offset, value);

  // Set all the words in the middle.
  memset(map_ + (begin / kIntBits), (value ? 0xFF : 0x00),
         ((end / kIntBits) - (begin / kIntBits)) * sizeof(*map_));
}

// Return true if any bit between begin inclusive and end exclusive
// is set.  0 <= begin <= end <= bits() is required.
bool Bitmap::TestRange(int begin, int end, bool value) const {
  DCHECK_LT(begin, num_bits_);
  DCHECK_LE(end, num_bits_);
  DCHECK_LE(begin, end);
  DCHECK_GE(begin, 0);
  DCHECK_GE(end, 0);

  // Return false immediately if the range is empty.
  if (begin >= end || end <= 0)
    return false;

  // Calculate the indices of the words containing the first and last bits,
  // along with the positions of the bits within those words.
  int word = begin / kIntBits;
  int offset = begin & (kIntBits - 1);
  int last_word = (end - 1) / kIntBits;
  int last_offset = (end - 1) & (kIntBits - 1);

  // If we are looking for zeros, negate the data from the map.
  uint32 this_word = map_[word];
  if (!value)
    this_word = ~this_word;

  // If the range spans multiple words, discard the extraneous bits of the
  // first word by shifting to the right, and then test the remaining bits.
  if (word < last_word) {
    if (this_word >> offset)
      return true;
    offset = 0;

    word++;
    // Test each of the "middle" words that lies completely within the range.
    while (word < last_word) {
      this_word = map_[word++];
      if (!value)
        this_word = ~this_word;
      if (this_word)
        return true;
    }
  }

  // Test the portion of the last word that lies within the range. (This logic
  // also handles the case where the entire range lies within a single word.)
  const uint32 mask = ((2 << (last_offset - offset)) - 1) << offset;

  this_word = map_[last_word];
  if (!value)
    this_word = ~this_word;

  return (this_word & mask) != 0;
}

bool Bitmap::FindNextBit(int* index, int limit, bool value) const {
  DCHECK_LT(*index, num_bits_);
  DCHECK_LE(limit, num_bits_);
  DCHECK_LE(*index, limit);
  DCHECK_GE(*index, 0);
  DCHECK_GE(limit, 0);

  const int bit_index = *index;
  if (bit_index >= limit || limit <= 0)
    return false;

  // From now on limit != 0, since if it was we would have returned false.
  int word_index = bit_index >> kLogIntBits;
  uint32 one_word = map_[word_index];

  // Simple optimization where we can immediately return true if the first
  // bit is set.  This helps for cases where many bits are set, and doesn't
  // hurt too much if not.
  if (Get(bit_index) == value)
    return true;

  const int first_bit_offset = bit_index & (kIntBits - 1);

  // First word is special - we need to mask off leading bits.
  uint32 mask = 0xFFFFFFFF << first_bit_offset;
  if (value) {
    one_word &= mask;
  } else {
    one_word |= ~mask;
  }

  uint32 empty_value = value ? 0 : 0xFFFFFFFF;

  // Loop through all but the last word.  Note that 'limit' is one
  // past the last bit we want to check, and we don't want to read
  // past the end of "words".  E.g. if num_bits_ == 32 only words[0] is
  // valid, so we want to avoid reading words[1] when limit == 32.
  const int last_word_index = (limit - 1) >> kLogIntBits;
  while (word_index < last_word_index) {
    if (one_word != empty_value) {
      *index = (word_index << kLogIntBits) + FindLSBNonEmpty(one_word, value);
      return true;
    }
    one_word = map_[++word_index];
  }

  // Last word is special - we may need to mask off trailing bits.  Note that
  // 'limit' is one past the last bit we want to check, and if limit is a
  // multiple of 32 we want to check all bits in this word.
  const int last_bit_offset = (limit - 1) & (kIntBits - 1);
  mask = 0xFFFFFFFE << last_bit_offset;
  if (value) {
    one_word &= ~mask;
  } else {
    one_word |= mask;
  }
  if (one_word != empty_value) {
    *index = (word_index << kLogIntBits) + FindLSBNonEmpty(one_word, value);
    return true;
  }
  return false;
}

int Bitmap::FindBits(int* index, int limit, bool value) const {
  DCHECK_LT(*index, num_bits_);
  DCHECK_LE(limit, num_bits_);
  DCHECK_LE(*index, limit);
  DCHECK_GE(*index, 0);
  DCHECK_GE(limit, 0);

  if (!FindNextBit(index, limit, value))
    return false;

  // Now see how many bits have the same value.
  int end = *index;
  if (!FindNextBit(&end, limit, !value))
    return limit - *index;

  return end - *index;
}

void Bitmap::SetWordBits(int start, int len, bool value) {
  DCHECK_LT(len, kIntBits);
  DCHECK_GE(len, 0);
  if (!len)
    return;

  int word = start / kIntBits;
  int offset = start % kIntBits;

  uint32 to_add = 0xffffffff << len;
  to_add = (~to_add) << offset;
  if (value) {
    map_[word] |= to_add;
  } else {
    map_[word] &= ~to_add;
  }
}

}  // namespace disk_cache