ZipIOFileItemStream.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Base / MS / Internal / IO / Zip / ZipIOFileItemStream.cs / 1305600 / ZipIOFileItemStream.cs

                            //------------------------------------------------------------------------------ 
//-------------   *** WARNING ***
//-------------    This file is part of a legally monitored development project.
//-------------    Do not check in changes to this project.  Do not raid bugs on this
//-------------    code in the main PS database.  Do not contact the owner of this 
//-------------    code directly.  Contact the legal team at �ZSLegal� for assistance.
//-------------   *** WARNING *** 
//----------------------------------------------------------------------------- 

//----------------------------------------------------------------------------- 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: 
//  This is an internal class that enables interactions with Zip archives 
//  for OPC scenarios
// 
// History:
//  11/19/2004: IgorBel: Initial creation.
//  06/21/2005: BruceMac: Add Write-time-streaming support
//----------------------------------------------------------------------------- 

using System; 
using System.IO; 
using System.Diagnostics;
using System.Text; 
using System.Collections;
using System.Runtime.Serialization;
using System.Windows;
using MS.Internal.IO.Packaging;         // for PackagingUtilities 
using MS.Internal.WindowsBase;
 
namespace MS.Internal.IO.Zip 
{
    internal class ZipIOFileItemStream :  Stream 
    {
        ////////////////////////////////////
        // Stream section
        ///////////////////////////////// 
        override public bool CanRead
        { 
            get 
            {
                return (!_disposedFlag) && (_blockManager.Stream.CanRead); 
            }
        }

        override public bool CanSeek 
        {
            get 
            { 
                return (!_disposedFlag) && (_blockManager.Stream.CanSeek);
            } 
        }

        override public bool CanWrite
        { 
            get
            { 
                return (!_disposedFlag) && (_blockManager.Stream.CanWrite); 
            }
        } 

        override public long Length
        {
            get 
            {
                CheckDisposed(); 
 
                return  _currentStreamLength;
            } 
        }

        override public long Position
        { 
            get
            { 
                CheckDisposed(); 
                return _currentStreamPosition;
            } 
            set
            {
                CheckDisposed();
                Seek(value, SeekOrigin.Begin); 
            }
        } 
 
        public override void SetLength(long newLength)
        { 
            CheckDisposed();

            Debug.Assert(_cachePrefixStream == null); // we only expect this thing to be not null during Archive Save execution
                                                                                // that would between PreSaveNotofication call and Save SaveStreaming 

            if (newLength < 0) 
            { 
                throw new ArgumentOutOfRangeException("newLength");
            } 

            if (_currentStreamLength != newLength)
            {
                _dirtyFlag = true; 
                _dataChanged = true;
 
                if  (newLength <= _persistedSize) 
                {
                    // the stream becomes smaller than our disk block, which means that 
                    // we can drop the in-memory-sparse-suffix
                    if (_sparseMemoryStreamSuffix  != null)
                    {
                        _sparseMemoryStreamSuffix.Close(); 
                        _sparseMemoryStreamSuffix  = null;
                    } 
                } 
                else
                { 
                    // we need to construct Sparse Memory stream if we do not have one yet
                    if (_sparseMemoryStreamSuffix  == null)
                    {
                        _sparseMemoryStreamSuffix  = new SparseMemoryStream(_lowWaterMark, _highWaterMark); 
                    }
 
                    // set size on the Sparse Memory Stream 
                    _sparseMemoryStreamSuffix.SetLength(newLength - _persistedSize); // no need for checked as it was verified above
                } 

                _currentStreamLength = newLength;

                // if stream was truncated to the point that our current position is beyond the end of the stream, 
                // we need to reset position so it is at the end of the stream
                if (_currentStreamLength < _currentStreamPosition) 
                    Seek(_currentStreamLength, SeekOrigin.Begin); 
            }
        } 

        override public long Seek(long offset, SeekOrigin origin)
        {
            CheckDisposed(); 

            Debug.Assert(_cachePrefixStream == null); // we only expect this thing to be not null during Archive Save execution 
                                                                                // that would between PreSaveNotofication call and Save SaveStreaming 

            long newStreamPosition = _currentStreamPosition; 

            if (origin ==SeekOrigin.Begin)
            {
                newStreamPosition = offset; 
            }
            else if  (origin == SeekOrigin.Current) 
            { 
                checked{newStreamPosition += offset;}
            } 
            else if  (origin == SeekOrigin.End)
            {
                checked{newStreamPosition = _currentStreamLength + offset;}
            } 
            else
            { 
                throw new ArgumentOutOfRangeException("origin"); 
            }
 
            if (newStreamPosition  < 0)
            {
                 throw new ArgumentException(SR.Get(SRID.SeekNegative));
            } 
            _currentStreamPosition = newStreamPosition;
 
            return _currentStreamPosition; 
        }
 
