1# Compare Redis commadns against Tcl implementations of the same commands. 2proc count_bits s { 3 binary scan $s b* bits 4 string length [regsub -all {0} $bits {}] 5} 6 7proc simulate_bit_op {op args} { 8 set maxlen 0 9 set j 0 10 set count [llength $args] 11 foreach a $args { 12 binary scan $a b* bits 13 set b($j) $bits 14 if {[string length $bits] > $maxlen} { 15 set maxlen [string length $bits] 16 } 17 incr j 18 } 19 for {set j 0} {$j < $count} {incr j} { 20 if {[string length $b($j)] < $maxlen} { 21 append b($j) [string repeat 0 [expr $maxlen-[string length $b($j)]]] 22 } 23 } 24 set out {} 25 for {set x 0} {$x < $maxlen} {incr x} { 26 set bit [string range $b(0) $x $x] 27 if {$op eq {not}} {set bit [expr {!$bit}]} 28 for {set j 1} {$j < $count} {incr j} { 29 set bit2 [string range $b($j) $x $x] 30 switch $op { 31 and {set bit [expr {$bit & $bit2}]} 32 or {set bit [expr {$bit | $bit2}]} 33 xor {set bit [expr {$bit ^ $bit2}]} 34 } 35 } 36 append out $bit 37 } 38 binary format b* $out 39} 40 41start_server {tags {"bitops"}} { 42 test {BITCOUNT returns 0 against non existing key} { 43 r bitcount no-key 44 } 0 45 46 catch {unset num} 47 foreach vec [list "" "\xaa" "\x00\x00\xff" "foobar" "123"] { 48 incr num 49 test "BITCOUNT against test vector #$num" { 50 r set str $vec 51 assert {[r bitcount str] == [count_bits $vec]} 52 } 53 } 54 55 test {BITCOUNT fuzzing without start/end} { 56 for {set j 0} {$j < 100} {incr j} { 57 set str [randstring 0 3000] 58 r set str $str 59 assert {[r bitcount str] == [count_bits $str]} 60 } 61 } 62 63 test {BITCOUNT fuzzing with start/end} { 64 for {set j 0} {$j < 100} {incr j} { 65 set str [randstring 0 3000] 66 r set str $str 67 set l [string length $str] 68 set start [randomInt $l] 69 set end [randomInt $l] 70 if {$start > $end} { 71 lassign [list $end $start] start end 72 } 73 assert {[r bitcount str $start $end] == [count_bits [string range $str $start $end]]} 74 } 75 } 76 77 test {BITCOUNT with start, end} { 78 r set s "foobar" 79 assert_equal [r bitcount s 0 -1] [count_bits "foobar"] 80 assert_equal [r bitcount s 1 -2] [count_bits "ooba"] 81 assert_equal [r bitcount s -2 1] [count_bits ""] 82 assert_equal [r bitcount s 0 1000] [count_bits "foobar"] 83 } 84 85 test {BITCOUNT syntax error #1} { 86 catch {r bitcount s 0} e 87 set e 88 } {ERR*syntax*} 89 90 test {BITCOUNT regression test for github issue #582} { 91 r del str 92 r setbit foo 0 1 93 if {[catch {r bitcount foo 0 4294967296} e]} { 94 assert_match {*ERR*out of range*} $e 95 set _ 1 96 } else { 97 set e 98 } 99 } {1} 100 101 test {BITCOUNT misaligned prefix} { 102 r del str 103 r set str ab 104 r bitcount str 1 -1 105 } {3} 106 107 test {BITCOUNT misaligned prefix + full words + remainder} { 108 r del str 109 r set str __PPxxxxxxxxxxxxxxxxRR__ 110 r bitcount str 2 -3 111 } {74} 112 113 test {BITOP NOT (empty string)} { 114 r set s "" 115 r bitop not dest s 116 r get dest 117 } {} 118 119 test {BITOP NOT (known string)} { 120 r set s "\xaa\x00\xff\x55" 121 r bitop not dest s 122 r get dest 123 } "\x55\xff\x00\xaa" 124 125 test {BITOP where dest and target are the same key} { 126 r set s "\xaa\x00\xff\x55" 127 r bitop not s s 128 r get s 129 } "\x55\xff\x00\xaa" 130 131 test {BITOP AND|OR|XOR don't change the string with single input key} { 132 r set a "\x01\x02\xff" 133 r bitop and res1 a 134 r bitop or res2 a 135 r bitop xor res3 a 136 list [r get res1] [r get res2] [r get res3] 137 } [list "\x01\x02\xff" "\x01\x02\xff" "\x01\x02\xff"] 138 139 test {BITOP missing key is considered a stream of zero} { 140 r set a "\x01\x02\xff" 141 r bitop and res1 no-suck-key a 142 r bitop or res2 no-suck-key a no-such-key 143 r bitop xor res3 no-such-key a 144 list [r get res1] [r get res2] [r get res3] 145 } [list "\x00\x00\x00" "\x01\x02\xff" "\x01\x02\xff"] 146 147 test {BITOP shorter keys are zero-padded to the key with max length} { 148 r set a "\x01\x02\xff\xff" 149 r set b "\x01\x02\xff" 150 r bitop and res1 a b 151 r bitop or res2 a b 152 r bitop xor res3 a b 153 list [r get res1] [r get res2] [r get res3] 154 } [list "\x01\x02\xff\x00" "\x01\x02\xff\xff" "\x00\x00\x00\xff"] 155 156 foreach op {and or xor} { 157 test "BITOP $op fuzzing" { 158 for {set i 0} {$i < 10} {incr i} { 159 r flushall 160 set vec {} 161 set veckeys {} 162 set numvec [expr {[randomInt 10]+1}] 163 for {set j 0} {$j < $numvec} {incr j} { 164 set str [randstring 0 1000] 165 lappend vec $str 166 lappend veckeys vector_$j 167 r set vector_$j $str 168 } 169 r bitop $op target {*}$veckeys 170 assert_equal [r get target] [simulate_bit_op $op {*}$vec] 171 } 172 } 173 } 174 175 test {BITOP NOT fuzzing} { 176 for {set i 0} {$i < 10} {incr i} { 177 r flushall 178 set str [randstring 0 1000] 179 r set str $str 180 r bitop not target str 181 assert_equal [r get target] [simulate_bit_op not $str] 182 } 183 } 184 185 test {BITOP with integer encoded source objects} { 186 r set a 1 187 r set b 2 188 r bitop xor dest a b a 189 r get dest 190 } {2} 191 192 test {BITOP with non string source key} { 193 r del c 194 r set a 1 195 r set b 2 196 r lpush c foo 197 catch {r bitop xor dest a b c d} e 198 set e 199 } {WRONGTYPE*} 200 201 test {BITOP with empty string after non empty string (issue #529)} { 202 r flushdb 203 r set a "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 204 r bitop or x a b 205 } {32} 206 207 test {BITPOS bit=0 with empty key returns 0} { 208 r del str 209 r bitpos str 0 210 } {0} 211 212 test {BITPOS bit=1 with empty key returns -1} { 213 r del str 214 r bitpos str 1 215 } {-1} 216 217 test {BITPOS bit=0 with string less than 1 word works} { 218 r set str "\xff\xf0\x00" 219 r bitpos str 0 220 } {12} 221 222 test {BITPOS bit=1 with string less than 1 word works} { 223 r set str "\x00\x0f\x00" 224 r bitpos str 1 225 } {12} 226 227 test {BITPOS bit=0 starting at unaligned address} { 228 r set str "\xff\xf0\x00" 229 r bitpos str 0 1 230 } {12} 231 232 test {BITPOS bit=1 starting at unaligned address} { 233 r set str "\x00\x0f\xff" 234 r bitpos str 1 1 235 } {12} 236 237 test {BITPOS bit=0 unaligned+full word+reminder} { 238 r del str 239 r set str "\xff\xff\xff" ; # Prefix 240 # Followed by two (or four in 32 bit systems) full words 241 r append str "\xff\xff\xff\xff\xff\xff\xff\xff" 242 r append str "\xff\xff\xff\xff\xff\xff\xff\xff" 243 r append str "\xff\xff\xff\xff\xff\xff\xff\xff" 244 # First zero bit. 245 r append str "\x0f" 246 assert {[r bitpos str 0] == 216} 247 assert {[r bitpos str 0 1] == 216} 248 assert {[r bitpos str 0 2] == 216} 249 assert {[r bitpos str 0 3] == 216} 250 assert {[r bitpos str 0 4] == 216} 251 assert {[r bitpos str 0 5] == 216} 252 assert {[r bitpos str 0 6] == 216} 253 assert {[r bitpos str 0 7] == 216} 254 assert {[r bitpos str 0 8] == 216} 255 } 256 257 test {BITPOS bit=1 unaligned+full word+reminder} { 258 r del str 259 r set str "\x00\x00\x00" ; # Prefix 260 # Followed by two (or four in 32 bit systems) full words 261 r append str "\x00\x00\x00\x00\x00\x00\x00\x00" 262 r append str "\x00\x00\x00\x00\x00\x00\x00\x00" 263 r append str "\x00\x00\x00\x00\x00\x00\x00\x00" 264 # First zero bit. 265 r append str "\xf0" 266 assert {[r bitpos str 1] == 216} 267 assert {[r bitpos str 1 1] == 216} 268 assert {[r bitpos str 1 2] == 216} 269 assert {[r bitpos str 1 3] == 216} 270 assert {[r bitpos str 1 4] == 216} 271 assert {[r bitpos str 1 5] == 216} 272 assert {[r bitpos str 1 6] == 216} 273 assert {[r bitpos str 1 7] == 216} 274 assert {[r bitpos str 1 8] == 216} 275 } 276 277 test {BITPOS bit=1 returns -1 if string is all 0 bits} { 278 r set str "" 279 for {set j 0} {$j < 20} {incr j} { 280 assert {[r bitpos str 1] == -1} 281 r append str "\x00" 282 } 283 } 284 285 test {BITPOS bit=0 works with intervals} { 286 r set str "\x00\xff\x00" 287 assert {[r bitpos str 0 0 -1] == 0} 288 assert {[r bitpos str 0 1 -1] == 16} 289 assert {[r bitpos str 0 2 -1] == 16} 290 assert {[r bitpos str 0 2 200] == 16} 291 assert {[r bitpos str 0 1 1] == -1} 292 } 293 294 test {BITPOS bit=1 works with intervals} { 295 r set str "\x00\xff\x00" 296 assert {[r bitpos str 1 0 -1] == 8} 297 assert {[r bitpos str 1 1 -1] == 8} 298 assert {[r bitpos str 1 2 -1] == -1} 299 assert {[r bitpos str 1 2 200] == -1} 300 assert {[r bitpos str 1 1 1] == 8} 301 } 302 303 test {BITPOS bit=0 changes behavior if end is given} { 304 r set str "\xff\xff\xff" 305 assert {[r bitpos str 0] == 24} 306 assert {[r bitpos str 0 0] == 24} 307 assert {[r bitpos str 0 0 -1] == -1} 308 } 309 310 test {BITPOS bit=1 fuzzy testing using SETBIT} { 311 r del str 312 set max 524288; # 64k 313 set first_one_pos -1 314 for {set j 0} {$j < 1000} {incr j} { 315 assert {[r bitpos str 1] == $first_one_pos} 316 set pos [randomInt $max] 317 r setbit str $pos 1 318 if {$first_one_pos == -1 || $first_one_pos > $pos} { 319 # Update the position of the first 1 bit in the array 320 # if the bit we set is on the left of the previous one. 321 set first_one_pos $pos 322 } 323 } 324 } 325 326 test {BITPOS bit=0 fuzzy testing using SETBIT} { 327 set max 524288; # 64k 328 set first_zero_pos $max 329 r set str [string repeat "\xff" [expr $max/8]] 330 for {set j 0} {$j < 1000} {incr j} { 331 assert {[r bitpos str 0] == $first_zero_pos} 332 set pos [randomInt $max] 333 r setbit str $pos 0 334 if {$first_zero_pos > $pos} { 335 # Update the position of the first 0 bit in the array 336 # if the bit we clear is on the left of the previous one. 337 set first_zero_pos $pos 338 } 339 } 340 } 341} 342