SparseMemoryStream.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 / Packaging / SparseMemoryStream.cs / 1305600 / SparseMemoryStream.cs

                            //------------------------------------------------------------------------------ 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: 
//  This is an internal class that is build around ArrayList of Memory streams to enable really large (63 bit size) 
//  virtual streams.
// 
// History:
//  05/25/2005: IgorBel: Initial creation.
//  11/08/2005: BruceMac: Remove all Zip references and move file to Packaging namespace
// 
//-----------------------------------------------------------------------------
 
using System; 
using System.Diagnostics;
using System.Collections.Generic; 
using System.IO;
using System.IO.IsolatedStorage;
using System.Windows;
using MS.Internal.WindowsBase; 

namespace MS.Internal.IO.Packaging 
{ 
    internal class SparseMemoryStream:  Stream
    { 
        //-----------------------------------------------------
        //
        //  Public Methods
        // 
        //-----------------------------------------------------
        override public bool CanRead 
        { 
            get
            { 
                return (!_disposedFlag);
            }
        }
 
        override public bool CanSeek
        { 
            get 
            {
                return (!_disposedFlag); 
            }
        }

        override public bool CanWrite 
        {
            get 
            { 
                return (!_disposedFlag);
            } 
        }

        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();

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

#if DEBUG 
    DebugAssertConsistentArrayStructure();
#endif

            if (_currentStreamLength != newLength) 
            {
                if (_isolatedStorageMode) 
                { 
                    _isolatedStorageStream.SetLength(newLength);
                } 
                else
                {
                    // if length become smaller , we might be able to close some of memoryStreams that we keep around
                    if (_currentStreamLength > newLength) 
                    {
                        int removeIndex = _memoryStreamList.BinarySearch(GetSearchBlockForOffset(newLength)); 
 
                        // the new end of the stream does not fall into any existing blocks
                        if (removeIndex < 0) 
                            // ~removeIndex represents the place at which we would insert the new block for write
                            removeIndex = ~removeIndex;
                        else
                        { 
                            // we need to truncate the MemoryStream
                            MemoryStreamBlock memStreamBlock = _memoryStreamList[removeIndex]; 
                            checked 
                            {
                                long temp = newLength - memStreamBlock.Offset; 
                                if (temp > 0)
                                {
                                    memStreamBlock.Stream.SetLength(temp);
                                    ++removeIndex; 
                                }
                                // else fall through and remove below 
                            } 
                        }
 
                        for (int i = removeIndex; i < _memoryStreamList.Count; ++i)
                        {
                            _memoryStreamList[i].Stream.Close();    // we need to carefully close the memoryStreams so they properly report the memory usage
                        } 

                        _memoryStreamList.RemoveRange(removeIndex, _memoryStreamList.Count - removeIndex); 
                    } 
                }
 
                _currentStreamLength = newLength;
                if (_currentStreamPosition > _currentStreamLength)
                    _currentStreamPosition = _currentStreamLength;
            } 

            // this can potentially affect memory consumption 
            SwitchModeIfNecessary(); 

#if DEBUG 
    DebugAssertConsistentArrayStructure();
#endif
        }
 
        override public long Seek(long offset, SeekOrigin origin)
        { 
            CheckDisposed(); 
            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(_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; 
            }
 
            // No need to use checked{} since _currentStreamLength > _currentStreamPosition
            int bytesToRead = (int) Math.Min((long)count, _currentStreamLength - _currentStreamPosition);

            checked 
            {
                Debug.Assert(bytesToRead > 0); 
 
                int bytesRead;  // how much data we actually were able to read
                if (_isolatedStorageMode) 
                {
                    _isolatedStorageStream.Seek(_currentStreamPosition, SeekOrigin.Begin);
                    bytesRead = _isolatedStorageStream.Read(buffer, offset, bytesToRead);
                } 
                else
                { 
                    // let's reset data to 0 first, so that gaps will be filled with 0s 
                    // this is required for consistent behavior between the read calls used by the CRC Calculator
                    // and the WriteToStream calls used by the Flush/Save routines 
                    Array.Clear(buffer,offset,bytesToRead);

                    int index = _memoryStreamList.BinarySearch(GetSearchBlockForOffset(_currentStreamPosition));
                    if (index < 0) // the head of new write block does not overlap with any existing blocks 
                        // ~startIndex represents the insertion position
                        index = ~index; 
 
                    for ( ; index < _memoryStreamList.Count; ++index)
                    { 
                        MemoryStreamBlock memStreamBlock = _memoryStreamList[index];
                        long overlapBlockOffset;
                        long overlapBlockSize;
                        // let's check for overlap and fill up appropriate data 
                        PackagingUtilities.CalculateOverlap(memStreamBlock.Offset, (int)memStreamBlock.Stream.Length,
                                                _currentStreamPosition, bytesToRead, 
                                                out overlapBlockOffset, out overlapBlockSize); 
                        if (overlapBlockSize > 0)
                        { 
                            // we got an overlap let's copy data over to the target buffer
                            // _currentStreamPosition is not updated in this foreach loop; it will be updated later
                            Array.Copy(memStreamBlock.Stream.GetBuffer(), (int)(overlapBlockOffset - memStreamBlock.Offset),
                                            buffer, (int)(offset + overlapBlockOffset - _currentStreamPosition), 
                                            (int)overlapBlockSize);
                        } 
                        else 
                            break;
                    } 
                    // for memory stream case we get as much as we asked for
                    bytesRead = bytesToRead;
                }
 
                _currentStreamPosition += bytesRead;
 
                return bytesRead; 
            }
        } 

        override public void Write(byte[] buffer, int offset, int count)
        {
            CheckDisposed(); 
#if DEBUG
    DebugAssertConsistentArrayStructure(); 
#endif 

            PackagingUtilities.VerifyStreamWriteArgs(this, buffer, offset, count); 

            Debug.Assert(_currentStreamPosition >= 0);

            if (count == 0) 
            {
                return; 
            } 

            checked 
            {
                if (_isolatedStorageMode)
                {
                    _isolatedStorageStream.Seek(_currentStreamPosition, SeekOrigin.Begin); 
                    _isolatedStorageStream.Write(buffer, offset, count);
                    _currentStreamPosition += count; 
                } 
                else
                { 
                    WriteAndCollapseBlocks(buffer, offset, count);
                }
                _currentStreamLength = Math.Max(_currentStreamLength, _currentStreamPosition);
            } 

             // this can potentially affect memory consumption 
            SwitchModeIfNecessary(); 
#if DEBUG
    DebugAssertConsistentArrayStructure(); 
#endif
        }

        override public void Flush() 
        {
            CheckDisposed(); 
        } 

