ZipIOBlockManager.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Base / MS / Internal / IO / Zip / ZipIOBlockManager.cs / 1 / ZipIOBlockManager.cs

                            //------------------------------------------------------------------------------ 
//-------------   *** WARNING ***
//-------------    This file is part of a legally monitored development project.
//-------------    Do not check in changes to this project.  Do not raid bugs on this
//-------------    code in the main PS database.  Do not contact the owner of this 
//-------------    code directly.  Contact the legal team at ‘ZSLegal’ for assistance.
//-------------   *** WARNING *** 
//----------------------------------------------------------------------------- 

//----------------------------------------------------------------------------- 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: 
//  This is an internal class that enables interactions with Zip archives 
//  for OPC scenarios
// 
// History:
//  11/19/2004: IgorBel: Initial creation.
//  10/21/2005: brucemac: Apply security mitigations
// 
//-----------------------------------------------------------------------------
 
using System; 
using System.Diagnostics;
using System.IO; 
using System.Text;
using System.Collections;
using System.Globalization;
using System.Runtime.Serialization; 
using System.Windows;
using MS.Internal.IO.Packaging;  // for PackagingUtilities 
 
namespace MS.Internal.IO.Zip
{ 
    /// 
    /// This is the main class of the actual ZIP IO implementation. It is primary responsibility
    /// is to maintain the map and status of the parsed and loaded areas(blocks) of the file.
    /// It is also supports manipulating this map (adding and deleting blocks) 
    /// 
    internal class ZipIOBlockManager : IDisposable, IEnumerable 
    { 
        //------------------------------------------------------
        // 
        //  Public Methods
        //
        //-----------------------------------------------------
        public void Dispose() 
        {
            Dispose(true); 
            GC.SuppressFinalize(this); 
        }
 
        IEnumerator IEnumerable.GetEnumerator()
        {
            CheckDisposed();
 
            return _blockList.GetEnumerator();
        } 
 
        //------------------------------------------------------
        // 
        //  Internal Properties
        //
        //------------------------------------------------------
        ///  
        /// This property returns the status of whether Central directory is loaded or not.
        /// This property is rarely used, as most clients will just ask for CentralDirectoryBlock 
        /// and oif it isn't loaded it will be. 
        /// The only reason to use IsCentralDirectoryBlockLoaded property is to differentiate
        /// scenarios in which some optimization is possible, if central directory isn't loaded yet. 
        /// 
        internal bool IsCentralDirectoryBlockLoaded
        {
            get 
            {
                CheckDisposed(); 
                return (_centralDirectoryBlock != null); 
            }
        } 

        /// 
        /// This property returns the CentralDirectoryBlock and provides lazy load
        /// fuinctionality. This isthe only way other classes can access information 
        /// from the Central Directory Block
        ///  
        internal ZipIOCentralDirectoryBlock CentralDirectoryBlock 
        {
            get 
            {
                CheckDisposed();
                if (_centralDirectoryBlock == null)
                { 
                    // figure out if we are in ZIP64 mode or not
                    if (Zip64EndOfCentralDirectoryBlock.TotalNumberOfEntriesInTheCentralDirectory > 0) 
                    { 
                        LoadCentralDirectoryBlock();
                    } 
                    else
                    {
                        // We need to be aware of the special case of empty Zip Archive
                        // with a single record : End Of Central directory 
                        //In  such cases we should create new CentralDirectoryBlock
                        CreateCentralDirectoryBlock(); 
                    } 
                }
 
                return _centralDirectoryBlock;
            }
        }
 
        /// 
        /// This property returns the Zip64EndOfCentralDirectoryBlock and provides lazy load 
        /// fuinctionality. This is the only way other classes can access information 
        /// from the Zip64EndOfCentralDirectoryBlock
        ///  
        internal ZipIOZip64EndOfCentralDirectoryBlock Zip64EndOfCentralDirectoryBlock
        {
            get
            { 
                CheckDisposed();
 
                if (_zip64EndOfCentralDirectoryBlock == null) 
                {
                    CreateLoadZip64Blocks(); 
                }

                return _zip64EndOfCentralDirectoryBlock;
            } 
        }
 
        ///  
        /// This property returns the Zip64EndOfCentralDirectoryLocatorBlock and provides lazy load
        /// fuinctionality. This is the only way other classes can access information 
        /// from the Zip64EndOfCentralDirectoryLocator Block
        /// 
        internal ZipIOZip64EndOfCentralDirectoryLocatorBlock Zip64EndOfCentralDirectoryLocatorBlock
        { 
            get
            { 
                CheckDisposed(); 

                if (_zip64EndOfCentralDirectoryLocatorBlock == null) 
                {
                    CreateLoadZip64Blocks();
                }
 
                return _zip64EndOfCentralDirectoryLocatorBlock;
            } 
        } 

        ///  
        /// This property returns the CentralDirectoryBlock and provides lazy load
        /// fuinctionality. This is the only way other classes can access information
        /// from the Central Directory Block
        ///  
        internal ZipIOEndOfCentralDirectoryBlock EndOfCentralDirectoryBlock
        { 
            get 
            {
                CheckDisposed(); 
                if (_endOfCentralDirectoryBlock == null)
                {
                    LoadEndOfCentralDirectoryBlock();
                } 

                return _endOfCentralDirectoryBlock; 
            } 
        }
 
        internal Stream Stream
        {
            get
            { 
                CheckDisposed();
                return _archiveStream; 
            } 
        }
 
        internal bool Streaming
        {
            get
            { 
                CheckDisposed();
                return _openStreaming; 
            } 
        }
 
        internal BinaryReader BinaryReader
        {
            get
            { 
                CheckDisposed();
                Debug.Assert(!_openStreaming, "Not legal in Streaming mode"); 
 
                if (_binaryReader == null)
                { 
                    _binaryReader = new BinaryReader(Stream, Encoding);
                }
                return _binaryReader;
            } 
        }
 
        internal BinaryWriter BinaryWriter 
        {
            get 
            {
                CheckDisposed();
                if (_binaryWriter == null)
                { 
                    _binaryWriter = new BinaryWriter(Stream, Encoding);
                } 
                return _binaryWriter; 
            }
        } 

        internal Encoding Encoding
        {
            get 
            {
                CheckDisposed(); 
                return _encoding; 
            }
        } 

        internal bool DirtyFlag
        {
            set 
            {
                CheckDisposed(); 
                _dirtyFlag = value; 
            }
            get 
            {
                CheckDisposed();
                return _dirtyFlag;
            } 
        }
 
        static internal int MaxFileNameSize 
        {
            get 
            {
                return UInt16.MaxValue;
            }
        } 

        //----------------------------------------------------- 
        // 
        //  Internal Methods
        // 
        //------------------------------------------------------

        internal void  CreateEndOfCentralDirectoryBlock()
        { 
            CheckDisposed();
 
            // Prevent accidental call if underlying stream is non-empty since 
            // any legal zip archive contains an EOCD block.
            Debug.Assert(_openStreaming || _archiveStream.Length == 0); 

            // Disallow multiple calls.
            Debug.Assert(_endOfCentralDirectoryBlock == null);
 
            // construct Block find it and parse it
            long blockOffset = 0;   // this will be updated later 
            _endOfCentralDirectoryBlock = ZipIOEndOfCentralDirectoryBlock.CreateNew(this, blockOffset); 

            // this will add a block to the tail 
            AppendBlock(_endOfCentralDirectoryBlock);
            DirtyFlag = true;
        }
 
        internal void LoadEndOfCentralDirectoryBlock()
        { 
            Debug.Assert(_endOfCentralDirectoryBlock == null); 
            Debug.Assert(!_openStreaming, "Not legal in Streaming mode");
 
            // construct Block find it and parse it
            _endOfCentralDirectoryBlock = ZipIOEndOfCentralDirectoryBlock.SeekableLoad(this);

            //ask block manager to MAP this block 
            MapBlock(_endOfCentralDirectoryBlock);
        } 
 
        internal ZipIOLocalFileBlock CreateLocalFileBlock(string zipFileName, CompressionMethodEnum compressionMethod, DeflateOptionEnum deflateOption)
        { 
            CheckDisposed();

            // we are guaranteed uniqueness at this point , so let's just add a
            // block at the end of the file, just before the central directory 
            // construct Block find it and parse it
 
            // STREAMING Mode: 
            //   NOTE: _blockList is NOT in offset order except the last four blocks
            //      (CD, Zip64 EOCD, Zip64 EOCD Locator, and EOCD) 

            ZipIOLocalFileBlock localFileBlock = ZipIOLocalFileBlock.CreateNew(this,
                                                zipFileName,
                                                compressionMethod, 
                                                deflateOption);
 
            InsertBlock(CentralDirectoryBlockIndex, localFileBlock); 

            CentralDirectoryBlock.AddFileBlock(localFileBlock); 

            DirtyFlag = true;

            return localFileBlock; 
        }
 
        internal ZipIOLocalFileBlock LoadLocalFileBlock(string zipFileName) 
        {
            CheckDisposed(); 

            Debug.Assert(!_openStreaming, "Not legal in Streaming mode");
            Debug.Assert(CentralDirectoryBlock.FileExists(zipFileName)); // it must be in the central directory
 
            // construct Block find it and parse it
            ZipIOLocalFileBlock localFileBlock = ZipIOLocalFileBlock.SeekableLoad(this, zipFileName); 
 
            MapBlock(localFileBlock);
            return localFileBlock; 
        }

        internal void RemoveLocalFileBlock(ZipIOLocalFileBlock localFileBlock)
        { 
            CheckDisposed();
 
            Debug.Assert(!_openStreaming, "Not legal in Streaming mode"); 

            Debug.Assert(localFileBlock != null, " At this point local File block must be preloaded"); 
            Debug.Assert(CentralDirectoryBlock.FileExists(localFileBlock.FileName),
                            " At this point local File block must be mapped in central directory");

 
            // remove it from our list
            _blockList.Remove(localFileBlock); 
 
            // remove this from Central Directory
            CentralDirectoryBlock.RemoveFileBlock(localFileBlock.FileName); 
            DirtyFlag = true;

            // at this point we can Dispose it to make sure that any calls
            // to this file block through outstanding indirect references will result in object Disposed exception 
            localFileBlock.Dispose();
        } 
 
