InterleavedZipPartStream.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Base / MS / Internal / IO / Packaging / InterleavedZipPartStream.cs / 1 / InterleavedZipPartStream.cs

                            //------------------------------------------------------------------------------ 
//  Microsoft Avalon
//  Copyright (c) Microsoft Corporation, 2005
//
//  File:           InterleavedZipPartStream.cs 
//
//  Description:    The class InterleavedZipPartStream is used to wrap one or more Zip 
//                  part streams for an interleaved part. It hides the interleaving 
//                  from its callers by offering the abstraction of a continuous stream
//                  across pieces. 
//
//  History:        05/15/05 - johnlarc - initial implementation
//-----------------------------------------------------------------------------
 
using System;
using System.Diagnostics; 
using System.IO; 
using System.IO.Packaging;                  // For ZipPackagePart, etc.
using MS.Internal.IO.Zip;                   // For ZipFileInfo. 
using System.Windows;                       // for ExceptionStringTable
using System.Collections.Generic;           // For List<>
using MS.Internal;                          // for Invariant
 
namespace MS.Internal.IO.Packaging
{ 
    ///  
 	/// The class InterleavedZipPartStream is used to wrap one or more Zip part streams
    /// for an interleaved part. It hides the interleaving from its callers by offering 
    /// the abstraction of a continuous stream across pieces.
    /// 
    /// 
    /// This class is defined for the benefit of ZipPackage, ZipPackagePart and 
    /// InternalRelationshipCollection.
    /// Although it is quite specialized, it would hardly make sense to nest its definition in any 
    /// of these clases. 
    /// 
    internal partial class InterleavedZipPartStream : Stream 
    {
        #region Constructors

        //----------------------------------------------------- 
        //
        //  Constructors 
        // 
        //-----------------------------------------------------
        ///  
        /// Build a System.IO.Stream on a part that possibly consists of multiple files
        /// An InterleavedZipPartStream gets created by ZipPackagePart.GetStreamCore when the part
        /// is interleaved. It wraps one or more Zip streams (one per piece).
        /// (pieces). 
        /// 
        /// Mode (create, etc.) in which piece streams should be opened 
        /// Access (read, write, etc.) with which piece streams should be opened 
        /// 
        /// The part to build a stream on. It contains all ZipFileInfo descriptors for the part's pieces 
        /// (see ZipPackage.GetPartsCore).
        /// 
        internal InterleavedZipPartStream(ZipPackagePart owningPart, FileMode mode, FileAccess access)
            : this(PackUriHelper.GetStringForPartUri(owningPart.Uri), 
                owningPart.PieceDescriptors,
                mode, access) 
        { 
        }
 
        /// 
        /// This constructor is provided to be able to interleave other files than just parts,
        /// notably the contents type file.
        ///  
        internal InterleavedZipPartStream(string partName, List sortedPieceInfoList,
            FileMode mode, FileAccess access) 
        { 
            // The PieceDirectory mediates access to pieces.
            // It maps offsets to piece numbers and piece numbers to streams and start offsets. 
            // Mode and access are entirely managed by the underlying streams, assumed to be seekable.
            _dir = new PieceDirectory(sortedPieceInfoList, mode, access);

            // GetCurrentPieceNumber is operational from the beginning. 
            Invariant.Assert(_dir.GetStartOffset(GetCurrentPieceNumber()) == 0);
        } 
 
        #endregion Constructors
 
        //------------------------------------------------------
        //
        //  Public Methods
        // 
        //-----------------------------------------------------
 