        //------------------------------------------------------ 
        //
        //  Internal Methods
        //
        //----------------------------------------------------- 
        /// 
        /// WriteToStream(Stream stream) writes the sparse Memory stream to the Stream provided as parameter 
        /// starting at the current position in the stream 
        /// 
        internal void WriteToStream(Stream stream) 
        {
            checked
            {
                if (_isolatedStorageMode) 
                {
                    _isolatedStorageStream.Seek(0, SeekOrigin.Begin); 
                    PackagingUtilities.CopyStream(_isolatedStorageStream, stream, 
                                            Int64.MaxValue/*bytes to copy*/,
                                            0x80000 /*512K buffer size */); 
                 }
                else
                {
                    CopyMemoryBlocksToStream(stream); 
                }
            } 
        } 

        ///////////////////////////// 
        // Internal Constructor
        /////////////////////////////

        ///  
        /// SparseMemoryStream constructor
        ///  
        ///  
        ///     if we consume less memory than lowWaterMark implementation will use arraList of MemoryStreams
        ///     (vaue 0 will disable Memory Stream based mode) 
        /// 
        /// 
        ///      if we consume more memory than highWaterMark implementation will use the isolatedStorage
        ///      (vaue Int64.MaxVaue will disable isolated storage mode ) 
        /// 
        internal  SparseMemoryStream( 
                                        long lowWaterMark, 
                                        long highWaterMark): this(lowWaterMark, highWaterMark, true)
        { 
        }

        /// 
        /// SparseMemoryStream constructor 
        /// 
        ///  
        ///     if we consume less memory than lowWaterMark implementation will use arraList of MemoryStreams 
        ///     (vaue 0 will disable Memory Stream based mode)
        ///  
        /// 
        ///      if we consume more memory than highWaterMark implementation will use the isolatedStorage
        ///      (vaue Int64.MaxVaue will disable isolated storage mode )
        ///  
        /// 
        ///      There are 2 basic usages for the sparse memory stream. We use it as a buffering mechanism in ZIP IO, 
        ///       in which case it is acceptable to assume that gaps between blocks are 0s. In the other scenario 
        ///       (Encryption Stream ) we use it as a caching mechanism; in this case we ca not assume any values for the
        ///       that data located between blocks, so we shouldn't merge them (if the gap is small and doesn't justify an 
        ///       overhead of the extra block record)
        /// 
        internal  SparseMemoryStream(
                                        long lowWaterMark, 
                                        long highWaterMark,
                                        bool autoCloseSmallBlockGaps) 
        { 
            Invariant.Assert(lowWaterMark >=0 && highWaterMark >=0); // both of them must be positive or 0
            Invariant.Assert(lowWaterMark < highWaterMark); // low water mark must below high water mark 
            Invariant.Assert(lowWaterMark <= Int32.MaxValue);  // low water mark must fit single memory stream 2G

            _memoryStreamList = new List(5);
            _lowWaterMark = lowWaterMark; 
            _highWaterMark = highWaterMark;
            _autoCloseSmallBlockGaps = autoCloseSmallBlockGaps; 
        } 

