1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //     * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //     * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //     * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 #include "storage/louds/simple_succinct_bit_vector_index.h"
31 
32 #include <algorithm>
33 #include <iterator>
34 #include <vector>
35 
36 #include "base/iterator_adapter.h"
37 #include "base/logging.h"
38 #include "base/port.h"
39 
40 namespace mozc {
41 namespace storage {
42 namespace louds {
43 namespace {
44 
45 // Simple adapter to convert from 1-bit index to 0-bit index.
46 class ZeroBitAdapter : public AdapterBase<int> {
47  public:
48   // Needs to be default constructive to create invalid iterator.
ZeroBitAdapter()49   ZeroBitAdapter() : index_(nullptr), chunk_size_(0) {}
50 
ZeroBitAdapter(const std::vector<int> * index,int chunk_size)51   ZeroBitAdapter(const std::vector<int>* index, int chunk_size)
52       : index_(index), chunk_size_(chunk_size) {}
53 
operator ()(const int * ptr) const54   value_type operator()(const int *ptr) const {
55     // The number of 0-bits
56     //   = (total num bits) - (1-bits)
57     //   = (chunk_size [bytes] * 8 [bits/byte] * (ptr's offset) - (1-bits)
58     return chunk_size_ * 8 * (ptr - index_->data()) - *ptr;
59   }
60 
61  private:
62   const std::vector<int> *index_;
63   int chunk_size_;
64 };
65 
66 #ifdef __GNUC__
67 // TODO(hidehiko): Support XMM and 64-bits popcount for 64bits architectures.
BitCount1(uint32 x)68 inline int BitCount1(uint32 x) {
69   return __builtin_popcount(x);
70 }
71 #else
BitCount1(uint32 x)72 int BitCount1(uint32 x) {
73   x = ((x & 0xaaaaaaaa) >> 1) + (x & 0x55555555);
74   x = ((x & 0xcccccccc) >> 2) + (x & 0x33333333);
75   x = ((x >> 4) + x) & 0x0f0f0f0f;
76   x = (x >> 8) + x;
77   x = ((x >> 16) + x) & 0x3f;
78   return x;
79 }
80 #endif
81 
BitCount0(uint32 x)82 inline int BitCount0(uint32 x) {
83   // Flip all bits, and count 1-bits.
84   return BitCount1(~x);
85 }
86 
IsPowerOfTwo(int value)87 inline bool IsPowerOfTwo(int value) {
88   // value & -value is the well-known idiom to take the lowest 1-bit in
89   // value, so value & ~(value & -value) clears the lowest 1-bit in value.
90   // If the result is 0, value has only one 1-bit i.e. it is power of two.
91   return value != 0 && (value & ~(value & -value)) == 0;
92 }
93 
94 // Returns 1-bits in the data[0] ... data[length - 1].
Count1Bits(const uint32 * data,int length)95 int Count1Bits(const uint32 *data, int length) {
96   int num_bits = 0;
97   for (; length > 0; ++data, --length) {
98     num_bits += BitCount1(*data);
99   }
100   return num_bits;
101 }
102 
103 // Stores index (the camulative number of the 1-bits from begin of each chunk).
InitIndex(const uint8 * data,int length,int chunk_size,std::vector<int> * index)104 void InitIndex(
105     const uint8 *data, int length, int chunk_size, std::vector<int> *index) {
106   DCHECK_GE(chunk_size, 4);
107   DCHECK(IsPowerOfTwo(chunk_size)) << chunk_size;
108   DCHECK_EQ(length % 4, 0);
109 
110   index->clear();
111 
112   // Count the number of chunks with ceiling.
113   const int chunk_length = (length + chunk_size - 1) / chunk_size;
114 
115   // Reserve the memory including a sentinel.
116   index->reserve(chunk_length + 1);
117 
118   int num_bits = 0;
119   for (int remaining_num_words = length / 4; remaining_num_words > 0;
120        data += chunk_size, remaining_num_words -= chunk_size / 4) {
121     index->push_back(num_bits);
122     num_bits += Count1Bits(reinterpret_cast<const uint32 *>(data),
123                            std::min(chunk_size / 4, remaining_num_words));
124   }
125   index->push_back(num_bits);
126 
127   CHECK_EQ(chunk_length + 1, index->size());
128 }
129 
InitLowerBound0Cache(const std::vector<int> & index,int chunk_size,size_t increment,size_t size,std::vector<const int * > * cache)130 void InitLowerBound0Cache(const std::vector<int> &index, int chunk_size,
131                           size_t increment, size_t size,
132                           std::vector<const int *> *cache) {
133   DCHECK_GT(increment, 0);
134   cache->clear();
135   cache->reserve(size + 2);
136   cache->push_back(index.data());
137   ZeroBitAdapter adapter(&index, chunk_size);
138   for (size_t i = 1; i <= size; ++i) {
139     const int target_index = increment * i;
140     const int *ptr = std::lower_bound(
141         MakeIteratorAdapter(index.data(), adapter),
142         MakeIteratorAdapter(index.data() + index.size(), adapter),
143         target_index).base();
144     cache->push_back(ptr);
145   }
146   cache->push_back(index.data() + index.size());
147 }
148 
InitLowerBound1Cache(const std::vector<int> & index,int chunk_size,size_t increment,size_t size,std::vector<const int * > * cache)149 void InitLowerBound1Cache(const std::vector<int> &index, int chunk_size,
150                           size_t increment, size_t size,
151                           std::vector<const int *> *cache) {
152   DCHECK_GT(increment, 0);
153   cache->clear();
154   cache->reserve(size + 2);
155   cache->push_back(index.data());
156   for (size_t i = 1; i <= size; ++i) {
157     const int target_index = increment * i;
158     const int *ptr = std::lower_bound(index.data(), index.data() + index.size(),
159                                       target_index);
160     cache->push_back(ptr);
161   }
162   cache->push_back(index.data() + index.size());
163 }
164 
165 }  // namespace
166 
Init(const uint8 * data,int length,size_t lb0_cache_size,size_t lb1_cache_size)167 void SimpleSuccinctBitVectorIndex::Init(const uint8 *data, int length,
168                                         size_t lb0_cache_size,
169                                         size_t lb1_cache_size) {
170   data_ = data;
171   length_ = length;
172   InitIndex(data, length, chunk_size_, &index_);
173 
174   // TODO(noriyukit): Currently, we simply use uniform increment width for lower
175   // bound cache.  Nonuniform increment width may improve performance.
176   lb0_cache_increment_ =
177       lb0_cache_size == 0 ? GetNum0Bits() : GetNum0Bits() / lb0_cache_size;
178   if (lb0_cache_increment_ == 0) {
179     lb0_cache_increment_ = 1;
180   }
181   InitLowerBound0Cache(index_, chunk_size_, lb0_cache_increment_,
182                        lb0_cache_size, &lb0_cache_);
183 
184   lb1_cache_increment_ =
185       lb1_cache_size == 0 ? GetNum1Bits() : GetNum1Bits() / lb1_cache_size;
186   if (lb1_cache_increment_ == 0) {
187     lb1_cache_increment_ = 1;
188   }
189   InitLowerBound1Cache(index_, chunk_size_, lb1_cache_increment_,
190                        lb1_cache_size, &lb1_cache_);
191 }
192 
Reset()193 void SimpleSuccinctBitVectorIndex::Reset() {
194   data_ = nullptr;
195   length_ = 0;
196   index_.clear();
197   lb0_cache_increment_ = 1;
198   lb0_cache_.clear();
199   lb1_cache_increment_ = 1;
200   lb1_cache_.clear();
201 }
202 
Rank1(int n) const203 int SimpleSuccinctBitVectorIndex::Rank1(int n) const {
204   // Look up pre-computed 1-bits for the preceding chunks.
205   const int num_chunks = n / (chunk_size_ * 8);
206   int result = index_[n / (chunk_size_ * 8)];
207 
208   // Count 1-bits for remaining "words".
209   result += Count1Bits(
210       reinterpret_cast<const uint32 *>(data_ + num_chunks * chunk_size_),
211       (n / 8 - num_chunks * chunk_size_) / 4);
212 
213   // Count 1-bits for remaining "bits".
214   if (n % 32 > 0) {
215     const int index = n / 32;
216     const int shift = 32 - n % 32;
217     result +=
218         BitCount1(reinterpret_cast<const uint32 *>(data_)[index] << shift);
219   }
220 
221   return result;
222 }
223 
Select0(int n) const224 int SimpleSuccinctBitVectorIndex::Select0(int n) const {
225   DCHECK_GT(n, 0);
226 
227   // Narrow down the range of |index_| on which lower bound is performed.
228   int lb0_cache_index = n / lb0_cache_increment_;
229   if (lb0_cache_index > lb0_cache_.size() - 2) {
230     lb0_cache_index = lb0_cache_.size() - 2;
231   }
232   DCHECK_GE(lb0_cache_index, 0);
233 
234   // Binary search on chunks.
235   ZeroBitAdapter adapter(&index_, chunk_size_);
236   const int *chunk_ptr =
237       std::lower_bound(
238           MakeIteratorAdapter(lb0_cache_[lb0_cache_index], adapter),
239           MakeIteratorAdapter(lb0_cache_[lb0_cache_index + 1], adapter), n)
240           .base();
241   const int chunk_index = (chunk_ptr - index_.data()) - 1;
242   DCHECK_GE(chunk_index, 0);
243   n -= chunk_size_ * 8 * chunk_index - index_[chunk_index];
244 
245   // Linear search on remaining "words"
246   const uint32 *ptr =
247       reinterpret_cast<const uint32 *>(data_) + chunk_index * chunk_size_ / 4;
248   while (true) {
249     const int bit_count = BitCount0(*ptr);
250     if (bit_count >= n) {
251       break;
252     }
253     n -= bit_count;
254     ++ptr;
255   }
256 
257   int index = (ptr - reinterpret_cast<const uint32 *>(data_)) * 32;
258   for (uint32 word = ~(*ptr); n > 0; word >>= 1, ++index) {
259     n -= (word & 1);
260   }
261 
262   // Index points to the "next bit" of the target one.
263   // Thus, subtract one to adjust.
264   return index - 1;
265 }
266 
Select1(int n) const267 int SimpleSuccinctBitVectorIndex::Select1(int n) const {
268   DCHECK_GT(n, 0);
269 
270   // Narrow down the range of |index_| on which lower bound is performed.
271   int lb1_cache_index = n / lb1_cache_increment_;
272   if (lb1_cache_index > lb1_cache_.size() - 2) {
273     lb1_cache_index = lb1_cache_.size() - 2;
274   }
275   DCHECK_GE(lb1_cache_index, 0);
276 
277   // Binary search on chunks.
278   const int *chunk_ptr = std::lower_bound(lb1_cache_[lb1_cache_index],
279                                           lb1_cache_[lb1_cache_index + 1], n);
280   const int chunk_index = (chunk_ptr - index_.data()) - 1;
281   DCHECK_GE(chunk_index, 0);
282   n -= index_[chunk_index];
283 
284   // Linear search on remaining "words"
285   const uint32 *ptr =
286       reinterpret_cast<const uint32 *>(data_) + chunk_index * chunk_size_ / 4;
287   while (true) {
288     const int bit_count = BitCount1(*ptr);
289     if (bit_count >= n) {
290       break;
291     }
292     n -= bit_count;
293     ++ptr;
294   }
295 
296   int index = (ptr - reinterpret_cast<const uint32 *>(data_)) * 32;
297   for (uint32 word = *ptr; n > 0; word >>= 1, ++index) {
298     n -= (word & 1);
299   }
300 
301   // Index points to the "next bit" of the target one.
302   // Thus, subtract one to adjust.
303   return index - 1;
304 }
305 
306 }  // namespace louds
307 }  // namespace storage
308 }  // namespace mozc
309