        override public int Read(byte[] buffer, int offset, int count)
        {
            CheckDisposed();
 
            PackagingUtilities.VerifyStreamReadArgs(this, buffer, offset, count);
 
            Debug.Assert(_cachePrefixStream == null); // we only expect this thing to be not null during Archive Save execution 
                                                      // that would between PreSaveNotofication call and Save SaveStreaming
 
            Debug.Assert(_currentStreamPosition >= 0);

            if (count == 0)
            { 
                return 0;
            } 
 
            if (_currentStreamLength <= _currentStreamPosition)
            { 
                // we are past the end of the stream so let's just return 0
                return 0;
            }
 
            int totalBytesRead;
            int diskBytesRead = 0; 
            int diskBytesToRead = 0; 
            long persistedTailSize = 0;
 
            int memoryBytesRead = 0;
            long newStreamPosition = _currentStreamPosition;

            checked 
            {
                // Try to satisfy request with the Read from the Disk 
                if  (newStreamPosition < _persistedSize) 
                {
                    // we have at least partial overlap between request and the data on disk 

                    //first let's get min between size of the stream's tail and the tail of the persisted chunk
                    // in some cases stream might be smaller
                    // e.g. _currentStreamLength  < _persistedSize, if let's say stream was truncated 
                    persistedTailSize = Math.Min(_currentStreamLength, _persistedSize) - newStreamPosition;
                    Debug.Assert(persistedTailSize > 0); 
 
                    // we also do not want to read more data than was requested by the user
                    diskBytesToRead = (int)Math.Min((long)count, persistedTailSize); // this is a safe cast as count has int type 
                    Debug.Assert(diskBytesToRead > 0);

                    // and now we can actually read it
                    _blockManager.Stream.Seek(_persistedOffset + newStreamPosition, SeekOrigin.Begin); 

                    // we are ready for getting fewer bytes than reqested 
                    diskBytesRead = _blockManager.Stream.Read(buffer, offset, diskBytesToRead); 

                    newStreamPosition += diskBytesRead; 
                     count -= diskBytesRead;
                     offset +=diskBytesRead;

                    if (diskBytesRead  <  diskBytesToRead) 
                    {
                        // we didn't everything that we hae asked for. In such case we shouldn't 
                        // try to get data from the   _sparseMemoryStreamSuffix 
                        _currentStreamPosition = newStreamPosition;
 
                        return diskBytesRead;
                    }
                }
 
                // check whether we need to get data from the memory Stream;
                if  ((_sparseMemoryStreamSuffix  != null) && (newStreamPosition + count > _persistedSize)) 
                { 
                    // we are either trying to finish the request partially satisfied by the
                    // on disk data  or  the read is entirely within the suffix 
                     _sparseMemoryStreamSuffix.Seek(newStreamPosition - _persistedSize, SeekOrigin.Begin);
                    memoryBytesRead = _sparseMemoryStreamSuffix.Read(buffer, offset, count);

                    newStreamPosition += memoryBytesRead; 
                }
 
                totalBytesRead = diskBytesRead + memoryBytesRead; 
            }
 
            _currentStreamPosition = newStreamPosition;
            return totalBytesRead ;
        }
 
        /// 
        /// Write 
        ///  
        /// 
        ///  
        /// 
        /// In streaming mode, write should accumulate data into the SparseMemoryStream.
        override public void Write(byte[] buffer, int offset, int count)
        { 
            CheckDisposed();
 
            PackagingUtilities.VerifyStreamWriteArgs(this, buffer, offset, count); 

            Debug.Assert(_cachePrefixStream == null); // we only expect this thing to be not null during Archive Save execution 
                                                      // that would between PreSaveNotofication call and Save SaveStreaming

            Debug.Assert(_currentStreamPosition >= 0);
 
            if (count == 0)
            { 
                return; 
            }
 
            int diskBytesToWrite = 0;

            _dirtyFlag = true;
            _dataChanged = true; 
            long newStreamPosition = _currentStreamPosition;
            checked 
            { 
                // Try to satisfy request with the Write to the Disk
                if  (newStreamPosition  < _persistedSize) 
                {
                    Debug.Assert(!_blockManager.Streaming);

                    // we have at least partial overlap between request and the data on disk 
                    _blockManager.Stream.Seek(_persistedOffset + newStreamPosition, SeekOrigin.Begin);
                    // Note on casting: 
                    //  It is safe to cast the result of Math.Min(count, _persistedSize - newStreamPosition)) 
                    //      from long to int since it cannot be bigger than count and count is int type
                    diskBytesToWrite = (int) (Math.Min(count, _persistedSize - newStreamPosition));  // this is a safe cast as count has int type 

                    _blockManager.Stream.Write(buffer, offset, diskBytesToWrite);
                    newStreamPosition += diskBytesToWrite;
                    count -= diskBytesToWrite; 
                    offset += diskBytesToWrite;
                } 
 
                // check whether we need to save data to the memory Stream;
                if  (newStreamPosition + count > _persistedSize) 
                {
                    if (_sparseMemoryStreamSuffix  == null)
                    {
                         _sparseMemoryStreamSuffix  = new SparseMemoryStream(_lowWaterMark, _highWaterMark); 
                    }
 
                    _sparseMemoryStreamSuffix.Seek(newStreamPosition - _persistedSize, SeekOrigin.Begin); 

                    _sparseMemoryStreamSuffix.Write(buffer, offset, count); 
                    newStreamPosition += count;
                }

                _currentStreamPosition = newStreamPosition; 
                _currentStreamLength = Math.Max(_currentStreamLength, _currentStreamPosition);
            } 
            return; 
        }
 
