Summary

Class:ICSharpCode.SharpZipLib.Zip.ZipExtraData
Assembly:ICSharpCode.SharpZipLib
File(s):C:\Users\Neil\Documents\Visual Studio 2015\Projects\icsharpcode\SZL_master\ICSharpCode.SharpZipLib\Zip\ZipExtraData.cs
Covered lines:121
Uncovered lines:19
Coverable lines:140
Total lines:896
Line coverage:86.4%
Branch coverage:67.2%

Metrics

MethodCyclomatic ComplexitySequence CoverageBranch Coverage
.ctor()1100100
.ctor(...)2100100
GetEntryData()266.6766.67
Clear()310060
GetStreamForTag(...)210066.67
GetData()26066.67
Find(...)6100100
AddEntry(...)200
AddEntry(...)89076.92
StartNewEntry()1100100
AddNewEntry(...)1100100
AddData(...)1100100
AddData(...)200
AddLeShort(...)1100100
AddLeInt(...)1100100
AddLeLong(...)1100100
Delete(...)2100100
ReadLong()1100100
ReadInt()1100100
ReadShort()1100100
ReadByte()3100100
Skip(...)1100100
ReadCheck(...)585.7177.78
ReadShortInternal()28066.67
SetShort(...)1100100
Dispose()200

File(s)

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

