Main Page | Class Hierarchy | Alphabetical List | Class List | 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 
00027                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00028 
00029 
00030         @version        Initial version, March 2004      
00031         @author         Kris, John Reimer
00032 
00033 
00034 *******************************************************************************/
00035 
00036 module mango.io.FileConduit;
00037 
00038 public  import  mango.base.System;
00039 
00040 public  import  mango.io.Buffer,
00041                 mango.io.FilePath,
00042                 mango.io.FileProxy,
00043                 mango.io.FileStyle;
00044 
00045 private import  mango.io.Conduit,
00046                 mango.io.Exception,
00047                 mango.io.ConduitStyle;
00048 
00049 /*******************************************************************************
00050 
00051         Bring in native linux functions. Note that the functions read()
00052         and close() will actually hide the inherited Conduit.read() and 
00053         Resource.close() if the import is placed within the body of the
00054         FileConduit class. That is surely a compiler bug.
00055 
00056 *******************************************************************************/
00057 
00058 version (linux)
00059         {
00060         private import std.c.linux.linux;
00061         }
00062 
00063 /*******************************************************************************
00064 
00065         Bring in native Windows functions
00066 
00067 *******************************************************************************/
00068 
00069 version (Win32)
00070 {
00071         private import std.c.windows.windows;  
00072              
00073         private extern (Windows) 
00074         {
00075         HANDLE GetStdHandle (DWORD);
00076         BOOL   SetEndOfFile (HANDLE);
00077 
00078         BOOL   UnmapViewOfFile    (LPCVOID);
00079         BOOL   FlushViewOfFile    (LPCVOID, DWORD);
00080         LPVOID MapViewOfFile      (HANDLE, DWORD, DWORD, DWORD, DWORD);
00081         HANDLE CreateFileMappingA (HANDLE, LPSECURITY_ATTRIBUTES, DWORD, DWORD, DWORD, LPCTSTR);
00082         }
00083 }
00084 
00085 
00086 /*******************************************************************************
00087 
00088         Implements a means of reading and writing a generic file. Conduits
00089         are the primary means of accessing external data, and are usually 
00090         routed through a Buffer. File conduit extends the generic conduit
00091         by providing file-specific methods to set the file size, seek to a
00092         specific file position, and so on. Also provided is a factory for
00093         create a memory-mapped Buffer upon a file.     
00094         
00095         Serial input and output is straightforward. In this example we
00096         copy a file directly to the console:
00097 
00098         @code
00099         // open a file for reading
00100         FileConduit fc = new FileConduit ("test.txt");
00101 
00102         // stream directly to console
00103         Stdio.stdout.copy (fc);
00104         @endcode
00105 
00106         And here we copy one file to another:
00107 
00108         @code
00109         // open a file for reading
00110         FileConduit from = new FileConduit ("test.txt");
00111 
00112         // open another for writing
00113         FileConduit to = new FileConduit ("copy.txt", FileStyle.WriteTruncate);
00114 
00115         // copy file
00116         to.copy (from);
00117         @endcode
00118         
00119         FileConduit can just as easily handle random IO. Here we see how
00120         a Reader and Writer are used to perform simple input and output:
00121 
00122         @code
00123         // open a file for reading
00124         FileConduit fc = new FileConduit ("random.bin", FileStyle.ReadWriteCreate);
00125 
00126         // construct (binary) reader & writer upon this conduit
00127         Reader r = new Reader(fc);
00128         Writer w = new Writer(fc);
00129 
00130         int x=10, y=20;
00131 
00132         // write some data
00133         w.put(x).put(y);
00134 
00135         // flush output since IO is buffered
00136         w.flush();
00137 
00138         // rewind to file start
00139         fc.seek (0);
00140 
00141         // read data back again, but swap destinations
00142         r.get(y).get(x);
00143 
00144         assert (y==10);
00145         assert (x==20);
00146 
00147         fc.close();
00148         @endcode
00149 
00150         See FilePath, FileProxy, and FileSystem for additional functionality
00151         related to file manipulation. Note that classes CompositeWriter and
00152         CompositeReader may be more appropriate for the writing & reading of
00153         structured data. Doxygen has a hard time with D version() statements, 
00154         so part of this class is documented within FileConduit::VersionWin32 
00155         instead.
00156         
00157 *******************************************************************************/
00158 
00159 class FileConduit : Conduit, ISeekable
00160 {
00161         // the file we're working with 
00162         private FilePath path;
00163 
00164         // expose conduit copy() method also (alias is truly non-intuitive ...)
00165         alias Conduit.copy copy;
00166 
00167         /***********************************************************************
00168         
00169                 Create a FileConduit with the provided path and style.
00170 
00171         ***********************************************************************/
00172 
00173         this (char[] name, FileStyle style = FileStyle.ReadExisting)
00174         {
00175                 this (new FilePath(name), style);
00176         }
00177 
00178         /***********************************************************************
00179         
00180                 Create a FileConduit from the provided proxy and style.
00181 
00182         ***********************************************************************/
00183 
00184         this (FileProxy proxy, FileStyle style = FileStyle.ReadExisting)
00185         {
00186                 this (proxy.getPath(), style);
00187         }
00188 
00189         /***********************************************************************
00190         
00191                 Create a FileConduit with the provided path and style.
00192 
00193         ***********************************************************************/
00194 
00195         this (FilePath path, FileStyle style = FileStyle.ReadExisting)
00196         {
00197                 // say we're seekable
00198                 super (style, true);
00199                 
00200                 // remember who we are
00201                 this.path = path;
00202 
00203                 // open the file
00204                 _open (style);
00205 
00206                 // bump lock count
00207                 acquire();
00208         }    
00209 
00210         /***********************************************************************
00211 
00212                 Create a FileConduit on the provided FileDevice. This is 
00213                 strictly for adapting existing devices such as Stdout and
00214                 friends.
00215         
00216         ***********************************************************************/
00217 
00218         this (FileDevice device)
00219         {
00220                 // say we're not seekable
00221                 super (device, false);
00222                 
00223                 // devices don't have a path (bug?)
00224                 this.path = null;
00225 
00226                 // open the file
00227                 _reopen (device);
00228 
00229                 // bump lock count
00230                 acquire();
00231         }    
00232 
00233         /***********************************************************************
00234         
00235                 Callback to close the file. This is invoked from the Resource
00236                 base-class when the resource is being closed.
00237 
00238         ***********************************************************************/
00239 
00240         protected override void closure ()
00241         {       
00242                 super.closure ();
00243                 _close ();
00244         }    
00245                    
00246         /***********************************************************************
00247         
00248                 Return the FilePath used by this file.
00249 
00250         ***********************************************************************/
00251 
00252         FilePath getPath ()
00253         {
00254                 return path;
00255         }               
00256 
00257         /***********************************************************************
00258         
00259                 Move the file position to the given offset (from the file 
00260                 start) and return the adjusted postion.
00261 
00262         ***********************************************************************/
00263 
00264         long seek (long offset)
00265         {
00266                 return seek (offset, SeekAnchor.Begin);
00267         }               
00268 
00269         /***********************************************************************
00270                 
00271                 Return the current file position.
00272                 
00273         ***********************************************************************/
00274 
00275         long getPosition ()
00276         {
00277                 return seek (0, SeekAnchor.Current);
00278         }               
00279 
00280 
00281         /***********************************************************************
00282 
00283                 Create a Buffer of the default FileConduit size, and
00284                 associate it with this conduit
00285 
00286         ***********************************************************************/
00287 
00288         override IBuffer createBuffer()
00289         {
00290                 const int DefaultFileBufferSize = 1024 * 16; 
00292                 IBuffer buffer = new Buffer (DefaultFileBufferSize);
00293 
00294                 buffer.setConduit (this);
00295                 return buffer;
00296         } 
00297 
00298         /***********************************************************************
00299         
00300                 Return the total length of this file.
00301 
00302         ***********************************************************************/
00303 
00304         long length ()
00305         {
00306                 long    pos,    
00307                         ret;
00308                         
00309                 pos = getPosition ();
00310                 ret = seek (0, SeekAnchor.End);
00311                 seek (pos);
00312                 return ret;
00313         }               
00314 
00315         /***********************************************************************
00316 
00317                 Transfer the content of another file to this one. Returns a
00318                 reference to this class on success, or throws an IOException 
00319                 upon failure.
00320         
00321         ***********************************************************************/
00322 
00323         FileConduit copy (FilePath source)
00324         {
00325                 auto FileConduit fc = new FileConduit (source, FileStyle.ReadExisting);
00326                 super.copy (fc);
00327                 return this;
00328         }               
00329 
00330         /***********************************************************************
00331 
00332                 Windows-specific code
00333         
00334         ***********************************************************************/
00335 
00336         version(Win32)
00337         {
00338                 private MappedBuffer    mapped;
00339                 private HANDLE          handle;
00340                 private bool            appending;
00341 
00342                 /***************************************************************
00343 
00344                         Throw an IOException noting the last error
00345 
00346                 ***************************************************************/
00347 
00348                 private void exception ()
00349                 {
00350                         throw new IOException (path.toString() ~ ": " ~
00351                                                System.error);
00352                 }
00353 
00354                 /***************************************************************
00355 
00356                         Gain access to the standard IO handles (console etc).
00357 
00358                 ***************************************************************/
00359 
00360                 private void _reopen (FileDevice device)
00361                 {
00362                         static const DWORD[] id = [-10, -11, -12];
00363                         handle = GetStdHandle (id[device.id]);
00364                 }
00365 
00366                 /***************************************************************
00367 
00368                         Open a file with the provided style.
00369 
00370                 ***************************************************************/
00371 
00372                 private void _open (FileStyle style)
00373                 {
00374                         DWORD   attr,
00375                                 share,
00376                                 access,
00377                                 create;
00378 
00379                         alias DWORD[] Flags;
00380 
00381                         static const Flags Access =  
00382                                         [
00383                                         0,                      // invalid
00384                                         GENERIC_READ,
00385                                         GENERIC_WRITE,
00386                                         GENERIC_READ | GENERIC_WRITE,
00387                                         ];
00388                                                 
00389                         static const Flags Create =  
00390                                         [
00391                                         OPEN_EXISTING,          // must exist
00392                                         CREATE_ALWAYS,          // create always
00393                                         TRUNCATE_EXISTING,      // must exist
00394                                         CREATE_ALWAYS,          // (for appending)
00395                                         ];
00396                                                 
00397                         static const Flags Share =   
00398                                         [
00399                                         FILE_SHARE_READ,
00400                                         FILE_SHARE_WRITE,
00401                                         FILE_SHARE_READ | FILE_SHARE_WRITE,
00402                                         ];
00403                                                 
00404                         static const Flags Attr =   
00405                                         [
00406                                         0,
00407                                         FILE_FLAG_RANDOM_ACCESS,
00408                                         FILE_FLAG_SEQUENTIAL_SCAN,
00409                                         0,
00410                                         FILE_FLAG_WRITE_THROUGH,
00411                                         ];
00412 
00413                         attr = Attr[style.cache];
00414                         share = Share[style.share];
00415                         create = Create[style.open];
00416                         access = Access[style.access];
00417 
00418                         handle = CreateFileA (path.toStringZ(), access, share, 
00419                                               null, create, 
00420                                               attr | FILE_ATTRIBUTE_NORMAL,
00421                                               cast(HANDLE) null);
00422 
00423                         if (handle == INVALID_HANDLE_VALUE)
00424                             exception ();
00425 
00426                         // move to end of file?
00427                         if (style.open == style.Open.Append)
00428                             appending = true;
00429                 }
00430                 
00431                 /***************************************************************
00432 
00433                         Close the underlying file
00434 
00435                 ***************************************************************/
00436 
00437                 private void _close ()
00438                 {
00439                         if (! CloseHandle (handle))
00440                               exception ();
00441                 }
00442 
00443                 /***************************************************************
00444 
00445                         Read a chunk of bytes from the file into the provided
00446                         array (typically that belonging to an IBuffer)
00447 
00448                 ***************************************************************/
00449 
00450                 protected override int reader (void[] dst)
00451                 {
00452                         DWORD read;
00453                         void *p = dst;
00454 
00455                         if (! ReadFile (handle, p, dst.length, &read, null))
00456                               exception ();
00457 
00458                         if (read == 0 && dst.length > 0)
00459                             return Eof;
00460                         return read;
00461                 }
00462 
00463                 /***************************************************************
00464 
00465                         Write a chunk of bytes to the file from the provided
00466                         array (typically that belonging to an IBuffer)
00467 
00468                 ***************************************************************/
00469 
00470                 protected override int writer (void[] src)
00471                 {
00472                         DWORD written;
00473 
00474                         // try to emulate the Unix O_APPEND mode
00475                         if (appending)
00476                             SetFilePointer (handle, 0, null, SeekAnchor.End);
00477 
00478                         if (! WriteFile (handle, src, src.length, &written, null))
00479                               exception ();
00480                         return written;
00481                 }
00482                 
00483                 /***************************************************************
00484 
00485                         Set the file size to be that of the current seek 
00486                         position. The file must be writable for this to
00487                         succeed.
00488 
00489                 ***************************************************************/
00490 
00491                 void truncate ()
00492                 {
00493                         // must have Generic_Write access
00494                         if (! SetEndOfFile (handle))
00495                               exception ();                            
00496                 }               
00497 
00498                 /***************************************************************
00499 
00500                         Set the file seek position to the specified offset
00501                         from the given anchor. 
00502 
00503                 ***************************************************************/
00504 
00505                 long seek (long offset, SeekAnchor anchor)
00506                 {
00507                         if (mapped)
00508                             return mapped.seek (offset, anchor);
00509 
00510                         LONG high = (offset >> 32);
00511                         long result = SetFilePointer (handle, cast(LONG) offset, 
00512                                                       &high, anchor);
00513 
00514                         if (result == -1 && 
00515                             GetLastError() != ERROR_SUCCESS)
00516                             exception ();
00517 
00518                         return result + (cast(long) high << 32);
00519                 }               
00520 
00521                 /***************************************************************
00522 
00523                         Create a memory-mapped buffer of the entire file
00524 
00525                 ***************************************************************/
00526 
00527                 MappedBuffer createMappedBuffer ()
00528                 {
00529                         mapped = new Win32MappedBuffer (this);
00530                         return mapped;
00531                 }    
00532 
00533                 /***************************************************************
00534 
00535                         Nested class to handle memory-mapped files. This 
00536                         should only be created via a utility method such
00537                         as FileConduit.createMappedBuffer()
00538 
00539                 ***************************************************************/
00540                 /+
00541                 #ifdef INCLUDE_ALL_FOR_DOXYGEN
00542                 +/
00543                 private class Win32MappedBuffer : MappedBuffer
00544                 {
00545                         private void*           base;
00546                         private HANDLE          mmFile;
00547                         private FileConduit     parent;
00548 
00549                         /*******************************************************
00550 
00551                                 Construct a MappedBuffer upon the given
00552                                 FileConduit. One should set the file size
00553                                 using seek() and truncate() to setup the
00554                                 available working space.
00555 
00556                         *******************************************************/
00557 
00558                         this (FileConduit parent)
00559                         {
00560                                 this.parent = parent;
00561 
00562                                 // can only do 32bit mapping on 32bit platform
00563                                 long size = parent.length();
00564                                 ConduitStyle style = parent.getStyle();
00565                                 
00566                                 DWORD flags = PAGE_READWRITE;
00567                                 if (style.access == ConduitStyle.Access.Read)
00568                                     flags = PAGE_READONLY;
00569 
00570                                 mmFile = CreateFileMappingA (parent.handle, null, flags, 0, 0, null);
00571                                 if (mmFile is null)
00572                                     parent.exception ();
00573 
00574                                 flags = FILE_MAP_WRITE;
00575                                 if (style.access == ConduitStyle.Access.Read)
00576                                     flags = FILE_MAP_READ;
00577 
00578                                 base = MapViewOfFile (mmFile, flags, 0, 0, 0);
00579                                 if (base is null)
00580                                     parent.exception ();
00581 
00582                                 void[] mem = base[0..size];
00583                                 setValidContent (mem);
00584                         }
00585 
00586                         /*******************************************************
00587 
00588                                 Ensure this is closed when GC'd
00589 
00590                         *******************************************************/
00591 
00592                         ~this ()
00593                         {
00594                                 close();        
00595                         }
00596 
00597                         /*******************************************************
00598 
00599                                 Close this mapped buffer
00600 
00601                         *******************************************************/
00602 
00603                         void close ()
00604                         {
00605                                 if (base)
00606                                     UnmapViewOfFile (base);
00607 
00608                                 if (mmFile)
00609                                     CloseHandle (mmFile);       
00610 
00611                                 mmFile = null;
00612                                 base = null;
00613                         }
00614 
00615                         /*******************************************************
00616                 
00617                                 Flush dirty content out to the drive. This
00618                                 fails with error 33 if the file content is
00619                                 virgin. Opening a file for ReadWriteExists
00620                                 followed by a flush() will cause this.
00621 
00622                         *******************************************************/
00623 
00624                         void flush ()
00625                         {
00626                                 // flush all dirty pages
00627                                 if (! FlushViewOfFile (base, 0))
00628                                       parent.exception();
00629                         }
00630                 }    
00631                 /+
00632                 #endif
00633                 +/
00634         }
00635 
00636 
00637         /***********************************************************************
00638 
00639                  Linux-specific code. Note that some methods are 32bit only
00640         
00641         ***********************************************************************/
00642 
00643         version (linux)
00644         {
00645                 private int handle = -1;
00646 
00647                 /***************************************************************
00648 
00649                         Throw an IOException noting the last error
00650 
00651                 ***************************************************************/
00652 
00653                 private void exception ()
00654                 {
00655                         throw new IOException (path.toString() ~ ": " ~
00656                                                System.error);
00657                 }
00658 
00659                 /***************************************************************
00660 
00661                         Gain access to the standard IO handles (console etc).
00662 
00663                 ***************************************************************/
00664 
00665                 private void _reopen (FileDevice device)
00666                 {
00667                         handle = device.id;
00668                 }
00669 
00670                 /***************************************************************
00671 
00672                         Open a file with the provided style.
00673 
00674                 ***************************************************************/
00675 
00676                 private void _open (FileStyle style)
00677                 {
00678                         int     share, 
00679                                 access;
00680 
00681                         alias int[] Flags;
00682 
00683                         static const Flags Access =  
00684                                         [
00685                                         0,              // invalid
00686                                         O_RDONLY,
00687                                         O_WRONLY,
00688                                         O_RDWR,
00689                                         ];
00690                                                 
00691                         static const Flags Create =  
00692                                         [
00693                                         0,              // open existing
00694                                         O_CREAT,        // create always
00695                                         O_TRUNC,        // must exist
00696                                         O_APPEND | O_CREAT, 
00697                                         ];
00698 
00699                         // this is not the same as Windows sharing,
00700                         // but it's perhaps a reasonable approximation
00701                         static const Flags Share =   
00702                                         [
00703                                         0640,           // read access
00704                                         0620,           // write access
00705                                         0660,           // read & write
00706                                         ];
00707                                                 
00708                         share = Share[style.share];
00709                         access = Access[style.access] | Create[style.open];
00710 
00711                         handle = std.c.linux.linux.open (path.toStringZ(), access, share);
00712                         if (handle == -1)
00713                             exception ();
00714                 }
00715 
00716                 /***************************************************************
00717 
00718                         Close the underlying file
00719 
00720                 ***************************************************************/
00721 
00722                 private void _close ()
00723                 {
00724                         if (std.c.linux.linux.close (handle) == -1)
00725                             exception ();
00726                 }
00727 
00728                 /***************************************************************
00729 
00730                         Read a chunk of bytes from the file into the provided
00731                         array (typically that belonging to an IBuffer)
00732 
00733                 ***************************************************************/
00734 
00735                 protected override int reader (void[] dst)
00736                 {
00737                         int read = std.c.linux.linux.read (handle, dst, dst.length);
00738                         if (read == -1)
00739                             exception ();
00740                         else
00741                            if (read == 0 && dst.length > 0)
00742                                return Eof;
00743                         return read;
00744                 }
00745 
00746                 /***************************************************************
00747 
00748                         Write a chunk of bytes to the file from the provided
00749                         array (typically that belonging to an IBuffer)
00750 
00751                 ***************************************************************/
00752 
00753                 protected override int writer (void[] src)
00754                 {
00755                         int written = std.c.linux.linux.write (handle, src, src.length);
00756                         if (written == -1)
00757                             exception ();
00758                         return written;
00759                 }
00760                 
00761                 /***************************************************************
00762 
00763                         32bit only ...
00764 
00765                         Set the file size to be that of the current seek 
00766                         position. The file must be writable for this to
00767                         succeed.
00768 
00769                         @todo not yet implemented
00770 
00771                 ***************************************************************/
00772 
00773                 void truncate ()
00774                 {
00775                         // set filesize to be current seek-position
00776 //                        if (ftruncate (handle, position()) == -1)
00777 //                            exception ();
00778 
00779                         assert(0);
00780                 }               
00781 
00782                 /***************************************************************
00783 
00784                         32bit only ...
00785 
00786                         Set the file seek position to the specified offset
00787                         from the given anchor. 
00788 
00789                 ***************************************************************/
00790 
00791                 long seek (long offset, SeekAnchor anchor)
00792                 {
00793                         int result = std.c.linux.linux.lseek (handle, offset, anchor);
00794                         if (result == -1)
00795                             exception ();
00796                         return result;
00797                 }               
00798 
00799                 /***************************************************************
00800 
00801                         Create a memory-mapped buffer of the entire file, and
00802                         associate it with this conduit
00803 
00804                         @todo not yet implemented
00805                 ***************************************************************/
00806 
00807                 MappedBuffer createMappedBuffer ()
00808                 {
00809                         assert(0);
00810                         return null;
00811                 }    
00812 
00813         }
00814 }
00815 
00816 
00817 
00818 /*******************************************************************************
00819 
00820         Class used to wrap an existing file-oriented handle, such as Stdout
00821         and its cohorts.
00822 
00823 *******************************************************************************/
00824 
00825 class FileDevice : ConduitStyle
00826 {
00827         private int id;
00828 
00829         this (int id, Access access)
00830         {
00831                 super (access);
00832                 this.id = id;
00833         }
00834 }
00835 
00836 

Generated on Sun Oct 24 22:31:14 2004 for Mango by doxygen 1.3.6