        override public void Flush()
        {
            CheckDisposed();
 
            // tell the BlockManager that the caller called Flush on us. Block manager will process this
            // and possibly call us back on Save or SaveStreaming 
            _blockManager.SaveStream(_block, false);  // second parameter is a closing indicator 
        }
 
        /////////////////////////////
        // Internal Constructor
        /////////////////////////////
        internal  ZipIOFileItemStream(ZipIOBlockManager blockManager,   // blockManager is only needed 
                                                                        // to pass through to it Flush requests
                                      ZipIOLocalFileBlock block,        // our owning block - needed for Streaming scenarios 
                                      long persistedOffset,             // to map to the stream 
                                      long persistedSize)               // to map to the stream )
        { 
            Debug.Assert(blockManager != null);
            Debug.Assert(persistedOffset >=0);
            Debug.Assert(persistedSize >= 0);
            Debug.Assert(block != null); 

            _persistedOffset = persistedOffset; 
            _offset = persistedOffset; 
            _persistedSize = persistedSize;
 
            _blockManager = blockManager;
            _block = block;

            _currentStreamLength = persistedSize; 
        }
 
        ///////////////////////////// 
        // Internal Methods for the LocalFileBlock to call in order to know Dirty status and the new size
        ///////////////////////////// 
        internal PreSaveNotificationScanControlInstruction PreSaveNotification(long offset, long size)
        {
            return ZipIOBlockManager.CommonPreSaveNotificationHandler(
                    _blockManager.Stream, 
                    offset,
                    size, 
                    _persistedOffset, 
                    Math.Min(_persistedSize, _currentStreamLength),  // in case the stream is smaller then our persisted block on
                                                                            // disk, there is no need to preserve the meaningless persisted suffix 
                    ref _cachePrefixStream);
        }

        internal bool DirtyFlag 
        {
            get 
            { 
                return _dirtyFlag;
            } 
        }

        internal bool DataChanged
        { 
            get
            { 
                return _dataChanged; 
            }
        } 

        internal long Offset
        {
            get 
            {
                return _offset; 
            } 
        }
 
        internal void Move(long shiftSize)
        {
            CheckDisposed();
            if (shiftSize != 0) 
            {
                checked{_offset +=shiftSize;} 
                _dirtyFlag = true; 
                Debug.Assert(_offset >=0);
            } 
        }

        /// 
        /// Streaming-specific variant of Save() 
        /// 
        /// Writes current data to the underlying stream. 
        /// Assumes the stream is in the correct place. 
        internal void SaveStreaming()
        { 
            CheckDisposed();

            Debug.Assert(_cachePrefixStream == null); // _cachePrefixStream must not be used in streaming cases at all
 
            Debug.Assert(_blockManager.Streaming);
 
            if (_dirtyFlag) 
            {
                // in streaming cases all the data collected in the _sparseMemoryStreamSuffix 
                // and now we can save the SparseMemoryStream
                if (_sparseMemoryStreamSuffix  != null)
                {
                    _sparseMemoryStreamSuffix.WriteToStream(_blockManager.Stream); 

                    // update so that subsequent MemoryStreams will know where they begin 
                    checked{_persistedSize += _sparseMemoryStreamSuffix.Length;} 
                    _sparseMemoryStreamSuffix.Close();
                    _sparseMemoryStreamSuffix  = null; 
                }

                _dirtyFlag = false;
                _dataChanged = false; 
            }
        } 
 
