Skip to content

Commit 1e056a8

Browse files
authored
Fix MemoryStream.CanWrite to allow read-only (#67)
1 parent f4db059 commit 1e056a8

File tree

2 files changed

+54
-8
lines changed

2 files changed

+54
-8
lines changed

System.IO.Streams/MemoryStream.cs

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ public class MemoryStream : Stream
2727
private readonly bool _expandable;
2828
// Is this stream open or closed?
2929
private bool _isOpen;
30+
// Is the stream writable
31+
private bool _isWritable;
3032

3133
private const int MemStreamMaxLength = 0xFFFF;
3234

@@ -50,6 +52,7 @@ public MemoryStream()
5052
// Must be 0 for byte[]'s created by MemoryStream
5153
_origin = 0;
5254
_isOpen = true;
55+
_isWritable = true;
5356
}
5457

5558
/// <summary>
@@ -61,18 +64,24 @@ public MemoryStream()
6164
/// <para>
6265
/// The <see cref="CanRead"/>, <see cref="CanSeek"/>, and <see cref="CanWrite"/> properties are all set to <see langword="true"/>.
6366
/// </para>
64-
/// <para>
65-
/// The capacity of the current stream automatically increases when you use the <see cref="SetLength"/> method to set the length to a value larger than the capacity of the current stream.
66-
/// </para>
6767
/// </remarks>
68-
public MemoryStream(byte[] buffer)
68+
public MemoryStream(byte[] buffer) : this(buffer, true) { }
69+
70+
/// <summary>
71+
/// Initializes a new non-resizable instance of the <see cref="MemoryStream"/> class based on the specified byte array with the <see cref="CanWrite"/> property set as specified.
72+
/// </summary>
73+
/// <param name="buffer">The array of unsigned bytes from which to create the current stream.</param>
74+
/// <param name="isWritable">A bool indicating whether the stream should be writable</param>
75+
/// <exception cref="ArgumentNullException"><paramref name="buffer"/> is <see langword="null"/>.</exception>
76+
public MemoryStream(byte[] buffer, bool isWritable)
6977
{
7078
_buffer = buffer ?? throw new ArgumentNullException();
7179

7280
_length = _capacity = buffer.Length;
7381
_expandable = false;
7482
_origin = 0;
7583
_isOpen = true;
84+
_isWritable = isWritable;
7685
}
7786

7887
/// <summary>
@@ -115,7 +124,7 @@ public MemoryStream(byte[] buffer)
115124
/// If the stream is closed, this property returns <see langword="false"/>.
116125
/// </para>
117126
/// </remarks>
118-
public override bool CanWrite => _isOpen;
127+
public override bool CanWrite => _isWritable;
119128

120129
/// <inheritdoc/>
121130
protected override void Dispose(bool disposing)
@@ -329,11 +338,13 @@ public override long Seek(
329338

330339
/// <inheritdoc/>
331340
/// <exception cref="ObjectDisposedException"></exception>
332-
/// <exception cref="NotSupportedException">The stream buffer does not have the capacity to hold the data and is not expandable.</exception>
341+
/// <exception cref="NotSupportedException">The stream buffer does not have the capacity to hold the
342+
/// data and is not expandable, and/or the stream is not writable.</exception>
333343
/// <exception cref="ArgumentOutOfRangeException">The MemoryStream max size was exceeded</exception>
334344
public override void SetLength(long value)
335345
{
336346
EnsureOpen();
347+
EnsureWritable();
337348

338349
if (value > MemStreamMaxLength || value < 0)
339350
{
@@ -368,14 +379,16 @@ public virtual byte[] ToArray()
368379

369380
/// <inheritdoc/>
370381
/// <exception cref="ObjectDisposedException"></exception>
371-
/// <exception cref="NotSupportedException">The stream buffer does not have the capacity to hold the data and is not expandable.</exception>
382+
/// <exception cref="NotSupportedException">The stream buffer does not have the capacity to hold the
383+
/// data and is not expandable, and/or the stream is not writable.</exception>
372384
/// <exception cref="ArgumentOutOfRangeException">
373385
/// The MemoryStream max size was exceeded or
374386
/// <paramref name="offset"/> or <paramref name="count"/> are less than 0
375387
/// </exception>
376388
public override void Write(byte[] buffer, int offset, int count)
377389
{
378390
EnsureOpen();
391+
EnsureWritable();
379392

380393
if (buffer == null)
381394
{
@@ -411,11 +424,13 @@ public override void Write(byte[] buffer, int offset, int count)
411424

412425
/// <inheritdoc/>
413426
/// <exception cref="ObjectDisposedException"></exception>
414-
/// <exception cref="NotSupportedException">The stream buffer does not have the capacity to hold the data and is not expandable.</exception>
427+
/// <exception cref="NotSupportedException">The stream buffer does not have the capacity to hold the
428+
/// data and is not expandable, and/or the stream is not writable.</exception>
415429
/// <exception cref="ArgumentOutOfRangeException">The MemoryStream max size was exceeded</exception>
416430
public override void WriteByte(byte value)
417431
{
418432
EnsureOpen();
433+
EnsureWritable();
419434

420435
if (_position >= _capacity)
421436
{
@@ -459,6 +474,18 @@ private void EnsureOpen()
459474
}
460475
}
461476

477+
/// <summary>
478+
/// Check that stream is writable.
479+
/// </summary>
480+
/// <exception cref="NotSupportedException"></exception>
481+
private void EnsureWritable()
482+
{
483+
if (!CanWrite)
484+
{
485+
throw new NotSupportedException();
486+
}
487+
}
488+
462489
/// <summary>
463490
/// Verifies that there is enough capacity in the stream.
464491
/// </summary>

UnitTests/MemoryStreamUnitTests/CanWrite.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,24 @@ public void CanWrite_Byte_Ctor()
5151
OutputHelper.WriteLine($"Unexpected exception {ex}");
5252
}
5353
}
54+
55+
[TestMethod]
56+
[DataRow(true)]
57+
[DataRow(false)]
58+
public void CanWrite_bool_isWritable_Ctor(bool isWritable)
59+
{
60+
// Arrange
61+
byte[] buffer = new byte[1024];
62+
63+
// Act
64+
using MemoryStream fs = new MemoryStream(buffer, isWritable);
65+
66+
// Assert
67+
Assert.AreEqual(isWritable, fs.CanWrite, $"Expected CanWrite == {isWritable}, but got CanWrite == {!isWritable}");
68+
if(!isWritable)
69+
{
70+
Assert.ThrowsException(typeof(NotSupportedException), () => fs.WriteByte(0), "Expected exception when attempt to write to a read only stream.");
71+
}
72+
}
5473
}
5574
}

0 commit comments

Comments
 (0)