        //------------------------------------------------------ 
        //
        //  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 Dipose 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)
                    { 
                        // go through all the Memory Streams and close them 
                        foreach (MemoryStreamBlock memStreamBlock in _memoryStreamList)
                        { 
                            // this will report the appropriate Memory usage back to the  ITrackingMemoryStreamFactory
                            memStreamBlock.Stream.Close();
                        }
 
                        // clean up isolated storage resources if in use
                        if (_isolatedStorageStream != null) 
                        { 
                            // can only rely on _isolatedStorageStream behaving correctly if we are not in our finalizer
                            _isolatedStorageStream.Close(); 
                        }
                    }
                }
            } 
            finally
            { 
                _disposedFlag = true; 
                _isolatedStorageStream = null;
                _memoryStreamList = null; 

                base.Dispose(disposing);
            }
        } 

        ///  
        /// Expose collection for use by clients that use this as 
        /// a caching mechanism.
        ///  
        /// Cannot be IList because clients use
        /// BinarySearch() method.
        internal List MemoryBlockCollection
        { 
            get
            { 
                CheckDisposed(); 
                return _memoryStreamList;
            } 
        }

        internal long MemoryConsumption
        { 
            get
            { 
                CheckDisposed(); 
                return _trackingMemoryStreamFactory.CurrentMemoryConsumption;
            } 
        }

        //-----------------------------------------------------
        // 
        //  Private Methods
        // 
        //------------------------------------------------------ 
        private void CheckDisposed()
        { 
            if (_disposedFlag)
            {
                throw new ObjectDisposedException(SR.Get(SRID.StreamObjectDisposed));
            } 
        }
 
        private MemoryStreamBlock GetSearchBlockForOffset(long offset) 
        {
            if (_searchBlock == null) 
                _searchBlock = new MemoryStreamBlock(null, offset);
            else
                _searchBlock.Offset = offset;
            return _searchBlock; 
        }
 
        private bool CanCollapseWithPreviousBlock(MemoryStreamBlock memStreamBlock, 
                                                        long offset,
                                                        long length) 
        {
            // There was an explicit request by the client not to merge near- by blocks
            if (!_autoCloseSmallBlockGaps || memStreamBlock == null)
            { 
                return false;
            } 
 
            checked
            { 
                long gap = offset - (memStreamBlock.Offset + memStreamBlock.Stream.Length);

                Debug.Assert(gap >= 0);
 
                // if gap between them is smaller then a fix overhead of an extra block
                // and these are not too big to not fit in the MemoryStream Int32.MaxValue 
                if (gap <= _fixBlockInMemoryOverhead 
                    && gap + length + memStreamBlock.Stream.Length <= Int32.MaxValue)
                { 
                    return true;
                }
            }
 
            return false;
        } 
 
        private void WriteAndCollapseBlocks(byte[] buffer,
                                                int offset, 
                                                int count)
        {
            int index = _memoryStreamList.BinarySearch(GetSearchBlockForOffset(_currentStreamPosition));
            bool writeDone = false; 
            MemoryStreamBlock memStreamBlock = null;
            MemoryStreamBlock prevMemStreamBlock = null; 
 
            checked
            { 
                if (index < 0) // the head of new write block does not overlap with any existing blocks
                {
                    // ~startIndex represents the place at which we would insert the new block for write
                    index = ~index; 
                    if (index != 0)    // Get the previous block of the new write block
                        prevMemStreamBlock = _memoryStreamList[index - 1]; 
 
                    // If the write request is close enough to the previous block and if the collapsing is allowed
                    if (CanCollapseWithPreviousBlock(prevMemStreamBlock, _currentStreamPosition, (long) count)) 
                    {
                        // write out any intervening zero's
                        prevMemStreamBlock.Stream.Seek(0, SeekOrigin.End);
                        SkipWrite(prevMemStreamBlock.Stream, prevMemStreamBlock.EndOffset, _currentStreamPosition); 
                        prevMemStreamBlock.Stream.Write(buffer, offset, count);
                        writeDone = true; 
                    } 
                }
                else 
                {
                    prevMemStreamBlock = _memoryStreamList[index];

                    // Write the requested bytes to the existing block if possible 
                    if (prevMemStreamBlock.Stream.Length + count <= Int32.MaxValue) // Make sure there is enough space to append
                    { 
                        prevMemStreamBlock.Stream.Seek(_currentStreamPosition - prevMemStreamBlock.Offset, SeekOrigin.Begin); 
                        prevMemStreamBlock.Stream.Write(buffer, offset, count);
                        writeDone = true; 
                        ++index;
                    }
                    else    // Not enough space
                    { 
                        // There is overlap but we will created a new block for the write request; need to truncate the prev block
                        prevMemStreamBlock.Stream.SetLength(_currentStreamPosition - memStreamBlock.Offset); 
                        Debug.Assert(prevMemStreamBlock.Stream.Length > 0); 
                    }
                } 

                if (!writeDone)    // create a new block for the write request
                {
                    prevMemStreamBlock = ConstructMemoryStreamFromWriteRequest(buffer, _currentStreamPosition, count, offset); 
                    Debug.Assert(prevMemStreamBlock.Stream.Length > 0);
                    _memoryStreamList.Insert(index, prevMemStreamBlock); 
                    ++index; 
                }
 
                _currentStreamPosition += count;   // Update the stream position since the write request is satisfied by this point

                int i;
                // Close and remove all completely-overlapping blocks 
                for (i = index; i < _memoryStreamList.Count; ++i)
                { 
                    if (_memoryStreamList[i].EndOffset > _currentStreamPosition) 
                        break;
 
                    _memoryStreamList[i].Stream.Close();    // we need to carefully close the memoryStreams so they properly report the memory usage
                }
                if (i - index > 0)
                    _memoryStreamList.RemoveRange(index, i - index); 

                /////////////////////////////////////////// 
                // Check if the tail of the new write block needs to be collapsed with the following block 
                ///////////////////////////////////////////
 
                long blockOffset = -1;
                if (index < _memoryStreamList.Count)   // Get the next block of the new write block
                {
                    memStreamBlock = _memoryStreamList[index]; 
                    blockOffset = _currentStreamPosition - memStreamBlock.Offset;
                } 
                else 
                    memStreamBlock = null;  // No next block to check
 
                if (blockOffset <= 0)   // No overlapping
                {
                    // Check if we should collapse the block
                    if (memStreamBlock != null 
                        && (CanCollapseWithPreviousBlock(prevMemStreamBlock, memStreamBlock.Offset, memStreamBlock.Stream.Length)))
                    { 
                        // remove the following block  memStreamBlock 
                        _memoryStreamList.RemoveAt(index);
 
                        // write out any intervening zero's
                        prevMemStreamBlock.Stream.Seek(0, SeekOrigin.End);
                        SkipWrite(prevMemStreamBlock.Stream, _currentStreamPosition, memStreamBlock.Offset);
                        prevMemStreamBlock.Stream.Write(memStreamBlock.Stream.GetBuffer(), 0, (int) memStreamBlock.Stream.Length); 
                    }
                } 
                else    // Overlapping 
                {
                    _memoryStreamList.RemoveAt(index); 
                    // Memory stream length or buffer offset cannot be bigger than Int32.MaxValue
                    int leftoverSize = (int) (memStreamBlock.Stream.Length - blockOffset);

                    if (prevMemStreamBlock.Stream.Length + leftoverSize <= Int32.MaxValue) 
                    {
                        prevMemStreamBlock.Stream.Seek(0, SeekOrigin.End); 
                        prevMemStreamBlock.Stream.Write(memStreamBlock.Stream.GetBuffer(), (int) blockOffset, leftoverSize); 
                    }
                    else 
                    {
                        memStreamBlock = ConstructMemoryStreamFromWriteRequest(memStreamBlock.Stream.GetBuffer(),
                                                                _currentStreamPosition,
                                                                leftoverSize, 
                                                                (int) blockOffset);
                        Debug.Assert(memStreamBlock.Stream.Length > 0); 
                        _memoryStreamList.Insert(index, memStreamBlock); 
                    }
                } 
            }
        }

        private MemoryStreamBlock ConstructMemoryStreamFromWriteRequest( 
                                                                                byte[] buffer,  // data buffer to be used for the new Memory Stream Block
                                                                                long writeRequestOffset, 
                                                                                int  writeRequestSize, 
                                                                                int  bufferOffset)
        { 
            Debug.Assert(!_isolatedStorageMode);
            MemoryStreamBlock newMemStreamBlock  = new MemoryStreamBlock
                                                    (_trackingMemoryStreamFactory.Create(writeRequestSize),
                                                    writeRequestOffset); 

            newMemStreamBlock.Stream.Seek(0,SeekOrigin.Begin); 
            newMemStreamBlock.Stream.Write(buffer,bufferOffset,writeRequestSize); 

            return newMemStreamBlock; 
        }

        private void SwitchModeIfNecessary()
        { 
            if (_isolatedStorageMode)
            { 
                Debug.Assert(_memoryStreamList.Count ==0); // it must be empty in isolated storage mode 

                // if we are in isolated storage mode we need to check the Low Water Mark crossing 
                if (_isolatedStorageStream.Length < _lowWaterMark)
                {
                    if (_isolatedStorageStream.Length > 0)
                    { 
                        //build memory stream
                        MemoryStreamBlock newMemStreamBlock  = new MemoryStreamBlock 
                                                    (_trackingMemoryStreamFactory.Create((int)_isolatedStorageStream.Length), 
                                                    0);
 
                        //copy data from iso storage to memory stream
                        _isolatedStorageStream.Seek(0, SeekOrigin.Begin);
                        newMemStreamBlock.Stream.Seek(0, SeekOrigin.Begin);
                        PackagingUtilities.CopyStream(_isolatedStorageStream, newMemStreamBlock.Stream, 
                                                Int64.MaxValue/*bytes to copy*/,
                                                0x80000 /*512K buffer size */); 
 
                        Debug.Assert(newMemStreamBlock.Stream.Length > 0);
                        _memoryStreamList.Add(newMemStreamBlock); 
                    }

                    //switch mode
                     _isolatedStorageMode = false; 

                    // release isolated storage disk space by setting its length to 0 
                    // This way we don't have to re-open the isolated storage again if the memory consumption 
                    //  goes above the High Water Mark
                    _isolatedStorageStream.SetLength(0); 
                    _isolatedStorageStream.Flush();
                }
            }
            else 
            {
                // if we are in Memory Stream mode we need to check the High Water Mark crossing 
                if (_trackingMemoryStreamFactory.CurrentMemoryConsumption > _highWaterMark) 
                {
                    //copy data to isolated storage 
                    EnsureIsolatedStoreStream();
                    CopyMemoryBlocksToStream(_isolatedStorageStream);

                    //switch mode 
                    _isolatedStorageMode = true;
 
                    //release memory stream resources 
                    foreach(MemoryStreamBlock memStreamBlock in _memoryStreamList)
                    { 
                        // this will report the appropriate Memory usage back to the  ITrackingMemoryStreamFactory
                        memStreamBlock.Stream.Close();
                    }
                    _memoryStreamList.Clear(); 
                }
            } 
        } 

        ///  
        /// CopyMemoryBlocksToStream - makes the stream reflect what is in memory
        /// 
        /// Stream that is modified to be contain the same data as
        /// that logically represented by the memory blocks.  The stream length is modified as 
        /// necessary, and any "gaps" are filled with zero's.
        /// This function copies Memory Stream Array List to the target stream. 
        /// It is used in 2 cases: 
        /// 1. When we need to switch to isolated storage mode
        /// 2. When WriteToStream function is called 
        private void CopyMemoryBlocksToStream(Stream targetStream)
        {
            Debug.Assert(!_isolatedStorageMode);
            checked 
            {
                // emit all memory blocks 
                long trackingPosition = 0; 
                foreach(MemoryStreamBlock memStreamBlock in _memoryStreamList)
                { 
                    // write out any intervening zero's
                    trackingPosition = SkipWrite(targetStream, trackingPosition, memStreamBlock.Offset);

                    // write the memory block data 
                    targetStream.Write(memStreamBlock.Stream.GetBuffer(), 0, (int)memStreamBlock.Stream.Length);
                    trackingPosition += memStreamBlock.Stream.Length; 
                } 

                // emit any trailing zero's that could result from a SetLength with no corresponding Write() 
                if (trackingPosition < _currentStreamLength)
                    trackingPosition = SkipWrite(targetStream, trackingPosition, _currentStreamLength);

                Debug.Assert(trackingPosition == _currentStreamLength); 
            }
            targetStream.Flush(); 
        } 

        ///  
        /// Writes out zero's to the targetStream from currentPos to the current offset
        /// 
        /// 
        ///  
        /// 
        /// writes from the current stream position 
        /// offset 
        private long SkipWrite(Stream targetStream, long currentPos, long offset)
        { 
            long toSkip = offset - currentPos;
            Debug.Assert(toSkip >= 0);

            if (toSkip > 0) 
            {
                // we must write out 0s so that the behavior is consistent between Read calls used by the CRC calculations 
                // and the WriteToStream calls used by the Flush/Save logic 
                byte[] zeroBytesBuf = new byte[Math.Min(0x80000, toSkip)]; // 512K chunks max
                while (toSkip > 0) 
                {
                    int bytes = (int)Math.Min(toSkip, zeroBytesBuf.Length);
                    targetStream.Write(zeroBytesBuf, 0, bytes);
                    toSkip -= bytes; 
                }
            } 
 
            return offset;
        } 