        internal void MoveData(long moveBlockSourceOffset, long moveBlockTargetOffset, long moveBlockSize)
        { 
            Debug.Assert(moveBlockSize >=0);
            Debug.Assert(!_openStreaming, "Not legal in Streaming mode");

            if ((moveBlockSize ==0) || (moveBlockSourceOffset == moveBlockTargetOffset)) 
            {
                //trivial empty move case 
                return; 
            }
 
            checked
            {
                byte[] tempBuffer = new byte [Math.Min(moveBlockSize,0x100000)]; // min(1mb, requested block size)
                long bytesMoved = 0; 
                while(bytesMoved < moveBlockSize)
                { 
                    long subBlockSourceOffset; 
                    long subBlockTargetOffset;
                    int subBlockSize = (int)Math.Min((long)tempBuffer.Length,  moveBlockSize - bytesMoved); 

                    if (moveBlockSourceOffset > moveBlockTargetOffset)
                    {
                        subBlockSourceOffset = moveBlockSourceOffset  + bytesMoved; 
                        subBlockTargetOffset = moveBlockTargetOffset  + bytesMoved;
                    } 
                    else 
                    {
                        subBlockSourceOffset = moveBlockSourceOffset + moveBlockSize - bytesMoved - subBlockSize; 
                        subBlockTargetOffset =  moveBlockTargetOffset  + moveBlockSize - bytesMoved - subBlockSize;
                    }

                    _archiveStream.Seek(subBlockSourceOffset, SeekOrigin.Begin); 
                    int bytesRead = PackagingUtilities.ReliableRead(_archiveStream, tempBuffer, 0, subBlockSize);
 
                    if (bytesRead != subBlockSize) 
                    {
                        throw new FileFormatException(SR.Get(SRID.CorruptedData)); 
                    }

                    _archiveStream.Seek(subBlockTargetOffset, SeekOrigin.Begin);
                    _archiveStream.Write(tempBuffer, 0, subBlockSize); 

                    checked{bytesMoved += subBlockSize;} 
                } 
            }
        } 

        /// 
        /// Save - stream level
        ///  
        /// 
        /// closing or flushing 
        internal void SaveStream(ZipIOLocalFileBlock blockRequestingFlush, bool closingFlag) 
        {
            // Prevent recursion when propagating Flush or Disposed to our minions 
            // because ZipIOFileItemStream.Flush calls us.
            if (_propagatingFlushDisposed)
                return;
            else 
                _propagatingFlushDisposed = true;   // enter first time
 
            try 
            {
                // redirect depending on our mode 
                if (_openStreaming)
                {
                    StreamingSaveStream(blockRequestingFlush, closingFlag);
                } 
                else
                    SaveContainer(false); 
            } 
            finally
            { 
                // all done so restore state
                _propagatingFlushDisposed = false;
            }
        } 

        ///  
        /// Save - container level 
        /// 
        /// true if closing, false if flushing 
        internal void Save(bool closingFlag)
        {
            CheckDisposed();
 
            // Prevent recursion when propagating Flush or Disposed to our minions
            // because ZipIOFileItemStream.Flush calls us. 
            if (_propagatingFlushDisposed) 
                return;
            else 
                _propagatingFlushDisposed = true;   // enter first time

            try
            { 
                // redirect depending on our mode
                if (_openStreaming) 
                { 
                    StreamingSaveContainer(closingFlag);
                } 
                else
                    SaveContainer(closingFlag);
            }
            finally 
            {
                // all done so restore state 
                _propagatingFlushDisposed = false; 
            }
        } 

        /// 
        /// Constructor
        ///  
        /// stream we operate on
        ///  
        /// true if we own the stream and are expected to close it when we are disposed 
        internal ZipIOBlockManager(Stream archiveStream, bool streaming, bool ownStream)
        { 
            Debug.Assert(archiveStream != null);

            _archiveStream = archiveStream;
            _openStreaming = streaming; 
            _ownStream = ownStream;
 
            if (streaming) 
            {
                // wrap the archive stream in a WriteTimeStream which keeps track of current position 
                _archiveStream = new WriteTimeStream(_archiveStream);
            }
            else if (archiveStream.Length > 0)
            { 
                // for non-empty stream we need to map the whole stream into a raw data block
                // which helps keep track of shifts and dirty areas 
                ZipIORawDataFileBlock rawBlock = ZipIORawDataFileBlock.Assign(this, 0, archiveStream.Length); 

                _blockList.Add(rawBlock); 
            }
        }

        internal static UInt32 ToMsDosDateTime(DateTime dateTime) 
        {
 
            UInt32 result = 0; 

            result |= (((UInt32)dateTime.Second) /2) & 0x1F;   // seconds need to be divided by 2 
                                                                                // as they stored in 5 bits
            result |= (((UInt32)dateTime.Minute) & 0x3F) << 5;
            result |= (((UInt32)dateTime.Hour) & 0x1F) << 11;
 
            result |= (((UInt32)dateTime.Day) & 0x1F) << 16;
            result |= (((UInt32)dateTime.Month) & 0xF) << 21; 
            result |= (((UInt32)(dateTime.Year - 1980)) & 0x7F) << 25; 

            return result; 
        }

        internal static DateTime FromMsDosDateTime(UInt32 dosDateTime)
        { 
            int seconds = (int)((dosDateTime & 0x1F) << 1); // seconds need to be multiplied by 2
                                                                                       // as they stored in 5 bits 
            int minutes  = (int)((dosDateTime >> 5) & 0x3F); 
            int hours = (int)((dosDateTime >> 11) & 0x1F);
 
            int day = (int)((dosDateTime >> 16) & 0x1F);
            int month  =(int)((dosDateTime >> 21) & 0xF);
            int year = (int)(1980 + ((dosDateTime >> 25) & 0x7F));
 
            //this will throw if parameters are out of range
            return new DateTime(year, month,day,hours,minutes,seconds); 
        } 

        ///  
        /// This is standard way to normalize Zip File Item names. At this point we only
        /// getting rid of the spaces. The Exists calls are responsible or making sure
        /// that they check for uniqueness in a case insensitive manner. It is up to the
        /// higher levels to add stricter restrictions like URI character set, and so on. 
        /// 
        static internal string ValidateNormalizeFileName(string zipFileName) 
        { 
            // Validate parameteres
            if (zipFileName == null) 
            {
                throw new ArgumentNullException("zipFileName");
            }
 
            if (zipFileName.Length > ZipIOBlockManager.MaxFileNameSize)
            { 
                throw new ArgumentOutOfRangeException("zipFileName"); 
            }
 
            zipFileName = zipFileName.Trim();

            if (zipFileName.Length < 1)//it must be at least one character
            { 
                throw new ArgumentOutOfRangeException("zipFileName");
            } 
 
            //Based on the  Appnote :
            //    << The path stored should not contain a drive or device letter, or a leading slash.  >> 

            return zipFileName;
        }
 
        //-----------------------------------------------------
        //  Internal helper CopyBytes functions for storing data into a byte[] 
        // it is a similar to a BinaryWriter , but not for streams but ratrher 
        // for byte[]
        // These functiona used in the Extra field parsing, as that functionality is buit 
        // in terms of byte[] not streams
        //-----------------------------------------------------
        internal static int CopyBytes(Int16 value, byte[] buffer, int offset)
        { 
            Debug.Assert(checked(buffer.Length-offset) >= sizeof(Int16));
 
             byte[] tempBuffer = BitConverter.GetBytes(value); 
            Array.Copy(tempBuffer, 0, buffer, offset, tempBuffer.Length);
 
            return offset + tempBuffer.Length;
        }

        internal static int CopyBytes(Int32 value, byte[] buffer, int offset) 
        {
            Debug.Assert(checked(buffer.Length-offset) >= sizeof(Int32)); 
 
             byte[] tempBuffer = BitConverter.GetBytes(value);
            Array.Copy(tempBuffer, 0, buffer, offset, tempBuffer.Length); 

            return offset + tempBuffer.Length;
        }
 
        internal static int CopyBytes(Int64 value, byte[] buffer, int offset)
        { 
            Debug.Assert(checked(buffer.Length-offset) >= sizeof(Int64)); 

             byte[] tempBuffer = BitConverter.GetBytes(value); 
            Array.Copy(tempBuffer, 0, buffer, offset, tempBuffer.Length);

            return offset + tempBuffer.Length;
        } 

        internal static int CopyBytes(UInt16 value, byte[] buffer, int offset) 
        { 
            Debug.Assert(checked(buffer.Length-offset) >= sizeof(UInt16));
 
             byte[] tempBuffer = BitConverter.GetBytes(value);
            Array.Copy(tempBuffer, 0, buffer, offset, tempBuffer.Length);

            return offset + tempBuffer.Length; 
        }
 
        internal static int CopyBytes(UInt32 value, byte[] buffer, int offset) 
        {
            Debug.Assert(checked(buffer.Length-offset) >= sizeof(UInt32)); 

             byte[] tempBuffer = BitConverter.GetBytes(value);
            Array.Copy(tempBuffer, 0, buffer, offset, tempBuffer.Length);
 
            return offset + tempBuffer.Length;
        } 
 
        internal static int CopyBytes(UInt64 value, byte[] buffer, int offset)
        { 
            Debug.Assert(checked(buffer.Length-offset) >= sizeof(UInt64));

             byte[] tempBuffer = BitConverter.GetBytes(value);
            Array.Copy(tempBuffer, 0, buffer, offset, tempBuffer.Length); 

            return offset + tempBuffer.Length; 
        } 

        internal static UInt64 ConvertToUInt64(UInt32 loverAddressValue, UInt32 higherAddressValue) 
        {
            return checked((UInt64)loverAddressValue + (((UInt64)higherAddressValue)  << 32));
        }
 
        internal static ZipIOVersionNeededToExtract CalcVersionNeededToExtractFromCompression
                                                                                    (CompressionMethodEnum compression) 
        { 
            switch (compression)
            { 
                case CompressionMethodEnum.Stored:
                        return ZipIOVersionNeededToExtract.StoredData;
                case CompressionMethodEnum.Deflated:
                        return ZipIOVersionNeededToExtract.DeflatedData; 
                default:
                        throw new NotSupportedException();    // Deflated64 this is OFF 
            } 
        }
 