        ///  
        /// Return the bytes requested.
        ///  
        /// Destination buffer.
        /// 
        /// The zero-based byte offset in buffer at which to begin storing the data read
        /// from the current stream. 
        /// 
        /// How many bytes requested. 
        /// How many bytes were written into buffer. 
        public override int Read(byte[] buffer, int offset, int count)
        { 
            CheckClosed();

            // Check arguments.
            PackagingUtilities.VerifyStreamReadArgs(this, buffer, offset, count); 

            // Leave capability and FileAccess checks up to the underlying stream(s). 
 
            // Reading 0 bytes is a no-op.
            if (count == 0) 
                return 0;

            int pieceNumber = GetCurrentPieceNumber();
            int totalBytesRead = 0; 

            Stream pieceStream = _dir.GetStream(pieceNumber); 
 
            checked
            { 
                //Seek to the correct location in the underlying stream for the current piece
                pieceStream.Seek(_currentOffset - _dir.GetStartOffset(pieceNumber), SeekOrigin.Begin);

                while (totalBytesRead < count) 
                {
                    int numBytesRead = pieceStream.Read( 
                        buffer, 
                        offset + totalBytesRead,
                        count - totalBytesRead); 

                    // End of the current stream: try to move to the next stream.
                    if (numBytesRead == 0)
                    { 
                        if (_dir.IsLastPiece(pieceNumber))
                            break; 
 
                        ++pieceNumber;
                        Invariant.Assert(_dir.GetStartOffset(pieceNumber) == _currentOffset + totalBytesRead); 

                        pieceStream = _dir.GetStream(pieceNumber);

                        //Seek inorder to set the correct pointer for the next piece stream 
                        pieceStream.Seek(0, SeekOrigin.Begin);
                    } 
 
                    totalBytesRead += numBytesRead;
                } 

                // Advance current position now we know the operation completed successfully.
                _currentOffset += totalBytesRead;
            } 

            return totalBytesRead; 
        } 

        ///  
        /// Seek
        /// 
        /// Offset in byte.
        /// Offset origin (start, current, or end). 
        public override long Seek(long offset, SeekOrigin origin)
        { 
            CheckClosed(); 

            // Check stream capabilities. (Normally, CanSeek will be false only 
            // when the stream is closed.)
            if (!CanSeek)
                throw new NotSupportedException(SR.Get(SRID.SeekNotSupported));
 
            // Convert offset to a start-based offset.
            switch (origin) 
            { 
                case SeekOrigin.Begin:
                    break; 

                case SeekOrigin.Current:
                    checked { offset += _currentOffset; }
                    break; 

                case SeekOrigin.End: 
                    checked { offset += Length; } 
                    break;
 
                default:
                    throw new ArgumentOutOfRangeException("origin");
            }
 
            // Check offset validity.
            if (offset < 0) 
                throw new ArgumentException(SR.Get(SRID.SeekNegative)); 

            // OK if _currentOffset points beyond end of stream. 

            // Update position field and return.
            _currentOffset = offset;
 
            return _currentOffset;
        } 
 
        /// 
        /// SetLength 
        /// 
        public override void SetLength(long newLength)
        {
            CheckClosed(); 

            // Check argument and stream capabilities. 
            if (newLength < 0) 
                throw new ArgumentOutOfRangeException("newLength");
            if (!CanWrite) 
                throw new NotSupportedException(SR.Get(SRID.StreamDoesNotSupportWrite));
            if (!CanSeek)
                throw new NotSupportedException(SR.Get(SRID.SeekNotSupported));
 
            // If some pieces are to be deleted, this is reflected only in memory at present.
            int lastPieceNumber; 
            if (newLength == 0) 
            {
                // This is special-cased because there is no last offset to speak of, and 
                // so the piece directory cannot return any piece by offset.
                lastPieceNumber = 0;
            }
            else 
            {
                lastPieceNumber = _dir.GetPieceNumberFromOffset(newLength - 1); // No need to use checked{] since newLength != 0 
            } 
            _dir.SetLogicalLastPiece(lastPieceNumber);
 
            // Adjust last active stream to new size.
            Stream lastPieceStream = _dir.GetStream(lastPieceNumber);

            Debug.Assert(newLength - _dir.GetStartOffset(lastPieceNumber) >= 0); 
            long lastPieceStreamSize = newLength - _dir.GetStartOffset(lastPieceNumber);
            lastPieceStream.SetLength(lastPieceStreamSize); 
 
            if (_currentOffset > newLength)
            { 
                _currentOffset = newLength;
            }
        }
 
