Browse files

Added WrappingStream and tests.

  • Loading branch information...
1 parent 50e9931 commit f769db842a3e2fcd5403d53c8b915799749aea8e @bgrainger bgrainger committed Feb 2, 2010
View
220 src/Logos.Utility/IO/WrappingStream.cs
@@ -0,0 +1,220 @@
+
+using System;
+using System.IO;
+
+namespace Logos.Utility.IO
+{
+ /// <summary>
+ /// A <see cref="Stream"/> that wraps another stream. One major feature of <see cref="WrappingStream"/> is that it does not dispose the
+ /// underlying stream when it is disposed if Ownership.None is used; this is useful when using classes such as <see cref="BinaryReader"/> and
+ /// <see cref="System.Security.Cryptography.CryptoStream"/> that take ownership of the stream passed to their constructors.
+ /// </summary>
+ /// <remarks>See <a href="http://code.logos.com/blog/2009/05/wrappingstream_implementation.html">WrappingStream Implementation</a>.</remarks>
+ public class WrappingStream : Stream
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="WrappingStream"/> class.
+ /// </summary>
+ /// <param name="streamBase">The wrapped stream.</param>
+ /// <param name="ownership">Use Owns if the wrapped stream should be disposed when this stream is disposed.</param>
+ public WrappingStream(Stream streamBase, Ownership ownership)
+ {
+ // check parameters
+ if (streamBase == null)
+ throw new ArgumentNullException("streamBase");
+
+ m_streamBase = streamBase;
+ m_ownership = ownership;
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether the current stream supports reading.
+ /// </summary>
+ /// <returns><c>true</c> if the stream supports reading; otherwise, <c>false</c>.</returns>
+ public override bool CanRead
+ {
+ get { return m_streamBase == null ? false : m_streamBase.CanRead; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether the current stream supports seeking.
+ /// </summary>
+ /// <returns><c>true</c> if the stream supports seeking; otherwise, <c>false</c>.</returns>
+ public override bool CanSeek
+ {
+ get { return m_streamBase == null ? false : m_streamBase.CanSeek; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether the current stream supports writing.
+ /// </summary>
+ /// <returns><c>true</c> if the stream supports writing; otherwise, <c>false</c>.</returns>
+ public override bool CanWrite
+ {
+ get { return m_streamBase == null ? false : m_streamBase.CanWrite; }
+ }
+
+ /// <summary>
+ /// Gets the length in bytes of the stream.
+ /// </summary>
+ public override long Length
+ {
+ get { ThrowIfDisposed(); return m_streamBase.Length; }
+ }
+
+ /// <summary>
+ /// Gets or sets the position within the current stream.
+ /// </summary>
+ public override long Position
+ {
+ get { ThrowIfDisposed(); return m_streamBase.Position; }
+ set { ThrowIfDisposed(); m_streamBase.Position = value; }
+ }
+
+ /// <summary>
+ /// Begins an asynchronous read operation.
+ /// </summary>
+ public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
+ {
+ ThrowIfDisposed();
+ return m_streamBase.BeginRead(buffer, offset, count, callback, state);
+ }
+
+ /// <summary>
+ /// Begins an asynchronous write operation.
+ /// </summary>
+ public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
+ {
+ ThrowIfDisposed();
+ return m_streamBase.BeginWrite(buffer, offset, count, callback, state);
+ }
+
+ /// <summary>
+ /// Waits for the pending asynchronous read to complete.
+ /// </summary>
+ public override int EndRead(IAsyncResult asyncResult)
+ {
+ ThrowIfDisposed();
+ return m_streamBase.EndRead(asyncResult);
+ }
+
+ /// <summary>
+ /// Ends an asynchronous write operation.
+ /// </summary>
+ public override void EndWrite(IAsyncResult asyncResult)
+ {
+ ThrowIfDisposed();
+ m_streamBase.EndWrite(asyncResult);
+ }
+
+ /// <summary>
+ /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device.
+ /// </summary>
+ public override void Flush()
+ {
+ ThrowIfDisposed();
+ m_streamBase.Flush();
+ }
+
+ /// <summary>
+ /// Reads a sequence of bytes from the current stream and advances the position
+ /// within the stream by the number of bytes read.
+ /// </summary>
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ ThrowIfDisposed();
+ return m_streamBase.Read(buffer, offset, count);
+ }
+
+ /// <summary>
+ /// Reads a byte from the stream and advances the position within the stream by one byte, or returns -1 if at the end of the stream.
+ /// </summary>
+ public override int ReadByte()
+ {
+ ThrowIfDisposed();
+ return m_streamBase.ReadByte();
+ }
+
+ /// <summary>
+ /// Sets the position within the current stream.
+ /// </summary>
+ /// <param name="offset">A byte offset relative to the <paramref name="origin"/> parameter.</param>
+ /// <param name="origin">A value of type <see cref="T:System.IO.SeekOrigin"/> indicating the reference point used to obtain the new position.</param>
+ /// <returns>The new position within the current stream.</returns>
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ ThrowIfDisposed();
+ return m_streamBase.Seek(offset, origin);
+ }
+
+ /// <summary>
+ /// Sets the length of the current stream.
+ /// </summary>
+ /// <param name="value">The desired length of the current stream in bytes.</param>
+ public override void SetLength(long value)
+ {
+ ThrowIfDisposed();
+ m_streamBase.SetLength(value);
+ }
+
+ /// <summary>
+ /// Writes a sequence of bytes to the current stream and advances the current position
+ /// within this stream by the number of bytes written.
+ /// </summary>
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ ThrowIfDisposed();
+ m_streamBase.Write(buffer, offset, count);
+ }
+
+ /// <summary>
+ /// Writes a byte to the current position in the stream and advances the position within the stream by one byte.
+ /// </summary>
+ public override void WriteByte(byte value)
+ {
+ ThrowIfDisposed();
+ m_streamBase.WriteByte(value);
+ }
+
+ /// <summary>
+ /// Gets the wrapped stream.
+ /// </summary>
+ /// <value>The wrapped stream.</value>
+ protected Stream WrappedStream
+ {
+ get { return m_streamBase; }
+ }
+
+ /// <summary>
+ /// Releases the unmanaged resources used by the <see cref="WrappingStream"/> and optionally releases the managed resources.
+ /// </summary>
+ /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
+ protected override void Dispose(bool disposing)
+ {
+ try
+ {
+ // doesn't close the base stream, but just prevents access to it through this WrappingStream
+ if (disposing)
+ {
+ if (m_streamBase != null && m_ownership == Ownership.Owns)
+ m_streamBase.Dispose();
+ m_streamBase = null;
+ }
+ }
+ finally
+ {
+ base.Dispose(disposing);
+ }
+ }
+
+ private void ThrowIfDisposed()
+ {
+ // throws an ObjectDisposedException if this object has been disposed
+ if (m_streamBase == null)
+ throw new ObjectDisposedException(GetType().Name);
+ }
+
+ Stream m_streamBase;
+ readonly Ownership m_ownership;
+ }
+}
View
2 src/Logos.Utility/Logos.Utility.csproj
@@ -47,8 +47,10 @@
<Compile Include="DictionaryUtility.cs" />
<Compile Include="DisposableService.cs" />
<Compile Include="HashCodeUtility.cs" />
+ <Compile Include="IO\WrappingStream.cs" />
<Compile Include="ObjectImpl.cs" />
<Compile Include="ObjectUtility.cs" />
+ <Compile Include="Ownership.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Scope.cs" />
</ItemGroup>
View
19 src/Logos.Utility/Ownership.cs
@@ -0,0 +1,19 @@
+
+namespace Logos.Utility
+{
+ /// <summary>
+ /// Indicates whether an object takes ownership of an item.
+ /// </summary>
+ public enum Ownership
+ {
+ /// <summary>
+ /// The object does not own this item.
+ /// </summary>
+ None,
+
+ /// <summary>
+ /// The object owns this item, and is responsible for releasing it.
+ /// </summary>
+ Owns
+ }
+}
View
1 tests/Logos.Utility.Tests/Logos.Utility.Tests.csproj
@@ -58,6 +58,7 @@
<Compile Include="ObjectUtilityTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ScopeTests.cs" />
+ <Compile Include="WrappingStreamTests.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Logos.Utility\Logos.Utility.csproj">
View
174 tests/Logos.Utility.Tests/WrappingStreamTests.cs
@@ -0,0 +1,174 @@
+
+using System;
+using System.IO;
+using System.Linq;
+using Logos.Utility.IO;
+using NUnit.Framework;
+
+namespace Logos.Utility.Tests
+{
+ [TestFixture]
+ public class WrappingStreamTests
+ {
+ [SetUp]
+ public void SetUp()
+ {
+ m_memStream = new MemoryStream();
+ m_memStream.Write(s_streamData, 0, s_streamData.Length);
+
+ m_stream = new WrappingStream(m_memStream, Ownership.None);
+ }
+
+ [TearDown]
+ public void TearDown()
+ {
+ m_stream = null;
+ m_memStream = null;
+ }
+
+ [Test]
+ public void Constructor()
+ {
+ Assert.IsTrue(m_stream.CanRead);
+ Assert.IsTrue(m_stream.CanSeek);
+ Assert.IsTrue(m_stream.CanWrite);
+ Assert.AreEqual(s_streamData.Length, m_stream.Length);
+ }
+
+ [Test]
+ public void ConstructorNull()
+ {
+ Assert.Throws<ArgumentNullException>(() => new WrappingStream(null, Ownership.None));
+ }
+
+ [Test]
+ public void Dispose()
+ {
+ m_stream.Dispose();
+ m_stream.Dispose();
+
+ Assert.IsTrue(m_memStream.CanRead);
+ Assert.IsTrue(m_memStream.CanSeek);
+ Assert.IsTrue(m_memStream.CanWrite);
+ Assert.IsFalse(m_stream.CanRead);
+ Assert.IsFalse(m_stream.CanSeek);
+ Assert.IsFalse(m_stream.CanWrite);
+
+ long l;
+ Assert.Throws<ObjectDisposedException>(() => l = m_stream.Length);
+ Assert.Throws<ObjectDisposedException>(() => l = m_stream.Position);
+ Assert.Throws<ObjectDisposedException>(() => m_stream.Position = 0);
+ Assert.Throws<ObjectDisposedException>(() => m_stream.BeginRead(new byte[1], 0, 1, null, null));
+ Assert.Throws<ObjectDisposedException>(() => m_stream.EndRead(null));
+ Assert.Throws<ObjectDisposedException>(() => m_stream.BeginWrite(new byte[1], 0, 1, null, null));
+ Assert.Throws<ObjectDisposedException>(() => m_stream.EndWrite(null));
+ Assert.Throws<ObjectDisposedException>(() => m_stream.Flush());
+ Assert.Throws<ObjectDisposedException>(() => m_stream.Read(new byte[1], 0, 1));
+ Assert.Throws<ObjectDisposedException>(() => m_stream.ReadByte());
+ Assert.Throws<ObjectDisposedException>(() => m_stream.Write(new byte[1], 0, 1));
+ Assert.Throws<ObjectDisposedException>(() => m_stream.WriteByte(0));
+ Assert.Throws<ObjectDisposedException>(() => m_stream.Seek(0, SeekOrigin.Begin));
+ Assert.Throws<ObjectDisposedException>(() => m_stream.SetLength(16));
+ }
+
+ [Test]
+ public void Flush()
+ {
+ m_stream.Flush();
+ }
+
+ [Test]
+ public void Read()
+ {
+ m_stream.Position = 0;
+ byte[] bytes = new byte[s_streamData.Length];
+ Assert.AreEqual(bytes.Length, m_stream.Read(bytes, 0, bytes.Length));
+ CollectionAssert.AreEqual(s_streamData, bytes);
+ }
+
+ [Test]
+ public void ReadAsync()
+ {
+ m_stream.Position = 0;
+ byte[] bytes = new byte[s_streamData.Length];
+ IAsyncResult ar = m_stream.BeginRead(bytes, 0, 8, null, null);
+ Assert.AreEqual(bytes.Length, m_stream.EndRead(ar));
+ CollectionAssert.AreEqual(s_streamData, bytes);
+ }
+
+ [Test]
+ public void ReadByte()
+ {
+ m_stream.Position = 0;
+ Assert.AreEqual(s_streamData[0], m_stream.ReadByte());
+ Assert.AreEqual(s_streamData[1], m_stream.ReadByte());
+ Assert.AreEqual(s_streamData[2], m_stream.ReadByte());
+ m_stream.Seek(1, SeekOrigin.Current);
+ Assert.AreEqual(s_streamData[4], m_stream.ReadByte());
+ m_stream.Position = m_stream.Position - 2;
+ Assert.AreEqual(s_streamData[3], m_stream.ReadByte());
+ m_stream.Seek(7, SeekOrigin.Begin);
+ Assert.AreEqual(s_streamData[7], m_stream.ReadByte());
+ m_stream.Seek(0, SeekOrigin.End);
+ Assert.AreEqual(-1, m_stream.ReadByte());
+ }
+
+ [Test]
+ public void WriteByte()
+ {
+ m_stream.WriteByte(9);
+ Assert.AreEqual(s_streamData.Length + 1, m_stream.Length);
+ m_stream.Position = m_stream.Position - 1;
+ Assert.AreEqual(9, m_stream.ReadByte());
+ Assert.AreEqual(-1, m_stream.ReadByte());
+ }
+
+ [Test]
+ public void Write()
+ {
+ m_stream.Write(s_streamData, 0, s_streamData.Length);
+ VerifyWrite();
+ }
+
+ [Test]
+ public void BeginWrite()
+ {
+ IAsyncResult ar = m_stream.BeginWrite(s_streamData, 0, s_streamData.Length, null, null);
+ m_stream.EndWrite(ar);
+ VerifyWrite();
+ }
+
+ [Test]
+ public void SetLength()
+ {
+ m_stream.SetLength(4);
+ Assert.AreEqual(4, m_stream.Length);
+ Assert.AreEqual(4, m_stream.Position);
+
+ m_stream.SetLength(0);
+ Assert.AreEqual(0, m_stream.Length);
+ Assert.AreEqual(0, m_stream.Position);
+
+ m_stream.SetLength(256);
+ Assert.AreEqual(256, m_stream.Length);
+ Assert.AreEqual(0, m_stream.Position);
+ }
+
+ private void VerifyWrite()
+ {
+ Assert.AreEqual(s_streamData.Length * 2, m_stream.Length);
+
+ m_stream.Position = 0;
+
+ byte[] bytes = new byte[s_streamData.Length * 2];
+ m_stream.Read(bytes, 0, bytes.Length);
+ CollectionAssert.AreEqual(s_streamData, bytes.Take(s_streamData.Length));
+ CollectionAssert.AreEqual(s_streamData, bytes.Skip(s_streamData.Length));
+ }
+
+ Stream m_memStream;
+ Stream m_stream;
+
+ static readonly byte[] s_streamData = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 };
+ }
+}

0 comments on commit f769db8

Please sign in to comment.