/Users/deen/code/yugabyte-db/src/yb/util/bitmap.cc
| Line | Count | Source (jump to first uncovered line) | 
| 1 |  | // Licensed to the Apache Software Foundation (ASF) under one | 
| 2 |  | // or more contributor license agreements.  See the NOTICE file | 
| 3 |  | // distributed with this work for additional information | 
| 4 |  | // regarding copyright ownership.  The ASF licenses this file | 
| 5 |  | // to you under the Apache License, Version 2.0 (the | 
| 6 |  | // "License"); you may not use this file except in compliance | 
| 7 |  | // with the License.  You may obtain a copy of the License at | 
| 8 |  | // | 
| 9 |  | //   http://www.apache.org/licenses/LICENSE-2.0 | 
| 10 |  | // | 
| 11 |  | // Unless required by applicable law or agreed to in writing, | 
| 12 |  | // software distributed under the License is distributed on an | 
| 13 |  | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | 
| 14 |  | // KIND, either express or implied.  See the License for the | 
| 15 |  | // specific language governing permissions and limitations | 
| 16 |  | // under the License. | 
| 17 |  | // | 
| 18 |  | // The following only applies to changes made to this file as part of YugaByte development. | 
| 19 |  | // | 
| 20 |  | // Portions Copyright (c) YugaByte, Inc. | 
| 21 |  | // | 
| 22 |  | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except | 
| 23 |  | // in compliance with the License.  You may obtain a copy of the License at | 
| 24 |  | // | 
| 25 |  | // http://www.apache.org/licenses/LICENSE-2.0 | 
| 26 |  | // | 
| 27 |  | // Unless required by applicable law or agreed to in writing, software distributed under the License | 
| 28 |  | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express | 
| 29 |  | // or implied.  See the License for the specific language governing permissions and limitations | 
| 30 |  | // under the License. | 
| 31 |  | // | 
| 32 |  | #include "yb/util/bitmap.h" | 
| 33 |  |  | 
| 34 |  | #include <string> | 
| 35 |  |  | 
| 36 |  | #include <glog/logging.h> | 
| 37 |  |  | 
| 38 |  | #include "yb/gutil/stringprintf.h" | 
| 39 |  | #include "yb/util/coding.h" | 
| 40 |  | #include "yb/util/result.h" | 
| 41 |  | #include "yb/util/status_format.h" | 
| 42 |  |  | 
| 43 |  | namespace yb { | 
| 44 |  |  | 
| 45 | 66.0k | void BitmapChangeBits(uint8_t *bitmap, size_t offset, size_t num_bits, bool value) { | 
| 46 | 66.0k |   DCHECK_GT(num_bits, 0); | 
| 47 |  |  | 
| 48 | 66.0k |   size_t start_byte = (offset >> 3); | 
| 49 | 66.0k |   size_t end_byte = (offset + num_bits - 1) >> 3; | 
| 50 | 66.0k |   int single_byte = (start_byte == end_byte); | 
| 51 |  |  | 
| 52 |  |   // Change the last bits of the first byte | 
| 53 | 66.0k |   size_t left = offset & 0x7; | 
| 54 | 63.7k |   size_t right = (single_byte) ? (left + num_bits) : 8; | 
| 55 | 66.0k |   uint8_t mask = ((0xff << left) & (0xff >> (8 - right))); | 
| 56 | 66.0k |   if (value) { | 
| 57 | 33.0k |     bitmap[start_byte++] |= mask; | 
| 58 | 33.0k |   } else { | 
| 59 | 33.0k |     bitmap[start_byte++] &= ~mask; | 
| 60 | 33.0k |   } | 
| 61 |  |  | 
| 62 |  |   // Nothing left... I'm done | 
| 63 | 66.0k |   if (single_byte) { | 
| 64 | 2.30k |     return; | 
| 65 | 2.30k |   } | 
| 66 |  |  | 
| 67 |  |   // change the middle bits | 
| 68 | 63.7k |   if (end_byte > start_byte) { | 
| 69 | 59.9k |     const uint8_t pattern8[2] = { 0x00, 0xff }; | 
| 70 | 59.9k |     memset(bitmap + start_byte, pattern8[value], end_byte - start_byte); | 
| 71 | 59.9k |   } | 
| 72 |  |  | 
| 73 |  |   // change the first bits of the last byte | 
| 74 | 63.7k |   right = offset + num_bits - (end_byte << 3); | 
| 75 | 63.7k |   mask = (0xff >> (8 - right)); | 
| 76 | 63.7k |   if (value) { | 
| 77 | 31.8k |     bitmap[end_byte] |= mask; | 
| 78 | 31.8k |   } else { | 
| 79 | 31.8k |     bitmap[end_byte] &= ~mask; | 
| 80 | 31.8k |   } | 
| 81 | 63.7k | } | 
| 82 |  |  | 
| 83 |  | bool BitmapFindFirst(const uint8_t *bitmap, size_t offset, size_t bitmap_size, | 
| 84 | 195k |                      bool value, size_t *idx) { | 
| 85 | 195k |   const uint64_t pattern64[2] = { 0xffffffffffffffff, 0x0000000000000000 }; | 
| 86 | 195k |   const uint8_t pattern8[2] = { 0xff, 0x00 }; | 
| 87 | 195k |   size_t bit; | 
| 88 |  |  | 
| 89 | 195k |   DCHECK_LE(offset, bitmap_size); | 
| 90 |  |  | 
| 91 |  |   // Jump to the byte at specified offset | 
| 92 | 195k |   const uint8_t *p = bitmap + (offset >> 3); | 
| 93 | 195k |   size_t num_bits = bitmap_size - offset; | 
| 94 |  |  | 
| 95 |  |   // Find a 'value' bit at the end of the first byte | 
| 96 | 195k |   if ((bit = offset & 0x7)) { | 
| 97 | 325k |     for (; bit < 8 && num_bits > 0; ++bit) { | 
| 98 | 272k |       if (BitmapTest(p, bit) == value) { | 
| 99 | 61.2k |         *idx = ((p - bitmap) << 3) + bit; | 
| 100 | 61.2k |         return true; | 
| 101 | 61.2k |       } | 
| 102 |  |  | 
| 103 | 211k |       num_bits--; | 
| 104 | 211k |     } | 
| 105 |  |  | 
| 106 | 53.2k |     p++; | 
| 107 | 53.2k |   } | 
| 108 |  |  | 
| 109 |  |   // check 64bit at the time for a 'value' bit | 
| 110 | 134k |   const uint64_t *u64 = (const uint64_t *)p; | 
| 111 | 160k |   while (num_bits >= 64 && *u64 == pattern64[value]) { | 
| 112 | 25.9k |     num_bits -= 64; | 
| 113 | 25.9k |     u64++; | 
| 114 | 25.9k |   } | 
| 115 |  |  | 
| 116 |  |   // check 8bit at the time for a 'value' bit | 
| 117 | 134k |   p = (const uint8_t *)u64; | 
| 118 | 395k |   while (num_bits >= 8 && *p == pattern8[value]) { | 
| 119 | 261k |     num_bits -= 8; | 
| 120 | 261k |     p++; | 
| 121 | 261k |   } | 
| 122 |  |  | 
| 123 |  |   // Find a 'value' bit at the beginning of the last byte | 
| 124 | 383k |   for (bit = 0; num_bits > 0; ++bit) { | 
| 125 | 293k |     if (BitmapTest(p, bit) == value) { | 
| 126 | 44.4k |       *idx = ((p - bitmap) << 3) + bit; | 
| 127 | 44.4k |       return true; | 
| 128 | 44.4k |     } | 
| 129 | 248k |     num_bits--; | 
| 130 | 248k |   } | 
| 131 |  |  | 
| 132 | 89.8k |   return false; | 
| 133 | 134k | } | 
| 134 |  |  | 
| 135 | 1 | std::string BitmapToString(const uint8_t *bitmap, size_t num_bits) { | 
| 136 | 1 |   std::string s; | 
| 137 | 1 |   size_t index = 0; | 
| 138 | 2 |   while (index < num_bits) { | 
| 139 | 1 |     StringAppendF(&s, "%4zu: ", index); | 
| 140 | 9 |     for (int i = 0; i < 8 && index < num_bits; ++i) { | 
| 141 | 72 |       for (int j = 0; j < 8 && index < num_bits; ++j) { | 
| 142 | 64 |         StringAppendF(&s, "%d", BitmapTest(bitmap, index)); | 
| 143 | 64 |         index++; | 
| 144 | 64 |       } | 
| 145 | 8 |       StringAppendF(&s, " "); | 
| 146 | 8 |     } | 
| 147 | 1 |     StringAppendF(&s, "\n"); | 
| 148 | 1 |   } | 
| 149 | 1 |   return s; | 
| 150 | 1 | } | 
| 151 |  |  | 
| 152 | 1.26M | void OneWayBitmap::Set(size_t bit) { | 
| 153 | 1.26M |   if (bit < fully_filled_bits_) { | 
| 154 | 157 |     return; | 
| 155 | 157 |   } | 
| 156 | 1.26M |   bit -= fully_filled_bits_; | 
| 157 | 1.26M |   data_.resize(std::max(data_.size(), bit / kBitsPerElement + 1)); | 
| 158 | 1.26M |   auto& element = data_[bit / kBitsPerElement]; | 
| 159 | 1.26M |   auto mask = 1ULL << (bit % kBitsPerElement); | 
| 160 | 1.26M |   if ((element & mask) != 0) { | 
| 161 | 1.84k |     return; | 
| 162 | 1.84k |   } | 
| 163 |  |  | 
| 164 | 1.26M |   element |= mask; | 
| 165 | 1.26M |   ++ones_counter_; | 
| 166 |  |  | 
| 167 | 1.26M |   size_t i = 0; | 
| 168 | 1.29M |   while (i < data_.size() && data_[i] == kAllOnes) { | 
| 169 | 31.9k |     ++i; | 
| 170 | 31.9k |   } | 
| 171 |  |  | 
| 172 | 1.26M |   if (i) { | 
| 173 | 31.8k |     fully_filled_bits_ += kBitsPerElement * i; | 
| 174 | 31.8k |     data_.erase(data_.begin(), data_.begin() + i); | 
| 175 | 31.8k |   } | 
| 176 | 1.26M | } | 
| 177 |  |  | 
| 178 | 7.53M | bool OneWayBitmap::Test(size_t bit) const { | 
| 179 | 7.53M |   if (bit < fully_filled_bits_) { | 
| 180 | 2.00M |     return true; | 
| 181 | 2.00M |   } | 
| 182 | 5.52M |   bit -= fully_filled_bits_; | 
| 183 | 5.52M |   size_t idx = bit / kBitsPerElement; | 
| 184 | 5.52M |   if (idx >= data_.size()) { | 
| 185 | 2.40M |     return false; | 
| 186 | 2.40M |   } | 
| 187 |  |  | 
| 188 | 3.12M |   return (data_[idx] & 1ULL << (bit % kBitsPerElement)) != 0; | 
| 189 | 3.12M | } | 
| 190 |  |  | 
| 191 | 2 | std::string OneWayBitmap::ToString() const { | 
| 192 | 2 |   std::vector<size_t> values; | 
| 193 | 2 |   size_t max = fully_filled_bits_ + data_.size() * 8 * sizeof(size_t); | 
| 194 | 130 |   for (size_t idx = 0; idx != max; ++idx) { | 
| 195 | 128 |     if (Test(idx)) { | 
| 196 | 3 |       values.push_back(idx); | 
| 197 | 3 |     } | 
| 198 | 128 |   } | 
| 199 | 2 |   return AsString(values); | 
| 200 | 2 | } | 
| 201 |  |  | 
| 202 | 1.10M | void OneWayBitmap::EncodeTo(boost::container::small_vector_base<uint8_t>* out) const { | 
| 203 | 1.10M |   uint8_t buf[16]; | 
| 204 | 1.10M |   auto fully_filled_bytes_size = EncodeVarint64(buf, fully_filled_bits_ / 8) - buf; | 
| 205 |  |  | 
| 206 | 1.10M |   size_t len = data_.size(); | 
| 207 | 1.10M |   size_t body_len = len + fully_filled_bytes_size; | 
| 208 | 1.10M |   PutVarint64(out, body_len); | 
| 209 | 1.10M |   size_t new_size = out->size() + body_len; | 
| 210 | 1.10M |   out->resize(new_size); | 
| 211 | 1.10M |   memcpy(out->data() + new_size - body_len, buf, fully_filled_bytes_size); | 
| 212 | 1.10M | #ifdef IS_LITTLE_ENDIAN | 
| 213 | 1.10M |   memcpy(out->data() + out->size() - len, data_.data(), len); | 
| 214 |  | #else | 
| 215 |  |   #error Not implemented | 
| 216 |  | #endif | 
| 217 | 1.10M | } | 
| 218 |  |  | 
| 219 | 2 | std::string OneWayBitmap::EncodeToHexString() const { | 
| 220 | 2 |   boost::container::small_vector<uint8_t, 16> buffer; | 
| 221 | 2 |   EncodeTo(&buffer); | 
| 222 | 2 |   return Slice(buffer.data(), buffer.size()).ToDebugHexString(); | 
| 223 | 2 | } | 
| 224 |  |  | 
| 225 | 6.92k | Result<Slice> DecodeBody(Slice* slice) { | 
| 226 | 6.92k |   uint64_t body_len; | 
| 227 | 6.92k |   if (!GetVarint64(slice, &body_len)) { | 
| 228 | 0 |     return STATUS(Corruption, "Unable to decode data bytes"); | 
| 229 | 0 |   } | 
| 230 | 6.92k |   Slice body(slice->data(), body_len); | 
| 231 | 6.92k |   if (body.end() > slice->end()) { | 
| 232 | 0 |     return STATUS(Corruption, "Not enough body bytes"); | 
| 233 | 0 |   } | 
| 234 | 6.92k |   slice->remove_prefix(body.size()); | 
| 235 | 6.92k |   return body; | 
| 236 | 6.92k | } | 
| 237 |  |  | 
| 238 | 3.46k | Status OneWayBitmap::Skip(Slice* slice) { | 
| 239 | 3.46k |   return ResultToStatus(DecodeBody(slice)); | 
| 240 | 3.46k | } | 
| 241 |  |  | 
| 242 | 3.46k | Result<OneWayBitmap> OneWayBitmap::Decode(Slice* slice) { | 
| 243 | 3.46k |   OneWayBitmap result; | 
| 244 |  |  | 
| 245 | 3.46k |   auto body = VERIFY_RESULT(DecodeBody(slice)); | 
| 246 |  |  | 
| 247 | 3.46k |   uint64_t fully_filled_bytes; | 
| 248 | 3.46k |   if (!GetVarint64(&body, &fully_filled_bytes)) { | 
| 249 | 0 |     return STATUS(Corruption, "Unable to decode fully filled bytes"); | 
| 250 | 0 |   } | 
| 251 | 3.46k |   result.fully_filled_bits_ = fully_filled_bytes * 8; | 
| 252 | 3.46k |   result.ones_counter_ = result.fully_filled_bits_; | 
| 253 | 3.46k | #ifdef IS_LITTLE_ENDIAN | 
| 254 | 3.46k |   result.data_.resize(body.size()); | 
| 255 | 3.46k |   memcpy(result.data_.data(), body.data(), body.size()); | 
| 256 |  | #else | 
| 257 |  |   #error Not implemented | 
| 258 |  | #endif | 
| 259 | 195k |   for (auto v : result.data_) { | 
| 260 | 195k |     result.ones_counter_ += __builtin_popcount(v); | 
| 261 | 195k |   } | 
| 262 |  |   // Data was | 
| 263 | 3.46k |   if (!result.data_.empty() && result.data_.back() == 0) { | 
| 264 | 0 |     return STATUS_FORMAT(Corruption, "Last bitmap byte is zero: $0", body.ToDebugHexString()); | 
| 265 | 0 |   } | 
| 266 |  |  | 
| 267 | 3.46k |   return result; | 
| 268 | 3.46k | } | 
| 269 |  |  | 
| 270 |  | } // namespace yb |