Summary

Class:ICSharpCode.SharpZipLib.Tar.TarOutputStream
Assembly:ICSharpCode.SharpZipLib
File(s):C:\Users\Neil\Documents\Visual Studio 2015\Projects\icsharpcode\SZL_master\ICSharpCode.SharpZipLib\Tar\TarOutputStream.cs
Covered lines:64
Uncovered lines:54
Coverable lines:118
Total lines:442
Line coverage:54.2%
Branch coverage:58.3%

Metrics

MethodCyclomatic ComplexitySequence CoverageBranch Coverage
.ctor(...)1100100
.ctor(...)287.566.67
Seek(...)100
SetLength(...)100
ReadByte()100
Read(...)100
Flush()1100100
Finish()2100100
Close()210066.67
GetRecordSize()100
PutNextEntry(...)526.9244.44
CloseEntry()377.7860
WriteByte(...)1100100
Write(...)1059.4668.42
WriteEofBlock()1100100

File(s)

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

#LineLine coverage
 1using System;
 2using System.IO;
 3
 4namespace ICSharpCode.SharpZipLib.Tar
 5{
 6  /// <summary>
 7  /// The TarOutputStream writes a UNIX tar archive as an OutputStream.
 8  /// Methods are provided to put entries, and then write their contents
 9  /// by writing to this stream using write().
 10  /// </summary>
 11  /// public
 12  public class TarOutputStream : Stream
 13  {
 14    #region Constructors
 15    /// <summary>
 16    /// Construct TarOutputStream using default block factor
 17    /// </summary>
 18    /// <param name="outputStream">stream to write to</param>
 19    public TarOutputStream(Stream outputStream)
 320      : this(outputStream, TarBuffer.DefaultBlockFactor)
 21    {
 322    }
 23
 24    /// <summary>
 25    /// Construct TarOutputStream with user specified block factor
 26    /// </summary>
 27    /// <param name="outputStream">stream to write to</param>
 28    /// <param name="blockFactor">blocking factor</param>
 7329    public TarOutputStream(Stream outputStream, int blockFactor)
 30    {
 7331       if (outputStream == null) {
 032        throw new ArgumentNullException(nameof(outputStream));
 33      }
 34
 7335      this.outputStream = outputStream;
 7336      buffer = TarBuffer.CreateOutputTarBuffer(outputStream, blockFactor);
 37
 7338      assemblyBuffer = new byte[TarBuffer.BlockSize];
 7339      blockBuffer = new byte[TarBuffer.BlockSize];
 7340    }
 41    #endregion
 42
 43    /// <summary>
 44    /// Get/set flag indicating ownership of the underlying stream.
 45    /// When the flag is true <see cref="Close"></see> will close the underlying stream also.
 46    /// </summary>
 47    public bool IsStreamOwner {
 048      get { return buffer.IsStreamOwner; }
 249      set { buffer.IsStreamOwner = value; }
 50    }
 51
 52    /// <summary>
 53    /// true if the stream supports reading; otherwise, false.
 54    /// </summary>
 55    public override bool CanRead {
 56      get {
 057        return outputStream.CanRead;
 58      }
 59    }
 60
 61    /// <summary>
 62    /// true if the stream supports seeking; otherwise, false.
 63    /// </summary>
 64    public override bool CanSeek {
 65      get {
 066        return outputStream.CanSeek;
 67      }
 68    }
 69
 70    /// <summary>
 71    /// true if stream supports writing; otherwise, false.
 72    /// </summary>
 73    public override bool CanWrite {
 74      get {
 075        return outputStream.CanWrite;
 76      }
 77    }
 78
 79    /// <summary>
 80    /// length of stream in bytes
 81    /// </summary>
 82    public override long Length {
 83      get {
 084        return outputStream.Length;
 85      }
 86    }
 87
 88    /// <summary>
 89    /// gets or sets the position within the current stream.
 90    /// </summary>
 91    public override long Position {
 92      get {
 093        return outputStream.Position;
 94      }
 95      set {
 096        outputStream.Position = value;
 097      }
 98    }
 99
 100    /// <summary>
 101    /// set the position within the current stream
 102    /// </summary>
 103    /// <param name="offset">The offset relative to the <paramref name="origin"/> to seek to</param>
 104    /// <param name="origin">The <see cref="SeekOrigin"/> to seek from.</param>
 105    /// <returns>The new position in the stream.</returns>
 106    public override long Seek(long offset, SeekOrigin origin)
 107    {
 0108      return outputStream.Seek(offset, origin);
 109    }
 110
 111    /// <summary>
 112    /// Set the length of the current stream
 113    /// </summary>
 114    /// <param name="value">The new stream length.</param>
 115    public override void SetLength(long value)
 116    {
 0117      outputStream.SetLength(value);
 0118    }
 119
 120    /// <summary>
 121    /// Read a byte from the stream and advance the position within the stream
 122    /// by one byte or returns -1 if at the end of the stream.
 123    /// </summary>
 124    /// <returns>The byte value or -1 if at end of stream</returns>
 125    public override int ReadByte()
 126    {
 0127      return outputStream.ReadByte();
 128    }
 129
 130    /// <summary>
 131    /// read bytes from the current stream and advance the position within the
 132    /// stream by the number of bytes read.
 133    /// </summary>
 134    /// <param name="buffer">The buffer to store read bytes in.</param>
 135    /// <param name="offset">The index into the buffer to being storing bytes at.</param>
 136    /// <param name="count">The desired number of bytes to read.</param>
 137    /// <returns>The total number of bytes read, or zero if at the end of the stream.
 138    /// The number of bytes may be less than the <paramref name="count">count</paramref>
 139    /// requested if data is not avialable.</returns>
 140    public override int Read(byte[] buffer, int offset, int count)
 141    {
 0142      return outputStream.Read(buffer, offset, count);
 143    }
 144
 145    /// <summary>
 146    /// All buffered data is written to destination
 147    /// </summary>
 148    public override void Flush()
 149    {
 1150      outputStream.Flush();
 1151    }
 152
 153    /// <summary>
 154    /// Ends the TAR archive without closing the underlying OutputStream.
 155    /// The result is that the EOF block of nulls is written.
 156    /// </summary>
 157    public void Finish()
 158    {
 73159       if (IsEntryOpen) {
 5160        CloseEntry();
 161      }
 73162      WriteEofBlock();
 73163    }
 164
 165    /// <summary>
 166    /// Ends the TAR archive and closes the underlying OutputStream.
 167    /// </summary>
 168    /// <remarks>This means that Finish() is called followed by calling the
 169    /// TarBuffer's Close().</remarks>
 170    public override void Close()
 171    {
 73172       if (!isClosed) {
 73173        isClosed = true;
 73174        Finish();
 73175        buffer.Close();
 176      }
 73177    }
 178
 179    /// <summary>
 180    /// Get the record size being used by this stream's TarBuffer.
 181    /// </summary>
 182    public int RecordSize {
 1183      get { return buffer.RecordSize; }
 184    }
 185
 186    /// <summary>
 187    /// Get the record size being used by this stream's TarBuffer.
 188    /// </summary>
 189    /// <returns>
 190    /// The TarBuffer record size.
 191    /// </returns>
 192    [Obsolete("Use RecordSize property instead")]
 193    public int GetRecordSize()
 194    {
 0195      return buffer.RecordSize;
 196    }
 197
 198    /// <summary>
 199    /// Get a value indicating wether an entry is open, requiring more data to be written.
 200    /// </summary>
 201    bool IsEntryOpen {
 73202      get { return (currBytes < currSize); }
 203
 204    }
 205
 206    /// <summary>
 207    /// Put an entry on the output stream. This writes the entry's
 208    /// header and positions the output stream for writing
 209    /// the contents of the entry. Once this method is called, the
 210    /// stream is ready for calls to write() to write the entry's
 211    /// contents. Once the contents are written, closeEntry()
 212    /// <B>MUST</B> be called to ensure that all buffered data
 213    /// is completely written to the output stream.
 214    /// </summary>
 215    /// <param name="entry">
 216    /// The TarEntry to be written to the archive.
 217    /// </param>
 218    public void PutNextEntry(TarEntry entry)
 219    {
 70220       if (entry == null) {
 0221        throw new ArgumentNullException(nameof(entry));
 222      }
 223
 70224       if (entry.TarHeader.Name.Length > TarHeader.NAMELEN) {
 0225        var longHeader = new TarHeader();
 0226        longHeader.TypeFlag = TarHeader.LF_GNU_LONGNAME;
 0227        longHeader.Name = longHeader.Name + "././@LongLink";
 0228        longHeader.Mode = 420;//644 by default
 0229        longHeader.UserId = entry.UserId;
 0230        longHeader.GroupId = entry.GroupId;
 0231        longHeader.GroupName = entry.GroupName;
 0232        longHeader.UserName = entry.UserName;
 0233        longHeader.LinkName = "";
 0234        longHeader.Size = entry.TarHeader.Name.Length + 1;  // Plus one to avoid dropping last char
 235
 0236        longHeader.WriteHeader(blockBuffer);
 0237        buffer.WriteBlock(blockBuffer);  // Add special long filename header block
 238
 0239        int nameCharIndex = 0;
 240
 0241         while (nameCharIndex < entry.TarHeader.Name.Length) {
 0242          Array.Clear(blockBuffer, 0, blockBuffer.Length);
 0243          TarHeader.GetAsciiBytes(entry.TarHeader.Name, nameCharIndex, this.blockBuffer, 0, TarBuffer.BlockSize);
 0244          nameCharIndex += TarBuffer.BlockSize;
 0245          buffer.WriteBlock(blockBuffer);
 246        }
 247      }
 248
 70249      entry.WriteEntryHeader(blockBuffer);
 70250      buffer.WriteBlock(blockBuffer);
 251
 70252      currBytes = 0;
 253
 70254       currSize = entry.IsDirectory ? 0 : entry.Size;
 70255    }
 256
 257    /// <summary>
 258    /// Close an entry. This method MUST be called for all file
 259    /// entries that contain data. The reason is that we must
 260    /// buffer data written to the stream in order to satisfy
 261    /// the buffer's block based writes. Thus, there may be
 262    /// data fragments still being assembled that must be written
 263    /// to the output stream before this entry is closed and the
 264    /// next entry written.
 265    /// </summary>
 266    public void CloseEntry()
 267    {
 5268       if (assemblyBufferLength > 0) {
 5269        Array.Clear(assemblyBuffer, assemblyBufferLength, assemblyBuffer.Length - assemblyBufferLength);
 270
 5271        buffer.WriteBlock(assemblyBuffer);
 272
 5273        currBytes += assemblyBufferLength;
 5274        assemblyBufferLength = 0;
 275      }
 276
 5277       if (currBytes < currSize) {
 0278        string errorText = string.Format(
 0279          "Entry closed at '{0}' before the '{1}' bytes specified in the header were written",
 0280          currBytes, currSize);
 0281        throw new TarException(errorText);
 282      }
 5283    }
 284
 285    /// <summary>
 286    /// Writes a byte to the current tar archive entry.
 287    /// This method simply calls Write(byte[], int, int).
 288    /// </summary>
 289    /// <param name="value">
 290    /// The byte to be written.
 291    /// </param>
 292    public override void WriteByte(byte value)
 293    {
 45294      Write(new byte[] { value }, 0, 1);
 45295    }
 296
 297    /// <summary>
 298    /// Writes bytes to the current tar archive entry. This method
 299    /// is aware of the current entry and will throw an exception if
 300    /// you attempt to write bytes past the length specified for the
 301    /// current entry. The method is also (painfully) aware of the
 302    /// record buffering required by TarBuffer, and manages buffers
 303    /// that are not a multiple of recordsize in length, including
 304    /// assembling records from small buffers.
 305    /// </summary>
 306    /// <param name = "buffer">
 307    /// The buffer to write to the archive.
 308    /// </param>
 309    /// <param name = "offset">
 310    /// The offset in the buffer from which to get bytes.
 311    /// </param>
 312    /// <param name = "count">
 313    /// The number of bytes to write.
 314    /// </param>
 315    public override void Write(byte[] buffer, int offset, int count)
 316    {
 4087317       if (buffer == null) {
 0318        throw new ArgumentNullException(nameof(buffer));
 319      }
 320
 4087321       if (offset < 0) {
 0322        throw new ArgumentOutOfRangeException(nameof(offset), "Cannot be negative");
 323      }
 324
 4087325       if (buffer.Length - offset < count) {
 0326        throw new ArgumentException("offset and count combination is invalid");
 327      }
 328
 4087329       if (count < 0) {
 0330        throw new ArgumentOutOfRangeException(nameof(count), "Cannot be negative");
 331      }
 332
 4087333       if ((currBytes + count) > currSize) {
 0334        string errorText = string.Format("request to write '{0}' bytes exceeds size in header of '{1}' bytes",
 0335          count, this.currSize);
 0336        throw new ArgumentOutOfRangeException(nameof(count), errorText);
 337      }
 338
 339      //
 340      // We have to deal with assembly!!!
 341      // The programmer can be writing little 32 byte chunks for all
 342      // we know, and we must assemble complete blocks for writing.
 343      // TODO  REVIEW Maybe this should be in TarBuffer? Could that help to
 344      //        eliminate some of the buffer copying.
 345      //
 4087346       if (assemblyBufferLength > 0) {
 40347         if ((assemblyBufferLength + count) >= blockBuffer.Length) {
 0348          int aLen = blockBuffer.Length - assemblyBufferLength;
 349
 0350          Array.Copy(assemblyBuffer, 0, blockBuffer, 0, assemblyBufferLength);
 0351          Array.Copy(buffer, offset, blockBuffer, assemblyBufferLength, aLen);
 352
 0353          this.buffer.WriteBlock(blockBuffer);
 354
 0355          currBytes += blockBuffer.Length;
 356
 0357          offset += aLen;
 0358          count -= aLen;
 359
 0360          assemblyBufferLength = 0;
 0361        } else {
 40362          Array.Copy(buffer, offset, assemblyBuffer, assemblyBufferLength, count);
 40363          offset += count;
 40364          assemblyBufferLength += count;
 40365          count -= count;
 366        }
 367      }
 368
 369      //
 370      // When we get here we have EITHER:
 371      //   o An empty "assembly" buffer.
 372      //   o No bytes to write (count == 0)
 373      //
 8129374       while (count > 0) {
 4047375         if (count < blockBuffer.Length) {
 5376          Array.Copy(buffer, offset, assemblyBuffer, assemblyBufferLength, count);
 5377          assemblyBufferLength += count;
 5378          break;
 379        }
 380
 4042381        this.buffer.WriteBlock(buffer, offset);
 382
 4042383        int bufferLength = blockBuffer.Length;
 4042384        currBytes += bufferLength;
 4042385        count -= bufferLength;
 4042386        offset += bufferLength;
 387      }
 4082388    }
 389
 390    /// <summary>
 391    /// Write an EOF (end of archive) block to the tar archive.
 392    /// An EOF block consists of all zeros.
 393    /// </summary>
 394    void WriteEofBlock()
 395    {
 73396      Array.Clear(blockBuffer, 0, blockBuffer.Length);
 73397      buffer.WriteBlock(blockBuffer);
 73398    }
 399
 400    #region Instance Fields
 401    /// <summary>
 402    /// bytes written for this entry so far
 403    /// </summary>
 404    long currBytes;
 405
 406    /// <summary>
 407    /// current 'Assembly' buffer length
 408    /// </summary>
 409    int assemblyBufferLength;
 410
 411    /// <summary>
 412    /// Flag indicating wether this instance has been closed or not.
 413    /// </summary>
 414    bool isClosed;
 415
 416    /// <summary>
 417    /// Size for the current entry
 418    /// </summary>
 419    protected long currSize;
 420
 421    /// <summary>
 422    /// single block working buffer
 423    /// </summary>
 424    protected byte[] blockBuffer;
 425
 426    /// <summary>
 427    /// 'Assembly' buffer used to assemble data before writing
 428    /// </summary>
 429    protected byte[] assemblyBuffer;
 430
 431    /// <summary>
 432    /// TarBuffer used to provide correct blocking factor
 433    /// </summary>
 434    protected TarBuffer buffer;
 435
 436    /// <summary>
 437    /// the destination stream for the archive contents
 438    /// </summary>
 439    protected Stream outputStream;
 440    #endregion
 441  }
 442}