Skip to content

Commit

Permalink
namespace, break into different classes
Browse files Browse the repository at this point in the history
  • Loading branch information
igrigorik committed Jan 5, 2011
1 parent 7d3532c commit cb71f03
Show file tree
Hide file tree
Showing 10 changed files with 213 additions and 187 deletions.
2 changes: 1 addition & 1 deletion bloomfilter.gemspec
@@ -1,6 +1,6 @@
# -*- encoding: utf-8 -*-
$:.push File.expand_path("../lib", __FILE__)
require "bloomfilter"
require "bloomfilter/version"

Gem::Specification.new do |s|
s.name = "bloomfilter"
Expand Down
116 changes: 7 additions & 109 deletions lib/bloomfilter.rb
@@ -1,110 +1,8 @@
require 'redisbloom'
require 'cbloomfilter'

class BloomFilter
VERSION = "1.3.1"

attr_reader :bf

def initialize(opts = {})
@opts = {
:size => 100,
:hashes => 4,
:seed => Time.now.to_i,
:bucket => 3,
:raise => false,
:type => :c,
:values => false
}.merge(opts)

@values = {}
@bf = create_filter
end

def create_filter(bitmap = nil)
case @opts[:type]
# arg 1: m => size : number of buckets in a bloom filter
# arg 2: k => hashes : number of hash functions
# arg 3: s => seed : seed of hash functions
# arg 4: b => bucket : number of bits in a bloom filter bucket
# arg 5: r => raise : raise on bucket overflow?
when :c then
bf = CBloomFilter.new(@opts[:size], @opts[:hashes], @opts[:seed], @opts[:bucket], @opts[:raise])
bf.load(bitmap) if !bitmap.nil?
bf
when :redis then RedisBloom.new(@opts)
else
raise "invalid type"
end
end

def insert(key, value=nil, ttl=nil)
@bf.insert(key, ttl)
@values[key] = value if @opts[:values]
end
alias :[]= :insert

def include?(*keys)
if @opts[:values]
keys.collect do |key|
@values[key] if @bf.include?(key)
end.compact
else
@bf.include?(*keys)
end
end
alias :key? :include?

def [](key)
return nil if not (@opts[:values] and include?(key))
@values[key]
end
require 'redis'
require 'zlib'

def keys
return nil if not @opts[:values]
@values.keys
end

def delete(key); @bf.delete(key); end
def clear; @bf.clear; end
def size; @bf.num_set; end
def merge!(o); @bf.merge!(o.bf); end

def bitmap
case @opts[:type]
when :c then @bf.bitmap
else
raise "cannot export bitmap for this bloomfilter type"
end
end

def marshal_load(ary)
@opts, @values, bitmap = *ary
@bf = create_filter(bitmap)
@bf
end

def marshal_dump
[@opts, @values, @bf.bitmap]
end

def self.load(filename)
Marshal.load(File.open(filename, 'r'))
end

def save(filename)
File.open(filename, 'w') do |f|
f << Marshal.dump(self)
end
end

def stats
fp = ((1.0 - Math.exp(-(@opts[:hashes] * size).to_f / @opts[:size])) ** @opts[:hashes]) * 100
printf "Number of filter buckets (m): %d\n" % @opts[:size]
printf "Number of bits per buckets (b): %d\n" % @opts[:bucket]
printf "Number of filter elements (n): %d\n" % size
printf "Number of filter hashes (k) : %d\n" % @opts[:hashes]
printf "Raise on overflow? (r) : %s\n" % @opts[:raise].to_s
printf "Predicted false positive rate = %.2f%\n" % fp
end
end
require 'cbloomfilter'
require 'bloomfilter/filter'
require 'bloomfilter/native'
require 'bloomfilter/redis'
require 'bloomfilter/version'
13 changes: 13 additions & 0 deletions lib/bloomfilter/filter.rb
@@ -0,0 +1,13 @@
module BloomFilter
class Filter
def stats
fp = ((1.0 - Math.exp(-(@opts[:hashes] * size).to_f / @opts[:size])) ** @opts[:hashes]) * 100
printf "Number of filter buckets (m): %d\n" % @opts[:size]
printf "Number of bits per buckets (b): %d\n" % @opts[:bucket]
printf "Number of filter elements (n): %d\n" % size
printf "Number of filter hashes (k) : %d\n" % @opts[:hashes]
printf "Raise on overflow? (r) : %s\n" % @opts[:raise].to_s
printf "Predicted false positive rate = %.2f%\n" % fp
end
end
end
98 changes: 98 additions & 0 deletions lib/bloomfilter/native.rb
@@ -0,0 +1,98 @@
module BloomFilter
class Native < Filter
attr_reader :bf

