-
Notifications
You must be signed in to change notification settings - Fork 4
/
marshal.go
126 lines (98 loc) · 2.45 KB
/
marshal.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// marshal.go - Marshal/Unmarshal for BBHash datastructure
//
// Implements the BBHash algorithm in: https://arxiv.org/abs/1702.03154
//
// Inspired by D Gryski's implementation of BBHash (https://github.com/dgryski/go-boomphf)
//
// (c) Sudhi Herle 2018
//
// License GPLv2
package bbhash
import (
"bytes"
"fmt"
"io"
"encoding/binary"
)
// MarshalBinary encodes the hash into a binary form suitable for durable storage.
// A subsequent call to UnmarshalBinary() will reconstruct the BBHash instance.
func (bb *BBHash) MarshalBinary(w io.Writer) error {
// Header: 4 64-bit words:
// o version
// o n-bitvectors
// o salt
// o resv
//
// Body:
// o <n> bitvectors laid out consecutively
var b bytes.Buffer
var x [8]byte
le := binary.LittleEndian
le.PutUint64(x[:], 1) // version 1
b.Write(x[:])
le.PutUint64(x[:], uint64(len(bb.bits)))
b.Write(x[:])
le.PutUint64(x[:], bb.salt)
b.Write(x[:])
le.PutUint64(x[:], 0) // reserved byte
b.Write(x[:])
n, err := w.Write(b.Bytes())
if err != nil {
return err
}
if n != b.Len() {
errShortWrite(n)
}
// Now, write the bitvectors themselves
for _, bv := range bb.bits {
err = bv.MarshalBinary(w)
if err != nil {
return err
}
}
// We don't store the rank vector; we can re-compute it when we unmarshal
// the bitvectors.
return nil
}
// MarshalBinarySize returns the size of the marshaled bbhash (in bytes)
func (bb *BBHash) MarshalBinarySize() uint64 {
var z uint64 = 4 * 8 // header
for _, bv := range bb.bits {
z += bv.MarshalBinarySize()
}
return z
}
// UnmarshalBBHash reads a previously marshalled binary stream from 'r' and recreates
// the in-memory instance of BBHash.
func UnmarshalBBHash(r io.Reader) (*BBHash, error) {
var b [32]byte // 4 x 64-bit words of header
_, err := io.ReadFull(r, b[:])
if err != nil {
return nil, err
}
le := binary.LittleEndian
v := le.Uint64(b[:8])
if v != 1 {
return nil, fmt.Errorf("bbhash: no support to un-marshal version %d", v)
}
v = le.Uint64(b[8:16])
if v == 0 || v > uint64(MaxLevel) {
return nil, fmt.Errorf("bbhash: invalid levels %d (max %d)", v, MaxLevel)
}
bb := &BBHash{
bits: make([]*bitVector, v),
salt: le.Uint64(b[16:24]),
}
for i := uint64(0); i < v; i++ {
bv, err := unmarshalbitVector(r)
if err != nil {
return nil, err
}
bb.bits[i] = bv
}
bb.preComputeRank()
return bb, nil
}
func errShortWrite(n int) error {
return fmt.Errorf("bbhash: incomplete write; exp 8, saw %d", n)
}