        /// 
        /// Write. Distribute the bytes to write across several contiguous streams if needed. 
        ///  
        /// 
        /// Zip streams can be assumed seekable so the length will be available for chaining 
        /// pieces.
        /// 
        public override void Write(byte[] buffer, int offset, int count)
        { 
            CheckClosed();
 
            // Check arguments. 
            PackagingUtilities.VerifyStreamWriteArgs(this, buffer, offset, count);
 
            // No check for FileAccess and stream capability (CanWrite). This is the responsibility
            // of the underlying stream(s).

            // A no-op if zero bytes to write. 
            if (count == 0)
                return; 
 
            // Write into piece streams, preserving all lengths in non-terminal pieces.
            int totalBytesWritten = 0; 
            int pieceNumber = GetCurrentPieceNumber();
            Stream pieceStream = _dir.GetStream(pieceNumber);

            checked 
            {
                //Seek to the correct location in the underlying stream for the current piece 
                pieceStream.Seek(_currentOffset - _dir.GetStartOffset(pieceNumber), SeekOrigin.Begin); 

                while (totalBytesWritten < count) 
                {
                    // Compute the number of bytes to write into pieceStream.
                    int numBytesToWriteInCurrentPiece = count - totalBytesWritten;
                    if (!_dir.IsLastPiece(pieceNumber)) 
                    {
                        // The write should not change the length of an intermediate piece. 
                        long currentPosition = _currentOffset + totalBytesWritten; 
                        long maxPosition = _dir.GetStartOffset(pieceNumber+1) - 1;
                        if (numBytesToWriteInCurrentPiece > (maxPosition - currentPosition + 1)) 
                        {
                            // Cast from long to cast is safe in so far as *count*, which is the
                            // absolute max for all byte counts, is a positive int.
                            numBytesToWriteInCurrentPiece = checked((int)(maxPosition - currentPosition + 1)); 
                        }
                    } 
 
                    // Do the write.
                    pieceStream.Write(buffer, offset + totalBytesWritten, numBytesToWriteInCurrentPiece); 

                    // Update the tally.
                    totalBytesWritten += numBytesToWriteInCurrentPiece;
 
                    // If there is more data to write, get the next piece stream
                    if (!_dir.IsLastPiece(pieceNumber) && totalBytesWritten < count) 
                    { 
                        // The next write, should involve the next piece.
                        ++pieceNumber; 

                        pieceStream = _dir.GetStream(pieceNumber);

                        //Seek inorder to set the correct pointer for the next piece stream 
                        pieceStream.Seek(0, SeekOrigin.Begin);
                    } 
                } 

                // Now we know the operation has completed, the current position can be updated. 
                Invariant.Assert(totalBytesWritten == count);
                _currentOffset += totalBytesWritten;
            }
        } 

        ///  
        /// Flush all dirty streams and commit pending piece deletions. 
        /// 
        ///  
        /// Flush gets called on all underlying streams ever accessed. If it turned out
        /// this is too inefficient, the PieceDirectory could be made to expose a SetDirty
        /// method that takes a piece number.
        ///  
        public override void Flush()
        { 
            CheckClosed(); 

            // The underlying streams know whether they are dirty or not; 
            // so _dir will indiscriminately flush all the streams that have been accessed.
            // It will also carry out necessary renamings and deletions to reflect calls to
            // SetLogicalLastPiece.
            _dir.Flush(); 
        }
 
        //------------------------------------------------------ 
        //
        //  Public Properties 
        //
        //------------------------------------------------------

        ///  
        /// Is stream readable?
        ///  
        ///  
        /// 
        /// Here, the assumption, as in all capability tests, is that the status of 
        /// the first piece reflects the status of all pieces for the part.
        /// This is justified by the fact that (i) all piece streams are opened with the same
        /// parameters against the same archive and (ii) the current piece stream cannot get
        /// closed unless the whole part stream is closed. 
        /// 
        ///  
        /// A further assumption is that, as soon as interleaved zip part stream is initialized, there 
        /// is a descriptor for the 1st piece.
        ///  
        /// 
        public override bool CanRead
        {
            get 
            {
                return _closed ? false : _dir.GetStream(0).CanRead; 
            } 
        }
 
        /// 
        /// Is stream seekable?
        /// 
        ///  
        /// 
        /// Here, the assumption, as in all capability tests, is that the status of 
        /// the first piece reflects the status of all pieces for the part. 
        /// This is justified by the fact that (i) all piece streams are opened with the same
        /// parameters against the same archive and (ii) the current piece stream cannot get 
        /// closed unless the whole part stream is closed.
        /// 
        /// 
        /// A further assumption is that, as soon as interleaved zip part stream is initialized, there 
        /// is a descriptor for the 1st piece.
        ///  
        ///  
        public override bool CanSeek
        { 
            get
            {
                return _closed ? false : _dir.GetStream(0).CanSeek;
            } 
        }
 
