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  }