#if DEBUG
        private void DebugAssertConsistentArrayStructure()
        { 
            if (_memoryStreamList != null)
            { 
                long testTrackingPosition = 0; 
                foreach(MemoryStreamBlock memStreamBlock in _memoryStreamList)
                { 
                    Debug.Assert(testTrackingPosition  <= memStreamBlock.Offset);
                    testTrackingPosition  = memStreamBlock.Offset + memStreamBlock.Stream.Length;
                }
 
                Debug.Assert(testTrackingPosition <= _currentStreamLength);
            } 
        } 
#endif
 
        private void EnsureIsolatedStoreStream()
        {
            if (_isolatedStorageStream == null)
            { 
                _isolatedStorageStream = PackagingUtilities.CreateUserScopedIsolatedStorageFileStreamWithRandomName(
                    3, out _isolatedStorageStreamFileName); 
            } 
        }
 
        //-----------------------------------------------------
        //
        //  Private Fields
        // 
        //-----------------------------------------------------
        //we use this class to track total memory consumed by the Memory streams that we are using 
        private TrackingMemoryStreamFactory _trackingMemoryStreamFactory = new TrackingMemoryStreamFactory(); 

        private string _isolatedStorageStreamFileName; 
        private Stream _isolatedStorageStream;
        private const int _fixBlockInMemoryOverhead = 100; // If the gap between blocks is smaller than this
                                    // threshold, it is not worth keeping them sperate due to overhead
                                    // This value is used to determine if blocks need to be collapsed 

        //support for Stream methods 
        private bool _disposedFlag; 

        private bool _isolatedStorageMode; 

        private long _currentStreamLength;
        private long _currentStreamPosition;
 
        private List _memoryStreamList;  // list of memory streams for buffering data
                                                        // it contains non-contiguous blocks of MemoryStreams which represents a whole stream 
                                                        // Memory Streams in Array must not overlap 
                                                        // This list is also maintained in offset order
        private MemoryStreamBlock _searchBlock; 

        private long _lowWaterMark;
        private long _highWaterMark;
 
        private bool _autoCloseSmallBlockGaps;
    } 
 
    internal class MemoryStreamBlock : IComparable
    { 
        internal MemoryStreamBlock(MemoryStream stream, long offset)
        {
            Debug.Assert(offset >=0);
 
            _stream = stream;
            _offset = offset; 
        } 

        internal MemoryStream Stream 
        {
            get
            {
                return _stream; 
            }
        } 
 
        internal long Offset
        { 
            get
            {
                return _offset;
            } 
            set
            { 
               Debug.Assert(value >= 0); 

                _offset = value; 
            }
        }

        internal long EndOffset 
        {
            get 
            { 
                checked
                { 
                    return _offset + (_stream == null ? 0 : _stream.Length);
                }
            }
        } 

        int IComparable.CompareTo(MemoryStreamBlock other) 
        { 
            if (other == null)
                return 1; 

            if (_offset == other.Offset)
                return 0;
            else if (_offset > other.Offset) 
            {
                if (_offset < other.EndOffset) 
                    return 0; 
                else
                    return 1; 
            }
            else
            {
                if (other.Offset < EndOffset) 
                    return 0;
                else 
                    return -1; 
            }
        } 

        private MemoryStream _stream;
        private long _offset;
    } 
}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------------------------ 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: 
//  This is an internal class that is build around ArrayList of Memory streams to enable really large (63 bit size) 
//  virtual streams.
// 
// History:
//  05/25/2005: IgorBel: Initial creation.
//  11/08/2005: BruceMac: Remove all Zip references and move file to Packaging namespace
// 
//-----------------------------------------------------------------------------
 
