-
Notifications
You must be signed in to change notification settings - Fork 212
/
Copy pathmmap_windows.go
133 lines (121 loc) · 3.2 KB
/
mmap_windows.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
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package mmap provides a way to memory-map a file.
package mmap
import (
"errors"
"fmt"
"io"
"os"
"runtime"
"syscall"
"unsafe"
)
// debug is whether to print debugging messages for manual testing.
//
// The runtime.SetFinalizer documentation says that, "The finalizer for x is
// scheduled to run at some arbitrary time after x becomes unreachable. There
// is no guarantee that finalizers will run before a program exits", so we
// cannot automatically test that the finalizer runs. Instead, set this to true
// when running the manual test.
const debug = false
// ReaderAt reads a memory-mapped file.
//
// Like any io.ReaderAt, clients can execute parallel ReadAt calls, but it is
// not safe to call Close and reading methods concurrently.
type ReaderAt struct {
data []byte
}
// Close closes the reader.
func (r *ReaderAt) Close() error {
if r.data == nil {
return nil
} else if len(r.data) == 0 {
r.data = nil
return nil
}
data := r.data
r.data = nil
if debug {
var p *byte
if len(data) != 0 {
p = &data[0]
}
println("munmap", r, p)
}
runtime.SetFinalizer(r, nil)
return syscall.UnmapViewOfFile(uintptr(unsafe.Pointer(&data[0])))
}
// Len returns the length of the underlying memory-mapped file.
func (r *ReaderAt) Len() int {
return len(r.data)
}
// At returns the byte at index i.
func (r *ReaderAt) At(i int) byte {
return r.data[i]
}
// ReadAt implements the io.ReaderAt interface.
func (r *ReaderAt) ReadAt(p []byte, off int64) (int, error) {
if r.data == nil {
return 0, errors.New("mmap: closed")
}
if off < 0 || int64(len(r.data)) < off {
return 0, fmt.Errorf("mmap: invalid ReadAt offset %d", off)
}
n := copy(p, r.data[off:])
if n < len(p) {
return n, io.EOF
}
return n, nil
}
// Open memory-maps the named file for reading.
func Open(filename string) (*ReaderAt, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()
fi, err := f.Stat()
if err != nil {
return nil, err
}
size := fi.Size()
if size == 0 {
// Treat (size == 0) as a special case, avoiding the syscall, to be
// consistent with mmap_unix.go.
//
// As we do not call syscall.MapViewOfFile, there is no need to call
// runtime.SetFinalizer to enforce a balancing syscall.UnmapViewOfFile.
return &ReaderAt{
data: make([]byte, 0),
}, nil
}
if size < 0 {
return nil, fmt.Errorf("mmap: file %q has negative size", filename)
}
if size != int64(int(size)) {
return nil, fmt.Errorf("mmap: file %q is too large", filename)
}
low, high := uint32(size), uint32(size>>32)
fmap, err := syscall.CreateFileMapping(syscall.Handle(f.Fd()), nil, syscall.PAGE_READONLY, high, low, nil)
if err != nil {
return nil, err
}
defer syscall.CloseHandle(fmap)
ptr, err := syscall.MapViewOfFile(fmap, syscall.FILE_MAP_READ, 0, 0, uintptr(size))
if err != nil {
return nil, err
}
data := unsafe.Slice((*byte)(unsafe.Pointer(ptr)), size)
r := &ReaderAt{data: data}
if debug {
var p *byte
if len(data) != 0 {
p = &data[0]
}
println("mmap", r, p)
}
runtime.SetFinalizer(r, (*ReaderAt).Close)
return r, nil
}