github.com/zuoyebang/bitalosdb@v1.1.1-0.20240516111551-79a8c4d8ce20/internal/mmap/mmap.go (about) 1 // Copyright 2021 The Bitalosdb author(hustxrb@163.com) and other contributors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package mmap 16 17 import ( 18 "encoding/binary" 19 "errors" 20 "os" 21 "reflect" 22 "unsafe" 23 ) 24 25 const ( 26 // RDONLY maps the memory read-only. 27 // Attempts to write to the MMap object will result in undefined behavior. 28 RDONLY = 0 29 // RDWR maps the memory as read-write. Writes to the MMap object will update the 30 // underlying file. 31 RDWR = 1 << iota 32 // COPY maps the memory as copy-on-write. Writes to the MMap object will affect 33 // memory, but the underlying file will remain unchanged. 34 COPY 35 // EXEC if set, the mapped memory is marked as executable. 36 EXEC 37 ) 38 39 const ( 40 ANON = 1 << iota 41 ) 42 43 type MMap struct { 44 name string 45 file *os.File 46 size int64 47 offset int64 48 m Mbuf 49 } 50 51 func Open(name string, size int64) (*MMap, error) { 52 m := &MMap{ 53 name: name, 54 size: size, 55 offset: 0, 56 } 57 58 var err error 59 60 m.file, err = os.OpenFile(name, os.O_CREATE|os.O_RDWR, 0644) 61 if err != nil { 62 return nil, err 63 } 64 65 if m.size == 0 { 66 st, _ := m.file.Stat() 67 m.size = st.Size() 68 } 69 70 if err = m.file.Truncate(m.size); err != nil { 71 return nil, err 72 } 73 74 if m.m, err = Map(m.file, RDWR, 0); err != nil { 75 return nil, err 76 } 77 78 return m, nil 79 } 80 81 func (m *MMap) ReadPointer(pos int) unsafe.Pointer { 82 return unsafe.Pointer(&m.m[pos]) 83 } 84 85 func (m *MMap) WriteInt64At(val int64, pos int) { 86 binary.PutVarint(m.m[pos:pos+8], val) 87 } 88 89 func (m *MMap) ReadInt64At(pos int) int64 { 90 res, _ := binary.Varint(m.m[pos : pos+8]) 91 return res 92 } 93 94 func (m *MMap) ReadUInt64At(pos int) uint64 { 95 res, _ := binary.Uvarint(m.m[pos : pos+8]) 96 return res 97 } 98 99 func (m *MMap) WriteUInt64At(val uint64, pos int) { 100 binary.PutUvarint(m.m[pos:pos+8], val) 101 } 102 103 func (m *MMap) ReadUInt16At(pos int) uint16 { 104 return binary.LittleEndian.Uint16(m.m[pos : pos+2]) 105 } 106 107 func (m *MMap) WriteUInt16At(val uint16, pos int) { 108 binary.LittleEndian.PutUint16(m.m[pos:pos+2], val) 109 } 110 111 func (m *MMap) ReadUInt32At(pos int) uint32 { 112 return binary.LittleEndian.Uint32(m.m[pos : pos+4]) 113 } 114 115 func (m *MMap) WriteUInt32At(val uint32, pos int) { 116 binary.LittleEndian.PutUint32(m.m[pos:pos+4], val) 117 } 118 119 func (m *MMap) ReadUInt8At(pos int) uint8 { 120 return m.m[pos] 121 } 122 123 func (m *MMap) WriteUInt8At(val uint8, pos int) { 124 m.m[pos] = val 125 } 126 127 func (m *MMap) GetMBuf() []byte { 128 return m.m 129 } 130 131 func (m *MMap) Len() int { 132 return len(m.m) 133 } 134 135 func (m *MMap) Cap() int { 136 return cap(m.m) 137 } 138 139 func (m *MMap) WriteAt(val []byte, pos int) { 140 size := len(val) 141 copy(m.m[pos:pos+size], val) 142 } 143 144 func (m *MMap) ReadAt(pos, size int) []byte { 145 return m.m[pos : pos+size : pos+size] 146 } 147 148 // Lock keeps the mapped region in physical memory, ensuring that it will not be 149 // swapped out. 150 func (m *MMap) Lock() error { 151 return m.m.lock() 152 } 153 154 // Unlock reverses the effect of Lock, allowing the mapped region to potentially 155 // be swapped out. If m is already unlocked, aan error will result. 156 func (m *MMap) Unlock() error { 157 return m.m.unlock() 158 } 159 160 func (m *MMap) Flush() (err error) { 161 return m.m.flush() 162 } 163 164 func (m *MMap) Unmap() error { 165 err := m.m.unmap() 166 m.m = nil 167 return err 168 } 169 170 func (m *MMap) Close() (err error) { 171 if err = m.m.flush(); err != nil { 172 return err 173 } 174 175 return m.file.Close() 176 } 177 178 type Mbuf []byte 179 180 func Map(f *os.File, prot, length int) (Mbuf, error) { 181 return MapRegion(f, length, prot, 0, 0) 182 } 183 184 // MapRegion maps part of a file into memory. 185 // The offset parameter must be a multiple of the system's page size. 186 // If length < 0, the entire file will be mapped. 187 // If ANON is set in flags, f is ignored. 188 func MapRegion(f *os.File, length int, prot, flags int, offset int64) (Mbuf, error) { 189 if offset%int64(os.Getpagesize()) != 0 { 190 return nil, errors.New("offset parameter must be a multiple of the system's page size") 191 } 192 193 var fd uintptr 194 if flags&ANON == 0 { 195 fd = f.Fd() 196 if length <= 0 { 197 fi, err := f.Stat() 198 if err != nil { 199 return nil, err 200 } 201 length = int(fi.Size()) 202 } 203 } else { 204 if length <= 0 { 205 return nil, errors.New("anonymous mapping requires non-zero length") 206 } 207 fd = ^uintptr(0) 208 } 209 210 return mmapfd(length, uintptr(prot), uintptr(flags), fd, offset) 211 } 212 213 func (m *Mbuf) header() *reflect.SliceHeader { 214 return (*reflect.SliceHeader)(unsafe.Pointer(m)) 215 } 216 217 func (m *Mbuf) addrLen() (uintptr, uintptr) { 218 header := m.header() 219 return header.Data, uintptr(header.Len) 220 } 221 222 func (m Mbuf) Flush() error { 223 return m.flush() 224 } 225 226 func (m *Mbuf) Unmap() error { 227 err := m.unmap() 228 *m = nil 229 return err 230 }