Permalink
Browse files

Added StreamUtility.ReadExactly.

ReadExactly repeatedly reads from the underlying stream until the specified number
of bytes have been read; this is useful for consuming streams that can't always
perform the entire read in one call.
  • Loading branch information...
1 parent b2f5ccd commit 1c579244f193219ab950f44143b601ae6a5bb471 @bgrainger bgrainger committed Dec 29, 2010
Showing with 151 additions and 0 deletions.
  1. +50 −0 src/Logos.Utility/IO/StreamUtility.cs
  2. +101 −0 tests/Logos.Utility.Tests/StreamUtilityTests.cs
@@ -73,5 +73,55 @@ public static Encoding DetectBestEncoding(Stream stream)
// return detected encoding (or null for failure)
return encoding;
}
+
+ /// <summary>
+ /// Reads exactly <paramref name="count"/> bytes from <paramref name="stream"/>.
+ /// </summary>
+ /// <param name="stream">The stream to read from.</param>
+ /// <param name="count">The count of bytes to read.</param>
+ /// <returns>A new byte array containing the data read from the stream.</returns>
+ public static byte[] ReadExactly(this Stream stream, int count)
+ {
+ if (count < 0)
+ throw new ArgumentOutOfRangeException("count");
+ byte[] buffer = new byte[count];
+ ReadExactly(stream, buffer, 0, count);
+ return buffer;
+ }
+
+ /// <summary>
+ /// Reads exactly <paramref name="count"/> bytes from <paramref name="stream"/> into
+ /// <paramref name="buffer"/>, starting at the byte given by <paramref name="offset"/>.
+ /// </summary>
+ /// <param name="stream">The stream to read from.</param>
+ /// <param name="buffer">The buffer to read data into.</param>
+ /// <param name="offset">The offset within the buffer at which data is first written.</param>
+ /// <param name="count">The count of bytes to read.</param>
+ public static void ReadExactly(this Stream stream, byte[] buffer, int offset, int count)
+ {
+ // check arguments
+ if (stream == null)
+ throw new ArgumentNullException("stream");
+ if (buffer == null)
+ throw new ArgumentNullException("buffer");
+ if (offset < 0 || offset > buffer.Length)
+ throw new ArgumentOutOfRangeException("offset");
+ if (count < 0 || buffer.Length - offset < count)
+ throw new ArgumentOutOfRangeException("count");
+
+ while (count > 0)
+ {
+ // read data
+ int bytesRead = stream.Read(buffer, offset, count);
+
+ // check for failure to read
+ if (bytesRead == 0)
+ throw new EndOfStreamException();
+
+ // move to next block
+ offset += bytesRead;
+ count -= bytesRead;
+ }
+ }
}
}
@@ -1,5 +1,7 @@
+using System;
using System.IO;
+using System.Linq;
using System.Text;
using Logos.Utility.IO;
using NUnit.Framework;
@@ -60,5 +62,104 @@ private static void DoDetectEncoding(Encoding encoding, string testPattern)
const string c_strEnglish = "‘This’ is “Unicode” text—it has €123.45 and symbols.™\r\nIn the beginning, God created the heavens and the earth. The earth was without form and void, and darkness was over the face of the deep. And the Spirit of God was hovering over the face of the waters.";
const string c_strKorean = "태초에 말씀이 계시니라 이 말씀이 하나님과 함께 계셨으니 이 말씀은 곧 하나님이시니라";
+
+ [Test]
+ public void ReadExactlyBadArguments()
+ {
+ using (Stream stream = new MemoryStream())
+ {
+ Assert.Throws<ArgumentNullException>(() => StreamUtility.ReadExactly(null, new byte[1], 0, 1));
+ Assert.Throws<ArgumentNullException>(() => stream.ReadExactly(null, 0, 1));
+ Assert.Throws<ArgumentOutOfRangeException>(() => stream.ReadExactly(new byte[1], -1, 1));
+ Assert.Throws<ArgumentOutOfRangeException>(() => stream.ReadExactly(new byte[1], 1, 1));
+ Assert.Throws<ArgumentOutOfRangeException>(() => stream.ReadExactly(new byte[1], 0, -1));
+ Assert.Throws<ArgumentOutOfRangeException>(() => stream.ReadExactly(new byte[1], 0, 2));
+ Assert.Throws<ArgumentOutOfRangeException>(() => stream.ReadExactly(new byte[1], 1, 1));
+ Assert.Throws<ArgumentOutOfRangeException>(() => stream.ReadExactly(-1));
+ }
+ }
+
+ [Test]
+ public void ReadExactlyZeroBytes()
+ {
+ using (Stream stream = new MemoryStream())
+ {
+ Assert.IsNotNull(stream.ReadExactly(0));
+ Assert.AreEqual(0, stream.ReadExactly(0).Length);
+ }
+ }
+
+ [Test]
+ public void ReadExactly()
+ {
+ byte[] abySource = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+
+ using (Stream streamSource = new MemoryStream(abySource))
+ using (Stream stream = new SlowStream(streamSource))
+ {
+ byte[] read = stream.ReadExactly(5);
+ CollectionAssert.AreEqual(abySource.Take(5), read);
+
+ read = stream.ReadExactly(6);
+ CollectionAssert.AreEqual(abySource.Skip(5), read);
+
+ Assert.Throws<EndOfStreamException>(() => stream.ReadExactly(1));
+ }
+
+ using (Stream streamSource = new MemoryStream(abySource))
+ using (Stream stream = new SlowStream(streamSource))
+ {
+ byte[] read = stream.ReadExactly(11);
+ CollectionAssert.AreEqual(abySource, read);
+ }
+
+ using (Stream streamSource = new MemoryStream(abySource))
+ using (Stream stream = new SlowStream(streamSource))
+ {
+ Assert.Throws<EndOfStreamException>(() => stream.ReadExactly(12));
+ }
+ }
+
+ [Test]
+ public void ReadExactlyArray()
+ {
+ byte[] abySource = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+
+ using (Stream streamSource = new MemoryStream(abySource))
+ using (Stream stream = new SlowStream(streamSource))
+ {
+ byte[] buffer = new byte[20];
+ stream.ReadExactly(buffer, 3, 8);
+ CollectionAssert.AreEqual(new byte[3].Concat(abySource.Take(8)).Concat(new byte[9]), buffer);
+ }
+
+ using (Stream streamSource = new MemoryStream(abySource))
+ using (Stream stream = new SlowStream(streamSource))
+ {
+ byte[] buffer = new byte[20];
+ stream.ReadExactly(buffer, 5, 11);
+ CollectionAssert.AreEqual(new byte[5].Concat(abySource).Concat(new byte[4]), buffer);
+ }
+
+ using (Stream streamSource = new MemoryStream(abySource))
+ using (Stream stream = new SlowStream(streamSource))
+ {
+ byte[] buffer = new byte[20];
+ Assert.Throws<EndOfStreamException>(() => stream.ReadExactly(buffer, 5, 12));
+ }
+ }
+
+ private class SlowStream : WrappingStream
+ {
+ public SlowStream(Stream stream)
+ : base(stream, Ownership.None)
+ {
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ return base.Read(buffer, offset, Math.Min(count, 2));
+ }
+ }
}
}

0 comments on commit 1c57924

Please sign in to comment.