view tango/tango/io/FileConduit.d @ 137:ce7b81fb957f trunk

[svn r141] fixed more problems with classinfo moved more IR state out of the AST classes
author lindquist
date Fri, 18 Jan 2008 16:42:16 +0100
parents 1700239cab2e
children
line wrap: on
line source

/*******************************************************************************

        copyright:      Copyright (c) 2004 Kris Bell. All rights reserved

        license:        BSD style: $(LICENSE)

        version:        Initial release: March 2004      
                        Outback release: December 2006
                        
        author:         $(UL Kris)
                        $(UL John Reimer)
                        $(UL Anders F Bjorklund (Darwin patches))
                        $(UL Chris Sauls (Win95 file support))

*******************************************************************************/

module tango.io.FileConduit;

private import  tango.sys.Common;

public  import  tango.io.FilePath;

private import  tango.io.DeviceConduit;

private import  Utf = tango.text.convert.Utf;

/*******************************************************************************

        Other O/S functions

*******************************************************************************/

version (Win32)
         private extern (Windows) BOOL SetEndOfFile (HANDLE);
     else
        private extern (C) int ftruncate (int, int);


/*******************************************************************************

        Implements a means of reading and writing a generic file. Conduits
        are the primary means of accessing external data and FileConduit
        extends the basic pattern by providing file-specific methods to
        set the file size, seek to a specific file position and so on. 
        
        Serial input and output is straightforward. In this example we
        copy a file directly to the console:
        ---
        // open a file for reading
        auto from = new FileConduit ("test.txt");

        // stream directly to console
        Stdout.copy (from);
        ---

        And here we copy one file to another:
        ---
        // open another for writing
        auto to = new FileConduit ("copy.txt", FileConduit.WriteCreate);

        // copy file
        to.output.copy (new FileConduit("test.txt"));
        ---
        
        To load a file directly into memory one might do this:
        ---
        // open file for reading
        auto fc = new FileConduit ("test.txt");

        // create an array to house the entire file
        auto content = new char[fc.length];

        // read the file content. Return value is the number of bytes read
        auto bytesRead = fc.input.read (content);
        ---

        Conversely, one may write directly to a FileConduit, like so:
        ---
        // open file for writing
        auto to = new FileConduit ("text.txt", FileConduit.WriteCreate);

        // write an array of content to it
        auto bytesWritten = to.output.write (content);
        ---

        FileConduit can just as easily handle random IO. Here we use seek()
        to relocate the file pointer and, for variation, apply a protocol to
        perform simple input and output:
        ---
        // open a file for reading
        auto fc = new FileConduit ("random.bin", FileConduit.ReadWriteCreate);

        // construct (binary) reader & writer upon this conduit
        auto read = new Reader (fc);
        auto write = new Writer (fc);

        int x=10, y=20;

        // write some data, and flush output since protocol IO is buffered
        write (x) (y) ();

        // rewind to file start
        fc.seek (0);

        // read data back again
        read (x) (y);

        fc.close();
        ---

        See File, FilePath, FileConst, FileScan, and FileSystem for 
        additional functionality related to file manipulation. 

        Compile with -version=Win32SansUnicode to enable Win95 & Win32s file 
        support.
        
*******************************************************************************/

class FileConduit : DeviceConduit, DeviceConduit.Seek
{
        /***********************************************************************
        
                Fits into 32 bits ...

        ***********************************************************************/

        struct Style
        {
                align (1):

                Access          access;                 /// access rights
                Open            open;                   /// how to open
                Share           share;                  /// how to share
                Cache           cache;                  /// how to cache
        }

        /***********************************************************************

        ***********************************************************************/

        enum Access : ubyte     {
                                Read      = 0x01,       /// is readable
                                Write     = 0x02,       /// is writable
                                ReadWrite = 0x03,       /// both
                                }

        /***********************************************************************
        
        ***********************************************************************/

        enum Open : ubyte       {
                                Exists=0,               /// must exist
                                Create,                 /// create or truncate
                                Sedate,                 /// create if necessary
                                Append,                 /// create if necessary
                                };

        /***********************************************************************
        
        ***********************************************************************/

        enum Share : ubyte      {
                                None=0,                 /// no sharing
                                Read,                   /// shared reading
                                ReadWrite,              /// open for anything
                                };

        /***********************************************************************
        
        ***********************************************************************/