        /// 
        /// This is the common Pre Save notiofication handler for
        ///  RawDataFile Block and File Item Stream 
        /// It makes assumption that the overlap generally start coming in at the beginning of a
        /// large disk image, so we should only try to cache cache overlaped data in the prefix 
        /// of the disk block 
        /// Block can also return a value indicating whether PreSaveNotification should be extended to the blocks that are positioned after
        /// it in the Block List. For example, if block has completely handled PreSaveNotification in a way that it cached the whole area that 
        /// was in danger (of being overwritten) it means that no blocks need to worry about this anymore. After all no 2 blocks should have
        /// share on disk buffers. Another scenario is when block can determine that area in danger is positioned before the block's on disk
        /// buffers; this means that all blocks that are positioned later in the block list do not need to worry about this PreSaveNotification
        /// as their buffers should be positioned even further alone in the file. 
        /// 
        internal static PreSaveNotificationScanControlInstruction CommonPreSaveNotificationHandler( 
                                                            Stream stream, 
                                                            long offset, long size,
                                                            long onDiskOffset, long onDiskSize, 
                                                            ref SparseMemoryStream cachePrefixStream)
        {
            checked
            { 
                Debug.Assert(size >=0);
                Debug.Assert(offset >=0); 
                Debug.Assert(onDiskSize >=0); 
                Debug.Assert(onDiskOffset >=0);
 
                // trivial request
                if (size == 0)
                {
                    // The area being overwritten is of size 0 so there is no need to notify any blocks about this. 
                    return PreSaveNotificationScanControlInstruction.Stop;
                } 
 
                if (cachePrefixStream != null)
                { 
                    // if we have something in cache prefix buffer  we only should check whatever tail data isn't cached
                    checked{onDiskOffset += cachePrefixStream.Length;}
                    checked{onDiskSize -= cachePrefixStream.Length;}
                    Debug.Assert(onDiskSize >=0); 
                }
 
                if (onDiskSize == 0) 
                {
                    // the raw data block happened to be fully cached 
                    // in this case (onDiskSize==0) can not be used as a reliable indicator of the position of the
                    // on disk buffer relative to the other; it is just an indicator of an empty buffer which might have a meaningless offset
                    // that shouldn't be driving any decisions
                    return PreSaveNotificationScanControlInstruction.Continue; 
                }
 
                // we need to first find out if the raw data that isn't cached yet overlaps with any disk space 
                // that is about to be overriden
                long overlapBlockOffset; 
                long  overlapBlockSize;

                PackagingUtilities.CalculateOverlap(onDiskOffset, onDiskSize,
                                           offset, size , 
                                            out overlapBlockOffset, out overlapBlockSize);
                if (overlapBlockSize <= 0) 
                { 
                    // No overlap , we can ignore this message.
                    // In addition to that, if (onDiskOffset > offset) it means that, given the fact that all blocks after 
                    // the current one will have even larger offsets, they couldn't possibly overlap with (offset ,size ) chunk .
                    return (onDiskOffset > offset) ?
                                                PreSaveNotificationScanControlInstruction.Stop  :
                                                PreSaveNotificationScanControlInstruction.Continue; 
                }
 
                // at this point we have an overlap, we need to read the data that is overlapped 
                // and merge it with whatever we already have in cache
                // let's figure out the part that isn't cached yet, and needs to be 
                long blockSizeToCache;
                checked
                {
                    blockSizeToCache = overlapBlockOffset + overlapBlockSize - onDiskOffset; 
                }
                Debug.Assert(blockSizeToCache >0); // there must be a non empty block at this point that needs to be cached 
 
                // We need to ensure that we do have a place to store this data
                if (cachePrefixStream == null) 
                {
                    cachePrefixStream = new SparseMemoryStream(_lowWaterMark, _highWaterMark);
                }
                else 
                {
                    // if we already have some cached prefix data we have to make sure we are 
                    // appending new data tro the tail of the already cached chunk 
                    cachePrefixStream.Seek(0, SeekOrigin.End);
                } 

                stream.Seek(onDiskOffset, SeekOrigin.Begin);
                long bytesCopied = PackagingUtilities.CopyStream(stream, cachePrefixStream, blockSizeToCache, 4096);
 
                if (bytesCopied  != blockSizeToCache)
                { 
                    throw new FileFormatException(SR.Get(SRID.CorruptedData)); 
                }
 
                // if the contdition below is true it means that, given the fact that all blocks after
                // the current one will have even larger offsets, they couldn't possibly overlap with (offset ,size ) chunk
                return ((onDiskOffset + onDiskSize) >=  (offset + size)) ?
                                            PreSaveNotificationScanControlInstruction.Stop  : 
                                            PreSaveNotificationScanControlInstruction.Continue;
            } 
        } 

        //----------------------------------------------------- 
        //
        //  Protected Methods
        //
        //------------------------------------------------------ 
        protected void Dispose(bool disposing)
        { 
            if (disposing) 
            {
                // multiple calls are fine - just ignore them 
                if (!_disposedFlag)
                {
                    // Prevent recursion into Save() when propagating Flush or Disposed to our minions
                    // because ZipIOFileItemStream.Flush calls us. 
                    if (_propagatingFlushDisposed)
                        return; 
                    else 
                        _propagatingFlushDisposed = true;   // enter first time
 
                    try
                    {
                        try
                        { 
                            foreach (IZipIOBlock block in _blockList)
                            { 
                                IDisposable disposableBlock = block as IDisposable; 
                                if (disposableBlock != null)
                                { 
                                    // only some Blocks are disposable, most are not
                                    disposableBlock.Dispose();
                                }
                            } 
                        }
                        finally 
                        { 
                            // If we own the stream, we should close it.
                            // If not, we cannot even close the binary reader or writer as these close the 
                            // underlying stream on us.
                            if (_ownStream)
                            {
                                if (_binaryReader != null) 
                                {   // this one might be null of we have been only writing
                                    _binaryReader.Close(); 
                                } 

                                if (_binaryWriter != null) 
                                {   // this one might be null of we have been only reading
                                    _binaryWriter.Close();
                                }
 
                                _archiveStream.Close();
                            } 
                        } 
                    }
                    finally 
                    {
                        _blockList = null;
                        _encoding = null;
                        _endOfCentralDirectoryBlock = null; 
                        _centralDirectoryBlock = null;
 
                        _disposedFlag = true; 
                        _propagatingFlushDisposed = false;   // reset
                    } 
                }
            }
        }
 
        //-----------------------------------------------------
        // 
        //  Private Properties 
        //
        //------------------------------------------------------ 
        /// 
        /// This property returns the index of CentralDirectoryBlock within _blockList
        /// 
        private int CentralDirectoryBlockIndex 
        {
            get 
            { 
                Invariant.Assert(_blockList.Count >= _requiredBlockCount);
                Debug.Assert(_centralDirectoryBlock != null 
                                && _endOfCentralDirectoryBlock != null
                                && _zip64EndOfCentralDirectoryBlock != null
                                && _zip64EndOfCentralDirectoryLocatorBlock != null);
 
                // We always have following blocks at the end of the block lists:
                //  CD, Zip64 EOCD, Zip64 EOCD Locator, and EOCD 
                // Thus the index of CD can be calculated from the total number of blocks 
                //  and _requiredBlockCount which is 4
                return _blockList.Count - _requiredBlockCount; 
            }
        }

 
        //------------------------------------------------------
        // 
        //  Private Methods 
        //
        //----------------------------------------------------- 
        /// 
        /// Throwes exception if object already Disposed/Closed.
        /// 
        private void CheckDisposed() 
        {
            if (_disposedFlag) 
            { 
                throw new ObjectDisposedException(null, SR.Get(SRID.ZipArchiveDisposed));
            } 
        }

        /// 
        /// Save - container level 
        /// 
        /// true if closing, false if flushing 
        private void SaveContainer(bool closingFlag) 
        {
            CheckDisposed(); 
            Debug.Assert(!_openStreaming, "Not legal in Streaming mode");


            if (!closingFlag && !DirtyFlag) 
            {
                // we are trying to save some cycles in the case of the subsequent Flush calls 
                // that do not close the container 
                // if it is being closed DirtyFlag isn't reliable as the Compressed streams carry
                // some extra bytes after flushing and only write them out on closing. 
                return;
            }

            // We need a separate cycle to update all the cross block references prior to saving blocks 
            // specifically the central directory needs "dirty" information from blocks in order to properly
            // update it's references, otherwise (if we call UpdateReferences and Save in the same loop) 
            // information about block shifts will be lost by the time we ask central directory to update it's 
            // references
 
            // offset of the first block
            long currentOffset = 0; // ZIP64 review type here
            foreach (IZipIOBlock currentBlock in _blockList)
            { 
                // move block so it is positioned right after the previous block
                currentBlock.Move(currentOffset - currentBlock.Offset); 
 
                // this will update references and as well as other internal structures (size)
                // specifically for the FileItemBlock it will flush buffers of 
                // all the outstanding streams
                currentBlock.UpdateReferences(closingFlag);

                //advance current stream position according to the size of the block 
                checked{currentOffset += currentBlock.Size;}
            } 
 
            // save dirty blocks
            bool dirtyBlockFound = false; 

            int blockListCount  = _blockList.Count;
            for (int i = 0; i < blockListCount ; i++)
            { 
                IZipIOBlock currentBlock = (IZipIOBlock)_blockList[i];
 
                if (currentBlock.GetDirtyFlag(closingFlag)) 
                {
                    dirtyBlockFound = true; 

                    long currentBlockOffset = currentBlock.Offset;
                    long currentBlockSize = currentBlock.Size;
 
                    if (currentBlockSize > 0)
                    { 
                        // before saving we need to warn all the blocks that have still some data on disk 
                        // that might be overriden
                        // second loop must start at the current position of the extrrnal loop 
                        // as all the items before have been saved
                        for (int j = i + 1; j < blockListCount; j++)
                        {
                            // This is an optimization which enabled us to stop going through the 
                            // tail blocks as soon as we find a block that returns a status indicating
                            // that it took care of the tail of the target area or is positioned after the 
                            // target area. 
                            if (((IZipIOBlock)_blockList[j]).PreSaveNotification(currentBlockOffset, currentBlockSize) ==
                                        PreSaveNotificationScanControlInstruction.Stop ) 
                            {
                                break;
                            }
                        } 
                    }
 
                    currentBlock.Save();    // Even if currentBlockSize == 0, call Save to clear DirtyFlag 
                }
            } 

            // originally we have had an assert for the case when no changes were made to the file
            // but calculated size didn't match the actual stream size.
            // As a result of the XPS Viewer dynamically switching streams underneath ZIP IO, we 
            // need to treat this case as a normal non-dirty scenario. So if nothing changed and
            // nothing was written out we shouldn't even validate whether stream underneath 
            // was modified in any way or not (even such simple modifications as an unexpected 
            // Stream.Length change). If it was modified by someone we assume that the stream
            // owner was aware of it's action. 
            if (dirtyBlockFound && (Stream.Length > currentOffset))
            {
                Stream.SetLength(currentOffset);
            } 

            Stream.Flush(); 
            DirtyFlag = false; 
        }
 
        /// 
        /// Streaming version of Save routine
        /// 
        /// true if closing the package 
        private void StreamingSaveContainer(bool closingFlag)
        { 
            // STREAMING Mode: 
            //   NOTE: _blockList is NOT in offset order except the last four blocks
            //      (CD, Zip64 EOCD, Zip64 EOCD Locator, and EOCD) 

            try
            {
                // save dirty blocks 
                long currentOffset = 0;
                for (int i = 0; i < _blockList.Count; i++) 
                { 
                    IZipIOBlock currentBlock = (IZipIOBlock)_blockList[i];
                    ZipIOLocalFileBlock localFileBlock = currentBlock as ZipIOLocalFileBlock; 

                    if (localFileBlock == null)
                    {
                        if (closingFlag) 
                        {
                            // Move block so it is positioned right after the previous block. 
                            // No need for nested loops like in SaveContainer because none of these 
                            // calls can cause a block to move in the Streaming case.
                            currentBlock.Move(currentOffset - currentBlock.Offset); 
                            currentBlock.UpdateReferences(closingFlag);
                            if (currentBlock.GetDirtyFlag(closingFlag))
                            {
                                currentBlock.Save(); 
                            }
                        } 
                    } 
                    else if (currentBlock.GetDirtyFlag(closingFlag))
                    { 
                        // no need to call UpdateReferences in streaming mode for regular
                        // local file blocks because
                        // we manually emit the local file header and the local file descriptor
                        localFileBlock.SaveStreaming(closingFlag); 
                    }
                    checked{currentOffset += currentBlock.Size;} 
                } 

                Stream.Flush(); 
            }
            finally
            {
                // all done so restore state 
                _propagatingFlushDisposed = false;
            } 
        } 

