Mercurial > projects > ldc
comparison lphobos/std/mmfile.d @ 473:373489eeaf90
Applied downs' lphobos update
author | Tomas Lindquist Olsen <tomas.l.olsen@gmail.com> |
---|---|
date | Mon, 04 Aug 2008 19:28:49 +0200 |
parents | |
children | 88e23f8c2354 |
comparison
equal
deleted
inserted
replaced
472:15c804b6ce77 | 473:373489eeaf90 |
---|---|
1 /* Copyright 2004-2005 by Digital Mars | |
2 * Written by Walter Bright and Matthew Wilson | |
3 * | |
4 * This software is provided 'as-is', without any express or implied | |
5 * warranty. In no event will the authors be held liable for any damages | |
6 * arising from the use of this software. | |
7 * | |
8 * Permission is granted to anyone to use this software for any purpose, | |
9 * including commercial applications, and to alter it and redistribute it | |
10 * freely, in both source and binary form, subject to the following | |
11 * restrictions: | |
12 * | |
13 * - The origin of this software must not be misrepresented; you must not | |
14 * claim that you wrote the original software. If you use this software | |
15 * in a product, an acknowledgment in the product documentation would be | |
16 * appreciated but is not required. | |
17 * - Altered source versions must be plainly marked as such, and must not | |
18 * be misrepresented as being the original software. | |
19 * - This notice may not be removed or altered from any source | |
20 * distribution. | |
21 * | |
22 */ | |
23 | |
24 /** | |
25 * Read and write memory mapped files. | |
26 * Macros: | |
27 * WIKI=Phobos/StdMmfile | |
28 */ | |
29 | |
30 /* NOTE: This file has been patched from the original DMD distribution to | |
31 work with the GDC compiler. | |
32 | |
33 Modified by David Friedman, September 2004 | |
34 */ | |
35 | |
36 module std.mmfile; | |
37 | |
38 private import std.c.stdio; | |
39 private import std.c.stdlib; | |
40 private import std.string; | |
41 | |
42 //debug = MMFILE; | |
43 | |
44 version (Win32) | |
45 { | |
46 private import std.c.windows.windows; | |
47 private import std.utf; | |
48 | |
49 private uint dwVersion; | |
50 | |
51 static this() | |
52 { // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sysinfo/base/getversion.asp | |
53 dwVersion = GetVersion(); | |
54 } | |
55 | |
56 private const bool Have_MMFile = true; // private for now... | |
57 } | |
58 else version (Unix) | |
59 { | |
60 version (/*GNU_Unix_Have_MMap*/Unix) | |
61 { | |
62 version(linux) { | |
63 import std.c.linux.linux; | |
64 alias std.c.linux.linux unix; | |
65 } else { | |
66 private import std.c.unix.unix; | |
67 alias std.c.unix.unix unix; | |
68 } | |
69 | |
70 version = unix_mm; | |
71 private const bool Have_MMFile = true; | |
72 } | |
73 else | |
74 { | |
75 private const bool Have_MMFile = false; | |
76 } | |
77 | |
78 } | |
79 else | |
80 { | |
81 private const bool Have_MMFile = false; | |
82 // Can't simply fail because std.stream imports this module. | |
83 //static assert(0); | |
84 } | |
85 | |
86 static if (Have_MMFile) | |
87 { | |
88 private import std.file; | |
89 private import std.path; | |
90 } | |
91 | |
92 /** | |
93 * MmFile objects control the memory mapped file resource. | |
94 */ | |
95 class MmFile | |
96 { | |
97 /** | |
98 * The mode the memory mapped file is opened with. | |
99 */ | |
100 enum Mode | |
101 { Read, /// read existing file | |
102 ReadWriteNew, /// delete existing file, write new file | |
103 ReadWrite, /// read/write existing file, create if not existing | |
104 ReadCopyOnWrite, /// read/write existing file, copy on write | |
105 } | |
106 | |
107 /** | |
108 * Open memory mapped file filename for reading. | |
109 * File is closed when the object instance is deleted. | |
110 * Throws: | |
111 * std.file.FileException | |
112 */ | |
113 this(char[] filename) | |
114 { | |
115 this(filename, Mode.Read, 0, null); | |
116 } | |
117 | |
118 /** | |
119 * Open memory mapped file filename in mode. | |
120 * File is closed when the object instance is deleted. | |
121 * Params: | |
122 * filename = name of the file. | |
123 * If null, an anonymous file mapping is created. | |
124 * mode = access mode defined above. | |
125 * size = the size of the file. If 0, it is taken to be the | |
126 * size of the existing file. | |
127 * address = the preferred address to map the file to, | |
128 * although the system is not required to honor it. | |
129 * If null, the system selects the most convenient address. | |
130 * window = preferred block size of the amount of data to map at one time | |
131 * with 0 meaning map the entire file. The window size must be a | |
132 * multiple of the memory allocation page size. | |
133 * Throws: | |
134 * std.file.FileException | |
135 */ | |
136 this(char[] filename, Mode mode, ulong size, void* address, | |
137 size_t window = 0) | |
138 { | |
139 this.filename = filename; | |
140 this.mMode = mode; | |
141 this.window = window; | |
142 this.address = address; | |
143 | |
144 version (Win32) | |
145 { | |
146 void* p; | |
147 uint dwDesiredAccess2; | |
148 uint dwShareMode; | |
149 uint dwCreationDisposition; | |
150 uint flProtect; | |
151 | |
152 if (dwVersion & 0x80000000 && (dwVersion & 0xFF) == 3) | |
153 { | |
154 throw new FileException(filename, | |
155 "Win32s does not implement mm files"); | |
156 } | |
157 | |
158 switch (mode) | |
159 { | |
160 case Mode.Read: | |
161 dwDesiredAccess2 = GENERIC_READ; | |
162 dwShareMode = FILE_SHARE_READ; | |
163 dwCreationDisposition = OPEN_EXISTING; | |
164 flProtect = PAGE_READONLY; | |
165 dwDesiredAccess = FILE_MAP_READ; | |
166 break; | |
167 | |
168 case Mode.ReadWriteNew: | |
169 assert(size != 0); | |
170 dwDesiredAccess2 = GENERIC_READ | GENERIC_WRITE; | |
171 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; | |
172 dwCreationDisposition = CREATE_ALWAYS; | |
173 flProtect = PAGE_READWRITE; | |
174 dwDesiredAccess = FILE_MAP_WRITE; | |
175 break; | |
176 | |
177 case Mode.ReadWrite: | |
178 dwDesiredAccess2 = GENERIC_READ | GENERIC_WRITE; | |
179 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; | |
180 dwCreationDisposition = OPEN_ALWAYS; | |
181 flProtect = PAGE_READWRITE; | |
182 dwDesiredAccess = FILE_MAP_WRITE; | |
183 break; | |
184 | |
185 case Mode.ReadCopyOnWrite: | |
186 if (dwVersion & 0x80000000) | |
187 { | |
188 throw new FileException(filename, | |
189 "Win9x does not implement copy on write"); | |
190 } | |
191 dwDesiredAccess2 = GENERIC_READ | GENERIC_WRITE; | |
192 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; | |
193 dwCreationDisposition = OPEN_EXISTING; | |
194 flProtect = PAGE_WRITECOPY; | |
195 dwDesiredAccess = FILE_MAP_COPY; | |
196 break; | |
197 | |
198 default: | |
199 assert(0); | |
200 } | |
201 | |
202 if (filename) | |
203 { | |
204 if (useWfuncs) | |
205 { | |
206 auto namez = std.utf.toUTF16z(filename); | |
207 hFile = CreateFileW(namez, | |
208 dwDesiredAccess2, | |
209 dwShareMode, | |
210 null, | |
211 dwCreationDisposition, | |
212 FILE_ATTRIBUTE_NORMAL, | |
213 cast(HANDLE)null); | |
214 } | |
215 else | |
216 { | |
217 auto namez = std.file.toMBSz(filename); | |
218 hFile = CreateFileA(namez, | |
219 dwDesiredAccess2, | |
220 dwShareMode, | |
221 null, | |
222 dwCreationDisposition, | |
223 FILE_ATTRIBUTE_NORMAL, | |
224 cast(HANDLE)null); | |
225 } | |
226 if (hFile == INVALID_HANDLE_VALUE) | |
227 goto err1; | |
228 } | |
229 else | |
230 hFile = null; | |
231 | |
232 int hi = cast(int)(size>>32); | |
233 hFileMap = CreateFileMappingA(hFile, null, flProtect, hi, cast(uint)size, null); | |
234 if (hFileMap == null) // mapping failed | |
235 goto err1; | |
236 | |
237 if (size == 0) | |
238 { | |
239 uint sizehi; | |
240 uint sizelow = GetFileSize(hFile,&sizehi); | |
241 size = (cast(ulong)sizehi << 32) + sizelow; | |
242 } | |
243 this.size = size; | |
244 | |
245 size_t initial_map = (window && 2*window<size)? 2*window : cast(size_t)size; | |
246 p = MapViewOfFileEx(hFileMap, dwDesiredAccess, 0, 0, initial_map, address); | |
247 if (!p) goto err1; | |
248 data = p[0 .. initial_map]; | |
249 | |
250 debug (MMFILE) printf("MmFile.this(): p = %p, size = %d\n", p, size); | |
251 return; | |
252 | |
253 err1: | |
254 if (hFileMap != null) | |
255 CloseHandle(hFileMap); | |
256 hFileMap = null; | |
257 | |
258 if (hFile != INVALID_HANDLE_VALUE) | |
259 CloseHandle(hFile); | |
260 hFile = INVALID_HANDLE_VALUE; | |
261 | |
262 errNo(); | |
263 } | |
264 else version (unix_mm) | |
265 { | |
266 char* namez = toStringz(filename); | |
267 void* p; | |
268 int oflag; | |
269 int fmode; | |
270 | |
271 switch (mode) | |
272 { | |
273 case Mode.Read: | |
274 flags = MAP_SHARED; | |
275 prot = PROT_READ; | |
276 oflag = O_RDONLY; | |
277 fmode = 0; | |
278 break; | |
279 | |
280 case Mode.ReadWriteNew: | |
281 assert(size != 0); | |
282 flags = MAP_SHARED; | |
283 prot = PROT_READ | PROT_WRITE; | |
284 oflag = O_CREAT | O_RDWR | O_TRUNC; | |
285 fmode = 0660; | |
286 break; | |
287 | |
288 case Mode.ReadWrite: | |
289 flags = MAP_SHARED; | |
290 prot = PROT_READ | PROT_WRITE; | |
291 oflag = O_CREAT | O_RDWR; | |
292 fmode = 0660; | |
293 break; | |
294 | |
295 case Mode.ReadCopyOnWrite: | |
296 flags = MAP_PRIVATE; | |
297 prot = PROT_READ | PROT_WRITE; | |
298 oflag = O_RDWR; | |
299 fmode = 0; | |
300 break; | |
301 | |
302 default: | |
303 assert(0); | |
304 } | |
305 | |
306 if (filename.length) | |
307 { | |
308 struct_stat statbuf; | |
309 | |
310 fd = unix.open(namez, oflag, fmode); | |
311 if (fd == -1) | |
312 { | |
313 // printf("\topen error, errno = %d\n",getErrno()); | |
314 errNo(); | |
315 } | |
316 | |
317 if (unix.fstat(fd, &statbuf)) | |
318 { | |
319 //printf("\tfstat error, errno = %d\n",getErrno()); | |
320 unix.close(fd); | |
321 errNo(); | |
322 } | |
323 | |
324 if (prot & PROT_WRITE && size > statbuf.st_size) | |
325 { | |
326 // Need to make the file size bytes big | |
327 unix.lseek(fd, cast(off_t)(size - 1), SEEK_SET); | |
328 char c = 0; | |
329 unix.write(fd, &c, 1); | |
330 } | |
331 else if (prot & PROT_READ && size == 0) | |
332 size = statbuf.st_size; | |
333 } | |
334 else | |
335 { | |
336 fd = -1; | |
337 flags |= MAP_ANONYMOUS; | |
338 } | |
339 this.size = size; | |
340 size_t initial_map = (window && 2*window<size)? 2*window : cast(size_t)size; | |
341 p = mmap(address, initial_map, prot, flags, fd, 0); | |
342 if (p == MAP_FAILED) { | |
343 if (fd != -1) | |
344 unix.close(fd); | |
345 errNo(); | |
346 } | |
347 | |
348 data = p[0 .. initial_map]; | |
349 } | |
350 else static if (! Have_MMFile) | |
351 { | |
352 throw new FileException("This system does support memory mapped files"); | |
353 } | |
354 else | |
355 { | |
356 static assert(0); | |
357 } | |
358 } | |
359 | |
360 /** | |
361 * Flushes pending output and closes the memory mapped file. | |
362 */ | |
363 ~this() | |
364 { | |
365 debug (MMFILE) printf("MmFile.~this()\n"); | |
366 unmap(); | |
367 version (Win32) | |
368 { | |
369 if (hFileMap != null && CloseHandle(hFileMap) != TRUE) | |
370 errNo(); | |
371 hFileMap = null; | |
372 | |
373 if (hFile != INVALID_HANDLE_VALUE && CloseHandle(hFile) != TRUE) | |
374 errNo(); | |
375 hFile = INVALID_HANDLE_VALUE; | |
376 } | |
377 else version (unix_mm) | |
378 { | |
379 if (fd != -1 && unix.close(fd) == -1) | |
380 errNo(); | |
381 fd = -1; | |
382 } | |
383 else static if (! Have_MMFile) | |
384 { | |
385 } | |
386 else | |
387 { | |
388 static assert(0); | |
389 } | |
390 data = null; | |
391 } | |
392 | |
393 /* Flush any pending output. | |
394 */ | |
395 void flush() | |
396 { | |
397 debug (MMFILE) printf("MmFile.flush()\n"); | |
398 version (Win32) | |
399 { | |
400 FlushViewOfFile(data.ptr, data.length); | |
401 } | |
402 else version (unix_mm) | |
403 { | |
404 int i; | |
405 | |
406 i = msync(cast(void*)data, data.length, MS_SYNC); // sys/mman.h | |
407 if (i != 0) | |
408 errNo(); | |
409 } | |
410 else static if (! Have_MMFile) | |
411 { | |
412 } | |
413 else | |
414 { | |
415 static assert(0); | |
416 } | |
417 } | |
418 | |
419 /** | |
420 * Gives size in bytes of the memory mapped file. | |
421 */ | |
422 ulong length() | |
423 { | |
424 debug (MMFILE) printf("MmFile.length()\n"); | |
425 return size; | |
426 } | |
427 | |
428 /** | |
429 * Read-only property returning the file mode. | |
430 */ | |
431 Mode mode() | |
432 { | |
433 debug (MMFILE) printf("MmFile.mode()\n"); | |
434 return mMode; | |
435 } | |
436 | |
437 /** | |
438 * Returns entire file contents as an array. | |
439 */ | |
440 void[] opSlice() | |
441 { | |
442 debug (MMFILE) printf("MmFile.opSlice()\n"); | |
443 return opSlice(0,size); | |
444 } | |
445 | |
446 /** | |
447 * Returns slice of file contents as an array. | |
448 */ | |
449 void[] opSlice(ulong i1, ulong i2) | |
450 { | |
451 debug (MMFILE) printf("MmFile.opSlice(%lld, %lld)\n", i1, i2); | |
452 ensureMapped(i1,i2); | |
453 size_t off1 = cast(size_t)(i1-start); | |
454 size_t off2 = cast(size_t)(i2-start); | |
455 return data[off1 .. off2]; | |
456 } | |
457 | |
458 /** | |
459 * Returns byte at index i in file. | |
460 */ | |
461 ubyte opIndex(ulong i) | |
462 { | |
463 debug (MMFILE) printf("MmFile.opIndex(%lld)\n", i); | |
464 ensureMapped(i); | |
465 size_t off = cast(size_t)(i-start); | |
466 return (cast(ubyte[])data)[off]; | |
467 } | |
468 | |
469 /** | |
470 * Sets and returns byte at index i in file to value. | |
471 */ | |
472 ubyte opIndexAssign(ubyte value, ulong i) | |
473 { | |
474 debug (MMFILE) printf("MmFile.opIndex(%lld, %d)\n", i, value); | |
475 ensureMapped(i); | |
476 size_t off = cast(size_t)(i-start); | |
477 return (cast(ubyte[])data)[off] = value; | |
478 } | |
479 | |
480 | |
481 // return true if the given position is currently mapped | |
482 private int mapped(ulong i) | |
483 { | |
484 debug (MMFILE) printf("MmFile.mapped(%lld, %lld, %d)\n", i,start, | |
485 data.length); | |
486 return i >= start && i < start+data.length; | |
487 } | |
488 | |
489 // unmap the current range | |
490 private void unmap() | |
491 { | |
492 debug (MMFILE) printf("MmFile.unmap()\n"); | |
493 version(Windows) { | |
494 /* Note that under Windows 95, UnmapViewOfFile() seems to return | |
495 * random values, not TRUE or FALSE. | |
496 */ | |
497 if (data && UnmapViewOfFile(data.ptr) == FALSE && | |
498 (dwVersion & 0x80000000) == 0) | |
499 errNo(); | |
500 } else version (unix_mm) { | |
501 if (data && munmap(cast(void*)data, data.length) != 0) | |
502 errNo(); | |
503 } | |
504 data = null; | |
505 } | |
506 | |
507 // map range | |
508 private void map(ulong start, size_t len) | |
509 { | |
510 debug (MMFILE) printf("MmFile.map(%lld, %d)\n", start, len); | |
511 void* p; | |
512 if (start+len > size) | |
513 len = cast(size_t)(size-start); | |
514 version(Windows) { | |
515 uint hi = cast(uint)(start>>32); | |
516 p = MapViewOfFileEx(hFileMap, dwDesiredAccess, hi, cast(uint)start, len, address); | |
517 if (!p) errNo(); | |
518 } else version (unix_mm) { | |
519 p = mmap(address, len, prot, flags, fd, cast(off_t)start); | |
520 if (p == MAP_FAILED) errNo(); | |
521 } | |
522 data = p[0 .. len]; | |
523 this.start = start; | |
524 } | |
525 | |
526 // ensure a given position is mapped | |
527 private void ensureMapped(ulong i) | |
528 { | |
529 debug (MMFILE) printf("MmFile.ensureMapped(%lld)\n", i); | |
530 if (!mapped(i)) { | |
531 unmap(); | |
532 if (window == 0) { | |
533 map(0,cast(size_t)size); | |
534 } else { | |
535 ulong block = i/window; | |
536 if (block == 0) | |
537 map(0,2*window); | |
538 else | |
539 map(window*(block-1),3*window); | |
540 } | |
541 } | |
542 } | |
543 | |
544 // ensure a given range is mapped | |
545 private void ensureMapped(ulong i, ulong j) | |
546 { | |
547 debug (MMFILE) printf("MmFile.ensureMapped(%lld, %lld)\n", i, j); | |
548 if (!mapped(i) || !mapped(j-1)) { | |
549 unmap(); | |
550 if (window == 0) { | |
551 map(0,cast(size_t)size); | |
552 } else { | |
553 ulong iblock = i/window; | |
554 ulong jblock = (j-1)/window; | |
555 if (iblock == 0) { | |
556 map(0,cast(size_t)(window*(jblock+2))); | |
557 } else { | |
558 map(window*(iblock-1),cast(size_t)(window*(jblock-iblock+3))); | |
559 } | |
560 } | |
561 } | |
562 } | |
563 | |
564 private: | |
565 char[] filename; | |
566 void[] data; | |
567 ulong start; | |
568 size_t window; | |
569 ulong size; | |
570 Mode mMode; | |
571 void* address; | |
572 | |
573 version (Win32) | |
574 { | |
575 HANDLE hFile = INVALID_HANDLE_VALUE; | |
576 HANDLE hFileMap = null; | |
577 uint dwDesiredAccess; | |
578 } | |
579 else version (unix_mm) | |
580 { | |
581 int fd; | |
582 int prot; | |
583 int flags; | |
584 int fmode; | |
585 } | |
586 else static if (! Have_MMFile) | |
587 { | |
588 } | |
589 else | |
590 { | |
591 static assert(0); | |
592 } | |
593 | |
594 // Report error, where errno gives the error number | |
595 void errNo() | |
596 { | |
597 version (Win32) | |
598 { | |
599 throw new FileException(filename, GetLastError()); | |
600 } | |
601 else version (Unix) | |
602 { | |
603 throw new FileException(filename, getErrno()); | |
604 } | |
605 else static if (! Have_MMFile) | |
606 { | |
607 throw new FileException(filename, "MMFile unsupported"); | |
608 } | |
609 else | |
610 { | |
611 static assert(0); | |
612 } | |
613 } | |
614 } | |
615 | |
616 unittest { | |
617 static if (Have_MMFile) | |
618 { | |
619 const size_t K = 1024; | |
620 size_t win = 64*K; // assume the page size is 64K | |
621 version(Win32) { | |
622 /+ these aren't defined in std.c.windows.windows so let's use the default | |
623 SYSTEM_INFO sysinfo; | |
624 GetSystemInfo(&sysinfo); | |
625 win = sysinfo.dwAllocationGranularity; | |
626 +/ | |
627 } else version (Unix) { | |
628 // getpagesize() is not defined in the unix D headers so use the guess | |
629 } | |
630 MmFile mf = new MmFile("testing.txt",MmFile.Mode.ReadWriteNew,100*K,null,win); | |
631 ubyte[] str = cast(ubyte[])"1234567890"; | |
632 ubyte[] data = cast(ubyte[])mf[0 .. 10]; | |
633 data[] = str[]; | |
634 assert( mf[0 .. 10] == str ); | |
635 data = cast(ubyte[])mf[50 .. 60]; | |
636 data[] = str[]; | |
637 assert( mf[50 .. 60] == str ); | |
638 ubyte[] data2 = cast(ubyte[])mf[20*K .. 60*K]; | |
639 assert( data2.length == 40*K ); | |
640 assert( data2[length-1] == 0 ); | |
641 mf[100*K-1] = cast(ubyte)'b'; | |
642 data2 = cast(ubyte[])mf[21*K .. 100*K]; | |
643 assert( data2.length == 79*K ); | |
644 assert( data2[length-1] == 'b' ); | |
645 delete mf; | |
646 std.file.remove("testing.txt"); | |
647 } | |
648 } |