        ///  
        /// Is stream writable?
        ///  
        /// 
        /// 
        /// Here, the assumption, as in all capability tests, is that the status of
        /// the first piece reflects the status of all pieces for the part. 
        /// This is justified by the fact that (i) all piece streams are opened with the same
        /// parameters against the same archive and (ii) the current piece stream cannot get 
        /// closed unless the whole part stream is closed. 
        /// 
        ///  
        /// A further assumption is that, as soon as interleaved zip part stream is initialized, there
        /// is a descriptor for the 1st piece.
        /// 
        ///  
        //
        public override bool CanWrite 
        { 
            get
            { 
                return _closed ? false : _dir.GetStream(0).CanWrite;
            }
        }
 
        /// 
        /// Logical byte position in this stream. 
        ///  
        public override long Position
        { 
            get
            {
                CheckClosed();
 
                // Current offset is systematically updated to reflect the current position.
                return _currentOffset; 
            } 
            set
            { 
                CheckClosed();
                Seek(value, SeekOrigin.Begin);
            }
        } 

        ///  
        /// Length. 
        /// 
        // 
        public override long Length
        {
            get
            { 
                CheckClosed();
                Invariant.Assert(CanSeek); 
 
                long length = 0;
                for (int pieceNumber = 0; pieceNumber < _dir.GetNumberOfPieces(); ++pieceNumber) 
                {
                    checked { length += _dir.GetStream(pieceNumber).Length; }
                }
                return length; 
            }
        } 
 
        //-----------------------------------------------------
        // 
        //  Protected Methods
        //
        //------------------------------------------------------
 
        #region Protected Methods
 
        ///  
        /// Dispose(bool)
        ///  
        /// 
        /// 
        /// An instance of streams' peculiar dispose pattern, whereby
        /// the inherited abstract class implements Close by calling 
        /// this virtual protected function.
        /// In turn, each implementation is responsible for calling back 
        /// its base's implementation. 
        /// 
        protected override void Dispose(bool disposing) 
        {


            try 
            {
                if (disposing) 
                { 
                    if (!_closed)
                    { 
                        _dir.Close();
                    }
                }
            } 
            finally
            { 
                _closed = true; 
                base.Dispose(disposing);
            } 
        }

        #endregion Protected Methods
 
        //-----------------------------------------------------
        // 
        //   Private Methods 
        //
        //----------------------------------------------------- 

        #region Private Methods

        private void CheckClosed() 
        {
            if (_closed) 
                throw new ObjectDisposedException(null, SR.Get(SRID.StreamObjectDisposed)); 
        }
 
        /// 
        /// Infer the current piece number from _currentOffset.
        /// 
        ///  
        /// Storing the current piece number in a field and computing the current offset from it
        /// would also have been possible, but less efficient. 
        ///  
        private int GetCurrentPieceNumber()
        { 
            // Since this property is likely to be read more often than _currentOffset
            // gets updated, its value is cached in _currentPieceNumber.
            // The validity of the cached value is monitored using _offsetForCurrentPieceNumber.
            if (_offsetForCurrentPieceNumber != _currentOffset) 
            {
                // Cached value is stale. Refresh. 
                _currentPieceNumber = _dir.GetPieceNumberFromOffset(_currentOffset); 
                _offsetForCurrentPieceNumber = _currentOffset;
            } 
            return _currentPieceNumber;
        }

        #endregion Private Methods 

        //----------------------------------------------------- 
        // 
        //  Private Fields
        // 
        //------------------------------------------------------

        #region Private Fields
 
        // High-level object to access the collection of pieces by offset and pieceNumber.
        private PieceDirectory       _dir; 
 
        // Cached value for the current piece number.
        // (Lazily [....]'ed to _currentOffset when GetCurrentPieceNumber() is invoked.) 
        private int                 _currentPieceNumber;

        // Control value to decide whether to use _currentPieceNumber without updating it.
        private Nullable      _offsetForCurrentPieceNumber; 

        // This variable continuously tracks the current stream position. 
        private long                _currentOffset = 0; 

