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  }