json-bloomfilter 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +1 -0
- data/Gemfile.lock +1 -1
- data/README.md +2 -0
- data/Rakefile +3 -3
- data/coffee/bitarray.coffee +43 -46
- data/coffee/bloomfilter.coffee +27 -29
- data/coffee/zlib.coffee +1 -1
- data/js/json-bloomfilter.min.js +1 -0
- data/lib/json/bloomfilter.rb +9 -5
- data/lib/json/bloomfilter/bitarray.rb +51 -0
- data/lib/json/bloomfilter/version.rb +1 -1
- data/pkg/json-bloomfilter-0.0.2.gem +0 -0
- data/spec/json/bloomfilter/bitarray_spec.rb +66 -0
- data/spec/json/bloomfilter_spec.rb +78 -0
- data/spec/spec_helper.rb +1 -0
- metadata +9 -6
- data/js/bloomfilter.min.js +0 -1
- data/lib/json/bitarray.rb +0 -45
- data/spec/json-bloomfilter_spec.rb +0 -9
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
data/Rakefile
CHANGED
@@ -54,7 +54,7 @@ namespace :js do
|
|
54
54
|
coffee_script += File.read(filename) + "\n"
|
55
55
|
end
|
56
56
|
|
57
|
-
js_file = "js/bloomfilter.js"
|
57
|
+
js_file = "js/json-bloomfilter.js"
|
58
58
|
directory_name = File.dirname js_file
|
59
59
|
FileUtils.mkdir_p(directory_name) unless File.exists? directory_name
|
60
60
|
puts "\t\t->#{js_file}"
|
@@ -68,8 +68,8 @@ namespace :js do
|
|
68
68
|
filename = args[:filename]
|
69
69
|
compressor = YUI::JavaScriptCompressor.new
|
70
70
|
|
71
|
-
js_file = "js/bloomfilter.js"
|
72
|
-
min_file = "js/bloomfilter.min.js"
|
71
|
+
js_file = "js/json-bloomfilter.js"
|
72
|
+
min_file = "js/json-bloomfilter.min.js"
|
73
73
|
|
74
74
|
puts "\t\t->#{min_file}"
|
75
75
|
directory_name = File.dirname min_file
|
data/coffee/bitarray.coffee
CHANGED
@@ -1,46 +1,43 @@
|
|
1
|
-
JsonBloomfilter.BitArray =
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
output = ""
|
45
|
-
output += @get(i) for i in [0..@size-1]
|
46
|
-
output
|
1
|
+
JsonBloomfilter.BitArray = (size, field = null) ->
|
2
|
+
@ELEMENT_WIDTH = 32
|
3
|
+
@size = size
|
4
|
+
@field = field || []
|
5
|
+
|
6
|
+
arrayLength = Math.floor(((size - 1) / @ELEMENT_WIDTH) + 1)
|
7
|
+
@field[i] = 0 for i in [0..arrayLength] unless field
|
8
|
+
|
9
|
+
this
|
10
|
+
|
11
|
+
JsonBloomfilter.BitArray.prototype.add = (position) ->
|
12
|
+
@set(position, 1)
|
13
|
+
|
14
|
+
JsonBloomfilter.BitArray.prototype.remove = (position) ->
|
15
|
+
@set(position, 0)
|
16
|
+
|
17
|
+
JsonBloomfilter.BitArray.prototype.set = (position, value) ->
|
18
|
+
aPos = @arrayPosition(position)
|
19
|
+
bChange = @bitChange(position)
|
20
|
+
if value == 1
|
21
|
+
@field[aPos] |= bChange
|
22
|
+
else if @field[aPos] & bChange != 0
|
23
|
+
@field[aPos] ^= bChange
|
24
|
+
true
|
25
|
+
|
26
|
+
JsonBloomfilter.BitArray.prototype.get = (position) ->
|
27
|
+
aPos = @arrayPosition(position)
|
28
|
+
bChange = @bitChange(position)
|
29
|
+
if (@field[aPos] & bChange) > 0
|
30
|
+
return 1
|
31
|
+
else
|
32
|
+
return 0
|
33
|
+
|
34
|
+
JsonBloomfilter.BitArray.prototype.arrayPosition = (position) ->
|
35
|
+
Math.floor(position / @ELEMENT_WIDTH)
|
36
|
+
|
37
|
+
JsonBloomfilter.BitArray.prototype.bitChange = (position) ->
|
38
|
+
Math.abs(1 << position % @ELEMENT_WIDTH)
|
39
|
+
|
40
|
+
JsonBloomfilter.BitArray.prototype.toString = ->
|
41
|
+
output = ""
|
42
|
+
output += @get(i) for i in [0..@size-1]
|
43
|
+
output
|
data/coffee/bloomfilter.coffee
CHANGED
@@ -1,40 +1,38 @@
|
|
1
|
-
JsonBloomfilter =
|
2
|
-
|
3
|
-
options:
|
1
|
+
JsonBloomfilter = (options = {}) ->
|
2
|
+
@options =
|
4
3
|
size: 100,
|
5
4
|
hashes: 4,
|
6
5
|
seed: (new Date().getTime() / 1000),
|
7
6
|
bits: null
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
this
|
8
|
+
@options[key] = value for key, value of options
|
9
|
+
@bits = new JsonBloomfilter.BitArray(@options["size"], @options["bits"])
|
10
|
+
this
|
13
11
|
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
JsonBloomfilter.prototype.add = (key) ->
|
13
|
+
@bits.add(index) for index in @indexesFor(key)
|
14
|
+
return
|
17
15
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
16
|
+
JsonBloomfilter.prototype.test = (key) ->
|
17
|
+
for index in @indexesFor(key)
|
18
|
+
return false if @bits.get(index) == 0
|
19
|
+
true
|
22
20
|
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
JsonBloomfilter.prototype.clear = ->
|
22
|
+
@bits = @BitArray.new(@options["size"], null)
|
23
|
+
return
|
26
24
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
25
|
+
JsonBloomfilter.prototype.toHash = ->
|
26
|
+
hash = {}
|
27
|
+
hash[key] = value for key, value of @options
|
28
|
+
hash["bits"] = @bits.field
|
29
|
+
hash
|
32
30
|
|
33
|
-
|
34
|
-
|
31
|
+
JsonBloomfilter.prototype.toJson = ->
|
32
|
+
JSON.stringify @toHash()
|
35
33
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
34
|
+
JsonBloomfilter.prototype.indexesFor = (key) ->
|
35
|
+
indexes = []
|
36
|
+
for index in [0..@options["hashes"]-1]
|
37
|
+
indexes.push (JsonBloomfilter.Zlib.crc32("#{key}:#{index+@options["seed"]}") % @options["size"])
|
38
|
+
indexes
|
data/coffee/zlib.coffee
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
var JsonBloomfilter;JsonBloomfilter=function(options){var key,value;if(options==null){options={}}this.options={size:100,hashes:4,seed:new Date().getTime()/1000,bits:null};for(key in options){value=options[key];this.options[key]=value}this.bits=new JsonBloomfilter.BitArray(this.options.size,this.options.bits);return this};JsonBloomfilter.prototype.add=function(key){var index,_i,_len,_ref;_ref=this.indexesFor(key);for(_i=0,_len=_ref.length;_i<_len;_i++){index=_ref[_i];this.bits.add(index)}};JsonBloomfilter.prototype.test=function(key){var index,_i,_len,_ref;_ref=this.indexesFor(key);for(_i=0,_len=_ref.length;_i<_len;_i++){index=_ref[_i];if(this.bits.get(index)===0){return false}}return true};JsonBloomfilter.prototype.clear=function(){this.bits=this.BitArray["new"](this.options.size,null)};JsonBloomfilter.prototype.toHash=function(){var hash,key,value,_ref;hash={};_ref=this.options;for(key in _ref){value=_ref[key];hash[key]=value}hash.bits=this.bits.field;return hash};JsonBloomfilter.prototype.toJson=function(){return JSON.stringify(this.toHash())};JsonBloomfilter.prototype.indexesFor=function(key){var index,indexes,_i,_ref;indexes=[];for(index=_i=0,_ref=this.options.hashes-1;0<=_ref?_i<=_ref:_i>=_ref;index=0<=_ref?++_i:--_i){indexes.push(JsonBloomfilter.Zlib.crc32(""+key+":"+(index+this.options.seed))%this.options.size)}return indexes};JsonBloomfilter.BitArray=function(size,field){var arrayLength,i,_i;if(field==null){field=null}this.ELEMENT_WIDTH=32;this.size=size;this.field=field||[];arrayLength=Math.floor(((size-1)/this.ELEMENT_WIDTH)+1);if(!field){for(i=_i=0;0<=arrayLength?_i<=arrayLength:_i>=arrayLength;i=0<=arrayLength?++_i:--_i){this.field[i]=0}}return this};JsonBloomfilter.BitArray.prototype.add=function(position){return this.set(position,1)};JsonBloomfilter.BitArray.prototype.remove=function(position){return this.set(position,0)};JsonBloomfilter.BitArray.prototype.set=function(position,value){var aPos,bChange;aPos=this.arrayPosition(position);bChange=this.bitChange(position);if(value===1){this.field[aPos]|=bChange}else{if(this.field[aPos]&bChange!==0){this.field[aPos]^=bChange}}return true};JsonBloomfilter.BitArray.prototype.get=function(position){var aPos,bChange;aPos=this.arrayPosition(position);bChange=this.bitChange(position);if((this.field[aPos]&bChange)>0){return 1}else{return 0}};JsonBloomfilter.BitArray.prototype.arrayPosition=function(position){return Math.floor(position/this.ELEMENT_WIDTH)};JsonBloomfilter.BitArray.prototype.bitChange=function(position){return Math.abs(1<<position%this.ELEMENT_WIDTH)};JsonBloomfilter.BitArray.prototype.toString=function(){var i,output,_i,_ref;output="";for(i=_i=0,_ref=this.size-1;0<=_ref?_i<=_ref:_i>=_ref;i=0<=_ref?++_i:--_i){output+=this.get(i)}return output};JsonBloomfilter.Zlib={CRC32_TABLE:new Array(0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,249268274,2044508324,3772115230,2547177864,162941995,2125561021,3887607047,2428444049,498536548,1789927666,4089016648,2227061214,450548861,1843258603,4107580753,2211677639,325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,997073096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,901097722,1119000684,3686517206,2898065728,853044451,1172266101,3705015759,2882616665,651767980,1373503546,3369554304,3218104598,565507253,1454621731,3485111705,3099436303,671266974,1594198024,3322730930,2970347812,795835527,1483230225,3244367275,3060149565,1994146192,31158534,2563907772,4023717930,1907459465,112637215,2680153253,3904427059,2013776290,251722036,2517215374,3775830040,2137656763,141376813,2439277719,3865271297,1802195444,476864866,2238001368,4066508878,1812370925,453092731,2181625025,4111451223,1706088902,314042704,2344532202,4240017532,1658658271,366619977,2362670323,4224994405,1303535960,984961486,2747007092,3569037538,1256170817,1037604311,2765210733,3554079995,1131014506,879679996,2909243462,3663771856,1141124467,855842277,2852801631,3708648649,1342533948,654459306,3188396048,3373015174,1466479909,544179635,3110523913,3462522015,1591671054,702138776,2966460450,3352799412,1504918807,783551873,3082640443,3233442989,3988292384,2596254646,62317068,1957810842,3939845945,2647816111,81470997,1943803523,3814918930,2489596804,225274430,2053790376,3826175755,2466906013,167816743,2097651377,4027552580,2265490386,503444072,1762050814,4150417245,2154129355,426522225,1852507879,4275313526,2312317920,282753626,1742555852,4189708143,2394877945,397917763,1622183637,3604390888,2714866558,953729732,1340076626,3518719985,2797360999,1068828381,1219638859,3624741850,2936675148,906185462,1090812512,3747672003,2825379669,829329135,1181335161,3412177804,3160834842,628085408,1382605366,3423369109,3138078467,570562233,1426400815,3317316542,2998733608,733239954,1555261956,3268935591,3050360625,752459403,1541320221,2607071920,3965973030,1969922972,40735498,2617837225,3943577151,1913087877,83908371,2512341634,3803740692,2075208622,213261112,2463272603,3855990285,2094854071,198958881,2262029012,4057260610,1759359992,534414190,2176718541,4139329115,1873836001,414664567,2282248934,4279200368,1711684554,285281116,2405801727,4167216745,1634467795,376229701,2685067896,3608007406,1308918612,956543938,2808555105,3495958263,1231636301,1047427035,2932959818,3654703836,1088359270,936918000,2847714899,3736837829,1202900863,817233897,3183342108,3401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,3009837614,3294710456,1567103746,711928724,3020668471,3272380065,1510334235,755167117),crc32:function(string){var bytes,crc,i,iTop,n;bytes=this.bytesFor(string);crc=0;n=0;crc=crc^(-1);i=0;iTop=bytes.length;while(i<iTop){n=(crc^bytes[i])&255;crc=(crc>>>8)^this.CRC32_TABLE[n];i++}crc=crc^(-1);if(crc<0){crc+=4294967296}return crc},bytesFor:function(string){var bytes,i;bytes=[];i=0;while(i<string.length){bytes.push(string.charCodeAt(i));++i}return bytes}};
|
data/lib/json/bloomfilter.rb
CHANGED
@@ -1,28 +1,28 @@
|
|
1
|
-
require_relative "bitarray"
|
1
|
+
require_relative "bloomfilter/bitarray"
|
2
2
|
require "json"
|
3
3
|
|
4
4
|
class JsonBloomfilter
|
5
5
|
DEFAULTS = { "size" => 100, "hashes" => 4, "seed" => Time.new.to_i, "bits" => nil }
|
6
6
|
|
7
7
|
def initialize options = {}
|
8
|
-
@options =
|
8
|
+
@options = merge_defaults_with options
|
9
9
|
@bits = BitArray.new(@options["size"], @options["bits"])
|
10
10
|
end
|
11
11
|
|
12
12
|
def add key
|
13
|
-
indexes_for(key).each { |index| @bits
|
13
|
+
indexes_for(key).each { |index| @bits.add(index) }
|
14
14
|
nil
|
15
15
|
end
|
16
16
|
|
17
17
|
def test key
|
18
18
|
indexes_for(key).each do |index|
|
19
|
-
return false if @bits
|
19
|
+
return false if @bits.get(index) == 0
|
20
20
|
end
|
21
21
|
true
|
22
22
|
end
|
23
23
|
|
24
24
|
def clear
|
25
|
-
@bits = BitArray.new(@options["size"]
|
25
|
+
@bits = BitArray.new(@options["size"])
|
26
26
|
end
|
27
27
|
|
28
28
|
def to_hash
|
@@ -40,4 +40,8 @@ class JsonBloomfilter
|
|
40
40
|
Zlib.crc32("#{key}:#{index+@options["seed"]}") % @options["size"]
|
41
41
|
end
|
42
42
|
end
|
43
|
+
|
44
|
+
def merge_defaults_with options
|
45
|
+
DEFAULTS.merge Hash[options.map {|k, v| ["#{k}", v] }]
|
46
|
+
end
|
43
47
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# Adapted from
|
2
|
+
# https://github.com/peterc/bitarray
|
3
|
+
|
4
|
+
class JsonBloomfilter
|
5
|
+
class BitArray
|
6
|
+
attr_reader :size
|
7
|
+
attr_reader :field
|
8
|
+
include Enumerable
|
9
|
+
|
10
|
+
ELEMENT_WIDTH = 32
|
11
|
+
|
12
|
+
def initialize(size, field = nil)
|
13
|
+
@size = size
|
14
|
+
@field = field || Array.new(((size - 1) / ELEMENT_WIDTH) + 1, 0)
|
15
|
+
end
|
16
|
+
|
17
|
+
def add position
|
18
|
+
self[position] = 1
|
19
|
+
end
|
20
|
+
|
21
|
+
def remove position
|
22
|
+
self[position] = 0
|
23
|
+
end
|
24
|
+
|
25
|
+
# Set a bit (1/0)
|
26
|
+
def []=(position, value)
|
27
|
+
raise ArgumentError.new("Position out of bound (#{position} for max #{@size})") if position >= @size
|
28
|
+
if value == 1
|
29
|
+
@field[position / ELEMENT_WIDTH] |= 1 << (position % ELEMENT_WIDTH)
|
30
|
+
elsif (@field[position / ELEMENT_WIDTH]) & (1 << (position % ELEMENT_WIDTH)) != 0
|
31
|
+
@field[position / ELEMENT_WIDTH] ^= 1 << (position % ELEMENT_WIDTH)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Read a bit (1/0)
|
36
|
+
def get position
|
37
|
+
raise ArgumentError.new("Position out of bound (#{position} for max #{@size})") if position >= @size
|
38
|
+
@field[position / ELEMENT_WIDTH] & 1 << (position % ELEMENT_WIDTH) > 0 ? 1 : 0
|
39
|
+
end
|
40
|
+
|
41
|
+
# Iterate over each bit
|
42
|
+
def each(&block)
|
43
|
+
@size.times { |position| yield self.get(position) }
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns the field as a string like "0101010100111100," etc.
|
47
|
+
def to_s
|
48
|
+
inject("") { |a, b| a + b.to_s }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
Binary file
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe JsonBloomfilter::BitArray do
|
4
|
+
|
5
|
+
describe "#initialize" do
|
6
|
+
it "should require a size" do
|
7
|
+
expect(->{JsonBloomfilter::BitArray.new}).to raise_error(ArgumentError)
|
8
|
+
expect(->{JsonBloomfilter::BitArray.new(100)}).not_to raise_error(ArgumentError)
|
9
|
+
end
|
10
|
+
it "should take an optional bit field" do
|
11
|
+
field = [0,0,0,2]
|
12
|
+
ba = JsonBloomfilter::BitArray.new(100, field)
|
13
|
+
expect(ba.field).to be == field
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "add" do
|
18
|
+
it "should set the bit to 1" do
|
19
|
+
ba = JsonBloomfilter::BitArray.new(10)
|
20
|
+
ba.add(9)
|
21
|
+
expect(ba.to_s).to be == "0000000001"
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should throw an error on out of bound" do
|
25
|
+
ba = JsonBloomfilter::BitArray.new(10)
|
26
|
+
expect(->{ba.add(10)}).to raise_error
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "remove" do
|
31
|
+
it "should set the bit to 0" do
|
32
|
+
ba = JsonBloomfilter::BitArray.new(10)
|
33
|
+
ba.add(9)
|
34
|
+
ba.remove(9)
|
35
|
+
expect(ba.to_s).to be == "0000000000"
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should throw an error on out of bound" do
|
39
|
+
ba = JsonBloomfilter::BitArray.new(10)
|
40
|
+
expect(->{ba.remove(10)}).to raise_error
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "get" do
|
45
|
+
it "should return the bit set" do
|
46
|
+
ba = JsonBloomfilter::BitArray.new(10)
|
47
|
+
ba.add(9)
|
48
|
+
expect(ba.get(9)).to be == 1
|
49
|
+
expect(ba.get(8)).to be == 0
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should throw an error on out of bound" do
|
53
|
+
ba = JsonBloomfilter::BitArray.new(10)
|
54
|
+
expect(->{ba.get(10)}).to raise_error
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "#to_s" do
|
59
|
+
it "should output the bit string" do
|
60
|
+
ba = JsonBloomfilter::BitArray.new(10)
|
61
|
+
ba.add(3)
|
62
|
+
ba.add(9)
|
63
|
+
expect(ba.to_s).to be == "0001000001"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe JsonBloomfilter do
|
4
|
+
|
5
|
+
describe "#initialize" do
|
6
|
+
it "should take the appropriate options" do
|
7
|
+
seed = Time.now.to_i - 24*60*60
|
8
|
+
bf = JsonBloomfilter.new size: 200, hashes: 10, seed: seed
|
9
|
+
expect(bf.to_hash["size"]).to be == 200
|
10
|
+
expect(bf.to_hash["hashes"]).to be == 10
|
11
|
+
expect(bf.to_hash["seed"]).to be == seed
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should be initializable with a field serialized by another bloom filter" do
|
15
|
+
bf1 = JsonBloomfilter.new
|
16
|
+
bf1.add "foo"
|
17
|
+
bf2 = JsonBloomfilter.new bf1.to_hash
|
18
|
+
expect(bf2.test "foo").to be_true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context "with an instance" do
|
23
|
+
before :each do
|
24
|
+
@bf = JsonBloomfilter.new
|
25
|
+
@bf.add("foobar")
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "#add, #test" do
|
29
|
+
it "should add a key" do
|
30
|
+
expect(@bf.test "foo").to be_false
|
31
|
+
@bf.add "foo"
|
32
|
+
expect(@bf.test "foo").to be_true
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should not change anything if added twice" do
|
36
|
+
expect(@bf.test "foobar").to be_true
|
37
|
+
bits = @bf.to_hash["bits"]
|
38
|
+
@bf.add "foobar"
|
39
|
+
expect(@bf.test "foobar").to be_true
|
40
|
+
expect(@bf.to_hash["bits"]).to be == bits
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#clear" do
|
45
|
+
it "should clear the bit array" do
|
46
|
+
expect(@bf.to_hash["bits"]).not_to be == [0,0,0,0]
|
47
|
+
@bf.clear
|
48
|
+
expect(@bf.to_hash["bits"]).to be == [0,0,0,0]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "#to_hash" do
|
53
|
+
it "should return the serialisable hash" do
|
54
|
+
hash = @bf.to_hash
|
55
|
+
expect(hash).to be_a(Hash)
|
56
|
+
|
57
|
+
expect(hash).to have_key("seed")
|
58
|
+
expect(hash["seed"]).to be_a(Integer)
|
59
|
+
|
60
|
+
expect(hash).to have_key("hashes")
|
61
|
+
expect(hash["hashes"]).to be_a(Integer)
|
62
|
+
|
63
|
+
expect(hash).to have_key("size")
|
64
|
+
expect(hash["size"]).to be_a(Integer)
|
65
|
+
|
66
|
+
expect(hash).to have_key("bits")
|
67
|
+
expect(hash["bits"]).to be_a(Array)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "#to_json" do
|
72
|
+
it "should return the hash serialised" do
|
73
|
+
expect(@bf.to_json).to be == JSON.generate(@bf.to_hash)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: json-bloomfilter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -116,6 +116,7 @@ executables: []
|
|
116
116
|
extensions: []
|
117
117
|
extra_rdoc_files: []
|
118
118
|
files:
|
119
|
+
- .rspec
|
119
120
|
- .rvmrc
|
120
121
|
- Gemfile
|
121
122
|
- Gemfile.lock
|
@@ -125,14 +126,15 @@ files:
|
|
125
126
|
- coffee/bitarray.coffee
|
126
127
|
- coffee/bloomfilter.coffee
|
127
128
|
- coffee/zlib.coffee
|
128
|
-
- js/bloomfilter.min.js
|
129
|
+
- js/json-bloomfilter.min.js
|
129
130
|
- json-bloomfilter.gemspec
|
130
|
-
- lib/json/bitarray.rb
|
131
131
|
- lib/json/bloomfilter.rb
|
132
|
+
- lib/json/bloomfilter/bitarray.rb
|
132
133
|
- lib/json/bloomfilter/version.rb
|
133
134
|
- pkg/json-bloomfilter-0.0.1.gem
|
134
135
|
- pkg/json-bloomfilter-0.0.2.gem
|
135
|
-
- spec/json
|
136
|
+
- spec/json/bloomfilter/bitarray_spec.rb
|
137
|
+
- spec/json/bloomfilter_spec.rb
|
136
138
|
- spec/spec_helper.rb
|
137
139
|
homepage: http://github.com/cbetta/json-bloomfilter
|
138
140
|
licenses:
|
@@ -155,7 +157,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
155
157
|
version: '0'
|
156
158
|
segments:
|
157
159
|
- 0
|
158
|
-
hash:
|
160
|
+
hash: 2570443821793762265
|
159
161
|
requirements: []
|
160
162
|
rubyforge_project:
|
161
163
|
rubygems_version: 1.8.24
|
@@ -163,5 +165,6 @@ signing_key:
|
|
163
165
|
specification_version: 3
|
164
166
|
summary: A bloomfilter implementation in both Ruby and Javascript.
|
165
167
|
test_files:
|
166
|
-
- spec/json
|
168
|
+
- spec/json/bloomfilter/bitarray_spec.rb
|
169
|
+
- spec/json/bloomfilter_spec.rb
|
167
170
|
- spec/spec_helper.rb
|
data/js/bloomfilter.min.js
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
var JsonBloomfilter;JsonBloomfilter={bits:null,options:{size:100,hashes:4,seed:new Date().getTime()/1000,bits:null},"new":function(options){var key,value;if(options==null){options={}}for(key in options){value=options[key];this.options[key]=value}this.bits=this.BitArray["new"](this.options.size,this.options.bits);return this},add:function(key){var index,_i,_len,_ref;_ref=this.indexesFor(key);for(_i=0,_len=_ref.length;_i<_len;_i++){index=_ref[_i];this.bits.add(index)}},test:function(key){var index,_i,_len,_ref;_ref=this.indexesFor(key);for(_i=0,_len=_ref.length;_i<_len;_i++){index=_ref[_i];if(this.bits.get(index)===0){return false}}return true},clear:function(){this.bits=this.BitArray["new"](this.options.size,null)},toHash:function(){var hash,key,value,_ref;hash={};_ref=this.options;for(key in _ref){value=_ref[key];hash[key]=value}hash.bits=this.bits.field;return hash},toJson:function(){return JSON.stringify(this.toHash())},indexesFor:function(key){var index,indexes,_i,_ref;indexes=[];for(index=_i=0,_ref=this.options.hashes-1;0<=_ref?_i<=_ref:_i>=_ref;index=0<=_ref?++_i:--_i){indexes.push(this.Zlib.crc32(""+key+":"+(index+this.options.seed))%this.options.size)}return indexes}};JsonBloomfilter.BitArray={size:null,field:null,ELEMENT_WIDTH:32,"new":function(size,field){var arrayLength,i,_i;if(field==null){field=null}this.size=size;arrayLength=Math.floor(((size-1)/this.ELEMENT_WIDTH)+1);this.field=field||[];if(!field){for(i=_i=0;0<=arrayLength?_i<=arrayLength:_i>=arrayLength;i=0<=arrayLength?++_i:--_i){this.field[i]=0}}return this},add:function(position){return this.set(position,1)},remove:function(position){return this.set(position,0)},set:function(position,value){var aPos,bChange;aPos=this.arrayPosition(position);bChange=this.bitChange(position);if(value===1){this.field[aPos]|=bChange}else{if(this.field[aPos]&bChange!==0){this.field[aPos]^=bChange}}return true},get:function(position){var aPos,bChange;aPos=this.arrayPosition(position);bChange=this.bitChange(position);if((this.field[aPos]&bChange)>0){return 1}else{return 0}},arrayPosition:function(position){return Math.floor(position/this.ELEMENT_WIDTH)},bitChange:function(position){return Math.abs(1<<position%this.ELEMENT_WIDTH)},toString:function(){var i,output,_i,_ref;output="";for(i=_i=0,_ref=this.size-1;0<=_ref?_i<=_ref:_i>=_ref;i=0<=_ref?++_i:--_i){output+=this.get(i)}return output}};JsonBloomfilter.Zlib={CRC32_TABLE:new Array(0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,249268274,2044508324,3772115230,2547177864,162941995,2125561021,3887607047,2428444049,498536548,1789927666,4089016648,2227061214,450548861,1843258603,4107580753,2211677639,325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,997073096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,901097722,1119000684,3686517206,2898065728,853044451,1172266101,3705015759,2882616665,651767980,1373503546,3369554304,3218104598,565507253,1454621731,3485111705,3099436303,671266974,1594198024,3322730930,2970347812,795835527,1483230225,3244367275,3060149565,1994146192,31158534,2563907772,4023717930,1907459465,112637215,2680153253,3904427059,2013776290,251722036,2517215374,3775830040,2137656763,141376813,2439277719,3865271297,1802195444,476864866,2238001368,4066508878,1812370925,453092731,2181625025,4111451223,1706088902,314042704,2344532202,4240017532,1658658271,366619977,2362670323,4224994405,1303535960,984961486,2747007092,3569037538,1256170817,1037604311,2765210733,3554079995,1131014506,879679996,2909243462,3663771856,1141124467,855842277,2852801631,3708648649,1342533948,654459306,3188396048,3373015174,1466479909,544179635,3110523913,3462522015,1591671054,702138776,2966460450,3352799412,1504918807,783551873,3082640443,3233442989,3988292384,2596254646,62317068,1957810842,3939845945,2647816111,81470997,1943803523,3814918930,2489596804,225274430,2053790376,3826175755,2466906013,167816743,2097651377,4027552580,2265490386,503444072,1762050814,4150417245,2154129355,426522225,1852507879,4275313526,2312317920,282753626,1742555852,4189708143,2394877945,397917763,1622183637,3604390888,2714866558,953729732,1340076626,3518719985,2797360999,1068828381,1219638859,3624741850,2936675148,906185462,1090812512,3747672003,2825379669,829329135,1181335161,3412177804,3160834842,628085408,1382605366,3423369109,3138078467,570562233,1426400815,3317316542,2998733608,733239954,1555261956,3268935591,3050360625,752459403,1541320221,2607071920,3965973030,1969922972,40735498,2617837225,3943577151,1913087877,83908371,2512341634,3803740692,2075208622,213261112,2463272603,3855990285,2094854071,198958881,2262029012,4057260610,1759359992,534414190,2176718541,4139329115,1873836001,414664567,2282248934,4279200368,1711684554,285281116,2405801727,4167216745,1634467795,376229701,2685067896,3608007406,1308918612,956543938,2808555105,3495958263,1231636301,1047427035,2932959818,3654703836,1088359270,936918000,2847714899,3736837829,1202900863,817233897,3183342108,3401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,3009837614,3294710456,1567103746,711928724,3020668471,3272380065,1510334235,755167117),crc32:function(string){var bytes,crc,i,iTop,n;bytes=this.bytesFor(string);crc=0;n=0;crc=crc^(-1);i=0;iTop=bytes.length;while(i<iTop){n=(crc^bytes[i])&255;crc=(crc>>>8)^this.CRC32_TABLE[n];i++}crc=crc^(-1);if(crc<0){crc+=4294967296}return crc},bytesFor:function(string){var bytes,i;bytes=[];i=0;while(i<string.length){bytes.push(string.charCodeAt(i));++i}return bytes}};
|
data/lib/json/bitarray.rb
DELETED
@@ -1,45 +0,0 @@
|
|
1
|
-
# Adapted from
|
2
|
-
# https://github.com/peterc/bitarray
|
3
|
-
|
4
|
-
class BitArray
|
5
|
-
attr_reader :size
|
6
|
-
attr_reader :field
|
7
|
-
include Enumerable
|
8
|
-
|
9
|
-
ELEMENT_WIDTH = 32
|
10
|
-
|
11
|
-
def initialize(size, field = nil)
|
12
|
-
@size = size
|
13
|
-
@field = field || Array.new(((size - 1) / ELEMENT_WIDTH) + 1, 0)
|
14
|
-
end
|
15
|
-
|
16
|
-
# Set a bit (1/0)
|
17
|
-
def []=(position, value)
|
18
|
-
if value == 1
|
19
|
-
@field[position / ELEMENT_WIDTH] |= 1 << (position % ELEMENT_WIDTH)
|
20
|
-
elsif (@field[position / ELEMENT_WIDTH]) & (1 << (position % ELEMENT_WIDTH)) != 0
|
21
|
-
@field[position / ELEMENT_WIDTH] ^= 1 << (position % ELEMENT_WIDTH)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
# Read a bit (1/0)
|
26
|
-
def [](position)
|
27
|
-
@field[position / ELEMENT_WIDTH] & 1 << (position % ELEMENT_WIDTH) > 0 ? 1 : 0
|
28
|
-
end
|
29
|
-
|
30
|
-
# Iterate over each bit
|
31
|
-
def each(&block)
|
32
|
-
@size.times { |position| yield self[position] }
|
33
|
-
end
|
34
|
-
|
35
|
-
# Returns the field as a string like "0101010100111100," etc.
|
36
|
-
def to_s
|
37
|
-
inject("") { |a, b| a + b.to_s }
|
38
|
-
end
|
39
|
-
|
40
|
-
# Returns the total number of bits that are set
|
41
|
-
# (The technique used here is about 6 times faster than using each or inject direct on the bitfield)
|
42
|
-
def total_set
|
43
|
-
@field.inject(0) { |a, byte| a += byte & 1 and byte >>= 1 until byte == 0; a }
|
44
|
-
end
|
45
|
-
end
|