forked from tidwall/mmap
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mmap.go
125 lines (117 loc) · 2.29 KB
/
mmap.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
package mmap
import (
"os"
"runtime"
"sync"
"unsafe"
"github.com/edsrzf/mmap-go"
)
type mapContext struct {
f *os.File
opened bool
}
var mmapMu sync.Mutex
var mmapFiles map[unsafe.Pointer]mapContext
// MapFile maps an opened file to a byte slice of data.
func MapFile(f *os.File, writable bool) (data []byte, err error) {
prot := mmap.RDONLY
if writable {
prot = mmap.RDWR
}
fi, err := f.Stat()
if err != nil {
return nil, err
}
if fi.Size() == 0 {
return nil, nil
}
m, err := mmap.Map(f, prot, 0)
if err != nil {
return nil, err
}
if len(m) == 0 {
// Empty file. Release map
m.Unmap()
f.Close()
return nil, nil
}
if runtime.GOOS == "windows" {
// Keep track of the file.
mmapMu.Lock()
if mmapFiles == nil {
mmapFiles = make(map[unsafe.Pointer]mapContext)
}
mmapFiles[unsafe.Pointer(&m[0])] = mapContext{f, false}
mmapMu.Unlock()
}
return []byte(m), nil
}
// Open will mmap a file to a byte slice of data.
func Open(path string, writable bool) (data []byte, err error) {
flag := os.O_RDONLY
if writable {
flag = os.O_RDWR
}
f, err := os.OpenFile(path, flag, 0)
if err != nil {
return nil, err
}
m, err := MapFile(f, writable)
if err != nil {
f.Close()
return nil, err
}
if len(m) == 0 {
// Empty file. Release map
Close(m)
f.Close()
return nil, nil
}
if runtime.GOOS == "windows" {
mmapMu.Lock()
ctx := mmapFiles[unsafe.Pointer(&m[0])]
ctx.opened = true
mmapFiles[unsafe.Pointer(&m[0])] = ctx
mmapMu.Unlock()
} else {
// Allowed to close the file.
f.Close()
}
return []byte(m), nil
}
// Close releases the data.
func Close(data []byte) error {
if len(data) == 0 {
return nil
}
if runtime.GOOS == "windows" {
// Close file first
var ctx mapContext
var ok bool
mmapMu.Lock()
ctx, ok = mmapFiles[unsafe.Pointer(&data[0])]
if ok {
delete(mmapFiles, unsafe.Pointer(&data[0]))
}
mmapMu.Unlock()
if ok && ctx.opened {
ctx.f.Close()
}
}
m := mmap.MMap(data)
return m.Unmap()
}
// Create a new mmap file with the provided size
func Create(path string, size int) ([]byte, error) {
f, err := os.Create(path)
if err != nil {
return nil, err
}
if _, err := f.WriteAt([]byte{0}, int64(size)-1); err != nil {
return nil, err
}
if err := f.Close(); err != nil {
return nil, err
}
return Open(path, true)
}