bloom_filter 0.6.1 → 0.6.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +1 -0
- data/VERSION +1 -1
- data/bloom_filter.gemspec +6 -3
- data/lib/bloom_filter/bit_vector.rb +48 -0
- data/lib/bloom_filter/client.rb +4 -3
- data/lib/bloom_filter/protocol.rb +1 -2
- data/lib/bloom_filter/server.rb +5 -1
- data/test/bloom_filter_bit_vector_test.rb +44 -0
- data/test/bloom_filter_client_test.rb +13 -7
- data/test/bloom_filter_server_test.rb +11 -9
- metadata +5 -2
data/README.md
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.6.
|
1
|
+
0.6.2
|
data/bloom_filter.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{bloom_filter}
|
8
|
-
s.version = "0.6.
|
8
|
+
s.version = "0.6.2"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Arya Asemanfar"]
|
12
|
-
s.date = %q{2010-01-
|
12
|
+
s.date = %q{2010-01-27}
|
13
13
|
s.default_executable = %q{bloom_filter_server}
|
14
14
|
s.description = %q{}
|
15
15
|
s.email = %q{misterfunnyarsal@gmail.com}
|
@@ -28,9 +28,11 @@ Gem::Specification.new do |s|
|
|
28
28
|
"bin/bloom_filter_server",
|
29
29
|
"bloom_filter.gemspec",
|
30
30
|
"lib/bloom_filter.rb",
|
31
|
+
"lib/bloom_filter/bit_vector.rb",
|
31
32
|
"lib/bloom_filter/client.rb",
|
32
33
|
"lib/bloom_filter/protocol.rb",
|
33
34
|
"lib/bloom_filter/server.rb",
|
35
|
+
"test/bloom_filter_bit_vector_test.rb",
|
34
36
|
"test/bloom_filter_client_test.rb",
|
35
37
|
"test/bloom_filter_server_test.rb",
|
36
38
|
"test/bloom_filter_test.rb",
|
@@ -42,7 +44,8 @@ Gem::Specification.new do |s|
|
|
42
44
|
s.rubygems_version = %q{1.3.5}
|
43
45
|
s.summary = %q{A simple BloomFilter implementation, usable in-process or as an EventMachine daemon.}
|
44
46
|
s.test_files = [
|
45
|
-
"test/
|
47
|
+
"test/bloom_filter_bit_vector_test.rb",
|
48
|
+
"test/bloom_filter_client_test.rb",
|
46
49
|
"test/bloom_filter_server_test.rb",
|
47
50
|
"test/bloom_filter_test.rb",
|
48
51
|
"test/test_helper.rb"
|
@@ -0,0 +1,48 @@
|
|
1
|
+
class BloomFilter
|
2
|
+
class BitVector
|
3
|
+
attr_reader :size
|
4
|
+
|
5
|
+
def initialize(bits, from_str = nil)
|
6
|
+
@bytes = Array.new((bits.to_f / 8).ceil, 0)
|
7
|
+
@size = bits
|
8
|
+
|
9
|
+
if from_str
|
10
|
+
from_str.size.times do |i|
|
11
|
+
@bytes[i] = from_str[i].ord
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def [](index)
|
17
|
+
(@bytes[index.to_i / 8] >> (index % 8)) & 0b1
|
18
|
+
end
|
19
|
+
|
20
|
+
def []=(index, value)
|
21
|
+
new_value = value && value != 0 ? 1 : 0
|
22
|
+
current_value = self[index]
|
23
|
+
if current_value != new_value
|
24
|
+
if new_value == 0
|
25
|
+
@bytes[index.to_i / 8] -= 1 << (index % 8)
|
26
|
+
else
|
27
|
+
@bytes[index.to_i / 8] += 1 << (index % 8)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_s
|
33
|
+
str = ""
|
34
|
+
@bytes.each { |byte| str << byte.chr }
|
35
|
+
str
|
36
|
+
end
|
37
|
+
|
38
|
+
def eql?(o)
|
39
|
+
return false unless o.size == self.size
|
40
|
+
self.size.times do |i|
|
41
|
+
return false unless self[i] == o[i]
|
42
|
+
end
|
43
|
+
true
|
44
|
+
end
|
45
|
+
alias_method :==, :eql?
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
data/lib/bloom_filter/client.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
require 'socket'
|
2
2
|
require 'bloom_filter/protocol'
|
3
|
+
require 'bloom_filter/bit_vector'
|
3
4
|
|
4
5
|
class BloomFilter
|
5
6
|
class Timeout < StandardError; end
|
6
7
|
|
7
8
|
class Client
|
8
|
-
|
9
9
|
PACK_N = "N"
|
10
|
+
|
10
11
|
def initialize(host, port, options = {})
|
11
12
|
@host, @port = host, port
|
12
13
|
@timeout = options[:timeout]
|
@@ -40,10 +41,10 @@ class BloomFilter
|
|
40
41
|
@socket.write("#{[elements.size + 1].pack(PACK_N)}#{Protocol::INCLUDE_MANY}#{elements}")
|
41
42
|
|
42
43
|
timeout_or_default(els) do
|
43
|
-
response = @socket.read(@socket.read(4).unpack(PACK_N).first)
|
44
|
+
response = BitVector.new(els.size, @socket.read(@socket.read(4).unpack(PACK_N).first))
|
44
45
|
result = []
|
45
46
|
els.size.times do |i|
|
46
|
-
result << els[i] if response[i
|
47
|
+
result << els[i] if response[i] == 1
|
47
48
|
end
|
48
49
|
result
|
49
50
|
end
|
data/lib/bloom_filter/server.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'bloom_filter'
|
2
2
|
require 'bloom_filter/protocol'
|
3
|
+
require 'bloom_filter/bit_vector'
|
3
4
|
require 'eventmachine'
|
4
5
|
|
5
6
|
|
@@ -86,7 +87,10 @@ class BloomFilter
|
|
86
87
|
end
|
87
88
|
|
88
89
|
def write_response(response)
|
89
|
-
|
90
|
+
response = Array(response)
|
91
|
+
vector = BitVector.new(response.size)
|
92
|
+
response.each_with_index { |t, index| vector[index] = t }
|
93
|
+
str = vector.to_s
|
90
94
|
send_data([str.size].pack(PACK_N) + str)
|
91
95
|
end
|
92
96
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'bloom_filter/bit_vector'
|
3
|
+
|
4
|
+
class BloomFilterBitVectorTest < Test::Unit::TestCase
|
5
|
+
context "with a bit vector of size 16" do
|
6
|
+
setup do
|
7
|
+
@bit_vector = BloomFilter::BitVector.new(16)
|
8
|
+
end
|
9
|
+
|
10
|
+
should "return all 0s at first" do
|
11
|
+
@bit_vector.size.times do |i|
|
12
|
+
fail "returned #{@bit_vector[i]} for bit #{i}" if @bit_vector[i] != 0
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
should "sets bits to 1 and returns 1 when retrieved" do
|
17
|
+
@bit_vector.size.times do |i|
|
18
|
+
@bit_vector[i] = 1
|
19
|
+
assert_equal 1, @bit_vector[i]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
should "sets bits back to 0" do
|
24
|
+
@bit_vector.size.times do |i|
|
25
|
+
@bit_vector[i] = 1
|
26
|
+
@bit_vector[i] = 0
|
27
|
+
assert_equal 0, @bit_vector[i]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
should "convert the bit vector into a string" do
|
32
|
+
assert @bit_vector.to_s.is_a?(String)
|
33
|
+
end
|
34
|
+
|
35
|
+
should "convert back to the same bit vector" do
|
36
|
+
[0,3,6,7].each do |bit|
|
37
|
+
@bit_vector[bit] = 1
|
38
|
+
end
|
39
|
+
|
40
|
+
assert_equal @bit_vector, BloomFilter::BitVector.new(16, @bit_vector.to_s)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -13,42 +13,48 @@ class BloomFilterClientTest < Test::Unit::TestCase
|
|
13
13
|
should "add elements" do
|
14
14
|
@socket.expects(:write).with("#{[6].pack("N")}#{BloomFilter::Protocol::ADD}hello")
|
15
15
|
@socket.expects(:read).with(4).returns([1].pack("N"))
|
16
|
-
@socket.expects(:read).with(1).returns(
|
16
|
+
@socket.expects(:read).with(1).returns(BloomFilter::Protocol::TRUE)
|
17
17
|
assert @client.add("hello")
|
18
18
|
end
|
19
19
|
|
20
20
|
should "check for elements" do
|
21
21
|
@socket.expects(:write).with("#{[6].pack("N")}#{BloomFilter::Protocol::INCLUDE}hello")
|
22
22
|
@socket.expects(:read).with(4).returns([1].pack("N"))
|
23
|
-
@socket.expects(:read).with(1).returns(
|
23
|
+
@socket.expects(:read).with(1).returns(BloomFilter::Protocol::TRUE)
|
24
24
|
assert @client.include?("hello")
|
25
25
|
end
|
26
26
|
|
27
27
|
should "return false for elements that arent there" do
|
28
28
|
@socket.expects(:write).with("#{[4].pack("N")}#{BloomFilter::Protocol::INCLUDE}bye")
|
29
29
|
@socket.expects(:read).with(4).returns([1].pack("N"))
|
30
|
-
@socket.expects(:read).with(1).returns(
|
30
|
+
@socket.expects(:read).with(1).returns(0.chr)
|
31
31
|
assert !@client.include?("bye")
|
32
32
|
end
|
33
33
|
|
34
34
|
should "check for multiple elements" do
|
35
35
|
@socket.expects(:write).with("#{[10].pack("N")}#{BloomFilter::Protocol::INCLUDE_MANY}hello,bye")
|
36
|
-
@socket.expects(:read).with(4).returns([
|
37
|
-
@socket.expects(:read).with(
|
36
|
+
@socket.expects(:read).with(4).returns([1].pack("N"))
|
37
|
+
@socket.expects(:read).with(1).returns(1.chr)
|
38
38
|
assert_equal ["hello"], @client & ["hello", "bye"]
|
39
|
+
|
40
|
+
@socket.expects(:write).with("#{[10].pack("N")}#{BloomFilter::Protocol::INCLUDE_MANY}bye,hello")
|
41
|
+
@socket.expects(:read).with(4).returns([1].pack("N"))
|
42
|
+
@socket.expects(:read).with(1).returns(2.chr)
|
43
|
+
assert_equal ["hello"], @client & ["bye", "hello"]
|
44
|
+
|
39
45
|
end
|
40
46
|
|
41
47
|
should "send dump" do
|
42
48
|
@socket.expects(:write).with("#{[7].pack("N")}#{BloomFilter::Protocol::DUMP}/tmp/f")
|
43
49
|
@socket.expects(:read).with(4).returns([1].pack("N"))
|
44
|
-
@socket.expects(:read).with(1).returns(
|
50
|
+
@socket.expects(:read).with(1).returns(BloomFilter::Protocol::TRUE)
|
45
51
|
assert @client.dump("/tmp/f")
|
46
52
|
end
|
47
53
|
|
48
54
|
should "send load" do
|
49
55
|
@socket.expects(:write).with("#{[7].pack("N")}#{BloomFilter::Protocol::LOAD}/tmp/f")
|
50
56
|
@socket.expects(:read).with(4).returns([1].pack("N"))
|
51
|
-
@socket.expects(:read).with(1).returns(
|
57
|
+
@socket.expects(:read).with(1).returns(BloomFilter::Protocol::TRUE)
|
52
58
|
assert @client.load("/tmp/f")
|
53
59
|
end
|
54
60
|
|
@@ -15,22 +15,24 @@ class BloomFilterServerTest < Test::Unit::TestCase
|
|
15
15
|
|
16
16
|
should "add elements" do
|
17
17
|
@filter.expects(:add).with("hello")
|
18
|
-
expects_send_data("#{[1].pack('N')}
|
18
|
+
expects_send_data("#{[1].pack('N')}#{BloomFilter::Protocol::TRUE}")
|
19
19
|
|
20
20
|
@server.receive_data("#{[6].pack("N")}#{BloomFilter::Protocol::ADD}hello")
|
21
21
|
end
|
22
22
|
|
23
23
|
should "check for elements" do
|
24
24
|
@filter.expects(:include?).with("hello").returns(true)
|
25
|
-
expects_send_data("#{[1].pack("N")}
|
25
|
+
expects_send_data("#{[1].pack("N")}#{BloomFilter::Protocol::TRUE}")
|
26
26
|
@server.receive_data("#{[6].pack("N")}#{BloomFilter::Protocol::INCLUDE}hello")
|
27
27
|
end
|
28
28
|
|
29
29
|
should "check for multiple elements" do
|
30
|
-
@filter.expects(:include?).with("hello").returns(true)
|
31
|
-
@filter.expects(:include?).with("bye").returns(false)
|
32
|
-
expects_send_data("#{[
|
30
|
+
@filter.expects(:include?).with("hello").returns(true).twice
|
31
|
+
@filter.expects(:include?).with("bye").returns(false).twice
|
32
|
+
expects_send_data("#{[1].pack("N")}#{1.chr}") # 1.chr => false, true
|
33
|
+
expects_send_data("#{[1].pack("N")}#{2.chr}") # 2.chr => true, false
|
33
34
|
@server.receive_data("#{[10].pack("N")}#{BloomFilter::Protocol::INCLUDE_MANY}hello,bye")
|
35
|
+
@server.receive_data("#{[10].pack("N")}#{BloomFilter::Protocol::INCLUDE_MANY}bye,hello")
|
34
36
|
end
|
35
37
|
|
36
38
|
|
@@ -42,7 +44,7 @@ class BloomFilterServerTest < Test::Unit::TestCase
|
|
42
44
|
File.expects(:open).with(path, 'w').yields(file)
|
43
45
|
file.expects(:write).with("dumped_data")
|
44
46
|
|
45
|
-
expects_send_data("#{[1].pack("N")}
|
47
|
+
expects_send_data("#{[1].pack("N")}#{BloomFilter::Protocol::TRUE}")
|
46
48
|
|
47
49
|
|
48
50
|
@server.receive_data("#{[path.size + 1].pack("N")}#{BloomFilter::Protocol::DUMP}#{path}")
|
@@ -54,7 +56,7 @@ class BloomFilterServerTest < Test::Unit::TestCase
|
|
54
56
|
@filter.expects(:replace).with(file)
|
55
57
|
File.expects(:open).with(path, 'r').yields(file)
|
56
58
|
|
57
|
-
expects_send_data("#{[1].pack("N")}
|
59
|
+
expects_send_data("#{[1].pack("N")}#{BloomFilter::Protocol::TRUE}")
|
58
60
|
|
59
61
|
@server.receive_data("#{[path.size + 1].pack("N")}#{BloomFilter::Protocol::LOAD}#{path}")
|
60
62
|
end
|
@@ -62,7 +64,7 @@ class BloomFilterServerTest < Test::Unit::TestCase
|
|
62
64
|
|
63
65
|
should "properly buffer partial messages" do
|
64
66
|
@filter.expects(:add).with("hello")
|
65
|
-
expects_send_data("#{[1].pack('N')}
|
67
|
+
expects_send_data("#{[1].pack('N')}#{BloomFilter::Protocol::TRUE}")
|
66
68
|
|
67
69
|
@server.receive_data("#{[6].pack("N")}#{BloomFilter::Protocol::ADD}hel")
|
68
70
|
@server.receive_data("lo")
|
@@ -71,7 +73,7 @@ class BloomFilterServerTest < Test::Unit::TestCase
|
|
71
73
|
|
72
74
|
should "handle two messages sent at once" do
|
73
75
|
@filter.expects(:add).with("hello")
|
74
|
-
expects_send_data("#{[1].pack('N')}
|
76
|
+
expects_send_data("#{[1].pack('N')}#{BloomFilter::Protocol::TRUE}").twice()
|
75
77
|
@filter.expects(:add).with("bye")
|
76
78
|
|
77
79
|
@server.receive_data("#{[6].pack("N")}#{BloomFilter::Protocol::ADD}hello#{[4].pack("N")}#{BloomFilter::Protocol::ADD}bye")
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bloom_filter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arya Asemanfar
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-01-
|
12
|
+
date: 2010-01-27 00:00:00 -08:00
|
13
13
|
default_executable: bloom_filter_server
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -61,9 +61,11 @@ files:
|
|
61
61
|
- bin/bloom_filter_server
|
62
62
|
- bloom_filter.gemspec
|
63
63
|
- lib/bloom_filter.rb
|
64
|
+
- lib/bloom_filter/bit_vector.rb
|
64
65
|
- lib/bloom_filter/client.rb
|
65
66
|
- lib/bloom_filter/protocol.rb
|
66
67
|
- lib/bloom_filter/server.rb
|
68
|
+
- test/bloom_filter_bit_vector_test.rb
|
67
69
|
- test/bloom_filter_client_test.rb
|
68
70
|
- test/bloom_filter_server_test.rb
|
69
71
|
- test/bloom_filter_test.rb
|
@@ -97,6 +99,7 @@ signing_key:
|
|
97
99
|
specification_version: 3
|
98
100
|
summary: A simple BloomFilter implementation, usable in-process or as an EventMachine daemon.
|
99
101
|
test_files:
|
102
|
+
- test/bloom_filter_bit_vector_test.rb
|
100
103
|
- test/bloom_filter_client_test.rb
|
101
104
|
- test/bloom_filter_server_test.rb
|
102
105
|
- test/bloom_filter_test.rb
|