json-bloomfilter 0.0.2 → 0.0.3
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/.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
|