        // Closed status. 
        private bool                _closed = false;

        #endregion Private Fields
    } 
}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------------------------ 
//  Microsoft Avalon
//  Copyright (c) Microsoft Corporation, 2005
//
//  File:           InterleavedZipPartStream.cs 
//
//  Description:    The class InterleavedZipPartStream is used to wrap one or more Zip 
//                  part streams for an interleaved part. It hides the interleaving 
//                  from its callers by offering the abstraction of a continuous stream
//                  across pieces. 
//
//  History:        05/15/05 - johnlarc - initial implementation
//-----------------------------------------------------------------------------
 
using System;
using System.Diagnostics; 
using System.IO; 
using System.IO.Packaging;                  // For ZipPackagePart, etc.
using MS.Internal.IO.Zip;                   // For ZipFileInfo. 
using System.Windows;                       // for ExceptionStringTable
using System.Collections.Generic;           // For List<>
using MS.Internal;                          // for Invariant
 
namespace MS.Internal.IO.Packaging
{ 
    ///  
 	/// The class InterleavedZipPartStream is used to wrap one or more Zip part streams
    /// for an interleaved part. It hides the interleaving from its callers by offering 
    /// the abstraction of a continuous stream across pieces.
    /// 
    /// 
    /// This class is defined for the benefit of ZipPackage, ZipPackagePart and 
    /// InternalRelationshipCollection.
    /// Although it is quite specialized, it would hardly make sense to nest its definition in any 
    /// of these clases. 
    /// 
    internal partial class InterleavedZipPartStream : Stream 
    {
        #region Constructors

        //----------------------------------------------------- 
        //
        //  Constructors 
        // 
        //-----------------------------------------------------
        ///  
        /// Build a System.IO.Stream on a part that possibly consists of multiple files
        /// An InterleavedZipPartStream gets created by ZipPackagePart.GetStreamCore when the part
        /// is interleaved. It wraps one or more Zip streams (one per piece).
        /// (pieces). 
        /// 
        /// Mode (create, etc.) in which piece streams should be opened 
        /// Access (read, write, etc.) with which piece streams should be opened 
        /// 
        /// The part to build a stream on. It contains all ZipFileInfo descriptors for the part's pieces 
        /// (see ZipPackage.GetPartsCore).
        /// 
        internal InterleavedZipPartStream(ZipPackagePart owningPart, FileMode mode, FileAccess access)
            : this(PackUriHelper.GetStringForPartUri(owningPart.Uri), 
                owningPart.PieceDescriptors,
                mode, access) 
        { 
        }
 
        /// 
        /// This constructor is provided to be able to interleave other files than just parts,
        /// notably the contents type file.
        ///  
        internal InterleavedZipPartStream(string partName, List sortedPieceInfoList,
            FileMode mode, FileAccess access) 
        { 
            // The PieceDirectory mediates access to pieces.
            // It maps offsets to piece numbers and piece numbers to streams and start offsets. 
            // Mode and access are entirely managed by the underlying streams, assumed to be seekable.
            _dir = new PieceDirectory(sortedPieceInfoList, mode, access);

            // GetCurrentPieceNumber is operational from the beginning. 
            Invariant.Assert(_dir.GetStartOffset(GetCurrentPieceNumber()) == 0);
        } 
 
        #endregion Constructors
 
        //------------------------------------------------------
        //
        //  Public Methods
        // 
        //-----------------------------------------------------
 
