redis-bitops 0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,16 @@
1
+ $: << "../../lib"
2
+
3
+ require 'redis/bitops'
4
+
5
+ require_relative './support/bitmap_examples'
6
+
7
+ require 'pry'
8
+ require 'awesome_print'
9
+
10
+ RSpec.configure do |config|
11
+ config.after(:each, redis_cleanup: true) do
12
+ key_prefix = example.metadata[:redis_key_prefix] or raise "Specify the key prefix using RSpec metadata (e.g. redis_key_prefix: 'rsb:')."
13
+ keys = redis.keys("#{key_prefix}*")
14
+ redis.del(*keys) unless keys.empty?
15
+ end
16
+ end
@@ -0,0 +1,313 @@
1
+ shared_examples_for "a bitmap" do |creation_method|
2
+ let(:redis) { Redis.new }
3
+ let(:a) { redis.send(creation_method, "rsb:a") }
4
+ let(:b) { redis.send(creation_method, "rsb:b") }
5
+ let(:c) { redis.send(creation_method, "rsb:c") }
6
+ let(:result) { redis.send(creation_method, "rsb:output") }
7
+
8
+ describe "#[]" do
9
+ it "sets individual bits" do
10
+ b[0] = true
11
+ b[99] = true
12
+ b[0].should be_true
13
+ b[99].should be_true
14
+ end
15
+
16
+ it "resizes the bitmap as necessary" do
17
+ expect { b[10_000_000] = 1 }.to_not raise_error
18
+ b[10_000_000].should be_true
19
+ end
20
+
21
+ it "doesn't zeroes unset bits" do
22
+ max_bit_pos = 5_000
23
+ approx_percent_bits_set = 0.2
24
+
25
+ # TODO: There are definitely faster ways to generate the data.
26
+ Inf = 1.0/0.0 unless Kernel.const_defined? "Inf"
27
+ set = Set.new((0..Inf).lazy.map {|i| (rand * max_bit_pos).to_i}.take(approx_percent_bits_set * max_bit_pos))
28
+
29
+ set.each do |pos|
30
+ b[pos] = true
31
+ end
32
+
33
+ set.each do |pos|
34
+ b[pos].should be_true
35
+ end
36
+
37
+ (0..Inf).lazy.take(max_bit_pos).each do |pos|
38
+ b[pos].should eq set.include?(pos)
39
+ end
40
+ end
41
+ end
42
+
43
+ describe "#bitcount" do
44
+ it "returns the number of set bits" do
45
+ b.bitcount.should == 0
46
+ b[1] = true
47
+ b[2] = true
48
+ b[100] = true
49
+ b.bitcount.should == 3
50
+ end
51
+ end
52
+
53
+ describe "#operator &" do
54
+ it "returns a result query that can be materialized" do
55
+ a[0] = true
56
+ a[1] = true
57
+ a[2] = true
58
+ a[100] = true
59
+ a[110] = true
60
+
61
+ b[0] = true
62
+ b[100] = true
63
+
64
+ q = a & b
65
+
66
+ result << q
67
+ result.bitcount.should == 2
68
+ end
69
+
70
+ it "allows operator nesting" do
71
+ a[0] = true
72
+ a[1] = true
73
+ a[2] = true
74
+ a[100] = true
75
+ a[110] = true
76
+
77
+ b[0] = true
78
+ b[100] = true
79
+
80
+ c[0] = true
81
+
82
+ q = a & b & (c & a)
83
+
84
+ result << q
85
+ result.bitcount.should == 1
86
+ end
87
+ end
88
+
89
+ describe "#operator |" do
90
+ it "returns a result query that can be materialized" do
91
+ a[0] = true
92
+ a[1] = true
93
+ a[2] = true
94
+ a[100] = true
95
+
96
+ b[0] = true
97
+ b[100] = true
98
+ b[110] = true
99
+
100
+ q = a | b
101
+
102
+ result << q
103
+ result.bitcount.should == 5
104
+ end
105
+
106
+ it "allows operator nesting" do
107
+ a[0] = true
108
+ a[1] = true
109
+ a[2] = true
110
+ a[100] = true
111
+ a[110] = true
112
+
113
+ b[0] = true
114
+ b[100] = true
115
+
116
+ c[0] = true
117
+
118
+ q = a | b | (c | a)
119
+
120
+ result << q
121
+ result.bitcount.should == 5
122
+ end
123
+ end
124
+
125
+ describe "#operator ~" do
126
+ it "returns a result query that can be materialized" do
127
+ a[0] = true
128
+ a[1] = true
129
+ a[2] = true
130
+ a[100] = true
131
+
132
+ q = ~a
133
+
134
+ result << q
135
+ result[0].should be_false
136
+ result[1].should be_false
137
+ result[2].should be_false
138
+ result[3].should be_true
139
+ result[99].should be_true
140
+ result[100].should be_false
141
+ end
142
+
143
+ it "allows operator nesting" do
144
+ a[0] = true
145
+ a[1] = true
146
+ a[2] = true
147
+ a[100] = true
148
+ a[110] = true
149
+
150
+ b[0] = true
151
+ b[100] = true
152
+
153
+ c[0] = true
154
+
155
+ q = ~a | b & c
156
+
157
+ result << q
158
+ result[0].should be_true
159
+ result[1].should be_false
160
+ result[2].should be_false
161
+ result[3].should be_true
162
+ result[99].should be_true
163
+ result[100].should be_false
164
+ result[109].should be_true
165
+ result[110].should be_false
166
+ result[111].should be_true
167
+ end
168
+
169
+ # Commented out because this is how redis BITOP NOT works:
170
+ # it pads the results to the full byte thus messing up with
171
+ # the operation.
172
+
173
+ # it "returns result with the correct bitcount" do
174
+ # pending
175
+ # a[0] = true
176
+ # a[1] = true
177
+ # a[2] = true
178
+ # a[100] = true
179
+ #
180
+ # q = ~a
181
+ #
182
+ # result << q
183
+ # result.bitcount.should == 96
184
+ # end
185
+ end
186
+
187
+ describe "#operator ^" do
188
+ it "returns a result query that can be materialized" do
189
+ a[0] = true
190
+ a[1] = true
191
+ a[2] = true
192
+ a[100] = true
193
+
194
+ b[0] = true
195
+ b[100] = true
196
+ b[110] = true
197
+
198
+ q = a ^ b
199
+
200
+ result << q
201
+ result.bitcount.should == 3
202
+ end
203
+
204
+ it "allows operator nesting" do
205
+ a[0] = true
206
+ a[1] = true
207
+ a[2] = true
208
+ a[100] = true
209
+ a[110] = true
210
+
211
+ b[0] = true
212
+ b[100] = true
213
+
214
+ c[0] = true
215
+
216
+ q = a ^ (b ^ c)
217
+
218
+ result << q
219
+ result.bitcount.should == 4
220
+ end
221
+ end
222
+
223
+ describe "#delete!" do
224
+ it "removes all bitmap's keys" do
225
+ a[0] = true
226
+ a[10_000] = true
227
+ a.delete!
228
+ redis.keys("rsb:*").should be_empty
229
+ end
230
+
231
+ it "effectively sets all bitmap's keys to zero" do
232
+ a[0] = true
233
+ a[10_000] = true
234
+ a.delete!
235
+ a[0].should be_false
236
+ a[10_000].should be_false
237
+ b[0] = true
238
+ result << (a & b)
239
+ result.bitcount.should == 0
240
+ end
241
+ end
242
+
243
+ describe "#<<" do
244
+ before do
245
+ a[0] = true
246
+ a[1] = true
247
+ a[2] = true
248
+ a[3] = true
249
+
250
+ b[0] = true
251
+ b[1] = true
252
+ b[2] = true
253
+
254
+ c[0] = true
255
+ c[1] = true
256
+ end
257
+
258
+ after do
259
+ @temp.delete! if @temp
260
+ end
261
+
262
+ it "materializes an arbitrarily-complicated expression" do
263
+ result << (a & (a & b) | c & b & a)
264
+ result.bitcount.should == 3
265
+ end
266
+
267
+ it "is lazy-invoked when expression is evaluated" do
268
+ result = (a & (a & b) | c & b & a)
269
+ result.should be_a Redis::Bitops::Queries::BinaryOperator
270
+ @temp = result
271
+ result.bitcount.should == 3
272
+ end
273
+
274
+ it "takes into account modifications made to the result" do
275
+ output = (a & (a & b) | c & b & a)
276
+ output[100] = true
277
+ @temp = output
278
+ result << output
279
+ result.bitcount.should == 4
280
+ end
281
+ end
282
+
283
+ describe "#copy_to" do
284
+ it "overrides the target bitmap" do
285
+ # Fix expression with bits set using [] after evaluation doesn't materialize the newly set bits.
286
+ result[1000] = true
287
+ a[0] = true
288
+ a[1] = true
289
+ a.copy_to(result)
290
+ result.bitcount.should == a.bitcount
291
+ result[1000].should be_false
292
+ end
293
+ end
294
+ end
295
+
296
+ shared_examples_for "a bitmap factory method" do |creation_method, bitmap_class|
297
+ let(:redis) { Redis.new }
298
+
299
+ after do
300
+ @bitmap.delete! if @bitmap
301
+ end
302
+
303
+ it "creates a new bitmap" do
304
+ @bitmap = redis.send(creation_method, "rsb:xxx")
305
+ @bitmap.should be_a bitmap_class
306
+ end
307
+
308
+ it "doesn't add keys until the bitmap is modified" do
309
+ @bitmap = redis.send(creation_method, "rsb:xxx")
310
+ expect { @bitmap }.to_not change { redis.keys.size }
311
+ expect { @bitmap[1] = true }.to change { redis.keys.size }
312
+ end
313
+ end
metadata ADDED
@@ -0,0 +1,173 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redis-bitops
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.2'
5
+ platform: ruby
6
+ authors:
7
+ - Martin Bilski
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: redis
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: hiredis
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: awesome_print
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard-rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: byebug
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: pry-byebug
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description: Optimized operations on Redis bitmaps using built-in Ruby operators.
126
+ Supports sparse bitmaps to preserve storage.
127
+ email: gyamtso@gmail.com
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - MIT-LICENSE
133
+ - README.md
134
+ - lib/redis/bitops.rb
135
+ - lib/redis/bitops/bitmap.rb
136
+ - lib/redis/bitops/configuration.rb
137
+ - lib/redis/bitops/queries/binary_operator.rb
138
+ - lib/redis/bitops/queries/lazy_evaluation.rb
139
+ - lib/redis/bitops/queries/materialization_helpers.rb
140
+ - lib/redis/bitops/queries/tree_building_helpers.rb
141
+ - lib/redis/bitops/queries/unary_operator.rb
142
+ - lib/redis/bitops/sparse_bitmap.rb
143
+ - spec/redis/bitops/bitmap_spec.rb
144
+ - spec/redis/bitops/queries/binary_operator_spec.rb
145
+ - spec/redis/bitops/queries/unary_operator_spec.rb
146
+ - spec/redis/bitops/sparse_bitmap_spec.rb
147
+ - spec/spec_helper.rb
148
+ - spec/support/bitmap_examples.rb
149
+ homepage: http://github.com/bilus/redis-bitops
150
+ licenses:
151
+ - MIT
152
+ metadata: {}
153
+ post_install_message:
154
+ rdoc_options: []
155
+ require_paths:
156
+ - lib
157
+ required_ruby_version: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ version: '0'
162
+ required_rubygems_version: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ requirements: []
168
+ rubyforge_project:
169
+ rubygems_version: 2.2.2
170
+ signing_key:
171
+ specification_version: 4
172
+ summary: Bitmap and sparse bitmap operations for Redis.
173
+ test_files: []