Skip to content

Commit 5a59d5e

Browse files
authored
Merge pull request #5 from yourbasic/tip
Adds binary format
2 parents 05de11b + 828a5e2 commit 5a59d5e

4 files changed

Lines changed: 107 additions & 4 deletions

File tree

binary.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package bloom
2+
3+
import (
4+
"bytes"
5+
"encoding/gob"
6+
)
7+
8+
const binVer0 = 0
9+
10+
// Data to be included in binary representation of Filter.
11+
type marshalFilter struct {
12+
Version int
13+
Data []uint64
14+
Lookups int
15+
Count int64
16+
}
17+
18+
// MarshalBinary returns a binary representation of the filter.
19+
//
20+
// This method implements the encoding.BinaryMarshaler interface.
21+
// The packages encoding/gob, encoding/json, and encoding/xml
22+
// all check for this interface.
23+
func (f *Filter) MarshalBinary() ([]byte, error) {
24+
mf := marshalFilter{
25+
Version: binVer0,
26+
Data: f.data,
27+
Lookups: f.lookups,
28+
Count: f.count,
29+
}
30+
var b bytes.Buffer
31+
err := gob.NewEncoder(&b).Encode(mf)
32+
return b.Bytes(), err
33+
}
34+
35+
// UnmarshalBinary imports binary data created by MarshalBinary
36+
// into an empty filter. If the filter is not empty, all previous
37+
// entries are overwritten.
38+
func (f *Filter) UnmarshalBinary(data []byte) error {
39+
var mf marshalFilter
40+
err := gob.NewDecoder(bytes.NewBuffer(data)).Decode(&mf)
41+
if err != nil {
42+
return err
43+
}
44+
f.data = mf.Data
45+
f.lookups = mf.Lookups
46+
f.count = mf.Count
47+
return nil
48+
}

example_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package bloom_test
22

33
import (
4+
"bytes"
5+
"encoding/gob"
46
"fmt"
57
"github.com/yourbasic/bloom"
8+
"log"
69
"math/rand"
710
"strconv"
811
)
@@ -72,3 +75,30 @@ func ExampleFilter_Union() {
7275
fmt.Println("f1 ∪ f2:", f1.Union(f2).Count())
7376
// Output: f1 ∪ f2: 505
7477
}
78+
79+
// Send a filter over a network using the encoding/gob package.
80+
func ExampleFilter_MarshalBinary_network() {
81+
// Create a mock network and a new Filter.
82+
var network bytes.Buffer
83+
f1 := bloom.New(1000, 100)
84+
f1.Add("Hello, filter!")
85+
86+
// Create an encoder and send the filter to the network.
87+
enc := gob.NewEncoder(&network)
88+
if err := enc.Encode(f1); err != nil {
89+
log.Fatal("encode error:", err)
90+
}
91+
92+
// Create a decoder and receive the filter from the network.
93+
dec := gob.NewDecoder(&network)
94+
var f2 bloom.Filter
95+
if err := dec.Decode(&f2); err != nil {
96+
log.Fatal("decode error:", err)
97+
}
98+
99+
// Check that we got the same filter back.
100+
if f2.Test("Hello, filter!") {
101+
fmt.Println("Filter arrived safely.")
102+
}
103+
// Output: Filter arrived safely.
104+
}

filter.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@
55
// A Bloom filter is a fast and space-efficient probabilistic data structure
66
// used to test set membership.
77
//
8-
// A membership test returns either
9-
// ”likely member” or ”definitely not a member”. Only false positives
10-
// can occur: an element that has been added to the filter
11-
// will be identified as ”likely member”.
8+
// A membership test returns either ”likely member” or ”definitely not
9+
// a member”. Only false positives can occur: an element that has been added
10+
// to the filter will always be identified as ”likely member”.
1211
//
1312
// Elements can be added, but not removed. With more elements in the filter,
1413
// the probability of false positives increases.
@@ -97,11 +96,13 @@ func (f *Filter) add(h1, h2 uint64) bool {
9796
}
9897

9998
// TestByte tells if b is a likely member of the filter.
99+
// If true, b is probably a member; if false, b is definitely not a member.
100100
func (f *Filter) TestByte(b []byte) bool {
101101
return f.test(hash(b))
102102
}
103103

104104
// Test tells if s is a likely member of the filter.
105+
// If true, s is probably a member; if false, s is definitely not a member.
105106
func (f *Filter) Test(s string) bool {
106107
return f.test(hashString(s))
107108
}

filter_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package bloom
22

33
import (
4+
"bytes"
5+
"encoding/gob"
6+
"reflect"
47
"testing"
58
)
69

@@ -140,6 +143,27 @@ func TestUnion(t *testing.T) {
140143
}
141144
}
142145

146+
func TestMarshal(t *testing.T) {
147+
var network bytes.Buffer
148+
f1 := New(10000, 100)
149+
f1.Add("Hello, filter!")
150+
151+
enc := gob.NewEncoder(&network)
152+
if err := enc.Encode(f1); err != nil {
153+
t.Errorf("Encode->err = %v; want nil\n", err)
154+
}
155+
156+
dec := gob.NewDecoder(&network)
157+
var f2 *Filter
158+
if err := dec.Decode(&f2); err != nil {
159+
t.Errorf("Decode->err = %v; want nil\n", err)
160+
}
161+
162+
if !reflect.DeepEqual(f1, f2) {
163+
t.Errorf("Encode(Code(f)) = %v; want %v\n", f1, f2)
164+
}
165+
}
166+
143167
var fox string = "The quick brown fox jumps over the lazy dog."
144168

145169
func BenchmarkAdd(b *testing.B) {

0 commit comments

Comments
 (0)