#LineLine coverage
 1using System;
 2using System.IO;
 3
 4namespace ICSharpCode.SharpZipLib.Zip
 5{
 6  // TODO: Sort out wether tagged data is useful and what a good implementation might look like.
 7  // Its just a sketch of an idea at the moment.
 8
 9  /// <summary>
 10  /// ExtraData tagged value interface.
 11  /// </summary>
 12  public interface ITaggedData
 13  {
 14    /// <summary>
 15    /// Get the ID for this tagged data value.
 16    /// </summary>
 17    short TagID { get; }
 18
 19    /// <summary>
 20    /// Set the contents of this instance from the data passed.
 21    /// </summary>
 22    /// <param name="data">The data to extract contents from.</param>
 23    /// <param name="offset">The offset to begin extracting data from.</param>
 24    /// <param name="count">The number of bytes to extract.</param>
 25    void SetData(byte[] data, int offset, int count);
 26
 27    /// <summary>
 28    /// Get the data representing this instance.
 29    /// </summary>
 30    /// <returns>Returns the data for this instance.</returns>
 31    byte[] GetData();
 32  }
 33
 34  /// <summary>
 35  /// A raw binary tagged value
 36  /// </summary>
 37  public class RawTaggedData : ITaggedData
 38  {
 39    /// <summary>
 40    /// Initialise a new instance.
 41    /// </summary>
 42    /// <param name="tag">The tag ID.</param>
 43    public RawTaggedData(short tag)
 44    {
 45      _tag = tag;
 46    }
 47
 48    #region ITaggedData Members
 49
 50    /// <summary>
 51    /// Get the ID for this tagged data value.
 52    /// </summary>
 53    public short TagID {
 54      get { return _tag; }
 55      set { _tag = value; }
 56    }
 57
 58    /// <summary>
 59    /// Set the data from the raw values provided.
 60    /// </summary>
 61    /// <param name="data">The raw data to extract values from.</param>
 62    /// <param name="offset">The index to start extracting values from.</param>
 63    /// <param name="count">The number of bytes available.</param>
 64    public void SetData(byte[] data, int offset, int count)
 65    {
 66      if (data == null) {
 67        throw new ArgumentNullException(nameof(data));
 68      }
 69
 70      _data = new byte[count];
 71      Array.Copy(data, offset, _data, 0, count);
 72    }
 73
 74    /// <summary>
 75    /// Get the binary data representing this instance.
 76    /// </summary>
 77    /// <returns>The raw binary data representing this instance.</returns>
 78    public byte[] GetData()
 79    {
 80      return _data;
 81    }
 82
 83    #endregion
 84
 85    /// <summary>
 86    /// Get /set the binary data representing this instance.
 87    /// </summary>
 88    /// <returns>The raw binary data representing this instance.</returns>
 89    public byte[] Data {
 90      get { return _data; }
 91      set { _data = value; }
 92    }
 93
 94    #region Instance Fields
 95    /// <summary>
 96    /// The tag ID for this instance.
 97    /// </summary>
 98    short _tag;
 99
 100    byte[] _data;
 101    #endregion
 102  }
 103
 104  /// <summary>
 105  /// Class representing extended unix date time values.
 106  /// </summary>
 107  public class ExtendedUnixData : ITaggedData
 108  {
 109    /// <summary>
 110    /// Flags indicate which values are included in this instance.
 111    /// </summary>
 112    [Flags]
 113    public enum Flags : byte
 114    {
 115      /// <summary>
 116      /// The modification time is included
 117      /// </summary>
 118      ModificationTime = 0x01,
 119
 120      /// <summary>
 121      /// The access time is included
 122      /// </summary>
 123      AccessTime = 0x02,
 124
 125      /// <summary>
 126      /// The create time is included.
 127      /// </summary>
 128      CreateTime = 0x04,
 129    }
 130
 131    #region ITaggedData Members
 132
 133    /// <summary>
 134    /// Get the ID
 135    /// </summary>
 136    public short TagID {
 137      get { return 0x5455; }
 138    }
 139
 140    /// <summary>
 141    /// Set the data from the raw values provided.
 142    /// </summary>
 143    /// <param name="data">The raw data to extract values from.</param>
 144    /// <param name="index">The index to start extracting values from.</param>
 145    /// <param name="count">The number of bytes available.</param>
 146    public void SetData(byte[] data, int index, int count)
 147    {
 148      using (MemoryStream ms = new MemoryStream(data, index, count, false))
 149      using (ZipHelperStream helperStream = new ZipHelperStream(ms)) {
 150        // bit 0           if set, modification time is present
 151        // bit 1           if set, access time is present
 152        // bit 2           if set, creation time is present
 153
 154        _flags = (Flags)helperStream.ReadByte();
 155        if (((_flags & Flags.ModificationTime) != 0))
 156        {
 157          int iTime = helperStream.ReadLEInt();
 158
 159          _modificationTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) +
 160            new TimeSpan(0, 0, 0, iTime, 0);
 161
 162          // Central-header version is truncated after modification time
 163          if (count <= 5) return;
 164        }
 165
 166        if ((_flags & Flags.AccessTime) != 0) {
 167          int iTime = helperStream.ReadLEInt();
 168
 169          _lastAccessTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) +
 170            new TimeSpan(0, 0, 0, iTime, 0);
 171        }
 172
 173        if ((_flags & Flags.CreateTime) != 0) {
 174          int iTime = helperStream.ReadLEInt();
 175
 176          _createTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) +
 177            new TimeSpan(0, 0, 0, iTime, 0);
 178        }
 179      }
 180    }
 181
 182    /// <summary>
 183    /// Get the binary data representing this instance.
 184    /// </summary>
 185    /// <returns>The raw binary data representing this instance.</returns>
 186    public byte[] GetData()
 187    {
 188      using (MemoryStream ms = new MemoryStream())
 189      using (ZipHelperStream helperStream = new ZipHelperStream(ms)) {
 190        helperStream.IsStreamOwner = false;
 191        helperStream.WriteByte((byte)_flags);     // Flags
 192        if ((_flags & Flags.ModificationTime) != 0) {
 193          TimeSpan span = _modificationTime - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
 194          var seconds = (int)span.TotalSeconds;
 195          helperStream.WriteLEInt(seconds);
 196        }
 197        if ((_flags & Flags.AccessTime) != 0) {
 198          TimeSpan span = _lastAccessTime - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
 199          var seconds = (int)span.TotalSeconds;
 200          helperStream.WriteLEInt(seconds);
 201        }
 202        if ((_flags & Flags.CreateTime) != 0) {
 203          TimeSpan span = _createTime - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
 204          var seconds = (int)span.TotalSeconds;
 205          helperStream.WriteLEInt(seconds);
 206        }
 207        return ms.ToArray();
 208      }
 209    }
 210
 211    #endregion
 212
 213    /// <summary>
 214    /// Test a <see cref="DateTime"> value to see if is valid and can be represented here.</see>
 215    /// </summary>
 216    /// <param name="value">The <see cref="DateTime">value</see> to test.</param>
 217    /// <returns>Returns true if the value is valid and can be represented; false if not.</returns>
 218    /// <remarks>The standard Unix time is a signed integer data type, directly encoding the Unix time number,
 219    /// which is the number of seconds since 1970-01-01.
 220    /// Being 32 bits means the values here cover a range of about 136 years.
 221    /// The minimum representable time is 1901-12-13 20:45:52,
 222    /// and the maximum representable time is 2038-01-19 03:14:07.
 223    /// </remarks>
 224    public static bool IsValidValue(DateTime value)
 225    {
 226      return ((value >= new DateTime(1901, 12, 13, 20, 45, 52)) ||
 227          (value <= new DateTime(2038, 1, 19, 03, 14, 07)));
 228    }
 229
 230    /// <summary>
 231    /// Get /set the Modification Time
 232    /// </summary>
 233    /// <exception cref="ArgumentOutOfRangeException"></exception>
 234    /// <seealso cref="IsValidValue"></seealso>
 235    public DateTime ModificationTime {
 236      get { return _modificationTime; }
 237      set {
 238        if (!IsValidValue(value)) {
 239          throw new ArgumentOutOfRangeException(nameof(value));
 240        }
 241
 242        _flags |= Flags.ModificationTime;
 243        _modificationTime = value;
 244      }
 245    }
 246
 247    /// <summary>
 248    /// Get / set the Access Time
 249    /// </summary>
 250    /// <exception cref="ArgumentOutOfRangeException"></exception>
 251    /// <seealso cref="IsValidValue"></seealso>
 252    public DateTime AccessTime {
 253      get { return _lastAccessTime; }
 254      set {
 255        if (!IsValidValue(value)) {
 256          throw new ArgumentOutOfRangeException(nameof(value));
 257        }
 258
 259        _flags |= Flags.AccessTime;
 260        _lastAccessTime = value;
 261      }
 262    }
 263
 264    /// <summary>
 265    /// Get / Set the Create Time
 266    /// </summary>
 267    /// <exception cref="ArgumentOutOfRangeException"></exception>
 268    /// <seealso cref="IsValidValue"></seealso>
 269    public DateTime CreateTime {
 270      get { return _createTime; }
 271      set {
 272        if (!IsValidValue(value)) {
 273          throw new ArgumentOutOfRangeException(nameof(value));
 274        }
 275
 276        _flags |= Flags.CreateTime;
 277        _createTime = value;
 278      }
 279    }
 280
 281    /// <summary>
 282    /// Get/set the <see cref="Flags">values</see> to include.
 283    /// </summary>
 284    public Flags Include
 285    {
 286      get { return _flags; }
 287      set { _flags = value; }
 288    }
 289
 290    #region Instance Fields
 291    Flags _flags;
 292    DateTime _modificationTime = new DateTime(1970, 1, 1);
 293    DateTime _lastAccessTime = new DateTime(1970, 1, 1);
 294    DateTime _createTime = new DateTime(1970, 1, 1);
 295    #endregion
 296  }
 297
 298  /// <summary>
 299  /// Class handling NT date time values.
 300  /// </summary>
 301  public class NTTaggedData : ITaggedData
 302  {
 303    /// <summary>
 304    /// Get the ID for this tagged data value.
 305    /// </summary>
 306    public short TagID {
 307      get { return 10; }
 308    }
 309
 310    /// <summary>
 311    /// Set the data from the raw values provided.
 312    /// </summary>
 313    /// <param name="data">The raw data to extract values from.</param>
 314    /// <param name="index">The index to start extracting values from.</param>
 315    /// <param name="count">The number of bytes available.</param>
 316    public void SetData(byte[] data, int index, int count)
 317    {
 318      using (MemoryStream ms = new MemoryStream(data, index, count, false))
 319      using (ZipHelperStream helperStream = new ZipHelperStream(ms)) {
 320        helperStream.ReadLEInt(); // Reserved
 321        while (helperStream.Position < helperStream.Length) {
 322          int ntfsTag = helperStream.ReadLEShort();
 323          int ntfsLength = helperStream.ReadLEShort();
 324          if (ntfsTag == 1) {
 325            if (ntfsLength >= 24) {
 326              long lastModificationTicks = helperStream.ReadLELong();
 327              _lastModificationTime = DateTime.FromFileTimeUtc(lastModificationTicks);
 328
 329              long lastAccessTicks = helperStream.ReadLELong();
 330              _lastAccessTime = DateTime.FromFileTimeUtc(lastAccessTicks);
 331
 332              long createTimeTicks = helperStream.ReadLELong();
 333              _createTime = DateTime.FromFileTimeUtc(createTimeTicks);
 334            }
 335            break;
 336          } else {
 337            // An unknown NTFS tag so simply skip it.
 338            helperStream.Seek(ntfsLength, SeekOrigin.Current);
 339          }
 340        }
 341      }
 342    }
 343
 344    /// <summary>
 345    /// Get the binary data representing this instance.
 346    /// </summary>
 347    /// <returns>The raw binary data representing this instance.</returns>
 348    public byte[] GetData()
 349    {
 350      using (MemoryStream ms = new MemoryStream())
 351      using (ZipHelperStream helperStream = new ZipHelperStream(ms)) {
 352        helperStream.IsStreamOwner = false;
 353        helperStream.WriteLEInt(0);       // Reserved
 354        helperStream.WriteLEShort(1);     // Tag
 355        helperStream.WriteLEShort(24);    // Length = 3 x 8.
 356        helperStream.WriteLELong(_lastModificationTime.ToFileTimeUtc());
 357        helperStream.WriteLELong(_lastAccessTime.ToFileTimeUtc());
 358        helperStream.WriteLELong(_createTime.ToFileTimeUtc());
 359        return ms.ToArray();
 360      }
 361    }
 362
 363    /// <summary>
 364    /// Test a <see cref="DateTime"> valuie to see if is valid and can be represented here.</see>
 365    /// </summary>
 366    /// <param name="value">The <see cref="DateTime">value</see> to test.</param>
 367    /// <returns>Returns true if the value is valid and can be represented; false if not.</returns>
 368    /// <remarks>
 369    /// NTFS filetimes are 64-bit unsigned integers, stored in Intel
 370    /// (least significant byte first) byte order. They determine the
 371    /// number of 1.0E-07 seconds (1/10th microseconds!) past WinNT "epoch",
 372    /// which is "01-Jan-1601 00:00:00 UTC". 28 May 60056 is the upper limit
 373    /// </remarks>
 374    public static bool IsValidValue(DateTime value)
 375    {
 376      bool result = true;
 377      try {
 378        value.ToFileTimeUtc();
 379      } catch {
 380        result = false;
 381      }
 382      return result;
 383    }
 384
 385    /// <summary>
 386    /// Get/set the <see cref="DateTime">last modification time</see>.
 387    /// </summary>
 388    public DateTime LastModificationTime {
 389      get { return _lastModificationTime; }
 390      set {
 391        if (!IsValidValue(value)) {
 392          throw new ArgumentOutOfRangeException(nameof(value));
 393        }
 394        _lastModificationTime = value;
 395      }
 396    }
 397
 398    /// <summary>
 399    /// Get /set the <see cref="DateTime">create time</see>
 400    /// </summary>
 401    public DateTime CreateTime {
 402      get { return _createTime; }
 403      set {
 404        if (!IsValidValue(value)) {
 405          throw new ArgumentOutOfRangeException(nameof(value));
 406        }
 407        _createTime = value;
 408      }
 409    }
 410
 411    /// <summary>
 412    /// Get /set the <see cref="DateTime">last access time</see>.
 413    /// </summary>
 414    public DateTime LastAccessTime {
 415      get { return _lastAccessTime; }
 416      set {
 417        if (!IsValidValue(value)) {
 418          throw new ArgumentOutOfRangeException(nameof(value));
 419        }
 420        _lastAccessTime = value;
 421      }
 422    }
 423
 424    #region Instance Fields
 425    DateTime _lastAccessTime = DateTime.FromFileTimeUtc(0);
 426    DateTime _lastModificationTime = DateTime.FromFileTimeUtc(0);
 427    DateTime _createTime = DateTime.FromFileTimeUtc(0);
 428    #endregion
 429  }
 430
 431  /// <summary>
 432  /// A factory that creates <see cref="ITaggedData">tagged data</see> instances.
 433  /// </summary>
 434  interface ITaggedDataFactory
 435  {
 436    /// <summary>
 437    /// Get data for a specific tag value.
 438    /// </summary>
 439    /// <param name="tag">The tag ID to find.</param>
 440    /// <param name="data">The data to search.</param>
 441    /// <param name="offset">The offset to begin extracting data from.</param>
 442    /// <param name="count">The number of bytes to extract.</param>
 443    /// <returns>The located <see cref="ITaggedData">value found</see>, or null if not found.</returns>
 444    ITaggedData Create(short tag, byte[] data, int offset, int count);
 445  }
 446
 447  ///
 448  /// <summary>
 449  /// A class to handle the extra data field for Zip entries
 450  /// </summary>
 451  /// <remarks>
 452  /// Extra data contains 0 or more values each prefixed by a header tag and length.
 453  /// They contain zero or more bytes of actual data.
 454  /// The data is held internally using a copy on write strategy.  This is more efficient but
 455  /// means that for extra data created by passing in data can have the values modified by the caller
 456  /// in some circumstances.
 457  /// </remarks>
 458  sealed public class ZipExtraData : IDisposable
 459  {
 460    #region Constructors
 461    /// <summary>
 462    /// Initialise a default instance.
 463    /// </summary>
 5464    public ZipExtraData()
 465    {
 5466      Clear();
 5467    }
 468
 469    /// <summary>
 470    /// Initialise with known extra data.
 471    /// </summary>
 472    /// <param name="data">The extra data.</param>
 329692473    public ZipExtraData(byte[] data)
 474    {
 329692475       if (data == null) {
 131944476        _data = new byte[0];
 131944477      } else {
 197748478        _data = data;
 479      }
 197748480    }
 481    #endregion
 482
 483    /// <summary>
 484    /// Get the raw extra data value
 485    /// </summary>
 486    /// <returns>Returns the raw byte[] extra data this instance represents.</returns>
 487    public byte[] GetEntryData()
 488    {
 131788489       if (Length > ushort.MaxValue) {
 0490        throw new ZipException("Data exceeds maximum length");
 491      }
 492
 131788493      return (byte[])_data.Clone();
 494    }
 495
 496    /// <summary>
 497    /// Clear the stored data.
 498    /// </summary>
 499    public void Clear()
 500    {
 5501       if ((_data == null) || (_data.Length != 0)) {
 5502        _data = new byte[0];
 503      }
 5504    }
 505
 506    /// <summary>
 507    /// Gets the current extra data length.
 508    /// </summary>
 509    public int Length {
 131811510      get { return _data.Length; }
 511    }
 512
 513    /// <summary>
 514    /// Get a read-only <see cref="Stream"/> for the associated tag.
 515    /// </summary>
 516    /// <param name="tag">The tag to locate data for.</param>
 517    /// <returns>Returns a <see cref="Stream"/> containing tag data or null if no tag was found.</returns>
 518    public Stream GetStreamForTag(int tag)
 519    {
 4520      Stream result = null;
 4521       if (Find(tag)) {
 4522        result = new MemoryStream(_data, _index, _readValueLength, false);
 523      }
 4524      return result;
 525    }
 526
 527    /// <summary>
 528    /// Get the <see cref="ITaggedData">tagged data</see> for a tag.
 529    /// </summary>
 530    /// <typeparam name="T">The tag to search for.</typeparam>
 531    /// <returns>Returns a <see cref="ITaggedData">tagged value</see> or null if none found.</returns>
 532    public T GetData<T>()
 533      where T : class, ITaggedData, new()
 534    {
 66037535      T result = new T();
 66037536       if (Find(result.TagID))
 537      {
 0538        result.SetData(_data, _readValueStart, _readValueLength);
 0539        return result;
 540      }
 66037541      else return null;
 542    }
 543
 544    /// <summary>
 545    /// Get the length of the last value found by <see cref="Find"/>
 546    /// </summary>
 547    /// <remarks>This is only valid if <see cref="Find"/> has previously returned true.</remarks>
 548    public int ValueLength {
 146549      get { return _readValueLength; }
 550    }
 551
 552    /// <summary>
 553    /// Get the index for the current read value.
 554    /// </summary>
 555    /// <remarks>This is only valid if <see cref="Find"/> has previously returned true.
 556    /// Initially the result will be the index of the first byte of actual data.  The value is updated after calls to
 557    /// <see cref="ReadInt"/>, <see cref="ReadShort"/> and <see cref="ReadLong"/>. </remarks>
 558    public int CurrentReadIndex {
 101559      get { return _index; }
 560    }
 561
 562    /// <summary>
 563    /// Get the number of bytes remaining to be read for the current value;
 564    /// </summary>
 565    public int UnreadCount {
 566      get {
 16567         if ((_readValueStart > _data.Length) ||
 16568          (_readValueStart < 4)) {
 0569          throw new ZipException("Find must be called before calling a Read method");
 570        }
 571
 16572        return _readValueStart + _readValueLength - _index;
 573      }
 574    }
 575
 576    /// <summary>
 577    /// Find an extra data value
 578    /// </summary>
 579    /// <param name="headerID">The identifier for the value to find.</param>
 580    /// <returns>Returns true if the value was found; false otherwise.</returns>
 581    public bool Find(int headerID)
 582    {
 396136583      _readValueStart = _data.Length;
 396136584      _readValueLength = 0;
 396136585      _index = 0;
 586
 396136587      int localLength = _readValueStart;
 396136588      int localTag = headerID - 1;
 589
 590      // Trailing bytes that cant make up an entry (as there arent enough
 591      // bytes for a tag and length) are ignored!
 396614592       while ((localTag != headerID) && (_index < _data.Length - 3)) {
 478593        localTag = ReadShortInternal();
 478594        localLength = ReadShortInternal();
 478595         if (localTag != headerID) {
 148596          _index += localLength;
 597        }
 598      }
 599
 396136600      bool result = (localTag == headerID) && ((_index + localLength) <= _data.Length);
 601
 396136602       if (result) {
 330603        _readValueStart = _index;
 330604        _readValueLength = localLength;
 605      }
 606
 396136607      return result;
 608    }
 609
 610    /// <summary>
 611    /// Add a new entry to extra data.
 612    /// </summary>
 613    /// <param name="taggedData">The <see cref="ITaggedData"/> value to add.</param>
 614    public void AddEntry(ITaggedData taggedData)
 615    {
 0616       if (taggedData == null) {
 0617        throw new ArgumentNullException(nameof(taggedData));
 618      }
 0619      AddEntry(taggedData.TagID, taggedData.GetData());
 0620    }
 621
 622    /// <summary>
 623    /// Add a new entry to extra data
 624    /// </summary>
 625    /// <param name="headerID">The ID for this entry.</param>
 626    /// <param name="fieldData">The data to add.</param>
 627    /// <remarks>If the ID already exists its contents are replaced.</remarks>
 628    public void AddEntry(int headerID, byte[] fieldData)
 629    {
 261630       if ((headerID > ushort.MaxValue) || (headerID < 0)) {
 0631        throw new ArgumentOutOfRangeException(nameof(headerID));
 632      }
 633
 261634      int addLength = (fieldData == null) ? 0 : fieldData.Length;
 635
 261636       if (addLength > ushort.MaxValue) {
 0637        throw new ArgumentOutOfRangeException(nameof(fieldData), "exceeds maximum length");
 638      }
 639
 640      // Test for new length before adjusting data.
 261641      int newLength = _data.Length + addLength + 4;
 642
 261643       if (Find(headerID)) {
 4644        newLength -= (ValueLength + 4);
 645      }
 646
 261647       if (newLength > ushort.MaxValue) {
 2648        throw new ZipException("Data exceeds maximum length");
 649      }
 650
 259651      Delete(headerID);
 652
 259653      byte[] newData = new byte[newLength];
 259654      _data.CopyTo(newData, 0);
 259655      int index = _data.Length;
 259656      _data = newData;
 259657      SetShort(ref index, headerID);
 259658      SetShort(ref index, addLength);
 259659       if (fieldData != null) {
 257660        fieldData.CopyTo(newData, index);
 661      }
 259662    }
 663
 664    /// <summary>
 665    /// Start adding a new entry.
 666    /// </summary>
 667    /// <remarks>Add data using <see cref="AddData(byte[])"/>, <see cref="AddLeShort"/>, <see cref="AddLeInt"/>, or <see
 668    /// The new entry is completed and actually added by calling <see cref="AddNewEntry"/></remarks>
 669    /// <seealso cref="AddEntry(ITaggedData)"/>
 670    public void StartNewEntry()
 671    {
 249672      _newEntry = new MemoryStream();
 249673    }
 674
 675    /// <summary>
 676    /// Add entry data added since <see cref="StartNewEntry"/> using the ID passed.
 677    /// </summary>
 678    /// <param name="headerID">The identifier to use for this entry.</param>
 679    public void AddNewEntry(int headerID)
 680    {
 249681      byte[] newData = _newEntry.ToArray();
 249682      _newEntry = null;
 249683      AddEntry(headerID, newData);
 249684    }
 685
 686    /// <summary>
 687    /// Add a byte of data to the pending new entry.
 688    /// </summary>
 689    /// <param name="data">The byte to add.</param>
 690    /// <seealso cref="StartNewEntry"/>
 691    public void AddData(byte data)
 692    {
 1693      _newEntry.WriteByte(data);
 1694    }
 695
 696    /// <summary>
 697    /// Add data to a pending new entry.
 698    /// </summary>
 699    /// <param name="data">The data to add.</param>
 700    /// <seealso cref="StartNewEntry"/>
 701    public void AddData(byte[] data)
 702    {
 0703       if (data == null) {
 0704        throw new ArgumentNullException(nameof(data));
 705      }
 706
 0707      _newEntry.Write(data, 0, data.Length);
 0708    }
 709
 710    /// <summary>
 711    /// Add a short value in little endian order to the pending new entry.
 712    /// </summary>
 713    /// <param name="toAdd">The data to add.</param>
 714    /// <seealso cref="StartNewEntry"/>
 715    public void AddLeShort(int toAdd)
 716    {
 717      unchecked {
 2004718        _newEntry.WriteByte((byte)toAdd);
 2004719        _newEntry.WriteByte((byte)(toAdd >> 8));
 720      }
 2004721    }
 722
 723    /// <summary>
 724    /// Add an integer value in little endian order to the pending new entry.
 725    /// </summary>
 726    /// <param name="toAdd">The data to add.</param>
 727    /// <seealso cref="StartNewEntry"/>
 728    public void AddLeInt(int toAdd)
 729    {
 730      unchecked {
 1002731        AddLeShort((short)toAdd);
 1002732        AddLeShort((short)(toAdd >> 16));
 733      }
 1002734    }
 735
 736    /// <summary>
 737    /// Add a long value in little endian order to the pending new entry.
 738    /// </summary>
 739    /// <param name="toAdd">The data to add.</param>
 740    /// <seealso cref="StartNewEntry"/>
 741    public void AddLeLong(long toAdd)
 742    {
 743      unchecked {
 501744        AddLeInt((int)(toAdd & 0xffffffff));
 501745        AddLeInt((int)(toAdd >> 32));
 746      }
 501747    }
 748
 749    /// <summary>
 750    /// Delete an extra data field.
 751    /// </summary>
 752    /// <param name="headerID">The identifier of the field to delete.</param>
 753    /// <returns>Returns true if the field was found and deleted.</returns>
 754    public bool Delete(int headerID)
 755    {
 131791756      bool result = false;
 757
 131791758       if (Find(headerID)) {
 6759        result = true;
 6760        int trueStart = _readValueStart - 4;
 761
 6762        byte[] newData = new byte[_data.Length - (ValueLength + 4)];
 6763        Array.Copy(_data, 0, newData, 0, trueStart);
 764
 6765        int trueEnd = trueStart + ValueLength + 4;
 6766        Array.Copy(_data, trueEnd, newData, trueStart, _data.Length - trueEnd);
 6767        _data = newData;
 768      }
 131791769      return result;
 770    }
 771
 772    #region Reading Support
 773    /// <summary>
 774    /// Read a long in little endian form from the last <see cref="Find">found</see> data value
 775    /// </summary>
 776    /// <returns>Returns the long value read.</returns>
 777    public long ReadLong()
 778    {
 357779      ReadCheck(8);
 354780      return (ReadInt() & 0xffffffff) | (((long)ReadInt()) << 32);
 781    }
 782
 783    /// <summary>
 784    /// Read an integer in little endian form from the last <see cref="Find">found</see> data value.
 785    /// </summary>
 786    /// <returns>Returns the integer read.</returns>
 787    public int ReadInt()
 788    {
 712789      ReadCheck(4);
 790
 709791      int result = _data[_index] + (_data[_index + 1] << 8) +
 709792        (_data[_index + 2] << 16) + (_data[_index + 3] << 24);
 709793      _index += 4;
 709794      return result;
 795    }
 796
 797    /// <summary>
 798    /// Read a short value in little endian form from the last <see cref="Find">found</see> data value.
 799    /// </summary>
 800    /// <returns>Returns the short value read.</returns>
 801    public int ReadShort()
 802    {
 4803      ReadCheck(2);
 1804      int result = _data[_index] + (_data[_index + 1] << 8);
 1805      _index += 2;
 1806      return result;
 807    }
 808
 809    /// <summary>
 810    /// Read a byte from an extra data
 811    /// </summary>
 812    /// <returns>The byte value read or -1 if the end of data has been reached.</returns>
 813    public int ReadByte()
 814    {
 26815      int result = -1;
 26816       if ((_index < _data.Length) && (_readValueStart + _readValueLength > _index)) {
 19817        result = _data[_index];
 19818        _index += 1;
 819      }
 26820      return result;
 821    }
 822
 823    /// <summary>
 824    /// Skip data during reading.
 825    /// </summary>
 826    /// <param name="amount">The number of bytes to skip.</param>
 827    public void Skip(int amount)
 828    {
 6829      ReadCheck(amount);
 4830      _index += amount;
 4831    }
 832
 833    void ReadCheck(int length)
 834    {
 1079835       if ((_readValueStart > _data.Length) ||
 1079836        (_readValueStart < 4)) {
 0837        throw new ZipException("Find must be called before calling a Read method");
 838      }
 839
 1079840       if (_index > _readValueStart + _readValueLength - length) {
 10841        throw new ZipException("End of extra data");
 842      }
 843
 1069844       if (_index + length < 4) {
 1845        throw new ZipException("Cannot read before start of tag");
 846      }
 1068847    }
 848
 849    /// <summary>
 850    /// Internal form of <see cref="ReadShort"/> that reads data at any location.
 851    /// </summary>
 852    /// <returns>Returns the short value read.</returns>
 853    int ReadShortInternal()
 854    {
 956855       if (_index > _data.Length - 2) {
 0856        throw new ZipException("End of extra data");
 857      }
 858
 956859      int result = _data[_index] + (_data[_index + 1] << 8);
 956860      _index += 2;
 956861      return result;
 862    }
 863
 864    void SetShort(ref int index, int source)
 865    {
 518866      _data[index] = (byte)source;
 518867      _data[index + 1] = (byte)(source >> 8);
 518868      index += 2;
 518869    }
 870
 871    #endregion
 872
 873    #region IDisposable Members
 874
 875    /// <summary>
 876    /// Dispose of this instance.
 877    /// </summary>
 878    public void Dispose()
 879    {
 0880       if (_newEntry != null) {
 0881        _newEntry.Close();
 882      }
 0883    }
 884
 885    #endregion
 886
 887    #region Instance Fields
 888    int _index;
 889    int _readValueStart;
 890    int _readValueLength;
 891
 892    MemoryStream _newEntry;
 893    byte[] _data;
 894    #endregion
 895  }
 896}