        /// 
        /// Save - called by the BlockManager to cause us to Flush to the underlying stream 
        /// 
        internal void Save()
        {
            CheckDisposed(); 
            Debug.Assert(!_blockManager.Streaming);
 
            if(_dirtyFlag) 
            {
                // we need to move the whole persisted block to the new position 
                long moveBlockSourceOffset = _persistedOffset;

                // in case the stream is smaller then our persisted block on disk there is
                // no need to move meaningless persisted suffix 
                long moveBlockSize = Math.Min(_persistedSize, _currentStreamLength);
 
                long moveBlockTargetOffset = _offset; 

                long newPersistedSize = 0; 

                if (_cachePrefixStream != null)
                {
                    // if we have something in cache we only should move whatever isn't cached 
                    checked{moveBlockSourceOffset += _cachePrefixStream.Length;}
                    checked{moveBlockTargetOffset += _cachePrefixStream.Length;} 
                    checked{moveBlockSize -= _cachePrefixStream.Length;} 
                    Debug.Assert(moveBlockSize >=0);
                } 

                _blockManager.MoveData(moveBlockSourceOffset, moveBlockTargetOffset, moveBlockSize);
                checked{newPersistedSize += moveBlockSize;}
 
                // only after data on disk was moved it is safe to flush the cached prefix buffer
                if (_cachePrefixStream != null) 
                { 
                    // we need to seek and it is safe to do as we are not in the streaming mode
                    _blockManager.Stream.Seek(_offset, SeekOrigin.Begin); 

                    Debug.Assert(_cachePrefixStream.Length > 0); // should be taken care of by the constructor
                                                                                        // and PreSaveNotification
 
                    _cachePrefixStream.WriteToStream(_blockManager.Stream);
                    checked{newPersistedSize += _cachePrefixStream.Length;} 
 
                    // we can free the memory
                    _cachePrefixStream.Close(); 
                    _cachePrefixStream = null;
                }

 
                // and now we can save the SparseMemoryStream
                if (_sparseMemoryStreamSuffix  != null) 
                { 
                    if (_blockManager.Stream.Position != checked (_offset + _persistedSize))
                    { 
                        // we need to seek
                        _blockManager.Stream.Seek(_offset + _persistedSize, SeekOrigin.Begin);
                    }
                    _sparseMemoryStreamSuffix.WriteToStream(_blockManager.Stream); 
                    checked{newPersistedSize += _sparseMemoryStreamSuffix.Length;}
 
                    _sparseMemoryStreamSuffix.Close(); 
                    _sparseMemoryStreamSuffix  = null;
                } 

                _blockManager.Stream.Flush();

                // we are not shifted between on disk image and in memory image any more 
                _persistedOffset = _offset;
                _persistedSize = newPersistedSize; 
 
                Debug.Assert(newPersistedSize == _currentStreamLength);
 
                Debug.Assert(_cachePrefixStream == null); // we only expect this thing to be not null during Archive Save execution
                                                                                // after we are saved this field must be clear

                _dirtyFlag = false; 
                _dataChanged = false;
            } 
        } 

        //------------------------------------------------------ 
        //
        //  Protected Methods
        //
        //----------------------------------------------------- 
        /// 
        /// Dispose(bool) 
        ///  
        /// 
        /// We implement this because we want a consistent experience (essentially Flush our data) if the user chooses to 
        /// call Dispose() instead of Close().
        protected override void Dispose(bool disposing)
        {
            try 
            {
                if (disposing) 
                { 
                    //streams wrapping this stream shouldn't pass Dispose calls through
                    // it is responsibility of the BlockManager or LocalFileBlock (in case of Remove) to call 
                    // this dispose as appropriate (that is the reason why Flush isn't called here)

                    // multiple calls are fine - just ignore them
                    if (!_disposedFlag) 
                    {
                        if (_sparseMemoryStreamSuffix  != null) 
                        { 
                            _sparseMemoryStreamSuffix.Close();
                        } 

                        if (_cachePrefixStream != null)
                        {
                            _cachePrefixStream.Close(); 
                        }
                    } 
                } 
            }
            finally 
            {
                _sparseMemoryStreamSuffix  = null;
                _cachePrefixStream = null;
                _disposedFlag = true; 

                base.Dispose(disposing); 
            } 
        }
 
        /////////////////////////////
        // Private Methods
        /////////////////////////////
        private void CheckDisposed() 
        {
            if (_disposedFlag) 
            { 
                throw new ObjectDisposedException(null, SR.Get(SRID.ZipFileItemDisposed));
            } 
        }

        private ZipIOBlockManager _blockManager;
        private ZipIOLocalFileBlock _block;         // our owning block 

        private long _offset; 
        private long _persistedOffset; 
        private long _persistedSize;
 
        private SparseMemoryStream _cachePrefixStream;

        private bool  _dirtyFlag;
        private bool  _dataChanged; 

        //support for Stream methods 
        private bool _disposedFlag; 

        private long _currentStreamLength; 
        private long _currentStreamPosition;

        private SparseMemoryStream _sparseMemoryStreamSuffix;
 
