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 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();
                }
            }
        }
 private MemoryStreamBlock GetSearchBlockForOffset(long offset)
 {
     if (_searchBlock == null)
     {
         _searchBlock = new MemoryStreamBlock(null, offset);
     }
     else
     {
         _searchBlock.Offset = offset;
     }
     return(_searchBlock);
 }
        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);
        }
        public override void SetLength(long newLength)
        {
            CheckDisposed();

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

#if DEBUG
            DebugAssertConsistentArrayStructure();
#endif

            if (_currentStreamLength != newLength)
            {
                if (_isolatedStorageMode)
                {
                    lock (PackagingUtilities.IsolatedStorageFileLock)
                    {
                        _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
        }
        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 - prevMemStreamBlock.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);
                    }
                }
            }
        }
        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)
                {
                    lock (PackagingUtilities.IsolatedStorageFileLock)
                    {
                        _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);
            }
        }