-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathFileMapped.go
140 lines (119 loc) · 3.19 KB
/
FileMapped.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
package wingo
import (
"github.com/rogeecn/wingo/util"
"strings"
"unsafe"
"github.com/rogeecn/wingo/co"
)
// High-level abstraction to HFILEMAP, providing several operations.
//
// Created with FileMappedOpen().
type FileMapped struct {
objFile *File
hMap HFILEMAP
pMem HFILEMAPVIEW
sz int
readOnly bool
}
// Opens a memory-mapped file, returning a new high-level FileMapped object.
//
// ⚠️ You must defer FileMapped.Close().
func FileMappedOpen(
filePath string, desiredAccess co.FILE_OPEN) (*FileMapped, error) {
objFile, err := FileOpen(filePath, desiredAccess)
if err != nil {
return nil, err
}
me := &FileMapped{
objFile: objFile,
hMap: HFILEMAP(0),
pMem: HFILEMAPVIEW(0),
sz: 0,
readOnly: desiredAccess == co.FILE_OPEN_READ_EXISTING,
}
if err := me.mapInMemory(); err != nil {
me.Close()
return nil, err
}
return me, nil
}
// Unmaps and releases the file resource.
func (me *FileMapped) Close() {
if me.pMem != 0 {
me.pMem.UnmapViewOfFile()
me.pMem = 0
}
if me.hMap != 0 {
me.hMap.CloseHandle()
me.hMap = 0
}
me.objFile.Close()
me.sz = 0
}
// Returns a slice to the memory-mapped bytes. The FileMapped object must remain
// open while the slice is being used.
//
// If you need to close the file right away, use CopyToBuffer() instead.
func (me *FileMapped) HotSlice() []byte {
return unsafe.Slice((*byte)(unsafe.Pointer(me.pMem)), me.sz)
}
// Returns a new []byte with a copy of all data in the file.
func (me *FileMapped) ReadAll() []byte {
return me.ReadChunk(0, me.sz)
}
// Returns a new []byte with a copy of data, start with offset, and with the
// undefined given undefined length.
func (me *FileMapped) ReadChunk(offset, length int) []byte {
hotSlice := me.HotSlice()
buf := make([]byte, length)
copy(buf, hotSlice[offset:offset+length])
return buf
}
// Parses the file content as text and returns the lines.
func (me *FileMapped) ReadLines() []string {
allText := string(me.HotSlice())
lines := strings.Split(allText, "\n")
for i := 0; i < len(lines); i++ {
line := lines[i]
if line[len(line)-1] == '\r' {
lines[i] = line[:len(line)-1] // trim trailing \r
}
}
return lines
}
// Truncates or expands the file, according to the new size. Zero will empty the
// file.
//
// Internally, the file is unmapped, then remapped back into memory.
func (me *FileMapped) Resize(numBytes int) error {
me.pMem.UnmapViewOfFile()
me.hMap.CloseHandle()
if err := me.objFile.Resize(numBytes); err != nil {
return err
}
return me.mapInMemory()
}
// Retrieves the file size. This value is cached.
func (me *FileMapped) Size() int {
return me.sz
}
func (me *FileMapped) mapInMemory() error {
// Mapping into memory.
pageFlags := util.Iif(me.readOnly,
co.PAGE_READONLY, co.PAGE_READWRITE).(co.PAGE)
var err error
me.hMap, err = me.objFile.Hfile().
CreateFileMapping(nil, pageFlags, co.SEC_NONE, 0, nil)
if err != nil {
return err
}
// Get pointer to data block.
mapFlags := util.Iif(me.readOnly,
co.FILE_MAP_READ, co.FILE_MAP_WRITE).(co.FILE_MAP)
if me.pMem, err = me.hMap.MapViewOfFile(mapFlags, 0, 0); err != nil {
return err
}
// Cache file size.
me.sz = me.objFile.Size()
return nil // file mapped successfully
}