using System; 
using System.Diagnostics;
using System.Collections.Generic; 
using System.IO;
using System.IO.IsolatedStorage;
using System.Windows;
using MS.Internal.WindowsBase; 

namespace MS.Internal.IO.Packaging 
{ 
    internal class SparseMemoryStream:  Stream
    { 
        //-----------------------------------------------------
        //
        //  Public Methods
        // 
        //-----------------------------------------------------
        override public bool CanRead 
        { 
            get
            { 
                return (!_disposedFlag);
            }
        }
 
        override public bool CanSeek
        { 
            get 
            {
                return (!_disposedFlag); 
            }
        }

        override public bool CanWrite 
        {
            get 
            { 
                return (!_disposedFlag);
            } 
        }

        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();

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

#if DEBUG 
    DebugAssertConsistentArrayStructure();
#endif

            if (_currentStreamLength != newLength) 
            {
                if (_isolatedStorageMode) 
                { 
                    _isolatedStorageStream.SetLength(newLength);
                } 
                else
                {
                    // if length become smaller , we might be able to close some of memoryStreams that we keep around
                    if (_currentStreamLength > newLength) 
                    {
                        int removeIndex = _memoryStreamList.BinarySearch(GetSearchBlockForOffset(newLength)); 
 
                        // the new end of the stream does not fall into any existing blocks
                        if (removeIndex < 0) 
                            // ~removeIndex represents the place at which we would insert the new block for write
                            removeIndex = ~removeIndex;
                        else
                        { 
                            // we need to truncate the MemoryStream
                            MemoryStreamBlock memStreamBlock = _memoryStreamList[removeIndex]; 
                            checked 
                            {
                                long temp = newLength - memStreamBlock.Offset; 
                                if (temp > 0)
                                {
                                    memStreamBlock.Stream.SetLength(temp);
                                    ++removeIndex; 
                                }
                                // else fall through and remove below 
                            } 
                        }
 
                        for (int i = removeIndex; i < _memoryStreamList.Count; ++i)
                        {
                            _memoryStreamList[i].Stream.Close();    // we need to carefully close the memoryStreams so they properly report the memory usage
                        } 

                        _memoryStreamList.RemoveRange(removeIndex, _memoryStreamList.Count - removeIndex); 
                    } 
                }
 
                _currentStreamLength = newLength;
                if (_currentStreamPosition > _currentStreamLength)
                    _currentStreamPosition = _currentStreamLength;
            } 

            // this can potentially affect memory consumption 
            SwitchModeIfNecessary(); 

#if DEBUG 
    DebugAssertConsistentArrayStructure();
#endif
        }
 
        override public long Seek(long offset, SeekOrigin origin)
        { 
            CheckDisposed(); 
            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(_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; 
            }
 
            // No need to use checked{} since _currentStreamLength > _currentStreamPosition
            int bytesToRead = (int) Math.Min((long)count, _currentStreamLength - _currentStreamPosition);

            checked 
            {
                Debug.Assert(bytesToRead > 0); 
 
                int bytesRead;  // how much data we actually were able to read
                if (_isolatedStorageMode) 
                {
                    _isolatedStorageStream.Seek(_currentStreamPosition, SeekOrigin.Begin);
                    bytesRead = _isolatedStorageStream.Read(buffer, offset, bytesToRead);
                } 
                else
                { 
                    // let's reset data to 0 first, so that gaps will be filled with 0s 
                    // this is required for consistent behavior between the read calls used by the CRC Calculator
                    // and the WriteToStream calls used by the Flush/Save routines 
                    Array.Clear(buffer,offset,bytesToRead);

                    int index = _memoryStreamList.BinarySearch(GetSearchBlockForOffset(_currentStreamPosition));
                    if (index < 0) // the head of new write block does not overlap with any existing blocks 
                        // ~startIndex represents the insertion position
                        index = ~index; 
 
                    for ( ; index < _memoryStreamList.Count; ++index)
                    { 
                        MemoryStreamBlock memStreamBlock = _memoryStreamList[index];
                        long overlapBlockOffset;
                        long overlapBlockSize;
                        // let's check for overlap and fill up appropriate data 
                        PackagingUtilities.CalculateOverlap(memStreamBlock.Offset, (int)memStreamBlock.Stream.Length,
                                                _currentStreamPosition, bytesToRead, 
                                                out overlapBlockOffset, out overlapBlockSize); 
                        if (overlapBlockSize > 0)
                        { 
                            // we got an overlap let's copy data over to the target buffer
                            // _currentStreamPosition is not updated in this foreach loop; it will be updated later
                            Array.Copy(memStreamBlock.Stream.GetBuffer(), (int)(overlapBlockOffset - memStreamBlock.Offset),
                                            buffer, (int)(offset + overlapBlockOffset - _currentStreamPosition), 
                                            (int)overlapBlockSize);
                        } 
                        else 
                            break;
                    } 
                    // for memory stream case we get as much as we asked for
                    bytesRead = bytesToRead;
                }
 
                _currentStreamPosition += bytesRead;
 
                return bytesRead; 
            }
        } 

        override public void Write(byte[] buffer, int offset, int count)
        {
            CheckDisposed(); 
#if DEBUG
    DebugAssertConsistentArrayStructure(); 
#endif 

            PackagingUtilities.VerifyStreamWriteArgs(this, buffer, offset, count); 

            Debug.Assert(_currentStreamPosition >= 0);

            if (count == 0) 
            {
                return; 
            } 

            checked 
            {
                if (_isolatedStorageMode)
                {
                    _isolatedStorageStream.Seek(_currentStreamPosition, SeekOrigin.Begin); 
                    _isolatedStorageStream.Write(buffer, offset, count);
                    _currentStreamPosition += count; 
                } 
                else
                { 
                    WriteAndCollapseBlocks(buffer, offset, count);
                }
                _currentStreamLength = Math.Max(_currentStreamLength, _currentStreamPosition);
            } 

             // this can potentially affect memory consumption 
            SwitchModeIfNecessary(); 
#if DEBUG
    DebugAssertConsistentArrayStructure(); 
#endif
        }

        override public void Flush() 
        {
            CheckDisposed(); 
        } 

