Free cookie consent management tool by TermsFeed Policy Generator

source: branches/ScopedBasicAlgorithm/HeuristicLab.ExtLibs/HeuristicLab.EPPlus/4.0.3/EPPlus-4.0.3/Packaging/DotNetZip/ZipSegmentedStream.cs @ 14419

Last change on this file since 14419 was 12074, checked in by sraggl, 10 years ago

#2341: Added EPPlus-4.0.3 to ExtLibs

File size: 18.3 KB
Line 
1// ZipSegmentedStream.cs
2// ------------------------------------------------------------------
3//
4// Copyright (c) 2009-2011 Dino Chiesa.
5// All rights reserved.
6//
7// This code module is part of DotNetZip, a zipfile class library.
8//
9// ------------------------------------------------------------------
10//
11// This code is licensed under the Microsoft Public License.
12// See the file License.txt for the license details.
13// More info on: http://dotnetzip.codeplex.com
14//
15// ------------------------------------------------------------------
16//
17// last saved (in emacs):
18// Time-stamp: <2011-July-13 22:25:45>
19//
20// ------------------------------------------------------------------
21//
22// This module defines logic for zip streams that span disk files.
23//
24// ------------------------------------------------------------------
25
26
27using System;
28using System.Collections.Generic;
29using System.IO;
30
31namespace OfficeOpenXml.Packaging.Ionic.Zip
32{
33    internal class ZipSegmentedStream : System.IO.Stream
34    {
35        enum RwMode
36        {
37            None = 0,
38            ReadOnly = 1,
39            Write = 2,
40            //Update = 3
41        }
42
43        private RwMode rwMode;
44        private bool _exceptionPending; // **see note below
45        private string _baseName;
46        private string _baseDir;
47        //private bool _isDisposed;
48        private string _currentName;
49        private string _currentTempName;
50        private uint _currentDiskNumber;
51        private uint _maxDiskNumber;
52        private int _maxSegmentSize;
53        private System.IO.Stream _innerStream;
54
55        // **Note regarding exceptions:
56        //
57        // When ZipSegmentedStream is employed within a using clause,
58        // which is the typical scenario, and an exception is thrown
59        // within the scope of the using, Dispose() is invoked
60        // implicitly before processing the initial exception.  If that
61        // happens, this class sets _exceptionPending to true, and then
62        // within the Dispose(bool), takes special action as
63        // appropriate. Need to be careful: any additional exceptions
64        // will mask the original one.
65
66        private ZipSegmentedStream() : base()
67        {
68            _exceptionPending = false;
69        }
70
71        public static ZipSegmentedStream ForReading(string name,
72                                                    uint initialDiskNumber,
73                                                    uint maxDiskNumber)
74        {
75            ZipSegmentedStream zss = new ZipSegmentedStream()
76                {
77                    rwMode = RwMode.ReadOnly,
78                    CurrentSegment = initialDiskNumber,
79                    _maxDiskNumber = maxDiskNumber,
80                    _baseName = name,
81                };
82
83            // Console.WriteLine("ZSS: ForReading ({0})",
84            //                    Path.GetFileName(zss.CurrentName));
85
86            zss._SetReadStream();
87
88            return zss;
89        }
90
91
92        public static ZipSegmentedStream ForWriting(string name, int maxSegmentSize)
93        {
94            ZipSegmentedStream zss = new ZipSegmentedStream()
95                {
96                    rwMode = RwMode.Write,
97                    CurrentSegment = 0,
98                    _baseName = name,
99                    _maxSegmentSize = maxSegmentSize,
100                    _baseDir = Path.GetDirectoryName(name)
101                };
102
103            // workitem 9522
104            if (zss._baseDir=="") zss._baseDir=".";
105
106            zss._SetWriteStream(0);
107
108            // Console.WriteLine("ZSS: ForWriting ({0})",
109            //                    Path.GetFileName(zss.CurrentName));
110
111            return zss;
112        }
113
114
115        /// <summary>
116        ///   Sort-of like a factory method, ForUpdate is used only when
117        ///   the application needs to update the zip entry metadata for
118        ///   a segmented zip file, when the starting segment is earlier
119        ///   than the ending segment, for a particular entry.
120        /// </summary>
121        /// <remarks>
122        ///   <para>
123        ///     The update is always contiguous, never rolls over.  As a
124        ///     result, this method doesn't need to return a ZSS; it can
125        ///     simply return a FileStream.  That's why it's "sort of"
126        ///     like a Factory method.
127        ///   </para>
128        ///   <para>
129        ///     Caller must Close/Dispose the stream object returned by
130        ///     this method.
131        ///   </para>
132        /// </remarks>
133        public static Stream ForUpdate(string name, uint diskNumber)
134        {
135            if (diskNumber >= 99)
136                throw new ArgumentOutOfRangeException("diskNumber");
137
138            string fname =
139                String.Format("{0}.z{1:D2}",
140                                 Path.Combine(Path.GetDirectoryName(name),
141                                              Path.GetFileNameWithoutExtension(name)),
142                                 diskNumber + 1);
143
144            // Console.WriteLine("ZSS: ForUpdate ({0})",
145            //                   Path.GetFileName(fname));
146
147            // This class assumes that the update will not expand the
148            // size of the segment. Update is used only for an in-place
149            // update of zip metadata. It never will try to write beyond
150            // the end of a segment.
151
152            return File.Open(fname,
153                             FileMode.Open,
154                             FileAccess.ReadWrite,
155                             FileShare.None);
156        }
157
158        public bool ContiguousWrite
159        {
160            get;
161            set;
162        }
163
164
165        public UInt32 CurrentSegment
166        {
167            get
168            {
169                return _currentDiskNumber;
170            }
171            private set
172            {
173                _currentDiskNumber = value;
174                _currentName = null; // it will get updated next time referenced
175            }
176        }
177
178        /// <summary>
179        ///   Name of the filesystem file corresponding to the current segment.
180        /// </summary>
181        /// <remarks>
182        ///   <para>
183        ///     The name is not always the name currently being used in the
184        ///     filesystem.  When rwMode is RwMode.Write, the filesystem file has a
185        ///     temporary name until the stream is closed or until the next segment is
186        ///     started.
187        ///   </para>
188        /// </remarks>
189        public String CurrentName
190        {
191            get
192            {
193                if (_currentName==null)
194                    _currentName = _NameForSegment(CurrentSegment);
195
196                return _currentName;
197            }
198        }
199
200
201        public String CurrentTempName
202        {
203            get
204            {
205                return _currentTempName;
206            }
207        }
208
209        private string _NameForSegment(uint diskNumber)
210        {
211            if (diskNumber >= 99)
212            {
213                _exceptionPending = true;
214                throw new OverflowException("The number of zip segments would exceed 99.");
215            }
216
217            return String.Format("{0}.z{1:D2}",
218                                 Path.Combine(Path.GetDirectoryName(_baseName),
219                                              Path.GetFileNameWithoutExtension(_baseName)),
220                                 diskNumber + 1);
221        }
222
223
224        // Returns the segment that WILL be current if writing
225        // a block of the given length.
226        // This isn't exactly true. It could roll over beyond
227        // this number.
228        public UInt32 ComputeSegment(int length)
229        {
230            if (_innerStream.Position + length > _maxSegmentSize)
231                // the block will go AT LEAST into the next segment
232                return CurrentSegment + 1;
233
234            // it will fit in the current segment
235            return CurrentSegment;
236        }
237
238
239        public override String ToString()
240        {
241            return String.Format("{0}[{1}][{2}], pos=0x{3:X})",
242                                 "ZipSegmentedStream", CurrentName,
243                                 rwMode.ToString(),
244                                 this.Position);
245        }
246
247
248        private void _SetReadStream()
249        {
250            if (_innerStream != null)
251            {
252#if NETCF
253                _innerStream.Close();
254#else
255                _innerStream.Dispose();
256#endif
257            }
258
259            if (CurrentSegment + 1 == _maxDiskNumber)
260                _currentName = _baseName;
261
262            // Console.WriteLine("ZSS: SRS ({0})",
263            //                   Path.GetFileName(CurrentName));
264
265            _innerStream = File.OpenRead(CurrentName);
266        }
267
268
269        /// <summary>
270        /// Read from the stream
271        /// </summary>
272        /// <param name="buffer">the buffer to read</param>
273        /// <param name="offset">the offset at which to start</param>
274        /// <param name="count">the number of bytes to read</param>
275        /// <returns>the number of bytes actually read</returns>
276        public override int Read(byte[] buffer, int offset, int count)
277        {
278            if (rwMode != RwMode.ReadOnly)
279            {
280                _exceptionPending = true;
281                throw new InvalidOperationException("Stream Error: Cannot Read.");
282            }
283
284            int r = _innerStream.Read(buffer, offset, count);
285            int r1 = r;
286
287            while (r1 != count)
288            {
289                if (_innerStream.Position != _innerStream.Length)
290                {
291                    _exceptionPending = true;
292                    throw new ZipException(String.Format("Read error in file {0}", CurrentName));
293
294                }
295
296                if (CurrentSegment + 1 == _maxDiskNumber)
297                    return r; // no more to read
298
299                CurrentSegment++;
300                _SetReadStream();
301                offset += r1;
302                count -= r1;
303                r1 = _innerStream.Read(buffer, offset, count);
304                r += r1;
305            }
306            return r;
307        }
308
309
310
311        private void _SetWriteStream(uint increment)
312        {
313            if (_innerStream != null)
314            {
315#if NETCF
316                _innerStream.Close();
317#else
318                _innerStream.Dispose();
319#endif
320                if (File.Exists(CurrentName))
321                    File.Delete(CurrentName);
322                File.Move(_currentTempName, CurrentName);
323                // Console.WriteLine("ZSS: SWS close ({0})",
324                //                   Path.GetFileName(CurrentName));
325            }
326
327            if (increment > 0)
328                CurrentSegment += increment;
329
330            SharedUtilities.CreateAndOpenUniqueTempFile(_baseDir,
331                                                        out _innerStream,
332                                                        out _currentTempName);
333
334            // Console.WriteLine("ZSS: SWS open ({0})",
335            //                   Path.GetFileName(_currentTempName));
336
337            if (CurrentSegment == 0)
338                _innerStream.Write(BitConverter.GetBytes(ZipConstants.SplitArchiveSignature), 0, 4);
339        }
340
341
342        /// <summary>
343        /// Write to the stream.
344        /// </summary>
345        /// <param name="buffer">the buffer from which to write</param>
346        /// <param name="offset">the offset at which to start writing</param>
347        /// <param name="count">the number of bytes to write</param>
348        public override void Write(byte[] buffer, int offset, int count)
349        {
350            if (rwMode != RwMode.Write)
351            {
352                _exceptionPending = true;
353                throw new InvalidOperationException("Stream Error: Cannot Write.");
354            }
355
356
357            if (ContiguousWrite)
358            {
359                // enough space for a contiguous write?
360                if (_innerStream.Position + count > _maxSegmentSize)
361                    _SetWriteStream(1);
362            }
363            else
364            {
365                while (_innerStream.Position + count > _maxSegmentSize)
366                {
367                    int c = unchecked(_maxSegmentSize - (int)_innerStream.Position);
368                    _innerStream.Write(buffer, offset, c);
369                    _SetWriteStream(1);
370                    count -= c;
371                    offset += c;
372                }
373            }
374
375            _innerStream.Write(buffer, offset, count);
376        }
377
378
379        public long TruncateBackward(uint diskNumber, long offset)
380        {
381            // Console.WriteLine("***ZSS.Trunc to disk {0}", diskNumber);
382            // Console.WriteLine("***ZSS.Trunc:  current disk {0}", CurrentSegment);
383            if (diskNumber >= 99)
384                throw new ArgumentOutOfRangeException("diskNumber");
385
386            if (rwMode != RwMode.Write)
387            {
388                _exceptionPending = true;
389                throw new ZipException("bad state.");
390            }
391
392            // Seek back in the segmented stream to a (maybe) prior segment.
393
394            // Check if it is the same segment.  If it is, very simple.
395            if (diskNumber == CurrentSegment)
396            {
397                var x =_innerStream.Seek(offset, SeekOrigin.Begin);
398                // workitem 10178
399                Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_innerStream);
400                return x;
401            }
402
403            // Seeking back to a prior segment.
404            // The current segment and any intervening segments must be removed.
405            // First, close the current segment, and then remove it.
406            if (_innerStream != null)
407            {
408#if NETCF
409                _innerStream.Close();
410#else
411                _innerStream.Dispose();
412#endif
413                if (File.Exists(_currentTempName))
414                    File.Delete(_currentTempName);
415            }
416
417            // Now, remove intervening segments.
418            for (uint j= CurrentSegment-1; j > diskNumber; j--)
419            {
420                string s = _NameForSegment(j);
421                // Console.WriteLine("***ZSS.Trunc:  removing file {0}", s);
422                if (File.Exists(s))
423                    File.Delete(s);
424            }
425
426            // now, open the desired segment.  It must exist.
427            CurrentSegment = diskNumber;
428
429            // get a new temp file, try 3 times:
430            for (int i = 0; i < 3; i++)
431            {
432                try
433                {
434                    _currentTempName = SharedUtilities.InternalGetTempFileName();
435                    // move the .z0x file back to a temp name
436                    File.Move(CurrentName, _currentTempName);
437                    break; // workitem 12403
438                }
439                catch(IOException)
440                {
441                    if (i == 2) throw;
442                }
443            }
444
445            // open it
446            _innerStream = new FileStream(_currentTempName, FileMode.Open);
447
448            var r =  _innerStream.Seek(offset, SeekOrigin.Begin);
449
450            // workitem 10178
451            Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_innerStream);
452
453            return r;
454        }
455
456
457
458        public override bool CanRead
459        {
460            get
461            {
462                return (rwMode == RwMode.ReadOnly &&
463                        (_innerStream != null) &&
464                        _innerStream.CanRead);
465            }
466        }
467
468
469        public override bool CanSeek
470        {
471            get
472            {
473                return (_innerStream != null) &&
474                        _innerStream.CanSeek;
475            }
476        }
477
478
479        public override bool CanWrite
480        {
481            get
482            {
483                return (rwMode == RwMode.Write) &&
484                        (_innerStream != null) &&
485                        _innerStream.CanWrite;
486            }
487        }
488
489        public override void Flush()
490        {
491            _innerStream.Flush();
492        }
493
494        public override long Length
495        {
496            get
497            {
498                return _innerStream.Length;
499            }
500        }
501
502        public override long Position
503        {
504            get { return _innerStream.Position; }
505            set { _innerStream.Position = value; }
506        }
507
508        public override long Seek(long offset, System.IO.SeekOrigin origin)
509        {
510            var x = _innerStream.Seek(offset, origin);
511            // workitem 10178
512            Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_innerStream);
513            return x;
514        }
515
516        public override void SetLength(long value)
517        {
518            if (rwMode != RwMode.Write)
519            {
520                _exceptionPending = true;
521                throw new InvalidOperationException();
522            }
523            _innerStream.SetLength(value);
524        }
525
526
527        protected override void Dispose(bool disposing)
528        {
529            // this gets called by Stream.Close()
530
531            // if (_isDisposed) return;
532            // _isDisposed = true;
533            //Console.WriteLine("Dispose (mode={0})\n", rwMode.ToString());
534
535            try
536            {
537                if (_innerStream != null)
538                {
539#if NETCF
540                    _innerStream.Close();
541#else
542                    _innerStream.Dispose();
543#endif
544                    //_innerStream = null;
545                    if (rwMode == RwMode.Write)
546                    {
547                        if (_exceptionPending)
548                        {
549                            // possibly could try to clean up all the
550                            // temp files created so far...
551                        }
552                        else
553                        {
554                            // // move the final temp file to the .zNN name
555                            // if (File.Exists(CurrentName))
556                            //     File.Delete(CurrentName);
557                            // if (File.Exists(_currentTempName))
558                            //     File.Move(_currentTempName, CurrentName);
559                        }
560                    }
561                }
562            }
563            finally
564            {
565                base.Dispose(disposing);
566            }
567        }
568
569    }
570
571}
Note: See TracBrowser for help on using the repository browser.