        private const long _lowWaterMark = 0x19000;                 // we definately would like to keep everythuing under 100 KB in memory
        private const long _highWaterMark = 0xA00000;   // we would like to keep everything over 10 MB on disk 
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------------------------ 
//-------------   *** WARNING ***
//-------------    This file is part of a legally monitored development project.
//-------------    Do not check in changes to this project.  Do not raid bugs on this
//-------------    code in the main PS database.  Do not contact the owner of this 
//-------------    code directly.  Contact the legal team at �ZSLegal� for assistance.
//-------------   *** WARNING *** 
//----------------------------------------------------------------------------- 

//----------------------------------------------------------------------------- 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: 
//  This is an internal class that enables interactions with Zip archives 
//  for OPC scenarios
// 
// History:
//  11/19/2004: IgorBel: Initial creation.
//  06/21/2005: BruceMac: Add Write-time-streaming support
//----------------------------------------------------------------------------- 

using System; 
using System.IO; 
using System.Diagnostics;
using System.Text; 
using System.Collections;
using System.Runtime.Serialization;
using System.Windows;
using MS.Internal.IO.Packaging;         // for PackagingUtilities 
using MS.Internal.WindowsBase;
 
namespace MS.Internal.IO.Zip 
{
    internal class ZipIOFileItemStream :  Stream 
    {
        ////////////////////////////////////
        // Stream section
        ///////////////////////////////// 
        override public bool CanRead
        { 
            get 
            {
                return (!_disposedFlag) && (_blockManager.Stream.CanRead); 
            }
        }

        override public bool CanSeek 
        {
            get 
            { 
                return (!_disposedFlag) && (_blockManager.Stream.CanSeek);
            } 
        }

        override public bool CanWrite
        { 
            get
            { 
                return (!_disposedFlag) && (_blockManager.Stream.CanWrite); 
            }
        } 

        override public long Length
        {
            get 
            {
                CheckDisposed(); 
 
                return  _currentStreamLength;
            } 
        }

        override public long Position
        { 
            get
            { 
                CheckDisposed(); 
                return _currentStreamPosition;
            } 
            set
            {
                CheckDisposed();
                Seek(value, SeekOrigin.Begin); 
            }
        } 
 
        public override void SetLength(long newLength)
        { 
            CheckDisposed();

            Debug.Assert(_cachePrefixStream == null); // we only expect this thing to be not null during Archive Save execution
                                                                                // that would between PreSaveNotofication call and Save SaveStreaming 

            if (newLength < 0) 
            { 
                throw new ArgumentOutOfRangeException("newLength");
            } 

            if (_currentStreamLength != newLength)
            {
                _dirtyFlag = true; 
                _dataChanged = true;
 
                if  (newLength <= _persistedSize) 
                {
                    // the stream becomes smaller than our disk block, which means that 
                    // we can drop the in-memory-sparse-suffix
                    if (_sparseMemoryStreamSuffix  != null)
                    {
                        _sparseMemoryStreamSuffix.Close(); 
                        _sparseMemoryStreamSuffix  = null;
                    } 
                } 
                else
                { 
                    // we need to construct Sparse Memory stream if we do not have one yet
                    if (_sparseMemoryStreamSuffix  == null)
                    {
                        _sparseMemoryStreamSuffix  = new SparseMemoryStream(_lowWaterMark, _highWaterMark); 
                    }
 
                    // set size on the Sparse Memory Stream 
                    _sparseMemoryStreamSuffix.SetLength(newLength - _persistedSize); // no need for checked as it was verified above
                } 

                _currentStreamLength = newLength;

                // if stream was truncated to the point that our current position is beyond the end of the stream, 
                // we need to reset position so it is at the end of the stream
                if (_currentStreamLength < _currentStreamPosition) 
                    Seek(_currentStreamLength, SeekOrigin.Begin); 
            }
        } 

        override public long Seek(long offset, SeekOrigin origin)
        {
            CheckDisposed(); 

            Debug.Assert(_cachePrefixStream == null); // we only expect this thing to be not null during Archive Save execution 
                                                                                // that would between PreSaveNotofication call and Save SaveStreaming 

            long newStreamPosition = _currentStreamPosition; 

            if (origin ==SeekOrigin.Begin)
            {
                newStreamPosition = offset; 
            }
            else if  (origin == SeekOrigin.Current) 
            { 
                checked{newStreamPosition += offset;}
            } 
            else if  (origin == SeekOrigin.End)
            {
                checked{newStreamPosition = _currentStreamLength + offset;}
            } 
            else
            { 
                throw new ArgumentOutOfRangeException("origin"); 
            }
 
            if (newStreamPosition  < 0)
            {
                 throw new ArgumentException(SR.Get(SRID.SeekNegative));
            } 
            _currentStreamPosition = newStreamPosition;
 
            return _currentStreamPosition; 
        }
 