        ///  
        /// Return the bytes requested.
        ///  
        /// Destination buffer.
        /// 
        /// The zero-based byte offset in buffer at which to begin storing the data read
        /// from the current stream. 
        /// 
        /// How many bytes requested. 
        /// How many bytes were written into buffer. 
        public override int Read(byte[] buffer, int offset, int count)
        { 
            CheckClosed();

            // Check arguments.
            PackagingUtilities.VerifyStreamReadArgs(this, buffer, offset, count); 

            // Leave capability and FileAccess checks up to the underlying stream(s). 
 
            // Reading 0 bytes is a no-op.
            if (count == 0) 
                return 0;

            int pieceNumber = GetCurrentPieceNumber();
            int totalBytesRead = 0; 

            Stream pieceStream = _dir.GetStream(pieceNumber); 
 
            checked
            { 
                //Seek to the correct location in the underlying stream for the current piece
                pieceStream.Seek(_currentOffset - _dir.GetStartOffset(pieceNumber), SeekOrigin.Begin);

                while (totalBytesRead < count) 
                {
                    int numBytesRead = pieceStream.Read( 
                        buffer, 
                        offset + totalBytesRead,
                        count - totalBytesRead); 

                    // End of the current stream: try to move to the next stream.
                    if (numBytesRead == 0)
                    { 
                        if (_dir.IsLastPiece(pieceNumber))
                            break; 
 
                        ++pieceNumber;
                        Invariant.Assert(_dir.GetStartOffset(pieceNumber) == _currentOffset + totalBytesRead); 

                        pieceStream = _dir.GetStream(pieceNumber);

                        //Seek inorder to set the correct pointer for the next piece stream 
                        pieceStream.Seek(0, SeekOrigin.Begin);
                    } 
 
                    totalBytesRead += numBytesRead;
                } 

                // Advance current position now we know the operation completed successfully.
                _currentOffset += totalBytesRead;
            } 

            return totalBytesRead; 
        } 

        ///  
        /// Seek
        /// 
        /// Offset in byte.
        /// Offset origin (start, current, or end). 
        public override long Seek(long offset, SeekOrigin origin)
        { 
            CheckClosed(); 

            // Check stream capabilities. (Normally, CanSeek will be false only 
            // when the stream is closed.)
            if (!CanSeek)
                throw new NotSupportedException(SR.Get(SRID.SeekNotSupported));
 
            // Convert offset to a start-based offset.
            switch (origin) 
            { 
                case SeekOrigin.Begin:
                    break; 

                case SeekOrigin.Current:
                    checked { offset += _currentOffset; }
                    break; 

                case SeekOrigin.End: 
                    checked { offset += Length; } 
                    break;
 
                default:
                    throw new ArgumentOutOfRangeException("origin");
            }
 
            // Check offset validity.
            if (offset < 0) 
                throw new ArgumentException(SR.Get(SRID.SeekNegative)); 

            // OK if _currentOffset points beyond end of stream. 

            // Update position field and return.
            _currentOffset = offset;
 
            return _currentOffset;
        } 
 
        /// 
        /// SetLength 
        /// 
        public override void SetLength(long newLength)
        {
            CheckClosed(); 

            // Check argument and stream capabilities. 
            if (newLength < 0) 
                throw new ArgumentOutOfRangeException("newLength");
            if (!CanWrite) 
                throw new NotSupportedException(SR.Get(SRID.StreamDoesNotSupportWrite));
            if (!CanSeek)
                throw new NotSupportedException(SR.Get(SRID.SeekNotSupported));
 
            // If some pieces are to be deleted, this is reflected only in memory at present.
            int lastPieceNumber; 
            if (newLength == 0) 
            {
                // This is special-cased because there is no last offset to speak of, and 
                // so the piece directory cannot return any piece by offset.
                lastPieceNumber = 0;
            }
            else 
            {
                lastPieceNumber = _dir.GetPieceNumberFromOffset(newLength - 1); // No need to use checked{] since newLength != 0 
            } 
            _dir.SetLogicalLastPiece(lastPieceNumber);
 
            // Adjust last active stream to new size.
            Stream lastPieceStream = _dir.GetStream(lastPieceNumber);

            Debug.Assert(newLength - _dir.GetStartOffset(lastPieceNumber) >= 0); 
            long lastPieceStreamSize = newLength - _dir.GetStartOffset(lastPieceNumber);
            lastPieceStream.SetLength(lastPieceStreamSize); 
 
            if (_currentOffset > newLength)
            { 
                _currentOffset = newLength;
            }
        }
 
