diff --git a/Settings.StyleCop b/Settings.StyleCop index 30c78e19f..b4cc1655f 100644 --- a/Settings.StyleCop +++ b/Settings.StyleCop @@ -38,6 +38,7 @@ bools desensitivity premultiplied + endianness diff --git a/src/ImageSharp/IO/EndianBinaryReader.cs b/src/ImageSharp/IO/EndianBinaryReader.cs index 105647675..c0753e765 100644 --- a/src/ImageSharp/IO/EndianBinaryReader.cs +++ b/src/ImageSharp/IO/EndianBinaryReader.cs @@ -10,9 +10,8 @@ namespace ImageSharp.IO using System.Text; /// - /// Equivalent of , but with either endianness, depending on - /// the EndianBitConverter it is constructed with. No data is buffered in the - /// reader; the client may seek within the stream at will. + /// Equivalent of , but with either endianness, depending on the it is constructed with. + /// No data is buffered in the reader; the client may seek within the stream at will. /// internal class EndianBinaryReader : IDisposable { @@ -24,7 +23,7 @@ namespace ImageSharp.IO /// /// Buffer used for temporary storage before conversion into primitives /// - private readonly byte[] buffer = new byte[16]; + private readonly byte[] storageBuffer = new byte[16]; /// /// Buffer used for temporary storage when reading a single character @@ -67,26 +66,10 @@ namespace ImageSharp.IO /// Encoding to use when reading character data public EndianBinaryReader(EndianBitConverter bitConverter, Stream stream, Encoding encoding) { - // TODO: Use Guard - if (bitConverter == null) - { - throw new ArgumentNullException("bitConverter"); - } - - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - if (encoding == null) - { - throw new ArgumentNullException("encoding"); - } - - if (!stream.CanRead) - { - throw new ArgumentException("Stream isn't writable", "stream"); - } + Guard.NotNull(bitConverter, nameof(bitConverter)); + Guard.NotNull(stream, nameof(stream)); + Guard.NotNull(encoding, nameof(encoding)); + Guard.IsTrue(stream.CanRead, nameof(stream), "Stream isn't readable."); this.BaseStream = stream; this.BitConverter = bitConverter; @@ -140,8 +123,8 @@ namespace ImageSharp.IO /// The byte read public byte ReadByte() { - this.ReadInternal(this.buffer, 1); - return this.buffer[0]; + this.ReadInternal(this.storageBuffer, 1); + return this.storageBuffer[0]; } /// @@ -150,8 +133,8 @@ namespace ImageSharp.IO /// The byte read public sbyte ReadSByte() { - this.ReadInternal(this.buffer, 1); - return unchecked((sbyte)this.buffer[0]); + this.ReadInternal(this.storageBuffer, 1); + return unchecked((sbyte)this.storageBuffer[0]); } /// @@ -160,8 +143,8 @@ namespace ImageSharp.IO /// The boolean read public bool ReadBoolean() { - this.ReadInternal(this.buffer, 1); - return this.BitConverter.ToBoolean(this.buffer, 0); + this.ReadInternal(this.storageBuffer, 1); + return this.BitConverter.ToBoolean(this.storageBuffer, 0); } /// @@ -171,8 +154,8 @@ namespace ImageSharp.IO /// The 16-bit integer read public short ReadInt16() { - this.ReadInternal(this.buffer, 2); - return this.BitConverter.ToInt16(this.buffer, 0); + this.ReadInternal(this.storageBuffer, 2); + return this.BitConverter.ToInt16(this.storageBuffer, 0); } /// @@ -182,8 +165,8 @@ namespace ImageSharp.IO /// The 32-bit integer read public int ReadInt32() { - this.ReadInternal(this.buffer, 4); - return this.BitConverter.ToInt32(this.buffer, 0); + this.ReadInternal(this.storageBuffer, 4); + return this.BitConverter.ToInt32(this.storageBuffer, 0); } /// @@ -193,8 +176,8 @@ namespace ImageSharp.IO /// The 64-bit integer read public long ReadInt64() { - this.ReadInternal(this.buffer, 8); - return this.BitConverter.ToInt64(this.buffer, 0); + this.ReadInternal(this.storageBuffer, 8); + return this.BitConverter.ToInt64(this.storageBuffer, 0); } /// @@ -204,8 +187,8 @@ namespace ImageSharp.IO /// The 16-bit unsigned integer read public ushort ReadUInt16() { - this.ReadInternal(this.buffer, 2); - return this.BitConverter.ToUInt16(this.buffer, 0); + this.ReadInternal(this.storageBuffer, 2); + return this.BitConverter.ToUInt16(this.storageBuffer, 0); } /// @@ -215,8 +198,8 @@ namespace ImageSharp.IO /// The 32-bit unsigned integer read public uint ReadUInt32() { - this.ReadInternal(this.buffer, 4); - return this.BitConverter.ToUInt32(this.buffer, 0); + this.ReadInternal(this.storageBuffer, 4); + return this.BitConverter.ToUInt32(this.storageBuffer, 0); } /// @@ -226,8 +209,8 @@ namespace ImageSharp.IO /// The 64-bit unsigned integer read public ulong ReadUInt64() { - this.ReadInternal(this.buffer, 8); - return this.BitConverter.ToUInt64(this.buffer, 0); + this.ReadInternal(this.storageBuffer, 8); + return this.BitConverter.ToUInt64(this.storageBuffer, 0); } /// @@ -237,8 +220,8 @@ namespace ImageSharp.IO /// The floating point value read public float ReadSingle() { - this.ReadInternal(this.buffer, 4); - return this.BitConverter.ToSingle(this.buffer, 0); + this.ReadInternal(this.storageBuffer, 4); + return this.BitConverter.ToSingle(this.storageBuffer, 0); } /// @@ -248,8 +231,8 @@ namespace ImageSharp.IO /// The floating point value read public double ReadDouble() { - this.ReadInternal(this.buffer, 8); - return this.BitConverter.ToDouble(this.buffer, 0); + this.ReadInternal(this.storageBuffer, 8); + return this.BitConverter.ToDouble(this.storageBuffer, 0); } /// @@ -259,8 +242,8 @@ namespace ImageSharp.IO /// The decimal value read public decimal ReadDecimal() { - this.ReadInternal(this.buffer, 16); - return this.BitConverter.ToDecimal(this.buffer, 0); + this.ReadInternal(this.storageBuffer, 16); + return this.BitConverter.ToDecimal(this.storageBuffer, 0); } /// @@ -296,33 +279,17 @@ namespace ImageSharp.IO { this.CheckDisposed(); - // TODO: Use Guard - if (this.buffer == null) - { - throw new ArgumentNullException("buffer"); - } - - if (index < 0) - { - throw new ArgumentOutOfRangeException("index"); - } - - if (count < 0) - { - throw new ArgumentOutOfRangeException("index"); - } - - if (count + index > data.Length) - { - throw new ArgumentException("Not enough space in buffer for specified number of characters starting at specified index"); - } + Guard.NotNull(this.storageBuffer, nameof(this.storageBuffer)); + Guard.MustBeGreaterThanOrEqualTo(index, 0, nameof(index)); + Guard.MustBeGreaterThanOrEqualTo(count, 0, nameof(count)); + Guard.IsFalse(count + index > data.Length, nameof(data.Length), "Not enough space in buffer for specified number of characters starting at specified index."); int read = 0; bool firstTime = true; // Use the normal buffer if we're only reading a small amount, otherwise // use at most 4K at a time. - byte[] byteBuffer = this.buffer; + byte[] byteBuffer = this.storageBuffer; if (byteBuffer.Length < count * this.minBytesPerChar) { @@ -339,11 +306,10 @@ namespace ImageSharp.IO amountToRead = count * this.minBytesPerChar; firstTime = false; } - - // After that we can only assume we need to fully read 'chars left -1' characters - // and a single byte of the character we may be in the middle of else { + // After that we can only assume we need to fully read 'chars left -1' characters + // and a single byte of the character we may be in the middle of amountToRead = ((count - read - 1) * this.minBytesPerChar) + 1; } @@ -379,25 +345,11 @@ namespace ImageSharp.IO public int Read(byte[] buffer, int index, int count) { this.CheckDisposed(); - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if (index < 0) - { - throw new ArgumentOutOfRangeException("index"); - } - - if (count < 0) - { - throw new ArgumentOutOfRangeException("index"); - } - - if (count + index > buffer.Length) - { - throw new ArgumentException("Not enough space in buffer for specified number of bytes starting at specified index"); - } + Guard.NotNull(this.storageBuffer, nameof(this.storageBuffer)); + Guard.MustBeGreaterThanOrEqualTo(index, 0, nameof(index)); + Guard.MustBeGreaterThanOrEqualTo(count, 0, nameof(count)); + Guard.IsFalse(count + index > buffer.Length, nameof(buffer.Length), "Not enough space in buffer for specified number of bytes starting at specified index."); int read = 0; while (count > 0) @@ -426,10 +378,7 @@ namespace ImageSharp.IO public byte[] ReadBytes(int count) { this.CheckDisposed(); - if (count < 0) - { - throw new ArgumentOutOfRangeException("count"); - } + Guard.MustBeGreaterThanOrEqualTo(count, 0, nameof(count)); byte[] ret = new byte[count]; int index = 0; @@ -581,11 +530,7 @@ namespace ImageSharp.IO int read = this.BaseStream.Read(data, index, size - index); if (read == 0) { - throw new EndOfStreamException( - string.Format( - "End of stream reached with {0} byte{1} left to read.", - size - index, - size - index == 1 ? "s" : string.Empty)); + throw new EndOfStreamException($"End of stream reached with {size - index} byte{(size - index == 1 ? "s" : string.Empty)} left to read."); } index += read; @@ -618,4 +563,4 @@ namespace ImageSharp.IO return index; } } -} +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/IO/EndianBinaryReaderTests.cs b/tests/ImageSharp.Tests/IO/EndianBinaryReaderTests.cs new file mode 100644 index 000000000..3eed88c87 --- /dev/null +++ b/tests/ImageSharp.Tests/IO/EndianBinaryReaderTests.cs @@ -0,0 +1,66 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.IO +{ + using System; + using System.IO; + using System.Text; + + using ImageSharp.IO; + + using Xunit; + + /// + /// The endian binary reader tests. + /// + public class EndianBinaryReaderTests + { + /// + /// The test string. + /// + private const string TestString = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmopqrstuvwxyz"; + + /// + /// The test bytes. + /// + private static readonly byte[] TestBytes = Encoding.ASCII.GetBytes(TestString); + + /// + /// Tests to ensure that the reader can read beyond internal buffer size. + /// + [Fact] + public void ReadCharsBeyondInternalBufferSize() + { + MemoryStream stream = new MemoryStream(TestBytes); + using (EndianBinaryReader subject = new EndianBinaryReader(EndianBitConverter.Little, stream)) + { + char[] chars = new char[TestString.Length]; + subject.Read(chars, 0, chars.Length); + Assert.Equal(TestString, new string(chars)); + } + } + + /// + /// Tests to ensure that the reader cannot read beyond the provided buffer size. + /// + [Fact] + public void ReadCharsBeyondProvidedBufferSize() + { + Assert.Throws( + typeof(ArgumentException), + () => + { + MemoryStream stream = new MemoryStream(TestBytes); + using (EndianBinaryReader subject = new EndianBinaryReader(EndianBitConverter.Little, stream)) + { + char[] chars = new char[TestString.Length - 1]; + + subject.Read(chars, 0, TestString.Length); + } + }); + } + } +}