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         private extern (C) int ftruncate (int, int);
00063         }
00064 
00065 /*******************************************************************************
00066 
00067         Bring in native Windows functions
00068 
00069 *******************************************************************************/
00070 
00071 version (Win32)
00072 {
00073         private import std.c.windows.windows;  
00074              
00075         private extern (Windows) 
00076         {
00077         HANDLE GetStdHandle (DWORD);
00078         BOOL   SetEndOfFile (HANDLE);
00079 
00080         BOOL   UnmapViewOfFile    (LPCVOID);
00081         BOOL   FlushViewOfFile    (LPCVOID, DWORD);
00082         LPVOID MapViewOfFile      (HANDLE, DWORD, DWORD, DWORD, DWORD);
00083         HANDLE CreateFileMappingA (HANDLE, LPSECURITY_ATTRIBUTES, DWORD, DWORD, DWORD, LPCTSTR);
00084         }
00085 }
00086 
00087 
00088 /*******************************************************************************
00089 
00090         Implements a means of reading and writing a generic file. Conduits
00091         are the primary means of accessing external data, and are usually 
00092         routed through a Buffer. File conduit extends the generic conduit
00093         by providing file-specific methods to set the file size, seek to a
00094         specific file position, and so on. Also provided is a factory for
00095         create a memory-mapped Buffer upon a file.     
00096         
00097         Serial input and output is straightforward. In this example we
00098         copy a file directly to the console:
00099 
00100         @code
00101         // open a file for reading
00102         FileConduit fc = new FileConduit ("test.txt");
00103 
00104         // stream directly to console
00105         Stdio.stdout.copy (fc);
00106         @endcode
00107 
00108         And here we copy one file to another:
00109 
00110         @code
00111         // open a file for reading
00112         FileConduit from = new FileConduit ("test.txt");
00113 
00114         // open another for writing
00115         FileConduit to = new FileConduit ("copy.txt", FileStyle.WriteTruncate);
00116 
00117         // copy file
00118         to.copy (from);
00119         @endcode
00120         
00121         FileConduit can just as easily handle random IO. Here we see how
00122         a Reader and Writer are used to perform simple input and output:
00123 
00124         @code
00125         // open a file for reading
00126         FileConduit fc = new FileConduit ("random.bin", FileStyle.ReadWriteCreate);
00127 
00128         // construct (binary) reader & writer upon this conduit
00129         Reader r = new Reader(fc);
00130         Writer w = new Writer(fc);
00131 
00132         int x=10, y=20;
00133 
00134         // write some data
00135         w.put(x).put(y);
00136 
00137         // flush output since IO is buffered
00138         w.flush();
00139 
00140         // rewind to file start
00141         fc.seek (0);
00142 
00143         // read data back again, but swap destinations
00144         r.get(y).get(x);
00145 
00146         assert (y==10);
00147         assert (x==20);
00148 
00149         fc.close();
00150         @endcode
00151 
00152         See FilePath, FileProxy, and FileSystem for additional functionality
00153         related to file manipulation. Note that classes CompositeWriter and
00154         CompositeReader may be more appropriate for the writing & reading of
00155         structured data. Doxygen has a hard time with D version() statements, 
00156         so part of this class is documented within FileConduit::VersionWin32 
00157         instead.
00158         
00159 *******************************************************************************/
00160 
00161 class FileConduit : Conduit, ISeekable
00162 {
00163         // the file we're working with 
00164         private FilePath path;
00165 
00166         // expose conduit copy() method also (alias is truly non-intuitive ...)
00167         alias Conduit.copy copy;
00168 
00169         /***********************************************************************
00170         
00171                 Create a FileConduit with the provided path and style.
00172 
00173         ***********************************************************************/
00174 
00175         this (char[] name, FileStyle style = FileStyle.ReadExisting)
00176         {
00177                 this (new FilePath(name), style);
00178         }
00179 
00180         /***********************************************************************
00181         
00182                 Create a FileConduit from the provided proxy and style.
00183 
00184         ***********************************************************************/
00185 
00186         this (FileProxy proxy, FileStyle style = FileStyle.ReadExisting)
00187         {
00188                 this (proxy.getPath(), style);
00189         }
00190 
00191         /***********************************************************************
00192         
00193                 Create a FileConduit with the provided path and style.
00194 
00195         ***********************************************************************/
00196 
00197         this (FilePath path, FileStyle style = FileStyle.ReadExisting)
00198         {
00199                 // say we're seekable
00200                 super (style, true);
00201                 
00202                 // remember who we are
00203                 this.path = path;
00204 
00205                 // open the file
00206                 _open (style);
00207 
00208                 // bump lock count
00209                 acquire();
00210         }    
00211 
00212         /***********************************************************************
00213 
00214                 Create a FileConduit on the provided FileDevice. This is 
00215                 strictly for adapting existing devices such as Stdout and
00216                 friends.
00217         
00218         ***********************************************************************/
00219 
00220         package this (FileDevice device)
00221         {
00222                 // say we're not seekable
00223                 super (device, false);
00224                 
00225                 // devices don't have a path (bug?)
00226                 this.path = null;
00227 
00228                 // open the file
00229                 _reopen (device);
00230 
00231                 // bump lock count
00232                 acquire();
00233         }    
00234 
00235         /***********************************************************************
00236         
00237                 Callback to close the file. This is invoked from the Resource
00238                 base-class when the resource is being closed.
00239 
00240         ***********************************************************************/
00241 
00242         protected override void closure ()
00243         {       
00244                 super.closure ();
00245                 _close ();
00246         }    
00247                    
00248         /***********************************************************************
00249         
00250                 Return the FilePath used by this file.
00251 
00252         ***********************************************************************/
00253 
00254         FilePath getPath ()
00255         {
00256                 return path;
00257         }               
00258 
00259         /***********************************************************************
00260         
00261                 Move the file position to the given offset (from the file 
00262                 start) and return the adjusted postion.
00263 
00264         ***********************************************************************/
00265 
00266         long seek (long offset)
00267         {
00268                 return seek (offset, SeekAnchor.Begin);
00269         }               
00270 
00271         /***********************************************************************
00272                 
00273                 Return the current file position.
00274                 
00275         ***********************************************************************/
00276 
00277         long getPosition ()
00278         {
00279                 return seek (0, SeekAnchor.Current);
00280         }               
00281 
00282 
00283         /***********************************************************************
00284 
00285                 Create a Buffer of the default FileConduit size, and
00286                 associate it with this conduit
00287 
00288         ***********************************************************************/
00289 
00290         override IBuffer createBuffer()
00291         {
00292                 const int DefaultFileBufferSize = 1024 * 16; 
00294                 IBuffer buffer = new Buffer (DefaultFileBufferSize);
00295 
00296                 buffer.setConduit (this);
00297                 return buffer;
00298         } 
00299 
00300         /***********************************************************************
00301         
00302                 Return the total length of this file.
00303 
00304         ***********************************************************************/
00305 
00306         long length ()
00307         {
00308                 long    pos,    
00309                         ret;
00310                         
00311                 pos = getPosition ();
00312                 ret = seek (0, SeekAnchor.End);
00313                 seek (pos);
00314                 return ret;
00315         }               
00316 
00317         /***********************************************************************
00318 
00319                 Transfer the content of another file to this one. Returns a
00320                 reference to this class on success, or throws an IOException 
00321                 upon failure.
00322         
00323         ***********************************************************************/
00324 
00325         FileConduit copy (FilePath source)
00326         {
00327                 auto FileConduit fc = new FileConduit (source, FileStyle.ReadExisting);
00328                 super.copy (fc);
00329                 return this;
00330         }               
00331 
00332         /***********************************************************************
00333 
00334                 Windows-specific code
00335         
00336         ***********************************************************************/
00337 
00338         version(Win32)
00339         {
00340                 private MappedBuffer    mapped;
00341                 private HANDLE          handle;
00342                 private bool            appending;
00343 
00344                 /***************************************************************
00345 
00346                         Throw an IOException noting the last error
00347 
00348                 ***************************************************************/
00349 
00350                 private void exception ()
00351                 {
00352                         throw new IOException (path.toString() ~ ": " ~
00353                                                System.error);
00354                 }
00355 
00356                 /***************************************************************
00357 
00358                         Gain access to the standard IO handles (console etc).
00359 
00360                 ***************************************************************/
00361 
00362                 private void _reopen (FileDevice device)
00363                 {
00364                         static const DWORD[] id = [-10, -11, -12];
00365                         handle = GetStdHandle (id[device.id]);
00366                 }
00367 
00368                 /***************************************************************
00369 
00370                         Open a file with the provided style.
00371 
00372                 ***************************************************************/
00373 
00374                 private void _open (FileStyle 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.access];
00419 
00420                         handle = CreateFileW (path.toUtf16, access, share, 
00421                                               null, create, 
00422                                               attr | FILE_ATTRIBUTE_NORMAL,
00423                                               cast(HANDLE) null);
00424 
00425                         if (handle == INVALID_HANDLE_VALUE)
00426                             exception ();
00427 
00428                         // move to end of file?
00429                         if (style.open == style.Open.Append)
00430                             appending = true;
00431                 }
00432                 
00433                 /***************************************************************
00434 
00435                         Close the underlying file
00436 
00437                 ***************************************************************/
00438 
00439                 private void _close ()
00440                 {
00441                         if (! CloseHandle (handle))
00442                               exception ();
00443                 }
00444 
00445                 /***************************************************************
00446 
00447                         Read a chunk of bytes from the file into the provided
00448                         array (typically that belonging to an IBuffer)
00449 
00450                 ***************************************************************/
00451 
00452                 protected override int reader (void[] dst)
00453                 {
00454                         DWORD read;
00455                         void *p = dst;
00456 
00457                         if (! ReadFile (handle, p, dst.length, &read, null))
00458                               exception ();
00459 
00460                         if (read == 0 && dst.length > 0)
00461                             return Eof;
00462                         return read;
00463                 }
00464 
00465                 /***************************************************************
00466 
00467                         Write a chunk of bytes to the file from the provided
00468                         array (typically that belonging to an IBuffer)
00469 
00470                 ***************************************************************/
00471 
00472                 protected override int writer (void[] src)
00473                 {
00474                         DWORD written;
00475 
00476                         // try to emulate the Unix O_APPEND mode
00477                         if (appending)
00478                             SetFilePointer (handle, 0, null, SeekAnchor.End);
00479 
00480                         if (! WriteFile (handle, src, src.length, &written, null))
00481                               exception ();
00482                         return written;
00483                 }
00484                 
00485                 /***************************************************************
00486 
00487                         Set the file size to be that of the current seek 
00488                         position. The file must be writable for this to
00489                         succeed.
00490 
00491                 ***************************************************************/
00492 
00493                 void truncate ()
00494                 {
00495                         // must have Generic_Write access
00496                         if (! SetEndOfFile (handle))
00497                               exception ();                            
00498                 }               
00499 
00500                 /***************************************************************
00501 
00502                         Set the file seek position to the specified offset
00503                         from the given anchor. 
00504 
00505                 ***************************************************************/
00506 
00507                 long seek (long offset, SeekAnchor anchor)
00508                 {
00509                         if (mapped)
00510                             return mapped.seek (offset, anchor);
00511 
00512                         LONG high = (offset >> 32);
00513                         long result = SetFilePointer (handle, cast(LONG) offset, 
00514                                                       &high, anchor);
00515 
00516                         if (result == -1 && 
00517                             GetLastError() != ERROR_SUCCESS)
00518                             exception ();
00519 
00520                         return result + (cast(long) high << 32);
00521                 }               
00522 
00523                 /***************************************************************
00524 
00525                         Create a memory-mapped buffer of the entire file
00526 
00527                 ***************************************************************/
00528 
00529                 MappedBuffer createMappedBuffer ()
00530                 {
00531                         mapped = new Win32MappedBuffer (this);
00532                         return mapped;
00533                 }    
00534 
00535                 /***************************************************************
00536 
00537                         Nested class to handle memory-mapped files. This 
00538                         should only be created via a utility method such
00539                         as FileConduit.createMappedBuffer()
00540 
00541                 ***************************************************************/
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 
00633 
00634         /***********************************************************************
00635 
00636                  Linux-specific code. Note that some methods are 32bit only
00637         
00638         ***********************************************************************/
00639 
00640         version (linux)
00641         {
00642                 private int handle = -1;
00643 
00644                 /***************************************************************
00645 
00646                         Throw an IOException noting the last error
00647 
00648                 ***************************************************************/
00649 
00650                 private void exception ()
00651                 {
00652                         throw new IOException (path.toString() ~ ": " ~
00653                                                System.error);
00654                 }
00655 
00656                 /***************************************************************
00657 
00658                         Gain access to the standard IO handles (console etc).
00659 
00660                 ***************************************************************/
00661 
00662                 private void _reopen (FileDevice device)
00663                 {
00664                         handle = device.id;
00665                 }
00666 
00667                 /***************************************************************
00668 
00669                         Open a file with the provided style.
00670 
00671                 ***************************************************************/
00672 
00673                 private void _open (FileStyle style)
00674                 {
00675                         int     share, 
00676                                 access;
00677 
00678                         alias int[] Flags;
00679 
00680                         static const Flags Access =  
00681                                         [
00682                                         0,              // invalid
00683                                         O_RDONLY,
00684                                         O_WRONLY,
00685                                         O_RDWR,
00686                                         ];
00687                                                 
00688                         static const Flags Create =  
00689                                         [
00690                                         0,              // open existing
00691                                         O_CREAT,        // create always
00692                                         O_TRUNC,        // must exist
00693                                         O_APPEND | O_CREAT, 
00694                                         ];
00695 
00696                         // this is not the same as Windows sharing,
00697                         // but it's perhaps a reasonable approximation
00698                         static const Flags Share =   
00699                                         [
00700                                         0640,           // read access
00701                                         0620,           // write access
00702                                         0660,           // read & write
00703                                         ];
00704                                                 
00705                         share = Share[style.share];
00706                         access = Access[style.access] | Create[style.open];
00707 
00708                         handle = std.c.linux.linux.open (path.toUtf8, access, share);
00709                         if (handle == -1)
00710                             exception ();
00711                 }
00712 
00713                 /***************************************************************
00714 
00715                         Close the underlying file
00716 
00717                 ***************************************************************/
00718 
00719                 private void _close ()
00720                 {
00721                         if (std.c.linux.linux.close (handle) == -1)
00722                             exception ();
00723                 }
00724 
00725                 /***************************************************************
00726 
00727                         Read a chunk of bytes from the file into the provided
00728                         array (typically that belonging to an IBuffer)
00729 
00730                 ***************************************************************/
00731 
00732                 protected override int reader (void[] dst)
00733                 {
00734                         int read = std.c.linux.linux.read (handle, dst, dst.length);
00735                         if (read == -1)
00736                             exception ();
00737                         else
00738                            if (read == 0 && dst.length > 0)
00739                                return Eof;
00740                         return read;
00741                 }
00742 
00743                 /***************************************************************
00744 
00745                         Write a chunk of bytes to the file from the provided
00746                         array (typically that belonging to an IBuffer)
00747 
00748                 ***************************************************************/
00749 
00750                 protected override int writer (void[] src)
00751                 {
00752                         int written = std.c.linux.linux.write (handle, src, src.length);
00753                         if (written == -1)
00754                             exception ();
00755                         return written;
00756                 }
00757                 
00758                 /***************************************************************
00759 
00760                         32bit only ...
00761 
00762                         Set the file size to be that of the current seek 
00763                         position. The file must be writable for this to
00764                         succeed.
00765 
00766                 ***************************************************************/
00767 
00768                 void truncate ()
00769                 {
00770                         // set filesize to be current seek-position
00771                         if (ftruncate (handle, getPosition()) == -1)
00772                             exception ();
00773                 }               
00774 
00775                 /***************************************************************
00776 
00777                         32bit only ...
00778 
00779                         Set the file seek position to the specified offset
00780                         from the given anchor. 
00781 
00782                 ***************************************************************/
00783 
00784                 long seek (long offset, SeekAnchor anchor)
00785                 {
00786                         int result = std.c.linux.linux.lseek (handle, offset, anchor);
00787                         if (result == -1)
00788                             exception ();
00789                         return result;
00790                 }               
00791 
00792                 /***************************************************************
00793 
00794                         Create a memory-mapped buffer of the entire file, and
00795                         associate it with this conduit
00796 
00797                         @todo not yet implemented
00798                 ***************************************************************/
00799 
00800                 MappedBuffer createMappedBuffer ()
00801                 {
00802                         assert(0);
00803                         return null;
00804                 }    
00805 
00806         }
00807 }
00808 
00809 
00810 
00811 /*******************************************************************************
00812 
00813         Class used to wrap an existing file-oriented handle, such as Stdout
00814         and its cohorts.
00815 
00816 *******************************************************************************/
00817 
00818 class FileDevice : ConduitStyle
00819 {
00820         private int id;
00821 
00822         this (int id, Access access)
00823         {
00824                 super (access);
00825                 this.id = id;
00826         }
00827 }
00828 
00829 

Generated on Sun Nov 7 19:06:50 2004 for Mango by doxygen 1.3.6