bloom_filter 0.6.1 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|