Summary

Class:ICSharpCode.SharpZipLib.Zip.ZipHelperStream
Assembly:ICSharpCode.SharpZipLib
File(s):C:\Users\Neil\Documents\Visual Studio 2015\Projects\icsharpcode\SZL_master\ICSharpCode.SharpZipLib\Zip\ZipHelperStream.cs
Covered lines:106
Uncovered lines:88
Coverable lines:194
Total lines:560
Line coverage:54.6%
Branch coverage:42.8%

Metrics

MethodCyclomatic ComplexitySequence CoverageBranch Coverage
.ctor(...)100
.ctor(...)1100100
Flush()100
Seek(...)1100100
SetLength(...)1100100
Read(...)1100100
Write(...)1100100
Close()3100100
WriteLocalHeader(...)1600
LocateBlockWithSignature(...)488.8985.71
WriteZip64EndOfCentralDirectory(...)1100100
WriteEndOfCentralDirectory(...)1180.7773.68
ReadLEShort()371.4360
ReadLEInt()1100100
ReadLELong()1100100
WriteLEShort(...)1100100
WriteLEUshort(...)1100100
WriteLEInt(...)1100100
WriteLEUint(...)100
WriteLELong(...)1100100
WriteLEUlong(...)100
WriteDataDescriptor(...)468.7557.14
ReadDataDescriptor(...)390.9180

File(s)

C:\Users\Neil\Documents\Visual Studio 2015\Projects\icsharpcode\SZL_master\ICSharpCode.SharpZipLib\Zip\ZipHelperStream.cs

