github.com/wencode/hack@v0.2.9/mmap/mmap.go (about)

     1  package mmap
     2  
     3  import (
     4  	"errors"
     5  	"io"
     6  	"os"
     7  	"reflect"
     8  	"syscall"
     9  	"unsafe"
    10  )
    11  
    12  const (
    13  	RDONLY = 0
    14  	RDWR   = 1 << iota
    15  	COW
    16  	EXEC
    17  )
    18  
    19  var (
    20  	ErrArgument = errors.New("argument error")
    21  )
    22  
    23  type MapBuf []byte
    24  
    25  type MapFile struct {
    26  	file   *os.File
    27  	prot   int
    28  	offset int
    29  	master MapBuf
    30  	index  int
    31  	extras []MapBuf
    32  }
    33  
    34  type Param struct {
    35  	Offset   int
    36  	Len      int
    37  	Prot     int
    38  	Private  bool
    39  	Append   bool
    40  	Truncate bool
    41  }
    42  
    43  type Option func(param *Param)
    44  
    45  func WithOffset(offset int) Option {
    46  	return func(param *Param) {
    47  		param.Offset = offset
    48  	}
    49  }
    50  
    51  func WithLength(len int) Option {
    52  	return func(param *Param) {
    53  		param.Len = len
    54  	}
    55  }
    56  
    57  func WithWrite() Option {
    58  	return func(param *Param) {
    59  		param.Prot |= RDWR
    60  	}
    61  }
    62  
    63  func WithCopyOnWrite() Option {
    64  	return func(param *Param) {
    65  		param.Prot |= COW
    66  	}
    67  }
    68  
    69  func WithPrivate() Option {
    70  	return func(param *Param) {
    71  		param.Private = true
    72  	}
    73  }
    74  
    75  func WithTruncate() Option {
    76  	return func(param *Param) {
    77  		param.Truncate = true
    78  	}
    79  }
    80  
    81  func parseMapParam(opts ...Option) *Param {
    82  	param := &Param{}
    83  	for _, opt := range opts {
    84  		opt(param)
    85  	}
    86  	if param.Len == 0 {
    87  		param.Len = os.Getpagesize()
    88  	}
    89  	return param
    90  }
    91  
    92  func Open(filename string, opts ...Option) (*MapFile, error) {
    93  	param := parseMapParam(opts...)
    94  
    95  	var (
    96  		file *os.File
    97  		err  error
    98  	)
    99  	if filename != "" {
   100  		file, err = OpenFile(filename, param.Prot, param.Truncate)
   101  		if err != nil {
   102  			return nil, err
   103  		}
   104  	}
   105  
   106  	if file == nil && param.Len == 0 {
   107  		return nil, ErrArgument
   108  	}
   109  
   110  	return _open(file, param)
   111  }
   112  
   113  func OpenWithFile(file *os.File, opts ...Option) (*MapFile, error) {
   114  	param := parseMapParam(opts...)
   115  	return _open(file, param)
   116  }
   117  
   118  func _open(file *os.File, param *Param) (*MapFile, error) {
   119  	if err := checkFile(file, param); err != nil {
   120  		return nil, err
   121  	}
   122  
   123  	var fd = -1
   124  	if file != nil {
   125  		fd = int(file.Fd())
   126  	}
   127  	buf, err := Mmap(fd, param.Prot, param.Offset, param.Len)
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  
   132  	return &MapFile{
   133  		file:   file,
   134  		prot:   param.Prot,
   135  		offset: param.Offset,
   136  		master: buf,
   137  	}, nil
   138  }
   139  
   140  func (m *MapFile) Close() {
   141  	m.Unmap()
   142  	for _, buf := range m.extras {
   143  		buf.Unmap()
   144  	}
   145  	m.extras = nil
   146  	if m.file != nil {
   147  		m.file.Close()
   148  		m.file = nil
   149  	}
   150  }
   151  
   152  func (m *MapFile) Unmap() {
   153  	if m.master != nil {
   154  		m.master.Unmap()
   155  		m.master = nil
   156  	}
   157  }
   158  
   159  func (m *MapFile) Remap(offset, len int) error {
   160  	m.Unmap()
   161  	buf, err := Mmap(int(m.file.Fd()), m.prot, offset, len)
   162  	if err != nil {
   163  		return err
   164  	}
   165  	m.master = buf
   166  	m.index = 0
   167  	return nil
   168  }
   169  
   170  func (m *MapFile) Buffer() MapBuf {
   171  	return m.master
   172  }
   173  
   174  func (m *MapFile) Read(b []byte) (n int, err error) {
   175  	if m.master == nil || m.index >= len(m.master) {
   176  		err = io.EOF
   177  		return
   178  	}
   179  	src := m.master[m.index:]
   180  	n = copy(b, src)
   181  	m.index += n
   182  	return
   183  }
   184  
   185  func (m *MapFile) Write(b []byte) (n int, err error) {
   186  	if m.master == nil || m.index >= len(m.master) {
   187  		err = io.EOF
   188  		return
   189  	}
   190  	dst := m.master[m.index:]
   191  	n = copy(dst, b)
   192  	m.index += n
   193  	return
   194  }
   195  
   196  func (m *MapFile) Seek(offset int64, whence int) (newoff int64, err error) {
   197  	switch whence {
   198  	case io.SeekStart:
   199  		if offset < 0 || offset > int64(len(m.master)) {
   200  			err = ErrArgument
   201  			return
   202  		}
   203  		m.index = int(offset)
   204  	case io.SeekCurrent:
   205  		cur := m.index + int(offset)
   206  		if cur < 0 || cur > len(m.master) {
   207  			err = ErrArgument
   208  			return
   209  		}
   210  		m.index = cur
   211  	case io.SeekEnd:
   212  		if offset < 0 || offset > int64(len(m.master)) {
   213  			err = ErrArgument
   214  			return
   215  		}
   216  		m.index = len(m.master) - 1 - int(offset)
   217  	default:
   218  		err = ErrArgument
   219  		return
   220  	}
   221  	newoff = int64(m.index)
   222  	return
   223  }
   224  
   225  func (m *MapFile) Sync() {
   226  	if m.master == nil {
   227  		return
   228  	}
   229  	m.master.Sync()
   230  }
   231  
   232  func (m *MapFile) Flush(b []byte) {
   233  	bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
   234  	baseh := m.master.header()
   235  	if bh.Data < baseh.Data || bh.Data >= baseh.Data+uintptr(baseh.Len) {
   236  		return
   237  	}
   238  	Flush(bh.Data, uintptr(bh.Len))
   239  }
   240  
   241  func (m *MapFile) Resize(newSize int, opts ...Option) error {
   242  	param := parseMapParam(opts...)
   243  	if param.Len > 0 {
   244  		if param.Offset < 0 {
   245  			return ErrArgument
   246  		}
   247  		if param.Offset+param.Len > newSize || param.Offset >= newSize {
   248  			return ErrArgument
   249  		}
   250  	}
   251  
   252  	if err := m.extendFile(newSize); err != nil {
   253  		return err
   254  	}
   255  
   256  	if param.Len > 0 {
   257  		if err := m.Remap(param.Offset, param.Len); err != nil {
   258  			return err
   259  		}
   260  	}
   261  	return nil
   262  }
   263  
   264  func (m *MapFile) ExtendMap(offset int, size int) (MapBuf, error) {
   265  	if m.file == nil {
   266  		return nil, ErrArgument
   267  	}
   268  
   269  	st, err := m.file.Stat()
   270  	if err != nil {
   271  		return nil, err
   272  	}
   273  	oldFileSize := int(st.Size())
   274  	if newSize := offset + size; newSize > oldFileSize {
   275  		fillFile(m.file, newSize)
   276  	}
   277  	buf, err := Mmap(int(m.file.Fd()), m.prot, offset, size)
   278  	if err != nil {
   279  		return nil, err
   280  	}
   281  	m.extras = append(m.extras, buf)
   282  	return buf, err
   283  }
   284  
   285  func (m *MapFile) extendFile(newSize int) error {
   286  	if m.file == nil {
   287  		return ErrArgument
   288  	} else {
   289  		st, err := m.file.Stat()
   290  		if err != nil {
   291  			return err
   292  		}
   293  		if int64(newSize) <= st.Size() {
   294  			return ErrArgument
   295  		}
   296  	}
   297  
   298  	fillFile(m.file, newSize)
   299  	return nil
   300  }
   301  
   302  func OpenFile(filename string, prot int, truncate bool) (*os.File, error) {
   303  	flags := os.O_RDONLY
   304  	if prot != RDONLY {
   305  		flags = os.O_RDWR
   306  	}
   307  	flags |= os.O_CREATE
   308  	if truncate {
   309  		flags |= os.O_TRUNC
   310  	}
   311  	mask := syscall.Umask(0)
   312  	defer syscall.Umask(mask)
   313  	file, err := os.OpenFile(filename, flags, 0644)
   314  	if err != nil {
   315  		return nil, err
   316  	}
   317  
   318  	return file, nil
   319  }
   320  
   321  func checkFile(file *os.File, param *Param) error {
   322  	st, err := file.Stat()
   323  	if err != nil {
   324  		return err
   325  	}
   326  
   327  	filesize := int(st.Size())
   328  	fill := false
   329  	if param.Len == 0 {
   330  		if ps := os.Getpagesize(); filesize < ps {
   331  			filesize = ps
   332  			fill = true
   333  		}
   334  		param.Len = filesize
   335  	} else {
   336  		if filesize < param.Len {
   337  			filesize = param.Len
   338  			fill = true
   339  		}
   340  	}
   341  
   342  	if fill {
   343  		fillFile(file, param.Len)
   344  	}
   345  
   346  	return nil
   347  }
   348  
   349  func fillFile(file *os.File, length int) {
   350  	var (
   351  		tmp [1]byte
   352  	)
   353  	file.Seek(int64(length-1), 0)
   354  	file.Write(tmp[:])
   355  	//file.Sync()
   356  	file.Seek(0, 0)
   357  }
   358  
   359  func (mb MapBuf) header() *reflect.SliceHeader {
   360  	return (*reflect.SliceHeader)(unsafe.Pointer(&mb))
   361  }