From fd0ae076b9a8c79014aa012ece7c5b9a8777f0d5 Mon Sep 17 00:00:00 2001 From: xuyu <11161569@vivo.com> Date: Thu, 21 Aug 2025 19:28:32 +0800 Subject: [PATCH 01/13] feat(bitop): Support DIFF, DIFF1, ANDOR, and ONE for command BITOP --- src/commands/cmd_bit.cc | 8 +++ src/types/redis_bitmap.cc | 110 +++++++++++++++++++++++++++++++++++++- src/types/redis_bitmap.h | 4 ++ 3 files changed, 120 insertions(+), 2 deletions(-) diff --git a/src/commands/cmd_bit.cc b/src/commands/cmd_bit.cc index 13be25f3134..26c00285233 100644 --- a/src/commands/cmd_bit.cc +++ b/src/commands/cmd_bit.cc @@ -225,6 +225,14 @@ class CommandBitOp : public Commander { op_flag_ = kBitOpXor; else if (opname == "not") op_flag_ = kBitOpNot; + else if (opname == "diff") + op_flag_ = kBitOpDiff; + else if (opname == "diff1") + op_flag_ = kBitOpDiff1; + else if (opname == "andor") + op_flag_ = kBitOpAndOr; + else if (opname == "one") + op_flag_ = kBitOpOne; else return {Status::RedisInvalidCmd, errInvalidSyntax}; if (op_flag_ == kBitOpNot && args.size() != 4) { diff --git a/src/types/redis_bitmap.cc b/src/types/redis_bitmap.cc index 00c8d1b3fde..8caeb755756 100644 --- a/src/types/redis_bitmap.cc +++ b/src/types/redis_bitmap.cc @@ -589,11 +589,94 @@ rocksdb::Status Bitmap::BitOp(engine::Context &ctx, BitOpFlags op_flag, const st j += sizeof(uint64_t) * 4; frag_minlen -= sizeof(uint64_t) * 4; } + } else if (op_flag == kBitOpDiff + || op_flag == kBitOpDiff1 + || op_flag == kBitOpAndOr) { + size_t processed = 0; + size_t k = 0; + + while(frag_minlen >= sizeof(uint64_t)*4) { + for (uint64_t i = 1; i < frag_numkeys; i++) { + lres[0] |= lp[i][k+0]; + lres[1] |= lp[i][k+1]; + lres[2] |= lp[i][k+2]; + lres[3] |= lp[i][k+3]; + } + k += 4; + lres += 4; + j += sizeof(uint64_t) * 4; + frag_minlen -= sizeof(uint64_t) * 4; + processed += sizeof(uint64_t) * 4; + } + + lres = reinterpret_cast(frag_res.get()); + auto *first_key = reinterpret_cast(fragments[0].data()); + switch (op_flag) { + case kBitOpDiff: + for (uint64_t i = 0; i < processed; i += sizeof(uint64_t) * 4) { + lres[0] = (first_key[0] & ~lres[0]); + lres[1] = (first_key[1] & ~lres[1]); + lres[2] = (first_key[2] & ~lres[2]); + lres[3] = (first_key[3] & ~lres[3]); + lres+=4; + first_key += 4; + } + break; + case kBitOpDiff1: + for (uint64_t i = 0; i < processed; i += sizeof(uint64_t) * 4) { + lres[0] = (~first_key[0] & lres[0]); + lres[1] = (~first_key[1] & lres[1]); + lres[2] = (~first_key[2] & lres[2]); + lres[3] = (~first_key[3] & lres[3]); + lres += 4; + first_key += 4; + } + break; + case kBitOpAndOr: + for (uint64_t i = 0; i < processed; i += sizeof(uint64_t) * 4) { + lres[0] = (first_key[0] & lres[0]); + lres[1] = (first_key[1] & lres[1]); + lres[2] = (first_key[2] & lres[2]); + lres[3] = (first_key[3] & lres[3]); + lres += 4; + first_key += 4; + } + break; + } + } else if (op_flag == kBitOpOne) { + uint64_t lcommon_bits[4]; + size_t k = 0; + + while(frag_minlen >= sizeof(uint64_t)*4) { + memset(lcommon_bits, 0, sizeof(lcommon_bits)); + + for (size_t i = 1; i < frag_numkeys; i++) { + lcommon_bits[0] |= (lres[0] & lp[i][k+0]); + lcommon_bits[1] |= (lres[1] & lp[i][k+1]); + lcommon_bits[2] |= (lres[2] & lp[i][k+2]); + lcommon_bits[3] |= (lres[3] & lp[i][k+3]); + + lres[0] ^= lp[i][k+0]; + lres[1] ^= lp[i][k+1]; + lres[2] ^= lp[i][k+2]; + lres[3] ^= lp[i][k+3]; + } + + lres[0] &= ~lcommon_bits[0]; + lres[1] &= ~lcommon_bits[1]; + lres[2] &= ~lcommon_bits[2]; + lres[3] &= ~lcommon_bits[3]; + + k += 4; + lres += 4; + j += sizeof(uint64_t) * 4; + frag_minlen -= sizeof(uint64_t) * 4; + } } } #endif - uint8_t output = 0, byte = 0; + uint8_t output = 0, byte = 0, disjunction = 0, common_bits = 0; for (; j < frag_maxlen; j++) { output = (fragments[0].size() <= j) ? 0 : fragments[0][j]; if (op_flag == kBitOpNot) output = ~output; @@ -609,11 +692,34 @@ rocksdb::Status Bitmap::BitOp(engine::Context &ctx, BitOpFlags op_flag, const st case kBitOpXor: output ^= byte; break; + case kBitOpDiff: + case kBitOpDiff1: + case kBitOpAndOr: + disjunction |= byte; + break; + case kBitOpOne: + common_bits |= (output & byte); + output ^= byte; + output &= ~common_bits; + break; default: break; } } - frag_res[j] = output; + switch(op_flag) { + case kBitOpDiff: + frag_res[j] = (output & ~disjunction); + break; + case kBitOpDiff1: + frag_res[j] = (~output & disjunction); + break; + case kBitOpAndOr: + frag_res[j] = (output & disjunction); + break; + default: + frag_res[j] = output; + break; + } } if (op_flag == kBitOpNot) { diff --git a/src/types/redis_bitmap.h b/src/types/redis_bitmap.h index 32a53cc72ab..0879266facd 100644 --- a/src/types/redis_bitmap.h +++ b/src/types/redis_bitmap.h @@ -34,6 +34,10 @@ enum BitOpFlags { kBitOpOr, kBitOpXor, kBitOpNot, + kBitOpDiff, + kBitOpDiff1, + kBitOpAndOr, + kBitOpOne }; namespace redis { From 07eabf29362df8290dae475362116fa40d1d382e Mon Sep 17 00:00:00 2001 From: xuyu <11161569@vivo.com> Date: Mon, 2 Mar 2026 10:30:04 +0800 Subject: [PATCH 02/13] fix conflict --- tests/gocase/go.mod | 8 +++++ tests/gocase/go.sum | 34 ++++++++++++++++++++ tests/gocase/unit/type/bitmap/bitmap_test.go | 11 +++++++ 3 files changed, 53 insertions(+) diff --git a/tests/gocase/go.mod b/tests/gocase/go.mod index 017cb902d6b..c13cbf6c60d 100644 --- a/tests/gocase/go.mod +++ b/tests/gocase/go.mod @@ -3,11 +3,19 @@ module github.com/apache/kvrocks/tests/gocase go 1.24.0 require ( +<<<<<<< HEAD github.com/redis/go-redis/v9 v9.17.2 github.com/shirou/gopsutil/v4 v4.25.12 github.com/stretchr/testify v1.11.1 golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 golang.org/x/sync v0.19.0 +======= + github.com/redis/go-redis/v9 v9.12.0 + github.com/shirou/gopsutil/v4 v4.25.4 + github.com/stretchr/testify v1.10.0 + golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 + golang.org/x/sync v0.14.0 +>>>>>>> 2c95f35b (Add testcases) ) require ( diff --git a/tests/gocase/go.sum b/tests/gocase/go.sum index f1bab797459..2c492cc0f2d 100644 --- a/tests/gocase/go.sum +++ b/tests/gocase/go.sum @@ -8,19 +8,30 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +<<<<<<< HEAD github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A= github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +======= +github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw= +github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +>>>>>>> 2c95f35b (Add testcases) github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +<<<<<<< HEAD github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k= github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= +======= +github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 h1:PpXWgLPs+Fqr325bN2FD2ISlRRztXibcX6e8f5FR5Dc= +github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= +>>>>>>> 2c95f35b (Add testcases) github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +<<<<<<< HEAD github.com/redis/go-redis/v9 v9.17.2 h1:P2EGsA4qVIM3Pp+aPocCJ7DguDHhqrXNhVcEp4ViluI= github.com/redis/go-redis/v9 v9.17.2/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370= github.com/shirou/gopsutil/v4 v4.25.12 h1:e7PvW/0RmJ8p8vPGJH4jvNkOyLmbkXgXW4m6ZPic6CY= @@ -42,6 +53,29 @@ golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +======= +github.com/redis/go-redis/v9 v9.12.0 h1:XlVPGlflh4nxfhsNXPA8Qp6EmEfTo0rp8oaBzPipXnU= +github.com/redis/go-redis/v9 v9.12.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= +github.com/shirou/gopsutil/v4 v4.25.4 h1:cdtFO363VEOOFrUCjZRh4XVJkb548lyF0q0uTeMqYPw= +github.com/shirou/gopsutil/v4 v4.25.4/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4= +github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4= +github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso= +github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= +golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= +golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +>>>>>>> 2c95f35b (Add testcases) gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/tests/gocase/unit/type/bitmap/bitmap_test.go b/tests/gocase/unit/type/bitmap/bitmap_test.go index 6211ae3c836..2137f98e117 100644 --- a/tests/gocase/unit/type/bitmap/bitmap_test.go +++ b/tests/gocase/unit/type/bitmap/bitmap_test.go @@ -271,6 +271,17 @@ func TestBitmap(t *testing.T) { require.EqualValues(t, []string{"\x55\xff\x00\xaa"}, GetBitmap(t, rdb, ctx, "s")) }) + t.Run("BITOP DIFF|DIFF1|ANDOR|ONE don't change the string with single input key", func(t *testing.T) { + Set2SetBit(t, rdb, ctx, "a", []byte("\x01\x02\xff")) + Set2SetBit(t, rdb, ctx, "b", []byte("\x01\x02\xff")) + Set2SetBit(t, rdb, ctx, "c", []byte("\x01\x02\xff")) + require.NoError(t, rdb.BitOpDiff(ctx, "res1", "a", "b", "c").Err()) + require.NoError(t, rdb.BitOpDiff1(ctx, "res2", "a", "b", "c").Err()) + require.NoError(t, rdb.BitOpAndOr(ctx, "res3", "a", "b", "c").Err()) + require.NoError(t, rdb.BitOpOne(ctx, "res4", "a", "b", "c").Err()) + require.EqualValues(t, []string{"\x00\x00\x00", "\x00\x01\x00", "\x01\x02\xff", "\x00\x00\x00"}, GetBitmap(t, rdb, ctx, "res1", "res2", "res3", "res4")) + }) + t.Run("BITOP AND|OR|XOR don't change the string with single input key", func(t *testing.T) { Set2SetBit(t, rdb, ctx, "a", []byte("\x01\x02\xff")) require.NoError(t, rdb.BitOpAnd(ctx, "res1", "a").Err()) From b1efa3066e8913df2704ab863caac562b6e18905 Mon Sep 17 00:00:00 2001 From: xuzifu666 <1206332514@qq.com> Date: Sat, 23 Aug 2025 09:38:22 +0800 Subject: [PATCH 03/13] Format fix --- src/types/redis_bitmap.cc | 38 ++++++++++++++++++-------------------- src/types/redis_bitmap.h | 11 +---------- 2 files changed, 19 insertions(+), 30 deletions(-) diff --git a/src/types/redis_bitmap.cc b/src/types/redis_bitmap.cc index 8caeb755756..3c2cf2ad99e 100644 --- a/src/types/redis_bitmap.cc +++ b/src/types/redis_bitmap.cc @@ -589,18 +589,16 @@ rocksdb::Status Bitmap::BitOp(engine::Context &ctx, BitOpFlags op_flag, const st j += sizeof(uint64_t) * 4; frag_minlen -= sizeof(uint64_t) * 4; } - } else if (op_flag == kBitOpDiff - || op_flag == kBitOpDiff1 - || op_flag == kBitOpAndOr) { + } else if (op_flag == kBitOpDiff || op_flag == kBitOpDiff1 || op_flag == kBitOpAndOr) { size_t processed = 0; size_t k = 0; - while(frag_minlen >= sizeof(uint64_t)*4) { + while (frag_minlen >= sizeof(uint64_t) * 4) { for (uint64_t i = 1; i < frag_numkeys; i++) { - lres[0] |= lp[i][k+0]; - lres[1] |= lp[i][k+1]; - lres[2] |= lp[i][k+2]; - lres[3] |= lp[i][k+3]; + lres[0] |= lp[i][k + 0]; + lres[1] |= lp[i][k + 1]; + lres[2] |= lp[i][k + 2]; + lres[3] |= lp[i][k + 3]; } k += 4; lres += 4; @@ -618,7 +616,7 @@ rocksdb::Status Bitmap::BitOp(engine::Context &ctx, BitOpFlags op_flag, const st lres[1] = (first_key[1] & ~lres[1]); lres[2] = (first_key[2] & ~lres[2]); lres[3] = (first_key[3] & ~lres[3]); - lres+=4; + lres += 4; first_key += 4; } break; @@ -647,19 +645,19 @@ rocksdb::Status Bitmap::BitOp(engine::Context &ctx, BitOpFlags op_flag, const st uint64_t lcommon_bits[4]; size_t k = 0; - while(frag_minlen >= sizeof(uint64_t)*4) { + while (frag_minlen >= sizeof(uint64_t) * 4) { memset(lcommon_bits, 0, sizeof(lcommon_bits)); for (size_t i = 1; i < frag_numkeys; i++) { - lcommon_bits[0] |= (lres[0] & lp[i][k+0]); - lcommon_bits[1] |= (lres[1] & lp[i][k+1]); - lcommon_bits[2] |= (lres[2] & lp[i][k+2]); - lcommon_bits[3] |= (lres[3] & lp[i][k+3]); - - lres[0] ^= lp[i][k+0]; - lres[1] ^= lp[i][k+1]; - lres[2] ^= lp[i][k+2]; - lres[3] ^= lp[i][k+3]; + lcommon_bits[0] |= (lres[0] & lp[i][k + 0]); + lcommon_bits[1] |= (lres[1] & lp[i][k + 1]); + lcommon_bits[2] |= (lres[2] & lp[i][k + 2]); + lcommon_bits[3] |= (lres[3] & lp[i][k + 3]); + + lres[0] ^= lp[i][k + 0]; + lres[1] ^= lp[i][k + 1]; + lres[2] ^= lp[i][k + 2]; + lres[3] ^= lp[i][k + 3]; } lres[0] &= ~lcommon_bits[0]; @@ -706,7 +704,7 @@ rocksdb::Status Bitmap::BitOp(engine::Context &ctx, BitOpFlags op_flag, const st break; } } - switch(op_flag) { + switch (op_flag) { case kBitOpDiff: frag_res[j] = (output & ~disjunction); break; diff --git a/src/types/redis_bitmap.h b/src/types/redis_bitmap.h index 0879266facd..55dfceed060 100644 --- a/src/types/redis_bitmap.h +++ b/src/types/redis_bitmap.h @@ -29,16 +29,7 @@ #include "storage/redis_db.h" #include "storage/redis_metadata.h" -enum BitOpFlags { - kBitOpAnd, - kBitOpOr, - kBitOpXor, - kBitOpNot, - kBitOpDiff, - kBitOpDiff1, - kBitOpAndOr, - kBitOpOne -}; +enum BitOpFlags { kBitOpAnd, kBitOpOr, kBitOpXor, kBitOpNot, kBitOpDiff, kBitOpDiff1, kBitOpAndOr, kBitOpOne }; namespace redis { From 80cdd1b2e40cf5097bd4e87155ee3ea0bec93667 Mon Sep 17 00:00:00 2001 From: xuzifu666 <1206332514@qq.com> Date: Sat, 23 Aug 2025 09:53:12 +0800 Subject: [PATCH 04/13] Fix format --- src/types/redis_bitmap.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/types/redis_bitmap.h b/src/types/redis_bitmap.h index 55dfceed060..0879266facd 100644 --- a/src/types/redis_bitmap.h +++ b/src/types/redis_bitmap.h @@ -29,7 +29,16 @@ #include "storage/redis_db.h" #include "storage/redis_metadata.h" -enum BitOpFlags { kBitOpAnd, kBitOpOr, kBitOpXor, kBitOpNot, kBitOpDiff, kBitOpDiff1, kBitOpAndOr, kBitOpOne }; +enum BitOpFlags { + kBitOpAnd, + kBitOpOr, + kBitOpXor, + kBitOpNot, + kBitOpDiff, + kBitOpDiff1, + kBitOpAndOr, + kBitOpOne +}; namespace redis { From 35c2801d66d482a1c28517d7afcf64ba5e08a80c Mon Sep 17 00:00:00 2001 From: xuzifu666 <1206332514@qq.com> Date: Sat, 23 Aug 2025 09:57:33 +0800 Subject: [PATCH 05/13] Fix format --- src/types/redis_bitmap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/redis_bitmap.h b/src/types/redis_bitmap.h index 0879266facd..6fa0cc16233 100644 --- a/src/types/redis_bitmap.h +++ b/src/types/redis_bitmap.h @@ -37,7 +37,7 @@ enum BitOpFlags { kBitOpDiff, kBitOpDiff1, kBitOpAndOr, - kBitOpOne + kBitOpOne, }; namespace redis { From 3123538f319767fc0c402f8fb51d47d2cf48b5d1 Mon Sep 17 00:00:00 2001 From: xuzifu666 <1206332514@qq.com> Date: Sat, 23 Aug 2025 16:15:57 +0800 Subject: [PATCH 06/13] fix --- src/types/redis_bitmap.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/types/redis_bitmap.cc b/src/types/redis_bitmap.cc index 3c2cf2ad99e..52a856ca1b4 100644 --- a/src/types/redis_bitmap.cc +++ b/src/types/redis_bitmap.cc @@ -554,7 +554,9 @@ rocksdb::Status Bitmap::BitOp(engine::Context &ctx, BitOpFlags op_flag, const st for (uint64_t i = 0; i < frag_numkeys; i++) { lp[i] = reinterpret_cast(fragments[i].data()); } - memcpy(frag_res.get(), fragments[0].data(), frag_minlen); + if (op_flag != kBitOpDiff && op_flag != kBitOpDiff1 && op_flag != kBitOpAndOr) { + memcpy(frag_res.get(), fragments[0].data(), frag_minlen); + } auto apply_fast_path_op = [&](auto op) { // Note: kBitOpNot cannot use this op, it only applying // to kBitOpAnd, kBitOpOr, kBitOpXor. From 73c6df081c1849ca589fb37b7eb0d5146944b24f Mon Sep 17 00:00:00 2001 From: xuyu <11161569@vivo.com> Date: Mon, 2 Mar 2026 10:31:52 +0800 Subject: [PATCH 07/13] fix conflict --- src/types/redis_bitmap.cc | 11 +++++++++++ tests/gocase/go.mod | 8 -------- tests/gocase/go.sum | 25 +++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/types/redis_bitmap.cc b/src/types/redis_bitmap.cc index 52a856ca1b4..24ced33caeb 100644 --- a/src/types/redis_bitmap.cc +++ b/src/types/redis_bitmap.cc @@ -602,6 +602,13 @@ rocksdb::Status Bitmap::BitOp(engine::Context &ctx, BitOpFlags op_flag, const st lres[2] |= lp[i][k + 2]; lres[3] |= lp[i][k + 3]; } + // For Diff1, we need the OR of ALL keys (including the first) + if (op_flag == kBitOpDiff1) { + lres[0] |= lp[0][k + 0]; + lres[1] |= lp[0][k + 1]; + lres[2] |= lp[0][k + 2]; + lres[3] |= lp[0][k + 3]; + } k += 4; lres += 4; j += sizeof(uint64_t) * 4; @@ -680,6 +687,10 @@ rocksdb::Status Bitmap::BitOp(engine::Context &ctx, BitOpFlags op_flag, const st for (; j < frag_maxlen; j++) { output = (fragments[0].size() <= j) ? 0 : fragments[0][j]; if (op_flag == kBitOpNot) output = ~output; + // For Diff1, disjunction starts with the first key's value + if (op_flag == kBitOpDiff1) { + disjunction = output; + } for (uint64_t i = 1; i < frag_numkeys; i++) { byte = (fragments[i].size() <= j) ? 0 : fragments[i][j]; switch (op_flag) { diff --git a/tests/gocase/go.mod b/tests/gocase/go.mod index c13cbf6c60d..017cb902d6b 100644 --- a/tests/gocase/go.mod +++ b/tests/gocase/go.mod @@ -3,19 +3,11 @@ module github.com/apache/kvrocks/tests/gocase go 1.24.0 require ( -<<<<<<< HEAD github.com/redis/go-redis/v9 v9.17.2 github.com/shirou/gopsutil/v4 v4.25.12 github.com/stretchr/testify v1.11.1 golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 golang.org/x/sync v0.19.0 -======= - github.com/redis/go-redis/v9 v9.12.0 - github.com/shirou/gopsutil/v4 v4.25.4 - github.com/stretchr/testify v1.10.0 - golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 - golang.org/x/sync v0.14.0 ->>>>>>> 2c95f35b (Add testcases) ) require ( diff --git a/tests/gocase/go.sum b/tests/gocase/go.sum index 2c492cc0f2d..3e887a7a35b 100644 --- a/tests/gocase/go.sum +++ b/tests/gocase/go.sum @@ -9,9 +9,14 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= <<<<<<< HEAD +<<<<<<< HEAD github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A= github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= ======= +======= +github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= +github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +>>>>>>> 98d6256b (fix1) github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw= github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= >>>>>>> 2c95f35b (Add testcases) @@ -21,9 +26,14 @@ github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= <<<<<<< HEAD +<<<<<<< HEAD github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k= github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= ======= +======= +github.com/lufia/plan9stats v0.0.0-20250303091104-876f3ea5145d h1:fjMbDVUGsMQiVZnSQsmouYJvMdwsGiDipOZoN66v844= +github.com/lufia/plan9stats v0.0.0-20250303091104-876f3ea5145d/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= +>>>>>>> 98d6256b (fix1) github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 h1:PpXWgLPs+Fqr325bN2FD2ISlRRztXibcX6e8f5FR5Dc= github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= >>>>>>> 2c95f35b (Add testcases) @@ -32,6 +42,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= <<<<<<< HEAD +<<<<<<< HEAD github.com/redis/go-redis/v9 v9.17.2 h1:P2EGsA4qVIM3Pp+aPocCJ7DguDHhqrXNhVcEp4ViluI= github.com/redis/go-redis/v9 v9.17.2/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370= github.com/shirou/gopsutil/v4 v4.25.12 h1:e7PvW/0RmJ8p8vPGJH4jvNkOyLmbkXgXW4m6ZPic6CY= @@ -56,6 +67,14 @@ golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= ======= github.com/redis/go-redis/v9 v9.12.0 h1:XlVPGlflh4nxfhsNXPA8Qp6EmEfTo0rp8oaBzPipXnU= github.com/redis/go-redis/v9 v9.12.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= +======= +github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM= +github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= +github.com/redis/go-redis/v9 v9.9.0 h1:URbPQ4xVQSQhZ27WMQVmZSo3uT3pL+4IdHVcYq2nVfM= +github.com/redis/go-redis/v9 v9.9.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= +github.com/shirou/gopsutil/v4 v4.25.2 h1:NMscG3l2CqtWFS86kj3vP7soOczqrQYIEhO/pMvvQkk= +github.com/shirou/gopsutil/v4 v4.25.2/go.mod h1:34gBYJzyqCDT11b6bMHP0XCvWeU3J61XRT7a2EmCRTA= +>>>>>>> 98d6256b (fix1) github.com/shirou/gopsutil/v4 v4.25.4 h1:cdtFO363VEOOFrUCjZRh4XVJkb548lyF0q0uTeMqYPw= github.com/shirou/gopsutil/v4 v4.25.4/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= @@ -66,13 +85,19 @@ github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfj github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw= +golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM= golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= >>>>>>> 2c95f35b (Add testcases) From 77ce6789f145876dbed545f0198bd260de3f7c58 Mon Sep 17 00:00:00 2001 From: xuyu <11161569@vivo.com> Date: Mon, 2 Mar 2026 10:37:12 +0800 Subject: [PATCH 08/13] fix --- tests/gocase/unit/type/bitmap/bitmap_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/gocase/unit/type/bitmap/bitmap_test.go b/tests/gocase/unit/type/bitmap/bitmap_test.go index 2137f98e117..2572445ae32 100644 --- a/tests/gocase/unit/type/bitmap/bitmap_test.go +++ b/tests/gocase/unit/type/bitmap/bitmap_test.go @@ -279,7 +279,7 @@ func TestBitmap(t *testing.T) { require.NoError(t, rdb.BitOpDiff1(ctx, "res2", "a", "b", "c").Err()) require.NoError(t, rdb.BitOpAndOr(ctx, "res3", "a", "b", "c").Err()) require.NoError(t, rdb.BitOpOne(ctx, "res4", "a", "b", "c").Err()) - require.EqualValues(t, []string{"\x00\x00\x00", "\x00\x01\x00", "\x01\x02\xff", "\x00\x00\x00"}, GetBitmap(t, rdb, ctx, "res1", "res2", "res3", "res4")) + require.EqualValues(t, []string{"\x00\x00\x00", "\x00\x00\x00", "\x01\x02\xff", "\x00\x00\x00"}, GetBitmap(t, rdb, ctx, "res1", "res2", "res3", "res4")) }) t.Run("BITOP AND|OR|XOR don't change the string with single input key", func(t *testing.T) { From 9e78d9e92114715ae856a7dddf7544dc4f38eea5 Mon Sep 17 00:00:00 2001 From: xuyu <11161569@vivo.com> Date: Mon, 2 Mar 2026 10:40:29 +0800 Subject: [PATCH 09/13] fix --- tests/gocase/go.sum | 61 +-------------------------------------------- 1 file changed, 1 insertion(+), 60 deletions(-) diff --git a/tests/gocase/go.sum b/tests/gocase/go.sum index 3e887a7a35b..03f399cda75 100644 --- a/tests/gocase/go.sum +++ b/tests/gocase/go.sum @@ -8,41 +8,19 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -<<<<<<< HEAD -<<<<<<< HEAD github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A= github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= -======= -======= -github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= -github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= ->>>>>>> 98d6256b (fix1) -github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw= -github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= ->>>>>>> 2c95f35b (Add testcases) github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -<<<<<<< HEAD -<<<<<<< HEAD github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k= github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= -======= -======= -github.com/lufia/plan9stats v0.0.0-20250303091104-876f3ea5145d h1:fjMbDVUGsMQiVZnSQsmouYJvMdwsGiDipOZoN66v844= -github.com/lufia/plan9stats v0.0.0-20250303091104-876f3ea5145d/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= ->>>>>>> 98d6256b (fix1) -github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 h1:PpXWgLPs+Fqr325bN2FD2ISlRRztXibcX6e8f5FR5Dc= -github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= ->>>>>>> 2c95f35b (Add testcases) github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -<<<<<<< HEAD -<<<<<<< HEAD github.com/redis/go-redis/v9 v9.17.2 h1:P2EGsA4qVIM3Pp+aPocCJ7DguDHhqrXNhVcEp4ViluI= github.com/redis/go-redis/v9 v9.17.2/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370= github.com/shirou/gopsutil/v4 v4.25.12 h1:e7PvW/0RmJ8p8vPGJH4jvNkOyLmbkXgXW4m6ZPic6CY= @@ -64,44 +42,7 @@ golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -======= -github.com/redis/go-redis/v9 v9.12.0 h1:XlVPGlflh4nxfhsNXPA8Qp6EmEfTo0rp8oaBzPipXnU= -github.com/redis/go-redis/v9 v9.12.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= -======= -github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM= -github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= -github.com/redis/go-redis/v9 v9.9.0 h1:URbPQ4xVQSQhZ27WMQVmZSo3uT3pL+4IdHVcYq2nVfM= -github.com/redis/go-redis/v9 v9.9.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= -github.com/shirou/gopsutil/v4 v4.25.2 h1:NMscG3l2CqtWFS86kj3vP7soOczqrQYIEhO/pMvvQkk= -github.com/shirou/gopsutil/v4 v4.25.2/go.mod h1:34gBYJzyqCDT11b6bMHP0XCvWeU3J61XRT7a2EmCRTA= ->>>>>>> 98d6256b (fix1) -github.com/shirou/gopsutil/v4 v4.25.4 h1:cdtFO363VEOOFrUCjZRh4XVJkb548lyF0q0uTeMqYPw= -github.com/shirou/gopsutil/v4 v4.25.4/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4= -github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4= -github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso= -github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ= -github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= -github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw= -golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM= -golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= -golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= -golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= ->>>>>>> 2c95f35b (Add testcases) gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= \ No newline at end of file From 697e717dc97c263862d0ae331cd3cb3b3daaae6d Mon Sep 17 00:00:00 2001 From: xuyu <11161569@vivo.com> Date: Mon, 2 Mar 2026 10:44:35 +0800 Subject: [PATCH 10/13] fix --- tests/gocase/go.sum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/gocase/go.sum b/tests/gocase/go.sum index 03f399cda75..f1bab797459 100644 --- a/tests/gocase/go.sum +++ b/tests/gocase/go.sum @@ -45,4 +45,4 @@ golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= \ No newline at end of file +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 69453f317618817f7b3ae752220d3101bce7907d Mon Sep 17 00:00:00 2001 From: xuyu <11161569@vivo.com> Date: Mon, 2 Mar 2026 14:47:32 +0800 Subject: [PATCH 11/13] add more tests --- tests/gocase/unit/type/bitmap/bitmap_test.go | 341 +++++++++++++++++++ 1 file changed, 341 insertions(+) diff --git a/tests/gocase/unit/type/bitmap/bitmap_test.go b/tests/gocase/unit/type/bitmap/bitmap_test.go index 2572445ae32..b7ab7dec055 100644 --- a/tests/gocase/unit/type/bitmap/bitmap_test.go +++ b/tests/gocase/unit/type/bitmap/bitmap_test.go @@ -579,4 +579,345 @@ func TestBitmap(t *testing.T) { } }) + t.Run("BITOP DIFF|DIFF1|ANDOR|ONE with long bitmaps (fast path)", func(t *testing.T) { + // Use bitmaps longer than 32 bytes to trigger fast path (sizeof(uint64_t) * 4) + Set2SetBit(t, rdb, ctx, "a", makeLongBitmap([]byte{0x01, 0x02, 0xff}, 40)) + Set2SetBit(t, rdb, ctx, "b", makeLongBitmap([]byte{0x01, 0x02, 0xff}, 40)) + Set2SetBit(t, rdb, ctx, "c", makeLongBitmap([]byte{0x01, 0x02, 0xff}, 40)) + + require.NoError(t, rdb.BitOpDiff(ctx, "res1", "a", "b", "c").Err()) + require.NoError(t, rdb.BitOpDiff1(ctx, "res2", "a", "b", "c").Err()) + require.NoError(t, rdb.BitOpAndOr(ctx, "res3", "a", "b", "c").Err()) + require.NoError(t, rdb.BitOpOne(ctx, "res4", "a", "b", "c").Err()) + + expected := make([]string, 4) + for i := range expected { + expected[i] = makeExpectedBitmap(makeLongBitmap([]byte{0x00, 0x00, 0x00}, 40)) + } + expected[2] = makeExpectedBitmap(makeLongBitmap([]byte{0x01, 0x02, 0xff}, 40)) // BitOpAndOr + + require.EqualValues(t, expected, GetBitmap(t, rdb, ctx, "res1", "res2", "res3", "res4")) + }) + + t.Run("BITOP DIFF1 with different long bitmaps", func(t *testing.T) { + // Test Diff1: (~first) & (all_or) + Set2SetBit(t, rdb, ctx, "a", makeLongBitmap([]byte{0xaa, 0x55, 0xff}, 40)) + Set2SetBit(t, rdb, ctx, "b", makeLongBitmap([]byte{0x55, 0xaa, 0x00}, 40)) + Set2SetBit(t, rdb, ctx, "c", makeLongBitmap([]byte{0x00, 0x00, 0x00}, 40)) + + require.NoError(t, rdb.BitOpDiff1(ctx, "res", "a", "b", "c").Err()) + + // ~a = 0x55aa00, all_or = 0xffaaff + // result = 0x55aa00 & 0xffaaff = 0x55aa00 + expected := makeExpectedBitmap(makeLongBitmap([]byte{0x55, 0xaa, 0x00}, 40)) + require.EqualValues(t, expected, rdb.Get(ctx, "res").Val()) + }) + + t.Run("BITOP DIFF with different long bitmaps", func(t *testing.T) { + // Test Diff: first & ~(others_or) + Set2SetBit(t, rdb, ctx, "a", makeLongBitmap([]byte{0xaa, 0x55, 0xff}, 40)) + Set2SetBit(t, rdb, ctx, "b", makeLongBitmap([]byte{0x55, 0xaa, 0x00}, 40)) + Set2SetBit(t, rdb, ctx, "c", makeLongBitmap([]byte{0x00, 0x00, 0x00}, 40)) + + require.NoError(t, rdb.BitOpDiff(ctx, "res", "a", "b", "c").Err()) + + // a = 0xaa55ff, others_or = 0x55aa00 + // result = 0xaa55ff & ~0x55aa00 = 0xaa55ff & 0xaa55ff = 0xaa55ff + expected := makeExpectedBitmap(makeLongBitmap([]byte{0xaa, 0x55, 0xff}, 40)) + require.EqualValues(t, expected, rdb.Get(ctx, "res").Val()) + }) + + t.Run("BITOP AndOr with different long bitmaps", func(t *testing.T) { + // Test AndOr: first & (others_or) + Set2SetBit(t, rdb, ctx, "a", makeLongBitmap([]byte{0xaa, 0x55, 0xff}, 40)) + Set2SetBit(t, rdb, ctx, "b", makeLongBitmap([]byte{0x55, 0xaa, 0x00}, 40)) + Set2SetBit(t, rdb, ctx, "c", makeLongBitmap([]byte{0x00, 0x00, 0x00}, 40)) + + require.NoError(t, rdb.BitOpAndOr(ctx, "res", "a", "b", "c").Err()) + + // a = 0xaa55ff, others_or = 0x55aa00 + // result = 0xaa55ff & 0x55aa00 = 0x000500 + expected := makeExpectedBitmap(makeLongBitmap([]byte{0x00, 0x05, 0x00}, 40)) + require.EqualValues(t, expected, rdb.Get(ctx, "res").Val()) + }) + + t.Run("BITOP One with different long bitmaps", func(t *testing.T) { + // Test One: (a XOR b XOR c) AND ~(a AND b AND c) + Set2SetBit(t, rdb, ctx, "a", makeLongBitmap([]byte{0xaa, 0x55, 0xff}, 40)) + Set2SetBit(t, rdb, ctx, "b", makeLongBitmap([]byte{0x55, 0xaa, 0x00}, 40)) + Set2SetBit(t, rdb, ctx, "c", makeLongBitmap([]byte{0x00, 0x00, 0x00}, 40)) + + require.NoError(t, rdb.BitOpOne(ctx, "res", "a", "b", "c").Err()) + + // First byte: 0xaa ^ 0x55 ^ 0x00 = 0xff, 0xaa & 0x55 & 0x00 = 0x00, result = 0xff & ~0x00 = 0xff + // Second byte: 0x55 ^ 0xaa ^ 0x00 = 0xff, 0x55 & 0xaa & 0x00 = 0x00, result = 0xff & ~0x00 = 0xff + // Third byte: 0xff ^ 0x00 ^ 0x00 = 0xff, 0xff & 0x00 & 0x00 = 0x00, result = 0xff & ~0x00 = 0xff + expected := makeExpectedBitmap(makeLongBitmap([]byte{0xff, 0xff, 0xff}, 40)) + require.EqualValues(t, expected, rdb.Get(ctx, "res").Val()) + }) + + t.Run("BITOP DIFF1 fuzzing with long bitmaps", func(t *testing.T) { + for i := 0; i < 5; i++ { + require.NoError(t, rdb.FlushAll(ctx).Err()) + numVec := util.RandomInt(5) + 2 + var vec [][]byte + var veckeys []string + for j := 0; j < int(numVec); j++ { + // Generate long bitmaps (>32 bytes) to trigger fast path + str := util.RandString(33, 100, util.Binary) + vec = append(vec, []byte(str)) + veckeys = append(veckeys, "vector_"+strconv.Itoa(j)) + Set2SetBit(t, rdb, ctx, "vector_"+strconv.Itoa(j), []byte(str)) + } + + // Test Diff1 + require.NoError(t, rdb.BitOpDiff1(ctx, "target", veckeys...).Err()) + expected := SimulateBitOpDiff1(vec) + require.EqualValues(t, expected, rdb.Get(ctx, "target").Val()) + } + }) + + t.Run("BITOP DIFF fuzzing with long bitmaps", func(t *testing.T) { + for i := 0; i < 5; i++ { + require.NoError(t, rdb.FlushAll(ctx).Err()) + numVec := util.RandomInt(5) + 2 + var vec [][]byte + var veckeys []string + for j := 0; j < int(numVec); j++ { + // Generate long bitmaps (>32 bytes) to trigger fast path + str := util.RandString(33, 100, util.Binary) + vec = append(vec, []byte(str)) + veckeys = append(veckeys, "vector_"+strconv.Itoa(j)) + Set2SetBit(t, rdb, ctx, "vector_"+strconv.Itoa(j), []byte(str)) + } + + // Test Diff + require.NoError(t, rdb.BitOpDiff(ctx, "target", veckeys...).Err()) + expected := SimulateBitOpDiff(vec) + require.EqualValues(t, expected, rdb.Get(ctx, "target").Val()) + } + }) + + t.Run("BITOP AndOr fuzzing with long bitmaps", func(t *testing.T) { + for i := 0; i < 5; i++ { + require.NoError(t, rdb.FlushAll(ctx).Err()) + numVec := util.RandomInt(5) + 2 + var vec [][]byte + var veckeys []string + for j := 0; j < int(numVec); j++ { + // Generate long bitmaps (>32 bytes) to trigger fast path + str := util.RandString(33, 100, util.Binary) + vec = append(vec, []byte(str)) + veckeys = append(veckeys, "vector_"+strconv.Itoa(j)) + Set2SetBit(t, rdb, ctx, "vector_"+strconv.Itoa(j), []byte(str)) + } + + // Test AndOr + require.NoError(t, rdb.BitOpAndOr(ctx, "target", veckeys...).Err()) + expected := SimulateBitOpAndOr(vec) + require.EqualValues(t, expected, rdb.Get(ctx, "target").Val()) + } + }) + + t.Run("BITOP One fuzzing with long bitmaps", func(t *testing.T) { + for i := 0; i < 5; i++ { + require.NoError(t, rdb.FlushAll(ctx).Err()) + numVec := util.RandomInt(5) + 2 + var vec [][]byte + var veckeys []string + for j := 0; j < int(numVec); j++ { + // Generate long bitmaps (>32 bytes) to trigger fast path + str := util.RandString(33, 100, util.Binary) + vec = append(vec, []byte(str)) + veckeys = append(veckeys, "vector_"+strconv.Itoa(j)) + Set2SetBit(t, rdb, ctx, "vector_"+strconv.Itoa(j), []byte(str)) + } + + // Test One + require.NoError(t, rdb.BitOpOne(ctx, "target", veckeys...).Err()) + expected := SimulateBitOpOne(vec) + require.EqualValues(t, expected, rdb.Get(ctx, "target").Val()) + } + }) +} + +// Helper functions for testing fast path + +// makeLongBitmap creates a long bitmap by repeating the pattern +func makeLongBitmap(pattern []byte, length int) []byte { + result := make([]byte, length) + for i := 0; i < length; i++ { + result[i] = pattern[i%len(pattern)] + } + return result +} + +// makeExpectedBitmap creates expected string from a byte slice +func makeExpectedBitmap(data []byte) string { + return string(data) +} + +// SimulateBitOpDiff1 simulates BitOpDiff1: (~first) & (all_or) +func SimulateBitOpDiff1(vec [][]byte) string { + if len(vec) == 0 { + return "" + } + + // Find max length + maxlen := 0 + for _, v := range vec { + if len(v) > maxlen { + maxlen = len(v) + } + } + + // Calculate all_or + allOr := make([]byte, maxlen) + for i := 0; i < maxlen; i++ { + for j := range vec { + if i < len(vec[j]) { + allOr[i] |= vec[j][i] + } + } + } + + // Calculate (~first) & allOr + result := make([]byte, maxlen) + for i := 0; i < maxlen; i++ { + if i < len(vec[0]) { + result[i] = (^vec[0][i]) & allOr[i] + } else { + result[i] = allOr[i] + } + } + + return string(result) +} + +// SimulateBitOpDiff simulates BitOpDiff: first & ~(others_or) +func SimulateBitOpDiff(vec [][]byte) string { + if len(vec) == 0 { + return "" + } + + // Find max length + maxlen := 0 + for _, v := range vec { + if len(v) > maxlen { + maxlen = len(v) + } + } + + // Calculate others_or + othersOr := make([]byte, maxlen) + for i := 0; i < maxlen; i++ { + for j := 1; j < len(vec); j++ { + if i < len(vec[j]) { + othersOr[i] |= vec[j][i] + } + } + } + + // Calculate first & ~othersOr + result := make([]byte, maxlen) + for i := 0; i < maxlen; i++ { + if i < len(vec[0]) { + result[i] = vec[0][i] & (^othersOr[i]) + } else { + result[i] = 0 + } + } + + return string(result) +} + +// SimulateBitOpAndOr simulates BitOpAndOr: first & (others_or) +func SimulateBitOpAndOr(vec [][]byte) string { + if len(vec) == 0 { + return "" + } + + // Find max length + maxlen := 0 + for _, v := range vec { + if len(v) > maxlen { + maxlen = len(v) + } + } + + // Calculate others_or + othersOr := make([]byte, maxlen) + for i := 0; i < maxlen; i++ { + for j := 1; j < len(vec); j++ { + if i < len(vec[j]) { + othersOr[i] |= vec[j][i] + } + } + } + + // Calculate first & othersOr + result := make([]byte, maxlen) + for i := 0; i < maxlen; i++ { + if i < len(vec[0]) { + result[i] = vec[0][i] & othersOr[i] + } else { + result[i] = 0 + } + } + + return string(result) } + +// SimulateBitOpOne simulates BitOpOne: (all_xor) & ~(all_and) +func SimulateBitOpOne(vec [][]byte) string { + if len(vec) == 0 { + return "" + } + + // Find max length + maxlen := 0 + for _, v := range vec { + if len(v) > maxlen { + maxlen = len(v) + } + } + + // Calculate all_xor + allXor := make([]byte, maxlen) + for i := 0; i < maxlen; i++ { + for j := range vec { + if i < len(vec[j]) { + allXor[i] ^= vec[j][i] + } + } + } + + // Calculate all_and + allAnd := make([]byte, maxlen) + for i := 0; i < maxlen; i++ { + // Initialize with the first key's value or 0 if out of bounds + if i < len(vec[0]) { + allAnd[i] = vec[0][i] + } else { + allAnd[i] = 0 + } + // AND with all other keys + for j := 1; j < len(vec); j++ { + if i < len(vec[j]) { + allAnd[i] &= vec[j][i] + } else { + // If key j is shorter at this position, treat it as 0 + allAnd[i] &= 0 + } + } + } + + // Calculate all_xor & ~all_and + result := make([]byte, maxlen) + for i := 0; i < maxlen; i++ { + result[i] = allXor[i] & (^allAnd[i]) + } + + return string(result) +} + From 6cfbae3ae3c10d314e194360d833b9b7cfe29934 Mon Sep 17 00:00:00 2001 From: xuyu <11161569@vivo.com> Date: Tue, 3 Mar 2026 10:01:47 +0800 Subject: [PATCH 12/13] fix --- tests/gocase/unit/type/bitmap/bitmap_test.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/gocase/unit/type/bitmap/bitmap_test.go b/tests/gocase/unit/type/bitmap/bitmap_test.go index b7ab7dec055..8c7dcacbd55 100644 --- a/tests/gocase/unit/type/bitmap/bitmap_test.go +++ b/tests/gocase/unit/type/bitmap/bitmap_test.go @@ -743,11 +743,15 @@ func TestBitmap(t *testing.T) { // Helper functions for testing fast path -// makeLongBitmap creates a long bitmap by repeating the pattern +// makeLongBitmap creates a long bitmap with pattern as prefix and zeros as padding func makeLongBitmap(pattern []byte, length int) []byte { result := make([]byte, length) for i := 0; i < length; i++ { - result[i] = pattern[i%len(pattern)] + if i < len(pattern) { + result[i] = pattern[i] + } else { + result[i] = 0 + } } return result } From 8f15a54cbe337c3aab0e3f0789afb82ce1cb99d2 Mon Sep 17 00:00:00 2001 From: xuyu <11161569@vivo.com> Date: Tue, 3 Mar 2026 10:56:07 +0800 Subject: [PATCH 13/13] fix test --- tests/gocase/unit/type/bitmap/bitmap_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/gocase/unit/type/bitmap/bitmap_test.go b/tests/gocase/unit/type/bitmap/bitmap_test.go index 8c7dcacbd55..5edc74d3247 100644 --- a/tests/gocase/unit/type/bitmap/bitmap_test.go +++ b/tests/gocase/unit/type/bitmap/bitmap_test.go @@ -635,9 +635,10 @@ func TestBitmap(t *testing.T) { require.NoError(t, rdb.BitOpAndOr(ctx, "res", "a", "b", "c").Err()) - // a = 0xaa55ff, others_or = 0x55aa00 - // result = 0xaa55ff & 0x55aa00 = 0x000500 - expected := makeExpectedBitmap(makeLongBitmap([]byte{0x00, 0x05, 0x00}, 40)) + // First byte: b|c = 0x55|0x00 = 0x55, a & (b|c) = 0xaa & 0x55 = 0x00 + // Second byte: b|c = 0xaa|0x00 = 0xaa, a & (b|c) = 0x55 & 0xaa = 0x00 + // Third byte: b|c = 0x00|0x00 = 0x00, a & (b|c) = 0xff & 0x00 = 0x00 + expected := makeExpectedBitmap(makeLongBitmap([]byte{0x00, 0x00, 0x00}, 40)) require.EqualValues(t, expected, rdb.Get(ctx, "res").Val()) })