Skip to content

Commit

Permalink
Merge pull request #16 from alexk307/feature-inverse-encoding
Browse files Browse the repository at this point in the history
Feature inverse encoding
  • Loading branch information
tylertreat committed Jan 13, 2017
2 parents 5642364 + a82d97a commit 6cd71b8
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 0 deletions.
98 changes: 98 additions & 0 deletions inverse.go
Expand Up @@ -33,8 +33,11 @@ package boom

import (
"bytes"
"encoding/binary"
"encoding/gob"
"hash"
"hash/fnv"
"io"
"sync"
"sync/atomic"
"unsafe"
Expand Down Expand Up @@ -138,3 +141,98 @@ func (i *InverseBloomFilter) index(data []byte) uint32 {
func (i *InverseBloomFilter) SetHashFactory(h func() hash.Hash32) {
i.hashPool = &sync.Pool{New: func() interface{} { return h() }}
}

// WriteTo writes a binary representation of the InverseBloomFilter to an i/o stream.
// It returns the number of bytes written.
func (i *InverseBloomFilter) WriteTo(stream io.Writer) (int64, error) {
err := binary.Write(stream, binary.BigEndian, uint64(i.capacity))
if err != nil {
return 0, err
}

// Dereference all pointers to []byte
array := make([][]byte, int(i.capacity))
for b := range i.array {
if i.array[b] != nil {
array[b] = *i.array[b]
} else {
array[b] = nil
}
}

// Encode array into a []byte
var buf bytes.Buffer
gob.NewEncoder(&buf).Encode(array)
serialized := buf.Bytes()

// Write the length of encoded slice
err = binary.Write(stream, binary.BigEndian, int64(len(serialized)))
if err != nil {
return 0, err
}

// Write the serialized bytes
written, err := stream.Write(serialized)
if err != nil {
return 0, err
}

return int64(written) + int64(2*binary.Size(uint64(0))), err
}

// ReadFrom reads a binary representation of InverseBloomFilter (such as might
// have been written by WriteTo()) from an i/o stream. It returns the number
// of bytes read.
func (i *InverseBloomFilter) ReadFrom(stream io.Reader) (int64, error) {
var capacity, size uint64

err := binary.Read(stream, binary.BigEndian, &capacity)
if err != nil {
return 0, err
}

err = binary.Read(stream, binary.BigEndian, &size)
if err != nil {
return 0, err
}

// Read the encoded slice and decode into [][]byte
encoded := make([]byte, size)
stream.Read(encoded)
buf := bytes.NewBuffer(encoded)
dec := gob.NewDecoder(buf)
decoded := make([][]byte, capacity)
dec.Decode(&decoded)

// Create []*[]byte and point to each item in decoded
decodedWithPointers := make([]*[]byte, capacity)
for p := range decodedWithPointers {
if len(decoded[p]) == 0 {
decodedWithPointers[p] = nil
} else {
decodedWithPointers[p] = &decoded[p]
}
}

i.array = decodedWithPointers
i.capacity = uint(capacity)
return int64(len(encoded)) + int64(2*binary.Size(uint64(0))), nil
}

// GobEncode implements gob.GobEncoder interface.
func (i *InverseBloomFilter) GobEncode() ([]byte, error) {
var buf bytes.Buffer
_, err := i.WriteTo(&buf)
if err != nil {
return nil, err
}

return buf.Bytes(), nil
}

// GobDecode implements gob.GobDecoder interface.
func (i *InverseBloomFilter) GobDecode(data []byte) error {
buf := bytes.NewBuffer(data)
_, err := i.ReadFrom(buf)
return err
}
40 changes: 40 additions & 0 deletions inverse_test.go
@@ -1,6 +1,9 @@
package boom

import (
"bytes"
"encoding/gob"
"github.com/d4l3k/messagediff"
"strconv"
"testing"
)
Expand Down Expand Up @@ -67,6 +70,43 @@ func TestInverseTestAndAdd(t *testing.T) {
}
}

// Tests that an InverseBloomFilter can be encoded and decoded properly without error
func TestInverseBloomFilter_Encode(t *testing.T) {
f := NewInverseBloomFilter(10000)

for i := 0; i < 1000; i++ {
f.Add([]byte(strconv.Itoa(i)))
}

var buf bytes.Buffer
if err := gob.NewEncoder(&buf).Encode(f); err != nil {
t.Error(err)
}

f2 := NewInverseBloomFilter(10000)
if err := gob.NewDecoder(&buf).Decode(f2); err != nil {
t.Error(err)
}

if f.capacity != f2.capacity {
t.Errorf("Different capacities")
}

if len(f.array) != len(f2.array) {
t.Errorf("Different data")
}

if diff, equal := messagediff.PrettyDiff(f.array, f2.array); !equal {
t.Errorf("BloomFilter Gob Encode and Decode = %+v; not %+v\n%s", f2, f, diff)
}

for i := 0; i < 100000; i++ {
if f.Test([]byte(strconv.Itoa(i))) != f2.Test([]byte(strconv.Itoa(i))) {
t.Errorf("Expected both filters to Test the same for %i", i)
}
}
}

func BenchmarkInverseAdd(b *testing.B) {
b.StopTimer()
f := NewInverseBloomFilter(100000)
Expand Down

0 comments on commit 6cd71b8

Please sign in to comment.