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

Generated on Sat Dec 24 17:28:32 2005 for Mango by  doxygen 1.4.0