2121#include " redis_bitmap.h"
2222
2323#include < algorithm>
24+ #include < cstdint>
2425#include < memory>
26+ #include < utility>
2527#include < vector>
2628
2729#include " common/bit_util.h"
@@ -307,7 +309,9 @@ rocksdb::Status Bitmap::BitCount(const Slice &user_key, int64_t start, int64_t s
307309}
308310
309311rocksdb::Status Bitmap::BitPos (const Slice &user_key, bool bit, int64_t start, int64_t stop, bool stop_given,
310- int64_t *pos) {
312+ int64_t *pos, bool is_bit_index) {
313+ if (is_bit_index) DCHECK (stop_given);
314+
311315 std::string raw_value;
312316 std::string ns_key = AppendNamespacePrefix (user_key);
313317
@@ -323,11 +327,15 @@ rocksdb::Status Bitmap::BitPos(const Slice &user_key, bool bit, int64_t start, i
323327 if (metadata.Type () == kRedisString ) {
324328 ss = std::nullopt ;
325329 redis::BitmapString bitmap_string_db (storage_, namespace_);
326- return bitmap_string_db.BitPos (raw_value, bit, start, stop, stop_given, pos);
330+ return bitmap_string_db.BitPos (raw_value, bit, start, stop, stop_given, pos, is_bit_index );
327331 }
328- std::tie (start, stop) = BitmapString::NormalizeRange (start, stop, static_cast <int64_t >(metadata.size ));
329- auto u_start = static_cast <uint32_t >(start);
330- auto u_stop = static_cast <uint32_t >(stop);
332+
333+ uint32_t to_bit_factor = is_bit_index ? 8 : 1 ;
334+ auto size = static_cast <int64_t >(metadata.size ) * static_cast <int64_t >(to_bit_factor);
335+
336+ std::tie (start, stop) = BitmapString::NormalizeRange (start, stop, size);
337+ auto u_start = static_cast <uint64_t >(start);
338+ auto u_stop = static_cast <uint64_t >(stop);
331339 if (u_start > u_stop) {
332340 *pos = -1 ;
333341 return rocksdb::Status::OK ();
@@ -341,13 +349,40 @@ rocksdb::Status Bitmap::BitPos(const Slice &user_key, bool bit, int64_t start, i
341349 return -1 ;
342350 };
343351
352+ auto bit_pos_in_byte_startstop = [](char byte, bool bit, uint32_t start, uint32_t stop) -> int {
353+ for (uint32_t i = start; i <= stop; i++) {
354+ if (bit && (byte & (1 << i)) != 0 ) return (int )i; // typecast to int since the value ranges from 0 to 7
355+ if (!bit && (byte & (1 << i)) == 0 ) return (int )i;
356+ }
357+ return -1 ;
358+ };
359+
344360 rocksdb::ReadOptions read_options;
345361 read_options.snapshot = ss->GetSnapShot ();
346- uint32_t start_index = u_start / kBitmapSegmentBytes ;
347- uint32_t stop_index = u_stop / kBitmapSegmentBytes ;
362+ // if bit index, (Eg start = 1, stop = 35), then
363+ // u_start = 1/8 = 0, u_stop = 35/8 = 4 (in bytes)
364+ uint32_t start_segment_index = (u_start / to_bit_factor) / kBitmapSegmentBytes ;
365+ uint32_t stop_segment_index = (u_stop / to_bit_factor) / kBitmapSegmentBytes ;
366+ uint32_t start_bit_pos_in_byte = 0 ;
367+ uint32_t stop_bit_pos_in_byte = 0 ;
368+
369+ if (is_bit_index) {
370+ start_bit_pos_in_byte = u_start % 8 ;
371+ stop_bit_pos_in_byte = u_stop % 8 ;
372+ }
373+
374+ auto range_in_byte = [start_bit_pos_in_byte, stop_bit_pos_in_byte](
375+ uint32_t byte_start, uint32_t byte_end,
376+ uint32_t curr_byte) -> std::pair<uint32_t , uint32_t > {
377+ if (curr_byte == byte_start && curr_byte == byte_end) return {start_bit_pos_in_byte, stop_bit_pos_in_byte};
378+ if (curr_byte == byte_start) return {start_bit_pos_in_byte, 7 };
379+ if (curr_byte == byte_end) return {0 , stop_bit_pos_in_byte};
380+ return {0 , 7 };
381+ };
382+
348383 // Don't use multi get to prevent large range query, and take too much memory
349384 // Searching bits in segments [start_index, stop_index].
350- for (uint32_t i = start_index ; i <= stop_index ; i++) {
385+ for (uint32_t i = start_segment_index ; i <= stop_segment_index ; i++) {
351386 rocksdb::PinnableSlice pin_value;
352387 std::string sub_key =
353388 InternalKey (ns_key, std::to_string (i * kBitmapSegmentBytes ), metadata.version , storage_->IsSlotIdEncoded ())
@@ -364,17 +399,33 @@ rocksdb::Status Bitmap::BitPos(const Slice &user_key, bool bit, int64_t start, i
364399 continue ;
365400 }
366401 size_t byte_pos_in_segment = 0 ;
367- if (i == start_index) byte_pos_in_segment = u_start % kBitmapSegmentBytes ;
402+ size_t byte_with_bit_start = -1 ;
403+ size_t byte_with_bit_stop = -2 ;
404+ // if bit index, (Eg start = 1, stop = 35), then
405+ // byte_pos_in_segment should be calculated in bytes, hence divide by 8
406+ if (i == start_segment_index) {
407+ byte_pos_in_segment = (u_start / to_bit_factor) % kBitmapSegmentBytes ;
408+ byte_with_bit_start = byte_pos_in_segment;
409+ }
368410 size_t stop_byte_in_segment = pin_value.size ();
369- if (i == stop_index) {
370- DCHECK_LE (u_stop % kBitmapSegmentBytes + 1 , pin_value.size ());
371- stop_byte_in_segment = u_stop % kBitmapSegmentBytes + 1 ;
411+ if (i == stop_segment_index) {
412+ DCHECK_LE ((u_stop / to_bit_factor) % kBitmapSegmentBytes + 1 , pin_value.size ());
413+ stop_byte_in_segment = (u_stop / to_bit_factor) % kBitmapSegmentBytes + 1 ;
414+ byte_with_bit_stop = stop_byte_in_segment;
372415 }
373416 // Invariant:
374417 // 1. pin_value.size() <= kBitmapSegmentBytes.
375418 // 2. If it's the last segment, metadata.size % kBitmapSegmentBytes <= pin_value.size().
376419 for (; byte_pos_in_segment < stop_byte_in_segment; byte_pos_in_segment++) {
377- int bit_pos_in_byte_value = bit_pos_in_byte (pin_value[byte_pos_in_segment], bit);
420+ int bit_pos_in_byte_value = -1 ;
421+ if (is_bit_index) {
422+ uint32_t start_bit = 0 , stop_bit = 7 ;
423+ std::tie (start_bit, stop_bit) = range_in_byte (byte_with_bit_start, byte_with_bit_stop, byte_pos_in_segment);
424+ bit_pos_in_byte_value = bit_pos_in_byte_startstop (pin_value[byte_pos_in_segment], bit, start_bit, stop_bit);
425+ } else {
426+ bit_pos_in_byte_value = bit_pos_in_byte (pin_value[byte_pos_in_segment], bit);
427+ }
428+
378429 if (bit_pos_in_byte_value != -1 ) {
379430 *pos = static_cast <int64_t >(i * kBitmapSegmentBits + byte_pos_in_segment * 8 + bit_pos_in_byte_value);
380431 return rocksdb::Status::OK ();
@@ -387,7 +438,7 @@ rocksdb::Status Bitmap::BitPos(const Slice &user_key, bool bit, int64_t start, i
387438 // 1. If it's the last segment, we've done searching in the above loop.
388439 // 2. If it's not the last segment, we can check if the segment is all 0.
389440 if (pin_value.size () < kBitmapSegmentBytes ) {
390- if (i == stop_index ) {
441+ if (i == stop_segment_index ) {
391442 continue ;
392443 }
393444 *pos = static_cast <int64_t >(i * kBitmapSegmentBits + pin_value.size () * 8 );
0 commit comments