        //------------------------------------------------------ 
        //
        //  Internal Methods
        //
        //----------------------------------------------------- 
        /// 
        /// WriteToStream(Stream stream) writes the sparse Memory stream to the Stream provided as parameter 
        /// starting at the current position in the stream 
        /// 
        internal void WriteToStream(Stream stream) 
        {
            checked
            {
                if (_isolatedStorageMode) 
                {
                    _isolatedStorageStream.Seek(0, SeekOrigin.Begin); 
                    PackagingUtilities.CopyStream(_isolatedStorageStream, stream, 
                                            Int64.MaxValue/*bytes to copy*/,
                                            0x80000 /*512K buffer size */); 
                 }
                else
                {
                    CopyMemoryBlocksToStream(stream); 
                }
            } 
        } 

        ///////////////////////////// 
        // Internal Constructor
        /////////////////////////////

        ///  
        /// SparseMemoryStream constructor
        ///  
        ///  
        ///     if we consume less memory than lowWaterMark implementation will use arraList of MemoryStreams
        ///     (vaue 0 will disable Memory Stream based mode) 
        /// 
        /// 
        ///      if we consume more memory than highWaterMark implementation will use the isolatedStorage
        ///      (vaue Int64.MaxVaue will disable isolated storage mode ) 
        /// 
        internal  SparseMemoryStream( 
                                        long lowWaterMark, 
                                        long highWaterMark): this(lowWaterMark, highWaterMark, true)
        { 
        }

        /// 
        /// SparseMemoryStream constructor 
        /// 
        ///  
        ///     if we consume less memory than lowWaterMark implementation will use arraList of MemoryStreams 
        ///     (vaue 0 will disable Memory Stream based mode)
        ///  
        /// 
        ///      if we consume more memory than highWaterMark implementation will use the isolatedStorage
        ///      (vaue Int64.MaxVaue will disable isolated storage mode )
        ///  
        /// 
        ///      There are 2 basic usages for the sparse memory stream. We use it as a buffering mechanism in ZIP IO, 
        ///       in which case it is acceptable to assume that gaps between blocks are 0s. In the other scenario 
        ///       (Encryption Stream ) we use it as a caching mechanism; in this case we ca not assume any values for the
        ///       that data located between blocks, so we shouldn't merge them (if the gap is small and doesn't justify an 
        ///       overhead of the extra block record)
        /// 
        internal  SparseMemoryStream(
                                        long lowWaterMark, 
                                        long highWaterMark,
                                        bool autoCloseSmallBlockGaps) 
        { 
            Invariant.Assert(lowWaterMark >=0 && highWaterMark >=0); // both of them must be positive or 0
            Invariant.Assert(lowWaterMark < highWaterMark); // low water mark must below high water mark 
            Invariant.Assert(lowWaterMark <= Int32.MaxValue);  // low water mark must fit single memory stream 2G

            _memoryStreamList = new List(5);
            _lowWaterMark = lowWaterMark; 
            _highWaterMark = highWaterMark;
            _autoCloseSmallBlockGaps = autoCloseSmallBlockGaps; 
        } 

