Main Page | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | File Members | Related Pages

FileConduit.d

Go to the documentation of this file.
00001 /*******************************************************************************
00002 
00003         @file FileConduit.d
00004         
00005         Copyright (c) 2004 Kris Bell
00006         
00007         This software is provided 'as-is', without any express or implied
00008         warranty. In no event will the authors be held liable for damages
00009         of any kind arising from the use of this software.
00010         
00011         Permission is hereby granted to anyone to use this software for any 
00012         purpose, including commercial applications, and to alter it and/or 
00013         redistribute it freely, subject to the following restrictions:
00014         
00015         1. The origin of this software must not be misrepresented; you must 
00016            not claim that you wrote the original software. If you use this 
00017            software in a product, an acknowledgment within documentation of 
00018            said product would be appreciated but is not required.
00019 
00020         2. Altered source versions must be plainly marked as such, and must 
00021            not be misrepresented as being the original software.
00022 
00023         3. This notice may not be removed or altered from any distribution
00024            of the source.
00025 
00026         4. Derivative works are permitted, but they must carry this notice
00027            in full and credit the original source.
00028 
00029 
00030                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00031 
00032 
00033         @version        Initial version; March 2004      
00034 
00035         @author         Kris 
00036                         John Reimer
00037                         Anders F Bjorklund (Darwin patches)
00038                         Chris Sauls (Win95 file support)
00039 
00040 *******************************************************************************/
00041 
00042 module mango.io.FileConduit;
00043 
00044 public  import  mango.sys.System;
00045 
00046 public  import  mango.io.Buffer,
00047                 mango.io.FilePath,
00048                 mango.io.FileProxy,
00049                 mango.io.FileStyle;
00050 
00051 private import  mango.io.Conduit,
00052                 mango.io.ConduitStyle,
00053                 mango.io.DeviceConduit;
00054 
00055 /*******************************************************************************
00056 
00057         Bring in native Unix functions. Note that the functions read()
00058         and close() will actually hide the inherited Conduit.read() and 
00059         Resource.close() if the import is placed within the body of the
00060         FileConduit class. Apparently this behaviour is 'by design', so
00061         fair warning to those who might expect otherwise.
00062 
00063 *******************************************************************************/
00064 
00065 version (Posix)
00066 {
00067         version (linux) 
00068         {
00069                 private import std.c.linux.linux;
00070                 alias std.c.linux.linux posix;
00071         }
00072 
00073         version (darwin) 
00074         {
00075                 private import std.c.darwin.darwin;
00076                 alias std.c.darwin.darwin posix;
00077         }
00078                 
00079         private extern (C) int ftruncate (int, int);
00080 }
00081 
00082 /*******************************************************************************
00083 
00084         Bring in native Windows functions
00085 
00086 *******************************************************************************/
00087 
00088 version (Win32)
00089 {
00090         private import std.c.windows.windows;  
00091              
00092         private extern (Windows) 
00093         {
00094                 BOOL   SetEndOfFile       (HANDLE);
00095                 BOOL   UnmapViewOfFile    (LPCVOID);
00096                 BOOL   FlushViewOfFile    (LPCVOID, DWORD);
00097                 LPVOID MapViewOfFile      (HANDLE, DWORD, DWORD, DWORD, DWORD);
00098                 HANDLE CreateFileMappingA (HANDLE, LPSECURITY_ATTRIBUTES, DWORD, DWORD, DWORD, LPCTSTR);
00099         }
00100 }
00101 
00102 
00103 /*******************************************************************************
00104 
00105         Implements a means of reading and writing a generic file. Conduits
00106         are the primary means of accessing external data, and are usually 
00107         routed through a Buffer. File conduit extends the generic conduit
00108         by providing file-specific methods to set the file size, seek to a
00109         specific file position, and so on. Also provided is a class for
00110         creating a memory-mapped Buffer upon a file.     
00111         
00112         Serial input and output is straightforward. In this example we
00113         copy a file directly to the console:
00114 
00115         @code
00116         // open a file for reading
00117         FileConduit from = new FileConduit ("test.txt");
00118 
00119         // stream directly to console
00120         Stdout.conduit.copy (from);
00121         @endcode
00122 
00123         And here we copy one file to another:
00124 
00125         @code
00126         // open a file for reading
00127         FileConduit from = new FileConduit ("test.txt");
00128 
00129         // open another for writing
00130         FileConduit to = new FileConduit ("copy.txt", FileStyle.WriteTruncate);
00131 
00132         // copy file
00133         to.copy (from);
00134         @endcode
00135         
00136         FileConduit can just as easily handle random IO. Here we see how
00137         a Reader and Writer are used to perform simple input and output:
00138 
00139         @code
00140         // open a file for reading
00141         FileConduit fc = new FileConduit ("random.bin", FileStyle.ReadWriteCreate);
00142 
00143         // construct (binary) reader & writer upon this conduit
00144         Reader read = new Reader (fc);
00145         Writer write = new Writer (fc);
00146 
00147         int x=10, y=20;
00148 
00149         // write some data, and flush output since IO is buffered
00150         write (x) (y) ();
00151 
00152         // rewind to file start
00153         fc.seek (0);
00154 
00155         // read data back again, but swap destinations
00156         read (y) (x);
00157 
00158         assert (y==10);
00159         assert (x==20);
00160 
00161         fc.close();
00162         @endcode
00163 
00164         FileConduits can also be used directly, without Readers, Writers, or
00165         Buffers. To load a file directly into local-memory one might do this:
00166 
00167         @code
00168         // open file for reading
00169         FileConduit fc = new FileConduit ("test.txt");
00170 
00171         // create an array to house the entire file
00172         char[] content = new char[fc.length];
00173 
00174         // read the file content. Return value is the number of bytes read
00175         int bytesRead = fc.read (content);
00176         @endcode
00177 
00178         Conversely, one may write directly to a FileConduit, like so:
00179 
00180         @code
00181         // open file for writing
00182         FileConduit to = new FileConduit ("text.txt", FileStyle.WriteTruncate);
00183 
00184         // write an array of content to it
00185         int bytesWritten = fc.write (content);
00186         @endcode
00187 
00188 
00189         See File, FilePath, FileProxy, FileConst, FileScan, and FileSystem for 
00190         additional functionality related to file manipulation. 
00191         
00192         Doxygen has a hard time with D version() statements, so part of this 
00193         class is documented within FileConduit::VersionWin32 instead.
00194 
00195         Compile with -version=Win32SansUnicode to enable Win95 & Win32s file 
00196         support.
00197         
00198 *******************************************************************************/
00199 
00200 class FileConduit : DeviceConduit, ISeekable
00201 {
00202         // the file we're working with 
00203         private FilePath path;
00204 
00205         // expose deviceconduit.copy() methods also 
00206         alias DeviceConduit.copy      copy;
00207         alias DeviceConduit.read      read;
00208         alias DeviceConduit.write     write;
00209 
00210         /***********************************************************************
00211         
00212                 Create a FileConduit with the provided path and style.
00213 
00214         ***********************************************************************/
00215 
00216         this (char[] name, FileStyle style = FileStyle.ReadExisting)
00217         {
00218                 this (new FilePath(name), style);
00219         }
00220 
00221         /***********************************************************************
00222         
00223                 Create a FileConduit from the provided proxy and style.
00224 
00225         ***********************************************************************/
00226 
00227         this (FileProxy proxy, FileStyle style = FileStyle.ReadExisting)
00228         {
00229                 this (proxy.getPath(), style);
00230         }
00231 
00232         /***********************************************************************
00233         
00234                 Create a FileConduit with the provided path and style.
00235 
00236         ***********************************************************************/
00237 
00238         this (FilePath path, FileStyle style = FileStyle.ReadExisting)
00239         {
00240                 // say we're seekable
00241                 super (style, true);
00242                 
00243                 // remember who we are
00244                 this.path = path;
00245 
00246                 // open the file
00247                 open (style);
00248 
00249                 // bump lock count
00250                 acquire();
00251         }    
00252 
00253         /***********************************************************************
00254         
00255                 Return the FilePath used by this file.
00256 
00257         ***********************************************************************/
00258 
00259         FilePath getPath ()
00260         {
00261                 return path;
00262         }               
00263 
00264         /***********************************************************************
00265         
00266                 Move the file position to the given offset (from the file 
00267                 start) and return the adjusted postion.
00268 
00269         ***********************************************************************/
00270 
00271         long seek (long offset)
00272         {
00273                 return seek (offset, SeekAnchor.Begin);
00274         }               
00275 
00276         /***********************************************************************
00277                 
00278                 Return the current file position.
00279                 
00280         ***********************************************************************/
00281 
00282         long getPosition ()
00283         {
00284                 return seek (0, SeekAnchor.Current);
00285         }               
00286 
00287         /***********************************************************************
00288         
00289                 Return the total length of this file.
00290 
00291         ***********************************************************************/
00292 
00293         long length ()
00294         {
00295                 long    pos,    
00296                         ret;
00297                         
00298                 pos = getPosition ();
00299                 ret = seek (0, SeekAnchor.End);
00300                 seek (pos);
00301                 return ret;
00302         }               
00303 
00304         /***********************************************************************
00305 
00306                 Transfer the content of another file to this one. Returns a
00307                 reference to this class on success, or throws an IOException 
00308                 upon failure.
00309         
00310         ***********************************************************************/
00311 
00312         FileConduit copy (FilePath source)
00313         {
00314                 auto FileConduit fc = new FileConduit (source);
00315                 super.copy (fc);
00316                 return this;
00317         }               
00318 
00319         /***********************************************************************
00320         
00321                 Return the name used by this file.
00322 
00323         ***********************************************************************/
00324 
00325         protected override char[] getName ()
00326         {
00327                 return path.toString;
00328         }               
00329 
00330 
00331         /***********************************************************************
00332 
00333                 Windows-specific code
00334         
00335         ***********************************************************************/
00336 
00337         version(Win32)
00338         {
00339                 private bool appending;
00340 
00341                 /***************************************************************
00342 
00343                         Open a file with the provided style.
00344 
00345                 ***************************************************************/
00346 
00347                 protected void open (FileStyle style)
00348                 {
00349                         DWORD   attr,
00350                                 share,
00351                                 access,
00352                                 create;
00353 
00354                         alias DWORD[] Flags;
00355 
00356                         static const Flags Access =  
00357                                         [
00358                                         0,                      // invalid
00359                                         GENERIC_READ,
00360                                         GENERIC_WRITE,
00361                                         GENERIC_READ | GENERIC_WRITE,
00362                                         ];
00363                                                 
00364                         static const Flags Create =  
00365                                         [
00366                                         OPEN_EXISTING,          // must exist
00367                                         CREATE_ALWAYS,          // create always
00368                                         TRUNCATE_EXISTING,      // must exist
00369                                         CREATE_ALWAYS,          // (for appending)
00370                                         ];
00371                                                 
00372                         static const Flags Share =   
00373                                         [
00374                                         FILE_SHARE_READ,
00375                                         FILE_SHARE_WRITE,
00376                                         FILE_SHARE_READ | FILE_SHARE_WRITE,
00377                                         ];
00378                                                 
00379                         static const Flags Attr =   
00380                                         [
00381                                         0,
00382                                         FILE_FLAG_RANDOM_ACCESS,
00383                                         FILE_FLAG_SEQUENTIAL_SCAN,
00384                                         0,
00385                                         FILE_FLAG_WRITE_THROUGH,
00386                                         ];
00387 
00388                         attr = Attr[style.cache];
00389                         share = Share[style.share];
00390                         create = Create[style.open];
00391                         access = Access[style.access];
00392 
00393                         version (Win32SansUnicode)
00394                                  handle = CreateFileA (path.toUtf8, access, share, 
00395                                                        null, create, 
00396                                                        attr | FILE_ATTRIBUTE_NORMAL,
00397                                                        cast(HANDLE) null);
00398                              else
00399                                 handle = CreateFileW (path.toUtf16, access, share, 
00400                                                       null, create, 
00401                                                       attr | FILE_ATTRIBUTE_NORMAL,
00402                                                       cast(HANDLE) null);
00403 
00404                         if (handle == INVALID_HANDLE_VALUE)
00405                             error ();
00406 
00407                         // move to end of file?
00408                         if (style.open == style.Open.Append)
00409                             appending = true;
00410                 }
00411                 
00412                 /***************************************************************
00413 
00414                         Write a chunk of bytes to the file from the provided
00415                         array (typically that belonging to an IBuffer)
00416 
00417                 ***************************************************************/
00418 
00419                 int write (void[] src)
00420                 {
00421                         DWORD written;
00422 
00423                         // try to emulate the Unix O_APPEND mode
00424                         if (appending)
00425                             SetFilePointer (handle, 0, null, SeekAnchor.End);
00426                         
00427                         return super.write (src);
00428                 }
00429             
00430                 /***************************************************************
00431 
00432                         Set the file size to be that of the current seek 
00433                         position. The file must be writable for this to
00434                         succeed.
00435 
00436                 ***************************************************************/
00437 
00438                 void truncate ()
00439                 {
00440                         // must have Generic_Write access
00441                         if (! SetEndOfFile (handle))
00442                               error ();                            
00443                 }               
00444 
00445                 /***************************************************************
00446 
00447                         Set the file seek position to the specified offset
00448                         from the given anchor. 
00449 
00450                 ***************************************************************/
00451 
00452                 long seek (long offset, SeekAnchor anchor)
00453                 {
00454                         LONG high = (offset >> 32);
00455                         long result = SetFilePointer (handle, cast(LONG) offset, 
00456                                                       &high, anchor);
00457 
00458                         if (result == -1 && 
00459                             GetLastError() != ERROR_SUCCESS)
00460                             error ();
00461 
00462                         return result + (cast(long) high << 32);
00463                 }               
00464         }
00465 
00466 
00467         /***********************************************************************
00468 
00469                  Unix-specific code. Note that some methods are 32bit only
00470         
00471         ***********************************************************************/
00472 
00473         version (Posix)
00474         {
00475                 /***************************************************************
00476 
00477                         Open a file with the provided style.
00478 
00479                 ***************************************************************/
00480 
00481                 protected void open (FileStyle style)
00482                 {
00483                         int     share, 
00484                                 access;
00485 
00486                         alias int[] Flags;
00487 
00488                         static const Flags Access =  
00489                                         [
00490                                         0,              // invalid
00491                                         O_RDONLY,
00492                                         O_WRONLY,
00493                                         O_RDWR,
00494                                         ];
00495                                                 
00496                         static const Flags Create =  
00497                                         [
00498                                         0,              // open existing
00499                                         O_CREAT,        // create always
00500                                         O_TRUNC,        // must exist
00501                                         O_APPEND | O_CREAT, 
00502                                         ];
00503 
00504                         // this is not the same as Windows sharing,
00505                         // but it's perhaps a reasonable approximation
00506                         static const Flags Share =   
00507                                         [
00508                                         0640,           // read access
00509                                         0620,           // write access
00510                                         0660,           // read & write
00511                                         ];
00512                                                 
00513                         share = Share[style.share];
00514                         access = Access[style.access] | Create[style.open];
00515 
00516                         handle = posix.open (path.toUtf8, access, share);
00517                         if (handle == -1)
00518                             error ();
00519                 }
00520 
00521                 /***************************************************************
00522 
00523                         32bit only ...
00524 
00525                         Set the file size to be that of the current seek 
00526                         position. The file must be writable for this to
00527                         succeed.
00528 
00529                 ***************************************************************/
00530 
00531                 void truncate ()
00532                 {
00533                         // set filesize to be current seek-position
00534                         if (ftruncate (handle, getPosition()) == -1)
00535                             error ();
00536                 }               
00537 
00538                 /***************************************************************
00539 
00540                         32bit only ...
00541 
00542                         Set the file seek position to the specified offset
00543                         from the given anchor. 
00544 
00545                 ***************************************************************/
00546 
00547                 long seek (long offset, SeekAnchor anchor)
00548                 {
00549                         int result = posix.lseek (handle, offset, anchor);
00550                         if (result == -1)
00551                             error ();
00552                         return result;
00553                 }               
00554         }
00555 }
00556 
00557 
00558 
00559 /*******************************************************************************
00560 
00561         Class to handle memory-mapped files
00562 
00563 *******************************************************************************/
00564 version (Mapped)
00565 {
00566 class MappedFile : MappedBuffer
00567 {
00568         version (Win32)
00569         {
00570                 private FileConduit     host;
00571                 private void*           base;
00572                 private HANDLE          mmFile;
00573 
00574                 /***************************************************************
00575 
00576                         Construct a MappedBuffer upon the given FileConduit. 
00577                         One should set the file size using seek() & truncate() 
00578                         to setup the available working space.
00579 
00580                 ***************************************************************/
00581 
00582                 this (FileConduit host)
00583                 {
00584                         this.host = host;
00585 
00586                         // can only do 32bit mapping on 32bit platform
00587                         long size = host.length;
00588                         ConduitStyle style = host.getStyle;
00589 
00590                         DWORD flags = PAGE_READWRITE;
00591                         if (style.access == ConduitStyle.Access.Read)
00592                             flags = PAGE_READONLY;
00593 
00594                         mmFile = CreateFileMappingA (host.handle, null, flags, 0, 0, null);
00595                         if (mmFile is null)
00596                             host.error ();
00597 
00598                         flags = FILE_MAP_WRITE;
00599                         if (style.access == ConduitStyle.Access.Read)
00600                             flags = FILE_MAP_READ;
00601 
00602                         base = MapViewOfFile (mmFile, flags, 0, 0, 0);
00603                         if (base is null)
00604                             host.error ();
00605 
00606                         void[] mem = base[0..size];
00607                         setValidContent (mem);
00608                 }
00609 
00610                 /***************************************************************
00611 
00612                         Ensure this is closed when GC'd
00613 
00614                 ***************************************************************/
00615 
00616                 ~this ()
00617                 {
00618                         close ();        
00619                 }
00620 
00621                 /***************************************************************
00622 
00623                         Close this mapped buffer
00624 
00625                 ***************************************************************/
00626 
00627                 void close ()
00628                 {
00629                         if (base)
00630                             UnmapViewOfFile (base);
00631 
00632                         if (mmFile)
00633                             CloseHandle (mmFile);       
00634 
00635                         mmFile = null;
00636                         base = null;
00637                 }
00638 
00639                 /***************************************************************
00640 
00641                         Flush dirty content out to the drive. This
00642                         fails with error 33 if the file content is
00643                         virgin. Opening a file for ReadWriteExists
00644                         followed by a flush() will cause this.
00645 
00646                 ***************************************************************/
00647 
00648                 void flush ()
00649                 {
00650                         // flush all dirty pages
00651                         if (! FlushViewOfFile (base, 0))
00652                               host.error ();
00653                 }
00654         }
00655 
00656 
00657         version (Posix)
00658         {
00659                 // not yet supported
00660                 private this (){};
00661         }
00662 }
00663 }

Generated on Fri May 27 18:11:55 2005 for Mango by  doxygen 1.4.0