| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360 | /******************************************************************************* copyright: Copyright (c) 2004 Kris Bell. All rights reserved license: BSD style: $(LICENSE) version: Initial release: March 2004 author: Kris *******************************************************************************/ module tango.io.device.FileMap; private import tango.sys.Common; private import tango.io.device.File, tango.io.device.Array; /******************************************************************************* External declarations. *******************************************************************************/ version (Win32) private extern (Windows) { BOOL UnmapViewOfFile (LPCVOID); BOOL FlushViewOfFile (LPCVOID, DWORD); LPVOID MapViewOfFile (HANDLE, DWORD, DWORD, DWORD, DWORD); HANDLE CreateFileMappingA (HANDLE, LPSECURITY_ATTRIBUTES, DWORD, DWORD, DWORD, LPCTSTR); } version (Posix) private import tango.stdc.posix.sys.mman; /******************************************************************************* *******************************************************************************/ class FileMap : Array { private MappedFile file; /*********************************************************************** Construct a FileMap upon the given path. You should use resize() to setup the available working space. ***********************************************************************/ this (const(char[]) path, File.Style style = File.ReadWriteOpen) { file = new MappedFile (path, style); super (file.map); } /*********************************************************************** Resize the file and return the remapped content. Usage of map() is not required following this call. ***********************************************************************/ final ubyte[] resize (long size) { auto ret = file.resize (size); super.assign (ret); return ret; } /*********************************************************************** Release external resources. ***********************************************************************/ override void close () { super.close(); if (file) file.close(); file = null; } } /******************************************************************************* *******************************************************************************/ class MappedFile { private File host; /*********************************************************************** Construct a FileMap upon the given path. You should use resize() to setup the available working space. ***********************************************************************/ this (const(char[]) path, File.Style style = File.ReadWriteOpen) { host = new File (path, style); } /*********************************************************************** ***********************************************************************/ @property final long length () { return host.length; } /*********************************************************************** ***********************************************************************/ @property final const(char)[] path () { return host.toString(); } /*********************************************************************** Resize the file and return the remapped content. Usage of map() is not required following this call. ***********************************************************************/ final ubyte[] resize (long size) { host.truncate (size); return map; } /*********************************************************************** ***********************************************************************/ version (Win32) { private void* base; // array pointer private HANDLE mmFile; // mapped file /*************************************************************** Return a slice representing file content as a memory-mapped array. ***************************************************************/ @property final ubyte[] map () { DWORD flags; // be wary of redundant references if (base) reset(); // can only do 32bit mapping on 32bit platform auto size = cast(size_t) host.length; auto access = host.style.access; flags = PAGE_READONLY; if (access & host.Access.Write) flags = PAGE_READWRITE; auto handle = cast(HANDLE) host.fileHandle; mmFile = CreateFileMappingA (handle, null, flags, 0, 0, null); if (mmFile is null) host.error(); flags = FILE_MAP_READ; if (access & host.Access.Write) flags |= FILE_MAP_WRITE; base = MapViewOfFile (mmFile, flags, 0, 0, 0); if (base is null) host.error(); return (cast(ubyte*) base) [0 .. size]; } /*************************************************************** Release this mapping without flushing. ***************************************************************/ final void close () { reset(); if (host) host.close(); host = null; } /*************************************************************** ***************************************************************/ private void reset () { if (base) UnmapViewOfFile (base); if (mmFile) CloseHandle (mmFile); mmFile = null; base = null; } /*************************************************************** Flush dirty content out to the drive. This fails with error 33 if the file content is virgin. Opening a file for ReadWriteExists followed by a flush() will cause this. ***************************************************************/ MappedFile flush () { // flush all dirty pages if (! FlushViewOfFile (base, 0)) host.error(); return this; } } /*********************************************************************** ***********************************************************************/ version (Posix) { // Linux code: not yet tested on other POSIX systems. private void* base; // array pointer private size_t size; // length of file /*************************************************************** Return a slice representing file content as a memory-mapped array. Use this to remap content each time the file size is changed. ***************************************************************/ @property final ubyte[] map () { // be wary of redundant references if (base) reset(); // can only do 32bit mapping on 32bit platform size = cast (size_t) host.length; // Make sure the mapping attributes are consistant with // the File attributes. int flags = MAP_SHARED; int protection = PROT_READ; auto access = host.style.access; if (access & host.Access.Write) protection |= PROT_WRITE; base = mmap (null, size, protection, flags, host.fileHandle, 0); if (base is MAP_FAILED) { base = null; host.error(); } return (cast(ubyte*) base) [0 .. size]; } /*************************************************************** Release this mapped buffer without flushing. ***************************************************************/ final void close () { reset(); if (host) host.close(); host = null; } /*************************************************************** ***************************************************************/ private void reset () { // NOTE: When a process ends, all mmaps belonging to that process // are automatically unmapped by system (Linux). // On the other hand, this is NOT the case when the related // file descriptor is closed. This function unmaps explicitly. if (base) if (munmap (base, size)) host.error(); base = null; } /*************************************************************** Flush dirty content out to the drive. ***************************************************************/ final MappedFile flush () { // MS_ASYNC: delayed flush; equivalent to "add-to-queue" // MS_SYNC: function flushes file immediately; no return until flush complete // MS_INVALIDATE: invalidate all mappings of the same file (shared) if (msync (base, size, MS_SYNC | MS_INVALIDATE)) host.error(); return this; } } } /******************************************************************************* *******************************************************************************/ debug (FileMap) { import tango.io.Path; void main() { auto file = new MappedFile ("foo.map"); auto heap = file.resize (1_000_000); auto file1 = new MappedFile ("foo1.map"); auto heap1 = file1.resize (1_000_000); file.close(); remove ("foo.map"); file1.close(); remove ("foo1.map"); } } |