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