File: binaryunmarshaler.go

package info (click to toggle)
golang-github-holiman-bloomfilter 2.0.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 144 kB
  • sloc: makefile: 2
file content (130 lines) | stat: -rw-r--r-- 3,008 bytes parent folder | download
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
126
127
128
129
130
// Package bloomfilter is face-meltingly fast, thread-safe,
// marshalable, unionable, probability- and
// optimal-size-calculating Bloom filter in go
//
// https://github.com/steakknife/bloomfilter
//
// Copyright © 2014, 2015, 2018 Barry Allard
//
// MIT license
//
package v2

import (
	"bytes"
	"crypto/sha512"
	"encoding/binary"
	"fmt"
	"hash"
	"io"
)

func unmarshalBinaryHeader(r io.Reader) (k, n, m uint64, err error) {
	magic := make([]byte, len(headerMagic))
	if _, err := io.ReadFull(r, magic); err != nil {
		return 0, 0, 0, err
	}
	if !bytes.Equal(magic, headerMagic) {
		return 0, 0, 0, fmt.Errorf("incompatible version (wrong magic), got %x", magic)
	}
	var knm = make([]uint64, 3)
	err = binary.Read(r, binary.LittleEndian, knm)
	if err != nil {
		return 0, 0, 0, err
	}
	k = knm[0]
	n = knm[1]
	m = knm[2]
	if k < KMin {
		return 0, 0, 0, fmt.Errorf("keys must have length %d or greater (was %d)", KMin, k)
	}
	if m < MMin {
		return 0, 0, 0, fmt.Errorf("number of bits in the filter must be >= %d (was %d)", MMin, m)
	}
	return k, n, m, err
}

func unmarshalBinaryBits(r io.Reader, m uint64) (bits []uint64, err error) {
	bits, err = newBits(m)
	if err != nil {
		return bits, err
	}
	bs := make([]byte, 8)
	for i := 0; i < len(bits) && err == nil; i++ {
		_, err = io.ReadFull(r, bs)
		bits[i] = binary.LittleEndian.Uint64(bs)
	}
	if err != nil {
		return nil, err
	}
	return bits, nil
}

func unmarshalBinaryKeys(r io.Reader, k uint64) (keys []uint64, err error) {
	keys = make([]uint64, k)
	err = binary.Read(r, binary.LittleEndian, keys)
	return keys, err
}

// hashingReader can be used to read from a reader, and simultaneously
// do a hash on the bytes that were read
type hashingReader struct {
	reader io.Reader
	hasher hash.Hash
	tot    int64
}

func (h *hashingReader) Read(p []byte) (n int, err error) {
	n, err = h.reader.Read(p)
	h.tot += int64(n)
	if err != nil {
		return n, err
	}
	_, _ = h.hasher.Write(p[:n])
	return n, err
}

// UnmarshalBinary converts []bytes into a Filter
// conforms to encoding.BinaryUnmarshaler
func (f *Filter) UnmarshalBinary(data []byte) (err error) {
	buf := bytes.NewBuffer(data)
	_, err = f.UnmarshalFromReader(buf)
	return err
}

func (f *Filter) UnmarshalFromReader(input io.Reader) (n int64, err error) {
	f.lock.Lock()
	defer f.lock.Unlock()

	buf := &hashingReader{
		reader: input,
		hasher: sha512.New384(),
	}
	var k uint64
	k, f.n, f.m, err = unmarshalBinaryHeader(buf)
	if err != nil {
		return buf.tot, err
	}

	f.keys, err = unmarshalBinaryKeys(buf, k)
	if err != nil {
		return buf.tot, err
	}
	f.bits, err = unmarshalBinaryBits(buf, f.m)
	if err != nil {
		return buf.tot, err
	}

	// Only the hash remains to be read now
	// so abort the hasher at this point
	gotHash := buf.hasher.Sum(nil)
	expHash := make([]byte, sha512.Size384)
	err = binary.Read(buf, binary.LittleEndian, expHash)
	if err != nil {
		return buf.tot, err
	}
	if !bytes.Equal(gotHash, expHash) {
		return buf.tot, errHashMismatch
	}
	return buf.tot, nil
}