Files
lmcache--lmcache/csrc/storage_manager/bitmap.cpp
T
2026-07-13 12:24:33 +08:00

244 lines
6.8 KiB
C++

// SPDX-License-Identifier: Apache-2.0
#include "bitmap.h"
#include <algorithm>
namespace lmcache {
namespace storage_manager {
namespace {
constexpr unsigned kBitsPerByte = 8;
inline size_t byte_index(size_t bit_index) { return bit_index / kBitsPerByte; }
inline unsigned bit_offset(size_t bit_index) {
return static_cast<unsigned>(bit_index % kBitsPerByte);
}
} // namespace
Bitmap::Bitmap(size_t size)
: size_(size), data_(size == 0 ? 0 : (size - 1) / kBitsPerByte + 1, 0) {}
Bitmap::Bitmap(size_t size, size_t prefix_bits) : Bitmap(size) {
if (prefix_bits == 0 || size == 0) return;
if (prefix_bits > size) prefix_bits = size;
// Set full bytes to 0xFF
const size_t full_bytes = prefix_bits / kBitsPerByte;
for (size_t i = 0; i < full_bytes; ++i) {
data_[i] = 0xFF;
}
// Set remaining bits in the partial byte
const unsigned remaining = prefix_bits % kBitsPerByte;
if (remaining > 0) {
data_[full_bytes] = static_cast<uint8_t>((1u << remaining) - 1);
}
}
void Bitmap::set(size_t index) {
if (index >= size_) return;
data_[byte_index(index)] |= static_cast<uint8_t>(1u << bit_offset(index));
}
void Bitmap::batched_set(const std::vector<size_t>& indices) {
for (size_t idx : indices) set(idx);
}
void Bitmap::set_range(size_t start, size_t end) {
if (end > size_) end = size_;
if (start >= end) return;
const size_t first_byte = start / kBitsPerByte;
const size_t last_byte = (end - 1) / kBitsPerByte; // inclusive
const unsigned head_low = bit_offset(start);
if (first_byte == last_byte) {
// Whole range lives in one byte: set bits [head_low, head_low + width).
const unsigned width = static_cast<unsigned>(end - start); // 1..8
const uint8_t mask = static_cast<uint8_t>(((1u << width) - 1u) << head_low);
data_[first_byte] |= mask;
return;
}
// Head partial byte: bits [head_low, 8).
data_[first_byte] |= static_cast<uint8_t>(0xFFu << head_low);
// Middle full bytes.
std::fill(data_.begin() + static_cast<std::ptrdiff_t>(first_byte + 1),
data_.begin() + static_cast<std::ptrdiff_t>(last_byte),
static_cast<uint8_t>(0xFF));
// Tail partial byte: bits [0, tail_width).
const unsigned tail_width =
static_cast<unsigned>(end - last_byte * kBitsPerByte); // 1..8
data_[last_byte] |= static_cast<uint8_t>((1u << tail_width) - 1u);
}
void Bitmap::clear(size_t index) {
if (index >= size_) return;
data_[byte_index(index)] &= static_cast<uint8_t>(~(1u << bit_offset(index)));
}
bool Bitmap::test(size_t index) const {
if (index >= size_) return false;
return (data_[byte_index(index)] >> bit_offset(index)) & 1u;
}
size_t Bitmap::popcount() const {
if (data_.empty()) return 0;
// process full bytes
size_t count = 0;
const size_t num_full_bytes = size_ / kBitsPerByte;
for (size_t i = 0; i < num_full_bytes; ++i) {
count += static_cast<size_t>(
__builtin_popcount(static_cast<unsigned>(data_[i])));
}
// process remaining bits in the last byte
const unsigned remaining_bits = size_ % kBitsPerByte;
if (remaining_bits > 0) {
uint8_t last_byte = data_.back();
uint8_t mask = static_cast<uint8_t>((1u << remaining_bits) - 1);
last_byte &= mask;
count += static_cast<size_t>(
__builtin_popcount(static_cast<unsigned>(last_byte)));
}
return count;
}
size_t Bitmap::clz() const {
if (size_ == 0) return 0;
const size_t num_full_bytes = size_ / kBitsPerByte;
size_t count = 0;
for (size_t i = 0; i < num_full_bytes; ++i) {
uint8_t b = data_[i];
if (b == 0) {
count += kBitsPerByte;
} else {
count += static_cast<size_t>(__builtin_ctz(static_cast<unsigned>(b)));
return count;
}
}
const unsigned remaining_bits = size_ % kBitsPerByte;
if (remaining_bits > 0) {
uint8_t last_byte = data_.back();
uint8_t mask = static_cast<uint8_t>((1u << remaining_bits) - 1);
last_byte &= mask;
if (last_byte == 0) {
count += remaining_bits;
} else {
count +=
static_cast<size_t>(__builtin_ctz(static_cast<unsigned>(last_byte)));
}
}
return count;
}
size_t Bitmap::clo() const {
const Bitmap inverted{~(*this)};
return inverted.clz();
}
int64_t Bitmap::highest_set_bit() const {
// Scan bytes from the most significant down; the first non-zero byte holds
// the highest set bit. Bits beyond size_ are never set, so the result is
// always < size_.
for (size_t byte = data_.size(); byte-- > 0;) {
const uint8_t value = data_[byte];
if (value != 0) {
const unsigned highest =
31u -
static_cast<unsigned>(__builtin_clz(static_cast<unsigned>(value)));
return static_cast<int64_t>(byte * kBitsPerByte + highest);
}
}
return -1;
}
Bitmap Bitmap::operator&(const Bitmap& other) const {
const size_t result_size = (size_ <= other.size_) ? size_ : other.size_;
Bitmap result(result_size);
for (size_t i = 0; i < result.data_.size(); ++i) {
result.data_[i] = data_[i] & other.data_[i];
}
return result;
}
Bitmap Bitmap::operator|(const Bitmap& other) const {
const size_t result_size = (size_ <= other.size_) ? size_ : other.size_;
Bitmap result(result_size);
for (size_t i = 0; i < result.data_.size(); ++i) {
result.data_[i] = data_[i] | other.data_[i];
}
return result;
}
std::vector<size_t> Bitmap::get_indices() const {
std::vector<size_t> indices;
indices.reserve(popcount());
for (size_t i = 0; i < data_.size(); ++i) {
uint8_t byte = data_[i];
while (byte != 0) {
unsigned bit =
static_cast<unsigned>(__builtin_ctz(static_cast<unsigned>(byte)));
size_t idx = i * kBitsPerByte + bit;
if (idx < size_) {
indices.push_back(idx);
}
byte &= static_cast<uint8_t>(byte - 1); // clear lowest set bit
}
}
return indices;
}
std::unordered_set<size_t> Bitmap::get_indices_set() const {
auto vec = get_indices();
return std::unordered_set<size_t>(vec.begin(), vec.end());
}
std::string Bitmap::to_string() const {
if (size_ == 0) return "";
std::string result(size_, '0');
for (size_t i = 0; i < data_.size(); ++i) {
uint8_t byte = data_[i];
for (unsigned j = 0; j < kBitsPerByte; ++j) {
size_t bit_index = i * kBitsPerByte + j;
if (bit_index >= size_) {
break;
}
if ((byte >> j) & 1u) {
result[bit_index] = '1';
}
}
}
return result;
}
Bitmap Bitmap::operator~() const {
Bitmap result(size_);
for (size_t i = 0; i < data_.size(); ++i) {
result.data_[i] = ~data_[i];
}
// Clear bits that are out of range in the last byte
const unsigned remaining_bits = size_ % kBitsPerByte;
if (remaining_bits > 0) {
uint8_t mask = static_cast<uint8_t>((1u << remaining_bits) - 1);
result.data_.back() &= mask;
}
return result;
}
Bitmap::~Bitmap() = default;
} // namespace storage_manager
} // namespace lmcache