@@ -14,11 +14,24 @@ import (
14
14
"bytes"
15
15
"crypto/sha512"
16
16
"encoding/binary"
17
+ "io"
17
18
)
18
19
20
+ // counter is a utility to count bytes written
21
+ type counter struct {
22
+ bytes int
23
+ }
24
+
25
+ func (c * counter ) Write (p []byte ) (n int , err error ) {
26
+ count := len (p )
27
+ c .bytes += count
28
+ return count , nil
29
+ }
30
+
19
31
// conforms to encoding.BinaryMarshaler
20
32
21
- // marshalled binary layout (Little Endian):
33
+ // MarshallToWriter marshalls the filter into the given io.Writer
34
+ // Binary layout (Little Endian):
22
35
//
23
36
// k 1 uint64
24
37
// n 1 uint64
@@ -29,59 +42,68 @@ import (
29
42
//
30
43
// size = (3 + k + (m+63)/64) * 8 bytes
31
44
//
32
-
33
- func (f * Filter ) marshal () (buf * bytes.Buffer ,
34
- hash [sha512 .Size384 ]byte ,
35
- err error ,
36
- ) {
45
+ func (f * Filter ) MarshallToWriter (out io.Writer ) (int , [sha512 .Size384 ]byte , error ) {
46
+ var (
47
+ c = & counter {0 }
48
+ hasher = sha512 .New384 ()
49
+ mw = io .MultiWriter (out , hasher , c )
50
+ hash [sha512 .Size384 ]byte
51
+ )
37
52
f .lock .RLock ()
38
53
defer f .lock .RUnlock ()
39
-
40
54
debug ("write bf k=%d n=%d m=%d\n " , f .K (), f .n , f .m )
41
55
42
- buf = new (bytes.Buffer )
43
-
44
- err = binary .Write (buf , binary .LittleEndian , f .K ())
45
- if err != nil {
46
- return nil , hash , err
56
+ if err := binary .Write (mw , binary .LittleEndian , f .K ()); err != nil {
57
+ return c .bytes , hash , err
47
58
}
48
-
49
- err = binary .Write (buf , binary .LittleEndian , f .n )
50
- if err != nil {
51
- return nil , hash , err
59
+ if err := binary .Write (mw , binary .LittleEndian , f .n ); err != nil {
60
+ return c .bytes , hash , err
52
61
}
53
-
54
- err = binary .Write (buf , binary .LittleEndian , f .m )
55
- if err != nil {
56
- return nil , hash , err
62
+ if err := binary .Write (mw , binary .LittleEndian , f .m ); err != nil {
63
+ return c .bytes , hash , err
57
64
}
58
-
59
- err = binary .Write (buf , binary .LittleEndian , f .keys )
60
- if err != nil {
61
- return nil , hash , err
65
+ if err := binary .Write (mw , binary .LittleEndian , f .keys ); err != nil {
66
+ return c .bytes , hash , err
62
67
}
63
-
64
- err = binary .Write (buf , binary .LittleEndian , f .bits )
68
+ // Write it in chunks of 5% (but at least 4K). Otherwise, the binary.Write will allocate a
69
+ // same-size slice of bytes, doubling the memory usage
70
+ var chunkSize = len (f .bits ) / 20
71
+ if chunkSize < 512 {
72
+ chunkSize = 512 // Min 4K bytes (512 uint64s)
73
+ }
74
+ bs := make ([]byte , chunkSize * 8 )
75
+ for start := 0 ; start < len (f .bits ); {
76
+ end := start + chunkSize
77
+ if end > len (f .bits ) {
78
+ end = len (f .bits )
79
+ }
80
+ for i , x := range f .bits [start :end ] {
81
+ binary .LittleEndian .PutUint64 (bs [8 * i :], x )
82
+ }
83
+ if _ , err := mw .Write (bs [0 : (end - start )* 8 ]); err != nil {
84
+ return c .bytes , hash , err
85
+ }
86
+ start = end
87
+ }
88
+ // Now we stop using the multiwriter, pick out the hash of what we've
89
+ // written so far, and then write the hash to the output
90
+ hashbytes := hasher .Sum (nil )
91
+ copy (hash [:], hashbytes [:sha512 .Size384 ])
92
+ err := binary .Write (out , binary .LittleEndian , hashbytes )
65
93
if err != nil {
66
- return nil , hash , err
94
+ debug ("bloomfilter.MarshalBinary: Successfully wrote %d byte(s), sha384 %v" ,
95
+ c .bytes , hash )
67
96
}
68
-
69
- hash = sha512 .Sum384 (buf .Bytes ())
70
- err = binary .Write (buf , binary .LittleEndian , hash )
71
- return buf , hash , err
97
+ return c .bytes , hash , err
72
98
}
73
99
74
100
// MarshalBinary converts a Filter into []bytes
75
101
func (f * Filter ) MarshalBinary () (data []byte , err error ) {
76
- buf , hash , err := f .marshal ()
102
+ buf := new (bytes.Buffer )
103
+ _ , _ , err = f .MarshallToWriter (buf )
77
104
if err != nil {
78
105
return nil , err
79
106
}
80
-
81
- debug (
82
- "bloomfilter.MarshalBinary: Successfully wrote %d byte(s), sha384 %v" ,
83
- buf .Len (), hash ,
84
- )
85
107
data = buf .Bytes ()
86
108
return data , nil
87
109
}
0 commit comments