-
Notifications
You must be signed in to change notification settings - Fork 0
/
reader.go
100 lines (74 loc) · 1.69 KB
/
reader.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
package diskbst
import (
"bytes"
"errors"
"os"
"syscall"
)
type reader struct {
data []byte // a byte slice that represents the entire contents of the BST file loaded into memory via memory mapping
}
type Reader interface {
Get(key []byte) ([]byte, error)
Close()
}
func OpenReader(pathName string) (Reader, error) {
var r reader
f, err := os.Open(pathName)
if err != nil {
return nil, err
}
defer f.Close()
// validate magic number
mn := make([]byte, len(magicNumber))
_, err = f.Read(mn)
if err != nil {
return nil, err
}
if bytes.Compare(mn, magicNumber) != 0 {
return nil, errInvalidMagicNumber
}
info, err := f.Stat()
if err != nil {
return nil, err
}
d, err := syscall.Mmap(int(f.Fd()), 0, int(info.Size()), syscall.PROT_READ, syscall.MAP_PRIVATE)
if err != nil {
return nil, err
}
r.data = d
return &r, nil
}
func (r *reader) Get(key []byte) ([]byte, error) {
if len(r.data) == len(magicNumber) {
return nil, errKeyNotFound
}
var currNode node
// skip node size as it is not utilized by the reader
currPos := uint64(len(magicNumber))
currPos += 8
for currPos < uint64(len(r.data)) {
currNode.deserialize(r.data[currPos:])
if bytes.Compare(key, currNode.key) == 0 {
return currNode.value, nil
}
var next uint64
if bytes.Compare(key, currNode.key) <= 0 {
next = currNode.leftChild
}
if bytes.Compare(key, currNode.key) > 0 {
next = currNode.rightChild
}
if next == 0 {
return nil, errKeyNotFound
}
currPos = next
// skip node size as it is not utilized by the reader
currPos += 8
}
return nil, errKeyNotFound
}
func (r *reader) Close() {
_ = syscall.Munmap(r.data)
}
var errKeyNotFound = errors.New("key not found")