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 }