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