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
}
|