        //------------------------------------------------------ 
        //
        //  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 Dipose 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)
                    { 
                        // go through all the Memory Streams and close them 
                        foreach (MemoryStreamBlock memStreamBlock in _memoryStreamList)
                        { 
                            // this will report the appropriate Memory usage back to the  ITrackingMemoryStreamFactory
                            memStreamBlock.Stream.Close();
                        }
 
                        // clean up isolated storage resources if in use
                        if (_isolatedStorageStream != null) 
                        { 
                            // can only rely on _isolatedStorageStream behaving correctly if we are not in our finalizer
                            _isolatedStorageStream.Close(); 
                        }
                    }
                }
            } 
            finally
            { 
                _disposedFlag = true; 
                _isolatedStorageStream = null;
                _memoryStreamList = null; 

                base.Dispose(disposing);
            }
        } 

        ///  
        /// Expose collection for use by clients that use this as 
        /// a caching mechanism.
        ///  
        /// Cannot be IList because clients use
        /// BinarySearch() method.
        internal List MemoryBlockCollection
        { 
            get
            { 
                CheckDisposed(); 
                return _memoryStreamList;
            } 
        }

        internal long MemoryConsumption
        { 
            get
            { 
                CheckDisposed(); 
                return _trackingMemoryStreamFactory.CurrentMemoryConsumption;
            } 
        }

        //-----------------------------------------------------
        // 
        //  Private Methods
        // 
        //------------------------------------------------------ 
        private void CheckDisposed()
        { 
            if (_disposedFlag)
            {
                throw new ObjectDisposedException(SR.Get(SRID.StreamObjectDisposed));
            } 
        }
 
        private MemoryStreamBlock GetSearchBlockForOffset(long offset) 
        {
            if (_searchBlock == null) 
                _searchBlock = new MemoryStreamBlock(null, offset);
            else
                _searchBlock.Offset = offset;
            return _searchBlock; 
        }
 
        private bool CanCollapseWithPreviousBlock(MemoryStreamBlock memStreamBlock, 
                                                        long offset,
                                                        long length) 
        {
            // There was an explicit request by the client not to merge near- by blocks
            if (!_autoCloseSmallBlockGaps || memStreamBlock == null)
            { 
                return false;
            } 
 
            checked
            { 
                long gap = offset - (memStreamBlock.Offset + memStreamBlock.Stream.Length);

                Debug.Assert(gap >= 0);
 
                // if gap between them is smaller then a fix overhead of an extra block
                // and these are not too big to not fit in the MemoryStream Int32.MaxValue 
                if (gap <= _fixBlockInMemoryOverhead 
                    && gap + length + memStreamBlock.Stream.Length <= Int32.MaxValue)
                { 
                    return true;
                }
            }
 
            return false;
        } 
 
        private void WriteAndCollapseBlocks(byte[] buffer,
                                                int offset, 
                                                int count)
        {
            int index = _memoryStreamList.BinarySearch(GetSearchBlockForOffset(_currentStreamPosition));
            bool writeDone = false; 
            MemoryStreamBlock memStreamBlock = null;
            MemoryStreamBlock prevMemStreamBlock = null; 
 
            checked
            { 
                if (index < 0) // the head of new write block does not overlap with any existing blocks
                {
                    // ~startIndex represents the place at which we would insert the new block for write
                    index = ~index; 
                    if (index != 0)    // Get the previous block of the new write block
                        prevMemStreamBlock = _memoryStreamList[index - 1]; 
 
                    // If the write request is close enough to the previous block and if the collapsing is allowed
                    if (CanCollapseWithPreviousBlock(prevMemStreamBlock, _currentStreamPosition, (long) count)) 
                    {
                        // write out any intervening zero's
                        prevMemStreamBlock.Stream.Seek(0, SeekOrigin.End);
                        SkipWrite(prevMemStreamBlock.Stream, prevMemStreamBlock.EndOffset, _currentStreamPosition); 
                        prevMemStreamBlock.Stream.Write(buffer, offset, count);
                        writeDone = true; 
                    } 
                }
                else 
                {
                    prevMemStreamBlock = _memoryStreamList[index];

                    // Write the requested bytes to the existing block if possible 
                    if (prevMemStreamBlock.Stream.Length + count <= Int32.MaxValue) // Make sure there is enough space to append
                    { 
                        prevMemStreamBlock.Stream.Seek(_currentStreamPosition - prevMemStreamBlock.Offset, SeekOrigin.Begin); 
                        prevMemStreamBlock.Stream.Write(buffer, offset, count);
                        writeDone = true; 
                        ++index;
                    }
                    else    // Not enough space
                    { 
                        // There is overlap but we will created a new block for the write request; need to truncate the prev block
                        prevMemStreamBlock.Stream.SetLength(_currentStreamPosition - memStreamBlock.Offset); 
                        Debug.Assert(prevMemStreamBlock.Stream.Length > 0); 
                    }
                } 

                if (!writeDone)    // create a new block for the write request
                {
                    prevMemStreamBlock = ConstructMemoryStreamFromWriteRequest(buffer, _currentStreamPosition, count, offset); 
                    Debug.Assert(prevMemStreamBlock.Stream.Length > 0);
                    _memoryStreamList.Insert(index, prevMemStreamBlock); 
                    ++index; 
                }
 
                _currentStreamPosition += count;   // Update the stream position since the write request is satisfied by this point

                int i;
                // Close and remove all completely-overlapping blocks 
                for (i = index; i < _memoryStreamList.Count; ++i)
                { 
                    if (_memoryStreamList[i].EndOffset > _currentStreamPosition) 
                        break;
 
                    _memoryStreamList[i].Stream.Close();    // we need to carefully close the memoryStreams so they properly report the memory usage
                }
                if (i - index > 0)
                    _memoryStreamList.RemoveRange(index, i - index); 

                /////////////////////////////////////////// 
                // Check if the tail of the new write block needs to be collapsed with the following block 
                ///////////////////////////////////////////
 
                long blockOffset = -1;
                if (index < _memoryStreamList.Count)   // Get the next block of the new write block
                {
                    memStreamBlock = _memoryStreamList[index]; 
                    blockOffset = _currentStreamPosition - memStreamBlock.Offset;
                } 
                else 
                    memStreamBlock = null;  // No next block to check
 
                if (blockOffset <= 0)   // No overlapping
                {
                    // Check if we should collapse the block
                    if (memStreamBlock != null 
                        && (CanCollapseWithPreviousBlock(prevMemStreamBlock, memStreamBlock.Offset, memStreamBlock.Stream.Length)))
                    { 
                        // remove the following block  memStreamBlock 
                        _memoryStreamList.RemoveAt(index);
 
                        // write out any intervening zero's
                        prevMemStreamBlock.Stream.Seek(0, SeekOrigin.End);
                        SkipWrite(prevMemStreamBlock.Stream, _currentStreamPosition, memStreamBlock.Offset);
                        prevMemStreamBlock.Stream.Write(memStreamBlock.Stream.GetBuffer(), 0, (int) memStreamBlock.Stream.Length); 
                    }
                } 
                else    // Overlapping 
                {
                    _memoryStreamList.RemoveAt(index); 
                    // Memory stream length or buffer offset cannot be bigger than Int32.MaxValue
                    int leftoverSize = (int) (memStreamBlock.Stream.Length - blockOffset);

                    if (prevMemStreamBlock.Stream.Length + leftoverSize <= Int32.MaxValue) 
                    {
                        prevMemStreamBlock.Stream.Seek(0, SeekOrigin.End); 
                        prevMemStreamBlock.Stream.Write(memStreamBlock.Stream.GetBuffer(), (int) blockOffset, leftoverSize); 
                    }
                    else 
                    {
                        memStreamBlock = ConstructMemoryStreamFromWriteRequest(memStreamBlock.Stream.GetBuffer(),
                                                                _currentStreamPosition,
                                                                leftoverSize, 
                                                                (int) blockOffset);
                        Debug.Assert(memStreamBlock.Stream.Length > 0); 
                        _memoryStreamList.Insert(index, memStreamBlock); 
                    }
                } 
            }
        }

        private MemoryStreamBlock ConstructMemoryStreamFromWriteRequest( 
                                                                                byte[] buffer,  // data buffer to be used for the new Memory Stream Block
                                                                                long writeRequestOffset, 
                                                                                int  writeRequestSize, 
                                                                                int  bufferOffset)
        { 
            Debug.Assert(!_isolatedStorageMode);
            MemoryStreamBlock newMemStreamBlock  = new MemoryStreamBlock
                                                    (_trackingMemoryStreamFactory.Create(writeRequestSize),
                                                    writeRequestOffset); 

            newMemStreamBlock.Stream.Seek(0,SeekOrigin.Begin); 
            newMemStreamBlock.Stream.Write(buffer,bufferOffset,writeRequestSize); 

            return newMemStreamBlock; 
        }

        private void SwitchModeIfNecessary()
        { 
            if (_isolatedStorageMode)
            { 
                Debug.Assert(_memoryStreamList.Count ==0); // it must be empty in isolated storage mode 

                // if we are in isolated storage mode we need to check the Low Water Mark crossing 
                if (_isolatedStorageStream.Length < _lowWaterMark)
                {
                    if (_isolatedStorageStream.Length > 0)
                    { 
                        //build memory stream
                        MemoryStreamBlock newMemStreamBlock  = new MemoryStreamBlock 
                                                    (_trackingMemoryStreamFactory.Create((int)_isolatedStorageStream.Length), 
                                                    0);
 
                        //copy data from iso storage to memory stream
                        _isolatedStorageStream.Seek(0, SeekOrigin.Begin);
                        newMemStreamBlock.Stream.Seek(0, SeekOrigin.Begin);
                        PackagingUtilities.CopyStream(_isolatedStorageStream, newMemStreamBlock.Stream, 
                                                Int64.MaxValue/*bytes to copy*/,
                                                0x80000 /*512K buffer size */); 
 
                        Debug.Assert(newMemStreamBlock.Stream.Length > 0);
                        _memoryStreamList.Add(newMemStreamBlock); 
                    }

                    //switch mode
                     _isolatedStorageMode = false; 

                    // release isolated storage disk space by setting its length to 0 
                    // This way we don't have to re-open the isolated storage again if the memory consumption 
                    //  goes above the High Water Mark
                    _isolatedStorageStream.SetLength(0); 
                    _isolatedStorageStream.Flush();
                }
            }
            else 
            {
                // if we are in Memory Stream mode we need to check the High Water Mark crossing 
                if (_trackingMemoryStreamFactory.CurrentMemoryConsumption > _highWaterMark) 
                {
                    //copy data to isolated storage 
                    EnsureIsolatedStoreStream();
                    CopyMemoryBlocksToStream(_isolatedStorageStream);

                    //switch mode 
                    _isolatedStorageMode = true;
 
                    //release memory stream resources 
                    foreach(MemoryStreamBlock memStreamBlock in _memoryStreamList)
                    { 
                        // this will report the appropriate Memory usage back to the  ITrackingMemoryStreamFactory
                        memStreamBlock.Stream.Close();
                    }
                    _memoryStreamList.Clear(); 
                }
            } 
        } 

        ///  
        /// CopyMemoryBlocksToStream - makes the stream reflect what is in memory
        /// 
        /// Stream that is modified to be contain the same data as
        /// that logically represented by the memory blocks.  The stream length is modified as 
        /// necessary, and any "gaps" are filled with zero's.
        /// This function copies Memory Stream Array List to the target stream. 
        /// It is used in 2 cases: 
        /// 1. When we need to switch to isolated storage mode
        /// 2. When WriteToStream function is called 
        private void CopyMemoryBlocksToStream(Stream targetStream)
        {
            Debug.Assert(!_isolatedStorageMode);
            checked 
            {
                // emit all memory blocks 
                long trackingPosition = 0; 
                foreach(MemoryStreamBlock memStreamBlock in _memoryStreamList)
                { 
                    // write out any intervening zero's
                    trackingPosition = SkipWrite(targetStream, trackingPosition, memStreamBlock.Offset);

                    // write the memory block data 
                    targetStream.Write(memStreamBlock.Stream.GetBuffer(), 0, (int)memStreamBlock.Stream.Length);
                    trackingPosition += memStreamBlock.Stream.Length; 
                } 

                // emit any trailing zero's that could result from a SetLength with no corresponding Write() 
                if (trackingPosition < _currentStreamLength)
                    trackingPosition = SkipWrite(targetStream, trackingPosition, _currentStreamLength);

                Debug.Assert(trackingPosition == _currentStreamLength); 
            }
            targetStream.Flush(); 
        } 

        ///  
        /// Writes out zero's to the targetStream from currentPos to the current offset
        /// 
        /// 
        ///  
        /// 
        /// writes from the current stream position 
        /// offset 
        private long SkipWrite(Stream targetStream, long currentPos, long offset)
        { 
            long toSkip = offset - currentPos;
            Debug.Assert(toSkip >= 0);

            if (toSkip > 0) 
            {
                // we must write out 0s so that the behavior is consistent between Read calls used by the CRC calculations 
                // and the WriteToStream calls used by the Flush/Save logic 
                byte[] zeroBytesBuf = new byte[Math.Min(0x80000, toSkip)]; // 512K chunks max
                while (toSkip > 0) 
                {
                    int bytes = (int)Math.Min(toSkip, zeroBytesBuf.Length);
                    targetStream.Write(zeroBytesBuf, 0, bytes);
                    toSkip -= bytes; 
                }
            } 
 
            return offset;
        } 