#LineLine coverage
 1using System;
 2using System.IO;
 3using System.Text;
 4
 5namespace ICSharpCode.SharpZipLib.Zip
 6{
 7  /// <summary>
 8  /// Holds data pertinent to a data descriptor.
 9  /// </summary>
 10  public class DescriptorData
 11  {
 12    /// <summary>
 13    /// Get /set the compressed size of data.
 14    /// </summary>
 15    public long CompressedSize {
 16      get { return compressedSize; }
 17      set { compressedSize = value; }
 18    }
 19
 20    /// <summary>
 21    /// Get / set the uncompressed size of data
 22    /// </summary>
 23    public long Size {
 24      get { return size; }
 25      set { size = value; }
 26    }
 27
 28    /// <summary>
 29    /// Get /set the crc value.
 30    /// </summary>
 31    public long Crc {
 32      get { return crc; }
 33      set { crc = (value & 0xffffffff); }
 34    }
 35
 36    #region Instance Fields
 37    long size;
 38    long compressedSize;
 39    long crc;
 40    #endregion
 41  }
 42
 43  class EntryPatchData
 44  {
 45    public long SizePatchOffset {
 46      get { return sizePatchOffset_; }
 47      set { sizePatchOffset_ = value; }
 48    }
 49
 50    public long CrcPatchOffset {
 51      get { return crcPatchOffset_; }
 52      set { crcPatchOffset_ = value; }
 53    }
 54
 55    #region Instance Fields
 56    long sizePatchOffset_;
 57    long crcPatchOffset_;
 58    #endregion
 59  }
 60
 61  /// <summary>
 62  /// This class assists with writing/reading from Zip files.
 63  /// </summary>
 64  internal class ZipHelperStream : Stream
 65  {
 66    #region Constructors
 67    /// <summary>
 68    /// Initialise an instance of this class.
 69    /// </summary>
 70    /// <param name="name">The name of the file to open.</param>
 071    public ZipHelperStream(string name)
 72    {
 073      stream_ = new FileStream(name, FileMode.Open, FileAccess.ReadWrite);
 074      isOwner_ = true;
 075    }
 76
 77    /// <summary>
 78    /// Initialise a new instance of <see cref="ZipHelperStream"/>.
 79    /// </summary>
 80    /// <param name="stream">The stream to use.</param>
 27281    public ZipHelperStream(Stream stream)
 82    {
 27283      stream_ = stream;
 27284    }
 85    #endregion
 86
 87    /// <summary>
 88    /// Get / set a value indicating wether the the underlying stream is owned or not.
 89    /// </summary>
 90    /// <remarks>If the stream is owned it is closed when this instance is closed.</remarks>
 91    public bool IsStreamOwner {
 092      get { return isOwner_; }
 893      set { isOwner_ = value; }
 94    }
 95
 96    #region Base Stream Methods
 97    public override bool CanRead {
 098      get { return stream_.CanRead; }
 99    }
 100
 101    public override bool CanSeek {
 0102      get { return stream_.CanSeek; }
 103    }
 104
 105    public override bool CanTimeout {
 0106      get { return stream_.CanTimeout; }
 107    }
 108
 109    public override long Length {
 1110      get { return stream_.Length; }
 111    }
 112
 113    public override long Position {
 124114      get { return stream_.Position; }
 6115      set { stream_.Position = value; }
 116    }
 117
 118    public override bool CanWrite {
 0119      get { return stream_.CanWrite; }
 120    }
 121
 122    public override void Flush()
 123    {
 0124      stream_.Flush();
 0125    }
 126
 127    public override long Seek(long offset, SeekOrigin origin)
 128    {
 131329129      return stream_.Seek(offset, origin);
 130    }
 131
 132    public override void SetLength(long value)
 133    {
 3134      stream_.SetLength(value);
 3135    }
 136
 137    public override int Read(byte[] buffer, int offset, int count)
 138    {
 1139      return stream_.Read(buffer, offset, count);
 140    }
 141
 142    public override void Write(byte[] buffer, int offset, int count)
 143    {
 9144      stream_.Write(buffer, offset, count);
 9145    }
 146
 147    /// <summary>
 148    /// Close the stream.
 149    /// </summary>
 150    /// <remarks>
 151    /// The underlying stream is closed only if <see cref="IsStreamOwner"/> is true.
 152    /// </remarks>
 153    override public void Close()
 154    {
 254155      Stream toClose = stream_;
 254156      stream_ = null;
 254157       if (isOwner_ && (toClose != null)) {
 1158        isOwner_ = false;
 1159        toClose.Close();
 160      }
 254161    }
 162    #endregion
 163
 164    // Write the local file header
 165    // TODO: ZipHelperStream.WriteLocalHeader is not yet used and needs checking for ZipFile and ZipOuptutStream usage
 166    void WriteLocalHeader(ZipEntry entry, EntryPatchData patchData)
 167    {
 0168      CompressionMethod method = entry.CompressionMethod;
 0169      bool headerInfoAvailable = true; // How to get this?
 0170      bool patchEntryHeader = false;
 171
 0172      WriteLEInt(ZipConstants.LocalHeaderSignature);
 173
 0174      WriteLEShort(entry.Version);
 0175      WriteLEShort(entry.Flags);
 0176      WriteLEShort((byte)method);
 0177      WriteLEInt((int)entry.DosTime);
 178
 0179       if (headerInfoAvailable == true) {
 0180        WriteLEInt((int)entry.Crc);
 0181         if (entry.LocalHeaderRequiresZip64) {
 0182          WriteLEInt(-1);
 0183          WriteLEInt(-1);
 0184        } else {
 0185          WriteLEInt(entry.IsCrypted ? (int)entry.CompressedSize + ZipConstants.CryptoHeaderSize : (int)entry.Compressed
 0186          WriteLEInt((int)entry.Size);
 187        }
 0188      } else {
 0189         if (patchData != null) {
 0190          patchData.CrcPatchOffset = stream_.Position;
 191        }
 0192        WriteLEInt(0);  // Crc
 193
 0194         if (patchData != null) {
 0195          patchData.SizePatchOffset = stream_.Position;
 196        }
 197
 198        // For local header both sizes appear in Zip64 Extended Information
 0199         if (entry.LocalHeaderRequiresZip64 && patchEntryHeader) {
 0200          WriteLEInt(-1);
 0201          WriteLEInt(-1);
 0202        } else {
 0203          WriteLEInt(0);  // Compressed size
 0204          WriteLEInt(0);  // Uncompressed size
 205        }
 206      }
 207
 0208      byte[] name = ZipConstants.ConvertToArray(entry.Flags, entry.Name);
 209
 0210       if (name.Length > 0xFFFF) {
 0211        throw new ZipException("Entry name too long.");
 212      }
 213
 0214      var ed = new ZipExtraData(entry.ExtraData);
 215
 0216       if (entry.LocalHeaderRequiresZip64 && (headerInfoAvailable || patchEntryHeader)) {
 0217        ed.StartNewEntry();
 0218         if (headerInfoAvailable) {
 0219          ed.AddLeLong(entry.Size);
 0220          ed.AddLeLong(entry.CompressedSize);
 0221        } else {
 0222          ed.AddLeLong(-1);
 0223          ed.AddLeLong(-1);
 224        }
 0225        ed.AddNewEntry(1);
 226
 0227         if (!ed.Find(1)) {
 0228          throw new ZipException("Internal error cant find extra data");
 229        }
 230
 0231         if (patchData != null) {
 0232          patchData.SizePatchOffset = ed.CurrentReadIndex;
 233        }
 0234      } else {
 0235        ed.Delete(1);
 236      }
 237
 0238      byte[] extra = ed.GetEntryData();
 239
 0240      WriteLEShort(name.Length);
 0241      WriteLEShort(extra.Length);
 242
 0243       if (name.Length > 0) {
 0244        stream_.Write(name, 0, name.Length);
 245      }
 246
 0247       if (entry.LocalHeaderRequiresZip64 && patchEntryHeader) {
 0248        patchData.SizePatchOffset += stream_.Position;
 249      }
 250
 0251       if (extra.Length > 0) {
 0252        stream_.Write(extra, 0, extra.Length);
 253      }
 0254    }
 255
 256    /// <summary>
 257    /// Locates a block with the desired <paramref name="signature"/>.
 258    /// </summary>
 259    /// <param name="signature">The signature to find.</param>
 260    /// <param name="endLocation">Location, marking the end of block.</param>
 261    /// <param name="minimumBlockSize">Minimum size of the block.</param>
 262    /// <param name="maximumVariableData">The maximum variable data.</param>
 263    /// <returns>Eeturns the offset of the first byte after the signature; -1 if not found</returns>
 264    public long LocateBlockWithSignature(int signature, long endLocation, int minimumBlockSize, int maximumVariableData)
 265    {
 118266      long pos = endLocation - minimumBlockSize;
 118267       if (pos < 0) {
 0268        return -1;
 269      }
 270
 118271      long giveUpMarker = Math.Max(pos - maximumVariableData, 0);
 272
 273      // TODO: This loop could be optimised for speed.
 274      do {
 131330275         if (pos < giveUpMarker) {
 1276          return -1;
 277        }
 131329278        Seek(pos--, SeekOrigin.Begin);
 131329279       } while (ReadLEInt() != signature);
 280
 117281      return Position;
 282    }
 283
 284    /// <summary>
 285    /// Write Zip64 end of central directory records (File header and locator).
 286    /// </summary>
 287    /// <param name="noOfEntries">The number of entries in the central directory.</param>
 288    /// <param name="sizeEntries">The size of entries in the central directory.</param>
 289    /// <param name="centralDirOffset">The offset of the dentral directory.</param>
 290    public void WriteZip64EndOfCentralDirectory(long noOfEntries, long sizeEntries, long centralDirOffset)
 291    {
 1292      long centralSignatureOffset = stream_.Position;
 1293      WriteLEInt(ZipConstants.Zip64CentralFileHeaderSignature);
 1294      WriteLELong(44);    // Size of this record (total size of remaining fields in header or full size - 12)
 1295      WriteLEShort(ZipConstants.VersionMadeBy);   // Version made by
 1296      WriteLEShort(ZipConstants.VersionZip64);   // Version to extract
 1297      WriteLEInt(0);      // Number of this disk
 1298      WriteLEInt(0);      // number of the disk with the start of the central directory
 1299      WriteLELong(noOfEntries);       // No of entries on this disk
 1300      WriteLELong(noOfEntries);       // Total No of entries in central directory
 1301      WriteLELong(sizeEntries);       // Size of the central directory
 1302      WriteLELong(centralDirOffset);  // offset of start of central directory
 303                      // zip64 extensible data sector not catered for here (variable size)
 304
 305      // Write the Zip64 end of central directory locator
 1306      WriteLEInt(ZipConstants.Zip64CentralDirLocatorSignature);
 307
 308      // no of the disk with the start of the zip64 end of central directory
 1309      WriteLEInt(0);
 310
 311      // relative offset of the zip64 end of central directory record
 1312      WriteLELong(centralSignatureOffset);
 313
 314      // total number of disks
 1315      WriteLEInt(1);
 1316    }
 317
 318    /// <summary>
 319    /// Write the required records to end the central directory.
 320    /// </summary>
 321    /// <param name="noOfEntries">The number of entries in the directory.</param>
 322    /// <param name="sizeEntries">The size of the entries in the directory.</param>
 323    /// <param name="startOfCentralDirectory">The start of the central directory.</param>
 324    /// <param name="comment">The archive comment.  (This can be null).</param>
 325    public void WriteEndOfCentralDirectory(long noOfEntries, long sizeEntries,
 326      long startOfCentralDirectory, byte[] comment)
 327    {
 328
 131329       if ((noOfEntries >= 0xffff) ||
 131330        (startOfCentralDirectory >= 0xffffffff) ||
 131331        (sizeEntries >= 0xffffffff)) {
 1332        WriteZip64EndOfCentralDirectory(noOfEntries, sizeEntries, startOfCentralDirectory);
 333      }
 334
 131335      WriteLEInt(ZipConstants.EndOfCentralDirectorySignature);
 336
 337      // TODO: ZipFile Multi disk handling not done
 130338      WriteLEShort(0);                    // number of this disk
 130339      WriteLEShort(0);                    // no of disk with start of central dir
 340
 341
 342      // Number of entries
 130343       if (noOfEntries >= 0xffff) {
 1344        WriteLEUshort(0xffff);  // Zip64 marker
 1345        WriteLEUshort(0xffff);
 1346      } else {
 129347        WriteLEShort((short)noOfEntries);          // entries in central dir for this disk
 129348        WriteLEShort((short)noOfEntries);          // total entries in central directory
 349      }
 350
 351      // Size of the central directory
 130352       if (sizeEntries >= 0xffffffff) {
 0353        WriteLEUint(0xffffffff);    // Zip64 marker
 0354      } else {
 130355        WriteLEInt((int)sizeEntries);
 356      }
 357
 358
 359      // offset of start of central directory
 130360       if (startOfCentralDirectory >= 0xffffffff) {
 0361        WriteLEUint(0xffffffff);    // Zip64 marker
 0362      } else {
 130363        WriteLEInt((int)startOfCentralDirectory);
 364      }
 365
 130366       int commentLength = (comment != null) ? comment.Length : 0;
 367
 130368       if (commentLength > 0xffff) {
 0369        throw new ZipException(string.Format("Comment length({0}) is too long can only be 64K", commentLength));
 370      }
 371
 130372      WriteLEShort(commentLength);
 373
 130374       if (commentLength > 0) {
 5375        Write(comment, 0, comment.Length);
 376      }
 130377    }
 378
 379    #region LE value reading/writing
 380    /// <summary>
 381    /// Read an unsigned short in little endian byte order.
 382    /// </summary>
 383    /// <returns>Returns the value read.</returns>
 384    /// <exception cref="IOException">
 385    /// An i/o error occurs.
 386    /// </exception>
 387    /// <exception cref="EndOfStreamException">
 388    /// The file ends prematurely
 389    /// </exception>
 390    public int ReadLEShort()
 391    {
 262792392      int byteValue1 = stream_.ReadByte();
 393
 262792394       if (byteValue1 < 0) {
 0395        throw new EndOfStreamException();
 396      }
 397
 262792398      int byteValue2 = stream_.ReadByte();
 262792399       if (byteValue2 < 0) {
 0400        throw new EndOfStreamException();
 401      }
 402
 262792403      return byteValue1 | (byteValue2 << 8);
 404    }
 405
 406    /// <summary>
 407    /// Read an int in little endian byte order.
 408    /// </summary>
 409    /// <returns>Returns the value read.</returns>
 410    /// <exception cref="IOException">
 411    /// An i/o error occurs.
 412    /// </exception>
 413    /// <exception cref="System.IO.EndOfStreamException">
 414    /// The file ends prematurely
 415    /// </exception>
 416    public int ReadLEInt()
 417    {
 131395418      return ReadLEShort() | (ReadLEShort() << 16);
 419    }
 420
 421    /// <summary>
 422    /// Read a long in little endian byte order.
 423    /// </summary>
 424    /// <returns>The value read.</returns>
 425    public long ReadLELong()
 426    {
 9427      return (uint)ReadLEInt() | ((long)ReadLEInt() << 32);
 428    }
 429
 430    /// <summary>
 431    /// Write an unsigned short in little endian byte order.
 432    /// </summary>
 433    /// <param name="value">The value to write.</param>
 434    public void WriteLEShort(int value)
 435    {
 1545436      stream_.WriteByte((byte)(value & 0xff));
 1544437      stream_.WriteByte((byte)((value >> 8) & 0xff));
 1544438    }
 439
 440    /// <summary>
 441    /// Write a ushort in little endian byte order.
 442    /// </summary>
 443    /// <param name="value">The value to write.</param>
 444    public void WriteLEUshort(ushort value)
 445    {
 2446      stream_.WriteByte((byte)(value & 0xff));
 2447      stream_.WriteByte((byte)(value >> 8));
 2448    }
 449
 450    /// <summary>
 451    /// Write an int in little endian byte order.
 452    /// </summary>
 453    /// <param name="value">The value to write.</param>
 454    public void WriteLEInt(int value)
 455    {
 444456      WriteLEShort(value);
 444457      WriteLEShort(value >> 16);
 443458    }
 459
 460    /// <summary>
 461    /// Write a uint in little endian byte order.
 462    /// </summary>
 463    /// <param name="value">The value to write.</param>
 464    public void WriteLEUint(uint value)
 465    {
 0466      WriteLEUshort((ushort)(value & 0xffff));
 0467      WriteLEUshort((ushort)(value >> 16));
 0468    }
 469
 470    /// <summary>
 471    /// Write a long in little endian byte order.
 472    /// </summary>
 473    /// <param name="value">The value to write.</param>
 474    public void WriteLELong(long value)
 475    {
 12476      WriteLEInt((int)value);
 12477      WriteLEInt((int)(value >> 32));
 12478    }
 479
 480    /// <summary>
 481    /// Write a ulong in little endian byte order.
 482    /// </summary>
 483    /// <param name="value">The value to write.</param>
 484    public void WriteLEUlong(ulong value)
 485    {
 0486      WriteLEUint((uint)(value & 0xffffffff));
 0487      WriteLEUint((uint)(value >> 32));
 0488    }
 489
 490    #endregion
 491
 492    /// <summary>
 493    /// Write a data descriptor.
 494    /// </summary>
 495    /// <param name="entry">The entry to write a descriptor for.</param>
 496    /// <returns>Returns the number of descriptor bytes written.</returns>
 497    public int WriteDataDescriptor(ZipEntry entry)
 498    {
 5499       if (entry == null) {
 0500        throw new ArgumentNullException(nameof(entry));
 501      }
 502
 5503      int result = 0;
 504
 505      // Add data descriptor if flagged as required
 5506       if ((entry.Flags & (int)GeneralBitFlags.Descriptor) != 0) {
 507        // The signature is not PKZIP originally but is now described as optional
 508        // in the PKZIP Appnote documenting trhe format.
 5509        WriteLEInt(ZipConstants.DataDescriptorSignature);
 5510        WriteLEInt(unchecked((int)(entry.Crc)));
 511
 5512        result += 8;
 513
 5514         if (entry.LocalHeaderRequiresZip64) {
 0515          WriteLELong(entry.CompressedSize);
 0516          WriteLELong(entry.Size);
 0517          result += 16;
 0518        } else {
 5519          WriteLEInt((int)entry.CompressedSize);
 5520          WriteLEInt((int)entry.Size);
 5521          result += 8;
 522        }
 523      }
 524
 5525      return result;
 526    }
 527
 528    /// <summary>
 529    /// Read data descriptor at the end of compressed data.
 530    /// </summary>
 531    /// <param name="zip64">if set to <c>true</c> [zip64].</param>
 532    /// <param name="data">The data to fill in.</param>
 533    /// <returns>Returns the number of bytes read in the descriptor.</returns>
 534    public void ReadDataDescriptor(bool zip64, DescriptorData data)
 535    {
 13536      int intValue = ReadLEInt();
 537
 538      // In theory this may not be a descriptor according to PKZIP appnote.
 539      // In practise its always there.
 13540       if (intValue != ZipConstants.DataDescriptorSignature) {
 0541        throw new ZipException("Data descriptor signature not found");
 542      }
 543
 13544      data.Crc = ReadLEInt();
 545
 13546       if (zip64) {
 3547        data.CompressedSize = ReadLELong();
 3548        data.Size = ReadLELong();
 3549      } else {
 10550        data.CompressedSize = ReadLEInt();
 10551        data.Size = ReadLEInt();
 552      }
 10553    }
 554
 555    #region Instance Fields
 556    bool isOwner_;
 557    Stream stream_;
 558    #endregion
 559  }
 560}