        ///  
        /// Flush was called on a ZipIOFileItemStream
        /// 
        /// block that owns the stream that Flush was called on
        /// close or dispose 
        private void StreamingSaveStream(ZipIOLocalFileBlock blockRequestingFlush, bool closingFlag)
        { 
            // STREAMING MODE: 
            // Flush will do one of two things, depending on the currently open stream:
            // 1) If the currently open stream matches the one passed (or none is currently opened) 
            //    then write will occur to the open stream.
            // 2) Otherwise, the currently opened stream will be flushed and closed, and the
            //    given stream will become the currently opened stream
            // NOTE: _blockList is NOT in offset order except the last four blocks 
            //      (CD, Zip64 EOCD, Zip64 EOCD Locator, and EOCD)
 
            // different stream? 
            if (_streamingCurrentlyOpenStreamBlock != blockRequestingFlush)
            { 
                // need to close the currently opened stream
                // unless its our first time through
                if (_streamingCurrentlyOpenStreamBlock != null)
                { 
                    _streamingCurrentlyOpenStreamBlock.SaveStreaming(true);
                } 
 
                // Now make the given stream the new "currently opened stream".
                _streamingCurrentlyOpenStreamBlock = blockRequestingFlush; 
            }

            // this should now be flushable/closable
            _streamingCurrentlyOpenStreamBlock.SaveStreaming(closingFlag); 

            // if closing - discard the stream because it is now closed 
            if (closingFlag) 
                _streamingCurrentlyOpenStreamBlock = null;
        } 

        private void  CreateCentralDirectoryBlock()
        {
            CheckDisposed(); 
            Debug.Assert(_zip64EndOfCentralDirectoryBlock != null);
 
            // It must not be loaded yet 
            Debug.Assert(!IsCentralDirectoryBlockLoaded);
 
            // The proper position is just before the Zip64EndOfCentralDirectoryRecord
            // Zip64EndOfCentralDirectoryRecord - might be of size 0 (if file is small enough)
            int blockPosition = _blockList.IndexOf(Zip64EndOfCentralDirectoryBlock);
            Debug.Assert(blockPosition >= 0); 

            // construct Block find it and parse it 
            _centralDirectoryBlock = ZipIOCentralDirectoryBlock.CreateNew(this); 

            //ask block manager to insert this this block 
            InsertBlock(blockPosition , _centralDirectoryBlock);
        }

        private void LoadCentralDirectoryBlock() 
        {
            Debug.Assert(_centralDirectoryBlock == null); 
            Debug.Assert(!_openStreaming, "Not legal in Streaming mode"); 

            // construct Block find it and parse it 
            _centralDirectoryBlock = ZipIOCentralDirectoryBlock.SeekableLoad(this);

            //ask block manager to MAP this block
            MapBlock(_centralDirectoryBlock); 
        }
 
        private void  CreateLoadZip64Blocks() 
        {
            CheckDisposed(); 

            Debug.Assert ((_zip64EndOfCentralDirectoryBlock == null) &&
                                  (_zip64EndOfCentralDirectoryLocatorBlock == null));
 
            // determine whether we want to create it or load it
            // this check doesn't provide us with a 100% guarantee. 
            // After discussion we have agreed that this should be sufficient 
            if (!Streaming && EndOfCentralDirectoryBlock.ContainValuesHintingToPossibilityOfZip64 &&
                ZipIOZip64EndOfCentralDirectoryLocatorBlock.SniffTheBlockSignature(this)) 
            {
                // attempt to sniff the header of the
                LoadZip64EndOfCentralDirectoryLocatorBlock();
                LoadZip64EndOfCentralDirectoryBlock(); 
            }
            else 
            { 
                // We delayed validation of some values in End of Central Directory that can give possible
                //  hints for Zip64; Since there is no Zip64 structure, we need to validate them here 
                _endOfCentralDirectoryBlock.ValidateZip64TriggerValues();

                CreateZip64EndOfCentralDirectoryLocatorBlock();
                CreateZip64EndOfCentralDirectoryBlock(); 
            }
        } 
 
        private void  CreateZip64EndOfCentralDirectoryBlock()
        { 
            Debug.Assert(_zip64EndOfCentralDirectoryBlock == null);

            // The proper position is just before the Zip64EndOfCentralDirectoryRecordLocator
            // Zip64EndOfCentralDirectoryRecord - might be of size 0 (if file is small enough) 
            int blockPosition = _blockList.IndexOf(Zip64EndOfCentralDirectoryLocatorBlock);
 
            // construct Block find it and parse it 
            _zip64EndOfCentralDirectoryBlock = ZipIOZip64EndOfCentralDirectoryBlock.CreateNew(this);
 
            //ask block manager to insert this this block
            InsertBlock(blockPosition, _zip64EndOfCentralDirectoryBlock);
        }
 
        private void  LoadZip64EndOfCentralDirectoryBlock()
        { 
            Debug.Assert(_zip64EndOfCentralDirectoryBlock == null); 
            Debug.Assert(!_openStreaming, "Not legal in Streaming mode");
 
            // construct Block find it and parse it
            _zip64EndOfCentralDirectoryBlock = ZipIOZip64EndOfCentralDirectoryBlock.SeekableLoad(this);

            //ask block manager to insert this this block 
            MapBlock(_zip64EndOfCentralDirectoryBlock);
        } 
 
        private void  CreateZip64EndOfCentralDirectoryLocatorBlock()
        { 
            Debug.Assert(_zip64EndOfCentralDirectoryLocatorBlock == null);

            // The proper position is just before the EOCD
            int blockPosition = _blockList.IndexOf(EndOfCentralDirectoryBlock); 

            // construct Block find it and parse it 
            _zip64EndOfCentralDirectoryLocatorBlock = ZipIOZip64EndOfCentralDirectoryLocatorBlock.CreateNew(this); 

            //ask block manager to MAP this block 
            InsertBlock(blockPosition, _zip64EndOfCentralDirectoryLocatorBlock);
        }

        private void  LoadZip64EndOfCentralDirectoryLocatorBlock() 
        {
            Debug.Assert(_zip64EndOfCentralDirectoryLocatorBlock == null); 
            Debug.Assert(!_openStreaming, "Not legal in Streaming mode"); 

            // construct Block find it and parse it 
            _zip64EndOfCentralDirectoryLocatorBlock = ZipIOZip64EndOfCentralDirectoryLocatorBlock.SeekableLoad(this);

            //ask block manager to MAP this block
            MapBlock(_zip64EndOfCentralDirectoryLocatorBlock); 
        }
 
        private void MapBlock(IZipIOBlock block) 
        {
            // as we map a block to existing file space it must be not dirty. 
            Debug.Assert(!block.GetDirtyFlag(true)); // closingFlag==true used as a more conservative option
            Debug.Assert(!_openStreaming, "Not legal in Streaming mode");

            for (int blockIndex = _blockList.Count - 1; blockIndex >= 0; --blockIndex) 
            {
                // if we need to find a RawDataBlock that maps to the target area 
                ZipIORawDataFileBlock rawBlock = _blockList[blockIndex] as ZipIORawDataFileBlock; 

                //check the original loaded RawBlock size / offset against the new block 
                if ((rawBlock != null) && rawBlock.DiskImageContains(block))
                {
                    ZipIORawDataFileBlock prefixBlock, suffixBlock;
 
                    //split raw block into prefixRawBlock, SuffixRawBlock
                    rawBlock.SplitIntoPrefixSuffix(block, out prefixBlock, out suffixBlock); 
 
                    _blockList.RemoveAt(blockIndex); // remove the old big raw data block
 
                    // add suffix Raw data block
                    if (suffixBlock != null)
                    {
                        _blockList.Insert(blockIndex, suffixBlock); 
                    }
 
                    // add new mapped block 
                    _blockList.Insert(blockIndex, block);
 
                    // add prefix Raw data block
                    if (prefixBlock != null)
                    {
                        _blockList.Insert(blockIndex, prefixBlock); 
                    }
 
                    return; 
                }
            } 

            // we couldn't find a raw data block for mapping this, we can only throw
            throw new FileFormatException(SR.Get(SRID.CorruptedData));
        } 

        private void InsertBlock(int blockPosition, IZipIOBlock block) 
        { 
            // as we are adding a new block it must be dirty unless its size is 0
            Debug.Assert(block.GetDirtyFlag(true) ||   // closingFlag==true used as a more conservative option 
                                    block.Size == 0);

            _blockList.Insert(blockPosition, block);
        } 

        private void AppendBlock(IZipIOBlock block) 
        { 
            // as we are adding a new block it must be dirty unless its size is 0
            Debug.Assert(block.GetDirtyFlag(true) || // closingFlag==true used as a more conservative option 
                                    block.Size == 0);

            // CentralDirectory persistence logic relies on the fact that we always add headers in a fashion that
            // matches the order of the corresponding file items in the physical archive (currently to the end of the list). 
            // If this invariant is violated, the corresponding central directory persistence logic must be updated.
            _blockList.Add(block); 
        } 

        // this flag is used for Perf reasons, it doesn't carry any additional information that isn't stored somewhere 
        // else. In order to prevent complex dirty calculations on the sequential flush calls, we are going to keep
        // this flag which will be set to true at the end of the flush (or close). This flag will be set from the ZipArchive
        // and CrcCalculating entry points that can potentially make our structure dirty.
        // This flag is only used for non-streaming cases. In streaming cases we do not believe there is a perf 
        // penalty of that nature.
        private bool _dirtyFlag = false; 
 
        private bool _disposedFlag;
        private bool _propagatingFlushDisposed;              // if true, we ignore calls back to Save to prevent recursion 
        private Stream _archiveStream;
        private bool _openStreaming;
        private bool _ownStream;                                    // true if we own the archive stream
 
        // Streaming Mode Only: stream that is currently able to write without interfering with other streams
        private ZipIOLocalFileBlock _streamingCurrentlyOpenStreamBlock; 
 
        private BinaryReader _binaryReader;
        private BinaryWriter _binaryWriter; 

        private const int _initialBlockListSize = 50;
        private ArrayList _blockList = new ArrayList(_initialBlockListSize);
 
        private ASCIIEncoding _encoding = new ASCIIEncoding();
 
        ZipIOZip64EndOfCentralDirectoryBlock  _zip64EndOfCentralDirectoryBlock; 
        ZipIOZip64EndOfCentralDirectoryLocatorBlock _zip64EndOfCentralDirectoryLocatorBlock;
        ZipIOEndOfCentralDirectoryBlock _endOfCentralDirectoryBlock; 
        ZipIOCentralDirectoryBlock _centralDirectoryBlock;