def initialize(opts = {})
@opts = {
:size => 100,
:hashes => 4,
:seed => Time.now.to_i,
:bucket => 3,
:raise => false,
:type => :c,
:values => false
}.merge(opts)

@values = {}
@bf = create_filter
end

def create_filter(bitmap = nil)
case @opts[:type]
# arg 1: m => size : number of buckets in a bloom filter
# arg 2: k => hashes : number of hash functions
# arg 3: s => seed : seed of hash functions
# arg 4: b => bucket : number of bits in a bloom filter bucket
# arg 5: r => raise : raise on bucket overflow?
when :c then
bf = CBloomFilter.new(@opts[:size], @opts[:hashes], @opts[:seed], @opts[:bucket], @opts[:raise])
bf.load(bitmap) if !bitmap.nil?
bf
when :redis then RedisBloom.new(@opts)
else
raise "invalid type"
end
end

def insert(key, value=nil, ttl=nil)
@bf.insert(key, ttl)
@values[key] = value if @opts[:values]
end
alias :[]= :insert

def include?(*keys)
if @opts[:values]
keys.collect do |key|
@values[key] if @bf.include?(key)
end.compact
else
@bf.include?(*keys)
end
end
alias :key? :include?

def [](key)
return nil if not (@opts[:values] and include?(key))
@values[key]
end

def keys
return nil if not @opts[:values]
@values.keys
end

def delete(key); @bf.delete(key); end
def clear; @bf.clear; end
def size; @bf.num_set; end
def merge!(o); @bf.merge!(o.bf); end

def bitmap
case @opts[:type]
when :c then @bf.bitmap
else
raise "cannot export bitmap for this bloomfilter type"
end
end

def marshal_load(ary)
@opts, @values, bitmap = *ary
@bf = create_filter(bitmap)
@bf
end

def marshal_dump
[@opts, @values, @bf.bitmap]
end

def self.load(filename)
Marshal.load(File.open(filename, 'r'))
end

def save(filename)
File.open(filename, 'w') do |f|
f << Marshal.dump(self)
end
end

end
end
64 changes: 64 additions & 0 deletions lib/bloomfilter/redis.rb
@@ -0,0 +1,64 @@
module BloomFilter
class Redis < Filter

def initialize(opts)
@opts = {
:size => 100,
:hashes => 4,
:seed => Time.now.to_i,
:bucket => 3,
:raise => false,
:values => false,

:ttl => false,
:server => {}
}.merge opts
@db = ::Redis.new(@opts[:server])
end

def insert(key, ttl=nil)
ttl = @opts[:ttl] if ttl.nil?

indexes_for(key).each do |idx|
@db.incr idx
@db.expire(idx, ttl) if ttl
end
end
alias :[]= :insert

def delete(key)
indexes_for(key).each do |idx|
if @db.decr(idx).to_i <= 0
@db.del(idx)
end
end
end

def include?(*keys)
indexes = keys.collect { |key| indexes_for(key) }
not @db.mget(*indexes.flatten).include? nil
end
alias :key? :include?

def num_set
@db.keys("rbloom:*").size
end
alias :size :num_set

def clear
@db.flushdb
end

private

# compute index offsets for provided key
def indexes_for(key)
indexes = []
@opts[:hashes].times do |i|
indexes.push "rbloom:" + (Zlib.crc32("#{key}:#{i+@opts[:seed]}") % @opts[:size]).to_s
end

indexes
end
end
end
3 changes: 3 additions & 0 deletions lib/bloomfilter/version.rb
@@ -0,0 +1,3 @@
module BloomFilter
VERSION = "1.3.1"
end
54 changes: 0 additions & 54 deletions lib/redisbloom.rb

This file was deleted.

0 comments on commit cb71f03

Please sign in to comment.