        override public int Read(byte[] buffer, int offset, int count)
        {
            CheckDisposed();
 
            PackagingUtilities.VerifyStreamReadArgs(this, buffer, offset, count);
 
            Debug.Assert(_cachePrefixStream == null); // we only expect this thing to be not null during Archive Save execution 
                                                      // that would between PreSaveNotofication call and Save SaveStreaming
 
            Debug.Assert(_currentStreamPosition >= 0);

            if (count == 0)
            { 
                return 0;
            } 
 
            if (_currentStreamLength <= _currentStreamPosition)
            { 
                // we are past the end of the stream so let's just return 0
                return 0;
            }
 
            int totalBytesRead;
            int diskBytesRead = 0; 
            int diskBytesToRead = 0; 
            long persistedTailSize = 0;
 
            int memoryBytesRead = 0;
            long newStreamPosition = _currentStreamPosition;

            checked 
            {
                // Try to satisfy request with the Read from the Disk 
                if  (newStreamPosition < _persistedSize) 
                {
                    // we have at least partial overlap between request and the data on disk 

                    //first let's get min between size of the stream's tail and the tail of the persisted chunk
                    // in some cases stream might be smaller
                    // e.g. _currentStreamLength  < _persistedSize, if let's say stream was truncated 
                    persistedTailSize = Math.Min(_currentStreamLength, _persistedSize) - newStreamPosition;
                    Debug.Assert(persistedTailSize > 0); 
 
                    // we also do not want to read more data than was requested by the user
                    diskBytesToRead = (int)Math.Min((long)count, persistedTailSize); // this is a safe cast as count has int type 
                    Debug.Assert(diskBytesToRead > 0);

                    // and now we can actually read it
                    _blockManager.Stream.Seek(_persistedOffset + newStreamPosition, SeekOrigin.Begin); 

                    // we are ready for getting fewer bytes than reqested 
                    diskBytesRead = _blockManager.Stream.Read(buffer, offset, diskBytesToRead); 

                    newStreamPosition += diskBytesRead; 
                     count -= diskBytesRead;
                     offset +=diskBytesRead;

                    if (diskBytesRead  <  diskBytesToRead) 
                    {
                        // we didn't everything that we hae asked for. In such case we shouldn't 
                        // try to get data from the   _sparseMemoryStreamSuffix 
                        _currentStreamPosition = newStreamPosition;
 
                        return diskBytesRead;
                    }
                }
 
                // check whether we need to get data from the memory Stream;
                if  ((_sparseMemoryStreamSuffix  != null) && (newStreamPosition + count > _persistedSize)) 
                { 
                    // we are either trying to finish the request partially satisfied by the
                    // on disk data  or  the read is entirely within the suffix 
                     _sparseMemoryStreamSuffix.Seek(newStreamPosition - _persistedSize, SeekOrigin.Begin);
                    memoryBytesRead = _sparseMemoryStreamSuffix.Read(buffer, offset, count);

                    newStreamPosition += memoryBytesRead; 
                }
 
                totalBytesRead = diskBytesRead + memoryBytesRead; 
            }
 
            _currentStreamPosition = newStreamPosition;
            return totalBytesRead ;
        }
 
        /// 
        /// Write 
        ///  
        /// 
        ///  
        /// 
        /// In streaming mode, write should accumulate data into the SparseMemoryStream.
        override public void Write(byte[] buffer, int offset, int count)
        { 
            CheckDisposed();
 
            PackagingUtilities.VerifyStreamWriteArgs(this, buffer, offset, count); 

            Debug.Assert(_cachePrefixStream == null); // we only expect this thing to be not null during Archive Save execution 
                                                      // that would between PreSaveNotofication call and Save SaveStreaming

            Debug.Assert(_currentStreamPosition >= 0);
 
            if (count == 0)
            { 
                return; 
            }
 
            int diskBytesToWrite = 0;

            _dirtyFlag = true;
            _dataChanged = true; 
            long newStreamPosition = _currentStreamPosition;
            checked 
            { 
                // Try to satisfy request with the Write to the Disk
                if  (newStreamPosition  < _persistedSize) 
                {
                    Debug.Assert(!_blockManager.Streaming);

                    // we have at least partial overlap between request and the data on disk 
                    _blockManager.Stream.Seek(_persistedOffset + newStreamPosition, SeekOrigin.Begin);
                    // Note on casting: 
                    //  It is safe to cast the result of Math.Min(count, _persistedSize - newStreamPosition)) 
                    //      from long to int since it cannot be bigger than count and count is int type
                    diskBytesToWrite = (int) (Math.Min(count, _persistedSize - newStreamPosition));  // this is a safe cast as count has int type 

                    _blockManager.Stream.Write(buffer, offset, diskBytesToWrite);
                    newStreamPosition += diskBytesToWrite;
                    count -= diskBytesToWrite; 
                    offset += diskBytesToWrite;
                } 
 
                // check whether we need to save data to the memory Stream;
                if  (newStreamPosition + count > _persistedSize) 
                {
                    if (_sparseMemoryStreamSuffix  == null)
                    {
                         _sparseMemoryStreamSuffix  = new SparseMemoryStream(_lowWaterMark, _highWaterMark); 
                    }
 
                    _sparseMemoryStreamSuffix.Seek(newStreamPosition - _persistedSize, SeekOrigin.Begin); 

                    _sparseMemoryStreamSuffix.Write(buffer, offset, count); 
                    newStreamPosition += count;
                }

                _currentStreamPosition = newStreamPosition; 
                _currentStreamLength = Math.Max(_currentStreamLength, _currentStreamPosition);
            } 
            return; 
        }
 
