File: Base\MS\Internal\IO\Zip\ZipIORawDataFileBlock.cs
Project: wpf\src\WindowsBase.csproj (WindowsBase)
//-----------------------------------------------------------------------------
//-------------   *** WARNING ***
//-------------    This file is part of a legally monitored development project.  
//-------------    Do not check in changes to this project.  Do not raid bugs on this
//-------------    code in the main PS database.  Do not contact the owner of this
//-------------    code directly.  Contact the legal team at ‘ZSLegal’ for assistance.
//-------------   *** WARNING ***
//-----------------------------------------------------------------------------
 
//-----------------------------------------------------------------------------
//
// <copyright file="ZipIORawDataFileBlock.cs" company="Microsoft">
//    Copyright (C) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// Description:
//  This is an internal class that enables interactions with Zip archives
//  for OPC scenarios 
//
// History:
//  11/19/2004: IgorBel: Initial creation.
//
//-----------------------------------------------------------------------------
 
using System;
using System.IO;
using System.Diagnostics;
using System.Collections;
using System.Runtime.Serialization;
using System.Windows;  
using MS.Internal.IO.Packaging;         // for PackagingUtilities
 
namespace MS.Internal.IO.Zip
{
    internal class ZipIORawDataFileBlock :  IZipIOBlock
    {
        //------------------------------------------------------
        //
        //  Public Properties
        //
        //------------------------------------------------------
        // standard IZipIOBlock functionality
        public long Offset
        {
            get
            {
                return _offset;
            }
        }
 
        public long Size
        {
            get
            {
                return  _size;
            }
        }
 
        public bool GetDirtyFlag(bool closingFlag)
        {
            return _dirtyFlag;
        }
 
        //------------------------------------------------------
        //
        //  Public Methods
        //
        //------------------------------------------------------
        public void Move(long shiftSize)
        {
            if (shiftSize != 0)
            {
                checked{_offset +=shiftSize;}
                _dirtyFlag = true;
                Debug.Assert(_offset >=0);                
            }
        }
 
        public void Save()
        {
            if(GetDirtyFlag(true)) // in case we do not know whether we are closing or not we should think we are closing as a more conservative approach 
            {                
                // we need to move the whole block to the new position 
                long moveBlockSourceOffset = _persistedOffset;
                long moveBlockSize = _size;
                long moveBlockTargetOffset = _offset;
 
                if (_cachePrefixStream != null)
                {   
                    // if we have something in cache we only should move whatever isn't cached
                    checked{moveBlockSourceOffset += _cachePrefixStream.Length;}
                    checked{moveBlockTargetOffset += _cachePrefixStream.Length;}
                    checked{moveBlockSize -= _cachePrefixStream.Length;}
                    Debug.Assert(moveBlockSize >=0);                    
                }
 
                _blockManager.MoveData(moveBlockSourceOffset, moveBlockTargetOffset, moveBlockSize);
 
                // only after data on disk was moved it is safe to flush the cached prefix buffer 
                if (_cachePrefixStream != null)
                {
                    if (_blockManager.Stream.Position != _offset)
                    {
                        // we need to seek 
                        _blockManager.Stream.Seek(_offset, SeekOrigin.Begin);
                    }
                    
                    Debug.Assert(_cachePrefixStream.Length > 0);     // should be taken care of by the constructor 
                                                                                        // and PreSaveNotification                
 
                    // we do not need to flush here because Block Manager is responsible for flushing
                    // this in it's Save method
                    _cachePrefixStream.WriteToStream(_blockManager.Stream);
 
                    // we can free the memory
                    _cachePrefixStream.Close();
                    _cachePrefixStream = null;
                }
 
                // we are not shifted between on disk image and in memory image any more 
                _persistedOffset = _offset;
 
                _dirtyFlag = false;         
            }
        }
 
        public void UpdateReferences(bool closingFlag)
        {
            // this block doesn't have external references so there is nothing we need to do here 
        }
        
