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

Generated on Sun Mar 6 00:30:56 2005 for Mango by doxygen 1.3.6