1
0

bitops.tcl 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. # Compare Redis commands 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. test {BITCOUNT returns 0 with out of range indexes} {
  44. r set str "xxxx"
  45. r bitcount str 4 10
  46. } 0
  47. test {BITCOUNT returns 0 with negative indexes where start > end} {
  48. r set str "xxxx"
  49. r bitcount str -6 -7
  50. } 0
  51. catch {unset num}
  52. foreach vec [list "" "\xaa" "\x00\x00\xff" "foobar" "123"] {
  53. incr num
  54. test "BITCOUNT against test vector #$num" {
  55. r set str $vec
  56. assert {[r bitcount str] == [count_bits $vec]}
  57. }
  58. }
  59. test {BITCOUNT fuzzing without start/end} {
  60. for {set j 0} {$j < 100} {incr j} {
  61. set str [randstring 0 3000]
  62. r set str $str
  63. assert {[r bitcount str] == [count_bits $str]}
  64. }
  65. }
  66. test {BITCOUNT fuzzing with start/end} {
  67. for {set j 0} {$j < 100} {incr j} {
  68. set str [randstring 0 3000]
  69. r set str $str
  70. set l [string length $str]
  71. set start [randomInt $l]
  72. set end [randomInt $l]
  73. if {$start > $end} {
  74. lassign [list $end $start] start end
  75. }
  76. assert {[r bitcount str $start $end] == [count_bits [string range $str $start $end]]}
  77. }
  78. }
  79. test {BITCOUNT with start, end} {
  80. r set s "foobar"
  81. assert_equal [r bitcount s 0 -1] [count_bits "foobar"]
  82. assert_equal [r bitcount s 1 -2] [count_bits "ooba"]
  83. assert_equal [r bitcount s -2 1] [count_bits ""]
  84. assert_equal [r bitcount s 0 1000] [count_bits "foobar"]
  85. }
  86. test {BITCOUNT syntax error #1} {
  87. catch {r bitcount s 0} e
  88. set e
  89. } {ERR*syntax*}
  90. test {BITCOUNT regression test for github issue #582} {
  91. r del foo
  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. test {BITCOUNT misaligned prefix} {
  101. r del str
  102. r set str ab
  103. r bitcount str 1 -1
  104. } {3}
  105. test {BITCOUNT misaligned prefix + full words + remainder} {
  106. r del str
  107. r set str __PPxxxxxxxxxxxxxxxxRR__
  108. r bitcount str 2 -3
  109. } {74}
  110. test {BITOP NOT (empty string)} {
  111. r set s{t} ""
  112. r bitop not dest{t} s{t}
  113. r get dest{t}
  114. } {}
  115. test {BITOP NOT (known string)} {
  116. r set s{t} "\xaa\x00\xff\x55"
  117. r bitop not dest{t} s{t}
  118. r get dest{t}
  119. } "\x55\xff\x00\xaa"
  120. test {BITOP where dest and target are the same key} {
  121. r set s "\xaa\x00\xff\x55"
  122. r bitop not s s
  123. r get s
  124. } "\x55\xff\x00\xaa"
  125. test {BITOP AND|OR|XOR don't change the string with single input key} {
  126. r set a{t} "\x01\x02\xff"
  127. r bitop and res1{t} a{t}
  128. r bitop or res2{t} a{t}
  129. r bitop xor res3{t} a{t}
  130. list [r get res1{t}] [r get res2{t}] [r get res3{t}]
  131. } [list "\x01\x02\xff" "\x01\x02\xff" "\x01\x02\xff"]
  132. test {BITOP missing key is considered a stream of zero} {
  133. r set a{t} "\x01\x02\xff"
  134. r bitop and res1{t} no-suck-key{t} a{t}
  135. r bitop or res2{t} no-suck-key{t} a{t} no-such-key{t}
  136. r bitop xor res3{t} no-such-key{t} a{t}
  137. list [r get res1{t}] [r get res2{t}] [r get res3{t}]
  138. } [list "\x00\x00\x00" "\x01\x02\xff" "\x01\x02\xff"]
  139. test {BITOP shorter keys are zero-padded to the key with max length} {
  140. r set a{t} "\x01\x02\xff\xff"
  141. r set b{t} "\x01\x02\xff"
  142. r bitop and res1{t} a{t} b{t}
  143. r bitop or res2{t} a{t} b{t}
  144. r bitop xor res3{t} a{t} b{t}
  145. list [r get res1{t}] [r get res2{t}] [r get res3{t}]
  146. } [list "\x01\x02\xff\x00" "\x01\x02\xff\xff" "\x00\x00\x00\xff"]
  147. foreach op {and or xor} {
  148. test "BITOP $op fuzzing" {
  149. for {set i 0} {$i < 10} {incr i} {
  150. r flushall
  151. set vec {}
  152. set veckeys {}
  153. set numvec [expr {[randomInt 10]+1}]
  154. for {set j 0} {$j < $numvec} {incr j} {
  155. set str [randstring 0 1000]
  156. lappend vec $str
  157. lappend veckeys vector_$j{t}
  158. r set vector_$j{t} $str
  159. }
  160. r bitop $op target{t} {*}$veckeys
  161. assert_equal [r get target{t}] [simulate_bit_op $op {*}$vec]
  162. }
  163. }
  164. }
  165. test {BITOP NOT fuzzing} {
  166. for {set i 0} {$i < 10} {incr i} {
  167. r flushall
  168. set str [randstring 0 1000]
  169. r set str{t} $str
  170. r bitop not target{t} str{t}
  171. assert_equal [r get target{t}] [simulate_bit_op not $str]
  172. }
  173. }
  174. test {BITOP with integer encoded source objects} {
  175. r set a{t} 1
  176. r set b{t} 2
  177. r bitop xor dest{t} a{t} b{t} a{t}
  178. r get dest{t}
  179. } {2}
  180. test {BITOP with non string source key} {
  181. r del c{t}
  182. r set a{t} 1
  183. r set b{t} 2
  184. r lpush c{t} foo
  185. catch {r bitop xor dest{t} a{t} b{t} c{t} d{t}} e
  186. set e
  187. } {WRONGTYPE*}
  188. test {BITOP with empty string after non empty string (issue #529)} {
  189. r flushdb
  190. r set a{t} "\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"
  191. r bitop or x{t} a{t} b{t}
  192. } {32}
  193. test {BITPOS bit=0 with empty key returns 0} {
  194. r del str
  195. r bitpos str 0
  196. } {0}
  197. test {BITPOS bit=1 with empty key returns -1} {
  198. r del str
  199. r bitpos str 1
  200. } {-1}
  201. test {BITPOS bit=0 with string less than 1 word works} {
  202. r set str "\xff\xf0\x00"
  203. r bitpos str 0
  204. } {12}
  205. test {BITPOS bit=1 with string less than 1 word works} {
  206. r set str "\x00\x0f\x00"
  207. r bitpos str 1
  208. } {12}
  209. test {BITPOS bit=0 starting at unaligned address} {
  210. r set str "\xff\xf0\x00"
  211. r bitpos str 0 1
  212. } {12}
  213. test {BITPOS bit=1 starting at unaligned address} {
  214. r set str "\x00\x0f\xff"
  215. r bitpos str 1 1
  216. } {12}
  217. test {BITPOS bit=0 unaligned+full word+reminder} {
  218. r del str
  219. r set str "\xff\xff\xff" ; # Prefix
  220. # Followed by two (or four in 32 bit systems) full words
  221. r append str "\xff\xff\xff\xff\xff\xff\xff\xff"
  222. r append str "\xff\xff\xff\xff\xff\xff\xff\xff"
  223. r append str "\xff\xff\xff\xff\xff\xff\xff\xff"
  224. # First zero bit.
  225. r append str "\x0f"
  226. assert {[r bitpos str 0] == 216}
  227. assert {[r bitpos str 0 1] == 216}
  228. assert {[r bitpos str 0 2] == 216}
  229. assert {[r bitpos str 0 3] == 216}
  230. assert {[r bitpos str 0 4] == 216}
  231. assert {[r bitpos str 0 5] == 216}
  232. assert {[r bitpos str 0 6] == 216}
  233. assert {[r bitpos str 0 7] == 216}
  234. assert {[r bitpos str 0 8] == 216}
  235. }
  236. test {BITPOS bit=1 unaligned+full word+reminder} {
  237. r del str
  238. r set str "\x00\x00\x00" ; # Prefix
  239. # Followed by two (or four in 32 bit systems) full words
  240. r append str "\x00\x00\x00\x00\x00\x00\x00\x00"
  241. r append str "\x00\x00\x00\x00\x00\x00\x00\x00"
  242. r append str "\x00\x00\x00\x00\x00\x00\x00\x00"
  243. # First zero bit.
  244. r append str "\xf0"
  245. assert {[r bitpos str 1] == 216}
  246. assert {[r bitpos str 1 1] == 216}
  247. assert {[r bitpos str 1 2] == 216}
  248. assert {[r bitpos str 1 3] == 216}
  249. assert {[r bitpos str 1 4] == 216}
  250. assert {[r bitpos str 1 5] == 216}
  251. assert {[r bitpos str 1 6] == 216}
  252. assert {[r bitpos str 1 7] == 216}
  253. assert {[r bitpos str 1 8] == 216}
  254. }
  255. test {BITPOS bit=1 returns -1 if string is all 0 bits} {
  256. r set str ""
  257. for {set j 0} {$j < 20} {incr j} {
  258. assert {[r bitpos str 1] == -1}
  259. r append str "\x00"
  260. }
  261. }
  262. test {BITPOS bit=0 works with intervals} {
  263. r set str "\x00\xff\x00"
  264. assert {[r bitpos str 0 0 -1] == 0}
  265. assert {[r bitpos str 0 1 -1] == 16}
  266. assert {[r bitpos str 0 2 -1] == 16}
  267. assert {[r bitpos str 0 2 200] == 16}
  268. assert {[r bitpos str 0 1 1] == -1}
  269. }
  270. test {BITPOS bit=1 works with intervals} {
  271. r set str "\x00\xff\x00"
  272. assert {[r bitpos str 1 0 -1] == 8}
  273. assert {[r bitpos str 1 1 -1] == 8}
  274. assert {[r bitpos str 1 2 -1] == -1}
  275. assert {[r bitpos str 1 2 200] == -1}
  276. assert {[r bitpos str 1 1 1] == 8}
  277. }
  278. test {BITPOS bit=0 changes behavior if end is given} {
  279. r set str "\xff\xff\xff"
  280. assert {[r bitpos str 0] == 24}
  281. assert {[r bitpos str 0 0] == 24}
  282. assert {[r bitpos str 0 0 -1] == -1}
  283. }
  284. test {SETBIT/BITFIELD only increase dirty when the value changed} {
  285. r del foo{t} foo2{t} foo3{t}
  286. set dirty [s rdb_changes_since_last_save]
  287. # Create a new key, always increase the dirty.
  288. r setbit foo{t} 0 0
  289. r bitfield foo2{t} set i5 0 0
  290. set dirty2 [s rdb_changes_since_last_save]
  291. assert {$dirty2 == $dirty + 2}
  292. # No change.
  293. r setbit foo{t} 0 0
  294. r bitfield foo2{t} set i5 0 0
  295. set dirty3 [s rdb_changes_since_last_save]
  296. assert {$dirty3 == $dirty2}
  297. # Do a change and a no change.
  298. r setbit foo{t} 0 1
  299. r setbit foo{t} 0 1
  300. r setbit foo{t} 0 0
  301. r setbit foo{t} 0 0
  302. r bitfield foo2{t} set i5 0 1
  303. r bitfield foo2{t} set i5 0 1
  304. r bitfield foo2{t} set i5 0 0
  305. r bitfield foo2{t} set i5 0 0
  306. set dirty4 [s rdb_changes_since_last_save]
  307. assert {$dirty4 == $dirty3 + 4}
  308. # BITFIELD INCRBY always increase dirty.
  309. r bitfield foo3{t} incrby i5 0 1
  310. r bitfield foo3{t} incrby i5 0 1
  311. set dirty5 [s rdb_changes_since_last_save]
  312. assert {$dirty5 == $dirty4 + 2}
  313. }
  314. test {BITPOS bit=1 fuzzy testing using SETBIT} {
  315. r del str
  316. set max 524288; # 64k
  317. set first_one_pos -1
  318. for {set j 0} {$j < 1000} {incr j} {
  319. assert {[r bitpos str 1] == $first_one_pos}
  320. set pos [randomInt $max]
  321. r setbit str $pos 1
  322. if {$first_one_pos == -1 || $first_one_pos > $pos} {
  323. # Update the position of the first 1 bit in the array
  324. # if the bit we set is on the left of the previous one.
  325. set first_one_pos $pos
  326. }
  327. }
  328. }
  329. test {BITPOS bit=0 fuzzy testing using SETBIT} {
  330. set max 524288; # 64k
  331. set first_zero_pos $max
  332. r set str [string repeat "\xff" [expr $max/8]]
  333. for {set j 0} {$j < 1000} {incr j} {
  334. assert {[r bitpos str 0] == $first_zero_pos}
  335. set pos [randomInt $max]
  336. r setbit str $pos 0
  337. if {$first_zero_pos > $pos} {
  338. # Update the position of the first 0 bit in the array
  339. # if the bit we clear is on the left of the previous one.
  340. set first_zero_pos $pos
  341. }
  342. }
  343. }
  344. test "BIT pos larger than UINT_MAX" {
  345. set bytes [expr (1 << 29) + 1]
  346. set bitpos [expr (1 << 32)]
  347. set oldval [lindex [r config get proto-max-bulk-len] 1]
  348. r config set proto-max-bulk-len $bytes
  349. r setbit mykey $bitpos 1
  350. assert_equal $bytes [r strlen mykey]
  351. assert_equal 1 [r getbit mykey $bitpos]
  352. assert_equal [list 128 128 -1] [r bitfield mykey get u8 $bitpos set u8 $bitpos 255 get i8 $bitpos]
  353. assert_equal $bitpos [r bitpos mykey 1]
  354. assert_equal $bitpos [r bitpos mykey 1 [expr $bytes - 1]]
  355. if {$::accurate} {
  356. # set all bits to 1
  357. set mega [expr (1 << 23)]
  358. set part [string repeat "\xFF" $mega]
  359. for {set i 0} {$i < 64} {incr i} {
  360. r setrange mykey [expr $i * $mega] $part
  361. }
  362. r setrange mykey [expr $bytes - 1] "\xFF"
  363. assert_equal [expr $bitpos + 8] [r bitcount mykey]
  364. assert_equal -1 [r bitpos mykey 0 0 [expr $bytes - 1]]
  365. }
  366. r config set proto-max-bulk-len $oldval
  367. r del mykey
  368. } {1} {large-memory}
  369. }