        /// 
        /// Write. Distribute the bytes to write across several contiguous streams if needed. 
        ///  
        /// 
        /// Zip streams can be assumed seekable so the length will be available for chaining 
        /// pieces.
        /// 
        public override void Write(byte[] buffer, int offset, int count)
        { 
            CheckClosed();
 
            // Check arguments. 
            PackagingUtilities.VerifyStreamWriteArgs(this, buffer, offset, count);
 
            // No check for FileAccess and stream capability (CanWrite). This is the responsibility
            // of the underlying stream(s).

            // A no-op if zero bytes to write. 
            if (count == 0)
                return; 
 
            // Write into piece streams, preserving all lengths in non-terminal pieces.
            int totalBytesWritten = 0; 
            int pieceNumber = GetCurrentPieceNumber();
            Stream pieceStream = _dir.GetStream(pieceNumber);

            checked 
            {
                //Seek to the correct location in the underlying stream for the current piece 
                pieceStream.Seek(_currentOffset - _dir.GetStartOffset(pieceNumber), SeekOrigin.Begin); 

                while (totalBytesWritten < count) 
                {
                    // Compute the number of bytes to write into pieceStream.
                    int numBytesToWriteInCurrentPiece = count - totalBytesWritten;
                    if (!_dir.IsLastPiece(pieceNumber)) 
                    {
                        // The write should not change the length of an intermediate piece. 
                        long currentPosition = _currentOffset + totalBytesWritten; 
                        long maxPosition = _dir.GetStartOffset(pieceNumber+1) - 1;
                        if (numBytesToWriteInCurrentPiece > (maxPosition - currentPosition + 1)) 
                        {
                            // Cast from long to cast is safe in so far as *count*, which is the
                            // absolute max for all byte counts, is a positive int.
                            numBytesToWriteInCurrentPiece = checked((int)(maxPosition - currentPosition + 1)); 
                        }
                    } 
 
                    // Do the write.
                    pieceStream.Write(buffer, offset + totalBytesWritten, numBytesToWriteInCurrentPiece); 

                    // Update the tally.
                    totalBytesWritten += numBytesToWriteInCurrentPiece;
 
                    // If there is more data to write, get the next piece stream
                    if (!_dir.IsLastPiece(pieceNumber) && totalBytesWritten < count) 
                    { 
                        // The next write, should involve the next piece.
                        ++pieceNumber; 

                        pieceStream = _dir.GetStream(pieceNumber);

                        //Seek inorder to set the correct pointer for the next piece stream 
                        pieceStream.Seek(0, SeekOrigin.Begin);
                    } 
                } 

                // Now we know the operation has completed, the current position can be updated. 
                Invariant.Assert(totalBytesWritten == count);
                _currentOffset += totalBytesWritten;
            }
        } 

        ///  
        /// Flush all dirty streams and commit pending piece deletions. 
        /// 
        ///  
        /// Flush gets called on all underlying streams ever accessed. If it turned out
        /// this is too inefficient, the PieceDirectory could be made to expose a SetDirty
        /// method that takes a piece number.
        ///  
        public override void Flush()
        { 
            CheckClosed(); 

            // The underlying streams know whether they are dirty or not; 
            // so _dir will indiscriminately flush all the streams that have been accessed.
            // It will also carry out necessary renamings and deletions to reflect calls to
            // SetLogicalLastPiece.
            _dir.Flush(); 
        }
 
        //------------------------------------------------------ 
        //
        //  Public Properties 
        //
        //------------------------------------------------------

        ///  
        /// Is stream readable?
        ///  
        ///  
        /// 
        /// Here, the assumption, as in all capability tests, is that the status of 
        /// the first piece reflects the status of all pieces for the part.
        /// This is justified by the fact that (i) all piece streams are opened with the same
        /// parameters against the same archive and (ii) the current piece stream cannot get
        /// closed unless the whole part stream is closed. 
        /// 
        ///  
        /// A further assumption is that, as soon as interleaved zip part stream is initialized, there 
        /// is a descriptor for the 1st piece.
        ///  
        /// 
        public override bool CanRead
        {
            get 
            {
                return _closed ? false : _dir.GetStream(0).CanRead; 
            } 
        }
 
        /// 
        /// Is stream seekable?
        /// 
        ///  
        /// 
        /// Here, the assumption, as in all capability tests, is that the status of 
        /// the first piece reflects the status of all pieces for the part. 
        /// This is justified by the fact that (i) all piece streams are opened with the same
        /// parameters against the same archive and (ii) the current piece stream cannot get 
        /// closed unless the whole part stream is closed.
        /// 
        /// 
        /// A further assumption is that, as soon as interleaved zip part stream is initialized, there 
        /// is a descriptor for the 1st piece.
        ///  
        ///  
        public override bool CanSeek
        { 
            get
            {
                return _closed ? false : _dir.GetStream(0).CanSeek;
            } 
        }
 