        private const long _lowWaterMark = 0x19000;                 // we definately would like to keep everythuing under 100 KB in memory
        private const long _highWaterMark = 0xA00000;   // we would like to keep everything over 10 MB on disk 
        private const int _requiredBlockCount = 4;      // We always have following blocks: CD, Zip64 EOCD, Zip64 EOCD Locator, and EOCD
                                                       // This value is used to calculate the index of CD within _blockList 
    } 
}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------------------------ 
//-------------   *** WARNING ***
//-------------    This file is part of a legally monitored development project.
//-------------    Do not check in changes to this project.  Do not raid bugs on this
//-------------    code in the main PS database.  Do not contact the owner of this 
//-------------    code directly.  Contact the legal team at ‘ZSLegal’ for assistance.
//-------------   *** WARNING *** 
//----------------------------------------------------------------------------- 

//----------------------------------------------------------------------------- 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: 
//  This is an internal class that enables interactions with Zip archives 
//  for OPC scenarios
// 
// History:
//  11/19/2004: IgorBel: Initial creation.
//  10/21/2005: brucemac: Apply security mitigations
// 
//-----------------------------------------------------------------------------
 
using System; 
using System.Diagnostics;
using System.IO; 
using System.Text;
using System.Collections;
using System.Globalization;
using System.Runtime.Serialization; 
using System.Windows;
using MS.Internal.IO.Packaging;  // for PackagingUtilities 
 
namespace MS.Internal.IO.Zip
{ 
    /// 
    /// This is the main class of the actual ZIP IO implementation. It is primary responsibility
    /// is to maintain the map and status of the parsed and loaded areas(blocks) of the file.
    /// It is also supports manipulating this map (adding and deleting blocks) 
    /// 
    internal class ZipIOBlockManager : IDisposable, IEnumerable 
    { 
        //------------------------------------------------------
        // 
        //  Public Methods
        //
        //-----------------------------------------------------
        public void Dispose() 
        {
            Dispose(true); 
            GC.SuppressFinalize(this); 
        }
 
        IEnumerator IEnumerable.GetEnumerator()
        {
            CheckDisposed();
 
            return _blockList.GetEnumerator();
        } 
 
        //------------------------------------------------------
        // 
        //  Internal Properties
        //
        //------------------------------------------------------
        ///  
        /// This property returns the status of whether Central directory is loaded or not.
        /// This property is rarely used, as most clients will just ask for CentralDirectoryBlock 
        /// and oif it isn't loaded it will be. 
        /// The only reason to use IsCentralDirectoryBlockLoaded property is to differentiate
        /// scenarios in which some optimization is possible, if central directory isn't loaded yet. 
        /// 
        internal bool IsCentralDirectoryBlockLoaded
        {
            get 
            {
                CheckDisposed(); 
                return (_centralDirectoryBlock != null); 
            }
        } 

        /// 
        /// This property returns the CentralDirectoryBlock and provides lazy load
        /// fuinctionality. This isthe only way other classes can access information 
        /// from the Central Directory Block
        ///  
        internal ZipIOCentralDirectoryBlock CentralDirectoryBlock 
        {
            get 
            {
                CheckDisposed();
                if (_centralDirectoryBlock == null)
                { 
                    // figure out if we are in ZIP64 mode or not
                    if (Zip64EndOfCentralDirectoryBlock.TotalNumberOfEntriesInTheCentralDirectory > 0) 
                    { 
                        LoadCentralDirectoryBlock();
                    } 
                    else
                    {
                        // We need to be aware of the special case of empty Zip Archive
                        // with a single record : End Of Central directory 
                        //In  such cases we should create new CentralDirectoryBlock
                        CreateCentralDirectoryBlock(); 
                    } 
                }
 
                return _centralDirectoryBlock;
            }
        }
 
        /// 
        /// This property returns the Zip64EndOfCentralDirectoryBlock and provides lazy load 
        /// fuinctionality. This is the only way other classes can access information 
        /// from the Zip64EndOfCentralDirectoryBlock
        ///  
        internal ZipIOZip64EndOfCentralDirectoryBlock Zip64EndOfCentralDirectoryBlock
        {
            get
            { 
                CheckDisposed();
 
                if (_zip64EndOfCentralDirectoryBlock == null) 
                {
                    CreateLoadZip64Blocks(); 
                }

                return _zip64EndOfCentralDirectoryBlock;
            } 
        }
 
        ///  
        /// This property returns the Zip64EndOfCentralDirectoryLocatorBlock and provides lazy load
        /// fuinctionality. This is the only way other classes can access information 
        /// from the Zip64EndOfCentralDirectoryLocator Block
        /// 
        internal ZipIOZip64EndOfCentralDirectoryLocatorBlock Zip64EndOfCentralDirectoryLocatorBlock
        { 
            get
            { 
                CheckDisposed(); 

                if (_zip64EndOfCentralDirectoryLocatorBlock == null) 
                {
                    CreateLoadZip64Blocks();
                }
 
                return _zip64EndOfCentralDirectoryLocatorBlock;
            } 
        } 

        ///  
        /// This property returns the CentralDirectoryBlock and provides lazy load
        /// fuinctionality. This is the only way other classes can access information
        /// from the Central Directory Block
        ///  
        internal ZipIOEndOfCentralDirectoryBlock EndOfCentralDirectoryBlock
        { 
            get 
            {
                CheckDisposed(); 
                if (_endOfCentralDirectoryBlock == null)
                {
                    LoadEndOfCentralDirectoryBlock();
                } 

                return _endOfCentralDirectoryBlock; 
            } 
        }
 
        internal Stream Stream
        {
            get
            { 
                CheckDisposed();
                return _archiveStream; 
            } 
        }
 
        internal bool Streaming
        {
            get
            { 
                CheckDisposed();
                return _openStreaming; 
            } 
        }
 
        internal BinaryReader BinaryReader
        {
            get
            { 
                CheckDisposed();
                Debug.Assert(!_openStreaming, "Not legal in Streaming mode"); 
 
                if (_binaryReader == null)
                { 
                    _binaryReader = new BinaryReader(Stream, Encoding);
                }
                return _binaryReader;
            } 
        }
 
        internal BinaryWriter BinaryWriter 
        {
            get 
            {
                CheckDisposed();
                if (_binaryWriter == null)
                { 
                    _binaryWriter = new BinaryWriter(Stream, Encoding);
                } 
                return _binaryWriter; 
            }
        } 

        internal Encoding Encoding
        {
            get 
            {
                CheckDisposed(); 
                return _encoding; 
            }
        } 

        internal bool DirtyFlag
        {
            set 
            {
                CheckDisposed(); 
                _dirtyFlag = value; 
            }
            get 
            {
                CheckDisposed();
                return _dirtyFlag;
            } 
        }
 
        static internal int MaxFileNameSize 
        {
            get 
            {
                return UInt16.MaxValue;
            }
        } 

        //----------------------------------------------------- 
        // 
        //  Internal Methods
        // 
        //------------------------------------------------------

        internal void  CreateEndOfCentralDirectoryBlock()
        { 
            CheckDisposed();
 
            // Prevent accidental call if underlying stream is non-empty since 
            // any legal zip archive contains an EOCD block.
            Debug.Assert(_openStreaming || _archiveStream.Length == 0); 

            // Disallow multiple calls.
            Debug.Assert(_endOfCentralDirectoryBlock == null);
 
            // construct Block find it and parse it
            long blockOffset = 0;   // this will be updated later 
            _endOfCentralDirectoryBlock = ZipIOEndOfCentralDirectoryBlock.CreateNew(this, blockOffset); 

            // this will add a block to the tail 
            AppendBlock(_endOfCentralDirectoryBlock);
            DirtyFlag = true;
        }
 
        internal void LoadEndOfCentralDirectoryBlock()
        { 
            Debug.Assert(_endOfCentralDirectoryBlock == null); 
            Debug.Assert(!_openStreaming, "Not legal in Streaming mode");
 
            // construct Block find it and parse it
            _endOfCentralDirectoryBlock = ZipIOEndOfCentralDirectoryBlock.SeekableLoad(this);

            //ask block manager to MAP this block 
            MapBlock(_endOfCentralDirectoryBlock);
        } 
 
        internal ZipIOLocalFileBlock CreateLocalFileBlock(string zipFileName, CompressionMethodEnum compressionMethod, DeflateOptionEnum deflateOption)
        { 
            CheckDisposed();

            // we are guaranteed uniqueness at this point , so let's just add a
            // block at the end of the file, just before the central directory 
            // construct Block find it and parse it
 
            // STREAMING Mode: 
            //   NOTE: _blockList is NOT in offset order except the last four blocks
            //      (CD, Zip64 EOCD, Zip64 EOCD Locator, and EOCD) 

            ZipIOLocalFileBlock localFileBlock = ZipIOLocalFileBlock.CreateNew(this,
                                                zipFileName,
                                                compressionMethod, 
                                                deflateOption);
 
            InsertBlock(CentralDirectoryBlockIndex, localFileBlock); 

            CentralDirectoryBlock.AddFileBlock(localFileBlock); 

            DirtyFlag = true;

            return localFileBlock; 
        }
 
        internal ZipIOLocalFileBlock LoadLocalFileBlock(string zipFileName) 
        {
            CheckDisposed(); 

            Debug.Assert(!_openStreaming, "Not legal in Streaming mode");
            Debug.Assert(CentralDirectoryBlock.FileExists(zipFileName)); // it must be in the central directory
 
            // construct Block find it and parse it
            ZipIOLocalFileBlock localFileBlock = ZipIOLocalFileBlock.SeekableLoad(this, zipFileName); 
 
            MapBlock(localFileBlock);
            return localFileBlock; 
        }

        internal void RemoveLocalFileBlock(ZipIOLocalFileBlock localFileBlock)
        { 
            CheckDisposed();
 
            Debug.Assert(!_openStreaming, "Not legal in Streaming mode"); 

            Debug.Assert(localFileBlock != null, " At this point local File block must be preloaded"); 
            Debug.Assert(CentralDirectoryBlock.FileExists(localFileBlock.FileName),
                            " At this point local File block must be mapped in central directory");

 
            // remove it from our list
            _blockList.Remove(localFileBlock); 
 
            // remove this from Central Directory
            CentralDirectoryBlock.RemoveFileBlock(localFileBlock.FileName); 
            DirtyFlag = true;

            // at this point we can Dispose it to make sure that any calls
            // to this file block through outstanding indirect references will result in object Disposed exception 
            localFileBlock.Dispose();
        } 
 