#if DEBUG
        private void DebugAssertConsistentArrayStructure()
        { 
            if (_memoryStreamList != null)
            { 
                long testTrackingPosition = 0; 
                foreach(MemoryStreamBlock memStreamBlock in _memoryStreamList)
                { 
                    Debug.Assert(testTrackingPosition  <= memStreamBlock.Offset);
                    testTrackingPosition  = memStreamBlock.Offset + memStreamBlock.Stream.Length;
                }
 
                Debug.Assert(testTrackingPosition <= _currentStreamLength);
            } 
        } 
#endif
 
        private void EnsureIsolatedStoreStream()
        {
            if (_isolatedStorageStream == null)
            { 
                _isolatedStorageStream = PackagingUtilities.CreateUserScopedIsolatedStorageFileStreamWithRandomName(
                    3, out _isolatedStorageStreamFileName); 
            } 
        }
 
        //-----------------------------------------------------
        //
        //  Private Fields
        // 
        //-----------------------------------------------------
        //we use this class to track total memory consumed by the Memory streams that we are using 
        private TrackingMemoryStreamFactory _trackingMemoryStreamFactory = new TrackingMemoryStreamFactory(); 

        private string _isolatedStorageStreamFileName; 
        private Stream _isolatedStorageStream;
        private const int _fixBlockInMemoryOverhead = 100; // If the gap between blocks is smaller than this
                                    // threshold, it is not worth keeping them sperate due to overhead
                                    // This value is used to determine if blocks need to be collapsed 

        //support for Stream methods 
        private bool _disposedFlag; 

        private bool _isolatedStorageMode; 

        private long _currentStreamLength;
        private long _currentStreamPosition;
 
        private List _memoryStreamList;  // list of memory streams for buffering data
                                                        // it contains non-contiguous blocks of MemoryStreams which represents a whole stream 
                                                        // Memory Streams in Array must not overlap 
                                                        // This list is also maintained in offset order
        private MemoryStreamBlock _searchBlock; 

        private long _lowWaterMark;
        private long _highWaterMark;
 
        private bool _autoCloseSmallBlockGaps;
    } 
 
    internal class MemoryStreamBlock : IComparable
    { 
        internal MemoryStreamBlock(MemoryStream stream, long offset)
        {
            Debug.Assert(offset >=0);
 
            _stream = stream;
            _offset = offset; 
        } 

        internal MemoryStream Stream 
        {
            get
            {
                return _stream; 
            }
        } 
 
        internal long Offset
        { 
            get
            {
                return _offset;
            } 
            set
            { 
               Debug.Assert(value >= 0); 

                _offset = value; 
            }
        }

        internal long EndOffset 
        {
            get 
            { 
                checked
                { 
                    return _offset + (_stream == null ? 0 : _stream.Length);
                }
            }
        } 

        int IComparable.CompareTo(MemoryStreamBlock other) 
        { 
            if (other == null)
                return 1; 

            if (_offset == other.Offset)
                return 0;
            else if (_offset > other.Offset) 
            {
                if (_offset < other.EndOffset) 
                    return 0; 
                else
                    return 1; 
            }
            else
            {
                if (other.Offset < EndOffset) 
                    return 0;
                else 
                    return -1; 
            }
        } 

        private MemoryStream _stream;
        private long _offset;
    } 
}
 

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