        override public void Flush()
        {
            CheckDisposed();
 
            // tell the BlockManager that the caller called Flush on us. Block manager will process this
            // and possibly call us back on Save or SaveStreaming 
            _blockManager.SaveStream(_block, false);  // second parameter is a closing indicator 
        }
 
        /////////////////////////////
        // Internal Constructor
        /////////////////////////////
        internal  ZipIOFileItemStream(ZipIOBlockManager blockManager,   // blockManager is only needed 
                                                                        // to pass through to it Flush requests
                                      ZipIOLocalFileBlock block,        // our owning block - needed for Streaming scenarios 
                                      long persistedOffset,             // to map to the stream 
                                      long persistedSize)               // to map to the stream )
        { 
            Debug.Assert(blockManager != null);
            Debug.Assert(persistedOffset >=0);
            Debug.Assert(persistedSize >= 0);
            Debug.Assert(block != null); 

            _persistedOffset = persistedOffset; 
            _offset = persistedOffset; 
            _persistedSize = persistedSize;
 
            _blockManager = blockManager;
            _block = block;

            _currentStreamLength = persistedSize; 
        }
 
        ///////////////////////////// 
        // Internal Methods for the LocalFileBlock to call in order to know Dirty status and the new size
        ///////////////////////////// 
        internal PreSaveNotificationScanControlInstruction PreSaveNotification(long offset, long size)
        {
            return ZipIOBlockManager.CommonPreSaveNotificationHandler(
                    _blockManager.Stream, 
                    offset,
                    size, 
                    _persistedOffset, 
                    Math.Min(_persistedSize, _currentStreamLength),  // in case the stream is smaller then our persisted block on
                                                                            // disk, there is no need to preserve the meaningless persisted suffix 
                    ref _cachePrefixStream);
        }

        internal bool DirtyFlag 
        {
            get 
            { 
                return _dirtyFlag;
            } 
        }

        internal bool DataChanged
        { 
            get
            { 
                return _dataChanged; 
            }
        } 

        internal long Offset
        {
            get 
            {
                return _offset; 
            } 
        }
 
        internal void Move(long shiftSize)
        {
            CheckDisposed();
            if (shiftSize != 0) 
            {
                checked{_offset +=shiftSize;} 
                _dirtyFlag = true; 
                Debug.Assert(_offset >=0);
            } 
        }

        /// 
        /// Streaming-specific variant of Save() 
        /// 
        /// Writes current data to the underlying stream. 
        /// Assumes the stream is in the correct place. 
        internal void SaveStreaming()
        { 
            CheckDisposed();

            Debug.Assert(_cachePrefixStream == null); // _cachePrefixStream must not be used in streaming cases at all
 
            Debug.Assert(_blockManager.Streaming);
 
            if (_dirtyFlag) 
            {
                // in streaming cases all the data collected in the _sparseMemoryStreamSuffix 
                // and now we can save the SparseMemoryStream
                if (_sparseMemoryStreamSuffix  != null)
                {
                    _sparseMemoryStreamSuffix.WriteToStream(_blockManager.Stream); 

                    // update so that subsequent MemoryStreams will know where they begin 
                    checked{_persistedSize += _sparseMemoryStreamSuffix.Length;} 
                    _sparseMemoryStreamSuffix.Close();
                    _sparseMemoryStreamSuffix  = null; 
                }

                _dirtyFlag = false;
                _dataChanged = false; 
            }
        } 
 
