github.com/blong14/gache@v0.0.0-20240124023949-89416fd8bbfa/internal/io/file/mmap.go (about)

     1  package file
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"sync"
     9  	"syscall"
    10  )
    11  
    12  // Adapted from https://github.com/johnsiilver/golib
    13  
    14  const (
    15  	Read  = syscall.PROT_READ
    16  	Write = syscall.PROT_WRITE
    17  	Exec  = syscall.PROT_EXEC
    18  
    19  	Shared = syscall.MAP_SHARED
    20  )
    21  
    22  type Map interface {
    23  	io.ReadWriteCloser
    24  	io.Seeker
    25  	io.ReaderAt
    26  	Bytes() []byte
    27  	Len() int
    28  	Pos() int
    29  	MLock() error
    30  	MUnlock() error
    31  	Append([]byte) (int, int, error)
    32  	Peek([]byte, int64, int64) (int, error)
    33  }
    34  
    35  type Option func(m *mmap)
    36  
    37  func Prot(p int) Option {
    38  	return func(m *mmap) {
    39  		if p == Write {
    40  			m.write = true
    41  		}
    42  		if m.prot != -1 {
    43  			m.prot |= p
    44  			return
    45  		}
    46  		m.prot = p
    47  	}
    48  }
    49  
    50  func Flag(f int) Option {
    51  	return func(m *mmap) {
    52  		m.flags = f
    53  	}
    54  }
    55  
    56  func Length(s int) Option {
    57  	return func(m *mmap) {
    58  		m.len = s
    59  	}
    60  }
    61  
    62  func Offset(o int64) Option {
    63  	return func(m *mmap) {
    64  		m.offset = o
    65  	}
    66  }
    67  
    68  func NewMap(f *os.File, opts ...Option) (Map, error) {
    69  	return newMap(f, opts...)
    70  }
    71  
    72  func newMap(f *os.File, opts ...Option) (*mmap, error) {
    73  	m := &mmap{
    74  		f:     f,
    75  		flags: -1,
    76  		prot:  -1,
    77  		len:   -1,
    78  	}
    79  	for _, opt := range opts {
    80  		opt(m)
    81  	}
    82  	if m.flags == -1 || m.prot == -1 {
    83  		return nil, errors.New("must pass options to set the flag or prot values")
    84  	}
    85  
    86  	s, err := f.Stat()
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  	if s.Size() == 0 {
    91  		return nil, errors.New("cannot mmap 0 length file")
    92  	}
    93  	if m.len == -1 {
    94  		m.len = int(s.Size())
    95  	}
    96  
    97  	m.data, err = syscall.Mmap(int(f.Fd()), m.offset, m.len, m.prot, m.flags)
    98  	if err != nil {
    99  		return nil, fmt.Errorf("problem with mmap system call: %w", err)
   100  	}
   101  
   102  	return m, nil
   103  }
   104  
   105  type mmap struct {
   106  	sync.RWMutex
   107  	flags, prot, len int
   108  	offset           int64
   109  	data             []byte
   110  	ptr              int
   111  	write            bool
   112  	f                *os.File
   113  }
   114  
   115  func (m *mmap) Read(p []byte) (int, error) {
   116  	m.Lock()
   117  	defer m.Unlock()
   118  	if m.ptr >= m.len {
   119  		return 0, io.EOF
   120  	}
   121  	n := copy(p, m.data[m.ptr:])
   122  	m.ptr += n
   123  	if n == m.ptr-m.len {
   124  		return n, io.EOF
   125  	}
   126  	return n, nil
   127  }
   128  
   129  func (m *mmap) ReadAt(p []byte, off int64) (int, error) {
   130  	m.RLock()
   131  	defer m.RUnlock()
   132  	if int(off) >= m.len {
   133  		return 0, errors.New("offset is larger than the mmap []byte")
   134  	}
   135  	n := copy(p, m.data[off:])
   136  	if n < len(p) {
   137  		return n, errors.New("len(p) was greater than mmap[off:]")
   138  	}
   139  	return n, nil
   140  }
   141  
   142  func (m *mmap) Peek(p []byte, start, length int64) (int, error) {
   143  	m.RLock()
   144  	defer m.RUnlock()
   145  	if int(start) >= m.len || int(start+length) >= m.len {
   146  		return 0, errors.New("offset is larger than the mmap []byte")
   147  	}
   148  	n := copy(p, m.data[start:start+length])
   149  	if n < len(p) {
   150  		return n, errors.New("len(p) was greater than mmap[off:]")
   151  	}
   152  	return n, nil
   153  }
   154  
   155  func (m *mmap) Write(p []byte) (int, error) {
   156  	err := m.MLock()
   157  	if err != nil {
   158  		return 0, fmt.Errorf("cannot lock memory: %w", err)
   159  	}
   160  	defer func() { _ = m.MUnlock() }()
   161  	if !m.write {
   162  		return 0, errors.New("cannot write to non-writeable mmap")
   163  	}
   164  	m.Lock()
   165  	n := copy(m.data[m.ptr:], p)
   166  	m.ptr += n
   167  	m.Unlock()
   168  	return n, nil
   169  }
   170  
   171  func (m *mmap) Append(p []byte) (int, int, error) {
   172  	err := m.MLock()
   173  	if err != nil {
   174  		return 0, 0, fmt.Errorf("cannot lock memory: %w", err)
   175  	}
   176  	defer func() { _ = m.MUnlock() }()
   177  	if !m.write {
   178  		return 0, 0, errors.New("cannot write to non-writeable mmap")
   179  	}
   180  	offset := m.Pos()
   181  	m.Lock()
   182  	n := copy(m.data[m.ptr:], p)
   183  	m.ptr += n
   184  	m.Unlock()
   185  	return n, offset, nil
   186  }
   187  
   188  func (m *mmap) Seek(offset int64, whence int) (int64, error) {
   189  	if offset < 0 {
   190  		return 0, fmt.Errorf("cannot seek to a negative offset")
   191  	}
   192  	err := m.MLock()
   193  	if err != nil {
   194  		return 0, fmt.Errorf("cannot lock memory: %w", err)
   195  	}
   196  	defer func() { _ = m.MUnlock() }()
   197  	m.Lock()
   198  	defer m.Unlock()
   199  	switch whence {
   200  	case 0:
   201  		if offset < int64(m.len) {
   202  			m.ptr = int(offset)
   203  			return int64(m.ptr), nil
   204  		}
   205  		return 0, errors.New("offset goes beyond the data size")
   206  	case 1:
   207  		if m.ptr+int(offset) < m.len {
   208  			m.ptr += int(offset)
   209  			return int64(m.ptr), nil
   210  		}
   211  		return 0, errors.New("offset goes beyond the data size")
   212  	case 2:
   213  		if m.ptr-int(offset) > -1 {
   214  			m.ptr -= int(offset)
   215  			return int64(m.ptr), nil
   216  		}
   217  		return 0, errors.New("offset would set the offset as a negative number")
   218  	default:
   219  		return 0, errors.New("whence arg was not set to a valid value")
   220  	}
   221  }
   222  
   223  func (m *mmap) Bytes() []byte {
   224  	m.RLock()
   225  	defer m.RUnlock()
   226  	return m.data
   227  }
   228  
   229  func (m *mmap) Len() int {
   230  	m.RLock()
   231  	defer m.RUnlock()
   232  	return m.len
   233  }
   234  
   235  func (m *mmap) Pos() int {
   236  	m.RLock()
   237  	defer m.RUnlock()
   238  	return m.ptr
   239  }
   240  
   241  func (m *mmap) MLock() error {
   242  	m.RLock()
   243  	defer m.RUnlock()
   244  	return syscall.Mlock(m.data)
   245  }
   246  
   247  func (m *mmap) MUnlock() error {
   248  	m.RLock()
   249  	defer m.RUnlock()
   250  	return syscall.Munlock(m.data)
   251  }
   252  
   253  func (m *mmap) Close() error {
   254  	m.RLock()
   255  	defer m.RUnlock()
   256  	defer func() { _ = m.f.Close() }()
   257  	return syscall.Munmap(m.data)
   258  }