github.com/zignig/go-ipfs@v0.0.0-20141111235910-c9e5fdf55a52/blocks/bloom/filter.go (about)

     1  // package bloom implements a simple bloom filter.
     2  package bloom
     3  
     4  import (
     5  	"errors"
     6  	"fmt"
     7  	"hash"
     8  	"hash/adler32"
     9  	"hash/crc32"
    10  	"hash/fnv"
    11  	"math/big"
    12  )
    13  
    14  type Filter interface {
    15  	Add([]byte)
    16  	Find([]byte) bool
    17  	Merge(Filter) (Filter, error)
    18  }
    19  
    20  func BasicFilter() Filter {
    21  	// Non crypto hashes, because speed
    22  	return NewFilter(2048, adler32.New(), fnv.New32(), crc32.NewIEEE())
    23  }
    24  
    25  func NewFilter(size int, hashes ...hash.Hash) Filter {
    26  	return &filter{
    27  		filter: make([]byte, size),
    28  		hashes: hashes,
    29  	}
    30  }
    31  
    32  type filter struct {
    33  	filter []byte
    34  	hashes []hash.Hash
    35  }
    36  
    37  func (f *filter) Add(k []byte) {
    38  	for _, h := range f.hashes {
    39  		i := bytesMod(h.Sum(k), int64(len(f.filter)*8))
    40  		f.setBit(i)
    41  	}
    42  }
    43  
    44  func (f *filter) Find(k []byte) bool {
    45  	for _, h := range f.hashes {
    46  		i := bytesMod(h.Sum(k), int64(len(f.filter)*8))
    47  		if !f.getBit(i) {
    48  			return false
    49  		}
    50  	}
    51  	return true
    52  }
    53  
    54  func (f *filter) setBit(i int64) {
    55  	fmt.Printf("setting bit %d\n", i)
    56  	f.filter[i/8] |= (1 << byte(i%8))
    57  }
    58  
    59  func (f *filter) getBit(i int64) bool {
    60  	fmt.Printf("getting bit %d\n", i)
    61  	return f.filter[i/8]&(1<<byte(i%8)) != 0
    62  }
    63  
    64  func bytesMod(b []byte, modulo int64) int64 {
    65  	i := big.NewInt(0)
    66  	i = i.SetBytes(b)
    67  
    68  	bigmod := big.NewInt(int64(modulo))
    69  	result := big.NewInt(0)
    70  	result.Mod(i, bigmod)
    71  
    72  	return result.Int64()
    73  }
    74  
    75  func (f *filter) Merge(o Filter) (Filter, error) {
    76  	casfil, ok := o.(*filter)
    77  	if !ok {
    78  		return nil, errors.New("Unsupported filter type")
    79  	}
    80  
    81  	if len(casfil.filter) != len(f.filter) {
    82  		return nil, errors.New("filter lengths must match!")
    83  	}
    84  
    85  	nfilt := new(filter)
    86  
    87  	// this bit is sketchy, need a way of comparing hash functions
    88  	nfilt.hashes = f.hashes
    89  
    90  	nfilt.filter = make([]byte, len(f.filter))
    91  	for i, v := range f.filter {
    92  		nfilt.filter[i] = v | casfil.filter[i]
    93  	}
    94  
    95  	return nfilt, nil
    96  }