golang.org/x/exp@v0.0.0-20240506185415-9bf2ced13842/mmap/mmap_windows.go (about) 1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package mmap provides a way to memory-map a file. 6 package mmap 7 8 import ( 9 "errors" 10 "fmt" 11 "io" 12 "os" 13 "runtime" 14 "syscall" 15 "unsafe" 16 ) 17 18 // debug is whether to print debugging messages for manual testing. 19 // 20 // The runtime.SetFinalizer documentation says that, "The finalizer for x is 21 // scheduled to run at some arbitrary time after x becomes unreachable. There 22 // is no guarantee that finalizers will run before a program exits", so we 23 // cannot automatically test that the finalizer runs. Instead, set this to true 24 // when running the manual test. 25 const debug = false 26 27 // ReaderAt reads a memory-mapped file. 28 // 29 // Like any io.ReaderAt, clients can execute parallel ReadAt calls, but it is 30 // not safe to call Close and reading methods concurrently. 31 type ReaderAt struct { 32 data []byte 33 } 34 35 // Close closes the reader. 36 func (r *ReaderAt) Close() error { 37 if r.data == nil { 38 return nil 39 } else if len(r.data) == 0 { 40 r.data = nil 41 return nil 42 } 43 data := r.data 44 r.data = nil 45 if debug { 46 var p *byte 47 if len(data) != 0 { 48 p = &data[0] 49 } 50 println("munmap", r, p) 51 } 52 runtime.SetFinalizer(r, nil) 53 return syscall.UnmapViewOfFile(uintptr(unsafe.Pointer(&data[0]))) 54 } 55 56 // Len returns the length of the underlying memory-mapped file. 57 func (r *ReaderAt) Len() int { 58 return len(r.data) 59 } 60 61 // At returns the byte at index i. 62 func (r *ReaderAt) At(i int) byte { 63 return r.data[i] 64 } 65 66 // ReadAt implements the io.ReaderAt interface. 67 func (r *ReaderAt) ReadAt(p []byte, off int64) (int, error) { 68 if r.data == nil { 69 return 0, errors.New("mmap: closed") 70 } 71 if off < 0 || int64(len(r.data)) < off { 72 return 0, fmt.Errorf("mmap: invalid ReadAt offset %d", off) 73 } 74 n := copy(p, r.data[off:]) 75 if n < len(p) { 76 return n, io.EOF 77 } 78 return n, nil 79 } 80 81 // Open memory-maps the named file for reading. 82 func Open(filename string) (*ReaderAt, error) { 83 f, err := os.Open(filename) 84 if err != nil { 85 return nil, err 86 } 87 defer f.Close() 88 fi, err := f.Stat() 89 if err != nil { 90 return nil, err 91 } 92 93 size := fi.Size() 94 if size == 0 { 95 // Treat (size == 0) as a special case, avoiding the syscall, to be 96 // consistent with mmap_unix.go. 97 // 98 // As we do not call syscall.MapViewOfFile, there is no need to call 99 // runtime.SetFinalizer to enforce a balancing syscall.UnmapViewOfFile. 100 return &ReaderAt{ 101 data: make([]byte, 0), 102 }, nil 103 } 104 if size < 0 { 105 return nil, fmt.Errorf("mmap: file %q has negative size", filename) 106 } 107 if size != int64(int(size)) { 108 return nil, fmt.Errorf("mmap: file %q is too large", filename) 109 } 110 111 low, high := uint32(size), uint32(size>>32) 112 fmap, err := syscall.CreateFileMapping(syscall.Handle(f.Fd()), nil, syscall.PAGE_READONLY, high, low, nil) 113 if err != nil { 114 return nil, err 115 } 116 defer syscall.CloseHandle(fmap) 117 ptr, err := syscall.MapViewOfFile(fmap, syscall.FILE_MAP_READ, 0, 0, uintptr(size)) 118 if err != nil { 119 return nil, err 120 } 121 data := unsafe.Slice((*byte)(unsafe.Pointer(ptr)), size) 122 123 r := &ReaderAt{data: data} 124 if debug { 125 var p *byte 126 if len(data) != 0 { 127 p = &data[0] 128 } 129 println("mmap", r, p) 130 } 131 runtime.SetFinalizer(r, (*ReaderAt).Close) 132 return r, nil 133 }