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