        internal void MoveData(long moveBlockSourceOffset, long moveBlockTargetOffset, long moveBlockSize)
        { 
            Debug.Assert(moveBlockSize >=0);
            Debug.Assert(!_openStreaming, "Not legal in Streaming mode");

            if ((moveBlockSize ==0) || (moveBlockSourceOffset == moveBlockTargetOffset)) 
            {
                //trivial empty move case 
                return; 
            }
 
            checked
            {
                byte[] tempBuffer = new byte [Math.Min(moveBlockSize,0x100000)]; // min(1mb, requested block size)
                long bytesMoved = 0; 
                while(bytesMoved < moveBlockSize)
                { 
                    long subBlockSourceOffset; 
                    long subBlockTargetOffset;
                    int subBlockSize = (int)Math.Min((long)tempBuffer.Length,  moveBlockSize - bytesMoved); 

                    if (moveBlockSourceOffset > moveBlockTargetOffset)
                    {
                        subBlockSourceOffset = moveBlockSourceOffset  + bytesMoved; 
                        subBlockTargetOffset = moveBlockTargetOffset  + bytesMoved;
                    } 
                    else 
                    {
                        subBlockSourceOffset = moveBlockSourceOffset + moveBlockSize - bytesMoved - subBlockSize; 
                        subBlockTargetOffset =  moveBlockTargetOffset  + moveBlockSize - bytesMoved - subBlockSize;
                    }

                    _archiveStream.Seek(subBlockSourceOffset, SeekOrigin.Begin); 
                    int bytesRead = PackagingUtilities.ReliableRead(_archiveStream, tempBuffer, 0, subBlockSize);
 
                    if (bytesRead != subBlockSize) 
                    {
                        throw new FileFormatException(SR.Get(SRID.CorruptedData)); 
                    }

                    _archiveStream.Seek(subBlockTargetOffset, SeekOrigin.Begin);
                    _archiveStream.Write(tempBuffer, 0, subBlockSize); 

                    checked{bytesMoved += subBlockSize;} 
                } 
            }
        } 

        /// 
        /// Save - stream level
        ///  
        /// 
        /// closing or flushing 
        internal void SaveStream(ZipIOLocalFileBlock blockRequestingFlush, bool closingFlag) 
        {
            // Prevent recursion when propagating Flush or Disposed to our minions 
            // because ZipIOFileItemStream.Flush calls us.
            if (_propagatingFlushDisposed)
                return;
            else 
                _propagatingFlushDisposed = true;   // enter first time
 
            try 
            {
                // redirect depending on our mode 
                if (_openStreaming)
                {
                    StreamingSaveStream(blockRequestingFlush, closingFlag);
                } 
                else
                    SaveContainer(false); 
            } 
            finally
            { 
                // all done so restore state
                _propagatingFlushDisposed = false;
            }
        } 

        ///  
        /// Save - container level 
        /// 
        /// true if closing, false if flushing 
        internal void Save(bool closingFlag)
        {
            CheckDisposed();
 
            // Prevent recursion when propagating Flush or Disposed to our minions
            // because ZipIOFileItemStream.Flush calls us. 
            if (_propagatingFlushDisposed) 
                return;
            else 
                _propagatingFlushDisposed = true;   // enter first time

            try
            { 
                // redirect depending on our mode
                if (_openStreaming) 
                { 
                    StreamingSaveContainer(closingFlag);
                } 
                else
                    SaveContainer(closingFlag);
            }
            finally 
            {
                // all done so restore state 
                _propagatingFlushDisposed = false; 
            }
        } 

        /// 
        /// Constructor
        ///  
        /// stream we operate on
        ///  
        /// true if we own the stream and are expected to close it when we are disposed 
        internal ZipIOBlockManager(Stream archiveStream, bool streaming, bool ownStream)
        { 
            Debug.Assert(archiveStream != null);

            _archiveStream = archiveStream;
            _openStreaming = streaming; 
            _ownStream = ownStream;
 
            if (streaming) 
            {
                // wrap the archive stream in a WriteTimeStream which keeps track of current position 
                _archiveStream = new WriteTimeStream(_archiveStream);
            }
            else if (archiveStream.Length > 0)
            { 
                // for non-empty stream we need to map the whole stream into a raw data block
                // which helps keep track of shifts and dirty areas 
                ZipIORawDataFileBlock rawBlock = ZipIORawDataFileBlock.Assign(this, 0, archiveStream.Length); 

                _blockList.Add(rawBlock); 
            }
        }

        internal static UInt32 ToMsDosDateTime(DateTime dateTime) 
        {
 
            UInt32 result = 0; 

            result |= (((UInt32)dateTime.Second) /2) & 0x1F;   // seconds need to be divided by 2 
                                                                                // as they stored in 5 bits
            result |= (((UInt32)dateTime.Minute) & 0x3F) << 5;
            result |= (((UInt32)dateTime.Hour) & 0x1F) << 11;
 
            result |= (((UInt32)dateTime.Day) & 0x1F) << 16;
            result |= (((UInt32)dateTime.Month) & 0xF) << 21; 
            result |= (((UInt32)(dateTime.Year - 1980)) & 0x7F) << 25; 

            return result; 
        }

        internal static DateTime FromMsDosDateTime(UInt32 dosDateTime)
        { 
            int seconds = (int)((dosDateTime & 0x1F) << 1); // seconds need to be multiplied by 2
                                                                                       // as they stored in 5 bits 
            int minutes  = (int)((dosDateTime >> 5) & 0x3F); 
            int hours = (int)((dosDateTime >> 11) & 0x1F);
 
            int day = (int)((dosDateTime >> 16) & 0x1F);
            int month  =(int)((dosDateTime >> 21) & 0xF);
            int year = (int)(1980 + ((dosDateTime >> 25) & 0x7F));
 
            //this will throw if parameters are out of range
            return new DateTime(year, month,day,hours,minutes,seconds); 
        } 

        ///  
        /// This is standard way to normalize Zip File Item names. At this point we only
        /// getting rid of the spaces. The Exists calls are responsible or making sure
        /// that they check for uniqueness in a case insensitive manner. It is up to the
        /// higher levels to add stricter restrictions like URI character set, and so on. 
        /// 
        static internal string ValidateNormalizeFileName(string zipFileName) 
        { 
            // Validate parameteres
            if (zipFileName == null) 
            {
                throw new ArgumentNullException("zipFileName");
            }
 
            if (zipFileName.Length > ZipIOBlockManager.MaxFileNameSize)
            { 
                throw new ArgumentOutOfRangeException("zipFileName"); 
            }
 
            zipFileName = zipFileName.Trim();

            if (zipFileName.Length < 1)//it must be at least one character
            { 
                throw new ArgumentOutOfRangeException("zipFileName");
            } 
 
            //Based on the  Appnote :
            //    << The path stored should not contain a drive or device letter, or a leading slash.  >> 

            return zipFileName;
        }
 
        //-----------------------------------------------------
        //  Internal helper CopyBytes functions for storing data into a byte[] 
        // it is a similar to a BinaryWriter , but not for streams but ratrher 
        // for byte[]
        // These functiona used in the Extra field parsing, as that functionality is buit 
        // in terms of byte[] not streams
        //-----------------------------------------------------
        internal static int CopyBytes(Int16 value, byte[] buffer, int offset)
        { 
            Debug.Assert(checked(buffer.Length-offset) >= sizeof(Int16));
 
             byte[] tempBuffer = BitConverter.GetBytes(value); 
            Array.Copy(tempBuffer, 0, buffer, offset, tempBuffer.Length);
 
            return offset + tempBuffer.Length;
        }

        internal static int CopyBytes(Int32 value, byte[] buffer, int offset) 
        {
            Debug.Assert(checked(buffer.Length-offset) >= sizeof(Int32)); 
 
             byte[] tempBuffer = BitConverter.GetBytes(value);
            Array.Copy(tempBuffer, 0, buffer, offset, tempBuffer.Length); 

            return offset + tempBuffer.Length;
        }
 
        internal static int CopyBytes(Int64 value, byte[] buffer, int offset)
        { 
            Debug.Assert(checked(buffer.Length-offset) >= sizeof(Int64)); 

             byte[] tempBuffer = BitConverter.GetBytes(value); 
            Array.Copy(tempBuffer, 0, buffer, offset, tempBuffer.Length);

            return offset + tempBuffer.Length;
        } 

        internal static int CopyBytes(UInt16 value, byte[] buffer, int offset) 
        { 
            Debug.Assert(checked(buffer.Length-offset) >= sizeof(UInt16));
 
             byte[] tempBuffer = BitConverter.GetBytes(value);
            Array.Copy(tempBuffer, 0, buffer, offset, tempBuffer.Length);

            return offset + tempBuffer.Length; 
        }
 
        internal static int CopyBytes(UInt32 value, byte[] buffer, int offset) 
        {
            Debug.Assert(checked(buffer.Length-offset) >= sizeof(UInt32)); 

             byte[] tempBuffer = BitConverter.GetBytes(value);
            Array.Copy(tempBuffer, 0, buffer, offset, tempBuffer.Length);
 
            return offset + tempBuffer.Length;
        } 
 
        internal static int CopyBytes(UInt64 value, byte[] buffer, int offset)
        { 
            Debug.Assert(checked(buffer.Length-offset) >= sizeof(UInt64));

             byte[] tempBuffer = BitConverter.GetBytes(value);
            Array.Copy(tempBuffer, 0, buffer, offset, tempBuffer.Length); 

            return offset + tempBuffer.Length; 
        } 

        internal static UInt64 ConvertToUInt64(UInt32 loverAddressValue, UInt32 higherAddressValue) 
        {
            return checked((UInt64)loverAddressValue + (((UInt64)higherAddressValue)  << 32));
        }
 
        internal static ZipIOVersionNeededToExtract CalcVersionNeededToExtractFromCompression
                                                                                    (CompressionMethodEnum compression) 
        { 
            switch (compression)
            { 
                case CompressionMethodEnum.Stored:
                        return ZipIOVersionNeededToExtract.StoredData;
                case CompressionMethodEnum.Deflated:
                        return ZipIOVersionNeededToExtract.DeflatedData; 
                default:
                        throw new NotSupportedException();    // Deflated64 this is OFF 
            } 
        }
 

