/
map.go
120 lines (108 loc) · 2.94 KB
/
map.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
// Copyright 2018 GRAIL, Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package mapio
import (
"errors"
"io"
"sync"
)
// Map is a read-only, sorted map backed by an io.ReadSeeker. The
// on-disk layout of maps are described by the package documentation.
// Maps support both lookup and (ordered) iteration. A Map instance
// maintains a current position, starting out at the first entry.
type Map struct {
mu sync.Mutex
r io.ReadSeeker
index block
}
// New opens the map at the provided io.ReadSeeker (usually a file).
func New(r io.ReadSeeker) (*Map, error) {
m := &Map{r: r}
return m, m.init()
}
func (m *Map) init() error {
if _, err := m.r.Seek(-mapTrailerSize, io.SeekEnd); err != nil {
return err
}
trailer := make([]byte, mapTrailerSize)
if _, err := io.ReadFull(m.r, trailer); err != nil {
return err
}
metaAddr, _ := getBlockAddr(trailer)
if metaAddr != (blockAddr{}) {
return errors.New("non-empty meta block index")
}
indexAddr, _ := getBlockAddr(trailer[maxBlockAddrSize:])
magic := order.Uint64(trailer[len(trailer)-8:])
if magic != mapTrailerMagic {
return errors.New("wrong magic")
}
if err := m.readBlock(indexAddr, &m.index); err != nil {
return err
}
if !m.index.Scan() {
return errors.New("empty index")
}
return nil
}
func (m *Map) readBlock(addr blockAddr, block *block) error {
if block.p != nil && cap(block.p) >= int(addr.len) {
block.p = block.p[:addr.len]
} else {
block.p = make([]byte, addr.len)
}
m.mu.Lock()
defer m.mu.Unlock()
if _, err := m.r.Seek(int64(addr.off), io.SeekStart); err != nil {
return err
}
if _, err := io.ReadFull(m.r, block.p); err != nil {
return err
}
return block.init()
}
// Seek returns a map scanner beginning at the first key in the map
// >= the provided key.
func (m *Map) Seek(key []byte) *MapScanner {
s := &MapScanner{parent: m, index: m.index}
s.index.Seek(key)
if s.index.Scan() {
addr, _ := getBlockAddr(s.index.Value())
if s.err = m.readBlock(addr, &s.data); s.err == nil {
s.data.Seek(key)
}
}
return s
}
// MapScanner implements ordered iteration over a map.
type MapScanner struct {
parent *Map
err error
data, index block
}
// Scan scans the next entry, returning true on success. When Scan
// returns false, the caller should inspect Err to distinguish
// between scan completion and scan error.
func (m *MapScanner) Scan() bool {
for m.err == nil && !m.data.Scan() {
if !m.index.Scan() {
return false
}
addr, _ := getBlockAddr(m.index.Value())
m.err = m.parent.readBlock(addr, &m.data)
}
return m.err == nil
}
// Err returns the last error encountered while scanning.
func (m *MapScanner) Err() error {
return m.err
}
// Key returns the key that was last scanned.
func (m *MapScanner) Key() []byte {
return m.data.Key()
}
// Value returns the value that was last scanned.
func (m *MapScanner) Value() []byte {
return m.data.Value()
}