xref: /redis-3.2.3/tests/unit/bitops.tcl (revision 44acd50a)
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