        /// 
        /// Save - called by the BlockManager to cause us to Flush to the underlying stream 
        /// 
        internal void Save()
        {
            CheckDisposed(); 
            Debug.Assert(!_blockManager.Streaming);
 
            if(_dirtyFlag) 
            {
                // we need to move the whole persisted block to the new position 
                long moveBlockSourceOffset = _persistedOffset;

                // in case the stream is smaller then our persisted block on disk there is
                // no need to move meaningless persisted suffix 
                long moveBlockSize = Math.Min(_persistedSize, _currentStreamLength);
 
                long moveBlockTargetOffset = _offset; 

                long newPersistedSize = 0; 

                if (_cachePrefixStream != null)
                {
                    // if we have something in cache we only should move whatever isn't cached 
                    checked{moveBlockSourceOffset += _cachePrefixStream.Length;}
                    checked{moveBlockTargetOffset += _cachePrefixStream.Length;} 
                    checked{moveBlockSize -= _cachePrefixStream.Length;} 
                    Debug.Assert(moveBlockSize >=0);
                } 

                _blockManager.MoveData(moveBlockSourceOffset, moveBlockTargetOffset, moveBlockSize);
                checked{newPersistedSize += moveBlockSize;}
 
                // only after data on disk was moved it is safe to flush the cached prefix buffer
                if (_cachePrefixStream != null) 
                { 
                    // we need to seek and it is safe to do as we are not in the streaming mode
                    _blockManager.Stream.Seek(_offset, SeekOrigin.Begin); 

                    Debug.Assert(_cachePrefixStream.Length > 0); // should be taken care of by the constructor
                                                                                        // and PreSaveNotification
 
                    _cachePrefixStream.WriteToStream(_blockManager.Stream);
                    checked{newPersistedSize += _cachePrefixStream.Length;} 
 
                    // we can free the memory
                    _cachePrefixStream.Close(); 
                    _cachePrefixStream = null;
                }

 
                // and now we can save the SparseMemoryStream
                if (_sparseMemoryStreamSuffix  != null) 
                { 
                    if (_blockManager.Stream.Position != checked (_offset + _persistedSize))
                    { 
                        // we need to seek
                        _blockManager.Stream.Seek(_offset + _persistedSize, SeekOrigin.Begin);
                    }
                    _sparseMemoryStreamSuffix.WriteToStream(_blockManager.Stream); 
                    checked{newPersistedSize += _sparseMemoryStreamSuffix.Length;}
 
                    _sparseMemoryStreamSuffix.Close(); 
                    _sparseMemoryStreamSuffix  = null;
                } 

                _blockManager.Stream.Flush();

                // we are not shifted between on disk image and in memory image any more 
                _persistedOffset = _offset;
                _persistedSize = newPersistedSize; 
 
                Debug.Assert(newPersistedSize == _currentStreamLength);
 
                Debug.Assert(_cachePrefixStream == null); // we only expect this thing to be not null during Archive Save execution
                                                                                // after we are saved this field must be clear

                _dirtyFlag = false; 
                _dataChanged = false;
            } 
        } 

        //------------------------------------------------------ 
        //
        //  Protected Methods
        //
        //----------------------------------------------------- 
        /// 
        /// Dispose(bool) 
        ///  
        /// 
        /// We implement this because we want a consistent experience (essentially Flush our data) if the user chooses to 
        /// call Dispose() instead of Close().
        protected override void Dispose(bool disposing)
        {
            try 
            {
                if (disposing) 
                { 
                    //streams wrapping this stream shouldn't pass Dispose calls through
                    // it is responsibility of the BlockManager or LocalFileBlock (in case of Remove) to call 
                    // this dispose as appropriate (that is the reason why Flush isn't called here)

                    // multiple calls are fine - just ignore them
                    if (!_disposedFlag) 
                    {
                        if (_sparseMemoryStreamSuffix  != null) 
                        { 
                            _sparseMemoryStreamSuffix.Close();
                        } 

                        if (_cachePrefixStream != null)
                        {
                            _cachePrefixStream.Close(); 
                        }
                    } 
                } 
            }
            finally 
            {
                _sparseMemoryStreamSuffix  = null;
                _cachePrefixStream = null;
                _disposedFlag = true; 

                base.Dispose(disposing); 
            } 
        }
 
        /////////////////////////////
        // Private Methods
        /////////////////////////////
        private void CheckDisposed() 
        {
            if (_disposedFlag) 
            { 
                throw new ObjectDisposedException(null, SR.Get(SRID.ZipFileItemDisposed));
            } 
        }

        private ZipIOBlockManager _blockManager;
        private ZipIOLocalFileBlock _block;         // our owning block 

        private long _offset; 
        private long _persistedOffset; 
        private long _persistedSize;
 
        private SparseMemoryStream _cachePrefixStream;

        private bool  _dirtyFlag;
        private bool  _dataChanged; 

        //support for Stream methods 
        private bool _disposedFlag; 

        private long _currentStreamLength; 
        private long _currentStreamPosition;

        private SparseMemoryStream _sparseMemoryStreamSuffix;
 
        private const long _lowWaterMark = 0x19000;                 // we definately would like to keep everythuing under 100 KB in memory
        private const long _highWaterMark = 0xA00000;   // we would like to keep everything over 10 MB on disk 
    } 
}

// 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