github.com/sunvim/utils@v0.1.0/fs/os_mmap.go (about) 1 package fs 2 3 import ( 4 "io" 5 "os" 6 ) 7 8 const ( 9 initialMmapSize = 1024 << 20 // 1 GiB 10 ) 11 12 type osMMapFS struct { 13 osFS 14 } 15 16 // OSMMap is a file system backed by the os package and memory-mapped files. 17 var OSMMap FileSystem = &osMMapFS{} 18 19 func (fs *osMMapFS) OpenFile(name string, flag int, perm os.FileMode) (File, error) { 20 if flag&os.O_APPEND != 0 { 21 // osMMapFS doesn't support opening files in append-only mode. 22 // The database doesn't currently use O_APPEND. 23 return nil, errAppendModeNotSupported 24 } 25 26 // open file and mmap 27 f, err := os.OpenFile(name, flag, perm) 28 if err != nil { 29 return nil, err 30 } 31 32 stat, err := f.Stat() 33 if err != nil { 34 return nil, err 35 } 36 37 mf := &osMMapFile{ 38 File: f, 39 size: stat.Size(), 40 } 41 if err := mf.mremap(); err != nil { 42 return nil, err 43 } 44 return mf, nil 45 } 46 47 type osMMapFile struct { 48 *os.File 49 data []byte 50 offset int64 51 size int64 52 mmapSize int64 53 } 54 55 func (f *osMMapFile) WriteAt(p []byte, off int64) (int, error) { 56 n, err := f.File.WriteAt(p, off) 57 if err != nil { 58 return 0, err 59 } 60 writeOff := off + int64(n) 61 if writeOff > f.size { 62 f.size = writeOff 63 } 64 return n, f.mremap() 65 } 66 67 func (f *osMMapFile) Write(p []byte) (int, error) { 68 n, err := f.File.Write(p) 69 if err != nil { 70 return 0, err 71 } 72 f.offset += int64(n) 73 if f.offset > f.size { 74 f.size = f.offset 75 } 76 return n, f.mremap() 77 } 78 79 func (f *osMMapFile) Seek(offset int64, whence int) (int64, error) { 80 off, err := f.File.Seek(offset, whence) 81 f.offset = off 82 return off, err 83 } 84 85 func (f *osMMapFile) Read(p []byte) (int, error) { 86 n, err := f.File.Read(p) 87 f.offset += int64(n) 88 return n, err 89 } 90 91 func (f *osMMapFile) Slice(start int64, end int64) ([]byte, error) { 92 if end > f.size { 93 return nil, io.EOF 94 } 95 if f.data == nil { 96 return nil, os.ErrClosed 97 } 98 return f.data[start:end], nil 99 } 100 101 func (f *osMMapFile) munmap() error { 102 if f.data == nil { 103 return nil 104 } 105 if err := Munmap(f.data); err != nil { 106 return err 107 } 108 f.data = nil 109 f.mmapSize = 0 110 return nil 111 } 112 113 func (f *osMMapFile) mmap(mappingSize int64) error { 114 if f.data != nil { 115 if err := Munmap(f.data); err != nil { 116 return err 117 } 118 } 119 120 data, err := Mmap(f.File, mappingSize) 121 if err != nil { 122 return err 123 } 124 125 _ = MadviceRandom(data) 126 127 f.data = data 128 return nil 129 } 130 131 func (f *osMMapFile) mremap() error { 132 mmapSize := f.mmapSize 133 134 if mmapSize >= f.size { 135 return nil 136 } 137 138 if mmapSize == 0 { 139 mmapSize = initialMmapSize 140 if mmapSize < f.size { 141 mmapSize = f.size 142 } 143 } else { 144 if err := f.munmap(); err != nil { 145 return err 146 } 147 mmapSize *= 2 148 } 149 150 if err := f.mmap(mmapSize); err != nil { 151 return err 152 } 153 154 // On Windows mmap may memory-map less than the requested size. 155 f.mmapSize = int64(len(f.data)) 156 157 return nil 158 } 159 160 func (f *osMMapFile) Close() error { 161 if err := f.munmap(); err != nil { 162 return err 163 } 164 return f.File.Close() 165 }