        /// 
        /// This is the common Pre Save notiofication handler for
        ///  RawDataFile Block and File Item Stream 
        /// It makes assumption that the overlap generally start coming in at the beginning of a
        /// large disk image, so we should only try to cache cache overlaped data in the prefix 
        /// of the disk block 
        /// Block can also return a value indicating whether PreSaveNotification should be extended to the blocks that are positioned after
        /// it in the Block List. For example, if block has completely handled PreSaveNotification in a way that it cached the whole area that 
        /// was in danger (of being overwritten) it means that no blocks need to worry about this anymore. After all no 2 blocks should have
        /// share on disk buffers. Another scenario is when block can determine that area in danger is positioned before the block's on disk
        /// buffers; this means that all blocks that are positioned later in the block list do not need to worry about this PreSaveNotification
        /// as their buffers should be positioned even further alone in the file. 
        /// 
        internal static PreSaveNotificationScanControlInstruction CommonPreSaveNotificationHandler( 
                                                            Stream stream, 
                                                            long offset, long size,
                                                            long onDiskOffset, long onDiskSize, 
                                                            ref SparseMemoryStream cachePrefixStream)
        {
            checked
            { 
                Debug.Assert(size >=0);
                Debug.Assert(offset >=0); 
                Debug.Assert(onDiskSize >=0); 
                Debug.Assert(onDiskOffset >=0);
 
                // trivial request
                if (size == 0)
                {
                    // The area being overwritten is of size 0 so there is no need to notify any blocks about this. 
                    return PreSaveNotificationScanControlInstruction.Stop;
                } 
 
                if (cachePrefixStream != null)
                { 
                    // if we have something in cache prefix buffer  we only should check whatever tail data isn't cached
                    checked{onDiskOffset += cachePrefixStream.Length;}
                    checked{onDiskSize -= cachePrefixStream.Length;}
                    Debug.Assert(onDiskSize >=0); 
                }
 
                if (onDiskSize == 0) 
                {
                    // the raw data block happened to be fully cached 
                    // in this case (onDiskSize==0) can not be used as a reliable indicator of the position of the
                    // on disk buffer relative to the other; it is just an indicator of an empty buffer which might have a meaningless offset
                    // that shouldn't be driving any decisions
                    return PreSaveNotificationScanControlInstruction.Continue; 
                }
 
                // we need to first find out if the raw data that isn't cached yet overlaps with any disk space 
                // that is about to be overriden
                long overlapBlockOffset; 
                long  overlapBlockSize;

                PackagingUtilities.CalculateOverlap(onDiskOffset, onDiskSize,
                                           offset, size , 
                                            out overlapBlockOffset, out overlapBlockSize);
                if (overlapBlockSize <= 0) 
                { 
                    // No overlap , we can ignore this message.
                    // In addition to that, if (onDiskOffset > offset) it means that, given the fact that all blocks after 
                    // the current one will have even larger offsets, they couldn't possibly overlap with (offset ,size ) chunk .
                    return (onDiskOffset > offset) ?
                                                PreSaveNotificationScanControlInstruction.Stop  :
                                                PreSaveNotificationScanControlInstruction.Continue; 
                }
 
                // at this point we have an overlap, we need to read the data that is overlapped 
                // and merge it with whatever we already have in cache
                // let's figure out the part that isn't cached yet, and needs to be 
                long blockSizeToCache;
                checked
                {
                    blockSizeToCache = overlapBlockOffset + overlapBlockSize - onDiskOffset; 
                }
                Debug.Assert(blockSizeToCache >0); // there must be a non empty block at this point that needs to be cached 
 
                // We need to ensure that we do have a place to store this data
                if (cachePrefixStream == null) 
                {
                    cachePrefixStream = new SparseMemoryStream(_lowWaterMark, _highWaterMark);
                }
                else 
                {
                    // if we already have some cached prefix data we have to make sure we are 
                    // appending new data tro the tail of the already cached chunk 
                    cachePrefixStream.Seek(0, SeekOrigin.End);
                } 

                stream.Seek(onDiskOffset, SeekOrigin.Begin);
                long bytesCopied = PackagingUtilities.CopyStream(stream, cachePrefixStream, blockSizeToCache, 4096);
 
                if (bytesCopied  != blockSizeToCache)
                { 
                    throw new FileFormatException(SR.Get(SRID.CorruptedData)); 
                }
 
                // if the contdition below is true it means that, given the fact that all blocks after
                // the current one will have even larger offsets, they couldn't possibly overlap with (offset ,size ) chunk
                return ((onDiskOffset + onDiskSize) >=  (offset + size)) ?
                                            PreSaveNotificationScanControlInstruction.Stop  : 
                                            PreSaveNotificationScanControlInstruction.Continue;
            } 
        } 

        //----------------------------------------------------- 
        //
        //  Protected Methods
        //
        //------------------------------------------------------ 
        protected void Dispose(bool disposing)
        { 
            if (disposing) 
            {
                // multiple calls are fine - just ignore them 
                if (!_disposedFlag)
                {
                    // Prevent recursion into Save() when propagating Flush or Disposed to our minions
                    // because ZipIOFileItemStream.Flush calls us. 
                    if (_propagatingFlushDisposed)
                        return; 
                    else 
                        _propagatingFlushDisposed = true;   // enter first time
 
                    try
                    {
                        try
                        { 
                            foreach (IZipIOBlock block in _blockList)
                            { 
                                IDisposable disposableBlock = block as IDisposable; 
                                if (disposableBlock != null)
                                { 
                                    // only some Blocks are disposable, most are not
                                    disposableBlock.Dispose();
                                }
                            } 
                        }
                        finally 
                        { 
                            // If we own the stream, we should close it.
                            // If not, we cannot even close the binary reader or writer as these close the 
                            // underlying stream on us.
                            if (_ownStream)
                            {
                                if (_binaryReader != null) 
                                {   // this one might be null of we have been only writing
                                    _binaryReader.Close(); 
                                } 

                                if (_binaryWriter != null) 
                                {   // this one might be null of we have been only reading
                                    _binaryWriter.Close();
                                }
 
                                _archiveStream.Close();
                            } 
                        } 
                    }
                    finally 
                    {
                        _blockList = null;
                        _encoding = null;
                        _endOfCentralDirectoryBlock = null; 
                        _centralDirectoryBlock = null;
 
                        _disposedFlag = true; 
                        _propagatingFlushDisposed = false;   // reset
                    } 
                }
            }
        }
 
        //-----------------------------------------------------
        // 
        //  Private Properties 
        //
        //------------------------------------------------------ 
        /// 
        /// This property returns the index of CentralDirectoryBlock within _blockList
        /// 
        private int CentralDirectoryBlockIndex 
        {
            get 
            { 
                Invariant.Assert(_blockList.Count >= _requiredBlockCount);
                Debug.Assert(_centralDirectoryBlock != null 
                                && _endOfCentralDirectoryBlock != null
                                && _zip64EndOfCentralDirectoryBlock != null
                                && _zip64EndOfCentralDirectoryLocatorBlock != null);
 
                // We always have following blocks at the end of the block lists:
                //  CD, Zip64 EOCD, Zip64 EOCD Locator, and EOCD 
                // Thus the index of CD can be calculated from the total number of blocks 
                //  and _requiredBlockCount which is 4
                return _blockList.Count - _requiredBlockCount; 
            }
        }

 
        //------------------------------------------------------
        // 
        //  Private Methods 
        //
        //----------------------------------------------------- 
        /// 
        /// Throwes exception if object already Disposed/Closed.
        /// 
        private void CheckDisposed() 
        {
            if (_disposedFlag) 
            { 
                throw new ObjectDisposedException(null, SR.Get(SRID.ZipArchiveDisposed));
            } 
        }

        /// 
        /// Save - container level 
        /// 
        /// true if closing, false if flushing 
        private void SaveContainer(bool closingFlag) 
        {
            CheckDisposed(); 
            Debug.Assert(!_openStreaming, "Not legal in Streaming mode");


            if (!closingFlag && !DirtyFlag) 
            {
                // we are trying to save some cycles in the case of the subsequent Flush calls 
                // that do not close the container 
                // if it is being closed DirtyFlag isn't reliable as the Compressed streams carry
                // some extra bytes after flushing and only write them out on closing. 
                return;
            }

            // We need a separate cycle to update all the cross block references prior to saving blocks 
            // specifically the central directory needs "dirty" information from blocks in order to properly
            // update it's references, otherwise (if we call UpdateReferences and Save in the same loop) 
            // information about block shifts will be lost by the time we ask central directory to update it's 
            // references
 
            // offset of the first block
            long currentOffset = 0; // ZIP64 review type here
            foreach (IZipIOBlock currentBlock in _blockList)
            { 
                // move block so it is positioned right after the previous block
                currentBlock.Move(currentOffset - currentBlock.Offset); 
 
                // this will update references and as well as other internal structures (size)
                // specifically for the FileItemBlock it will flush buffers of 
                // all the outstanding streams
                currentBlock.UpdateReferences(closingFlag);

                //advance current stream position according to the size of the block 
                checked{currentOffset += currentBlock.Size;}
            } 
 
            // save dirty blocks
            bool dirtyBlockFound = false; 

            int blockListCount  = _blockList.Count;
            for (int i = 0; i < blockListCount ; i++)
            { 
                IZipIOBlock currentBlock = (IZipIOBlock)_blockList[i];
 
                if (currentBlock.GetDirtyFlag(closingFlag)) 
                {
                    dirtyBlockFound = true; 

                    long currentBlockOffset = currentBlock.Offset;
                    long currentBlockSize = currentBlock.Size;
 
                    if (currentBlockSize > 0)
                    { 
                        // before saving we need to warn all the blocks that have still some data on disk 
                        // that might be overriden
                        // second loop must start at the current position of the extrrnal loop 
                        // as all the items before have been saved
                        for (int j = i + 1; j < blockListCount; j++)
                        {
                            // This is an optimization which enabled us to stop going through the 
                            // tail blocks as soon as we find a block that returns a status indicating
                            // that it took care of the tail of the target area or is positioned after the 
                            // target area. 
                            if (((IZipIOBlock)_blockList[j]).PreSaveNotification(currentBlockOffset, currentBlockSize) ==
                                        PreSaveNotificationScanControlInstruction.Stop ) 
                            {
                                break;
                            }
                        } 
                    }
 
                    currentBlock.Save();    // Even if currentBlockSize == 0, call Save to clear DirtyFlag 
                }
            } 

            // originally we have had an assert for the case when no changes were made to the file
            // but calculated size didn't match the actual stream size.
            // As a result of the XPS Viewer dynamically switching streams underneath ZIP IO, we 
            // need to treat this case as a normal non-dirty scenario. So if nothing changed and
            // nothing was written out we shouldn't even validate whether stream underneath 
            // was modified in any way or not (even such simple modifications as an unexpected 
            // Stream.Length change). If it was modified by someone we assume that the stream
            // owner was aware of it's action. 
            if (dirtyBlockFound && (Stream.Length > currentOffset))
            {
                Stream.SetLength(currentOffset);
            } 

            Stream.Flush(); 
            DirtyFlag = false; 
        }
 
        /// 
        /// Streaming version of Save routine
        /// 
        /// true if closing the package 
        private void StreamingSaveContainer(bool closingFlag)
        { 
            // STREAMING Mode: 
            //   NOTE: _blockList is NOT in offset order except the last four blocks
            //      (CD, Zip64 EOCD, Zip64 EOCD Locator, and EOCD) 

            try
            {
                // save dirty blocks 
                long currentOffset = 0;
                for (int i = 0; i < _blockList.Count; i++) 
                { 
                    IZipIOBlock currentBlock = (IZipIOBlock)_blockList[i];
                    ZipIOLocalFileBlock localFileBlock = currentBlock as ZipIOLocalFileBlock; 

                    if (localFileBlock == null)
                    {
                        if (closingFlag) 
                        {
                            // Move block so it is positioned right after the previous block. 
                            // No need for nested loops like in SaveContainer because none of these 
                            // calls can cause a block to move in the Streaming case.
                            currentBlock.Move(currentOffset - currentBlock.Offset); 
                            currentBlock.UpdateReferences(closingFlag);
                            if (currentBlock.GetDirtyFlag(closingFlag))
                            {
                                currentBlock.Save(); 
                            }
                        } 
                    } 
                    else if (currentBlock.GetDirtyFlag(closingFlag))
                    { 
                        // no need to call UpdateReferences in streaming mode for regular
                        // local file blocks because
                        // we manually emit the local file header and the local file descriptor
                        localFileBlock.SaveStreaming(closingFlag); 
                    }
                    checked{currentOffset += currentBlock.Size;} 
                } 

                Stream.Flush(); 
            }
            finally
            {
                // all done so restore state 
                _propagatingFlushDisposed = false;
            } 
        } 