        ///  
        /// Is stream writable?
        ///  
        /// 
        /// 
        /// Here, the assumption, as in all capability tests, is that the status of
        /// the first piece reflects the status of all pieces for the part. 
        /// This is justified by the fact that (i) all piece streams are opened with the same
        /// parameters against the same archive and (ii) the current piece stream cannot get 
        /// closed unless the whole part stream is closed. 
        /// 
        ///  
        /// A further assumption is that, as soon as interleaved zip part stream is initialized, there
        /// is a descriptor for the 1st piece.
        /// 
        ///  
        //
        public override bool CanWrite 
        { 
            get
            { 
                return _closed ? false : _dir.GetStream(0).CanWrite;
            }
        }
 
        /// 
        /// Logical byte position in this stream. 
        ///  
        public override long Position
        { 
            get
            {
                CheckClosed();
 
                // Current offset is systematically updated to reflect the current position.
                return _currentOffset; 
            } 
            set
            { 
                CheckClosed();
                Seek(value, SeekOrigin.Begin);
            }
        } 

        ///  
        /// Length. 
        /// 
        // 
        public override long Length
        {
            get
            { 
                CheckClosed();
                Invariant.Assert(CanSeek); 
 
                long length = 0;
                for (int pieceNumber = 0; pieceNumber < _dir.GetNumberOfPieces(); ++pieceNumber) 
                {
                    checked { length += _dir.GetStream(pieceNumber).Length; }
                }
                return length; 
            }
        } 
 
        //-----------------------------------------------------
        // 
        //  Protected Methods
        //
        //------------------------------------------------------
 
        #region Protected Methods
 
        ///  
        /// Dispose(bool)
        ///  
        /// 
        /// 
        /// An instance of streams' peculiar dispose pattern, whereby
        /// the inherited abstract class implements Close by calling 
        /// this virtual protected function.
        /// In turn, each implementation is responsible for calling back 
        /// its base's implementation. 
        /// 
        protected override void Dispose(bool disposing) 
        {


            try 
            {
                if (disposing) 
                { 
                    if (!_closed)
                    { 
                        _dir.Close();
                    }
                }
            } 
            finally
            { 
                _closed = true; 
                base.Dispose(disposing);
            } 
        }

        #endregion Protected Methods
 
        //-----------------------------------------------------
        // 
        //   Private Methods 
        //
        //----------------------------------------------------- 

        #region Private Methods

        private void CheckClosed() 
        {
            if (_closed) 
                throw new ObjectDisposedException(null, SR.Get(SRID.StreamObjectDisposed)); 
        }
 
        /// 
        /// Infer the current piece number from _currentOffset.
        /// 
        ///  
        /// Storing the current piece number in a field and computing the current offset from it
        /// would also have been possible, but less efficient. 
        ///  
        private int GetCurrentPieceNumber()
        { 
            // Since this property is likely to be read more often than _currentOffset
            // gets updated, its value is cached in _currentPieceNumber.
            // The validity of the cached value is monitored using _offsetForCurrentPieceNumber.
            if (_offsetForCurrentPieceNumber != _currentOffset) 
            {
                // Cached value is stale. Refresh. 
                _currentPieceNumber = _dir.GetPieceNumberFromOffset(_currentOffset); 
                _offsetForCurrentPieceNumber = _currentOffset;
            } 
            return _currentPieceNumber;
        }

        #endregion Private Methods 

        //----------------------------------------------------- 
        // 
        //  Private Fields
        // 
        //------------------------------------------------------

        #region Private Fields
 
        // High-level object to access the collection of pieces by offset and pieceNumber.
        private PieceDirectory       _dir; 
 
        // Cached value for the current piece number.
        // (Lazily [....]'ed to _currentOffset when GetCurrentPieceNumber() is invoked.) 
        private int                 _currentPieceNumber;

        // Control value to decide whether to use _currentPieceNumber without updating it.
        private Nullable      _offsetForCurrentPieceNumber; 

        // This variable continuously tracks the current stream position. 
        private long                _currentOffset = 0; 

        // Closed status. 
        private bool                _closed = false;

        #endregion Private Fields
    } 
}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK