1 /******************************************************************************* 2 3 copyright: Copyright (c) 2004 Kris Bell. All rights reserved 4 5 license: BSD style: $(LICENSE) 6 7 version: Initial release: March 2004 8 9 author: Kris 10 11 *******************************************************************************/ 12 13 module tango.io.device.FileMap; 14 15 private import tango.sys.Common; 16 17 private import tango.io.device.File, 18 tango.io.device.Array; 19 20 /******************************************************************************* 21 22 External declarations. 23 24 *******************************************************************************/ 25 26 version (Win32) 27 private extern (Windows) 28 { 29 BOOL UnmapViewOfFile (LPCVOID); 30 BOOL FlushViewOfFile (LPCVOID, DWORD); 31 LPVOID MapViewOfFile (HANDLE, DWORD, DWORD, DWORD, DWORD); 32 HANDLE CreateFileMappingA (HANDLE, LPSECURITY_ATTRIBUTES, DWORD, DWORD, DWORD, LPCTSTR); 33 } 34 35 version (Posix) 36 private import tango.stdc.posix.sys.mman; 37 38 39 /******************************************************************************* 40 41 *******************************************************************************/ 42 43 class FileMap : Array 44 { 45 private MappedFile file; 46 47 /*********************************************************************** 48 49 Construct a FileMap upon the given path. 50 51 You should use resize() to setup the available 52 working space. 53 54 ***********************************************************************/ 55 56 this (const(char[]) path, File.Style style = File.ReadWriteOpen) 57 { 58 file = new MappedFile (path, style); 59 super (file.map); 60 } 61 62 /*********************************************************************** 63 64 Resize the file and return the remapped content. Usage of 65 map() is not required following this call. 66 67 ***********************************************************************/ 68 69 final ubyte[] resize (long size) 70 { 71 auto ret = file.resize (size); 72 super.assign (ret); 73 return ret; 74 } 75 76 /*********************************************************************** 77 78 Release external resources. 79 80 ***********************************************************************/ 81 82 override void close () 83 { 84 super.close(); 85 if (file) 86 file.close(); 87 file = null; 88 } 89 } 90 91 92 /******************************************************************************* 93 94 *******************************************************************************/ 95 96 class MappedFile 97 { 98 private File host; 99 100 /*********************************************************************** 101 102 Construct a FileMap upon the given path. 103 104 You should use resize() to setup the available 105 working space. 106 107 ***********************************************************************/ 108 109 this (const(char[]) path, File.Style style = File.ReadWriteOpen) 110 { 111 host = new File (path, style); 112 } 113 114 /*********************************************************************** 115 116 ***********************************************************************/ 117 118 @property final long length () 119 { 120 return host.length; 121 } 122 123 /*********************************************************************** 124 125 ***********************************************************************/ 126 127 @property final const(char)[] path () 128 { 129 return host.toString(); 130 } 131 132 /*********************************************************************** 133 134 Resize the file and return the remapped content. Usage of 135 map() is not required following this call. 136 137 ***********************************************************************/ 138 139 final ubyte[] resize (long size) 140 { 141 host.truncate (size); 142 return map; 143 } 144 145 /*********************************************************************** 146 147 ***********************************************************************/ 148 149 version (Win32) 150 { 151 private void* base; // array pointer 152 private HANDLE mmFile; // mapped file 153 154 /*************************************************************** 155 156 Return a slice representing file content as a 157 memory-mapped array. 158 159 ***************************************************************/ 160 161 @property final ubyte[] map () 162 { 163 DWORD flags; 164 165 // be wary of redundant references 166 if (base) 167 reset(); 168 169 // can only do 32bit mapping on 32bit platform 170 auto size = cast(size_t) host.length; 171 auto access = host.style.access; 172 173 flags = PAGE_READONLY; 174 if (access & host.Access.Write) 175 flags = PAGE_READWRITE; 176 177 auto handle = cast(HANDLE) host.fileHandle; 178 mmFile = CreateFileMappingA (handle, null, flags, 0, 0, null); 179 if (mmFile is null) 180 host.error(); 181 182 flags = FILE_MAP_READ; 183 if (access & host.Access.Write) 184 flags |= FILE_MAP_WRITE; 185 186 base = MapViewOfFile (mmFile, flags, 0, 0, 0); 187 if (base is null) 188 host.error(); 189 190 return (cast(ubyte*) base) [0 .. size]; 191 } 192 193 /*************************************************************** 194 195 Release this mapping without flushing. 196 197 ***************************************************************/ 198 199 final void close () 200 { 201 reset(); 202 if (host) 203 host.close(); 204 host = null; 205 } 206 207 /*************************************************************** 208 209 ***************************************************************/ 210 211 private void reset () 212 { 213 if (base) 214 UnmapViewOfFile (base); 215 216 if (mmFile) 217 CloseHandle (mmFile); 218 219 mmFile = null; 220 base = null; 221 } 222 223 /*************************************************************** 224 225 Flush dirty content out to the drive. This 226 fails with error 33 if the file content is 227 virgin. Opening a file for ReadWriteExists 228 followed by a flush() will cause this. 229 230 ***************************************************************/ 231 232 MappedFile flush () 233 { 234 // flush all dirty pages 235 if (! FlushViewOfFile (base, 0)) 236 host.error(); 237 return this; 238 } 239 } 240 241 /*********************************************************************** 242 243 ***********************************************************************/ 244 245 version (Posix) 246 { 247 // Linux code: not yet tested on other POSIX systems. 248 private void* base; // array pointer 249 private size_t size; // length of file 250 251 /*************************************************************** 252 253 Return a slice representing file content as a 254 memory-mapped array. Use this to remap content 255 each time the file size is changed. 256 257 ***************************************************************/ 258 259 @property final ubyte[] map () 260 { 261 // be wary of redundant references 262 if (base) 263 reset(); 264 265 // can only do 32bit mapping on 32bit platform 266 size = cast (size_t) host.length; 267 268 // Make sure the mapping attributes are consistant with 269 // the File attributes. 270 int flags = MAP_SHARED; 271 int protection = PROT_READ; 272 auto access = host.style.access; 273 if (access & host.Access.Write) 274 protection |= PROT_WRITE; 275 276 base = mmap (null, size, protection, flags, host.fileHandle, 0); 277 if (base is MAP_FAILED) 278 { 279 base = null; 280 host.error(); 281 } 282 283 return (cast(ubyte*) base) [0 .. size]; 284 } 285 286 /*************************************************************** 287 288 Release this mapped buffer without flushing. 289 290 ***************************************************************/ 291 292 final void close () 293 { 294 reset(); 295 if (host) 296 host.close(); 297 host = null; 298 } 299 300 /*************************************************************** 301 302 ***************************************************************/ 303 304 private void reset () 305 { 306 // NOTE: When a process ends, all mmaps belonging to that process 307 // are automatically unmapped by system (Linux). 308 // On the other hand, this is NOT the case when the related 309 // file descriptor is closed. This function unmaps explicitly. 310 if (base) 311 if (munmap (base, size)) 312 host.error(); 313 314 base = null; 315 } 316 317 /*************************************************************** 318 319 Flush dirty content out to the drive. 320 321 ***************************************************************/ 322 323 final MappedFile flush () 324 { 325 // MS_ASYNC: delayed flush; equivalent to "add-to-queue" 326 // MS_SYNC: function flushes file immediately; no return until flush complete 327 // MS_INVALIDATE: invalidate all mappings of the same file (shared) 328 329 if (msync (base, size, MS_SYNC | MS_INVALIDATE)) 330 host.error(); 331 return this; 332 } 333 } 334 } 335 336 337 /******************************************************************************* 338 339 *******************************************************************************/ 340 341 debug (FileMap) 342 { 343 import tango.io.Path; 344 345 void main() 346 { 347 auto file = new MappedFile ("foo.map"); 348 auto heap = file.resize (1_000_000); 349 350 auto file1 = new MappedFile ("foo1.map"); 351 auto heap1 = file1.resize (1_000_000); 352 353 file.close(); 354 remove ("foo.map"); 355 356 file1.close(); 357 remove ("foo1.map"); 358 } 359 }