@@ -39,10 +39,14 @@ import (
3939type BITOP int32
4040
4141const (
42- AND BITOP = 0
43- OR BITOP = 1
44- XOR BITOP = 2
45- NOT BITOP = 3
42+ AND BITOP = 0
43+ OR BITOP = 1
44+ XOR BITOP = 2
45+ NOT BITOP = 3
46+ DIFF BITOP = 4
47+ DIFF1 BITOP = 5
48+ ANDOR BITOP = 6
49+ ONE BITOP = 7
4650)
4751
4852func Set2SetBit (t * testing.T , rdb * redis.Client , ctx context.Context , key string , bs []byte ) {
@@ -88,22 +92,69 @@ func SimulateBitOp(op BITOP, values ...[]byte) string {
8892 } else {
8993 x = '0'
9094 }
91- }
92- for j := 1 ; j < len (binaryArray ); j ++ {
93- left := int (x - '0' )
94- right := int (binaryArray [j ][i ] - '0' )
95- switch op {
96- case AND :
97- left = left & right
98- case XOR :
99- left = left ^ right
100- case OR :
101- left = left | right
95+ } else if op == DIFF {
96+ // bits in X but not in any Y
97+ for j := 1 ; j < len (binaryArray ); j ++ {
98+ if binaryArray [j ][i ] == '1' {
99+ x = '0'
100+ }
101+ }
102+ } else if op == DIFF1 {
103+ // bits in any Y but not in X
104+ orRest := byte ('0' )
105+ for j := 1 ; j < len (binaryArray ); j ++ {
106+ if binaryArray [j ][i ] == '1' {
107+ orRest = '1'
108+ }
102109 }
103- if left == 0 {
110+ if orRest == '1' && x == '0' {
111+ x = '1'
112+ } else {
104113 x = '0'
114+ }
115+ } else if op == ANDOR {
116+ // bits in X AND at least one Y
117+ orRest := byte ('0' )
118+ for j := 1 ; j < len (binaryArray ); j ++ {
119+ if binaryArray [j ][i ] == '1' {
120+ orRest = '1'
121+ }
122+ }
123+ if x == '1' && orRest == '1' {
124+ x = '1'
105125 } else {
126+ x = '0'
127+ }
128+ } else if op == ONE {
129+ // bits set in exactly one key
130+ count := 0
131+ for j := 0 ; j < len (binaryArray ); j ++ {
132+ if binaryArray [j ][i ] == '1' {
133+ count ++
134+ }
135+ }
136+ if count == 1 {
106137 x = '1'
138+ } else {
139+ x = '0'
140+ }
141+ } else {
142+ for j := 1 ; j < len (binaryArray ); j ++ {
143+ left := int (x - '0' )
144+ right := int (binaryArray [j ][i ] - '0' )
145+ switch op {
146+ case AND :
147+ left = left & right
148+ case XOR :
149+ left = left ^ right
150+ case OR :
151+ left = left | right
152+ }
153+ if left == 0 {
154+ x = '0'
155+ } else {
156+ x = '1'
157+ }
107158 }
108159 }
109160 binaryResult = append (binaryResult , x )
@@ -357,6 +408,124 @@ func TestBitmap(t *testing.T) {
357408 require .EqualValues (t , 32 , rdb .BitOpOr (ctx , "x" , "a" , "b" ).Val ())
358409 })
359410
411+ t .Run ("BITOP DIFF basic" , func (t * testing.T ) {
412+ require .NoError (t , rdb .FlushDB (ctx ).Err ())
413+ // X=0xff, Y=0x0f -> DIFF = 0xf0 (bits in X not in Y)
414+ Set2SetBit (t , rdb , ctx , "x" , []byte ("\xff " ))
415+ Set2SetBit (t , rdb , ctx , "y" , []byte ("\x0f " ))
416+ require .NoError (t , rdb .Do (ctx , "BITOP" , "DIFF" , "dest" , "x" , "y" ).Err ())
417+ require .EqualValues (t , SimulateBitOp (DIFF , []byte ("\xff " ), []byte ("\x0f " )), rdb .Get (ctx , "dest" ).Val ())
418+ })
419+
420+ t .Run ("BITOP DIFF with multiple Y keys" , func (t * testing.T ) {
421+ require .NoError (t , rdb .FlushDB (ctx ).Err ())
422+ Set2SetBit (t , rdb , ctx , "x" , []byte ("\xff " ))
423+ Set2SetBit (t , rdb , ctx , "y1" , []byte ("\x0f " ))
424+ Set2SetBit (t , rdb , ctx , "y2" , []byte ("\xf0 " ))
425+ require .NoError (t , rdb .Do (ctx , "BITOP" , "DIFF" , "dest" , "x" , "y1" , "y2" ).Err ())
426+ require .EqualValues (t , SimulateBitOp (DIFF , []byte ("\xff " ), []byte ("\x0f " ), []byte ("\xf0 " )), rdb .Get (ctx , "dest" ).Val ())
427+ })
428+
429+ t .Run ("BITOP DIFF missing key treated as zero" , func (t * testing.T ) {
430+ require .NoError (t , rdb .FlushDB (ctx ).Err ())
431+ Set2SetBit (t , rdb , ctx , "x" , []byte ("\xaa " ))
432+ require .NoError (t , rdb .Do (ctx , "BITOP" , "DIFF" , "dest" , "x" , "no-such-key" ).Err ())
433+ require .EqualValues (t , SimulateBitOp (DIFF , []byte ("\xaa " ), []byte ("\x00 " )), rdb .Get (ctx , "dest" ).Val ())
434+ })
435+
436+ t .Run ("BITOP DIFF1 basic" , func (t * testing.T ) {
437+ require .NoError (t , rdb .FlushDB (ctx ).Err ())
438+ // X=0xff, Y=0x0f -> DIFF1 = 0x00 (bits in Y not in X, but X has all bits set)
439+ Set2SetBit (t , rdb , ctx , "x" , []byte ("\xff " ))
440+ Set2SetBit (t , rdb , ctx , "y" , []byte ("\x0f " ))
441+ require .NoError (t , rdb .Do (ctx , "BITOP" , "DIFF1" , "dest" , "x" , "y" ).Err ())
442+ require .EqualValues (t , SimulateBitOp (DIFF1 , []byte ("\xff " ), []byte ("\x0f " )), rdb .Get (ctx , "dest" ).Val ())
443+ })
444+
445+ t .Run ("BITOP DIFF1 with partial overlap" , func (t * testing.T ) {
446+ require .NoError (t , rdb .FlushDB (ctx ).Err ())
447+ // X=0x0f, Y=0xff -> DIFF1 = 0xf0 (bits in Y not in X)
448+ Set2SetBit (t , rdb , ctx , "x" , []byte ("\x0f " ))
449+ Set2SetBit (t , rdb , ctx , "y" , []byte ("\xff " ))
450+ require .NoError (t , rdb .Do (ctx , "BITOP" , "DIFF1" , "dest" , "x" , "y" ).Err ())
451+ require .EqualValues (t , SimulateBitOp (DIFF1 , []byte ("\x0f " ), []byte ("\xff " )), rdb .Get (ctx , "dest" ).Val ())
452+ })
453+
454+ t .Run ("BITOP ANDOR basic" , func (t * testing.T ) {
455+ require .NoError (t , rdb .FlushDB (ctx ).Err ())
456+ // X=0xff, Y=0x0f -> ANDOR = 0x0f (bits in X AND at least one Y)
457+ Set2SetBit (t , rdb , ctx , "x" , []byte ("\xff " ))
458+ Set2SetBit (t , rdb , ctx , "y" , []byte ("\x0f " ))
459+ require .NoError (t , rdb .Do (ctx , "BITOP" , "ANDOR" , "dest" , "x" , "y" ).Err ())
460+ require .EqualValues (t , SimulateBitOp (ANDOR , []byte ("\xff " ), []byte ("\x0f " )), rdb .Get (ctx , "dest" ).Val ())
461+ })
462+
463+ t .Run ("BITOP ANDOR with multiple Y keys" , func (t * testing.T ) {
464+ require .NoError (t , rdb .FlushDB (ctx ).Err ())
465+ Set2SetBit (t , rdb , ctx , "x" , []byte ("\xff " ))
466+ Set2SetBit (t , rdb , ctx , "y1" , []byte ("\x0f " ))
467+ Set2SetBit (t , rdb , ctx , "y2" , []byte ("\xf0 " ))
468+ require .NoError (t , rdb .Do (ctx , "BITOP" , "ANDOR" , "dest" , "x" , "y1" , "y2" ).Err ())
469+ require .EqualValues (t , SimulateBitOp (ANDOR , []byte ("\xff " ), []byte ("\x0f " ), []byte ("\xf0 " )), rdb .Get (ctx , "dest" ).Val ())
470+ })
471+
472+ t .Run ("BITOP ONE basic" , func (t * testing.T ) {
473+ require .NoError (t , rdb .FlushDB (ctx ).Err ())
474+ // A=0xff, B=0x0f -> ONE = 0xf0 (bits set in exactly one key)
475+ Set2SetBit (t , rdb , ctx , "a" , []byte ("\xff " ))
476+ Set2SetBit (t , rdb , ctx , "b" , []byte ("\x0f " ))
477+ require .NoError (t , rdb .Do (ctx , "BITOP" , "ONE" , "dest" , "a" , "b" ).Err ())
478+ require .EqualValues (t , SimulateBitOp (ONE , []byte ("\xff " ), []byte ("\x0f " )), rdb .Get (ctx , "dest" ).Val ())
479+ })
480+
481+ t .Run ("BITOP ONE with three keys" , func (t * testing.T ) {
482+ require .NoError (t , rdb .FlushDB (ctx ).Err ())
483+ Set2SetBit (t , rdb , ctx , "a" , []byte ("\xff " ))
484+ Set2SetBit (t , rdb , ctx , "b" , []byte ("\x0f " ))
485+ Set2SetBit (t , rdb , ctx , "c" , []byte ("\xf0 " ))
486+ require .NoError (t , rdb .Do (ctx , "BITOP" , "ONE" , "dest" , "a" , "b" , "c" ).Err ())
487+ require .EqualValues (t , SimulateBitOp (ONE , []byte ("\xff " ), []byte ("\x0f " ), []byte ("\xf0 " )), rdb .Get (ctx , "dest" ).Val ())
488+ })
489+
490+ t .Run ("BITOP ONE single key returns same key" , func (t * testing.T ) {
491+ require .NoError (t , rdb .FlushDB (ctx ).Err ())
492+ Set2SetBit (t , rdb , ctx , "a" , []byte ("\xaa " ))
493+ require .NoError (t , rdb .Do (ctx , "BITOP" , "ONE" , "dest" , "a" ).Err ())
494+ require .EqualValues (t , SimulateBitOp (ONE , []byte ("\xaa " )), rdb .Get (ctx , "dest" ).Val ())
495+ })
496+
497+ t .Run ("BITOP new ops fuzzing" , func (t * testing.T ) {
498+ require .NoError (t , rdb .FlushDB (ctx ).Err ())
499+ for i := 0 ; i < 10 ; i ++ {
500+ numKeys := 2 + i % 3
501+ vec := make ([][]byte , numKeys )
502+ veckeys := make ([]string , numKeys )
503+ for k := 0 ; k < numKeys ; k ++ {
504+ vec [k ] = []byte (util .RandStringWithSeed (1 , 10 , util .Binary , int64 (i * 100 + k )))
505+ veckeys [k ] = fmt .Sprintf ("fuzz-%d-%d" , i , k )
506+ Set2SetBit (t , rdb , ctx , veckeys [k ], vec [k ])
507+ }
508+ doArgs := func (op string ) []interface {} {
509+ args := []interface {}{"BITOP" , op , "target" }
510+ for _ , k := range veckeys {
511+ args = append (args , k )
512+ }
513+ return args
514+ }
515+ require .NoError (t , rdb .Do (ctx , doArgs ("DIFF" )... ).Err ())
516+ require .EqualValues (t , SimulateBitOp (DIFF , vec ... ), rdb .Get (ctx , "target" ).Val ())
517+
518+ require .NoError (t , rdb .Do (ctx , doArgs ("DIFF1" )... ).Err ())
519+ require .EqualValues (t , SimulateBitOp (DIFF1 , vec ... ), rdb .Get (ctx , "target" ).Val ())
520+
521+ require .NoError (t , rdb .Do (ctx , doArgs ("ANDOR" )... ).Err ())
522+ require .EqualValues (t , SimulateBitOp (ANDOR , vec ... ), rdb .Get (ctx , "target" ).Val ())
523+
524+ require .NoError (t , rdb .Do (ctx , doArgs ("ONE" )... ).Err ())
525+ require .EqualValues (t , SimulateBitOp (ONE , vec ... ), rdb .Get (ctx , "target" ).Val ())
526+ }
527+ })
528+
360529 t .Run ("BITFIELD and BITFIELD_RO on string type" , func (t * testing.T ) {
361530 str := "zhe ge ren hen lan, shen me dou mei you liu xia."
362531 require .NoError (t , rdb .Set (ctx , "str" , str , 0 ).Err ())
0 commit comments