        public PreSaveNotificationScanControlInstruction PreSaveNotification(long offset, long size)
        {
            return ZipIOBlockManager.CommonPreSaveNotificationHandler(
                                                            _blockManager.Stream,
                                                        offset, size,
                                                        _persistedOffset, _size,
                                                        ref _cachePrefixStream);
        }
 
        //------------------------------------------------------
        //
        //  Internal Properties
        //
        //------------------------------------------------------
        internal long DiskImageShift 
        {
            get
            {   
                return _offset - _persistedOffset;
            }
        }
 
        //------------------------------------------------------
        //
        //  Internal Methods
        //
        //------------------------------------------------------
        internal static ZipIORawDataFileBlock Assign(ZipIOBlockManager blockManager, long offset, long size)          
        {
            if (size <= 0)
            {
                throw new ArgumentOutOfRangeException ("size");
            }
 
            if (offset < 0)
            {
                throw new ArgumentOutOfRangeException ("offset");
            }
                
            ZipIORawDataFileBlock block = new ZipIORawDataFileBlock(blockManager);
            block._persistedOffset = offset;
            block._offset = offset;
            block._size = size;
 
            return block;
        }
 
        internal bool DiskImageContains(IZipIOBlock block)
        {
            checked
            {
                Debug.Assert(block != null);
                Debug.Assert(block.Offset >=0);
                Debug.Assert(block.Size > 0);
 
                return (_persistedOffset <= block.Offset) && 
                            (_persistedOffset + _size >= block.Offset +block.Size);
            }
        }
        
        internal bool DiskImageContains(long offset)
        {
            checked
            {        
                Debug.Assert(offset >=0);
 
                return (_persistedOffset <= offset) && (_persistedOffset + _size > offset); 
            }
        }
    
        internal void SplitIntoPrefixSuffix(IZipIOBlock block, 
                                        out ZipIORawDataFileBlock prefixBlock, out ZipIORawDataFileBlock suffixBlock)
        {
            // assert that current's block cache isn't loaded, if it is  
            // we probably missed an opportunity to used this cache in order to parse the new block 
            // and it might be based on the overriden data on disk 
            // This block can only be in cached state as a part of single BlockManager.Save execution.
            // It can NOT be in cached state prior to BlockManager.Save function entry or after 
            // BlockManager.Save execution completed
            Debug.Assert(_cachePrefixStream == null);
    
            // Assert that block is containe inside the current raw block 
            Debug.Assert(DiskImageContains(block));
 
            checked
            {
                prefixBlock = null;
                suffixBlock = null;
                if (block.Offset > _persistedOffset)
                {
                    // we have a new non empty prefix;
                    long newBlockOffset = _persistedOffset; 
                    long newBlockSize = block.Offset - _persistedOffset;
 
                    prefixBlock = Assign(_blockManager, newBlockOffset , newBlockSize);
                }
                
                if (block.Offset + block.Size < _persistedOffset + _size)
                {
                    // we have a new non empty suffix;
                    long newBlockOffset = block.Offset + block.Size; 
                    long newBlockSize = _persistedOffset + _size - newBlockOffset;
                    
                    suffixBlock = Assign(_blockManager, newBlockOffset, newBlockSize);
                }
            }
        }
 
        //------------------------------------------------------
        //
        //  Private Methods
        //
        //------------------------------------------------------
        private ZipIORawDataFileBlock(ZipIOBlockManager blockManager) 
        {
            _blockManager = blockManager;
        }
 
        //------------------------------------------------------
        //
        //  Private Members
        //
        //------------------------------------------------------
        private SparseMemoryStream _cachePrefixStream = null;
 
        private ZipIOBlockManager _blockManager;
 
        private long _persistedOffset;
 
        private long _offset;
        private long _size;
        private bool  _dirtyFlag;        
    }
}