github.com/scottcagno/storage@v1.8.0/pkg/mmap/mmap.go (about)

     1  package mmap
     2  
     3  import (
     4  	"github.com/scottcagno/storage/pkg/mmap/segment"
     5  	"io"
     6  	"math"
     7  )
     8  
     9  // MaxInt is the maximum platform dependent signed integer.
    10  const MaxInt = int(^uint(0) >> 1)
    11  
    12  // Mode is a mapping mode.
    13  type Mode int
    14  
    15  const (
    16  	// Share this mapping and allow the read-only access
    17  	ModeReadOnly Mode = iota
    18  
    19  	// Share this mapping
    20  	// Updates to the mapping are visible to other processes
    21  	// mapping the same region, and are carried through to the underlying file.
    22  	// To precisely control when updates are carried through to the underlying file
    23  	// requires the use of Mapping.Sync.
    24  	ModeReadWrite
    25  
    26  	// Create a private copy-on-write mapping.
    27  	// Updates to the mapping are not visible to other processes
    28  	// mapping the same region, and are not carried through to the underlying file.
    29  	// It is unspecified whether changes made to the file are visible in the mapped region
    30  	ModeWriteCopy
    31  )
    32  
    33  // Flag is a mapping flag
    34  type Flag int
    35  
    36  const (
    37  	// Mapped memory pages may be executed
    38  	FlagExecutable Flag = 1 << iota
    39  )
    40  
    41  // Mapping contains the cross-platform parts of a mapping.
    42  type mapping struct {
    43  	// writable specifies whether the mapped memory pages may be written.
    44  	writable bool
    45  	// executable specifies whether the mapped memory pages may be executed.
    46  	executable bool
    47  	// address specifies the pointer to the mapped memory.
    48  	address uintptr
    49  	// memory specifies the byte slice which wraps the mapped memory.
    50  	memory []byte
    51  	// off is the current offset
    52  	off int64
    53  	// segment specifies the lazily initialized data segment on top of the mapped memory.
    54  	segment *segment.Segment
    55  }
    56  
    57  // Writable returns true if the mapped memory pages may be written.
    58  func (m *mapping) Writable() bool {
    59  	return m.writable
    60  }
    61  
    62  // Executable returns true if the mapped memory pages may be executed.
    63  func (m *mapping) Executable() bool {
    64  	return m.executable
    65  }
    66  
    67  // Address returns the pointer to the mapped memory.
    68  func (m *mapping) Address() uintptr {
    69  	return m.address
    70  }
    71  
    72  // Length returns the mapped memory length in bytes.
    73  func (m *mapping) Length() uintptr {
    74  	return uintptr(len(m.memory))
    75  }
    76  
    77  // Memory returns the byte slice which wraps the mapped memory.
    78  func (m *mapping) Memory() []byte {
    79  	return m.memory
    80  }
    81  
    82  // Segment returns the data segment on top of the mapped memory.
    83  func (m *mapping) Segment() *segment.Segment {
    84  	if m.segment == nil {
    85  		m.segment = segment.New(0, m.memory)
    86  	}
    87  	return m.segment
    88  }
    89  
    90  // access checks given offset and length to match the available bounds
    91  // and returns ErrOutOfBounds error at the access violation.
    92  func (m *mapping) access(offset int64, length int) error {
    93  	if offset < 0 || offset > math.MaxInt64-int64(length) || offset+int64(length) > int64(len(m.memory)) {
    94  		return ErrOutOfBounds
    95  	}
    96  	return nil
    97  }
    98  
    99  // Read reads len(buf) bytes from the internal offset from the mapped memory.
   100  // If the offset is out of the available bounds or there are not enough bytes to read
   101  // the ErrOutOfBounds error will be returned. Otherwise len(buf) will be returned
   102  // with no errors. Read implements the io.Reader interface.
   103  func (m *mapping) Read(buf []byte) (int, error) {
   104  	if m.memory == nil {
   105  		return 0, ErrClosed
   106  	}
   107  	if err := m.access(m.off, len(buf)); err != nil {
   108  		return 0, err
   109  	}
   110  	n := copy(buf, m.memory[m.off:])
   111  	m.off += int64(n)
   112  	return n, nil
   113  }
   114  
   115  // ReadAt reads len(buf) bytes at the given offset from start of the mapped memory from the mapped memory.
   116  // If the given offset is out of the available bounds or there are not enough bytes to read
   117  // the ErrOutOfBounds error will be returned. Otherwise len(buf) will be returned with no errors.
   118  // ReadAt implements the io.ReaderAt interface.
   119  func (m *mapping) ReadAt(buf []byte, offset int64) (int, error) {
   120  	if m.memory == nil {
   121  		return 0, ErrClosed
   122  	}
   123  	if err := m.access(offset, len(buf)); err != nil {
   124  		return 0, err
   125  	}
   126  	return copy(buf, m.memory[offset:]), nil
   127  }
   128  
   129  // Write writes len(buf) bytes from the internal offset into the mapped memory.
   130  // If the offset is out of the available bounds or there are not enough space to write all given bytes
   131  // the ErrOutOfBounds error will be returned. Otherwise len(buf) will be returned with no errors.
   132  // Write implements the io.Writer interface.
   133  func (m *mapping) Write(buf []byte) (int, error) {
   134  	if m.memory == nil {
   135  		return 0, ErrClosed
   136  	}
   137  	if !m.writable {
   138  		return 0, ErrReadOnly
   139  	}
   140  	if err := m.access(m.off, len(buf)); err != nil {
   141  		return 0, err
   142  	}
   143  	n := copy(m.memory[m.off:], buf)
   144  	m.off += int64(n)
   145  	return n, nil
   146  }
   147  
   148  // WriteAt writes len(buf) bytes at the given offset from start of the mapped memory into the mapped memory.
   149  // If the given offset is out of the available bounds or there are not enough space to write all given bytes
   150  // the ErrOutOfBounds error will be returned. Otherwise len(buf) will be returned with no errors.
   151  // WriteAt implements the io.WriterAt interface.
   152  func (m *mapping) WriteAt(buf []byte, offset int64) (int, error) {
   153  	if m.memory == nil {
   154  		return 0, ErrClosed
   155  	}
   156  	if !m.writable {
   157  		return 0, ErrReadOnly
   158  	}
   159  	if err := m.access(offset, len(buf)); err != nil {
   160  		return 0, err
   161  	}
   162  	return copy(m.memory[offset:], buf), nil
   163  }
   164  
   165  // Seek sets the offset for the next Read or Write to offset, interpreted according to whence: SeekStart means
   166  // relative to the start of the file, SeekCurrent means relative to the current offset, and SeekEnd means relative
   167  // to the end. Seek returns the new offset relative to the start of the file and an error, if any.
   168  // Seeking to an offset before the start of the file is an error. Seeking to any positive offset is legal, but the
   169  // behavior of subsequent I/O operations on the underlying object is implementation-dependent.
   170  func (m *mapping) Seek(offset int64, whence int) (int64, error) {
   171  	if m.memory == nil {
   172  		return 0, ErrClosed
   173  	}
   174  	if err := m.access(offset, 0); err != nil {
   175  		return 0, err
   176  	}
   177  	switch whence {
   178  	default:
   179  		return 0, ErrSeekWhence
   180  	case io.SeekStart:
   181  		offset += 0
   182  	case io.SeekCurrent:
   183  		offset += m.off
   184  	case io.SeekEnd:
   185  		offset += int64(len(m.memory))
   186  	}
   187  	if offset < 0 {
   188  		return 0, ErrSeekOffset
   189  	}
   190  	m.off = offset
   191  	return offset - 0, nil
   192  }