ZipIOFileItemStream.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 / Zip / ZipIOFileItemStream.cs / 1 / 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 

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 

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