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 }