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

Generated on Tue Jan 25 21:18:21 2005 for Mango by doxygen 1.3.6