Skip to content

Commit fea7fcf

Browse files
committed
bit_op support
1 parent 792df36 commit fea7fcf

4 files changed

Lines changed: 491 additions & 0 deletions

File tree

src/commands/cmd_bit.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,14 @@ class CommandBitOp : public Commander {
225225
op_flag_ = kBitOpXor;
226226
else if (opname == "not")
227227
op_flag_ = kBitOpNot;
228+
else if (opname == "diff")
229+
op_flag_ = kBitOpDiff;
230+
else if (opname == "diff1")
231+
op_flag_ = kBitOpDiff1;
232+
else if (opname == "andor")
233+
op_flag_ = kBitOpAndOr;
234+
else if (opname == "one")
235+
op_flag_ = kBitOpOne;
228236
else
229237
return {Status::RedisInvalidCmd, errInvalidSyntax};
230238
if (op_flag_ == kBitOpNot && args.size() != 4) {

src/types/redis_bitmap.cc

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,80 @@ rocksdb::Status Bitmap::BitOp(engine::Context &ctx, BitOpFlags op_flag, const st
579579
apply_fast_path_op([](uint64_t &a, uint64_t b) { a |= b; });
580580
} else if (op_flag == kBitOpXor) {
581581
apply_fast_path_op([](uint64_t &a, uint64_t b) { a ^= b; });
582+
} else if (op_flag == kBitOpDiff) {
583+
// X & (~(Y1 | Y2 | ...))
584+
uint64_t others_or[4];
585+
while (frag_minlen >= sizeof(uint64_t) * 4) {
586+
for (uint64_t k = 0; k < 4; k++) {
587+
others_or[k] = 0;
588+
for (uint64_t i = 1; i < frag_numkeys; i++) {
589+
others_or[k] |= lp[i][k];
590+
}
591+
lres[k] = lres[k] & ~others_or[k];
592+
}
593+
for (uint64_t i = 1; i < frag_numkeys; i++) {
594+
lp[i] += 4;
595+
}
596+
lres += 4;
597+
j += sizeof(uint64_t) * 4;
598+
frag_minlen -= sizeof(uint64_t) * 4;
599+
}
600+
} else if (op_flag == kBitOpDiff1) {
601+
// (~X) & (Y1 | Y2 | ...)
602+
uint64_t others_or[4];
603+
while (frag_minlen >= sizeof(uint64_t) * 4) {
604+
for (uint64_t k = 0; k < 4; k++) {
605+
others_or[k] = 0;
606+
for (uint64_t i = 1; i < frag_numkeys; i++) {
607+
others_or[k] |= lp[i][k];
608+
}
609+
lres[k] = ~lres[k] & others_or[k];
610+
}
611+
for (uint64_t i = 1; i < frag_numkeys; i++) {
612+
lp[i] += 4;
613+
}
614+
lres += 4;
615+
j += sizeof(uint64_t) * 4;
616+
frag_minlen -= sizeof(uint64_t) * 4;
617+
}
618+
} else if (op_flag == kBitOpAndOr) {
619+
// X & (Y1 | Y2 | ...)
620+
uint64_t others_or[4];
621+
while (frag_minlen >= sizeof(uint64_t) * 4) {
622+
for (uint64_t k = 0; k < 4; k++) {
623+
others_or[k] = 0;
624+
for (uint64_t i = 1; i < frag_numkeys; i++) {
625+
others_or[k] |= lp[i][k];
626+
}
627+
lres[k] = lres[k] & others_or[k];
628+
}
629+
for (uint64_t i = 1; i < frag_numkeys; i++) {
630+
lp[i] += 4;
631+
}
632+
lres += 4;
633+
j += sizeof(uint64_t) * 4;
634+
frag_minlen -= sizeof(uint64_t) * 4;
635+
}
636+
} else if (op_flag == kBitOpOne) {
637+
// (X1 ^ X2 ^ ...) & (~(X1 & X2 & ...))
638+
uint64_t all_and[4], all_xor[4];
639+
while (frag_minlen >= sizeof(uint64_t) * 4) {
640+
for (uint64_t k = 0; k < 4; k++) {
641+
all_and[k] = lp[0][k];
642+
all_xor[k] = lp[0][k];
643+
for (uint64_t i = 1; i < frag_numkeys; i++) {
644+
all_and[k] &= lp[i][k];
645+
all_xor[k] ^= lp[i][k];
646+
}
647+
lres[k] = all_xor[k] & ~all_and[k];
648+
}
649+
for (uint64_t i = 0; i < frag_numkeys; i++) {
650+
lp[i] += 4;
651+
}
652+
lres += 4;
653+
j += sizeof(uint64_t) * 4;
654+
frag_minlen -= sizeof(uint64_t) * 4;
655+
}
582656
} else if (op_flag == kBitOpNot) {
583657
while (frag_minlen >= sizeof(uint64_t) * 4) {
584658
lres[0] = ~lres[0];
@@ -597,6 +671,46 @@ rocksdb::Status Bitmap::BitOp(engine::Context &ctx, BitOpFlags op_flag, const st
597671
for (; j < frag_maxlen; j++) {
598672
output = (fragments[0].size() <= j) ? 0 : fragments[0][j];
599673
if (op_flag == kBitOpNot) output = ~output;
674+
675+
// For DIFF, DIFF1, ANDOR, and ONE operations, we need special handling
676+
if (op_flag == kBitOpDiff || op_flag == kBitOpDiff1 || op_flag == kBitOpAndOr) {
677+
// Calculate OR of all keys except the first one
678+
uint8_t others_or = 0;
679+
for (uint64_t i = 1; i < frag_numkeys; i++) {
680+
byte = (fragments[i].size() <= j) ? 0 : fragments[i][j];
681+
others_or |= byte;
682+
}
683+
684+
if (op_flag == kBitOpDiff) {
685+
// X & (~(Y1 | Y2 | ...))
686+
output = output & ~others_or;
687+
} else if (op_flag == kBitOpDiff1) {
688+
// (~X) & (Y1 | Y2 | ...)
689+
output = ~output & others_or;
690+
} else if (op_flag == kBitOpAndOr) {
691+
// X & (Y1 | Y2 | ...)
692+
output = output & others_or;
693+
}
694+
frag_res[j] = output;
695+
continue;
696+
}
697+
698+
if (op_flag == kBitOpOne) {
699+
// Calculate AND and XOR of all keys
700+
uint8_t all_and = (fragments[0].size() <= j) ? 0 : fragments[0][j];
701+
uint8_t all_xor = all_and;
702+
for (uint64_t i = 1; i < frag_numkeys; i++) {
703+
byte = (fragments[i].size() <= j) ? 0 : fragments[i][j];
704+
all_and &= byte;
705+
all_xor ^= byte;
706+
}
707+
// (X1 ^ X2 ^ ...) & (~(X1 & X2 & ...))
708+
output = all_xor & ~all_and;
709+
frag_res[j] = output;
710+
continue;
711+
}
712+
713+
// Standard operations: AND, OR, XOR, NOT
600714
for (uint64_t i = 1; i < frag_numkeys; i++) {
601715
byte = (fragments[i].size() <= j) ? 0 : fragments[i][j];
602716
switch (op_flag) {
@@ -609,6 +723,8 @@ rocksdb::Status Bitmap::BitOp(engine::Context &ctx, BitOpFlags op_flag, const st
609723
case kBitOpXor:
610724
output ^= byte;
611725
break;
726+
case kBitOpNot:
727+
// NOT operation ignores other keys after the first one
612728
default:
613729
break;
614730
}

src/types/redis_bitmap.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ enum BitOpFlags {
3434
kBitOpOr,
3535
kBitOpXor,
3636
kBitOpNot,
37+
kBitOpDiff,
38+
kBitOpDiff1,
39+
kBitOpAndOr,
40+
kBitOpOne,
3741
};
3842

3943
namespace redis {

0 commit comments

Comments
 (0)