diff --git a/System.IO.Streams/MemoryStream.cs b/System.IO.Streams/MemoryStream.cs index 7ad45dd..1bddaa5 100644 --- a/System.IO.Streams/MemoryStream.cs +++ b/System.IO.Streams/MemoryStream.cs @@ -72,8 +72,9 @@ public MemoryStream(byte[] buffer) : this(buffer, true) { } /// is . public MemoryStream(byte[] buffer, bool isWritable) { - _buffer = buffer ?? throw new ArgumentNullException(); + ArgumentNullException.ThrowIfNull(buffer); + _buffer = buffer; _length = _capacity = buffer.Length; _expandable = false; _origin = 0; @@ -181,7 +182,7 @@ public override long Position { EnsureOpen(); - if (value < 0 || value > MemStreamMaxLength) + if (value is < 0 or > MemStreamMaxLength) { throw new ArgumentOutOfRangeException(); } @@ -196,7 +197,7 @@ public override int Read(Span buffer) { EnsureOpen(); - var bytesToRead = _length - _position; + int bytesToRead = _length - _position; if (bytesToRead > buffer.Length) { @@ -209,7 +210,6 @@ public override int Read(Span buffer) } new Span(_buffer, _position, bytesToRead).CopyTo(buffer); - _position += bytesToRead; return bytesToRead; @@ -384,10 +384,7 @@ public override void Write(byte[] buffer, int offset, int count) EnsureOpen(); EnsureWritable(); - if (buffer == null) - { - throw new ArgumentNullException(); - } + ArgumentNullException.ThrowIfNull(buffer); if (offset < 0 || count < 0) { @@ -399,44 +396,59 @@ public override void Write(byte[] buffer, int offset, int count) throw new ArgumentException(); } - int i = _position + count; + int newPosition = _position + count; // check for overflow - if (i > _length) + if (newPosition > _length) { - if (i > _capacity) + if (newPosition > _capacity) { - EnsureCapacity(i); + EnsureCapacity(newPosition); } - _length = i; + _length = newPosition; } - Array.Copy(buffer, offset, _buffer, _position, count); - _position = i; + new Span(buffer, offset, count).CopyTo(new Span(_buffer, _position, count)); + _position = newPosition; } - /// - /// + /// + /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. + /// + /// A region of memory. This method copies the contents of this region to the current stream. + /// The current stream instance is closed. /// The stream buffer does not have the capacity to hold the /// data and is not expandable, and/or the stream is not writable. - /// The MemoryStream max size was exceeded - public override void WriteByte(byte value) + /// The MemoryStream max size was exceeded. + /// + /// Use the property to determine whether the current instance supports writing. + /// If the write operation is successful, the position within the stream advances by the number of bytes written. + /// If an exception occurs, the position within the stream remains unchanged. + /// + public override void Write(ReadOnlySpan buffer) { EnsureOpen(); EnsureWritable(); - if (_position >= _capacity) + int count = buffer.Length; + int newPosition = _position + count; + + // check for overflow + if (newPosition > _length) { - EnsureCapacity(_position + 1); + if (newPosition > _capacity) + { + EnsureCapacity(newPosition); + } + + _length = newPosition; } - _buffer[_position++] = value; + // Copy ReadOnlySpan to buffer + buffer.CopyTo(new Span(_buffer, _position, count)); - if (_position > _length) - { - _length = _position; - } + _position = newPosition; } /// diff --git a/System.IO.Streams/Properties/AssemblyInfo.cs b/System.IO.Streams/Properties/AssemblyInfo.cs index 3a72384..d57d8cf 100644 --- a/System.IO.Streams/Properties/AssemblyInfo.cs +++ b/System.IO.Streams/Properties/AssemblyInfo.cs @@ -1,5 +1,8 @@ -using System.Reflection; -using System.Runtime.CompilerServices; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -15,3 +18,6 @@ // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] +// Mark the assembly as CLS compliant +[assembly: CLSCompliant(true)] + diff --git a/System.IO.Streams/SeekOrigin.cs b/System.IO.Streams/SeekOrigin.cs index fe2bb79..2f6e2c7 100644 --- a/System.IO.Streams/SeekOrigin.cs +++ b/System.IO.Streams/SeekOrigin.cs @@ -1,31 +1,44 @@ -// -// Copyright (c) .NET Foundation and Contributors -// Portions Copyright (c) Microsoft Corporation. All rights reserved. -// See LICENSE file in the project root for full license information. -// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. namespace System.IO { /// /// Specifies the position in a stream to use for seeking. /// - /// These constants match Win32's FILE_BEGIN, FILE_CURRENT, and FILE_END + /// + /// + /// is used by the Seek methods of , , and other streams. + /// + /// + /// These constants match Win32's FILE_BEGIN, FILE_CURRENT, and FILE_END values. + /// + /// [Serializable] public enum SeekOrigin { /// /// Specifies the beginning of a stream. /// + /// + /// Seeking to the beginning of a stream sets the position to zero. + /// Begin = 0, /// /// Specifies the current position within a stream. /// + /// + /// Seeking relative to the current position allows you to move forward or backward from the current position by specifying a positive or negative offset. + /// Current = 1, /// /// Specifies the end of a stream. /// + /// + /// Seeking relative to the end of a stream allows you to position at or before the end by specifying a zero or negative offset. + /// End = 2, } } diff --git a/System.IO.Streams/Stream.cs b/System.IO.Streams/Stream.cs index 1eb342f..d47dcd4 100644 --- a/System.IO.Streams/Stream.cs +++ b/System.IO.Streams/Stream.cs @@ -9,46 +9,38 @@ namespace System.IO [Serializable] public abstract class Stream : MarshalByRefObject, IDisposable { - // this value has been trimmed down from the original value that is used in full framework: 81920. + // this value has been trimmed down from the original value that is used in full framework:81920. private const int _CopyToBufferSize = 2048; /// /// When overridden in a derived class, gets a value indicating whether the current stream supports reading. /// - /// - /// true if the stream supports reading; otherwise, false. - /// + /// if the stream supports reading; otherwise, . public abstract bool CanRead { get; } /// /// When overridden in a derived class, gets a value indicating whether the current stream supports seeking. /// - /// - /// true if the stream supports seeking; otherwise, false. - /// + /// if the stream supports seeking; otherwise, . public abstract bool CanSeek { get; } /// /// Gets a value that determines whether the current stream can time out. /// - /// - /// A value that determines whether the current stream can time out. - /// + /// if the current stream can time out; otherwise, . public virtual bool CanTimeout => false; /// /// When overridden in a derived class, gets a value indicating whether the current stream supports writing. /// - /// - /// true if the stream supports writing; otherwise, false. - /// + /// if the stream supports writing; otherwise, . public abstract bool CanWrite { get; } /// /// When overridden in a derived class, gets the length in bytes of the stream. /// /// - /// A long value representing the length of the stream in bytes. + /// A value representing the length of the stream in bytes. /// public abstract long Length { get; } @@ -66,7 +58,7 @@ public abstract class Stream : MarshalByRefObject, IDisposable /// /// A value, in milliseconds, that determines how long the stream will attempt to read before timing out. /// - /// + /// Always thrown. Timeouts are not supported on this stream. public virtual int ReadTimeout { get @@ -86,7 +78,7 @@ public virtual int ReadTimeout /// /// A value, in milliseconds, that determines how long the stream will attempt to write before timing out. /// - /// + /// Always thrown. Timeouts are not supported on this stream. public virtual int WriteTimeout { get @@ -101,18 +93,15 @@ public virtual int WriteTimeout } /// - /// Closes the current stream and releases any resources (such as sockets and file handles) associated with the current stream. + /// Closes the current stream and releases any resources (such as sockets and file handles) associated with the current stream. /// Instead of calling this method, ensure that the stream is properly disposed. /// /// - /// Stream used to require that all cleanup logic went into Close(), - /// which was thought up before we invented IDisposable. However, we - /// need to follow the IDisposable pattern so that users can write - /// sensible subclasses without needing to inspect all their base - /// classes, and without worrying about version brittleness, from a - /// base class switching to the Dispose pattern. We're moving - /// Stream to the Dispose(bool) pattern - that's where all subclasses - /// should put their cleanup starting in V2. + /// Stream used to require that all cleanup logic went into , + /// which was thought up before existed. However, the + /// pattern should be followed so that users can write + /// sensible subclasses without inspecting all their base classes and without + /// worrying about version brittleness. /// public virtual void Close() { @@ -126,46 +115,25 @@ public virtual void Close() /// The stream to which the contents of the current stream will be copied. /// is . /// - /// /// The current stream does not support reading. - /// - /// /// -or- - /// - /// /// does not support writing. - /// /// - /// Either the current stream or were closed before the method was called. + /// Either the current stream or was closed before the method was called. /// An I/O error occurred. /// /// Copying begins at the current position in the current stream, and does not reset the position of the destination stream after the copy operation is complete. /// public void CopyTo(Stream destination) { - if (destination == null) - { -#pragma warning disable S3928 // Parameter names used into ArgumentException constructors should match an existing one - throw new ArgumentNullException(); -#pragma warning restore S3928 // Parameter names used into ArgumentException constructors should match an existing one - } - - if (!CanRead && !CanWrite) - { - throw new ObjectDisposedException(); - } + ArgumentNullException.ThrowIfNull(destination); - if (!destination.CanRead && !destination.CanWrite) + if (!CanRead && !CanWrite || !destination.CanRead && !destination.CanWrite) { throw new ObjectDisposedException(); } - if (!CanRead) - { - throw new NotSupportedException(); - } - - if (!destination.CanWrite) + if (!CanRead || !destination.CanWrite) { throw new NotSupportedException(); } @@ -194,7 +162,7 @@ public void Dispose() } /// - /// + /// Finalizes the current instance of the class. /// ~Stream() { @@ -202,9 +170,9 @@ public void Dispose() } /// - /// Releases the unmanaged resources used by the Stream and optionally releases the managed resources. + /// Releases the unmanaged resources used by the and optionally releases the managed resources. /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + /// to release both managed and unmanaged resources; to release only unmanaged resources. protected virtual void Dispose(bool disposing) { } @@ -217,8 +185,8 @@ protected virtual void Dispose(bool disposing) /// /// When overridden in a derived class, sets the position within the current stream. /// - /// A byte offset relative to the origin parameter. - /// A value of type SeekOrigin indicating the reference point used to obtain the new position. + /// A byte offset relative to the parameter. + /// A value of type indicating the reference point used to obtain the new position. /// The new position within the current stream. public abstract long Seek( long offset, @@ -229,21 +197,27 @@ public abstract long Seek( /// /// The desired length of the current stream in bytes. public abstract void SetLength(long value); - + /// /// When overridden in a derived class, reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. /// /// A region of memory. When this method returns, the contents of this region are replaced by the bytes read from the current source. - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. + /// + /// The total number of bytes read into the . This can be less than the number of bytes requested if that many bytes are not currently available, + /// or zero (0) if the end of the stream has been reached. + /// public abstract int Read(Span buffer); /// /// When overridden in a derived class, reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. /// - /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source. - /// The zero-based byte offset in buffer at which to begin storing the data read from the current stream. + /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between and ( + -1) replaced by the bytes read from the current source. + /// The zero-based byte offset in at which to begin storing the data read from the current stream. /// The maximum number of bytes to be read from the current stream. - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. + /// + /// The total number of bytes read into the . This can be less than the number of bytes requested if that many bytes are not currently available, + /// or zero (0) if the end of the stream has been reached. + /// public abstract int Read( byte[] buffer, int offset, @@ -252,28 +226,38 @@ public abstract int Read( /// /// 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. /// - /// The unsigned byte cast to an Int32, or -1 if at the end of the stream. + /// The unsigned byte cast to an , or -1 if at the end of the stream. public virtual int ReadByte() { Span oneByteSpan = stackalloc byte[1]; - var r = Read(oneByteSpan); - if (r == 0) return -1; + int r = Read(oneByteSpan); - return oneByteSpan[0]; + return r == 0 ? -1 : oneByteSpan[0]; } /// /// When overridden in a derived class, writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. /// - /// An array of bytes. This method copies count bytes from buffer to the current stream. - /// The zero-based byte offset in buffer at which to begin copying bytes to the current stream. + /// An array of bytes. This method copies bytes from to the current stream. + /// The zero-based byte offset in at which to begin copying bytes to the current stream. /// The number of bytes to be written to the current stream. public abstract void Write( byte[] buffer, int offset, int count); + /// + /// When overridden in a derived class, writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. + /// + /// A region of memory. This method copies the contents of this region to the current stream. + /// + /// Use the property to determine whether the current instance supports writing. + /// If the write operation is successful, the position within the stream advances by the number of bytes written. + /// If an exception occurs, the position within the stream remains unchanged. + /// + public abstract void Write(ReadOnlySpan buffer); + /// /// Writes a byte to the current position in the stream and advances the position within the stream by one byte. /// @@ -281,8 +265,8 @@ public abstract void Write( public virtual void WriteByte(byte value) { var oneByteArray = new byte[1]; - oneByteArray[0] = value; + oneByteArray[0] = value; Write(oneByteArray, 0, 1); - } + } } } diff --git a/System.IO.Streams/StreamReader.cs b/System.IO.Streams/StreamReader.cs index 8015b31..2b48ca7 100644 --- a/System.IO.Streams/StreamReader.cs +++ b/System.IO.Streams/StreamReader.cs @@ -1,8 +1,5 @@ -// -// Copyright (c) .NET Foundation and Contributors -// Portions Copyright (c) Microsoft Corporation. All rights reserved. -// See LICENSE file in the project root for full license information. -// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using System.Collections; using System.Text; @@ -39,7 +36,12 @@ public class StreamReader : TextReader /// /// The underlying stream. /// - /// You use this property to access the underlying stream. The StreamReader class buffers input from the underlying stream when you call one of the Read methods. If you manipulate the position of the underlying stream after reading data into the buffer, the position of the underlying stream might not match the position of the internal buffer. To reset the internal buffer, call the DiscardBufferedData method; however, this method slows performance and should be called only when absolutely necessary. The StreamReader constructors that have the detectEncodingFromByteOrderMarks parameter can change the encoding the first time you read from the StreamReader object. + /// + /// You use this property to access the underlying stream. The class buffers input from the underlying stream when you call one of the Read methods. + /// + /// + /// If you manipulate the position of the underlying stream after reading data into the buffer, the position of the underlying stream might not match the position of the internal buffer. To reset the internal buffer, call the DiscardBufferedData method; however, this method slows performance and should be called only when absolutely necessary. + /// /// public virtual Stream BaseStream { get; private set; } @@ -47,37 +49,26 @@ public class StreamReader : TextReader /// Gets the current character encoding that the current object is using. /// /// The current character encoding used by the current reader. The value can be different after the first call to any method of , since encoding autodetection is not done until the first call to a method. - public virtual Encoding CurrentEncoding => System.Text.Encoding.UTF8; + public virtual Encoding CurrentEncoding => Encoding.UTF8; /// /// Gets a value that indicates whether the current stream position is at the end of the stream. /// /// if the current stream position is at the end of the stream; otherwise . - public bool EndOfStream - { - get - { - return _curBufLen == _curBufPos; - } - } - internal bool LeaveOpen - { - get { return !_closable; } - } + public bool EndOfStream => _curBufLen == _curBufPos; + + internal bool LeaveOpen => !_closable; /// /// Initializes a new instance of the class for the specified stream and optionally leaves the stream open. /// /// The stream to be read. - /// to leave the stream open after the object is disposed; otherwise, . + /// to leave the stream open after the object is disposed; otherwise, . /// is . /// does not support reading. public StreamReader(Stream stream, bool leaveOpen = false) { - if (stream == null) - { - throw new ArgumentNullException(); - } + ArgumentNullException.ThrowIfNull(stream); if (!stream.CanRead) { @@ -115,7 +106,7 @@ public override void Close() /// to release both managed and unmanaged resources; to release only unmanaged resources. /// /// This method is called by the public method and the Finalize method. Dispose invokes the protected method with the disposing parameter set to . Finalize invokes with disposing set to . - /// When the disposing parameter is , this method releases all resources held by any managed objects that the StreamReader object references.This method invokes the method of each referenced object. + /// When the disposing parameter is , this method releases all resources held by any managed objects that the object references. This method invokes the method of each referenced object. /// protected override void Dispose(bool disposing) { @@ -172,7 +163,7 @@ public override int Peek() // retry read until response timeout expires while (BaseStream.Length > 0 && totRead < _buffer.Length) { - int len = (int)(_buffer.Length - totRead); + int len = _buffer.Length - totRead; if (len > BaseStream.Length) { @@ -212,12 +203,15 @@ public override int Peek() /// /// The next character from the input stream represented as an object, or -1 if no more characters are available. /// + /// /// This method overrides . - /// This method returns an integer so that it can return -1 if the end of the stream has been reached. If you manipulate the position of the underlying stream after reading data into the buffer, the position of the underlying stream might not match the position of the internal buffer.To reset the internal buffer, call the DiscardBufferedData method; however, this method slows performance and should be called only when absolutely necessary. + /// + /// + /// This method returns an integer so that it can return -1 if the end of the stream has been reached. If you manipulate the position of the underlying stream after reading data into the buffer, the position of the underlying stream might not match the position of the internal buffer. To reset the internal buffer, call the DiscardBufferedData method; however, this method slows performance and should be called only when absolutely necessary. + /// /// public override int Read() { - int byteUsed; while (true) { @@ -229,7 +223,7 @@ public override int Read() 0, 1, false, - out byteUsed, + out int byteUsed, out System.Int32 charUsed, out _); @@ -278,29 +272,29 @@ public override int Read() /// is . /// or is negative. /// The buffer length minus is less than . - /// An I/O error occurs, such as the stream is closed. + /// The stream is closed. /// - /// This method overrides TextReader.Read. + /// + /// This method overrides . + /// + /// /// This method returns an integer so that it can return 0 if the end of the stream has been reached. - /// When using the Read method, it is more efficient to use a buffer that is the same size as the internal buffer of the stream, where the internal buffer is set to your desired block size, and to always read less than the block size.If the size of the internal buffer was unspecified when the stream was constructed, its default size is 4 kilobytes(4096 bytes). If you manipulate the position of the underlying stream after reading data into the buffer, the position of the underlying stream might not match the position of the internal buffer.To reset the internal buffer, call the DiscardBufferedData method; however, this method slows performance and should be called only when absolutely necessary. - /// This method returns after either the number of characters specified by the count parameter are read, or the end of the file is reached. is a blocking version of . + /// + /// + /// When using the Read method, it is more efficient to use a buffer that is the same size as the internal buffer of the stream, where the internal buffer is set to your desired block size, and to always read less than the block size. If the size of the internal buffer was unspecified when the stream was constructed, its default size is 4 kilobytes (4096 bytes). If you manipulate the position of the underlying stream after reading data into the buffer, the position of the underlying stream might not match the position of the internal buffer. To reset the internal buffer, call the DiscardBufferedData method; however, this method slows performance and should be called only when absolutely necessary. + /// + /// + /// This method returns after either the number of characters specified by the parameter are read, or the end of the file is reached. is a blocking version of . + /// /// public override int Read( char[] buffer, int index, int count) { - if (buffer == null) - { - throw new ArgumentNullException(); - } - - if (index < 0) - { - throw new ArgumentOutOfRangeException(); - } + ArgumentNullException.ThrowIfNull(buffer); - if (count < 0) + if (index < 0 || count < 0) { throw new ArgumentOutOfRangeException(); } @@ -315,47 +309,48 @@ public override int Read( throw new ObjectDisposedException(); } - int byteUsed, charUsed = 0; - - if (_curBufLen == 0) - { - _ = FillBufferAndReset(count); - } - - int offset = 0; + return ReadHelper(new Span(buffer, index, count)); + } - while (true) + /// + /// Reads a specified maximum number of characters from the current stream into a character span. + /// + /// When this method returns, contains the characters read from the current source. If the total number of characters read is zero, the span remains unmodified. + /// + /// The number of characters that have been read, or 0 if at the end of the stream and no data was read. The number will be less than or equal to the length, depending on whether the data is available within the stream. + /// + /// The stream is closed. + /// + /// + /// This method reads at most .Length characters from the current stream and stores them in . + /// + /// + /// This method returns an integer so that it can return 0 if the end of the stream has been reached. + /// + /// + public override int Read(Span buffer) + { + if (_disposed) { - _decoder.Convert( - _buffer, - _curBufPos, - _curBufLen - _curBufPos, - buffer, - offset, - count, - false, - out byteUsed, - out charUsed, - out _); - - count -= charUsed; - _curBufPos += byteUsed; - offset += charUsed; - - if (count == 0 || (FillBufferAndReset(count) == 0)) - { - break; - } + throw new ObjectDisposedException(); } - return charUsed; + return ReadHelper(buffer); } /// /// Reads a line of characters from the current stream and returns the data as a string. /// /// The next line from the input stream, or if the end of the input stream is reached. - /// + /// There is insufficient memory to allocate a buffer for the returned string. + /// + /// + /// A line is defined as a sequence of characters followed by a carriage return (\r), a line feed (\n), or a carriage return immediately followed by a line feed (\r\n). The string that is returned does not contain the terminating carriage return or line feed. + /// + /// + /// This method overrides . + /// + /// public override string ReadLine() { int bufLen = c_BufferSize; @@ -421,15 +416,25 @@ public override string ReadLine() /// /// The rest of the stream as a string, from the current position to the end. If the current position is at the end of the stream, returns an empty string (""). /// - /// This method overrides TextReader.ReadToEnd. - /// ReadToEnd works best when you need to read all the input from the current position to the end of the stream.If more control is needed over how many characters are read from the stream, use the Read(Char[], Int32, Int32) method overload, which generally results in better performance. - /// ReadToEnd assumes that the stream knows when it has reached an end.For interactive protocols in which the server sends data only when you ask for it and does not close the connection, ReadToEnd might block indefinitely because it does not reach an end, and should be avoided. - /// Note that when using the Read method, it is more efficient to use a buffer that is the same size as the internal buffer of the stream.If the size of the buffer was unspecified when the stream was constructed, its default size is 4 kilobytes (4096 bytes). - /// If the current method throws an OutOfMemoryException, the reader's position in the underlying Stream object is advanced by the number of characters the method was able to read, but the characters already read into the internal ReadLine buffer are discarded. If you manipulate the position of the underlying stream after reading data into the buffer, the position of the underlying stream might not match the position of the internal buffer. To reset the internal buffer, call the DiscardBufferedData method; however, this method slows performance and should be called only when absolutely necessary. + /// + /// This method overrides . + /// + /// + /// ReadToEnd works best when you need to read all the input from the current position to the end of the stream. If more control is needed over how many characters are read from the stream, use the method overload, which generally results in better performance. + /// + /// + /// ReadToEnd assumes that the stream knows when it has reached an end. For interactive protocols in which the server sends data only when you ask for it and does not close the connection, ReadToEnd might block indefinitely because it does not reach an end, and should be avoided. + /// + /// + /// Note that when using the Read method, it is more efficient to use a buffer that is the same size as the internal buffer of the stream. If the size of the buffer was unspecified when the stream was constructed, its default size is 4 kilobytes (4096 bytes). + /// + /// + /// If the current method throws an , the reader's position in the underlying object is advanced by the number of characters the method was able to read, but the characters already read into the internal ReadLine buffer are discarded. If you manipulate the position of the underlying stream after reading data into the buffer, the position of the underlying stream might not match the position of the internal buffer. To reset the internal buffer, call the DiscardBufferedData method; however, this method slows performance and should be called only when absolutely necessary. + /// /// public override string ReadToEnd() { - char[] result = null; + char[] result; if (BaseStream.CanSeek) { @@ -445,9 +450,22 @@ public override string ReadToEnd() private char[] ReadSeekableStream() { - char[] chars = new char[(int)BaseStream.Length]; - - _ = Read(chars, 0, chars.Length); + // remaining bytes from current position to end of stream + int remainingBytes = (int)(BaseStream.Length - BaseStream.Position); + + // estimation of char count (may be less if multi-byte encoding) + // we'll allocate enough space and resize if needed + char[] chars = new char[remainingBytes]; + + int charsRead = Read(chars, 0, chars.Length); + + // if we read fewer chars than allocated (due to multi-byte encoding), create a properly sized array + if (charsRead < chars.Length) + { + char[] result = new char[charsRead]; + Array.Copy(chars, 0, result, 0, charsRead); + return result; + } return chars; } @@ -470,9 +488,11 @@ private char[] ReadNonSeekableStream() totalRead += read; - if (read < c_BufferSize) // we are done + // we are done + if (read < c_BufferSize) { - if (read > 0) // copy last scraps + // copy last scraps + if (read > 0) { char[] newChars = new char[read]; @@ -497,6 +517,7 @@ private char[] ReadNonSeekableStream() char[] text = new char[totalRead]; int len = 0; + for (int i = 0; i < buffers.Count; ++i) { char[] buffer = (char[])buffers[i]; @@ -516,7 +537,10 @@ private char[] ReadNonSeekableStream() private int FillBufferAndReset(int count) { - if (_curBufPos != 0) Reset(); + if (_curBufPos != 0) + { + Reset(); + } int totalRead = 0; @@ -526,7 +550,10 @@ private int FillBufferAndReset(int count) { int spaceLeft = _buffer.Length - _curBufLen; - if (count > spaceLeft) count = spaceLeft; + if (count > spaceLeft) + { + count = spaceLeft; + } int read = BaseStream.Read(_buffer, _curBufLen, count); @@ -546,12 +573,69 @@ private int FillBufferAndReset(int count) return totalRead; } + private int ReadHelper(Span buffer) + { + int count = buffer.Length; + + if (_curBufLen == 0) + { + _ = FillBufferAndReset(count); + } + + int offset = 0; + int totalCharsRead = 0; + + // Use temporary array for decoder since Decoder.Convert doesn't have a Span overload + char[] tempBuffer = new char[count]; + + while (true) + { + _decoder.Convert( + _buffer, + _curBufPos, + _curBufLen - _curBufPos, + tempBuffer, + 0, + count, + false, + out int byteUsed, + out int charUsed, + out _); + + // Copy decoded characters to the span + if (charUsed > 0) + { + new Span( + tempBuffer, + 0, + charUsed).CopyTo(buffer.Slice(offset, charUsed)); + } + + count -= charUsed; + _curBufPos += byteUsed; + offset += charUsed; + totalCharsRead += charUsed; + + if (count == 0 || (FillBufferAndReset(count) == 0)) + { + break; + } + } + + return totalCharsRead; + } + private void Reset() { int bytesAvailable = _curBufLen - _curBufPos; // here we trust that the copy in place doe not overwrites data - Array.Copy(_buffer, _curBufPos, _buffer, 0, bytesAvailable); + Array.Copy( + _buffer, + _curBufPos, + _buffer, + 0, + bytesAvailable); _curBufPos = 0; _curBufLen = bytesAvailable; diff --git a/System.IO.Streams/StreamWriter.cs b/System.IO.Streams/StreamWriter.cs index 3147678..17012ab 100644 --- a/System.IO.Streams/StreamWriter.cs +++ b/System.IO.Streams/StreamWriter.cs @@ -1,8 +1,5 @@ -// -// Copyright (c) .NET Foundation and Contributors -// Portions Copyright (c) Microsoft Corporation. All rights reserved. -// See LICENSE file in the project root for full license information. -// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using System.Text; @@ -49,7 +46,7 @@ public override string NewLine set { base.NewLine = value; - _newLineBytes = this.Encoding.GetBytes(value); + _newLineBytes = Encoding.GetBytes(value); } } @@ -62,10 +59,7 @@ public override string NewLine /// is not writable. public StreamWriter(Stream stream, bool leaveOpen = false) { - if (stream == null) - { - throw new ArgumentNullException(); - } + ArgumentNullException.ThrowIfNull(stream); if (!stream.CanWrite) { @@ -83,9 +77,9 @@ public StreamWriter(Stream stream, bool leaveOpen = false) /// Closes the current object and the underlying stream. /// /// - /// This method overrides . + /// This method overrides . /// This implementation of calls the method passing a true value. - /// You must call to ensure that all data is correctly written out to the underlying stream.Following a call to , any operations on the might raise exceptions. If there is insufficient space on the disk, calling will raise an exception. + /// You must call to ensure that all data is correctly written out to the underlying stream. Following a call to , any operations on the might raise exceptions. If there is insufficient space on the disk, calling will raise an exception. /// Flushing the stream will not flush its underlying encoder unless you explicitly call or . /// public override void Close() @@ -94,11 +88,11 @@ public override void Close() } /// - /// Causes any buffered data to be written to the underlying stream, releases the unmanaged resources used by the StreamWriter, and optionally the managed resources. + /// Causes any buffered data to be written to the underlying stream, releases the unmanaged resources used by the , and optionally the managed resources. /// /// to release both managed and unmanaged resources; to release only unmanaged resources. /// - /// When the disposing parameter is , this method releases all resources held by any managed objects that this StreamWriter references. This method invokes the method of each referenced object. + /// When the disposing parameter is , this method releases all resources held by any managed objects that this references. This method invokes the method of each referenced object. /// protected override void Dispose(bool disposing) { @@ -145,8 +139,8 @@ protected override void Dispose(bool disposing) /// The current writer is closed. /// An I/O error has occurred. /// - /// This method overrides TextWriter.Flush. - /// Flushing the stream will not flush its underlying encoder unless you explicitly call Flush or . + /// This method overrides . + /// Flushing the stream will not flush its underlying encoder unless you explicitly call or . /// public override void Flush() { @@ -173,7 +167,7 @@ public override void Flush() /// /// Writes a character to the stream. /// - /// + /// The character to write to the stream. /// /// This method overrides . /// The specified character is written to the underlying stream unless the end of the stream is reached prematurely. @@ -185,10 +179,27 @@ public override void Write(char value) WriteBytes(buffer, 0, buffer.Length); } + /// + /// Writes a character span to the stream. + /// + /// The character span to write. + public override void Write(ReadOnlySpan buffer) + { + byte[] tempBuf = Encoding.GetBytes(new string(buffer.ToArray())); + + WriteBytes(tempBuf, 0, tempBuf.Length); + } + /// public override void Write(string value) { + if (value == null) + { + return; + } + byte[] tempBuf = Encoding.GetBytes(value); + WriteBytes(tempBuf, 0, tempBuf.Length); } @@ -201,10 +212,11 @@ public override void WriteLine() /// /// Writes a string to the stream, followed by a line terminator. /// + /// The string to write. If the value is , only the line terminator is written. /// - /// This overload is equivalent to the overload. - /// The line terminator is defined by the CoreNewLine field. - /// This method does not search the specified string for individual newline characters(hexadecimal 0x000a) and replace them with NewLine. + /// This method overrides . + /// The line terminator is defined by the property. + /// This method does not search the specified string for individual newline characters (hexadecimal 0x000a) and replace them with . /// public override void WriteLine(string value) { @@ -252,5 +264,3 @@ internal void WriteBytes( } } } - - diff --git a/System.IO.Streams/TextReader.cs b/System.IO.Streams/TextReader.cs index 0b62388..68fd883 100644 --- a/System.IO.Streams/TextReader.cs +++ b/System.IO.Streams/TextReader.cs @@ -1,8 +1,5 @@ -// -// Copyright (c) .NET Foundation and Contributors -// Portions Copyright (c) Microsoft Corporation. All rights reserved. -// See LICENSE file in the project root for full license information. -// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. namespace System.IO { @@ -46,10 +43,10 @@ public void Dispose() /// /// Releases the unmanaged resources used by the and optionally releases the managed resources. /// - /// to release both managed and unmanaged resources; false to release only unmanaged resources. + /// to release both managed and unmanaged resources; to release only unmanaged resources. /// - /// This method is called by the public method and the Finalize method. By default, this method specifies the disposing parameter as . Finalize specifies the disposing parameter as false. - /// When the disposing parameter is true, this method releases all resources held by any managed objects that this TextReader references. This method invokes the method of each referenced object. + /// This method is called by the public method and the Finalize method. By default, this method specifies the disposing parameter as . Finalize specifies the disposing parameter as . + /// When the disposing parameter is , this method releases all resources held by any managed objects that this references. This method invokes the method of each referenced object. /// protected virtual void Dispose(bool disposing) { @@ -61,12 +58,13 @@ protected virtual void Dispose(bool disposing) /// An integer representing the next character to be read, or -1 if no more characters are available or the reader does not support seeking. /// /// - /// The method returns an integer value in order to determine whether the end of the file, or another error has occurred. This allows a user to first check if the returned value is -1 before casting it to a Char type. + /// The method returns an integer value in order to determine whether the end of the file, or another error has occurred. This allows a user to first check if the returned value is -1 before casting it to a type. /// /// - /// The current position of the is not changed by this operation.The returned value is -1 if no more characters are available.The default implementation returns -1. + /// The current position of the is not changed by this operation. The returned value is -1 if no more characters are available. The default implementation returns -1. /// - /// The TextReader class is an abstract class. Therefore, you do not instantiate it in your code.For an example of using the Peek method, see the StreamReader.Peek method. + /// + /// The class is an abstract class. Therefore, you do not instantiate it in your code. For an example of using the method, see the method. /// /// public virtual int Peek() @@ -95,10 +93,10 @@ public virtual int Read() /// The number of characters that have been read. The number will be less than or equal to , depending on whether the data is available within the reader. This method returns 0 (zero) if it is called when no more characters are left to read. /// /// - /// This method returns after either count characters are read or the end of the file is reached. is a blocking version of this method. + /// This method returns after either characters are read or the end of the file is reached. is a blocking version of this method. /// /// - /// The class is an abstract class. Therefore, you do not instantiate it in your code.For an example of using the method, see the StreamReader.Read method. + /// The class is an abstract class. Therefore, you do not instantiate it in your code. For an example of using the method, see the method. /// /// public virtual int Read(char[] buffer, int index, int count) @@ -106,19 +104,45 @@ public virtual int Read(char[] buffer, int index, int count) return -1; } + /// + /// Reads a specified maximum number of characters from the current text reader into a character span. + /// + /// When this method returns, contains the character span with values replaced by the characters read from the current source. + /// The number of characters that have been read, or 0 if at the end of the reader and no data was read. The number will be less than or equal to the length, depending on whether the data is available within the reader. + /// + /// + /// This method reads at most .Length characters from the current text reader and stores them in . + /// + /// + /// The class is an abstract class. Therefore, you do not instantiate it in your code. For an example of using the method, see the StreamReader.Read method. + /// + /// + public virtual int Read(Span buffer) + { + char[] array = new char[buffer.Length]; + int numRead = Read(array, 0, buffer.Length); + + if (numRead > 0) + { + new Span(array, 0, numRead).CopyTo(buffer); + } + + return numRead; + } + /// /// Reads a specified maximum number of characters from the current text reader and writes the data to a buffer, beginning at the specified index. /// - /// When this method returns, this parameter contains the specified character array with the values between and ( + -1) replaced by the characters read from the current source. + /// When this method returns, this parameter contains the specified character array with the values between and ( + - 1) replaced by the characters read from the current source. /// The position in at which to begin writing. /// The maximum number of characters to read. /// The number of characters that have been read. The number will be less than or equal to , depending on whether all input characters have been read. /// /// - /// The position of the underlying text reader is advanced by the number of characters that were read into buffer. + /// The position of the underlying text reader is advanced by the number of characters that were read into . /// /// - /// The method blocks until either count characters are read, or all characters have been read.This is a blocking version of . + /// The method blocks until either characters are read, or all characters have been read. This is a blocking version of . /// /// public virtual int ReadBlock(char[] buffer, int index, int count) @@ -139,10 +163,18 @@ public virtual int ReadBlock(char[] buffer, int index, int count) /// /// The next line from the reader, or if all characters have been read. /// - /// A line is defined as a sequence of characters followed by a carriage return (0x000d), a line feed (0x000a), a carriage return followed by a line feed, Environment.NewLine, or the end-of-stream marker. The string that is returned does not contain the terminating carriage return or line feed. The return value is null if the end of the input stream has been reached. - /// If the method throws an OutOfMemoryException exception, the reader's position in the underlying Stream is advanced by the number of characters the method was able to read, but the characters that were already read into the internal ReadLine buffer are discarded. Because the position of the reader in the stream cannot be changed, the characters that were already read are unrecoverable and can be accessed only by reinitializing the TextReader object. If the initial position within the stream is unknown or the stream does not support seeking, the underlying Stream also needs to be reinitialized. - /// To avoid such a situation and produce robust code you should use the Read method and store the read characters in a preallocated buffer. - /// The TextReader class is an abstract class. Therefore, you do not instantiate it in your code.For an example of using the ReadLine method, see the StreamReader.ReadLine method. + /// + /// A line is defined as a sequence of characters followed by a carriage return (0x000d), a line feed (0x000a), a carriage return followed by a line feed, or the end-of-stream marker. The string that is returned does not contain the terminating carriage return or line feed. The return value is if the end of the input stream has been reached. + /// + /// + /// If the method throws an , the reader's position in the underlying is advanced by the number of characters the method was able to read, but the characters that were already read into the internal ReadLine buffer are discarded. Because the position of the reader in the stream cannot be changed, the characters that were already read are unrecoverable and can be accessed only by reinitializing the object. If the initial position within the stream is unknown or the stream does not support seeking, the underlying also needs to be reinitialized. + /// + /// + /// To avoid such a situation and produce robust code you should use the method and store the read characters in a preallocated buffer. + /// + /// + /// The class is an abstract class. Therefore, you do not instantiate it in your code. For an example of using the method, see the method. + /// /// public virtual string ReadLine() { @@ -154,9 +186,16 @@ public virtual string ReadLine() /// /// A string that contains all characters from the current position to the end of the text reader. /// - /// If the method throws an exception, the reader's position in the underlying Stream is advanced by the number of characters the method was able to read, but the characters that were already read into the internal ReadToEnd buffer are discarded. Because the position of the reader in the stream cannot be changed, the characters that were already read are unrecoverable and can be accessed only by reinitializing the TextReader. If the initial position within the stream is unknown or the stream does not support seeking, the underlying Stream also needs to be reinitialized. - /// To avoid such a situation and produce robust code you should use the Read method and store the read characters in a preallocated buffer. - /// The class is an abstract class. Therefore, you do not instantiate it in your code.For an example of using the ReadToEnd method, see the StreamReader.ReadToEnd method. + /// + /// If the method throws an , the reader's position in the underlying is advanced by the number of characters the method was able to read, but the characters that were already read into the internal ReadToEnd buffer are discarded. Because the position of the reader in the stream cannot be changed, the characters that were already read are unrecoverable and can be accessed only by reinitializing the . If the initial position within the stream is unknown or the stream does not support seeking, the underlying also needs to be reinitialized. + /// + /// + /// To avoid such a situation and produce robust code you should use the method and store the read characters in a preallocated buffer. + /// + /// + /// The class is an abstract class. Therefore, you do not instantiate it in your code. For an example of using the method, see the method. + /// + /// public virtual string ReadToEnd() { return null; diff --git a/System.IO.Streams/TextWriter.cs b/System.IO.Streams/TextWriter.cs index 26fc00f..7b8474d 100644 --- a/System.IO.Streams/TextWriter.cs +++ b/System.IO.Streams/TextWriter.cs @@ -1,8 +1,5 @@ -// -// Copyright (c) .NET Foundation and Contributors -// Portions Copyright (c) Microsoft Corporation. All rights reserved. -// See LICENSE file in the project root for full license information. -// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using System.Text; @@ -30,6 +27,15 @@ public abstract class TextWriter : MarshalByRefObject, IDisposable /// /// Gets or sets the line terminator string used by the current . /// + /// The line terminator string for the current . + /// + /// + /// For non-Unix platforms, the default line terminator string is a carriage return followed by a line feed ('\r\n'). For Unix platforms, it's a line feed ('\n'). + /// + /// + /// The line terminator string is written to the text stream whenever one of the methods is called. In order for text written by the to be readable by a , only "\n" or "\r\n" should be used as terminator strings. If is set to , the default newline character is used instead. + /// + /// public virtual string NewLine { get @@ -39,10 +45,7 @@ public virtual string NewLine set { - if (value == null) - { - value = _InitialNewLine; - } + value ??= _InitialNewLine; CoreNewLine = value.ToCharArray(); } @@ -51,10 +54,7 @@ public virtual string NewLine /// /// Closes the current writer and releases any system resources associated with the writer. /// - public virtual void Close() - { - Dispose(); - } + public virtual void Close() => Dispose(); /// /// Releases the unmanaged resources used by the and optionally releases the managed resources. @@ -118,17 +118,9 @@ public virtual void Write(char[] buffer) /// The length minus is less than . public virtual void Write(char[] buffer, int index, int count) { - if (buffer == null) - { - throw new ArgumentNullException(); - } - - if (index < 0) - { - throw new ArgumentOutOfRangeException(); - } + ArgumentNullException.ThrowIfNull(buffer); - if (count < 0) + if (index < 0 || count < 0) { throw new ArgumentOutOfRangeException(); } @@ -148,65 +140,57 @@ public virtual void Write(char[] buffer, int index, int count) /// Writes the text representation of a value to the text stream. /// /// The value to write. - public virtual void Write(bool value) - { - Write(value.ToString()); - } + public virtual void Write(bool value) => Write(value.ToString()); /// /// Writes the text representation of a 4-byte signed integer to the text stream. /// /// The 4-byte signed integer to write. - public virtual void Write(int value) - { - Write(value.ToString()); - } + public virtual void Write(int value) => Write(value.ToString()); /// /// Writes the text representation of a 4-byte unsigned integer to the text stream. /// /// The 4-byte unsigned integer to write. - [System.CLSCompliant(false)] - public virtual void Write(uint value) - { - Write(value.ToString()); - } + [CLSCompliant(false)] + public virtual void Write(uint value) => Write(value.ToString()); /// /// Writes the text representation of an 8-byte signed integer to the text stream. /// /// The 8-byte signed integer to write. - public virtual void Write(long value) - { - Write(value.ToString()); - } + public virtual void Write(long value) => Write(value.ToString()); /// /// Writes the text representation of an 8-byte unsigned integer to the text stream. /// /// The 8-byte unsigned integer to write. - [System.CLSCompliant(false)] - public virtual void Write(ulong value) - { - Write(value.ToString()); - } + [CLSCompliant(false)] + public virtual void Write(ulong value) => Write(value.ToString()); /// /// Writes the text representation of a 4-byte floating-point value to the text stream. /// /// The 4-byte floating-point value to write. - public virtual void Write(float value) - { - Write(value.ToString()); - } + public virtual void Write(float value) => Write(value.ToString()); /// /// Writes the text representation of an 8-byte floating-point value to the text stream. /// /// The 8-byte floating-point value to write. - public virtual void Write(double value) - { - Write(value.ToString()); + public virtual void Write(double value) => Write(value.ToString()); + + /// + /// Writes a character span to the text stream. + /// + /// The character span to write to the text stream. + /// + /// This method writes the contents of the span to the text stream. + /// The default implementation creates a temporary array from the span and calls . + /// Derived classes can override this method to provide a more efficient implementation. + /// + public virtual void Write(ReadOnlySpan buffer) + { } /// @@ -236,10 +220,7 @@ public virtual void Write(object value) /// /// Writes a line terminator to the text stream. /// - public virtual void WriteLine() - { - Write(CoreNewLine); - } + public virtual void WriteLine() => Write(CoreNewLine); /// /// Writes a character to the text stream, followed by a line terminator. @@ -300,7 +281,7 @@ public virtual void WriteLine(int value) /// Writes the text representation of a 4-byte unsigned integer to the text stream, followed by a line terminator. /// /// The 4-byte unsigned integer to write. - [System.CLSCompliant(false)] + [CLSCompliant(false)] public virtual void WriteLine(uint value) { Write(value); @@ -321,7 +302,7 @@ public virtual void WriteLine(long value) /// Writes the text representation of an 8-byte unsigned integer to the text stream, followed by a line terminator. /// /// The 8-byte unsigned integer to write. - [System.CLSCompliant(false)] + [CLSCompliant(false)] public virtual void WriteLine(ulong value) { Write(value); diff --git a/UnitTests/MemoryStreamUnitTests/CanRead.cs b/UnitTests/MemoryStreamUnitTests/CanRead.cs index d85ed31..25028db 100644 --- a/UnitTests/MemoryStreamUnitTests/CanRead.cs +++ b/UnitTests/MemoryStreamUnitTests/CanRead.cs @@ -1,19 +1,13 @@ -// -// Copyright (c) .NET Foundation and Contributors -// Portions Copyright (c) Microsoft Corporation. All rights reserved. -// See LICENSE file in the project root for full license information. -// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using nanoFramework.TestFramework; -using System; -using System.IO; namespace System.IO.MemoryStreamUnitTests { [TestClass] public class CanRead { - [TestMethod] public void CanRead_Default_Ctor() { diff --git a/UnitTests/MemoryStreamUnitTests/CanSeek.cs b/UnitTests/MemoryStreamUnitTests/CanSeek.cs index e401885..839f27b 100644 --- a/UnitTests/MemoryStreamUnitTests/CanSeek.cs +++ b/UnitTests/MemoryStreamUnitTests/CanSeek.cs @@ -1,12 +1,7 @@ -// -// Copyright (c) .NET Foundation and Contributors -// Portions Copyright (c) Microsoft Corporation. All rights reserved. -// See LICENSE file in the project root for full license information. -// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using nanoFramework.TestFramework; -using System; -using System.IO; namespace System.IO.MemoryStreamUnitTests { diff --git a/UnitTests/MemoryStreamUnitTests/CanWrite.cs b/UnitTests/MemoryStreamUnitTests/CanWrite.cs index a26d29c..1a17af3 100644 --- a/UnitTests/MemoryStreamUnitTests/CanWrite.cs +++ b/UnitTests/MemoryStreamUnitTests/CanWrite.cs @@ -1,26 +1,20 @@ -// -// Copyright (c) .NET Foundation and Contributors -// Portions Copyright (c) Microsoft Corporation. All rights reserved. -// See LICENSE file in the project root for full license information. -// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using nanoFramework.TestFramework; -using System; -using System.IO; namespace System.IO.MemoryStreamUnitTests { [TestClass] public class CanWrite { - [TestMethod] public void CanWrite_Default_Ctor() { try { OutputHelper.WriteLine("Verify CanWrite is true for default Ctor"); - + using (MemoryStream fs = new MemoryStream()) { Assert.IsTrue(fs.CanWrite, "Expected CanWrite == true, but got CanWrite == false"); @@ -28,7 +22,7 @@ public void CanWrite_Default_Ctor() } catch (Exception ex) { - OutputHelper.WriteLine($"Unexpected exception {ex}"); + OutputHelper.WriteLine($"Unexpected exception {ex}"); } } @@ -38,9 +32,9 @@ public void CanWrite_Byte_Ctor() try { OutputHelper.WriteLine("Verify CanWrite is true for Byte[] Ctor"); - + byte[] buffer = new byte[1024]; - + using (MemoryStream fs = new MemoryStream(buffer)) { Assert.IsTrue(fs.CanWrite, "Expected CanWrite == true, but got CanWrite == false"); @@ -48,7 +42,7 @@ public void CanWrite_Byte_Ctor() } catch (Exception ex) { - OutputHelper.WriteLine($"Unexpected exception {ex}"); + OutputHelper.WriteLine($"Unexpected exception {ex}"); } } @@ -65,7 +59,7 @@ public void CanWrite_bool_isWritable_Ctor(bool isWritable) // Assert Assert.AreEqual(isWritable, fs.CanWrite, $"Expected CanWrite == {isWritable}, but got CanWrite == {!isWritable}"); - if(!isWritable) + if (!isWritable) { Assert.ThrowsException(typeof(NotSupportedException), () => fs.WriteByte(0), "Expected exception when attempt to write to a read only stream."); } diff --git a/UnitTests/MemoryStreamUnitTests/Close.cs b/UnitTests/MemoryStreamUnitTests/Close.cs index 5947e2f..cd7efbc 100644 --- a/UnitTests/MemoryStreamUnitTests/Close.cs +++ b/UnitTests/MemoryStreamUnitTests/Close.cs @@ -1,12 +1,7 @@ -// -// Copyright (c) .NET Foundation and Contributors -// Portions Copyright (c) Microsoft Corporation. All rights reserved. -// See LICENSE file in the project root for full license information. -// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using nanoFramework.TestFramework; -using System; -using System.IO; namespace System.IO.MemoryStreamUnitTests { diff --git a/UnitTests/MemoryStreamUnitTests/Flush.cs b/UnitTests/MemoryStreamUnitTests/Flush.cs index 77cebf3..3f79d06 100644 --- a/UnitTests/MemoryStreamUnitTests/Flush.cs +++ b/UnitTests/MemoryStreamUnitTests/Flush.cs @@ -1,12 +1,7 @@ -// -// Copyright (c) .NET Foundation and Contributors -// Portions Copyright (c) Microsoft Corporation. All rights reserved. -// See LICENSE file in the project root for full license information. -// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using nanoFramework.TestFramework; -using System; -using System.IO; namespace System.IO.MemoryStreamUnitTests { diff --git a/UnitTests/MemoryStreamUnitTests/Length.cs b/UnitTests/MemoryStreamUnitTests/Length.cs index 1073c13..07d4335 100644 --- a/UnitTests/MemoryStreamUnitTests/Length.cs +++ b/UnitTests/MemoryStreamUnitTests/Length.cs @@ -1,12 +1,7 @@ -// -// Copyright (c) .NET Foundation and Contributors -// Portions Copyright (c) Microsoft Corporation. All rights reserved. -// See LICENSE file in the project root for full license information. -// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using nanoFramework.TestFramework; -using System; -using System.IO; namespace System.IO.MemoryStreamUnitTests { diff --git a/UnitTests/MemoryStreamUnitTests/MemoryStreamHelper.cs b/UnitTests/MemoryStreamUnitTests/MemoryStreamHelper.cs index b7addfa..a607f74 100644 --- a/UnitTests/MemoryStreamUnitTests/MemoryStreamHelper.cs +++ b/UnitTests/MemoryStreamUnitTests/MemoryStreamHelper.cs @@ -1,12 +1,7 @@ -// -// Copyright (c) .NET Foundation and Contributors -// Portions Copyright (c) Microsoft Corporation. All rights reserved. -// See LICENSE file in the project root for full license information. -// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using nanoFramework.TestFramework; -using System; -using System.IO; namespace System.IO.MemoryStreamUnitTests { @@ -104,7 +99,7 @@ public static byte[] GetRandomBytes(int length) byte[] byteArr = new byte[length]; Random s_random = new Random(); - + s_random.NextBytes(byteArr); return byteArr; diff --git a/UnitTests/MemoryStreamUnitTests/MemoryStreamUnitTests.nfproj b/UnitTests/MemoryStreamUnitTests/MemoryStreamUnitTests.nfproj index ff1eb5b..3160137 100644 --- a/UnitTests/MemoryStreamUnitTests/MemoryStreamUnitTests.nfproj +++ b/UnitTests/MemoryStreamUnitTests/MemoryStreamUnitTests.nfproj @@ -63,7 +63,6 @@ - diff --git a/UnitTests/MemoryStreamUnitTests/MemoryStream_Ctor.cs b/UnitTests/MemoryStreamUnitTests/MemoryStream_Ctor.cs index 42650b3..bae488d 100644 --- a/UnitTests/MemoryStreamUnitTests/MemoryStream_Ctor.cs +++ b/UnitTests/MemoryStreamUnitTests/MemoryStream_Ctor.cs @@ -1,12 +1,7 @@ -// -// Copyright (c) .NET Foundation and Contributors -// Portions Copyright (c) Microsoft Corporation. All rights reserved. -// See LICENSE file in the project root for full license information. -// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using nanoFramework.TestFramework; -using System; -using System.IO; namespace System.IO.MemoryStreamUnitTests { @@ -142,6 +137,5 @@ private bool ValidateMemoryStream( } #endregion Helper methods - } } diff --git a/UnitTests/MemoryStreamUnitTests/Position.cs b/UnitTests/MemoryStreamUnitTests/Position.cs index 34bc873..c5e7f02 100644 --- a/UnitTests/MemoryStreamUnitTests/Position.cs +++ b/UnitTests/MemoryStreamUnitTests/Position.cs @@ -1,12 +1,7 @@ -// -// Copyright (c) .NET Foundation and Contributors -// Portions Copyright (c) Microsoft Corporation. All rights reserved. -// See LICENSE file in the project root for full license information. -// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using nanoFramework.TestFramework; -using System; -using System.IO; namespace System.IO.MemoryStreamUnitTests { diff --git a/UnitTests/MemoryStreamUnitTests/Read.cs b/UnitTests/MemoryStreamUnitTests/Read.cs index 25dc893..3f91fa5 100644 --- a/UnitTests/MemoryStreamUnitTests/Read.cs +++ b/UnitTests/MemoryStreamUnitTests/Read.cs @@ -1,12 +1,7 @@ -// -// Copyright (c) .NET Foundation and Contributors -// Portions Copyright (c) Microsoft Corporation. All rights reserved. -// See LICENSE file in the project root for full license information. -// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using nanoFramework.TestFramework; -using System; -using System.IO; using System.Text; namespace System.IO.MemoryStreamUnitTests diff --git a/UnitTests/MemoryStreamUnitTests/ReadByte.cs b/UnitTests/MemoryStreamUnitTests/ReadByte.cs index 6ddf304..af8a210 100644 --- a/UnitTests/MemoryStreamUnitTests/ReadByte.cs +++ b/UnitTests/MemoryStreamUnitTests/ReadByte.cs @@ -1,12 +1,7 @@ -// -// Copyright (c) .NET Foundation and Contributors -// Portions Copyright (c) Microsoft Corporation. All rights reserved. -// See LICENSE file in the project root for full license information. -// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using nanoFramework.TestFramework; -using System; -using System.IO; namespace System.IO.MemoryStreamUnitTests { diff --git a/UnitTests/MemoryStreamUnitTests/Seek.cs b/UnitTests/MemoryStreamUnitTests/Seek.cs index 0dc1e2c..ca5df4b 100644 --- a/UnitTests/MemoryStreamUnitTests/Seek.cs +++ b/UnitTests/MemoryStreamUnitTests/Seek.cs @@ -1,19 +1,13 @@ -// -// Copyright (c) .NET Foundation and Contributors -// Portions Copyright (c) Microsoft Corporation. All rights reserved. -// See LICENSE file in the project root for full license information. -// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using nanoFramework.TestFramework; -using System; -using System.IO; namespace System.IO.MemoryStreamUnitTests { [TestClass] public class Seek { - #region Helper methods private bool TestSeek(MemoryStream ms, long offset, SeekOrigin origin, long expectedPosition) diff --git a/UnitTests/MemoryStreamUnitTests/SetLength.cs b/UnitTests/MemoryStreamUnitTests/SetLength.cs index 7c4eb24..59987ca 100644 --- a/UnitTests/MemoryStreamUnitTests/SetLength.cs +++ b/UnitTests/MemoryStreamUnitTests/SetLength.cs @@ -1,19 +1,13 @@ -// -// Copyright (c) .NET Foundation and Contributors -// Portions Copyright (c) Microsoft Corporation. All rights reserved. -// See LICENSE file in the project root for full license information. -// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using nanoFramework.TestFramework; -using System; -using System.IO; namespace System.IO.MemoryStreamUnitTests { [TestClass] public class SetLength { - #region Helper methods private bool TestLength(MemoryStream ms, long expectedLength) diff --git a/UnitTests/MemoryStreamUnitTests/ToArray.cs b/UnitTests/MemoryStreamUnitTests/ToArray.cs index f77815a..d5b8192 100644 --- a/UnitTests/MemoryStreamUnitTests/ToArray.cs +++ b/UnitTests/MemoryStreamUnitTests/ToArray.cs @@ -1,19 +1,13 @@ -// -// Copyright (c) .NET Foundation and Contributors -// Portions Copyright (c) Microsoft Corporation. All rights reserved. -// See LICENSE file in the project root for full license information. -// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using nanoFramework.TestFramework; -using System; -using System.IO; namespace System.IO.MemoryStreamUnitTests { [TestClass] public class ToArray { - #region Helper methods private bool VerifyArray(byte[] data, int expected) { diff --git a/UnitTests/MemoryStreamUnitTests/Write.cs b/UnitTests/MemoryStreamUnitTests/Write.cs index 74b8520..1edd332 100644 --- a/UnitTests/MemoryStreamUnitTests/Write.cs +++ b/UnitTests/MemoryStreamUnitTests/Write.cs @@ -1,12 +1,7 @@ -// -// Copyright (c) .NET Foundation and Contributors -// Portions Copyright (c) Microsoft Corporation. All rights reserved. -// See LICENSE file in the project root for full license information. -// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using nanoFramework.TestFramework; -using System; -using System.IO; using System.Text; namespace System.IO.MemoryStreamUnitTests diff --git a/UnitTests/MemoryStreamUnitTests/WriteByte.cs b/UnitTests/MemoryStreamUnitTests/WriteByte.cs index 93f558c..2824947 100644 --- a/UnitTests/MemoryStreamUnitTests/WriteByte.cs +++ b/UnitTests/MemoryStreamUnitTests/WriteByte.cs @@ -1,19 +1,13 @@ -// -// Copyright (c) .NET Foundation and Contributors -// Portions Copyright (c) Microsoft Corporation. All rights reserved. -// See LICENSE file in the project root for full license information. -// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using nanoFramework.TestFramework; -using System; -using System.IO; namespace System.IO.MemoryStreamUnitTests { [TestClass] public class WriteByte { - #region Helper methods private bool TestWrite(MemoryStream ms, int BytesToWrite) diff --git a/UnitTests/MemoryStreamUnitTests/WriteTo.cs b/UnitTests/MemoryStreamUnitTests/WriteTo.cs index d727688..f04d5bb 100644 --- a/UnitTests/MemoryStreamUnitTests/WriteTo.cs +++ b/UnitTests/MemoryStreamUnitTests/WriteTo.cs @@ -1,19 +1,13 @@ -// -// Copyright (c) .NET Foundation and Contributors -// Portions Copyright (c) Microsoft Corporation. All rights reserved. -// See LICENSE file in the project root for full license information. -// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using nanoFramework.TestFramework; -using System; -using System.IO; namespace System.IO.MemoryStreamUnitTests { [TestClass] public class WriteTo { - #region Test Cases [TestMethod] diff --git a/UnitTests/StreamReaderWriterUnitTests/StreamReaderTests.cs b/UnitTests/StreamReaderWriterUnitTests/StreamReaderTests.cs index 5a182e5..c18f8db 100644 --- a/UnitTests/StreamReaderWriterUnitTests/StreamReaderTests.cs +++ b/UnitTests/StreamReaderWriterUnitTests/StreamReaderTests.cs @@ -1,8 +1,5 @@ -// -// Copyright (c) .NET Foundation and Contributors -// Portions Copyright (c) Microsoft Corporation. All rights reserved. -// See LICENSE file in the project root for full license information. -// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using nanoFramework.TestFramework; @@ -62,5 +59,165 @@ public void TestReaderLeaveOpen() // cannot set position on a disposed stream, test this by trying to set the position Assert.ThrowsException(typeof(ObjectDisposedException), () => stream.Position = 0); } + + [TestMethod] + public void TestReadArray() + { + string testText = "Hello, World! This is a test of Read with arrays!!"; + byte[] bytes = System.Text.Encoding.UTF8.GetBytes(testText); + + using MemoryStream stream = new MemoryStream(bytes); + using StreamReader reader = new StreamReader(stream); + + // Test reading into a buffer + char[] buffer = new char[20]; + int charsRead = reader.Read(buffer, 0, 20); + + Assert.AreEqual(20, charsRead); + Assert.AreEqual("Hello, World! This i", new string(buffer)); + + // Read more + charsRead = reader.Read(buffer, 0, 20); + Assert.AreEqual(20, charsRead); + Assert.AreEqual("s a test of Read wit", new string(buffer)); + + // Read remaining (10 characters: "h arrays!!") + char[] smallBuffer = new char[10]; + charsRead = reader.Read(smallBuffer, 0, 10); + Assert.AreEqual(10, charsRead); + Assert.AreEqual("h arrays!!", new string(smallBuffer)); + + // Verify end of stream + charsRead = reader.Read(buffer, 0, 20); + Assert.AreEqual(0, charsRead); + } + + [TestMethod] + public void TestReadArray_EmptyStream() + { + using MemoryStream stream = new MemoryStream(); + using StreamReader reader = new StreamReader(stream); + + char[] buffer = new char[10]; + int charsRead = reader.Read(buffer, 0, 10); + + Assert.AreEqual(0, charsRead); + } + + [TestMethod] + public void TestReadArray_SmallChunks() + { + string testText = "ABC"; + byte[] bytes = System.Text.Encoding.UTF8.GetBytes(testText); + + using MemoryStream stream = new MemoryStream(bytes); + using StreamReader reader = new StreamReader(stream); + + // Read one character at a time + char[] buffer = new char[1]; + + int charsRead = reader.Read(buffer, 0, 1); + Assert.AreEqual(1, charsRead); + Assert.AreEqual('A', buffer[0]); + + charsRead = reader.Read(buffer, 0, 1); + Assert.AreEqual(1, charsRead); + Assert.AreEqual('B', buffer[0]); + + charsRead = reader.Read(buffer, 0, 1); + Assert.AreEqual(1, charsRead); + Assert.AreEqual('C', buffer[0]); + + charsRead = reader.Read(buffer, 0, 1); + Assert.AreEqual(0, charsRead); + } + + [TestMethod] + public void TestReadArray_LargerThanStream() + { + string testText = "Short"; + byte[] bytes = System.Text.Encoding.UTF8.GetBytes(testText); + + using MemoryStream stream = new MemoryStream(bytes); + using StreamReader reader = new StreamReader(stream); + + // Buffer larger than content + char[] buffer = new char[100]; + int charsRead = reader.Read(buffer, 0, 100); + + Assert.AreEqual(5, charsRead); + char[] result = new char[charsRead]; + Array.Copy(buffer, 0, result, 0, charsRead); + Assert.AreEqual("Short", new string(result)); + } + + [TestMethod] + public void TestReadArray_Disposed() + { + using MemoryStream stream = new MemoryStream(); + StreamReader reader = new StreamReader(stream); + reader.Dispose(); + + char[] buffer = new char[10]; + Assert.ThrowsException(typeof(ObjectDisposedException), () => reader.Read(buffer, 0, 10)); + } + + [TestMethod] + public void TestReadToEnd() + { + string testText = "This is a complete test of ReadToEnd method. It should read all characters from the current position to the end of the stream."; + byte[] bytes = System.Text.Encoding.UTF8.GetBytes(testText); + + using MemoryStream stream = new MemoryStream(bytes); + using StreamReader reader = new StreamReader(stream); + + string result = reader.ReadToEnd(); + + Assert.AreEqual(testText, result); + } + + [TestMethod] + public void TestReadToEnd_EmptyStream() + { + using MemoryStream stream = new MemoryStream(); + using StreamReader reader = new StreamReader(stream); + + string result = reader.ReadToEnd(); + + Assert.AreEqual("", result); + } + + [TestMethod] + public void TestReadToEnd_AfterPartialRead() + { + string testText = "First part. Second part. Third part."; + byte[] bytes = System.Text.Encoding.UTF8.GetBytes(testText); + + using MemoryStream stream = new MemoryStream(bytes); + using StreamReader reader = new StreamReader(stream); + + // Read first 10 characters ("First part") + char[] buffer = new char[10]; + reader.Read(buffer, 0, 10); + + // ReadToEnd should read the rest (". Second part. Third part.") + string result = reader.ReadToEnd(); + + Assert.AreEqual(". Second part. Third part.", result); + } + + [TestMethod] + public void TestReadToEnd_MultipleLines() + { + string testText = "Line 1\r\nLine 2\r\nLine 3"; + byte[] bytes = System.Text.Encoding.UTF8.GetBytes(testText); + + using MemoryStream stream = new MemoryStream(bytes); + using StreamReader reader = new StreamReader(stream); + + string result = reader.ReadToEnd(); + + Assert.AreEqual(testText, result); + } } } diff --git a/UnitTests/StreamReaderWriterUnitTests/StreamReaderWriterUnitTests.nfproj b/UnitTests/StreamReaderWriterUnitTests/StreamReaderWriterUnitTests.nfproj index 273cb40..85f4f80 100644 --- a/UnitTests/StreamReaderWriterUnitTests/StreamReaderWriterUnitTests.nfproj +++ b/UnitTests/StreamReaderWriterUnitTests/StreamReaderWriterUnitTests.nfproj @@ -45,7 +45,6 @@ - diff --git a/UnitTests/StreamReaderWriterUnitTests/StreamWriterTests.cs b/UnitTests/StreamReaderWriterUnitTests/StreamWriterTests.cs index 6b14ce0..3c7030b 100644 --- a/UnitTests/StreamReaderWriterUnitTests/StreamWriterTests.cs +++ b/UnitTests/StreamReaderWriterUnitTests/StreamWriterTests.cs @@ -1,8 +1,5 @@ -// -// Copyright (c) .NET Foundation and Contributors -// Portions Copyright (c) Microsoft Corporation. All rights reserved. -// See LICENSE file in the project root for full license information. -// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using nanoFramework.TestFramework; @@ -54,5 +51,161 @@ public void TestReaderLeaveOpen() // cannot set position on a disposed stream, test this by trying to set the position Assert.ThrowsException(typeof(ObjectDisposedException), () => stream.Position = 0); } + + [TestMethod] + public void TestWriteDouble() + { + using MemoryStream stream = new MemoryStream(); + using (StreamWriter writer = new StreamWriter(stream, true)) + { + writer.Write(3.14154); + writer.Write(" "); + writer.Write(-123.456); + writer.Write(" "); + writer.Write(0.0); + } + + stream.Position = 0; + + using StreamReader reader = new StreamReader(stream); + string result = reader.ReadToEnd(); + + Assert.IsTrue(result.Contains("3.14154")); + Assert.IsTrue(result.Contains("-123.456")); + Assert.IsTrue(result.Contains("0")); + } + + [TestMethod] + public void TestWriteDoubleWithWriteLine() + { + using MemoryStream stream = new MemoryStream(); + using (StreamWriter writer = new StreamWriter(stream, true)) + { + writer.WriteLine(123.456); + writer.WriteLine(-789.011); + } + + stream.Position = 0; + + using StreamReader reader = new StreamReader(stream); + string line1 = reader.ReadLine(); + string line2 = reader.ReadLine(); + + Assert.IsTrue(line1.Contains("123.456")); + Assert.IsTrue(line2.Contains("-789.011")); + } + + [TestMethod] + public void TestWriteReadOnlySpan() + { + string testText = "Hello from Span!"; + char[] charArray = testText.ToCharArray(); + ReadOnlySpan span = new ReadOnlySpan(charArray); + + using MemoryStream stream = new MemoryStream(); + using (StreamWriter writer = new StreamWriter(stream, true)) + { + writer.Write(span); + } + + stream.Position = 0; + + using StreamReader reader = new StreamReader(stream); + string result = reader.ReadToEnd(); + + Assert.AreEqual(testText, result); + } + + [TestMethod] + public void TestWriteReadOnlySpan_EmptySpan() + { + ReadOnlySpan span = ReadOnlySpan.Empty; + + using MemoryStream stream = new MemoryStream(); + using (StreamWriter writer = new StreamWriter(stream, true)) + { + writer.Write(span); + } + + Assert.AreEqual(0, (int)stream.Length); + } + + [TestMethod] + public void TestWriteReadOnlySpan_MultipleWrites() + { + ReadOnlySpan span1 = new ReadOnlySpan("First".ToCharArray()); + ReadOnlySpan span2 = new ReadOnlySpan(" ".ToCharArray()); + ReadOnlySpan span3 = new ReadOnlySpan("Second".ToCharArray()); + + using MemoryStream stream = new MemoryStream(); + using (StreamWriter writer = new StreamWriter(stream, true)) + { + writer.Write(span1); + writer.Write(span2); + writer.Write(span3); + } + + stream.Position = 0; + + using StreamReader reader = new StreamReader(stream); + string result = reader.ReadToEnd(); + + Assert.AreEqual("First Second", result); + } + + [TestMethod] + public void TestWriteReadOnlySpan_LargeSpan() + { + // Create a large span that exceeds internal buffer size + char[] chars = new char[5000]; + for (int i = 0; i < chars.Length; i++) + { + chars[i] = (char)('A' + (i % 26)); + } + ReadOnlySpan span = new ReadOnlySpan(chars); + + using MemoryStream stream = new MemoryStream(); + using (StreamWriter writer = new StreamWriter(stream, true)) + { + writer.Write(span); + } + + stream.Position = 0; + + using StreamReader reader = new StreamReader(stream); + string result = reader.ReadToEnd(); + + Assert.AreEqual(5000, result.Length); + Assert.AreEqual('A', result[0]); + Assert.AreEqual((char)('A' + (4999 % 26)), result[4999]); + } + + [TestMethod] + public void TestWriteMixedTypes() + { + using MemoryStream stream = new MemoryStream(); + using (StreamWriter writer = new StreamWriter(stream, true)) + { + writer.Write("Integer: "); + writer.Write(42); + writer.Write(", Double: "); + writer.Write(3.14); + writer.Write(", Float: "); + writer.Write(2.71f); + writer.Write(", String: "); + ReadOnlySpan span = new ReadOnlySpan("Hello".ToCharArray()); + writer.Write(span); + } + + stream.Position = 0; + + using StreamReader reader = new StreamReader(stream); + string result = reader.ReadToEnd(); + + Assert.IsTrue(result.Contains("42")); + Assert.IsTrue(result.Contains("3.14")); + Assert.IsTrue(result.Contains("2.71")); + Assert.IsTrue(result.Contains("Hello")); + } } }