1// Copyright 2016 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// +build darwin linux 6 7package core 8 9import ( 10 "errors" 11 "fmt" 12 "io" 13 "os" 14 "syscall" 15) 16 17var errMmapClosed = errors.New("mmap: closed") 18 19// mmapFile wraps a memory-mapped file. 20type mmapFile struct { 21 data []byte 22 pos uint64 23 writable bool 24} 25 26// mmapOpen opens the named file for reading. 27// If writable is true, the file is also open for writing. 28func mmapOpen(filename string, writable bool) (*mmapFile, error) { 29 f, err := os.Open(filename) 30 if err != nil { 31 return nil, err 32 } 33 defer f.Close() 34 st, err := f.Stat() 35 if err != nil { 36 return nil, err 37 } 38 39 size := st.Size() 40 if size == 0 { 41 return &mmapFile{data: []byte{}}, nil 42 } 43 if size < 0 { 44 return nil, fmt.Errorf("mmap: file %q has negative size: %d", filename, size) 45 } 46 if size != int64(int(size)) { 47 return nil, fmt.Errorf("mmap: file %q is too large", filename) 48 } 49 50 prot := syscall.PROT_READ 51 if writable { 52 prot |= syscall.PROT_WRITE 53 } 54 data, err := syscall.Mmap(int(f.Fd()), 0, int(size), prot, syscall.MAP_SHARED) 55 if err != nil { 56 return nil, err 57 } 58 return &mmapFile{data: data, writable: writable}, nil 59} 60 61// Size returns the size of the mapped file. 62func (f *mmapFile) Size() uint64 { 63 return uint64(len(f.data)) 64} 65 66// Pos returns the current file pointer. 67func (f *mmapFile) Pos() uint64 { 68 return f.pos 69} 70 71// SeekTo sets the current file pointer relative to the start of the file. 72func (f *mmapFile) SeekTo(offset uint64) { 73 f.pos = offset 74} 75 76// Read implements io.Reader. 77func (f *mmapFile) Read(p []byte) (int, error) { 78 if f.data == nil { 79 return 0, errMmapClosed 80 } 81 if f.pos >= f.Size() { 82 return 0, io.EOF 83 } 84 n := copy(p, f.data[f.pos:]) 85 f.pos += uint64(n) 86 if n < len(p) { 87 return n, io.EOF 88 } 89 return n, nil 90} 91 92// ReadByte implements io.ByteReader. 93func (f *mmapFile) ReadByte() (byte, error) { 94 if f.data == nil { 95 return 0, errMmapClosed 96 } 97 if f.pos >= f.Size() { 98 return 0, io.EOF 99 } 100 b := f.data[f.pos] 101 f.pos++ 102 return b, nil 103} 104 105// ReadSlice returns a slice of size n that points directly at the 106// underlying mapped file. There is no copying. Fails if it cannot 107// read at least n bytes. 108func (f *mmapFile) ReadSlice(n uint64) ([]byte, error) { 109 if f.data == nil { 110 return nil, errMmapClosed 111 } 112 if f.pos+n >= f.Size() { 113 return nil, io.EOF 114 } 115 first := f.pos 116 f.pos += n 117 return f.data[first:f.pos:f.pos], nil 118} 119 120// ReadSliceAt is like ReadSlice, but reads from a specific offset. 121// The file pointer is not used or advanced. 122func (f *mmapFile) ReadSliceAt(offset, n uint64) ([]byte, error) { 123 if f.data == nil { 124 return nil, errMmapClosed 125 } 126 if f.Size() < offset { 127 return nil, fmt.Errorf("mmap: out-of-bounds ReadSliceAt offset %d, size is %d", offset, f.Size()) 128 } 129 if offset+n >= f.Size() { 130 return nil, io.EOF 131 } 132 end := offset + n 133 return f.data[offset:end:end], nil 134} 135 136// Close closes the file. 137func (f *mmapFile) Close() error { 138 if f.data == nil { 139 return nil 140 } 141 err := syscall.Munmap(f.data) 142 f.data = nil 143 f.pos = 0 144 return err 145} 146