        enum Cache : ubyte      {
                                None      = 0x00,       /// don't optimize
                                Random    = 0x01,       /// optimize for random
                                Stream    = 0x02,       /// optimize for stream
                                WriteThru = 0x04,       /// backing-cache flag
                                };

        /***********************************************************************

            Read an existing file
        
        ***********************************************************************/

        const Style ReadExisting = {Access.Read, Open.Exists};

        /***********************************************************************
        
                Write on an existing file. Do not create

        ***********************************************************************/

        const Style WriteExisting = {Access.Write, Open.Exists};

        /***********************************************************************
        
                Write on a clean file. Create if necessary

        ***********************************************************************/

        const Style WriteCreate = {Access.Write, Open.Create};

        /***********************************************************************
        
                Write at the end of the file

        ***********************************************************************/

        deprecated const Style WriteAppending = {Access.Write, Open.Append};

        /***********************************************************************
        
                Read and write an existing file

        ***********************************************************************/

        const Style ReadWriteExisting = {Access.ReadWrite, Open.Exists}; 

        /***********************************************************************
        
                Read & write on a clean file. Create if necessary

        ***********************************************************************/

        const Style ReadWriteCreate = {Access.ReadWrite, Open.Create}; 

        /***********************************************************************
        
                Read and Write. Use existing file if present

        ***********************************************************************/

        const Style ReadWriteOpen = {Access.ReadWrite, Open.Sedate}; 




        // the file we're working with 
        private PathView path_;

        // the style we're opened with
        private Style    style_;

        /***********************************************************************
        
                Create a FileConduit with the provided path and style.

        ***********************************************************************/

        this (char[] name, Style style = ReadExisting)
        {
                this (new FilePath(name), style);
        }

        /***********************************************************************
        
                Create a FileConduit with the provided path and style.

        ***********************************************************************/

        this (PathView path, Style style = ReadExisting)
        {
                // remember who we are
                path_ = path;

                // open the file
                open (this.style_ = style);
        }    

        /***********************************************************************
        
                Return the PathView used by this file.

        ***********************************************************************/

        PathView path ()
        {
                return path_;
        }               

        /***********************************************************************
        
                Return the Style used for this file.

        ***********************************************************************/

        Style style ()
        {
                return style_;
        }               

        /***********************************************************************
        
                Return the name of the FilePath used by this file.

        ***********************************************************************/

        override char[] toString ()
        {
                return path_.toString;
        }               

        /***********************************************************************
                
                Return the current file position.
                
        ***********************************************************************/

        long position ()
        {
                return seek (0, Seek.Anchor.Current);
        }               

        /***********************************************************************
        
                Return the total length of this file.

        ***********************************************************************/

        long length ()
        {
                long    pos,    
                        ret;
                        
                pos = position ();
                ret = seek (0, Seek.Anchor.End);
                seek (pos);
                return ret;
        }               


        /***********************************************************************

                Windows-specific code
        
        ***********************************************************************/