        ///  
        /// Flush was called on a ZipIOFileItemStream
        /// 
        /// block that owns the stream that Flush was called on
        /// close or dispose 
        private void StreamingSaveStream(ZipIOLocalFileBlock blockRequestingFlush, bool closingFlag)
        { 
            // STREAMING MODE: 
            // Flush will do one of two things, depending on the currently open stream:
            // 1) If the currently open stream matches the one passed (or none is currently opened) 
            //    then write will occur to the open stream.
            // 2) Otherwise, the currently opened stream will be flushed and closed, and the
            //    given stream will become the currently opened stream
            // NOTE: _blockList is NOT in offset order except the last four blocks 
            //      (CD, Zip64 EOCD, Zip64 EOCD Locator, and EOCD)
 
            // different stream? 
            if (_streamingCurrentlyOpenStreamBlock != blockRequestingFlush)
            { 
                // need to close the currently opened stream
                // unless its our first time through
                if (_streamingCurrentlyOpenStreamBlock != null)
                { 
                    _streamingCurrentlyOpenStreamBlock.SaveStreaming(true);
                } 
 
                // Now make the given stream the new "currently opened stream".
                _streamingCurrentlyOpenStreamBlock = blockRequestingFlush; 
            }

            // this should now be flushable/closable
            _streamingCurrentlyOpenStreamBlock.SaveStreaming(closingFlag); 

            // if closing - discard the stream because it is now closed 
            if (closingFlag) 
                _streamingCurrentlyOpenStreamBlock = null;
        } 

        private void  CreateCentralDirectoryBlock()
        {
            CheckDisposed(); 
            Debug.Assert(_zip64EndOfCentralDirectoryBlock != null);
 
            // It must not be loaded yet 
            Debug.Assert(!IsCentralDirectoryBlockLoaded);
 
            // The proper position is just before the Zip64EndOfCentralDirectoryRecord
            // Zip64EndOfCentralDirectoryRecord - might be of size 0 (if file is small enough)
            int blockPosition = _blockList.IndexOf(Zip64EndOfCentralDirectoryBlock);
            Debug.Assert(blockPosition >= 0); 

            // construct Block find it and parse it 
            _centralDirectoryBlock = ZipIOCentralDirectoryBlock.CreateNew(this); 

            //ask block manager to insert this this block 
            InsertBlock(blockPosition , _centralDirectoryBlock);
        }

        private void LoadCentralDirectoryBlock() 
        {
            Debug.Assert(_centralDirectoryBlock == null); 
            Debug.Assert(!_openStreaming, "Not legal in Streaming mode"); 

            // construct Block find it and parse it 
            _centralDirectoryBlock = ZipIOCentralDirectoryBlock.SeekableLoad(this);

            //ask block manager to MAP this block
            MapBlock(_centralDirectoryBlock); 
        }
 
        private void  CreateLoadZip64Blocks() 
        {
            CheckDisposed(); 

            Debug.Assert ((_zip64EndOfCentralDirectoryBlock == null) &&
                                  (_zip64EndOfCentralDirectoryLocatorBlock == null));
 
            // determine whether we want to create it or load it
            // this check doesn't provide us with a 100% guarantee. 
            // After discussion we have agreed that this should be sufficient 
            if (!Streaming && EndOfCentralDirectoryBlock.ContainValuesHintingToPossibilityOfZip64 &&
                ZipIOZip64EndOfCentralDirectoryLocatorBlock.SniffTheBlockSignature(this)) 
            {
                // attempt to sniff the header of the
                LoadZip64EndOfCentralDirectoryLocatorBlock();
                LoadZip64EndOfCentralDirectoryBlock(); 
            }
            else 
            { 
                // We delayed validation of some values in End of Central Directory that can give possible
                //  hints for Zip64; Since there is no Zip64 structure, we need to validate them here 
                _endOfCentralDirectoryBlock.ValidateZip64TriggerValues();

                CreateZip64EndOfCentralDirectoryLocatorBlock();
                CreateZip64EndOfCentralDirectoryBlock(); 
            }
        } 
 
        private void  CreateZip64EndOfCentralDirectoryBlock()
        { 
            Debug.Assert(_zip64EndOfCentralDirectoryBlock == null);

            // The proper position is just before the Zip64EndOfCentralDirectoryRecordLocator
            // Zip64EndOfCentralDirectoryRecord - might be of size 0 (if file is small enough) 
            int blockPosition = _blockList.IndexOf(Zip64EndOfCentralDirectoryLocatorBlock);
 
            // construct Block find it and parse it 
            _zip64EndOfCentralDirectoryBlock = ZipIOZip64EndOfCentralDirectoryBlock.CreateNew(this);
 
            //ask block manager to insert this this block
            InsertBlock(blockPosition, _zip64EndOfCentralDirectoryBlock);
        }
 
        private void  LoadZip64EndOfCentralDirectoryBlock()
        { 
            Debug.Assert(_zip64EndOfCentralDirectoryBlock == null); 
            Debug.Assert(!_openStreaming, "Not legal in Streaming mode");
 
            // construct Block find it and parse it
            _zip64EndOfCentralDirectoryBlock = ZipIOZip64EndOfCentralDirectoryBlock.SeekableLoad(this);

            //ask block manager to insert this this block 
            MapBlock(_zip64EndOfCentralDirectoryBlock);
        } 
 
        private void  CreateZip64EndOfCentralDirectoryLocatorBlock()
        { 
            Debug.Assert(_zip64EndOfCentralDirectoryLocatorBlock == null);

            // The proper position is just before the EOCD
            int blockPosition = _blockList.IndexOf(EndOfCentralDirectoryBlock); 

            // construct Block find it and parse it 
            _zip64EndOfCentralDirectoryLocatorBlock = ZipIOZip64EndOfCentralDirectoryLocatorBlock.CreateNew(this); 

            //ask block manager to MAP this block 
            InsertBlock(blockPosition, _zip64EndOfCentralDirectoryLocatorBlock);
        }

        private void  LoadZip64EndOfCentralDirectoryLocatorBlock() 
        {
            Debug.Assert(_zip64EndOfCentralDirectoryLocatorBlock == null); 
            Debug.Assert(!_openStreaming, "Not legal in Streaming mode"); 

            // construct Block find it and parse it 
            _zip64EndOfCentralDirectoryLocatorBlock = ZipIOZip64EndOfCentralDirectoryLocatorBlock.SeekableLoad(this);

            //ask block manager to MAP this block
            MapBlock(_zip64EndOfCentralDirectoryLocatorBlock); 
        }
 
        private void MapBlock(IZipIOBlock block) 
        {
            // as we map a block to existing file space it must be not dirty. 
            Debug.Assert(!block.GetDirtyFlag(true)); // closingFlag==true used as a more conservative option
            Debug.Assert(!_openStreaming, "Not legal in Streaming mode");

            for (int blockIndex = _blockList.Count - 1; blockIndex >= 0; --blockIndex) 
            {
                // if we need to find a RawDataBlock that maps to the target area 
                ZipIORawDataFileBlock rawBlock = _blockList[blockIndex] as ZipIORawDataFileBlock; 

                //check the original loaded RawBlock size / offset against the new block 
                if ((rawBlock != null) && rawBlock.DiskImageContains(block))
                {
                    ZipIORawDataFileBlock prefixBlock, suffixBlock;
 
                    //split raw block into prefixRawBlock, SuffixRawBlock
                    rawBlock.SplitIntoPrefixSuffix(block, out prefixBlock, out suffixBlock); 
 
                    _blockList.RemoveAt(blockIndex); // remove the old big raw data block
 
                    // add suffix Raw data block
                    if (suffixBlock != null)
                    {
                        _blockList.Insert(blockIndex, suffixBlock); 
                    }
 
                    // add new mapped block 
                    _blockList.Insert(blockIndex, block);
 
                    // add prefix Raw data block
                    if (prefixBlock != null)
                    {
                        _blockList.Insert(blockIndex, prefixBlock); 
                    }
 
                    return; 
                }
            } 

            // we couldn't find a raw data block for mapping this, we can only throw
            throw new FileFormatException(SR.Get(SRID.CorruptedData));
        } 

        private void InsertBlock(int blockPosition, IZipIOBlock block) 
        { 
            // as we are adding a new block it must be dirty unless its size is 0
            Debug.Assert(block.GetDirtyFlag(true) ||   // closingFlag==true used as a more conservative option 
                                    block.Size == 0);

            _blockList.Insert(blockPosition, block);
        } 

        private void AppendBlock(IZipIOBlock block) 
        { 
            // as we are adding a new block it must be dirty unless its size is 0
            Debug.Assert(block.GetDirtyFlag(true) || // closingFlag==true used as a more conservative option 
                                    block.Size == 0);

            // CentralDirectory persistence logic relies on the fact that we always add headers in a fashion that
            // matches the order of the corresponding file items in the physical archive (currently to the end of the list). 
            // If this invariant is violated, the corresponding central directory persistence logic must be updated.
            _blockList.Add(block); 
        } 

        // this flag is used for Perf reasons, it doesn't carry any additional information that isn't stored somewhere 
        // else. In order to prevent complex dirty calculations on the sequential flush calls, we are going to keep
        // this flag which will be set to true at the end of the flush (or close). This flag will be set from the ZipArchive
        // and CrcCalculating entry points that can potentially make our structure dirty.
        // This flag is only used for non-streaming cases. In streaming cases we do not believe there is a perf 
        // penalty of that nature.
        private bool _dirtyFlag = false; 
 
        private bool _disposedFlag;
        private bool _propagatingFlushDisposed;              // if true, we ignore calls back to Save to prevent recursion 
        private Stream _archiveStream;
        private bool _openStreaming;
        private bool _ownStream;                                    // true if we own the archive stream
 
        // Streaming Mode Only: stream that is currently able to write without interfering with other streams
        private ZipIOLocalFileBlock _streamingCurrentlyOpenStreamBlock; 
 
        private BinaryReader _binaryReader;
        private BinaryWriter _binaryWriter; 

        private const int _initialBlockListSize = 50;
        private ArrayList _blockList = new ArrayList(_initialBlockListSize);
 
        private ASCIIEncoding _encoding = new ASCIIEncoding();
 
        ZipIOZip64EndOfCentralDirectoryBlock  _zip64EndOfCentralDirectoryBlock; 
        ZipIOZip64EndOfCentralDirectoryLocatorBlock _zip64EndOfCentralDirectoryLocatorBlock;
        ZipIOEndOfCentralDirectoryBlock _endOfCentralDirectoryBlock; 
        ZipIOCentralDirectoryBlock _centralDirectoryBlock;

        private const long _lowWaterMark = 0x19000;                 // we definately would like to keep everythuing under 100 KB in memory
        private const long _highWaterMark = 0xA00000;   // we would like to keep everything over 10 MB on disk 
        private const int _requiredBlockCount = 4;      // We always have following blocks: CD, Zip64 EOCD, Zip64 EOCD Locator, and EOCD
                                                       // This value is used to calculate the index of CD within _blockList 
    } 
}
 

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