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

Generated on Mon Nov 14 10:59:37 2005 for Mango by  doxygen 1.4.0