        version(Win32)
        {
                private bool appending;

                /***************************************************************

                        Open a file with the provided style.

                ***************************************************************/

                protected void open (Style style)
                {
                        DWORD   attr,
                                share,
                                access,
                                create;

                        alias DWORD[] Flags;

                        static const Flags Access =  
                                        [
                                        0,                      // invalid
                                        GENERIC_READ,
                                        GENERIC_WRITE,
                                        GENERIC_READ | GENERIC_WRITE,
                                        ];
                                                
                        static const Flags Create =  
                                        [
                                        OPEN_EXISTING,          // must exist
                                        CREATE_ALWAYS,          // truncate always
                                        OPEN_ALWAYS,            // create if needed
                                        OPEN_ALWAYS,            // (for appending)
                                        ];
                                                
                        static const Flags Share =   
                                        [
                                        0,
                                        FILE_SHARE_READ,
                                        FILE_SHARE_READ | FILE_SHARE_WRITE,
                                        ];
                                                
                        static const Flags Attr =   
                                        [
                                        0,
                                        FILE_FLAG_RANDOM_ACCESS,
                                        FILE_FLAG_SEQUENTIAL_SCAN,
                                        0,
                                        FILE_FLAG_WRITE_THROUGH,
                                        ];

                        attr   = Attr[style.cache];
                        share  = Share[style.share];
                        create = Create[style.open];
                        access = Access[style.access];

                        version (Win32SansUnicode)
                                 handle = CreateFileA (path.cString.ptr, access, share, 
                                                       null, create, 
                                                       attr | FILE_ATTRIBUTE_NORMAL,
                                                       cast(HANDLE) null);
                             else
                                {
                                wchar[256] tmp = void;
                                auto name = Utf.toString16 (path.cString, tmp);
                                handle = CreateFileW (name.ptr, access, share,
                                                      null, create, 
                                                      attr | FILE_ATTRIBUTE_NORMAL,
                                                      cast(HANDLE) null);
                                }

                        if (handle is INVALID_HANDLE_VALUE)
                            error ();

                        // move to end of file?
                        if (style.open is Open.Append)
                            appending = true;
                }
                
                /***************************************************************

                        Write a chunk of bytes to the file from the provided
                        array (typically that belonging to an IBuffer)

                ***************************************************************/

                override uint write (void[] src)
                {
                        DWORD written;

                        // try to emulate the Unix O_APPEND mode
                        if (appending)
                            SetFilePointer (handle, 0, null, Seek.Anchor.End);
                        
                        return super.write (src);
                }
            
                /***************************************************************

                        Ensures that data is flushed immediately to disk

                ***************************************************************/
/+
                override void commit ()
                {
                        if (style_.access & Access.Write)
                            if (! FlushFileBuffers (handle))
                                  error ();
                }
+/
                /***************************************************************

                        Set the file size to be that of the current seek 
                        position. The file must be writable for this to
                        succeed.

                ***************************************************************/

                void truncate ()
                {
                        // must have Generic_Write access
                        if (! SetEndOfFile (handle))
                              error ();                            
                }               

                /***************************************************************

                        Set the file seek position to the specified offset
                        from the given anchor. 

                ***************************************************************/

                long seek (long offset, Seek.Anchor anchor = Seek.Anchor.Begin)
                {
                        LONG high = cast(LONG) (offset >> 32);
                        long result = SetFilePointer (handle, cast(LONG) offset, 
                                                      &high, anchor);

                        if (result is -1 && 
                            GetLastError() != ERROR_SUCCESS)
                            error ();

                        return result + (cast(long) high << 32);
                }               
        }


        /***********************************************************************

                 Unix-specific code. Note that some methods are 32bit only
        
        ***********************************************************************/

        version (Posix)
        {
                /***************************************************************

                        Open a file with the provided style.

                        Note that files default to no-sharing. That is, 
                        they are locked exclusively to the host process 
                        unless otherwise stipulated. We do this in order
                        to expose the same default behaviour as Win32

                        NO FILE LOCKING FOR BORKED POSIX

                ***************************************************************/

                protected void open (Style style)
                {
                        alias int[] Flags;

                        const O_LARGEFILE = 0x8000;

                        static const Flags Access =  
                                        [
                                        0,                      // invalid
                                        O_RDONLY,
                                        O_WRONLY,
                                        O_RDWR,
                                        ];
                                                
                        static const Flags Create =  
                                        [
                                        0,                      // open existing
                                        O_CREAT | O_TRUNC,      // truncate always
                                        O_CREAT,                // create if needed
                                        O_APPEND | O_CREAT,     // append
                                        ];

                        static const short[] Locks =   
                                        [
                                        F_WRLCK,                // no sharing
                                        F_RDLCK,                // shared read
                                        ];
                                                
                        auto mode = Access[style.access] | Create[style.open];

                        // always open as a large file
                        handle = posix.open (path.cString.ptr, mode | O_LARGEFILE, 0666);
                        if (handle is -1)
                            error ();
                }

                /***************************************************************

                        Ensures that data is flushed immediately to disk

                ***************************************************************/
/+
                override void commit ()
                {
                        // no Posix API for this :(
                }
+/
                /***************************************************************

                        Set the file size to be that of the current seek 
                        position. The file must be writable for this to
                        succeed.

                ***************************************************************/

                void truncate ()
                {
                        // set filesize to be current seek-position
                        if (ftruncate (handle, position) is -1)
                            error ();
                }               

                /***************************************************************

                        Set the file seek position to the specified offset
                        from the given anchor. 

                ***************************************************************/

                long seek (long offset, Seek.Anchor anchor = Seek.Anchor.Begin)
                {
                        long result = posix.lseek (handle, offset, anchor);
                        if (result is -1)
                            error ();
                        return result;
                }               
        }
}