2
0

bitops.tcl 11 KB

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