-
Notifications
You must be signed in to change notification settings - Fork 0
/
xorstore.go
151 lines (123 loc) · 3.09 KB
/
xorstore.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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package streisand
import (
"fmt"
"log"
"os"
"path/filepath"
"syscall"
"github.com/Jille/errchain"
)
func (s *XorStore) Add(h *Hash) {
for i := range s.layers {
s.layers[i].Add(h)
}
}
func (s *XorStore) GetLeaf(h *Hash) Hash {
return s.layers[len(s.layers)-1].Get(h)
}
type XorStore struct {
LayerCount int
LayerDepth int
Path string
layers []Layer
}
func (s *XorStore) Depth() int {
return s.LayerCount * s.LayerDepth
}
func (s *XorStore) Initialize() (err error) {
s.layers = make([]Layer, s.LayerCount)
for i := range s.layers {
layerName := fmt.Sprintf("xors-%d-layer-%d", s.LayerDepth, i)
s.layers[i] = Layer{
Path: filepath.Join(s.Path, layerName),
PrefixLength: uint((i + 1) * s.LayerDepth),
}
err = s.layers[i].Initialize()
if err != nil {
return fmt.Errorf("init layer %d: %w", i, err)
}
}
return
}
func (s *XorStore) Close() (err error) {
for _, layer := range s.layers {
errchain.Call(&err, layer.Close)
}
return
}
type Layer struct {
Path string
PrefixLength uint
mmap []byte
}
func (l *Layer) Initialize() (retErr error) {
if l.PrefixLength > 32 {
return fmt.Errorf("prefix length too great: %d > 32",
l.PrefixLength)
}
// open or create the file that holds the xors of this layer
f, err := os.OpenFile(l.Path, os.O_RDWR|os.O_CREATE, 0644)
if err != nil {
return err
}
// the file needn't be kept open for the mmap to persist
defer errchain.Call(&retErr, f.Close)
// check the file has the correct size, or, when it has size 0,
// truncate the file to the correct size.
xorCount := 1 << l.PrefixLength
expectedSize := pagesizemult(xorCount * BytesPerHash)
fi, err := f.Stat()
if err != nil {
return err
}
if size := fi.Size(); size != int64(expectedSize) {
if size != 0 {
// might be a misconfiguration, so let's not
// try to be clever
return fmt.Errorf("something is wrong: "+
"%s has size %d instead of %d",
l.Path, size, expectedSize)
}
// file was probably just created, so let us fix its size
log.Printf("truncating %v to %d", l.Path, expectedSize)
if err := f.Truncate(int64(expectedSize)); err != nil {
return err
}
// check truncate worked
fi, err = f.Stat()
if err != nil {
return err
}
if fi.Size() != int64(expectedSize) {
return fmt.Errorf("failed to correct size of %s from "+
"0 to %d", l.Path, expectedSize)
}
}
l.mmap, err = syscall.Mmap(int(f.Fd()), 0, expectedSize,
syscall.PROT_READ|syscall.PROT_WRITE,
syscall.MAP_SHARED, // carry changes to the underlying file
)
if err != nil {
return fmt.Errorf("mmap: %w", err)
}
return
}
func (l *Layer) Close() (err error) {
return syscall.Munmap(l.mmap)
}
func (l *Layer) Add(h *Hash) {
idx := BytesPerHash * h.PrefixToNumber(l.PrefixLength)
h.XorInto(l.mmap[idx : idx+BytesPerHash])
}
func (l *Layer) Get(h *Hash) Hash {
idx := BytesPerHash * h.PrefixToNumber(l.PrefixLength)
return *(*Hash)(l.mmap[idx : idx+BytesPerHash])
}
var pagesize = os.Getpagesize()
func pagesizemult(size int) int {
pagecount := size / pagesize
if size%pagesize > 0 {
pagecount++
}
return pagecount * pagesize
}