Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'netcore/System.Private.CoreLib/shared/System/IO')
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/BinaryReader.cs611
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/BinaryWriter.cs469
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/DirectoryNotFoundException.cs42
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/DisableMediaInsertionPrompt.cs41
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/DriveInfoInternal.Unix.cs15
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/DriveInfoInternal.Windows.cs87
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/EncodingCache.cs13
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/EndOfStreamException.cs36
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/Error.cs42
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/FileAccess.cs26
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/FileLoadException.cs85
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/FileMode.cs38
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/FileNotFoundException.cs107
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/FileOptions.cs29
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/FileShare.cs43
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/FileStream.Linux.cs30
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/FileStream.OSX.cs19
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/FileStream.Unix.cs852
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/FileStream.Win32.cs107
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/FileStream.Windows.cs1627
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/FileStream.cs915
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/FileStreamCompletionSource.Win32.cs259
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/IOException.cs41
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/MemoryStream.cs868
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/Path.Unix.cs147
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/Path.Windows.cs286
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/Path.cs930
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/PathHelper.Windows.cs251
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/PathInternal.Unix.cs98
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/PathInternal.Windows.cs414
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/PathInternal.cs248
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/PathTooLongException.cs36
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/PersistedFiles.Names.Unix.cs17
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/PersistedFiles.Unix.cs164
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/PinnedBufferMemoryStream.cs61
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/SeekOrigin.cs16
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/Stream.cs1400
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/StreamHelpers.CopyValidation.cs70
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/StreamReader.cs1347
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/StreamWriter.cs1078
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/TextReader.cs406
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/TextWriter.cs1016
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryAccessor.cs668
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStream.cs951
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStreamWrapper.cs195
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/Win32Marshal.cs101
46 files changed, 0 insertions, 16302 deletions
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/BinaryReader.cs b/netcore/System.Private.CoreLib/shared/System/IO/BinaryReader.cs
deleted file mode 100644
index cf8e4954593..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/BinaryReader.cs
+++ /dev/null
@@ -1,611 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-/*============================================================
-**
-**
-**
-**
-**
-** Purpose: Wraps a stream and provides convenient read functionality
-** for strings and primitive types.
-**
-**
-============================================================*/
-
-using System.Buffers.Binary;
-using System.Diagnostics;
-using System.Runtime.CompilerServices;
-using System.Text;
-
-namespace System.IO
-{
- public class BinaryReader : IDisposable
- {
- private const int MaxCharBytesSize = 128;
-
- private readonly Stream _stream;
- private readonly byte[] _buffer;
- private readonly Decoder _decoder;
- private byte[]? _charBytes;
- private char[]? _charBuffer;
- private readonly int _maxCharsSize; // From MaxCharBytesSize & Encoding
-
- // Performance optimization for Read() w/ Unicode. Speeds us up by ~40%
- private readonly bool _2BytesPerChar;
- private readonly bool _isMemoryStream; // "do we sit on MemoryStream?" for Read/ReadInt32 perf
- private readonly bool _leaveOpen;
- private bool _disposed;
-
- public BinaryReader(Stream input) : this(input, Encoding.UTF8, false)
- {
- }
-
- public BinaryReader(Stream input, Encoding encoding) : this(input, encoding, false)
- {
- }
-
- public BinaryReader(Stream input, Encoding encoding, bool leaveOpen)
- {
- if (input == null)
- {
- throw new ArgumentNullException(nameof(input));
- }
- if (encoding == null)
- {
- throw new ArgumentNullException(nameof(encoding));
- }
- if (!input.CanRead)
- {
- throw new ArgumentException(SR.Argument_StreamNotReadable);
- }
-
- _stream = input;
- _decoder = encoding.GetDecoder();
- _maxCharsSize = encoding.GetMaxCharCount(MaxCharBytesSize);
- int minBufferSize = encoding.GetMaxByteCount(1); // max bytes per one char
- if (minBufferSize < 16)
- {
- minBufferSize = 16;
- }
-
- _buffer = new byte[minBufferSize];
- // _charBuffer and _charBytes will be left null.
-
- // For Encodings that always use 2 bytes per char (or more),
- // special case them here to make Read() & Peek() faster.
- _2BytesPerChar = encoding is UnicodeEncoding;
- // check if BinaryReader is based on MemoryStream, and keep this for it's life
- // we cannot use "as" operator, since derived classes are not allowed
- _isMemoryStream = (_stream.GetType() == typeof(MemoryStream));
- _leaveOpen = leaveOpen;
-
- Debug.Assert(_decoder != null, "[BinaryReader.ctor]_decoder!=null");
- }
-
- public virtual Stream BaseStream => _stream;
-
- protected virtual void Dispose(bool disposing)
- {
- if (!_disposed)
- {
- if (disposing && !_leaveOpen)
- {
- _stream.Close();
- }
- _disposed = true;
- }
- }
-
- public void Dispose()
- {
- Dispose(true);
- }
-
- /// <remarks>
- /// Override Dispose(bool) instead of Close(). This API exists for compatibility purposes.
- /// </remarks>
- public virtual void Close()
- {
- Dispose(true);
- }
-
- private void ThrowIfDisposed()
- {
- if (_disposed)
- {
- throw Error.GetFileNotOpen();
- }
- }
-
- public virtual int PeekChar()
- {
- ThrowIfDisposed();
-
- if (!_stream.CanSeek)
- {
- return -1;
- }
-
- long origPos = _stream.Position;
- int ch = Read();
- _stream.Position = origPos;
- return ch;
- }
-
- public virtual int Read()
- {
- ThrowIfDisposed();
-
- int charsRead = 0;
- int numBytes;
- long posSav = 0;
-
- if (_stream.CanSeek)
- {
- posSav = _stream.Position;
- }
-
- _charBytes ??= new byte[MaxCharBytesSize];
-
- Span<char> singleChar = stackalloc char[1];
-
- while (charsRead == 0)
- {
- // We really want to know what the minimum number of bytes per char
- // is for our encoding. Otherwise for UnicodeEncoding we'd have to
- // do ~1+log(n) reads to read n characters.
- // Assume 1 byte can be 1 char unless _2BytesPerChar is true.
- numBytes = _2BytesPerChar ? 2 : 1;
-
- int r = _stream.ReadByte();
- _charBytes[0] = (byte)r;
- if (r == -1)
- {
- numBytes = 0;
- }
- if (numBytes == 2)
- {
- r = _stream.ReadByte();
- _charBytes[1] = (byte)r;
- if (r == -1)
- {
- numBytes = 1;
- }
- }
-
- if (numBytes == 0)
- {
- return -1;
- }
-
- Debug.Assert(numBytes == 1 || numBytes == 2, "BinaryReader::ReadOneChar assumes it's reading one or 2 bytes only.");
-
- try
- {
- charsRead = _decoder.GetChars(new ReadOnlySpan<byte>(_charBytes, 0, numBytes), singleChar, flush: false);
- }
- catch
- {
- // Handle surrogate char
-
- if (_stream.CanSeek)
- {
- _stream.Seek(posSav - _stream.Position, SeekOrigin.Current);
- }
- // else - we can't do much here
-
- throw;
- }
-
- Debug.Assert(charsRead < 2, "BinaryReader::ReadOneChar - assuming we only got 0 or 1 char, not 2!");
- }
- Debug.Assert(charsRead > 0);
- return singleChar[0];
- }
-
- public virtual byte ReadByte() => InternalReadByte();
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)] // Inlined to avoid some method call overhead with InternalRead.
- private byte InternalReadByte()
- {
- ThrowIfDisposed();
-
- int b = _stream.ReadByte();
- if (b == -1)
- {
- throw Error.GetEndOfFile();
- }
-
- return (byte)b;
- }
-
- [CLSCompliant(false)]
- public virtual sbyte ReadSByte() => (sbyte)InternalReadByte();
- public virtual bool ReadBoolean() => InternalReadByte() != 0;
-
- public virtual char ReadChar()
- {
- int value = Read();
- if (value == -1)
- {
- throw Error.GetEndOfFile();
- }
- return (char)value;
- }
-
- public virtual short ReadInt16() => BinaryPrimitives.ReadInt16LittleEndian(InternalRead(2));
-
- [CLSCompliant(false)]
- public virtual ushort ReadUInt16() => BinaryPrimitives.ReadUInt16LittleEndian(InternalRead(2));
-
- public virtual int ReadInt32() => BinaryPrimitives.ReadInt32LittleEndian(InternalRead(4));
- [CLSCompliant(false)]
- public virtual uint ReadUInt32() => BinaryPrimitives.ReadUInt32LittleEndian(InternalRead(4));
- public virtual long ReadInt64() => BinaryPrimitives.ReadInt64LittleEndian(InternalRead(8));
- [CLSCompliant(false)]
- public virtual ulong ReadUInt64() => BinaryPrimitives.ReadUInt64LittleEndian(InternalRead(8));
- public virtual unsafe float ReadSingle() => BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(InternalRead(4)));
- public virtual unsafe double ReadDouble() => BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(InternalRead(8)));
-
- public virtual decimal ReadDecimal()
- {
- ReadOnlySpan<byte> span = InternalRead(16);
- try
- {
- return decimal.ToDecimal(span);
- }
- catch (ArgumentException e)
- {
- // ReadDecimal cannot leak out ArgumentException
- throw new IOException(SR.Arg_DecBitCtor, e);
- }
- }
-
- public virtual string ReadString()
- {
- ThrowIfDisposed();
-
- int currPos = 0;
- int n;
- int stringLength;
- int readLength;
- int charsRead;
-
- // Length of the string in bytes, not chars
- stringLength = Read7BitEncodedInt();
- if (stringLength < 0)
- {
- throw new IOException(SR.Format(SR.IO_InvalidStringLen_Len, stringLength));
- }
-
- if (stringLength == 0)
- {
- return string.Empty;
- }
-
- _charBytes ??= new byte[MaxCharBytesSize];
- _charBuffer ??= new char[_maxCharsSize];
-
- StringBuilder? sb = null;
- do
- {
- readLength = ((stringLength - currPos) > MaxCharBytesSize) ? MaxCharBytesSize : (stringLength - currPos);
-
- n = _stream.Read(_charBytes, 0, readLength);
- if (n == 0)
- {
- throw Error.GetEndOfFile();
- }
-
- charsRead = _decoder.GetChars(_charBytes, 0, n, _charBuffer, 0);
-
- if (currPos == 0 && n == stringLength)
- {
- return new string(_charBuffer, 0, charsRead);
- }
-
- sb ??= StringBuilderCache.Acquire(stringLength); // Actual string length in chars may be smaller.
- sb.Append(_charBuffer, 0, charsRead);
- currPos += n;
- } while (currPos < stringLength);
-
- return StringBuilderCache.GetStringAndRelease(sb);
- }
-
- public virtual int Read(char[] buffer, int index, int count)
- {
- if (buffer == null)
- {
- throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
- }
- if (index < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (count < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (buffer.Length - index < count)
- {
- throw new ArgumentException(SR.Argument_InvalidOffLen);
- }
- ThrowIfDisposed();
-
- return InternalReadChars(new Span<char>(buffer, index, count));
- }
-
- public virtual int Read(Span<char> buffer)
- {
- ThrowIfDisposed();
- return InternalReadChars(buffer);
- }
-
- private int InternalReadChars(Span<char> buffer)
- {
- Debug.Assert(!_disposed);
-
- int totalCharsRead = 0;
-
- while (!buffer.IsEmpty)
- {
- int numBytes = buffer.Length;
-
- // We really want to know what the minimum number of bytes per char
- // is for our encoding. Otherwise for UnicodeEncoding we'd have to
- // do ~1+log(n) reads to read n characters.
- if (_2BytesPerChar)
- {
- numBytes <<= 1;
- }
-
- // We do not want to read even a single byte more than necessary.
- //
- // Subtract pending bytes that the decoder may be holding onto. This assumes that each
- // decoded char corresponds to one or more bytes. Note that custom encodings or encodings with
- // a custom replacement sequence may violate this assumption.
- if (numBytes > 1)
- {
- DecoderNLS? decoder = _decoder as DecoderNLS;
- // For internal decoders, we can check whether the decoder has any pending state.
- // For custom decoders, assume that the decoder has pending state.
- if (decoder == null || decoder.HasState)
- {
- numBytes--;
-
- // The worst case is charsRemaining = 2 and UTF32Decoder holding onto 3 pending bytes. We need to read just
- // one byte in this case.
- if (_2BytesPerChar && numBytes > 2)
- numBytes -= 2;
- }
- }
-
- ReadOnlySpan<byte> byteBuffer;
- if (_isMemoryStream)
- {
- Debug.Assert(_stream is MemoryStream);
- MemoryStream mStream = (MemoryStream)_stream;
-
- int position = mStream.InternalGetPosition();
- numBytes = mStream.InternalEmulateRead(numBytes);
- byteBuffer = new ReadOnlySpan<byte>(mStream.InternalGetBuffer(), position, numBytes);
- }
- else
- {
- _charBytes ??= new byte[MaxCharBytesSize];
-
- if (numBytes > MaxCharBytesSize)
- {
- numBytes = MaxCharBytesSize;
- }
-
- numBytes = _stream.Read(_charBytes, 0, numBytes);
- byteBuffer = new ReadOnlySpan<byte>(_charBytes, 0, numBytes);
- }
-
- if (byteBuffer.IsEmpty)
- {
- break;
- }
-
- int charsRead = _decoder.GetChars(byteBuffer, buffer, flush: false);
- buffer = buffer.Slice(charsRead);
-
- totalCharsRead += charsRead;
- }
-
- // we may have read fewer than the number of characters requested if end of stream reached
- // or if the encoding makes the char count too big for the buffer (e.g. fallback sequence)
- return totalCharsRead;
- }
-
- public virtual char[] ReadChars(int count)
- {
- if (count < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- ThrowIfDisposed();
-
- if (count == 0)
- {
- return Array.Empty<char>();
- }
-
- char[] chars = new char[count];
- int n = InternalReadChars(new Span<char>(chars));
- if (n != count)
- {
- char[] copy = new char[n];
- Buffer.BlockCopy(chars, 0, copy, 0, 2 * n); // sizeof(char)
- chars = copy;
- }
-
- return chars;
- }
-
- public virtual int Read(byte[] buffer, int index, int count)
- {
- if (buffer == null)
- {
- throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
- }
- if (index < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (count < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (buffer.Length - index < count)
- {
- throw new ArgumentException(SR.Argument_InvalidOffLen);
- }
- ThrowIfDisposed();
-
- return _stream.Read(buffer, index, count);
- }
-
- public virtual int Read(Span<byte> buffer)
- {
- ThrowIfDisposed();
- return _stream.Read(buffer);
- }
-
- public virtual byte[] ReadBytes(int count)
- {
- if (count < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- ThrowIfDisposed();
-
- if (count == 0)
- {
- return Array.Empty<byte>();
- }
-
- byte[] result = new byte[count];
- int numRead = 0;
- do
- {
- int n = _stream.Read(result, numRead, count);
- if (n == 0)
- {
- break;
- }
-
- numRead += n;
- count -= n;
- } while (count > 0);
-
- if (numRead != result.Length)
- {
- // Trim array. This should happen on EOF & possibly net streams.
- byte[] copy = new byte[numRead];
- Buffer.BlockCopy(result, 0, copy, 0, numRead);
- result = copy;
- }
-
- return result;
- }
-
- private ReadOnlySpan<byte> InternalRead(int numBytes)
- {
- Debug.Assert(numBytes >= 2 && numBytes <= 16, "value of 1 should use ReadByte. value > 16 requires to change the minimal _buffer size");
-
- if (_isMemoryStream)
- {
- // read directly from MemoryStream buffer
- Debug.Assert(_stream is MemoryStream);
- return ((MemoryStream)_stream).InternalReadSpan(numBytes);
- }
- else
- {
- ThrowIfDisposed();
-
- int bytesRead = 0;
- do
- {
- int n = _stream.Read(_buffer, bytesRead, numBytes - bytesRead);
- if (n == 0)
- {
- throw Error.GetEndOfFile();
- }
- bytesRead += n;
- } while (bytesRead < numBytes);
-
- return _buffer;
- }
- }
-
- // FillBuffer is not performing well when reading from MemoryStreams as it is using the public Stream interface.
- // We introduced new function InternalRead which can work directly on the MemoryStream internal buffer or using the public Stream
- // interface when working with all other streams. This function is not needed anymore but we decided not to delete it for compatibility
- // reasons. More about the subject in: https://github.com/dotnet/coreclr/pull/22102
- protected virtual void FillBuffer(int numBytes)
- {
- if (numBytes < 0 || numBytes > _buffer.Length)
- {
- throw new ArgumentOutOfRangeException(nameof(numBytes), SR.ArgumentOutOfRange_BinaryReaderFillBuffer);
- }
-
- int bytesRead = 0;
- int n = 0;
-
- ThrowIfDisposed();
-
- // Need to find a good threshold for calling ReadByte() repeatedly
- // vs. calling Read(byte[], int, int) for both buffered & unbuffered
- // streams.
- if (numBytes == 1)
- {
- n = _stream.ReadByte();
- if (n == -1)
- {
- throw Error.GetEndOfFile();
- }
-
- _buffer[0] = (byte)n;
- return;
- }
-
- do
- {
- n = _stream.Read(_buffer, bytesRead, numBytes - bytesRead);
- if (n == 0)
- {
- throw Error.GetEndOfFile();
- }
- bytesRead += n;
- } while (bytesRead < numBytes);
- }
-
- protected internal int Read7BitEncodedInt()
- {
- // Read out an Int32 7 bits at a time. The high bit
- // of the byte when on means to continue reading more bytes.
- int count = 0;
- int shift = 0;
- byte b;
- do
- {
- // Check for a corrupted stream. Read a max of 5 bytes.
- // In a future version, add a DataFormatException.
- if (shift == 5 * 7) // 5 bytes max per Int32, shift += 7
- {
- throw new FormatException(SR.Format_Bad7BitInt32);
- }
-
- // ReadByte handles end of stream cases for us.
- b = ReadByte();
- count |= (b & 0x7F) << shift;
- shift += 7;
- } while ((b & 0x80) != 0);
- return count;
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/BinaryWriter.cs b/netcore/System.Private.CoreLib/shared/System/IO/BinaryWriter.cs
deleted file mode 100644
index fde451656a5..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/BinaryWriter.cs
+++ /dev/null
@@ -1,469 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Text;
-using System.Diagnostics;
-using System.Buffers;
-using System.Threading.Tasks;
-
-namespace System.IO
-{
- // This abstract base class represents a writer that can write
- // primitives to an arbitrary stream. A subclass can override methods to
- // give unique encodings.
- //
- public class BinaryWriter : IDisposable, IAsyncDisposable
- {
- public static readonly BinaryWriter Null = new BinaryWriter();
-
- protected Stream OutStream;
- private readonly byte[] _buffer; // temp space for writing primitives to.
- private readonly Encoding _encoding;
- private readonly Encoder _encoder;
-
- private readonly bool _leaveOpen;
-
- // Perf optimization stuff
- private byte[]? _largeByteBuffer; // temp space for writing chars.
- private int _maxChars; // max # of chars we can put in _largeByteBuffer
- // Size should be around the max number of chars/string * Encoding's max bytes/char
- private const int LargeByteBufferSize = 256;
-
- // Protected default constructor that sets the output stream
- // to a null stream (a bit bucket).
- protected BinaryWriter()
- {
- OutStream = Stream.Null;
- _buffer = new byte[16];
- _encoding = EncodingCache.UTF8NoBOM;
- _encoder = _encoding.GetEncoder();
- }
-
- public BinaryWriter(Stream output) : this(output, EncodingCache.UTF8NoBOM, false)
- {
- }
-
- public BinaryWriter(Stream output, Encoding encoding) : this(output, encoding, false)
- {
- }
-
- public BinaryWriter(Stream output, Encoding encoding, bool leaveOpen)
- {
- if (output == null)
- throw new ArgumentNullException(nameof(output));
- if (encoding == null)
- throw new ArgumentNullException(nameof(encoding));
- if (!output.CanWrite)
- throw new ArgumentException(SR.Argument_StreamNotWritable);
-
- OutStream = output;
- _buffer = new byte[16];
- _encoding = encoding;
- _encoder = _encoding.GetEncoder();
- _leaveOpen = leaveOpen;
- }
-
- // Closes this writer and releases any system resources associated with the
- // writer. Following a call to Close, any operations on the writer
- // may raise exceptions.
- public virtual void Close()
- {
- Dispose(true);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- if (disposing)
- {
- if (_leaveOpen)
- OutStream.Flush();
- else
- OutStream.Close();
- }
- }
-
- public void Dispose()
- {
- Dispose(true);
- }
-
- public virtual ValueTask DisposeAsync()
- {
- try
- {
- if (GetType() == typeof(BinaryWriter))
- {
- if (_leaveOpen)
- {
- return new ValueTask(OutStream.FlushAsync());
- }
-
- OutStream.Close();
- }
- else
- {
- // Since this is a derived BinaryWriter, delegate to whatever logic
- // the derived implementation already has in Dispose.
- Dispose();
- }
-
- return default;
- }
- catch (Exception exc)
- {
- return new ValueTask(Task.FromException(exc));
- }
- }
-
- // Returns the stream associated with the writer. It flushes all pending
- // writes before returning. All subclasses should override Flush to
- // ensure that all buffered data is sent to the stream.
- public virtual Stream BaseStream
- {
- get
- {
- Flush();
- return OutStream;
- }
- }
-
- // Clears all buffers for this writer and causes any buffered data to be
- // written to the underlying device.
- public virtual void Flush()
- {
- OutStream.Flush();
- }
-
- public virtual long Seek(int offset, SeekOrigin origin)
- {
- return OutStream.Seek(offset, origin);
- }
-
- // Writes a boolean to this stream. A single byte is written to the stream
- // with the value 0 representing false or the value 1 representing true.
- //
- public virtual void Write(bool value)
- {
- _buffer[0] = (byte)(value ? 1 : 0);
- OutStream.Write(_buffer, 0, 1);
- }
-
- // Writes a byte to this stream. The current position of the stream is
- // advanced by one.
- //
- public virtual void Write(byte value)
- {
- OutStream.WriteByte(value);
- }
-
- // Writes a signed byte to this stream. The current position of the stream
- // is advanced by one.
- //
- [CLSCompliant(false)]
- public virtual void Write(sbyte value)
- {
- OutStream.WriteByte((byte)value);
- }
-
- // Writes a byte array to this stream.
- //
- // This default implementation calls the Write(Object, int, int)
- // method to write the byte array.
- //
- public virtual void Write(byte[] buffer)
- {
- if (buffer == null)
- throw new ArgumentNullException(nameof(buffer));
- OutStream.Write(buffer, 0, buffer.Length);
- }
-
- // Writes a section of a byte array to this stream.
- //
- // This default implementation calls the Write(Object, int, int)
- // method to write the byte array.
- //
- public virtual void Write(byte[] buffer, int index, int count)
- {
- OutStream.Write(buffer, index, count);
- }
-
-
- // Writes a character to this stream. The current position of the stream is
- // advanced by two.
- // Note this method cannot handle surrogates properly in UTF-8.
- //
- public virtual unsafe void Write(char ch)
- {
- if (char.IsSurrogate(ch))
- throw new ArgumentException(SR.Arg_SurrogatesNotAllowedAsSingleChar);
-
- Debug.Assert(_encoding.GetMaxByteCount(1) <= 16, "_encoding.GetMaxByteCount(1) <= 16)");
- int numBytes = 0;
- fixed (byte* pBytes = &_buffer[0])
- {
- numBytes = _encoder.GetBytes(&ch, 1, pBytes, _buffer.Length, flush: true);
- }
- OutStream.Write(_buffer, 0, numBytes);
- }
-
- // Writes a character array to this stream.
- //
- // This default implementation calls the Write(Object, int, int)
- // method to write the character array.
- //
- public virtual void Write(char[] chars)
- {
- if (chars == null)
- throw new ArgumentNullException(nameof(chars));
-
- byte[] bytes = _encoding.GetBytes(chars, 0, chars.Length);
- OutStream.Write(bytes, 0, bytes.Length);
- }
-
- // Writes a section of a character array to this stream.
- //
- // This default implementation calls the Write(Object, int, int)
- // method to write the character array.
- //
- public virtual void Write(char[] chars, int index, int count)
- {
- byte[] bytes = _encoding.GetBytes(chars, index, count);
- OutStream.Write(bytes, 0, bytes.Length);
- }
-
-
- // Writes a double to this stream. The current position of the stream is
- // advanced by eight.
- //
- public virtual unsafe void Write(double value)
- {
- ulong TmpValue = *(ulong*)&value;
- _buffer[0] = (byte)TmpValue;
- _buffer[1] = (byte)(TmpValue >> 8);
- _buffer[2] = (byte)(TmpValue >> 16);
- _buffer[3] = (byte)(TmpValue >> 24);
- _buffer[4] = (byte)(TmpValue >> 32);
- _buffer[5] = (byte)(TmpValue >> 40);
- _buffer[6] = (byte)(TmpValue >> 48);
- _buffer[7] = (byte)(TmpValue >> 56);
- OutStream.Write(_buffer, 0, 8);
- }
-
- public virtual void Write(decimal value)
- {
- decimal.GetBytes(value, _buffer);
- OutStream.Write(_buffer, 0, 16);
- }
-
- // Writes a two-byte signed integer to this stream. The current position of
- // the stream is advanced by two.
- //
- public virtual void Write(short value)
- {
- _buffer[0] = (byte)value;
- _buffer[1] = (byte)(value >> 8);
- OutStream.Write(_buffer, 0, 2);
- }
-
- // Writes a two-byte unsigned integer to this stream. The current position
- // of the stream is advanced by two.
- //
- [CLSCompliant(false)]
- public virtual void Write(ushort value)
- {
- _buffer[0] = (byte)value;
- _buffer[1] = (byte)(value >> 8);
- OutStream.Write(_buffer, 0, 2);
- }
-
- // Writes a four-byte signed integer to this stream. The current position
- // of the stream is advanced by four.
- //
- public virtual void Write(int value)
- {
- _buffer[0] = (byte)value;
- _buffer[1] = (byte)(value >> 8);
- _buffer[2] = (byte)(value >> 16);
- _buffer[3] = (byte)(value >> 24);
- OutStream.Write(_buffer, 0, 4);
- }
-
- // Writes a four-byte unsigned integer to this stream. The current position
- // of the stream is advanced by four.
- //
- [CLSCompliant(false)]
- public virtual void Write(uint value)
- {
- _buffer[0] = (byte)value;
- _buffer[1] = (byte)(value >> 8);
- _buffer[2] = (byte)(value >> 16);
- _buffer[3] = (byte)(value >> 24);
- OutStream.Write(_buffer, 0, 4);
- }
-
- // Writes an eight-byte signed integer to this stream. The current position
- // of the stream is advanced by eight.
- //
- public virtual void Write(long value)
- {
- _buffer[0] = (byte)value;
- _buffer[1] = (byte)(value >> 8);
- _buffer[2] = (byte)(value >> 16);
- _buffer[3] = (byte)(value >> 24);
- _buffer[4] = (byte)(value >> 32);
- _buffer[5] = (byte)(value >> 40);
- _buffer[6] = (byte)(value >> 48);
- _buffer[7] = (byte)(value >> 56);
- OutStream.Write(_buffer, 0, 8);
- }
-
- // Writes an eight-byte unsigned integer to this stream. The current
- // position of the stream is advanced by eight.
- //
- [CLSCompliant(false)]
- public virtual void Write(ulong value)
- {
- _buffer[0] = (byte)value;
- _buffer[1] = (byte)(value >> 8);
- _buffer[2] = (byte)(value >> 16);
- _buffer[3] = (byte)(value >> 24);
- _buffer[4] = (byte)(value >> 32);
- _buffer[5] = (byte)(value >> 40);
- _buffer[6] = (byte)(value >> 48);
- _buffer[7] = (byte)(value >> 56);
- OutStream.Write(_buffer, 0, 8);
- }
-
- // Writes a float to this stream. The current position of the stream is
- // advanced by four.
- //
- public virtual unsafe void Write(float value)
- {
- uint TmpValue = *(uint*)&value;
- _buffer[0] = (byte)TmpValue;
- _buffer[1] = (byte)(TmpValue >> 8);
- _buffer[2] = (byte)(TmpValue >> 16);
- _buffer[3] = (byte)(TmpValue >> 24);
- OutStream.Write(_buffer, 0, 4);
- }
-
-
- // Writes a length-prefixed string to this stream in the BinaryWriter's
- // current Encoding. This method first writes the length of the string as
- // a four-byte unsigned integer, and then writes that many characters
- // to the stream.
- //
- public virtual unsafe void Write(string value)
- {
- if (value == null)
- throw new ArgumentNullException(nameof(value));
-
- int len = _encoding.GetByteCount(value);
- Write7BitEncodedInt(len);
-
- if (_largeByteBuffer == null)
- {
- _largeByteBuffer = new byte[LargeByteBufferSize];
- _maxChars = _largeByteBuffer.Length / _encoding.GetMaxByteCount(1);
- }
-
- if (len <= _largeByteBuffer.Length)
- {
- _encoding.GetBytes(value, 0, value.Length, _largeByteBuffer, 0);
- OutStream.Write(_largeByteBuffer, 0, len);
- }
- else
- {
- // Aggressively try to not allocate memory in this loop for
- // runtime performance reasons. Use an Encoder to write out
- // the string correctly (handling surrogates crossing buffer
- // boundaries properly).
- int charStart = 0;
- int numLeft = value.Length;
-#if DEBUG
- int totalBytes = 0;
-#endif
- while (numLeft > 0)
- {
- // Figure out how many chars to process this round.
- int charCount = (numLeft > _maxChars) ? _maxChars : numLeft;
- int byteLen;
-
- checked
- {
- if (charStart < 0 || charCount < 0 || charStart > value.Length - charCount)
- {
- throw new ArgumentOutOfRangeException(nameof(charCount));
- }
- fixed (char* pChars = value)
- {
- fixed (byte* pBytes = &_largeByteBuffer[0])
- {
- byteLen = _encoder.GetBytes(pChars + charStart, charCount, pBytes, _largeByteBuffer.Length, charCount == numLeft);
- }
- }
- }
-#if DEBUG
- totalBytes += byteLen;
- Debug.Assert(totalBytes <= len && byteLen <= _largeByteBuffer.Length, "BinaryWriter::Write(String) - More bytes encoded than expected!");
-#endif
- OutStream.Write(_largeByteBuffer, 0, byteLen);
- charStart += charCount;
- numLeft -= charCount;
- }
-#if DEBUG
- Debug.Assert(totalBytes == len, "BinaryWriter::Write(String) - Didn't write out all the bytes!");
-#endif
- }
- }
-
- public virtual void Write(ReadOnlySpan<byte> buffer)
- {
- if (GetType() == typeof(BinaryWriter))
- {
- OutStream.Write(buffer);
- }
- else
- {
- byte[] array = ArrayPool<byte>.Shared.Rent(buffer.Length);
- try
- {
- buffer.CopyTo(array);
- Write(array, 0, buffer.Length);
- }
- finally
- {
- ArrayPool<byte>.Shared.Return(array);
- }
- }
- }
-
- public virtual void Write(ReadOnlySpan<char> chars)
- {
- byte[] bytes = ArrayPool<byte>.Shared.Rent(_encoding.GetMaxByteCount(chars.Length));
- try
- {
- int bytesWritten = _encoding.GetBytes(chars, bytes);
- Write(bytes, 0, bytesWritten);
- }
- finally
- {
- ArrayPool<byte>.Shared.Return(bytes);
- }
- }
-
- protected void Write7BitEncodedInt(int value)
- {
- // Write out an int 7 bits at a time. The high bit of the byte,
- // when on, tells reader to continue reading more bytes.
- uint v = (uint)value; // support negative numbers
- while (v >= 0x80)
- {
- Write((byte)(v | 0x80));
- v >>= 7;
- }
- Write((byte)v);
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/DirectoryNotFoundException.cs b/netcore/System.Private.CoreLib/shared/System/IO/DirectoryNotFoundException.cs
deleted file mode 100644
index 2b0f5026919..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/DirectoryNotFoundException.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Runtime.Serialization;
-
-namespace System.IO
-{
- /*
- * Thrown when trying to access a directory that doesn't exist on disk.
- * From COM Interop, this exception is thrown for 2 HRESULTS:
- * the Win32 errorcode-as-HRESULT ERROR_PATH_NOT_FOUND (0x80070003)
- * and STG_E_PATHNOTFOUND (0x80030003).
- */
- [Serializable]
- [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
- public class DirectoryNotFoundException : IOException
- {
- public DirectoryNotFoundException()
- : base(SR.Arg_DirectoryNotFoundException)
- {
- HResult = HResults.COR_E_DIRECTORYNOTFOUND;
- }
-
- public DirectoryNotFoundException(string? message)
- : base(message)
- {
- HResult = HResults.COR_E_DIRECTORYNOTFOUND;
- }
-
- public DirectoryNotFoundException(string? message, Exception? innerException)
- : base(message, innerException)
- {
- HResult = HResults.COR_E_DIRECTORYNOTFOUND;
- }
-
- protected DirectoryNotFoundException(SerializationInfo info, StreamingContext context)
- : base(info, context)
- {
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/DisableMediaInsertionPrompt.cs b/netcore/System.Private.CoreLib/shared/System/IO/DisableMediaInsertionPrompt.cs
deleted file mode 100644
index d14043c8304..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/DisableMediaInsertionPrompt.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-#if MS_IO_REDIST
-using System;
-
-namespace Microsoft.IO
-#else
-namespace System.IO
-#endif
-{
- /// <summary>
- /// Simple wrapper to safely disable the normal media insertion prompt for
- /// removable media (floppies, cds, memory cards, etc.)
- /// </summary>
- /// <remarks>
- /// Note that removable media file systems lazily load. After starting the OS
- /// they won't be loaded until you have media in the drive- and as such the
- /// prompt won't happen. You have to have had media in at least once to get
- /// the file system to load and then have removed it.
- /// </remarks>
- internal struct DisableMediaInsertionPrompt : IDisposable
- {
- private bool _disableSuccess;
- private uint _oldMode;
-
- public static DisableMediaInsertionPrompt Create()
- {
- DisableMediaInsertionPrompt prompt = default;
- prompt._disableSuccess = Interop.Kernel32.SetThreadErrorMode(Interop.Kernel32.SEM_FAILCRITICALERRORS, out prompt._oldMode);
- return prompt;
- }
-
- public void Dispose()
- {
- if (_disableSuccess)
- Interop.Kernel32.SetThreadErrorMode(_oldMode, out _);
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/DriveInfoInternal.Unix.cs b/netcore/System.Private.CoreLib/shared/System/IO/DriveInfoInternal.Unix.cs
deleted file mode 100644
index 78ef95704fe..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/DriveInfoInternal.Unix.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Diagnostics;
-using System.Text;
-
-namespace System.IO
-{
- /// <summary>Contains internal volume helpers that are shared between many projects.</summary>
- internal static partial class DriveInfoInternal
- {
- internal static string[] GetLogicalDrives() => Interop.Sys.GetAllMountPoints();
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/DriveInfoInternal.Windows.cs b/netcore/System.Private.CoreLib/shared/System/IO/DriveInfoInternal.Windows.cs
deleted file mode 100644
index c811a876920..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/DriveInfoInternal.Windows.cs
+++ /dev/null
@@ -1,87 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-#nullable enable
-using System.Diagnostics;
-
-namespace System.IO
-{
- /// <summary>Contains internal volume helpers that are shared between many projects.</summary>
- internal static partial class DriveInfoInternal
- {
- public static string[] GetLogicalDrives()
- {
- int drives = Interop.Kernel32.GetLogicalDrives();
- if (drives == 0)
- {
- throw Win32Marshal.GetExceptionForLastWin32Error();
- }
-
- // GetLogicalDrives returns a bitmask starting from
- // position 0 "A" indicating whether a drive is present.
- // Loop over each bit, creating a string for each one
- // that is set.
-
- uint d = (uint)drives;
- int count = 0;
- while (d != 0)
- {
- if (((int)d & 1) != 0) count++;
- d >>= 1;
- }
-
- string[] result = new string[count];
- Span<char> root = stackalloc char[] { 'A', ':', '\\' };
- d = (uint)drives;
- count = 0;
- while (d != 0)
- {
- if (((int)d & 1) != 0)
- {
- result[count++] = root.ToString();
- }
- d >>= 1;
- root[0]++;
- }
- return result;
- }
-
- public static string NormalizeDriveName(string driveName)
- {
- Debug.Assert(driveName != null);
-
- string? name;
-
- if (driveName.Length == 1)
- {
- name = driveName + ":\\";
- }
- else
- {
- name = Path.GetPathRoot(driveName);
- // Disallow null or empty drive letters and UNC paths
- if (string.IsNullOrEmpty(name) || name.StartsWith("\\\\", StringComparison.Ordinal))
- {
- throw new ArgumentException(SR.Arg_MustBeDriveLetterOrRootDir, nameof(driveName));
- }
- }
- // We want to normalize to have a trailing backslash so we don't have two equivalent forms and
- // because some Win32 API don't work without it.
- if (name.Length == 2 && name[1] == ':')
- {
- name += "\\";
- }
-
- // Now verify that the drive letter could be a real drive name.
- // On Windows this means it's between A and Z, ignoring case.
- char letter = driveName[0];
- if (!((letter >= 'A' && letter <= 'Z') || (letter >= 'a' && letter <= 'z')))
- {
- throw new ArgumentException(SR.Arg_MustBeDriveLetterOrRootDir, nameof(driveName));
- }
-
- return name;
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/EncodingCache.cs b/netcore/System.Private.CoreLib/shared/System/IO/EncodingCache.cs
deleted file mode 100644
index 53379bc77f3..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/EncodingCache.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Text;
-
-namespace System.IO
-{
- internal static class EncodingCache
- {
- internal static readonly Encoding UTF8NoBOM = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/EndOfStreamException.cs b/netcore/System.Private.CoreLib/shared/System/IO/EndOfStreamException.cs
deleted file mode 100644
index c965b6a1c20..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/EndOfStreamException.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Runtime.Serialization;
-
-namespace System.IO
-{
- [Serializable]
- [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
- public class EndOfStreamException : IOException
- {
- public EndOfStreamException()
- : base(SR.Arg_EndOfStreamException)
- {
- HResult = HResults.COR_E_ENDOFSTREAM;
- }
-
- public EndOfStreamException(string? message)
- : base(message)
- {
- HResult = HResults.COR_E_ENDOFSTREAM;
- }
-
- public EndOfStreamException(string? message, Exception? innerException)
- : base(message, innerException)
- {
- HResult = HResults.COR_E_ENDOFSTREAM;
- }
-
- protected EndOfStreamException(SerializationInfo info, StreamingContext context)
- : base(info, context)
- {
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/Error.cs b/netcore/System.Private.CoreLib/shared/System/IO/Error.cs
deleted file mode 100644
index fd39f166cf9..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/Error.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-namespace System.IO
-{
- /// <summary>
- /// Provides centralized methods for creating exceptions for System.IO.FileSystem.
- /// </summary>
- internal static class Error
- {
- internal static Exception GetStreamIsClosed()
- {
- return new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed);
- }
-
- internal static Exception GetEndOfFile()
- {
- return new EndOfStreamException(SR.IO_EOF_ReadBeyondEOF);
- }
-
- internal static Exception GetFileNotOpen()
- {
- return new ObjectDisposedException(null, SR.ObjectDisposed_FileClosed);
- }
-
- internal static Exception GetReadNotSupported()
- {
- return new NotSupportedException(SR.NotSupported_UnreadableStream);
- }
-
- internal static Exception GetSeekNotSupported()
- {
- return new NotSupportedException(SR.NotSupported_UnseekableStream);
- }
-
- internal static Exception GetWriteNotSupported()
- {
- return new NotSupportedException(SR.NotSupported_UnwritableStream);
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/FileAccess.cs b/netcore/System.Private.CoreLib/shared/System/IO/FileAccess.cs
deleted file mode 100644
index c0b75374522..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/FileAccess.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-namespace System.IO
-{
- // Contains constants for specifying the access you want for a file.
- // You can have Read, Write or ReadWrite access.
- //
- [Flags]
- public enum FileAccess
- {
- // Specifies read access to the file. Data can be read from the file and
- // the file pointer can be moved. Combine with WRITE for read-write access.
- Read = 1,
-
- // Specifies write access to the file. Data can be written to the file and
- // the file pointer can be moved. Combine with READ for read-write access.
- Write = 2,
-
- // Specifies read and write access to the file. Data can be written to the
- // file and the file pointer can be moved. Data can also be read from the
- // file.
- ReadWrite = 3,
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/FileLoadException.cs b/netcore/System.Private.CoreLib/shared/System/IO/FileLoadException.cs
deleted file mode 100644
index f7ef7c4c050..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/FileLoadException.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Runtime.Serialization;
-
-namespace System.IO
-{
- [Serializable]
- [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
- public partial class FileLoadException : IOException
- {
- public FileLoadException()
- : base(SR.IO_FileLoad)
- {
- HResult = HResults.COR_E_FILELOAD;
- }
-
- public FileLoadException(string? message)
- : base(message)
- {
- HResult = HResults.COR_E_FILELOAD;
- }
-
- public FileLoadException(string? message, Exception? inner)
- : base(message, inner)
- {
- HResult = HResults.COR_E_FILELOAD;
- }
-
- public FileLoadException(string? message, string? fileName) : base(message)
- {
- HResult = HResults.COR_E_FILELOAD;
- FileName = fileName;
- }
-
- public FileLoadException(string? message, string? fileName, Exception? inner)
- : base(message, inner)
- {
- HResult = HResults.COR_E_FILELOAD;
- FileName = fileName;
- }
-
- public override string Message => _message ??= FormatFileLoadExceptionMessage(FileName, HResult);
-
- public string? FileName { get; }
- public string? FusionLog { get; }
-
- public override string ToString()
- {
- string s = GetType().ToString() + ": " + Message;
-
- if (!string.IsNullOrEmpty(FileName))
- s += Environment.NewLineConst + SR.Format(SR.IO_FileName_Name, FileName);
-
- if (InnerException != null)
- s += Environment.NewLineConst + InnerExceptionPrefix + InnerException.ToString();
-
- if (StackTrace != null)
- s += Environment.NewLineConst + StackTrace;
-
- if (FusionLog != null)
- {
- s ??= " ";
- s += Environment.NewLineConst + Environment.NewLineConst + FusionLog;
- }
-
- return s;
- }
-
- protected FileLoadException(SerializationInfo info, StreamingContext context)
- : base(info, context)
- {
- FileName = info.GetString("FileLoad_FileName");
- FusionLog = info.GetString("FileLoad_FusionLog");
- }
-
- public override void GetObjectData(SerializationInfo info, StreamingContext context)
- {
- base.GetObjectData(info, context);
- info.AddValue("FileLoad_FileName", FileName, typeof(string));
- info.AddValue("FileLoad_FusionLog", FusionLog, typeof(string));
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/FileMode.cs b/netcore/System.Private.CoreLib/shared/System/IO/FileMode.cs
deleted file mode 100644
index 83f48229958..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/FileMode.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-namespace System.IO
-{
- // Contains constants for specifying how the OS should open a file.
- // These will control whether you overwrite a file, open an existing
- // file, or some combination thereof.
- //
- // To append to a file, use Append (which maps to OpenOrCreate then we seek
- // to the end of the file). To truncate a file or create it if it doesn't
- // exist, use Create.
- //
- public enum FileMode
- {
- // Creates a new file. An exception is raised if the file already exists.
- CreateNew = 1,
-
- // Creates a new file. If the file already exists, it is overwritten.
- Create = 2,
-
- // Opens an existing file. An exception is raised if the file does not exist.
- Open = 3,
-
- // Opens the file if it exists. Otherwise, creates a new file.
- OpenOrCreate = 4,
-
- // Opens an existing file. Once opened, the file is truncated so that its
- // size is zero bytes. The calling process must open the file with at least
- // WRITE access. An exception is raised if the file does not exist.
- Truncate = 5,
-
- // Opens the file if it exists and seeks to the end. Otherwise,
- // creates a new file.
- Append = 6,
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/FileNotFoundException.cs b/netcore/System.Private.CoreLib/shared/System/IO/FileNotFoundException.cs
deleted file mode 100644
index 48c513b7f37..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/FileNotFoundException.cs
+++ /dev/null
@@ -1,107 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Diagnostics;
-using System.Runtime.Serialization;
-
-namespace System.IO
-{
- // Thrown when trying to access a file that doesn't exist on disk.
- [Serializable]
- [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
- public partial class FileNotFoundException : IOException
- {
- public FileNotFoundException()
- : base(SR.IO_FileNotFound)
- {
- HResult = HResults.COR_E_FILENOTFOUND;
- }
-
- public FileNotFoundException(string? message)
- : base(message)
- {
- HResult = HResults.COR_E_FILENOTFOUND;
- }
-
- public FileNotFoundException(string? message, Exception? innerException)
- : base(message, innerException)
- {
- HResult = HResults.COR_E_FILENOTFOUND;
- }
-
- public FileNotFoundException(string? message, string? fileName)
- : base(message)
- {
- HResult = HResults.COR_E_FILENOTFOUND;
- FileName = fileName;
- }
-
- public FileNotFoundException(string? message, string? fileName, Exception? innerException)
- : base(message, innerException)
- {
- HResult = HResults.COR_E_FILENOTFOUND;
- FileName = fileName;
- }
-
- public override string Message
- {
- get
- {
- SetMessageField();
- Debug.Assert(_message != null, "_message was null after calling SetMessageField");
- return _message;
- }
- }
-
- private void SetMessageField()
- {
- if (_message == null)
- {
- if ((FileName == null) &&
- (HResult == System.HResults.COR_E_EXCEPTION))
- _message = SR.IO_FileNotFound;
- else if (FileName != null)
- _message = FileLoadException.FormatFileLoadExceptionMessage(FileName, HResult);
- }
- }
-
- public string? FileName { get; }
- public string? FusionLog { get; }
-
- public override string ToString()
- {
- string s = GetType().ToString() + ": " + Message;
-
- if (!string.IsNullOrEmpty(FileName))
- s += Environment.NewLineConst + SR.Format(SR.IO_FileName_Name, FileName);
-
- if (InnerException != null)
- s += Environment.NewLineConst + InnerExceptionPrefix + InnerException.ToString();
-
- if (StackTrace != null)
- s += Environment.NewLineConst + StackTrace;
-
- if (FusionLog != null)
- {
- s ??= " ";
- s += Environment.NewLineConst + Environment.NewLineConst + FusionLog;
- }
- return s;
- }
-
- protected FileNotFoundException(SerializationInfo info, StreamingContext context)
- : base(info, context)
- {
- FileName = info.GetString("FileNotFound_FileName");
- FusionLog = info.GetString("FileNotFound_FusionLog");
- }
-
- public override void GetObjectData(SerializationInfo info, StreamingContext context)
- {
- base.GetObjectData(info, context);
- info.AddValue("FileNotFound_FileName", FileName, typeof(string));
- info.AddValue("FileNotFound_FusionLog", FusionLog, typeof(string));
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/FileOptions.cs b/netcore/System.Private.CoreLib/shared/System/IO/FileOptions.cs
deleted file mode 100644
index eed14c86b05..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/FileOptions.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-namespace System.IO
-{
- // Maps to FILE_FLAG_DELETE_ON_CLOSE and similar values from winbase.h.
- // We didn't expose a number of these values because we didn't believe
- // a number of them made sense in managed code, at least not yet.
- [Flags]
- public enum FileOptions
- {
- // NOTE: any change to FileOptions enum needs to be
- // matched in the FileStream ctor for error validation
- None = 0,
- WriteThrough = unchecked((int)0x80000000),
- Asynchronous = unchecked((int)0x40000000), // FILE_FLAG_OVERLAPPED
- // NoBuffering = 0x20000000,
- RandomAccess = 0x10000000,
- DeleteOnClose = 0x04000000,
- SequentialScan = 0x08000000,
- // AllowPosix = 0x01000000, // FILE_FLAG_POSIX_SEMANTICS
- // BackupOrRestore,
- // DisallowReparsePoint = 0x00200000, // FILE_FLAG_OPEN_REPARSE_POINT
- // NoRemoteRecall = 0x00100000, // FILE_FLAG_OPEN_NO_RECALL
- // FirstPipeInstance = 0x00080000, // FILE_FLAG_FIRST_PIPE_INSTANCE
- Encrypted = 0x00004000, // FILE_ATTRIBUTE_ENCRYPTED
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/FileShare.cs b/netcore/System.Private.CoreLib/shared/System/IO/FileShare.cs
deleted file mode 100644
index 24742a71b3f..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/FileShare.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-namespace System.IO
-{
- // Contains constants for controlling file sharing options while
- // opening files. You can specify what access other processes trying
- // to open the same file concurrently can have.
- //
- // Note these values currently match the values for FILE_SHARE_READ,
- // FILE_SHARE_WRITE, and FILE_SHARE_DELETE in winnt.h
- //
- [Flags]
- public enum FileShare
- {
- // No sharing. Any request to open the file (by this process or another
- // process) will fail until the file is closed.
- None = 0,
-
- // Allows subsequent opening of the file for reading. If this flag is not
- // specified, any request to open the file for reading (by this process or
- // another process) will fail until the file is closed.
- Read = 1,
-
- // Allows subsequent opening of the file for writing. If this flag is not
- // specified, any request to open the file for writing (by this process or
- // another process) will fail until the file is closed.
- Write = 2,
-
- // Allows subsequent opening of the file for writing or reading. If this flag
- // is not specified, any request to open the file for writing or reading (by
- // this process or another process) will fail until the file is closed.
- ReadWrite = 3,
-
- // Open the file, but allow someone else to delete the file.
- Delete = 4,
-
- // Whether the file handle should be inheritable by child processes.
- // Note this is not directly supported like this by Win32.
- Inheritable = 0x10,
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/FileStream.Linux.cs b/netcore/System.Private.CoreLib/shared/System/IO/FileStream.Linux.cs
deleted file mode 100644
index 873c4eb5599..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/FileStream.Linux.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using Microsoft.Win32.SafeHandles;
-using System.Diagnostics;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace System.IO
-{
- public partial class FileStream : Stream
- {
- /// <summary>Prevents other processes from reading from or writing to the FileStream.</summary>
- /// <param name="position">The beginning of the range to lock.</param>
- /// <param name="length">The range to be locked.</param>
- private void LockInternal(long position, long length)
- {
- CheckFileCall(Interop.Sys.LockFileRegion(_fileHandle, position, length, Interop.Sys.LockType.F_WRLCK));
- }
-
- /// <summary>Allows access by other processes to all or part of a file that was previously locked.</summary>
- /// <param name="position">The beginning of the range to unlock.</param>
- /// <param name="length">The range to be unlocked.</param>
- private void UnlockInternal(long position, long length)
- {
- CheckFileCall(Interop.Sys.LockFileRegion(_fileHandle, position, length, Interop.Sys.LockType.F_UNLCK));
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/FileStream.OSX.cs b/netcore/System.Private.CoreLib/shared/System/IO/FileStream.OSX.cs
deleted file mode 100644
index f29e9223373..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/FileStream.OSX.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-namespace System.IO
-{
- public partial class FileStream : Stream
- {
- private void LockInternal(long position, long length)
- {
- throw new PlatformNotSupportedException(SR.PlatformNotSupported_OSXFileLocking);
- }
-
- private void UnlockInternal(long position, long length)
- {
- throw new PlatformNotSupportedException(SR.PlatformNotSupported_OSXFileLocking);
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/FileStream.Unix.cs b/netcore/System.Private.CoreLib/shared/System/IO/FileStream.Unix.cs
deleted file mode 100644
index c1f0fcfb57f..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/FileStream.Unix.cs
+++ /dev/null
@@ -1,852 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using Microsoft.Win32.SafeHandles;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace System.IO
-{
- /// <summary>Provides an implementation of a file stream for Unix files.</summary>
- public partial class FileStream : Stream
- {
- /// <summary>File mode.</summary>
- private FileMode _mode;
-
- /// <summary>Advanced options requested when opening the file.</summary>
- private FileOptions _options;
-
- /// <summary>If the file was opened with FileMode.Append, the length of the file when opened; otherwise, -1.</summary>
- private long _appendStart = -1;
-
- /// <summary>
- /// Extra state used by the file stream when _useAsyncIO is true. This includes
- /// the semaphore used to serialize all operation, the buffer/offset/count provided by the
- /// caller for ReadAsync/WriteAsync operations, and the last successful task returned
- /// synchronously from ReadAsync which can be reused if the count matches the next request.
- /// Only initialized when <see cref="_useAsyncIO"/> is true.
- /// </summary>
- private AsyncState? _asyncState;
-
- /// <summary>Lazily-initialized value for whether the file supports seeking.</summary>
- private bool? _canSeek;
-
- private SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options)
- {
- // FileStream performs most of the general argument validation. We can assume here that the arguments
- // are all checked and consistent (e.g. non-null-or-empty path; valid enums in mode, access, share, and options; etc.)
- // Store the arguments
- _mode = mode;
- _options = options;
-
- if (_useAsyncIO)
- _asyncState = new AsyncState();
-
- // Translate the arguments into arguments for an open call.
- Interop.Sys.OpenFlags openFlags = PreOpenConfigurationFromOptions(mode, _access, share, options);
-
- // If the file gets created a new, we'll select the permissions for it. Most Unix utilities by default use 666 (read and
- // write for all), so we do the same (even though this doesn't match Windows, where by default it's possible to write out
- // a file and then execute it). No matter what we choose, it'll be subject to the umask applied by the system, such that the
- // actual permissions will typically be less than what we select here.
- const Interop.Sys.Permissions OpenPermissions =
- Interop.Sys.Permissions.S_IRUSR | Interop.Sys.Permissions.S_IWUSR |
- Interop.Sys.Permissions.S_IRGRP | Interop.Sys.Permissions.S_IWGRP |
- Interop.Sys.Permissions.S_IROTH | Interop.Sys.Permissions.S_IWOTH;
-
- // Open the file and store the safe handle.
- return SafeFileHandle.Open(_path!, openFlags, (int)OpenPermissions);
- }
-
- private static bool GetDefaultIsAsync(SafeFileHandle handle) => handle.IsAsync ?? DefaultIsAsync;
-
- /// <summary>Initializes a stream for reading or writing a Unix file.</summary>
- /// <param name="mode">How the file should be opened.</param>
- /// <param name="share">What other access to the file should be allowed. This is currently ignored.</param>
- private void Init(FileMode mode, FileShare share, string originalPath)
- {
- _fileHandle.IsAsync = _useAsyncIO;
-
- // Lock the file if requested via FileShare. This is only advisory locking. FileShare.None implies an exclusive
- // lock on the file and all other modes use a shared lock. While this is not as granular as Windows, not mandatory,
- // and not atomic with file opening, it's better than nothing.
- Interop.Sys.LockOperations lockOperation = (share == FileShare.None) ? Interop.Sys.LockOperations.LOCK_EX : Interop.Sys.LockOperations.LOCK_SH;
- if (Interop.Sys.FLock(_fileHandle, lockOperation | Interop.Sys.LockOperations.LOCK_NB) < 0)
- {
- // The only error we care about is EWOULDBLOCK, which indicates that the file is currently locked by someone
- // else and we would block trying to access it. Other errors, such as ENOTSUP (locking isn't supported) or
- // EACCES (the file system doesn't allow us to lock), will only hamper FileStream's usage without providing value,
- // given again that this is only advisory / best-effort.
- Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
- if (errorInfo.Error == Interop.Error.EWOULDBLOCK)
- {
- throw Interop.GetExceptionForIoErrno(errorInfo, _path, isDirectory: false);
- }
- }
-
- // These provide hints around how the file will be accessed. Specifying both RandomAccess
- // and Sequential together doesn't make sense as they are two competing options on the same spectrum,
- // so if both are specified, we prefer RandomAccess (behavior on Windows is unspecified if both are provided).
- Interop.Sys.FileAdvice fadv =
- (_options & FileOptions.RandomAccess) != 0 ? Interop.Sys.FileAdvice.POSIX_FADV_RANDOM :
- (_options & FileOptions.SequentialScan) != 0 ? Interop.Sys.FileAdvice.POSIX_FADV_SEQUENTIAL :
- 0;
- if (fadv != 0)
- {
- CheckFileCall(Interop.Sys.PosixFAdvise(_fileHandle, 0, 0, fadv),
- ignoreNotSupported: true); // just a hint.
- }
-
- if (_mode == FileMode.Append)
- {
- // Jump to the end of the file if opened as Append.
- _appendStart = SeekCore(_fileHandle, 0, SeekOrigin.End);
- }
- else if (mode == FileMode.Create || mode == FileMode.Truncate)
- {
- // Truncate the file now if the file mode requires it. This ensures that the file only will be truncated
- // if opened successfully.
- if (Interop.Sys.FTruncate(_fileHandle, 0) < 0)
- {
- Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
- if (errorInfo.Error != Interop.Error.EBADF && errorInfo.Error != Interop.Error.EINVAL)
- {
- // We know the file descriptor is valid and we know the size argument to FTruncate is correct,
- // so if EBADF or EINVAL is returned, it means we're dealing with a special file that can't be
- // truncated. Ignore the error in such cases; in all others, throw.
- throw Interop.GetExceptionForIoErrno(errorInfo, _path, isDirectory: false);
- }
- }
- }
- }
-
- /// <summary>Initializes a stream from an already open file handle (file descriptor).</summary>
- private void InitFromHandle(SafeFileHandle handle, FileAccess access, bool useAsyncIO)
- {
- if (useAsyncIO)
- _asyncState = new AsyncState();
-
- if (CanSeekCore(handle)) // use non-virtual CanSeekCore rather than CanSeek to avoid making virtual call during ctor
- SeekCore(handle, 0, SeekOrigin.Current);
- }
-
- /// <summary>Translates the FileMode, FileAccess, and FileOptions values into flags to be passed when opening the file.</summary>
- /// <param name="mode">The FileMode provided to the stream's constructor.</param>
- /// <param name="access">The FileAccess provided to the stream's constructor</param>
- /// <param name="share">The FileShare provided to the stream's constructor</param>
- /// <param name="options">The FileOptions provided to the stream's constructor</param>
- /// <returns>The flags value to be passed to the open system call.</returns>
- private static Interop.Sys.OpenFlags PreOpenConfigurationFromOptions(FileMode mode, FileAccess access, FileShare share, FileOptions options)
- {
- // Translate FileMode. Most of the values map cleanly to one or more options for open.
- Interop.Sys.OpenFlags flags = default(Interop.Sys.OpenFlags);
- switch (mode)
- {
- default:
- case FileMode.Open: // Open maps to the default behavior for open(...). No flags needed.
- case FileMode.Truncate: // We truncate the file after getting the lock
- break;
-
- case FileMode.Append: // Append is the same as OpenOrCreate, except that we'll also separately jump to the end later
- case FileMode.OpenOrCreate:
- case FileMode.Create: // We truncate the file after getting the lock
- flags |= Interop.Sys.OpenFlags.O_CREAT;
- break;
-
- case FileMode.CreateNew:
- flags |= (Interop.Sys.OpenFlags.O_CREAT | Interop.Sys.OpenFlags.O_EXCL);
- break;
- }
-
- // Translate FileAccess. All possible values map cleanly to corresponding values for open.
- switch (access)
- {
- case FileAccess.Read:
- flags |= Interop.Sys.OpenFlags.O_RDONLY;
- break;
-
- case FileAccess.ReadWrite:
- flags |= Interop.Sys.OpenFlags.O_RDWR;
- break;
-
- case FileAccess.Write:
- flags |= Interop.Sys.OpenFlags.O_WRONLY;
- break;
- }
-
- // Handle Inheritable, other FileShare flags are handled by Init
- if ((share & FileShare.Inheritable) == 0)
- {
- flags |= Interop.Sys.OpenFlags.O_CLOEXEC;
- }
-
- // Translate some FileOptions; some just aren't supported, and others will be handled after calling open.
- // - Asynchronous: Handled in ctor, setting _useAsync and SafeFileHandle.IsAsync to true
- // - DeleteOnClose: Doesn't have a Unix equivalent, but we approximate it in Dispose
- // - Encrypted: No equivalent on Unix and is ignored
- // - RandomAccess: Implemented after open if posix_fadvise is available
- // - SequentialScan: Implemented after open if posix_fadvise is available
- // - WriteThrough: Handled here
- if ((options & FileOptions.WriteThrough) != 0)
- {
- flags |= Interop.Sys.OpenFlags.O_SYNC;
- }
-
- return flags;
- }
-
- /// <summary>Gets a value indicating whether the current stream supports seeking.</summary>
- public override bool CanSeek => CanSeekCore(_fileHandle);
-
- /// <summary>Gets a value indicating whether the current stream supports seeking.</summary>
- /// <remarks>
- /// Separated out of CanSeek to enable making non-virtual call to this logic.
- /// We also pass in the file handle to allow the constructor to use this before it stashes the handle.
- /// </remarks>
- private bool CanSeekCore(SafeFileHandle fileHandle)
- {
- if (fileHandle.IsClosed)
- {
- return false;
- }
-
- if (!_canSeek.HasValue)
- {
- // Lazily-initialize whether we're able to seek, tested by seeking to our current location.
- _canSeek = Interop.Sys.LSeek(fileHandle, 0, Interop.Sys.SeekWhence.SEEK_CUR) >= 0;
- }
-
- return _canSeek.GetValueOrDefault();
- }
-
- private long GetLengthInternal()
- {
- // Get the length of the file as reported by the OS
- Interop.Sys.FileStatus status;
- CheckFileCall(Interop.Sys.FStat(_fileHandle, out status));
- long length = status.Size;
-
- // But we may have buffered some data to be written that puts our length
- // beyond what the OS is aware of. Update accordingly.
- if (_writePos > 0 && _filePosition + _writePos > length)
- {
- length = _writePos + _filePosition;
- }
-
- return length;
- }
-
- /// <summary>Releases the unmanaged resources used by the stream.</summary>
- /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
- protected override void Dispose(bool disposing)
- {
- try
- {
- if (_fileHandle != null && !_fileHandle.IsClosed)
- {
- // Flush any remaining data in the file
- try
- {
- FlushWriteBuffer();
- }
- catch (Exception e) when (IsIoRelatedException(e) && !disposing)
- {
- // On finalization, ignore failures from trying to flush the write buffer,
- // e.g. if this stream is wrapping a pipe and the pipe is now broken.
- }
-
- // If DeleteOnClose was requested when constructed, delete the file now.
- // (Unix doesn't directly support DeleteOnClose, so we mimic it here.)
- if (_path != null && (_options & FileOptions.DeleteOnClose) != 0)
- {
- // Since we still have the file open, this will end up deleting
- // it (assuming we're the only link to it) once it's closed, but the
- // name will be removed immediately.
- Interop.Sys.Unlink(_path); // ignore errors; it's valid that the path may no longer exist
- }
- }
- }
- finally
- {
- if (_fileHandle != null && !_fileHandle.IsClosed)
- {
- _fileHandle.Dispose();
- }
- base.Dispose(disposing);
- }
- }
-
- public override ValueTask DisposeAsync()
- {
- // On Unix, we don't have any special support for async I/O, simply queueing writes
- // rather than doing them synchronously. As such, if we're "using async I/O" and we
- // have something to flush, queue the call to Dispose, so that we end up queueing whatever
- // write work happens to flush the buffer. Otherwise, just delegate to the base implementation,
- // which will synchronously invoke Dispose. We don't need to factor in the current type
- // as we're using the virtual Dispose either way, and therefore factoring in whatever
- // override may already exist on a derived type.
- if (_useAsyncIO && _writePos > 0)
- {
- return new ValueTask(Task.Factory.StartNew(s => ((FileStream)s!).Dispose(), this,
- CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
- }
-
- return base.DisposeAsync();
- }
-
- /// <summary>Flushes the OS buffer. This does not flush the internal read/write buffer.</summary>
- private void FlushOSBuffer()
- {
- if (Interop.Sys.FSync(_fileHandle) < 0)
- {
- Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
- switch (errorInfo.Error)
- {
- case Interop.Error.EROFS:
- case Interop.Error.EINVAL:
- case Interop.Error.ENOTSUP:
- // Ignore failures due to the FileStream being bound to a special file that
- // doesn't support synchronization. In such cases there's nothing to flush.
- break;
- default:
- throw Interop.GetExceptionForIoErrno(errorInfo, _path, isDirectory: false);
- }
- }
- }
-
- private void FlushWriteBufferForWriteByte()
- {
- _asyncState?.Wait();
- try { FlushWriteBuffer(); }
- finally { _asyncState?.Release(); }
- }
-
- /// <summary>Writes any data in the write buffer to the underlying stream and resets the buffer.</summary>
- private void FlushWriteBuffer()
- {
- AssertBufferInvariants();
- if (_writePos > 0)
- {
- WriteNative(new ReadOnlySpan<byte>(GetBuffer(), 0, _writePos));
- _writePos = 0;
- }
- }
-
- /// <summary>Asynchronously clears all buffers for this stream, causing any buffered data to be written to the underlying device.</summary>
- /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
- /// <returns>A task that represents the asynchronous flush operation.</returns>
- private Task FlushAsyncInternal(CancellationToken cancellationToken)
- {
- if (cancellationToken.IsCancellationRequested)
- {
- return Task.FromCanceled(cancellationToken);
- }
- if (_fileHandle.IsClosed)
- {
- throw Error.GetFileNotOpen();
- }
-
- // As with Win32FileStream, flush the buffers synchronously to avoid race conditions.
- try
- {
- FlushInternalBuffer();
- }
- catch (Exception e)
- {
- return Task.FromException(e);
- }
-
- // We then separately flush to disk asynchronously. This is only
- // necessary if we support writing; otherwise, we're done.
- if (CanWrite)
- {
- return Task.Factory.StartNew(
- state => ((FileStream)state!).FlushOSBuffer(),
- this,
- cancellationToken,
- TaskCreationOptions.DenyChildAttach,
- TaskScheduler.Default);
- }
- else
- {
- return Task.CompletedTask;
- }
- }
-
- /// <summary>Sets the length of this stream to the given value.</summary>
- /// <param name="value">The new length of the stream.</param>
- private void SetLengthInternal(long value)
- {
- FlushInternalBuffer();
-
- if (_appendStart != -1 && value < _appendStart)
- {
- throw new IOException(SR.IO_SetLengthAppendTruncate);
- }
-
- long origPos = _filePosition;
-
- VerifyOSHandlePosition();
-
- if (_filePosition != value)
- {
- SeekCore(_fileHandle, value, SeekOrigin.Begin);
- }
-
- CheckFileCall(Interop.Sys.FTruncate(_fileHandle, value));
-
- // Return file pointer to where it was before setting length
- if (origPos != value)
- {
- if (origPos < value)
- {
- SeekCore(_fileHandle, origPos, SeekOrigin.Begin);
- }
- else
- {
- SeekCore(_fileHandle, 0, SeekOrigin.End);
- }
- }
- }
-
- /// <summary>Reads a block of bytes from the stream and writes the data in a given buffer.</summary>
- private int ReadSpan(Span<byte> destination)
- {
- PrepareForReading();
-
- // Are there any bytes available in the read buffer? If yes,
- // we can just return from the buffer. If the buffer is empty
- // or has no more available data in it, we can either refill it
- // (and then read from the buffer into the user's buffer) or
- // we can just go directly into the user's buffer, if they asked
- // for more data than we'd otherwise buffer.
- int numBytesAvailable = _readLength - _readPos;
- bool readFromOS = false;
- if (numBytesAvailable == 0)
- {
- // If we're not able to seek, then we're not able to rewind the stream (i.e. flushing
- // a read buffer), in which case we don't want to use a read buffer. Similarly, if
- // the user has asked for more data than we can buffer, we also want to skip the buffer.
- if (!CanSeek || (destination.Length >= _bufferLength))
- {
- // Read directly into the user's buffer
- _readPos = _readLength = 0;
- return ReadNative(destination);
- }
- else
- {
- // Read into our buffer.
- _readLength = numBytesAvailable = ReadNative(GetBuffer());
- _readPos = 0;
- if (numBytesAvailable == 0)
- {
- return 0;
- }
-
- // Note that we did an OS read as part of this Read, so that later
- // we don't try to do one again if what's in the buffer doesn't
- // meet the user's request.
- readFromOS = true;
- }
- }
-
- // Now that we know there's data in the buffer, read from it into the user's buffer.
- Debug.Assert(numBytesAvailable > 0, "Data must be in the buffer to be here");
- int bytesRead = Math.Min(numBytesAvailable, destination.Length);
- new Span<byte>(GetBuffer(), _readPos, bytesRead).CopyTo(destination);
- _readPos += bytesRead;
-
- // We may not have had enough data in the buffer to completely satisfy the user's request.
- // While Read doesn't require that we return as much data as the user requested (any amount
- // up to the requested count is fine), FileStream on Windows tries to do so by doing a
- // subsequent read from the file if we tried to satisfy the request with what was in the
- // buffer but the buffer contained less than the requested count. To be consistent with that
- // behavior, we do the same thing here on Unix. Note that we may still get less the requested
- // amount, as the OS may give us back fewer than we request, either due to reaching the end of
- // file, or due to its own whims.
- if (!readFromOS && bytesRead < destination.Length)
- {
- Debug.Assert(_readPos == _readLength, "bytesToRead should only be < destination.Length if numBytesAvailable < destination.Length");
- _readPos = _readLength = 0; // no data left in the read buffer
- bytesRead += ReadNative(destination.Slice(bytesRead));
- }
-
- return bytesRead;
- }
-
- /// <summary>Unbuffered, reads a block of bytes from the file handle into the given buffer.</summary>
- /// <param name="buffer">The buffer into which data from the file is read.</param>
- /// <returns>
- /// The total number of bytes read into the buffer. This might be less than the number of bytes requested
- /// if that number of bytes are not currently available, or zero if the end of the stream is reached.
- /// </returns>
- private unsafe int ReadNative(Span<byte> buffer)
- {
- FlushWriteBuffer(); // we're about to read; dump the write buffer
-
- VerifyOSHandlePosition();
-
- int bytesRead;
- fixed (byte* bufPtr = &MemoryMarshal.GetReference(buffer))
- {
- bytesRead = CheckFileCall(Interop.Sys.Read(_fileHandle, bufPtr, buffer.Length));
- Debug.Assert(bytesRead <= buffer.Length);
- }
- _filePosition += bytesRead;
- return bytesRead;
- }
-
- /// <summary>
- /// Asynchronously reads a sequence of bytes from the current stream and advances
- /// the position within the stream by the number of bytes read.
- /// </summary>
- /// <param name="destination">The buffer to write the data into.</param>
- /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
- /// <param name="synchronousResult">If the operation completes synchronously, the number of bytes read.</param>
- /// <returns>A task that represents the asynchronous read operation.</returns>
- private Task<int>? ReadAsyncInternal(Memory<byte> destination, CancellationToken cancellationToken, out int synchronousResult)
- {
- Debug.Assert(_useAsyncIO);
- Debug.Assert(_asyncState != null);
-
- if (!CanRead) // match Windows behavior; this gets thrown synchronously
- {
- throw Error.GetReadNotSupported();
- }
-
- // Serialize operations using the semaphore.
- Task waitTask = _asyncState.WaitAsync();
-
- // If we got ownership immediately, and if there's enough data in our buffer
- // to satisfy the full request of the caller, hand back the buffered data.
- // While it would be a legal implementation of the Read contract, we don't
- // hand back here less than the amount requested so as to match the behavior
- // in ReadCore that will make a native call to try to fulfill the remainder
- // of the request.
- if (waitTask.Status == TaskStatus.RanToCompletion)
- {
- int numBytesAvailable = _readLength - _readPos;
- if (numBytesAvailable >= destination.Length)
- {
- try
- {
- PrepareForReading();
-
- new Span<byte>(GetBuffer(), _readPos, destination.Length).CopyTo(destination.Span);
- _readPos += destination.Length;
-
- synchronousResult = destination.Length;
- return null;
- }
- catch (Exception exc)
- {
- synchronousResult = 0;
- return Task.FromException<int>(exc);
- }
- finally
- {
- _asyncState.Release();
- }
- }
- }
-
- // Otherwise, issue the whole request asynchronously.
- synchronousResult = 0;
- _asyncState.Memory = destination;
- return waitTask.ContinueWith((t, s) =>
- {
- // The options available on Unix for writing asynchronously to an arbitrary file
- // handle typically amount to just using another thread to do the synchronous write,
- // which is exactly what this implementation does. This does mean there are subtle
- // differences in certain FileStream behaviors between Windows and Unix when multiple
- // asynchronous operations are issued against the stream to execute concurrently; on
- // Unix the operations will be serialized due to the usage of a semaphore, but the
- // position /length information won't be updated until after the write has completed,
- // whereas on Windows it may happen before the write has completed.
-
- Debug.Assert(t.Status == TaskStatus.RanToCompletion);
- var thisRef = (FileStream)s!;
- Debug.Assert(thisRef._asyncState != null);
- try
- {
- Memory<byte> memory = thisRef._asyncState.Memory;
- thisRef._asyncState.Memory = default(Memory<byte>);
- return thisRef.ReadSpan(memory.Span);
- }
- finally { thisRef._asyncState.Release(); }
- }, this, CancellationToken.None, TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default);
- }
-
- /// <summary>Reads from the file handle into the buffer, overwriting anything in it.</summary>
- private int FillReadBufferForReadByte()
- {
- _asyncState?.Wait();
- try { return ReadNative(_buffer); }
- finally { _asyncState?.Release(); }
- }
-
- /// <summary>Writes a block of bytes to the file stream.</summary>
- /// <param name="source">The buffer containing data to write to the stream.</param>
- private void WriteSpan(ReadOnlySpan<byte> source)
- {
- PrepareForWriting();
-
- // If no data is being written, nothing more to do.
- if (source.Length == 0)
- {
- return;
- }
-
- // If there's already data in our write buffer, then we need to go through
- // our buffer to ensure data isn't corrupted.
- if (_writePos > 0)
- {
- // If there's space remaining in the buffer, then copy as much as
- // we can from the user's buffer into ours.
- int spaceRemaining = _bufferLength - _writePos;
- if (spaceRemaining >= source.Length)
- {
- source.CopyTo(GetBuffer().AsSpan(_writePos));
- _writePos += source.Length;
- return;
- }
- else if (spaceRemaining > 0)
- {
- source.Slice(0, spaceRemaining).CopyTo(GetBuffer().AsSpan(_writePos));
- _writePos += spaceRemaining;
- source = source.Slice(spaceRemaining);
- }
-
- // At this point, the buffer is full, so flush it out.
- FlushWriteBuffer();
- }
-
- // Our buffer is now empty. If using the buffer would slow things down (because
- // the user's looking to write more data than we can store in the buffer),
- // skip the buffer. Otherwise, put the remaining data into the buffer.
- Debug.Assert(_writePos == 0);
- if (source.Length >= _bufferLength)
- {
- WriteNative(source);
- }
- else
- {
- source.CopyTo(new Span<byte>(GetBuffer()));
- _writePos = source.Length;
- }
- }
-
- /// <summary>Unbuffered, writes a block of bytes to the file stream.</summary>
- /// <param name="source">The buffer containing data to write to the stream.</param>
- private unsafe void WriteNative(ReadOnlySpan<byte> source)
- {
- VerifyOSHandlePosition();
-
- fixed (byte* bufPtr = &MemoryMarshal.GetReference(source))
- {
- int offset = 0;
- int count = source.Length;
- while (count > 0)
- {
- int bytesWritten = CheckFileCall(Interop.Sys.Write(_fileHandle, bufPtr + offset, count));
- _filePosition += bytesWritten;
- offset += bytesWritten;
- count -= bytesWritten;
- }
- }
- }
-
- /// <summary>
- /// Asynchronously writes a sequence of bytes to the current stream, advances
- /// the current position within this stream by the number of bytes written, and
- /// monitors cancellation requests.
- /// </summary>
- /// <param name="source">The buffer to write data from.</param>
- /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
- /// <returns>A task that represents the asynchronous write operation.</returns>
- private ValueTask WriteAsyncInternal(ReadOnlyMemory<byte> source, CancellationToken cancellationToken)
- {
- Debug.Assert(_useAsyncIO);
- Debug.Assert(_asyncState != null);
-
- if (cancellationToken.IsCancellationRequested)
- return new ValueTask(Task.FromCanceled(cancellationToken));
-
- if (_fileHandle.IsClosed)
- throw Error.GetFileNotOpen();
-
- if (!CanWrite) // match Windows behavior; this gets thrown synchronously
- {
- throw Error.GetWriteNotSupported();
- }
-
- // Serialize operations using the semaphore.
- Task waitTask = _asyncState.WaitAsync();
-
- // If we got ownership immediately, and if there's enough space in our buffer
- // to buffer the entire write request, then do so and we're done.
- if (waitTask.Status == TaskStatus.RanToCompletion)
- {
- int spaceRemaining = _bufferLength - _writePos;
- if (spaceRemaining >= source.Length)
- {
- try
- {
- PrepareForWriting();
-
- source.Span.CopyTo(new Span<byte>(GetBuffer(), _writePos, source.Length));
- _writePos += source.Length;
-
- return default;
- }
- catch (Exception exc)
- {
- return new ValueTask(Task.FromException(exc));
- }
- finally
- {
- _asyncState.Release();
- }
- }
- }
-
- // Otherwise, issue the whole request asynchronously.
- _asyncState.ReadOnlyMemory = source;
- return new ValueTask(waitTask.ContinueWith((t, s) =>
- {
- // The options available on Unix for writing asynchronously to an arbitrary file
- // handle typically amount to just using another thread to do the synchronous write,
- // which is exactly what this implementation does. This does mean there are subtle
- // differences in certain FileStream behaviors between Windows and Unix when multiple
- // asynchronous operations are issued against the stream to execute concurrently; on
- // Unix the operations will be serialized due to the usage of a semaphore, but the
- // position/length information won't be updated until after the write has completed,
- // whereas on Windows it may happen before the write has completed.
-
- Debug.Assert(t.Status == TaskStatus.RanToCompletion);
- var thisRef = (FileStream)s!;
- Debug.Assert(thisRef._asyncState != null);
- try
- {
- ReadOnlyMemory<byte> readOnlyMemory = thisRef._asyncState.ReadOnlyMemory;
- thisRef._asyncState.ReadOnlyMemory = default(ReadOnlyMemory<byte>);
- thisRef.WriteSpan(readOnlyMemory.Span);
- }
- finally { thisRef._asyncState.Release(); }
- }, this, CancellationToken.None, TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default));
- }
-
- public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) =>
- // Windows version overrides this method, so the Unix version does as well, but it doesn't
- // currently have any special optimizations to be done and so just calls to the base.
- base.CopyToAsync(destination, bufferSize, cancellationToken);
-
- /// <summary>Sets the current position of this stream to the given value.</summary>
- /// <param name="offset">The point relative to origin from which to begin seeking. </param>
- /// <param name="origin">
- /// Specifies the beginning, the end, or the current position as a reference
- /// point for offset, using a value of type SeekOrigin.
- /// </param>
- /// <returns>The new position in the stream.</returns>
- public override long Seek(long offset, SeekOrigin origin)
- {
- if (origin < SeekOrigin.Begin || origin > SeekOrigin.End)
- {
- throw new ArgumentException(SR.Argument_InvalidSeekOrigin, nameof(origin));
- }
- if (_fileHandle.IsClosed)
- {
- throw Error.GetFileNotOpen();
- }
- if (!CanSeek)
- {
- throw Error.GetSeekNotSupported();
- }
-
- VerifyOSHandlePosition();
-
- // Flush our write/read buffer. FlushWrite will output any write buffer we have and reset _bufferWritePos.
- // We don't call FlushRead, as that will do an unnecessary seek to rewind the read buffer, and since we're
- // about to seek and update our position, we can simply update the offset as necessary and reset our read
- // position and length to 0. (In the future, for some simple cases we could potentially add an optimization
- // here to just move data around in the buffer for short jumps, to avoid re-reading the data from disk.)
- FlushWriteBuffer();
- if (origin == SeekOrigin.Current)
- {
- offset -= (_readLength - _readPos);
- }
- _readPos = _readLength = 0;
-
- // Keep track of where we were, in case we're in append mode and need to verify
- long oldPos = 0;
- if (_appendStart >= 0)
- {
- oldPos = SeekCore(_fileHandle, 0, SeekOrigin.Current);
- }
-
- // Jump to the new location
- long pos = SeekCore(_fileHandle, offset, origin);
-
- // Prevent users from overwriting data in a file that was opened in append mode.
- if (_appendStart != -1 && pos < _appendStart)
- {
- SeekCore(_fileHandle, oldPos, SeekOrigin.Begin);
- throw new IOException(SR.IO_SeekAppendOverwrite);
- }
-
- // Return the new position
- return pos;
- }
-
- /// <summary>Sets the current position of this stream to the given value.</summary>
- /// <param name="offset">The point relative to origin from which to begin seeking. </param>
- /// <param name="origin">
- /// Specifies the beginning, the end, or the current position as a reference
- /// point for offset, using a value of type SeekOrigin.
- /// </param>
- /// <returns>The new position in the stream.</returns>
- private long SeekCore(SafeFileHandle fileHandle, long offset, SeekOrigin origin)
- {
- Debug.Assert(!fileHandle.IsClosed && (GetType() != typeof(FileStream) || CanSeekCore(fileHandle))); // verify that we can seek, but only if CanSeek won't be a virtual call (which could happen in the ctor)
- Debug.Assert(origin >= SeekOrigin.Begin && origin <= SeekOrigin.End);
-
- long pos = CheckFileCall(Interop.Sys.LSeek(fileHandle, offset, (Interop.Sys.SeekWhence)(int)origin)); // SeekOrigin values are the same as Interop.libc.SeekWhence values
- _filePosition = pos;
- return pos;
- }
-
- private long CheckFileCall(long result, bool ignoreNotSupported = false)
- {
- if (result < 0)
- {
- Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
- if (!(ignoreNotSupported && errorInfo.Error == Interop.Error.ENOTSUP))
- {
- throw Interop.GetExceptionForIoErrno(errorInfo, _path, isDirectory: false);
- }
- }
-
- return result;
- }
-
- private int CheckFileCall(int result, bool ignoreNotSupported = false)
- {
- CheckFileCall((long)result, ignoreNotSupported);
-
- return result;
- }
-
- /// <summary>State used when the stream is in async mode.</summary>
- private sealed class AsyncState : SemaphoreSlim
- {
- internal ReadOnlyMemory<byte> ReadOnlyMemory;
- internal Memory<byte> Memory;
-
- /// <summary>Initialize the AsyncState.</summary>
- internal AsyncState() : base(initialCount: 1, maxCount: 1) { }
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/FileStream.Win32.cs b/netcore/System.Private.CoreLib/shared/System/IO/FileStream.Win32.cs
deleted file mode 100644
index 310d5d2d1fb..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/FileStream.Win32.cs
+++ /dev/null
@@ -1,107 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Diagnostics;
-using Microsoft.Win32.SafeHandles;
-
-namespace System.IO
-{
- public partial class FileStream : Stream
- {
- private SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options)
- {
- return CreateFileOpenHandle(mode, share, options);
- }
-
- private unsafe SafeFileHandle CreateFileOpenHandle(FileMode mode, FileShare share, FileOptions options)
- {
- Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share);
-
- int fAccess =
- ((_access & FileAccess.Read) == FileAccess.Read ? GENERIC_READ : 0) |
- ((_access & FileAccess.Write) == FileAccess.Write ? GENERIC_WRITE : 0);
-
- // Our Inheritable bit was stolen from Windows, but should be set in
- // the security attributes class. Don't leave this bit set.
- share &= ~FileShare.Inheritable;
-
- // Must use a valid Win32 constant here...
- if (mode == FileMode.Append)
- mode = FileMode.OpenOrCreate;
-
- int flagsAndAttributes = (int)options;
-
- // For mitigating local elevation of privilege attack through named pipes
- // make sure we always call CreateFile with SECURITY_ANONYMOUS so that the
- // named pipe server can't impersonate a high privileged client security context
- // (note that this is the effective default on CreateFile2)
- flagsAndAttributes |= (Interop.Kernel32.SecurityOptions.SECURITY_SQOS_PRESENT | Interop.Kernel32.SecurityOptions.SECURITY_ANONYMOUS);
-
- using (DisableMediaInsertionPrompt.Create())
- {
- Debug.Assert(_path != null);
- return ValidateFileHandle(
- Interop.Kernel32.CreateFile(_path, fAccess, share, ref secAttrs, mode, flagsAndAttributes, IntPtr.Zero));
- }
- }
-
- private static bool GetDefaultIsAsync(SafeFileHandle handle)
- {
- return handle.IsAsync ?? !IsHandleSynchronous(handle, ignoreInvalid: true) ?? DefaultIsAsync;
- }
-
- private static unsafe bool? IsHandleSynchronous(SafeFileHandle fileHandle, bool ignoreInvalid)
- {
- if (fileHandle.IsInvalid)
- return null;
-
- uint fileMode;
-
-
- int status = Interop.NtDll.NtQueryInformationFile(
- FileHandle: fileHandle,
- IoStatusBlock: out _,
- FileInformation: &fileMode,
- Length: sizeof(uint),
- FileInformationClass: Interop.NtDll.FileModeInformation);
-
- switch (status)
- {
- case 0:
- // We were successful
- break;
- case Interop.NtDll.STATUS_INVALID_HANDLE:
- if (!ignoreInvalid)
- {
- throw Win32Marshal.GetExceptionForWin32Error(Interop.Errors.ERROR_INVALID_HANDLE);
- }
- else
- {
- return null;
- }
- default:
- // Something else is preventing access
- Debug.Fail("Unable to get the file mode information, status was" + status.ToString());
- return null;
- }
-
- // If either of these two flags are set, the file handle is synchronous (not overlapped)
- return (fileMode & (Interop.NtDll.FILE_SYNCHRONOUS_IO_ALERT | Interop.NtDll.FILE_SYNCHRONOUS_IO_NONALERT)) > 0;
- }
-
- private static void VerifyHandleIsSync(SafeFileHandle handle, int fileType, FileAccess access)
- {
- // As we can accurately check the handle type when we have access to NtQueryInformationFile we don't need to skip for
- // any particular file handle type.
-
- // If the handle was passed in without an explicit async setting, we already looked it up in GetDefaultIsAsync
- if (!handle.IsAsync.HasValue)
- return;
-
- // If we can't check the handle, just assume it is ok.
- if (!(IsHandleSynchronous(handle, ignoreInvalid: false) ?? true))
- throw new ArgumentException(SR.Arg_HandleNotSync, nameof(handle));
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/FileStream.Windows.cs b/netcore/System.Private.CoreLib/shared/System/IO/FileStream.Windows.cs
deleted file mode 100644
index cc6da307f6a..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/FileStream.Windows.cs
+++ /dev/null
@@ -1,1627 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Buffers;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Win32.SafeHandles;
-using System.Runtime.CompilerServices;
-
-/*
- * Win32FileStream supports different modes of accessing the disk - async mode
- * and sync mode. They are two completely different codepaths in the
- * sync & async methods (i.e. Read/Write vs. ReadAsync/WriteAsync). File
- * handles in NT can be opened in only sync or overlapped (async) mode,
- * and we have to deal with this pain. Stream has implementations of
- * the sync methods in terms of the async ones, so we'll
- * call through to our base class to get those methods when necessary.
- *
- * Also buffering is added into Win32FileStream as well. Folded in the
- * code from BufferedStream, so all the comments about it being mostly
- * aggressive (and the possible perf improvement) apply to Win32FileStream as
- * well. Also added some buffering to the async code paths.
- *
- * Class Invariants:
- * The class has one buffer, shared for reading & writing. It can only be
- * used for one or the other at any point in time - not both. The following
- * should be true:
- * 0 <= _readPos <= _readLen < _bufferSize
- * 0 <= _writePos < _bufferSize
- * _readPos == _readLen && _readPos > 0 implies the read buffer is valid,
- * but we're at the end of the buffer.
- * _readPos == _readLen == 0 means the read buffer contains garbage.
- * Either _writePos can be greater than 0, or _readLen & _readPos can be
- * greater than zero, but neither can be greater than zero at the same time.
- *
- */
-
-namespace System.IO
-{
- public partial class FileStream : Stream
- {
- private bool _canSeek;
- private bool _isPipe; // Whether to disable async buffering code.
- private long _appendStart; // When appending, prevent overwriting file.
-
- private static readonly unsafe IOCompletionCallback s_ioCallback = FileStreamCompletionSource.IOCallback;
-
- private Task _activeBufferOperation = Task.CompletedTask; // tracks in-progress async ops using the buffer
- private PreAllocatedOverlapped? _preallocatedOverlapped; // optimization for async ops to avoid per-op allocations
- private FileStreamCompletionSource? _currentOverlappedOwner; // async op currently using the preallocated overlapped
-
- private void Init(FileMode mode, FileShare share, string originalPath)
- {
- if (!PathInternal.IsExtended(originalPath))
- {
- // To help avoid stumbling into opening COM/LPT ports by accident, we will block on non file handles unless
- // we were explicitly passed a path that has \\?\. GetFullPath() will turn paths like C:\foo\con.txt into
- // \\.\CON, so we'll only allow the \\?\ syntax.
-
- int fileType = Interop.Kernel32.GetFileType(_fileHandle);
- if (fileType != Interop.Kernel32.FileTypes.FILE_TYPE_DISK)
- {
- int errorCode = fileType == Interop.Kernel32.FileTypes.FILE_TYPE_UNKNOWN
- ? Marshal.GetLastWin32Error()
- : Interop.Errors.ERROR_SUCCESS;
-
- _fileHandle.Dispose();
-
- if (errorCode != Interop.Errors.ERROR_SUCCESS)
- {
- throw Win32Marshal.GetExceptionForWin32Error(errorCode);
- }
- throw new NotSupportedException(SR.NotSupported_FileStreamOnNonFiles);
- }
- }
-
- // This is necessary for async IO using IO Completion ports via our
- // managed Threadpool API's. This (theoretically) calls the OS's
- // BindIoCompletionCallback method, and passes in a stub for the
- // LPOVERLAPPED_COMPLETION_ROUTINE. This stub looks at the Overlapped
- // struct for this request and gets a delegate to a managed callback
- // from there, which it then calls on a threadpool thread. (We allocate
- // our native OVERLAPPED structs 2 pointers too large and store EE state
- // & GC handles there, one to an IAsyncResult, the other to a delegate.)
- if (_useAsyncIO)
- {
- try
- {
- _fileHandle.ThreadPoolBinding = ThreadPoolBoundHandle.BindHandle(_fileHandle);
- }
- catch (ArgumentException ex)
- {
- throw new IOException(SR.IO_BindHandleFailed, ex);
- }
- finally
- {
- if (_fileHandle.ThreadPoolBinding == null)
- {
- // We should close the handle so that the handle is not open until SafeFileHandle GC
- Debug.Assert(!_exposedHandle, "Are we closing handle that we exposed/not own, how?");
- _fileHandle.Dispose();
- }
- }
- }
-
- _canSeek = true;
-
- // For Append mode...
- if (mode == FileMode.Append)
- {
- _appendStart = SeekCore(_fileHandle, 0, SeekOrigin.End);
- }
- else
- {
- _appendStart = -1;
- }
- }
-
- private void InitFromHandle(SafeFileHandle handle, FileAccess access, bool useAsyncIO)
- {
-#if DEBUG
- bool hadBinding = handle.ThreadPoolBinding != null;
-
- try
- {
-#endif
- InitFromHandleImpl(handle, access, useAsyncIO);
-#if DEBUG
- }
- catch
- {
- Debug.Assert(hadBinding || handle.ThreadPoolBinding == null, "We should never error out with a ThreadPoolBinding we've added");
- throw;
- }
-#endif
- }
-
- private void InitFromHandleImpl(SafeFileHandle handle, FileAccess access, bool useAsyncIO)
- {
- int handleType = Interop.Kernel32.GetFileType(handle);
- Debug.Assert(handleType == Interop.Kernel32.FileTypes.FILE_TYPE_DISK || handleType == Interop.Kernel32.FileTypes.FILE_TYPE_PIPE || handleType == Interop.Kernel32.FileTypes.FILE_TYPE_CHAR, "FileStream was passed an unknown file type!");
-
- _canSeek = handleType == Interop.Kernel32.FileTypes.FILE_TYPE_DISK;
- _isPipe = handleType == Interop.Kernel32.FileTypes.FILE_TYPE_PIPE;
-
- // This is necessary for async IO using IO Completion ports via our
- // managed Threadpool API's. This calls the OS's
- // BindIoCompletionCallback method, and passes in a stub for the
- // LPOVERLAPPED_COMPLETION_ROUTINE. This stub looks at the Overlapped
- // struct for this request and gets a delegate to a managed callback
- // from there, which it then calls on a threadpool thread. (We allocate
- // our native OVERLAPPED structs 2 pointers too large and store EE
- // state & a handle to a delegate there.)
- //
- // If, however, we've already bound this file handle to our completion port,
- // don't try to bind it again because it will fail. A handle can only be
- // bound to a single completion port at a time.
- if (useAsyncIO && !(handle.IsAsync ?? false))
- {
- try
- {
- handle.ThreadPoolBinding = ThreadPoolBoundHandle.BindHandle(handle);
- }
- catch (Exception ex)
- {
- // If you passed in a synchronous handle and told us to use
- // it asynchronously, throw here.
- throw new ArgumentException(SR.Arg_HandleNotAsync, nameof(handle), ex);
- }
- }
- else if (!useAsyncIO)
- {
- VerifyHandleIsSync(handle, handleType, access);
- }
-
- if (_canSeek)
- SeekCore(handle, 0, SeekOrigin.Current);
- else
- _filePosition = 0;
- }
-
- private static unsafe Interop.Kernel32.SECURITY_ATTRIBUTES GetSecAttrs(FileShare share)
- {
- Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = default;
- if ((share & FileShare.Inheritable) != 0)
- {
- secAttrs = new Interop.Kernel32.SECURITY_ATTRIBUTES
- {
- nLength = (uint)sizeof(Interop.Kernel32.SECURITY_ATTRIBUTES),
- bInheritHandle = Interop.BOOL.TRUE
- };
- }
- return secAttrs;
- }
-
- private bool HasActiveBufferOperation => !_activeBufferOperation.IsCompleted;
-
- public override bool CanSeek => _canSeek;
-
- private unsafe long GetLengthInternal()
- {
- Interop.Kernel32.FILE_STANDARD_INFO info;
-
- if (!Interop.Kernel32.GetFileInformationByHandleEx(_fileHandle, Interop.Kernel32.FILE_INFO_BY_HANDLE_CLASS.FileStandardInfo, out info, (uint)sizeof(Interop.Kernel32.FILE_STANDARD_INFO)))
- throw Win32Marshal.GetExceptionForLastWin32Error(_path);
- long len = info.EndOfFile;
-
- // If we're writing near the end of the file, we must include our
- // internal buffer in our Length calculation. Don't flush because
- // we use the length of the file in our async write method.
- if (_writePos > 0 && _filePosition + _writePos > len)
- len = _writePos + _filePosition;
-
- return len;
- }
-
- protected override void Dispose(bool disposing)
- {
- // Nothing will be done differently based on whether we are
- // disposing vs. finalizing. This is taking advantage of the
- // weak ordering between normal finalizable objects & critical
- // finalizable objects, which I included in the SafeHandle
- // design for Win32FileStream, which would often "just work" when
- // finalized.
- try
- {
- if (_fileHandle != null && !_fileHandle.IsClosed && _writePos > 0)
- {
- // Flush data to disk iff we were writing. After
- // thinking about this, we also don't need to flush
- // our read position, regardless of whether the handle
- // was exposed to the user. They probably would NOT
- // want us to do this.
- try
- {
- FlushWriteBuffer(!disposing);
- }
- catch (Exception e) when (IsIoRelatedException(e) && !disposing)
- {
- // On finalization, ignore failures from trying to flush the write buffer,
- // e.g. if this stream is wrapping a pipe and the pipe is now broken.
- }
- }
- }
- finally
- {
- if (_fileHandle != null && !_fileHandle.IsClosed)
- {
- _fileHandle.ThreadPoolBinding?.Dispose();
- _fileHandle.Dispose();
- }
-
- _preallocatedOverlapped?.Dispose();
- _canSeek = false;
-
- // Don't set the buffer to null, to avoid a NullReferenceException
- // when users have a race condition in their code (i.e. they call
- // Close when calling another method on Stream like Read).
- }
- }
-
- public override ValueTask DisposeAsync() =>
- GetType() == typeof(FileStream) ?
- DisposeAsyncCore() :
- base.DisposeAsync();
-
- private async ValueTask DisposeAsyncCore()
- {
- // Same logic as in Dispose(), except with async counterparts.
- // TODO: https://github.com/dotnet/corefx/issues/32837: FlushAsync does synchronous work.
- try
- {
- if (_fileHandle != null && !_fileHandle.IsClosed && _writePos > 0)
- {
- await FlushAsyncInternal(default).ConfigureAwait(false);
- }
- }
- finally
- {
- if (_fileHandle != null && !_fileHandle.IsClosed)
- {
- _fileHandle.ThreadPoolBinding?.Dispose();
- _fileHandle.Dispose();
- }
-
- _preallocatedOverlapped?.Dispose();
- _canSeek = false;
- GC.SuppressFinalize(this); // the handle is closed; nothing further for the finalizer to do
- }
- }
-
- private void FlushOSBuffer()
- {
- if (!Interop.Kernel32.FlushFileBuffers(_fileHandle))
- {
- throw Win32Marshal.GetExceptionForLastWin32Error(_path);
- }
- }
-
- // Returns a task that flushes the internal write buffer
- private Task FlushWriteAsync(CancellationToken cancellationToken)
- {
- Debug.Assert(_useAsyncIO);
- Debug.Assert(_readPos == 0 && _readLength == 0, "FileStream: Read buffer must be empty in FlushWriteAsync!");
-
- // If the buffer is already flushed, don't spin up the OS write
- if (_writePos == 0) return Task.CompletedTask;
-
- Task flushTask = WriteAsyncInternalCore(new ReadOnlyMemory<byte>(GetBuffer(), 0, _writePos), cancellationToken);
- _writePos = 0;
-
- // Update the active buffer operation
- _activeBufferOperation = HasActiveBufferOperation ?
- Task.WhenAll(_activeBufferOperation, flushTask) :
- flushTask;
-
- return flushTask;
- }
-
- private void FlushWriteBufferForWriteByte() => FlushWriteBuffer();
-
- // Writes are buffered. Anytime the buffer fills up
- // (_writePos + delta > _bufferSize) or the buffer switches to reading
- // and there is left over data (_writePos > 0), this function must be called.
- private void FlushWriteBuffer(bool calledFromFinalizer = false)
- {
- if (_writePos == 0) return;
- Debug.Assert(_readPos == 0 && _readLength == 0, "FileStream: Read buffer must be empty in FlushWrite!");
-
- if (_useAsyncIO)
- {
- Task writeTask = FlushWriteAsync(CancellationToken.None);
- // With our Whidbey async IO & overlapped support for AD unloads,
- // we don't strictly need to block here to release resources
- // since that support takes care of the pinning & freeing the
- // overlapped struct. We need to do this when called from
- // Close so that the handle is closed when Close returns, but
- // we don't need to call EndWrite from the finalizer.
- // Additionally, if we do call EndWrite, we block forever
- // because AD unloads prevent us from running the managed
- // callback from the IO completion port. Blocking here when
- // called from the finalizer during AD unload is clearly wrong,
- // but we can't use any sort of test for whether the AD is
- // unloading because if we weren't unloading, an AD unload
- // could happen on a separate thread before we call EndWrite.
- if (!calledFromFinalizer)
- {
- writeTask.GetAwaiter().GetResult();
- }
- }
- else
- {
- WriteCore(new ReadOnlySpan<byte>(GetBuffer(), 0, _writePos));
- }
-
- _writePos = 0;
- }
-
- private void SetLengthInternal(long value)
- {
- // Handle buffering updates.
- if (_writePos > 0)
- {
- FlushWriteBuffer();
- }
- else if (_readPos < _readLength)
- {
- FlushReadBuffer();
- }
- _readPos = 0;
- _readLength = 0;
-
- if (_appendStart != -1 && value < _appendStart)
- throw new IOException(SR.IO_SetLengthAppendTruncate);
- SetLengthCore(value);
- }
-
- // We absolutely need this method broken out so that WriteInternalCoreAsync can call
- // a method without having to go through buffering code that might call FlushWrite.
- private void SetLengthCore(long value)
- {
- Debug.Assert(value >= 0, "value >= 0");
- long origPos = _filePosition;
-
- VerifyOSHandlePosition();
- if (_filePosition != value)
- SeekCore(_fileHandle, value, SeekOrigin.Begin);
- if (!Interop.Kernel32.SetEndOfFile(_fileHandle))
- {
- int errorCode = Marshal.GetLastWin32Error();
- if (errorCode == Interop.Errors.ERROR_INVALID_PARAMETER)
- throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_FileLengthTooBig);
- throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path);
- }
- // Return file pointer to where it was before setting length
- if (origPos != value)
- {
- if (origPos < value)
- SeekCore(_fileHandle, origPos, SeekOrigin.Begin);
- else
- SeekCore(_fileHandle, 0, SeekOrigin.End);
- }
- }
-
- // Instance method to help code external to this MarshalByRefObject avoid
- // accessing its fields by ref. This avoids a compiler warning.
- private FileStreamCompletionSource? CompareExchangeCurrentOverlappedOwner(FileStreamCompletionSource? newSource, FileStreamCompletionSource? existingSource) =>
- Interlocked.CompareExchange(ref _currentOverlappedOwner, newSource, existingSource);
-
- private int ReadSpan(Span<byte> destination)
- {
- Debug.Assert(!_useAsyncIO, "Must only be used when in synchronous mode");
- Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength),
- "We're either reading or writing, but not both.");
-
- bool isBlocked = false;
- int n = _readLength - _readPos;
- // if the read buffer is empty, read into either user's array or our
- // buffer, depending on number of bytes user asked for and buffer size.
- if (n == 0)
- {
- if (!CanRead) throw Error.GetReadNotSupported();
- if (_writePos > 0) FlushWriteBuffer();
- if (!CanSeek || (destination.Length >= _bufferLength))
- {
- n = ReadNative(destination);
- // Throw away read buffer.
- _readPos = 0;
- _readLength = 0;
- return n;
- }
- n = ReadNative(GetBuffer());
- if (n == 0) return 0;
- isBlocked = n < _bufferLength;
- _readPos = 0;
- _readLength = n;
- }
- // Now copy min of count or numBytesAvailable (i.e. near EOF) to array.
- if (n > destination.Length) n = destination.Length;
- new ReadOnlySpan<byte>(GetBuffer(), _readPos, n).CopyTo(destination);
- _readPos += n;
-
- // We may have read less than the number of bytes the user asked
- // for, but that is part of the Stream contract. Reading again for
- // more data may cause us to block if we're using a device with
- // no clear end of file, such as a serial port or pipe. If we
- // blocked here & this code was used with redirected pipes for a
- // process's standard output, this can lead to deadlocks involving
- // two processes. But leave this here for files to avoid what would
- // probably be a breaking change. --
-
- // If we are reading from a device with no clear EOF like a
- // serial port or a pipe, this will cause us to block incorrectly.
- if (!_isPipe)
- {
- // If we hit the end of the buffer and didn't have enough bytes, we must
- // read some more from the underlying stream. However, if we got
- // fewer bytes from the underlying stream than we asked for (i.e. we're
- // probably blocked), don't ask for more bytes.
- if (n < destination.Length && !isBlocked)
- {
- Debug.Assert(_readPos == _readLength, "Read buffer should be empty!");
- int moreBytesRead = ReadNative(destination.Slice(n));
- n += moreBytesRead;
- // We've just made our buffer inconsistent with our position
- // pointer. We must throw away the read buffer.
- _readPos = 0;
- _readLength = 0;
- }
- }
-
- return n;
- }
-
- [Conditional("DEBUG")]
- private void AssertCanRead()
- {
- Debug.Assert(!_fileHandle.IsClosed, "!_fileHandle.IsClosed");
- Debug.Assert(CanRead, "CanRead");
- }
-
- /// <summary>Reads from the file handle into the buffer, overwriting anything in it.</summary>
- private int FillReadBufferForReadByte() =>
- _useAsyncIO ?
- ReadNativeAsync(new Memory<byte>(_buffer), 0, CancellationToken.None).GetAwaiter().GetResult() :
- ReadNative(_buffer);
-
- private unsafe int ReadNative(Span<byte> buffer)
- {
- Debug.Assert(!_useAsyncIO, $"{nameof(ReadNative)} doesn't work on asynchronous file streams.");
- AssertCanRead();
-
- // Make sure we are reading from the right spot
- VerifyOSHandlePosition();
-
- int r = ReadFileNative(_fileHandle, buffer, null, out int errorCode);
-
- if (r == -1)
- {
- // For pipes, ERROR_BROKEN_PIPE is the normal end of the pipe.
- if (errorCode == ERROR_BROKEN_PIPE)
- {
- r = 0;
- }
- else
- {
- if (errorCode == ERROR_INVALID_PARAMETER)
- throw new ArgumentException(SR.Arg_HandleNotSync, "_fileHandle");
-
- throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path);
- }
- }
- Debug.Assert(r >= 0, "FileStream's ReadNative is likely broken.");
- _filePosition += r;
-
- return r;
- }
-
- public override long Seek(long offset, SeekOrigin origin)
- {
- if (origin < SeekOrigin.Begin || origin > SeekOrigin.End)
- throw new ArgumentException(SR.Argument_InvalidSeekOrigin, nameof(origin));
- if (_fileHandle.IsClosed) throw Error.GetFileNotOpen();
- if (!CanSeek) throw Error.GetSeekNotSupported();
-
- Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both.");
-
- // If we've got bytes in our buffer to write, write them out.
- // If we've read in and consumed some bytes, we'll have to adjust
- // our seek positions ONLY IF we're seeking relative to the current
- // position in the stream. This simulates doing a seek to the new
- // position, then a read for the number of bytes we have in our buffer.
- if (_writePos > 0)
- {
- FlushWriteBuffer();
- }
- else if (origin == SeekOrigin.Current)
- {
- // Don't call FlushRead here, which would have caused an infinite
- // loop. Simply adjust the seek origin. This isn't necessary
- // if we're seeking relative to the beginning or end of the stream.
- offset -= (_readLength - _readPos);
- }
- _readPos = _readLength = 0;
-
- // Verify that internal position is in sync with the handle
- VerifyOSHandlePosition();
-
- long oldPos = _filePosition + (_readPos - _readLength);
- long pos = SeekCore(_fileHandle, offset, origin);
-
- // Prevent users from overwriting data in a file that was opened in
- // append mode.
- if (_appendStart != -1 && pos < _appendStart)
- {
- SeekCore(_fileHandle, oldPos, SeekOrigin.Begin);
- throw new IOException(SR.IO_SeekAppendOverwrite);
- }
-
- // We now must update the read buffer. We can in some cases simply
- // update _readPos within the buffer, copy around the buffer so our
- // Position property is still correct, and avoid having to do more
- // reads from the disk. Otherwise, discard the buffer's contents.
- if (_readLength > 0)
- {
- // We can optimize the following condition:
- // oldPos - _readPos <= pos < oldPos + _readLen - _readPos
- if (oldPos == pos)
- {
- if (_readPos > 0)
- {
- Buffer.BlockCopy(GetBuffer(), _readPos, GetBuffer(), 0, _readLength - _readPos);
- _readLength -= _readPos;
- _readPos = 0;
- }
- // If we still have buffered data, we must update the stream's
- // position so our Position property is correct.
- if (_readLength > 0)
- SeekCore(_fileHandle, _readLength, SeekOrigin.Current);
- }
- else if (oldPos - _readPos < pos && pos < oldPos + _readLength - _readPos)
- {
- int diff = (int)(pos - oldPos);
- Buffer.BlockCopy(GetBuffer(), _readPos + diff, GetBuffer(), 0, _readLength - (_readPos + diff));
- _readLength -= (_readPos + diff);
- _readPos = 0;
- if (_readLength > 0)
- SeekCore(_fileHandle, _readLength, SeekOrigin.Current);
- }
- else
- {
- // Lose the read buffer.
- _readPos = 0;
- _readLength = 0;
- }
- Debug.Assert(_readLength >= 0 && _readPos <= _readLength, "_readLen should be nonnegative, and _readPos should be less than or equal _readLen");
- Debug.Assert(pos == Position, "Seek optimization: pos != Position! Buffer math was mangled.");
- }
- return pos;
- }
-
- // This doesn't do argument checking. Necessary for SetLength, which must
- // set the file pointer beyond the end of the file. This will update the
- // internal position
- private long SeekCore(SafeFileHandle fileHandle, long offset, SeekOrigin origin, bool closeInvalidHandle = false)
- {
- Debug.Assert(!fileHandle.IsClosed && _canSeek, "!fileHandle.IsClosed && _canSeek");
- Debug.Assert(origin >= SeekOrigin.Begin && origin <= SeekOrigin.End, "origin >= SeekOrigin.Begin && origin <= SeekOrigin.End");
-
- if (!Interop.Kernel32.SetFilePointerEx(fileHandle, offset, out long ret, (uint)origin))
- {
- if (closeInvalidHandle)
- {
- throw Win32Marshal.GetExceptionForWin32Error(GetLastWin32ErrorAndDisposeHandleIfInvalid(), _path);
- }
- else
- {
- throw Win32Marshal.GetExceptionForLastWin32Error(_path);
- }
- }
-
- _filePosition = ret;
- return ret;
- }
-
- partial void OnBufferAllocated()
- {
- Debug.Assert(_buffer != null);
- Debug.Assert(_preallocatedOverlapped == null);
-
- if (_useAsyncIO)
- _preallocatedOverlapped = new PreAllocatedOverlapped(s_ioCallback, this, _buffer);
- }
-
- private void WriteSpan(ReadOnlySpan<byte> source)
- {
- Debug.Assert(!_useAsyncIO, "Must only be used when in synchronous mode");
-
- if (_writePos == 0)
- {
- // Ensure we can write to the stream, and ready buffer for writing.
- if (!CanWrite) throw Error.GetWriteNotSupported();
- if (_readPos < _readLength) FlushReadBuffer();
- _readPos = 0;
- _readLength = 0;
- }
-
- // If our buffer has data in it, copy data from the user's array into
- // the buffer, and if we can fit it all there, return. Otherwise, write
- // the buffer to disk and copy any remaining data into our buffer.
- // The assumption here is memcpy is cheaper than disk (or net) IO.
- // (10 milliseconds to disk vs. ~20-30 microseconds for a 4K memcpy)
- // So the extra copying will reduce the total number of writes, in
- // non-pathological cases (i.e. write 1 byte, then write for the buffer
- // size repeatedly)
- if (_writePos > 0)
- {
- int numBytes = _bufferLength - _writePos; // space left in buffer
- if (numBytes > 0)
- {
- if (numBytes >= source.Length)
- {
- source.CopyTo(GetBuffer().AsSpan(_writePos));
- _writePos += source.Length;
- return;
- }
- else
- {
- source.Slice(0, numBytes).CopyTo(GetBuffer().AsSpan(_writePos));
- _writePos += numBytes;
- source = source.Slice(numBytes);
- }
- }
- // Reset our buffer. We essentially want to call FlushWrite
- // without calling Flush on the underlying Stream.
-
- WriteCore(new ReadOnlySpan<byte>(GetBuffer(), 0, _writePos));
- _writePos = 0;
- }
-
- // If the buffer would slow writes down, avoid buffer completely.
- if (source.Length >= _bufferLength)
- {
- Debug.Assert(_writePos == 0, "FileStream cannot have buffered data to write here! Your stream will be corrupted.");
- WriteCore(source);
- return;
- }
- else if (source.Length == 0)
- {
- return; // Don't allocate a buffer then call memcpy for 0 bytes.
- }
-
- // Copy remaining bytes into buffer, to write at a later date.
- source.CopyTo(GetBuffer().AsSpan(_writePos));
- _writePos = source.Length;
- return;
- }
-
- private unsafe void WriteCore(ReadOnlySpan<byte> source)
- {
- Debug.Assert(!_useAsyncIO);
- Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed");
- Debug.Assert(CanWrite, "_parent.CanWrite");
- Debug.Assert(_readPos == _readLength, "_readPos == _readLen");
-
- // Make sure we are writing to the position that we think we are
- VerifyOSHandlePosition();
-
- int errorCode = 0;
- int r = WriteFileNative(_fileHandle, source, null, out errorCode);
-
- if (r == -1)
- {
- // For pipes, ERROR_NO_DATA is not an error, but the pipe is closing.
- if (errorCode == ERROR_NO_DATA)
- {
- r = 0;
- }
- else
- {
- // ERROR_INVALID_PARAMETER may be returned for writes
- // where the position is too large or for synchronous writes
- // to a handle opened asynchronously.
- if (errorCode == ERROR_INVALID_PARAMETER)
- throw new IOException(SR.IO_FileTooLongOrHandleNotSync);
- throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path);
- }
- }
- Debug.Assert(r >= 0, "FileStream's WriteCore is likely broken.");
- _filePosition += r;
- return;
- }
-
- private Task<int>? ReadAsyncInternal(Memory<byte> destination, CancellationToken cancellationToken, out int synchronousResult)
- {
- Debug.Assert(_useAsyncIO);
- if (!CanRead) throw Error.GetReadNotSupported();
-
- Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both.");
-
- if (_isPipe)
- {
- // Pipes are tricky, at least when you have 2 different pipes
- // that you want to use simultaneously. When redirecting stdout
- // & stderr with the Process class, it's easy to deadlock your
- // parent & child processes when doing writes 4K at a time. The
- // OS appears to use a 4K buffer internally. If you write to a
- // pipe that is full, you will block until someone read from
- // that pipe. If you try reading from an empty pipe and
- // Win32FileStream's ReadAsync blocks waiting for data to fill it's
- // internal buffer, you will be blocked. In a case where a child
- // process writes to stdout & stderr while a parent process tries
- // reading from both, you can easily get into a deadlock here.
- // To avoid this deadlock, don't buffer when doing async IO on
- // pipes. But don't completely ignore buffered data either.
- if (_readPos < _readLength)
- {
- int n = Math.Min(_readLength - _readPos, destination.Length);
- new Span<byte>(GetBuffer(), _readPos, n).CopyTo(destination.Span);
- _readPos += n;
- synchronousResult = n;
- return null;
- }
- else
- {
- Debug.Assert(_writePos == 0, "Win32FileStream must not have buffered write data here! Pipes should be unidirectional.");
- synchronousResult = 0;
- return ReadNativeAsync(destination, 0, cancellationToken);
- }
- }
-
- Debug.Assert(!_isPipe, "Should not be a pipe.");
-
- // Handle buffering.
- if (_writePos > 0) FlushWriteBuffer();
- if (_readPos == _readLength)
- {
- // I can't see how to handle buffering of async requests when
- // filling the buffer asynchronously, without a lot of complexity.
- // The problems I see are issuing an async read, we do an async
- // read to fill the buffer, then someone issues another read
- // (either synchronously or asynchronously) before the first one
- // returns. This would involve some sort of complex buffer locking
- // that we probably don't want to get into, at least not in V1.
- // If we did a sync read to fill the buffer, we could avoid the
- // problem, and any async read less than 64K gets turned into a
- // synchronous read by NT anyways... --
-
- if (destination.Length < _bufferLength)
- {
- Task<int> readTask = ReadNativeAsync(new Memory<byte>(GetBuffer()), 0, cancellationToken);
- _readLength = readTask.GetAwaiter().GetResult();
- int n = Math.Min(_readLength, destination.Length);
- new Span<byte>(GetBuffer(), 0, n).CopyTo(destination.Span);
- _readPos = n;
-
- synchronousResult = n;
- return null;
- }
- else
- {
- // Here we're making our position pointer inconsistent
- // with our read buffer. Throw away the read buffer's contents.
- _readPos = 0;
- _readLength = 0;
- synchronousResult = 0;
- return ReadNativeAsync(destination, 0, cancellationToken);
- }
- }
- else
- {
- int n = Math.Min(_readLength - _readPos, destination.Length);
- new Span<byte>(GetBuffer(), _readPos, n).CopyTo(destination.Span);
- _readPos += n;
-
- if (n == destination.Length)
- {
- // Return a completed task
- synchronousResult = n;
- return null;
- }
- else
- {
- // For streams with no clear EOF like serial ports or pipes
- // we cannot read more data without causing an app to block
- // incorrectly. Pipes don't go down this path
- // though. This code needs to be fixed.
- // Throw away read buffer.
- _readPos = 0;
- _readLength = 0;
- synchronousResult = 0;
- return ReadNativeAsync(destination.Slice(n), n, cancellationToken);
- }
- }
- }
-
- private unsafe Task<int> ReadNativeAsync(Memory<byte> destination, int numBufferedBytesRead, CancellationToken cancellationToken)
- {
- AssertCanRead();
- Debug.Assert(_useAsyncIO, "ReadNativeAsync doesn't work on synchronous file streams!");
-
- // Create and store async stream class library specific data in the async result
- FileStreamCompletionSource completionSource = FileStreamCompletionSource.Create(this, numBufferedBytesRead, destination);
- NativeOverlapped* intOverlapped = completionSource.Overlapped;
-
- // Calculate position in the file we should be at after the read is done
- if (CanSeek)
- {
- long len = Length;
-
- // Make sure we are reading from the position that we think we are
- VerifyOSHandlePosition();
-
- if (_filePosition + destination.Length > len)
- {
- if (_filePosition <= len)
- {
- destination = destination.Slice(0, (int)(len - _filePosition));
- }
- else
- {
- destination = default;
- }
- }
-
- // Now set the position to read from in the NativeOverlapped struct
- // For pipes, we should leave the offset fields set to 0.
- intOverlapped->OffsetLow = unchecked((int)_filePosition);
- intOverlapped->OffsetHigh = (int)(_filePosition >> 32);
-
- // When using overlapped IO, the OS is not supposed to
- // touch the file pointer location at all. We will adjust it
- // ourselves. This isn't threadsafe.
-
- // WriteFile should not update the file pointer when writing
- // in overlapped mode, according to MSDN. But it does update
- // the file pointer when writing to a UNC path!
- // So changed the code below to seek to an absolute
- // location, not a relative one. ReadFile seems consistent though.
- SeekCore(_fileHandle, destination.Length, SeekOrigin.Current);
- }
-
- // queue an async ReadFile operation and pass in a packed overlapped
- int errorCode = 0;
- int r = ReadFileNative(_fileHandle, destination.Span, intOverlapped, out errorCode);
-
- // ReadFile, the OS version, will return 0 on failure. But
- // my ReadFileNative wrapper returns -1. My wrapper will return
- // the following:
- // On error, r==-1.
- // On async requests that are still pending, r==-1 w/ errorCode==ERROR_IO_PENDING
- // on async requests that completed sequentially, r==0
- // You will NEVER RELIABLY be able to get the number of bytes
- // read back from this call when using overlapped structures! You must
- // not pass in a non-null lpNumBytesRead to ReadFile when using
- // overlapped structures! This is by design NT behavior.
- if (r == -1)
- {
- // For pipes, when they hit EOF, they will come here.
- if (errorCode == ERROR_BROKEN_PIPE)
- {
- // Not an error, but EOF. AsyncFSCallback will NOT be
- // called. Call the user callback here.
-
- // We clear the overlapped status bit for this special case.
- // Failure to do so looks like we are freeing a pending overlapped later.
- intOverlapped->InternalLow = IntPtr.Zero;
- completionSource.SetCompletedSynchronously(0);
- }
- else if (errorCode != ERROR_IO_PENDING)
- {
- if (!_fileHandle.IsClosed && CanSeek) // Update Position - It could be anywhere.
- {
- SeekCore(_fileHandle, 0, SeekOrigin.Current);
- }
-
- completionSource.ReleaseNativeResource();
-
- if (errorCode == ERROR_HANDLE_EOF)
- {
- throw Error.GetEndOfFile();
- }
- else
- {
- throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path);
- }
- }
- else if (cancellationToken.CanBeCanceled) // ERROR_IO_PENDING
- {
- // Only once the IO is pending do we register for cancellation
- completionSource.RegisterForCancellation(cancellationToken);
- }
- }
- else
- {
- // Due to a workaround for a race condition in NT's ReadFile &
- // WriteFile routines, we will always be returning 0 from ReadFileNative
- // when we do async IO instead of the number of bytes read,
- // irregardless of whether the operation completed
- // synchronously or asynchronously. We absolutely must not
- // set asyncResult._numBytes here, since will never have correct
- // results.
- }
-
- return completionSource.Task;
- }
-
- private ValueTask WriteAsyncInternal(ReadOnlyMemory<byte> source, CancellationToken cancellationToken)
- {
- Debug.Assert(_useAsyncIO);
- Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both.");
- Debug.Assert(!_isPipe || (_readPos == 0 && _readLength == 0), "Win32FileStream must not have buffered data here! Pipes should be unidirectional.");
-
- if (!CanWrite) throw Error.GetWriteNotSupported();
-
- bool writeDataStoredInBuffer = false;
- if (!_isPipe) // avoid async buffering with pipes, as doing so can lead to deadlocks (see comments in ReadInternalAsyncCore)
- {
- // Ensure the buffer is clear for writing
- if (_writePos == 0)
- {
- if (_readPos < _readLength)
- {
- FlushReadBuffer();
- }
- _readPos = 0;
- _readLength = 0;
- }
-
- // Determine how much space remains in the buffer
- int remainingBuffer = _bufferLength - _writePos;
- Debug.Assert(remainingBuffer >= 0);
-
- // Simple/common case:
- // - The write is smaller than our buffer, such that it's worth considering buffering it.
- // - There's no active flush operation, such that we don't have to worry about the existing buffer being in use.
- // - And the data we're trying to write fits in the buffer, meaning it wasn't already filled by previous writes.
- // In that case, just store it in the buffer.
- if (source.Length < _bufferLength && !HasActiveBufferOperation && source.Length <= remainingBuffer)
- {
- source.Span.CopyTo(new Span<byte>(GetBuffer(), _writePos, source.Length));
- _writePos += source.Length;
- writeDataStoredInBuffer = true;
-
- // There is one special-but-common case, common because devs often use
- // byte[] sizes that are powers of 2 and thus fit nicely into our buffer, which is
- // also a power of 2. If after our write the buffer still has remaining space,
- // then we're done and can return a completed task now. But if we filled the buffer
- // completely, we want to do the asynchronous flush/write as part of this operation
- // rather than waiting until the next write that fills the buffer.
- if (source.Length != remainingBuffer)
- return default;
-
- Debug.Assert(_writePos == _bufferLength);
- }
- }
-
- // At this point, at least one of the following is true:
- // 1. There was an active flush operation (it could have completed by now, though).
- // 2. The data doesn't fit in the remaining buffer (or it's a pipe and we chose not to try).
- // 3. We wrote all of the data to the buffer, filling it.
- //
- // If there's an active operation, we can't touch the current buffer because it's in use.
- // That gives us a choice: we can either allocate a new buffer, or we can skip the buffer
- // entirely (even if the data would otherwise fit in it). For now, for simplicity, we do
- // the latter; it could also have performance wins due to OS-level optimizations, and we could
- // potentially add support for PreAllocatedOverlapped due to having a single buffer. (We can
- // switch to allocating a new buffer, potentially experimenting with buffer pooling, should
- // performance data suggest it's appropriate.)
- //
- // If the data doesn't fit in the remaining buffer, it could be because it's so large
- // it's greater than the entire buffer size, in which case we'd always skip the buffer,
- // or it could be because there's more data than just the space remaining. For the latter
- // case, we need to issue an asynchronous write to flush that data, which then turns this into
- // the first case above with an active operation.
- //
- // If we already stored the data, then we have nothing additional to write beyond what
- // we need to flush.
- //
- // In any of these cases, we have the same outcome:
- // - If there's data in the buffer, flush it by writing it out asynchronously.
- // - Then, if there's any data to be written, issue a write for it concurrently.
- // We return a Task that represents one or both.
-
- // Flush the buffer asynchronously if there's anything to flush
- Task? flushTask = null;
- if (_writePos > 0)
- {
- flushTask = FlushWriteAsync(cancellationToken);
-
- // If we already copied all of the data into the buffer,
- // simply return the flush task here. Same goes for if the task has
- // already completed and was unsuccessful.
- if (writeDataStoredInBuffer ||
- flushTask.IsFaulted ||
- flushTask.IsCanceled)
- {
- return new ValueTask(flushTask);
- }
- }
-
- Debug.Assert(!writeDataStoredInBuffer);
- Debug.Assert(_writePos == 0);
-
- // Finally, issue the write asynchronously, and return a Task that logically
- // represents the write operation, including any flushing done.
- Task writeTask = WriteAsyncInternalCore(source, cancellationToken);
- return new ValueTask(
- (flushTask == null || flushTask.Status == TaskStatus.RanToCompletion) ? writeTask :
- (writeTask.Status == TaskStatus.RanToCompletion) ? flushTask :
- Task.WhenAll(flushTask, writeTask));
- }
-
- private unsafe Task WriteAsyncInternalCore(ReadOnlyMemory<byte> source, CancellationToken cancellationToken)
- {
- Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed");
- Debug.Assert(CanWrite, "_parent.CanWrite");
- Debug.Assert(_readPos == _readLength, "_readPos == _readLen");
- Debug.Assert(_useAsyncIO, "WriteInternalCoreAsync doesn't work on synchronous file streams!");
-
- // Create and store async stream class library specific data in the async result
- FileStreamCompletionSource completionSource = FileStreamCompletionSource.Create(this, 0, source);
- NativeOverlapped* intOverlapped = completionSource.Overlapped;
-
- if (CanSeek)
- {
- // Make sure we set the length of the file appropriately.
- long len = Length;
-
- // Make sure we are writing to the position that we think we are
- VerifyOSHandlePosition();
-
- if (_filePosition + source.Length > len)
- {
- SetLengthCore(_filePosition + source.Length);
- }
-
- // Now set the position to read from in the NativeOverlapped struct
- // For pipes, we should leave the offset fields set to 0.
- intOverlapped->OffsetLow = (int)_filePosition;
- intOverlapped->OffsetHigh = (int)(_filePosition >> 32);
-
- // When using overlapped IO, the OS is not supposed to
- // touch the file pointer location at all. We will adjust it
- // ourselves. This isn't threadsafe.
- SeekCore(_fileHandle, source.Length, SeekOrigin.Current);
- }
-
- int errorCode = 0;
- // queue an async WriteFile operation and pass in a packed overlapped
- int r = WriteFileNative(_fileHandle, source.Span, intOverlapped, out errorCode);
-
- // WriteFile, the OS version, will return 0 on failure. But
- // my WriteFileNative wrapper returns -1. My wrapper will return
- // the following:
- // On error, r==-1.
- // On async requests that are still pending, r==-1 w/ errorCode==ERROR_IO_PENDING
- // On async requests that completed sequentially, r==0
- // You will NEVER RELIABLY be able to get the number of bytes
- // written back from this call when using overlapped IO! You must
- // not pass in a non-null lpNumBytesWritten to WriteFile when using
- // overlapped structures! This is ByDesign NT behavior.
- if (r == -1)
- {
- // For pipes, when they are closed on the other side, they will come here.
- if (errorCode == ERROR_NO_DATA)
- {
- // Not an error, but EOF. AsyncFSCallback will NOT be called.
- // Completing TCS and return cached task allowing the GC to collect TCS.
- completionSource.SetCompletedSynchronously(0);
- return Task.CompletedTask;
- }
- else if (errorCode != ERROR_IO_PENDING)
- {
- if (!_fileHandle.IsClosed && CanSeek) // Update Position - It could be anywhere.
- {
- SeekCore(_fileHandle, 0, SeekOrigin.Current);
- }
-
- completionSource.ReleaseNativeResource();
-
- if (errorCode == ERROR_HANDLE_EOF)
- {
- throw Error.GetEndOfFile();
- }
- else
- {
- throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path);
- }
- }
- else if (cancellationToken.CanBeCanceled) // ERROR_IO_PENDING
- {
- // Only once the IO is pending do we register for cancellation
- completionSource.RegisterForCancellation(cancellationToken);
- }
- }
- else
- {
- // Due to a workaround for a race condition in NT's ReadFile &
- // WriteFile routines, we will always be returning 0 from WriteFileNative
- // when we do async IO instead of the number of bytes written,
- // irregardless of whether the operation completed
- // synchronously or asynchronously. We absolutely must not
- // set asyncResult._numBytes here, since will never have correct
- // results.
- }
-
- return completionSource.Task;
- }
-
- // Windows API definitions, from winbase.h and others
-
- internal const int GENERIC_READ = unchecked((int)0x80000000);
- private const int GENERIC_WRITE = 0x40000000;
-
- // Error codes (not HRESULTS), from winerror.h
- internal const int ERROR_BROKEN_PIPE = 109;
- internal const int ERROR_NO_DATA = 232;
- private const int ERROR_HANDLE_EOF = 38;
- private const int ERROR_INVALID_PARAMETER = 87;
- private const int ERROR_IO_PENDING = 997;
-
- // __ConsoleStream also uses this code.
- private unsafe int ReadFileNative(SafeFileHandle handle, Span<byte> bytes, NativeOverlapped* overlapped, out int errorCode)
- {
- Debug.Assert(handle != null, "handle != null");
- Debug.Assert((_useAsyncIO && overlapped != null) || (!_useAsyncIO && overlapped == null), "Async IO and overlapped parameters inconsistent in call to ReadFileNative.");
-
- int r;
- int numBytesRead = 0;
-
- fixed (byte* p = &MemoryMarshal.GetReference(bytes))
- {
- r = _useAsyncIO ?
- Interop.Kernel32.ReadFile(handle, p, bytes.Length, IntPtr.Zero, overlapped) :
- Interop.Kernel32.ReadFile(handle, p, bytes.Length, out numBytesRead, IntPtr.Zero);
- }
-
- if (r == 0)
- {
- errorCode = GetLastWin32ErrorAndDisposeHandleIfInvalid();
- return -1;
- }
- else
- {
- errorCode = 0;
- return numBytesRead;
- }
- }
-
- private unsafe int WriteFileNative(SafeFileHandle handle, ReadOnlySpan<byte> buffer, NativeOverlapped* overlapped, out int errorCode)
- {
- Debug.Assert(handle != null, "handle != null");
- Debug.Assert((_useAsyncIO && overlapped != null) || (!_useAsyncIO && overlapped == null), "Async IO and overlapped parameters inconsistent in call to WriteFileNative.");
-
- int numBytesWritten = 0;
- int r;
-
- fixed (byte* p = &MemoryMarshal.GetReference(buffer))
- {
- r = _useAsyncIO ?
- Interop.Kernel32.WriteFile(handle, p, buffer.Length, IntPtr.Zero, overlapped) :
- Interop.Kernel32.WriteFile(handle, p, buffer.Length, out numBytesWritten, IntPtr.Zero);
- }
-
- if (r == 0)
- {
- errorCode = GetLastWin32ErrorAndDisposeHandleIfInvalid();
- return -1;
- }
- else
- {
- errorCode = 0;
- return numBytesWritten;
- }
- }
-
- private int GetLastWin32ErrorAndDisposeHandleIfInvalid()
- {
- int errorCode = Marshal.GetLastWin32Error();
-
- // If ERROR_INVALID_HANDLE is returned, it doesn't suffice to set
- // the handle as invalid; the handle must also be closed.
- //
- // Marking the handle as invalid but not closing the handle
- // resulted in exceptions during finalization and locked column
- // values (due to invalid but unclosed handle) in SQL Win32FileStream
- // scenarios.
- //
- // A more mainstream scenario involves accessing a file on a
- // network share. ERROR_INVALID_HANDLE may occur because the network
- // connection was dropped and the server closed the handle. However,
- // the client side handle is still open and even valid for certain
- // operations.
- //
- // Note that _parent.Dispose doesn't throw so we don't need to special case.
- // SetHandleAsInvalid only sets _closed field to true (without
- // actually closing handle) so we don't need to call that as well.
- if (errorCode == Interop.Errors.ERROR_INVALID_HANDLE)
- {
- _fileHandle.Dispose();
- }
-
- return errorCode;
- }
-
- public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
- {
- // If we're in sync mode, just use the shared CopyToAsync implementation that does
- // typical read/write looping. We also need to take this path if this is a derived
- // instance from FileStream, as a derived type could have overridden ReadAsync, in which
- // case our custom CopyToAsync implementation isn't necessarily correct.
- if (!_useAsyncIO || GetType() != typeof(FileStream))
- {
- return base.CopyToAsync(destination, bufferSize, cancellationToken);
- }
-
- StreamHelpers.ValidateCopyToArgs(this, destination, bufferSize);
-
- // Bail early for cancellation if cancellation has been requested
- if (cancellationToken.IsCancellationRequested)
- {
- return Task.FromCanceled<int>(cancellationToken);
- }
-
- // Fail if the file was closed
- if (_fileHandle.IsClosed)
- {
- throw Error.GetFileNotOpen();
- }
-
- // Do the async copy, with differing implementations based on whether the FileStream was opened as async or sync
- Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both.");
- return AsyncModeCopyToAsync(destination, bufferSize, cancellationToken);
- }
-
- private async Task AsyncModeCopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
- {
- Debug.Assert(_useAsyncIO, "This implementation is for async mode only");
- Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed");
- Debug.Assert(CanRead, "_parent.CanRead");
-
- // Make sure any pending writes have been flushed before we do a read.
- if (_writePos > 0)
- {
- await FlushWriteAsync(cancellationToken).ConfigureAwait(false);
- }
-
- // Typically CopyToAsync would be invoked as the only "read" on the stream, but it's possible some reading is
- // done and then the CopyToAsync is issued. For that case, see if we have any data available in the buffer.
- if (GetBuffer() != null)
- {
- int bufferedBytes = _readLength - _readPos;
- if (bufferedBytes > 0)
- {
- await destination.WriteAsync(new ReadOnlyMemory<byte>(GetBuffer(), _readPos, bufferedBytes), cancellationToken).ConfigureAwait(false);
- _readPos = _readLength = 0;
- }
- }
-
- // For efficiency, we avoid creating a new task and associated state for each asynchronous read.
- // Instead, we create a single reusable awaitable object that will be triggered when an await completes
- // and reset before going again.
- var readAwaitable = new AsyncCopyToAwaitable(this);
-
- // Make sure we are reading from the position that we think we are.
- // Only set the position in the awaitable if we can seek (e.g. not for pipes).
- bool canSeek = CanSeek;
- if (canSeek)
- {
- VerifyOSHandlePosition();
- readAwaitable._position = _filePosition;
- }
-
- // Get the buffer to use for the copy operation, as the base CopyToAsync does. We don't try to use
- // _buffer here, even if it's not null, as concurrent operations are allowed, and another operation may
- // actually be using the buffer already. Plus, it'll be rare for _buffer to be non-null, as typically
- // CopyToAsync is used as the only operation performed on the stream, and the buffer is lazily initialized.
- // Further, typically the CopyToAsync buffer size will be larger than that used by the FileStream, such that
- // we'd likely be unable to use it anyway. Instead, we rent the buffer from a pool.
- byte[] copyBuffer = ArrayPool<byte>.Shared.Rent(bufferSize);
-
- // Allocate an Overlapped we can use repeatedly for all operations
- var awaitableOverlapped = new PreAllocatedOverlapped(AsyncCopyToAwaitable.s_callback, readAwaitable, copyBuffer);
- var cancellationReg = default(CancellationTokenRegistration);
- try
- {
- // Register for cancellation. We do this once for the whole copy operation, and just try to cancel
- // whatever read operation may currently be in progress, if there is one. It's possible the cancellation
- // request could come in between operations, in which case we flag that with explicit calls to ThrowIfCancellationRequested
- // in the read/write copy loop.
- if (cancellationToken.CanBeCanceled)
- {
- cancellationReg = cancellationToken.UnsafeRegister(s =>
- {
- Debug.Assert(s is AsyncCopyToAwaitable);
- var innerAwaitable = (AsyncCopyToAwaitable)s;
- unsafe
- {
- lock (innerAwaitable.CancellationLock) // synchronize with cleanup of the overlapped
- {
- if (innerAwaitable._nativeOverlapped != null)
- {
- // Try to cancel the I/O. We ignore the return value, as cancellation is opportunistic and we
- // don't want to fail the operation because we couldn't cancel it.
- Interop.Kernel32.CancelIoEx(innerAwaitable._fileStream._fileHandle, innerAwaitable._nativeOverlapped);
- }
- }
- }
- }, readAwaitable);
- }
-
- // Repeatedly read from this FileStream and write the results to the destination stream.
- while (true)
- {
- cancellationToken.ThrowIfCancellationRequested();
- readAwaitable.ResetForNextOperation();
-
- try
- {
- bool synchronousSuccess;
- int errorCode;
- unsafe
- {
- // Allocate a native overlapped for our reusable overlapped, and set position to read based on the next
- // desired address stored in the awaitable. (This position may be 0, if either we're at the beginning or
- // if the stream isn't seekable.)
- readAwaitable._nativeOverlapped = _fileHandle.ThreadPoolBinding!.AllocateNativeOverlapped(awaitableOverlapped);
- if (canSeek)
- {
- readAwaitable._nativeOverlapped->OffsetLow = unchecked((int)readAwaitable._position);
- readAwaitable._nativeOverlapped->OffsetHigh = (int)(readAwaitable._position >> 32);
- }
-
- // Kick off the read.
- synchronousSuccess = ReadFileNative(_fileHandle, copyBuffer, readAwaitable._nativeOverlapped, out errorCode) >= 0;
- }
-
- // If the operation did not synchronously succeed, it either failed or initiated the asynchronous operation.
- if (!synchronousSuccess)
- {
- switch (errorCode)
- {
- case ERROR_IO_PENDING:
- // Async operation in progress.
- break;
- case ERROR_BROKEN_PIPE:
- case ERROR_HANDLE_EOF:
- // We're at or past the end of the file, and the overlapped callback
- // won't be raised in these cases. Mark it as completed so that the await
- // below will see it as such.
- readAwaitable.MarkCompleted();
- break;
- default:
- // Everything else is an error (and there won't be a callback).
- throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path);
- }
- }
-
- // Wait for the async operation (which may or may not have already completed), then throw if it failed.
- await readAwaitable;
- switch (readAwaitable._errorCode)
- {
- case 0: // success
- break;
- case ERROR_BROKEN_PIPE: // logically success with 0 bytes read (write end of pipe closed)
- case ERROR_HANDLE_EOF: // logically success with 0 bytes read (read at end of file)
- Debug.Assert(readAwaitable._numBytes == 0, $"Expected 0 bytes read, got {readAwaitable._numBytes}");
- break;
- case Interop.Errors.ERROR_OPERATION_ABORTED: // canceled
- throw new OperationCanceledException(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true));
- default: // error
- throw Win32Marshal.GetExceptionForWin32Error((int)readAwaitable._errorCode, _path);
- }
-
- // Successful operation. If we got zero bytes, we're done: exit the read/write loop.
- int numBytesRead = (int)readAwaitable._numBytes;
- if (numBytesRead == 0)
- {
- break;
- }
-
- // Otherwise, update the read position for next time accordingly.
- if (canSeek)
- {
- readAwaitable._position += numBytesRead;
- }
- }
- finally
- {
- // Free the resources for this read operation
- unsafe
- {
- NativeOverlapped* overlapped;
- lock (readAwaitable.CancellationLock) // just an Exchange, but we need this to be synchronized with cancellation, so using the same lock
- {
- overlapped = readAwaitable._nativeOverlapped;
- readAwaitable._nativeOverlapped = null;
- }
- if (overlapped != null)
- {
- _fileHandle.ThreadPoolBinding!.FreeNativeOverlapped(overlapped);
- }
- }
- }
-
- // Write out the read data.
- await destination.WriteAsync(new ReadOnlyMemory<byte>(copyBuffer, 0, (int)readAwaitable._numBytes), cancellationToken).ConfigureAwait(false);
- }
- }
- finally
- {
- // Cleanup from the whole copy operation
- cancellationReg.Dispose();
- awaitableOverlapped.Dispose();
-
- ArrayPool<byte>.Shared.Return(copyBuffer);
-
- // Make sure the stream's current position reflects where we ended up
- if (!_fileHandle.IsClosed && CanSeek)
- {
- SeekCore(_fileHandle, 0, SeekOrigin.End);
- }
- }
- }
-
- /// <summary>Used by CopyToAsync to enable awaiting the result of an overlapped I/O operation with minimal overhead.</summary>
- private sealed unsafe class AsyncCopyToAwaitable : ICriticalNotifyCompletion
- {
- /// <summary>Sentinel object used to indicate that the I/O operation has completed before being awaited.</summary>
- private static readonly Action s_sentinel = () => { };
- /// <summary>Cached delegate to IOCallback.</summary>
- internal static readonly IOCompletionCallback s_callback = IOCallback;
-
- /// <summary>The FileStream that owns this instance.</summary>
- internal readonly FileStream _fileStream;
-
- /// <summary>Tracked position representing the next location from which to read.</summary>
- internal long _position;
- /// <summary>The current native overlapped pointer. This changes for each operation.</summary>
- internal NativeOverlapped* _nativeOverlapped;
- /// <summary>
- /// null if the operation is still in progress,
- /// s_sentinel if the I/O operation completed before the await,
- /// s_callback if it completed after the await yielded.
- /// </summary>
- internal Action? _continuation;
- /// <summary>Last error code from completed operation.</summary>
- internal uint _errorCode;
- /// <summary>Last number of read bytes from completed operation.</summary>
- internal uint _numBytes;
-
- /// <summary>Lock object used to protect cancellation-related access to _nativeOverlapped.</summary>
- internal object CancellationLock => this;
-
- /// <summary>Initialize the awaitable.</summary>
- internal AsyncCopyToAwaitable(FileStream fileStream)
- {
- _fileStream = fileStream;
- }
-
- /// <summary>Reset state to prepare for the next read operation.</summary>
- internal void ResetForNextOperation()
- {
- Debug.Assert(_position >= 0, $"Expected non-negative position, got {_position}");
- _continuation = null;
- _errorCode = 0;
- _numBytes = 0;
- }
-
- /// <summary>Overlapped callback: store the results, then invoke the continuation delegate.</summary>
- internal static void IOCallback(uint errorCode, uint numBytes, NativeOverlapped* pOVERLAP)
- {
- var awaitable = (AsyncCopyToAwaitable?)ThreadPoolBoundHandle.GetNativeOverlappedState(pOVERLAP);
- Debug.Assert(awaitable != null);
-
- Debug.Assert(!ReferenceEquals(awaitable._continuation, s_sentinel), "Sentinel must not have already been set as the continuation");
- awaitable._errorCode = errorCode;
- awaitable._numBytes = numBytes;
-
- (awaitable._continuation ?? Interlocked.CompareExchange(ref awaitable._continuation, s_sentinel, null))?.Invoke();
- }
-
- /// <summary>
- /// Called when it's known that the I/O callback for an operation will not be invoked but we'll
- /// still be awaiting the awaitable.
- /// </summary>
- internal void MarkCompleted()
- {
- Debug.Assert(_continuation == null, "Expected null continuation");
- _continuation = s_sentinel;
- }
-
- public AsyncCopyToAwaitable GetAwaiter() => this;
- public bool IsCompleted => ReferenceEquals(_continuation, s_sentinel);
- public void GetResult() { }
- public void OnCompleted(Action continuation) => UnsafeOnCompleted(continuation);
- public void UnsafeOnCompleted(Action continuation)
- {
- if (ReferenceEquals(_continuation, s_sentinel) ||
- Interlocked.CompareExchange(ref _continuation, continuation, null) != null)
- {
- Debug.Assert(ReferenceEquals(_continuation, s_sentinel), $"Expected continuation set to s_sentinel, got ${_continuation}");
- Task.Run(continuation);
- }
- }
- }
-
- private Task FlushAsyncInternal(CancellationToken cancellationToken)
- {
- if (cancellationToken.IsCancellationRequested)
- return Task.FromCanceled(cancellationToken);
-
- if (_fileHandle.IsClosed)
- throw Error.GetFileNotOpen();
-
- // TODO: https://github.com/dotnet/corefx/issues/32837 (stop doing this synchronous work!!).
- // The always synchronous data transfer between the OS and the internal buffer is intentional
- // because this is needed to allow concurrent async IO requests. Concurrent data transfer
- // between the OS and the internal buffer will result in race conditions. Since FlushWrite and
- // FlushRead modify internal state of the stream and transfer data between the OS and the
- // internal buffer, they cannot be truly async. We will, however, flush the OS file buffers
- // asynchronously because it doesn't modify any internal state of the stream and is potentially
- // a long running process.
- try
- {
- FlushInternalBuffer();
- }
- catch (Exception e)
- {
- return Task.FromException(e);
- }
-
- return Task.CompletedTask;
- }
-
- private void LockInternal(long position, long length)
- {
- int positionLow = unchecked((int)(position));
- int positionHigh = unchecked((int)(position >> 32));
- int lengthLow = unchecked((int)(length));
- int lengthHigh = unchecked((int)(length >> 32));
-
- if (!Interop.Kernel32.LockFile(_fileHandle, positionLow, positionHigh, lengthLow, lengthHigh))
- {
- throw Win32Marshal.GetExceptionForLastWin32Error(_path);
- }
- }
-
- private void UnlockInternal(long position, long length)
- {
- int positionLow = unchecked((int)(position));
- int positionHigh = unchecked((int)(position >> 32));
- int lengthLow = unchecked((int)(length));
- int lengthHigh = unchecked((int)(length >> 32));
-
- if (!Interop.Kernel32.UnlockFile(_fileHandle, positionLow, positionHigh, lengthLow, lengthHigh))
- {
- throw Win32Marshal.GetExceptionForLastWin32Error(_path);
- }
- }
-
- private SafeFileHandle ValidateFileHandle(SafeFileHandle fileHandle)
- {
- if (fileHandle.IsInvalid)
- {
- // Return a meaningful exception with the full path.
-
- // NT5 oddity - when trying to open "C:\" as a Win32FileStream,
- // we usually get ERROR_PATH_NOT_FOUND from the OS. We should
- // probably be consistent w/ every other directory.
- int errorCode = Marshal.GetLastWin32Error();
-
- if (errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND && _path!.Length == PathInternal.GetRootLength(_path))
- errorCode = Interop.Errors.ERROR_ACCESS_DENIED;
-
- throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path);
- }
-
- fileHandle.IsAsync = _useAsyncIO;
- return fileHandle;
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/FileStream.cs b/netcore/System.Private.CoreLib/shared/System/IO/FileStream.cs
deleted file mode 100644
index 05b97e1bcc6..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/FileStream.cs
+++ /dev/null
@@ -1,915 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-using System.Runtime.Serialization;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Win32.SafeHandles;
-
-namespace System.IO
-{
- public partial class FileStream : Stream
- {
- private const FileShare DefaultShare = FileShare.Read;
- private const bool DefaultIsAsync = false;
- internal const int DefaultBufferSize = 4096;
-
- private byte[]? _buffer;
- private int _bufferLength;
- private readonly SafeFileHandle _fileHandle; // only ever null if ctor throws
-
- /// <summary>Whether the file is opened for reading, writing, or both.</summary>
- private readonly FileAccess _access;
-
- /// <summary>The path to the opened file.</summary>
- private readonly string? _path;
-
- /// <summary>The next available byte to be read from the _buffer.</summary>
- private int _readPos;
-
- /// <summary>The number of valid bytes in _buffer.</summary>
- private int _readLength;
-
- /// <summary>The next location in which a write should occur to the buffer.</summary>
- private int _writePos;
-
- /// <summary>
- /// Whether asynchronous read/write/flush operations should be performed using async I/O.
- /// On Windows FileOptions.Asynchronous controls how the file handle is configured,
- /// and then as a result how operations are issued against that file handle. On Unix,
- /// there isn't any distinction around how file descriptors are created for async vs
- /// sync, but we still differentiate how the operations are issued in order to provide
- /// similar behavioral semantics and performance characteristics as on Windows. On
- /// Windows, if non-async, async read/write requests just delegate to the base stream,
- /// and no attempt is made to synchronize between sync and async operations on the stream;
- /// if async, then async read/write requests are implemented specially, and sync read/write
- /// requests are coordinated with async ones by implementing the sync ones over the async
- /// ones. On Unix, we do something similar. If non-async, async read/write requests just
- /// delegate to the base stream, and no attempt is made to synchronize. If async, we use
- /// a semaphore to coordinate both sync and async operations.
- /// </summary>
- private readonly bool _useAsyncIO;
-
- /// <summary>cached task for read ops that complete synchronously</summary>
- private Task<int>? _lastSynchronouslyCompletedTask = null;
-
- /// <summary>
- /// Currently cached position in the stream. This should always mirror the underlying file's actual position,
- /// and should only ever be out of sync if another stream with access to this same file manipulates it, at which
- /// point we attempt to error out.
- /// </summary>
- private long _filePosition;
-
- /// <summary>Whether the file stream's handle has been exposed.</summary>
- private bool _exposedHandle;
-
- /// <summary>Caches whether Serialization Guard has been disabled for file writes</summary>
- private static int s_cachedSerializationSwitch = 0;
-
- [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access) instead. https://go.microsoft.com/fwlink/?linkid=14202")]
- public FileStream(IntPtr handle, FileAccess access)
- : this(handle, access, true, DefaultBufferSize, false)
- {
- }
-
- [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. https://go.microsoft.com/fwlink/?linkid=14202")]
- public FileStream(IntPtr handle, FileAccess access, bool ownsHandle)
- : this(handle, access, ownsHandle, DefaultBufferSize, false)
- {
- }
-
- [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. https://go.microsoft.com/fwlink/?linkid=14202")]
- public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize)
- : this(handle, access, ownsHandle, bufferSize, false)
- {
- }
-
- [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. https://go.microsoft.com/fwlink/?linkid=14202")]
- public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync)
- {
- SafeFileHandle safeHandle = new SafeFileHandle(handle, ownsHandle: ownsHandle);
- try
- {
- ValidateAndInitFromHandle(safeHandle, access, bufferSize, isAsync);
- }
- catch
- {
- // We don't want to take ownership of closing passed in handles
- // *unless* the constructor completes successfully.
- GC.SuppressFinalize(safeHandle);
-
- // This would also prevent Close from being called, but is unnecessary
- // as we've removed the object from the finalizer queue.
- //
- // safeHandle.SetHandleAsInvalid();
- throw;
- }
-
- // Note: Cleaner to set the following fields in ValidateAndInitFromHandle,
- // but we can't as they're readonly.
- _access = access;
- _useAsyncIO = isAsync;
-
- // As the handle was passed in, we must set the handle field at the very end to
- // avoid the finalizer closing the handle when we throw errors.
- _fileHandle = safeHandle;
- }
-
- public FileStream(SafeFileHandle handle, FileAccess access)
- : this(handle, access, DefaultBufferSize)
- {
- }
-
- public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize)
- : this(handle, access, bufferSize, GetDefaultIsAsync(handle))
- {
- }
-
- private void ValidateAndInitFromHandle(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync)
- {
- if (handle.IsInvalid)
- throw new ArgumentException(SR.Arg_InvalidHandle, nameof(handle));
-
- if (access < FileAccess.Read || access > FileAccess.ReadWrite)
- throw new ArgumentOutOfRangeException(nameof(access), SR.ArgumentOutOfRange_Enum);
- if (bufferSize <= 0)
- throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);
-
- if (handle.IsClosed)
- throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed);
- if (handle.IsAsync.HasValue && isAsync != handle.IsAsync.GetValueOrDefault())
- throw new ArgumentException(SR.Arg_HandleNotAsync, nameof(handle));
-
- _exposedHandle = true;
- _bufferLength = bufferSize;
-
- InitFromHandle(handle, access, isAsync);
- }
-
- public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync)
- {
- ValidateAndInitFromHandle(handle, access, bufferSize, isAsync);
-
- // Note: Cleaner to set the following fields in ValidateAndInitFromHandle,
- // but we can't as they're readonly.
- _access = access;
- _useAsyncIO = isAsync;
-
- // As the handle was passed in, we must set the handle field at the very end to
- // avoid the finalizer closing the handle when we throw errors.
- _fileHandle = handle;
- }
-
- public FileStream(string path, FileMode mode) :
- this(path, mode, mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite, DefaultShare, DefaultBufferSize, DefaultIsAsync)
- { }
-
- public FileStream(string path, FileMode mode, FileAccess access) :
- this(path, mode, access, DefaultShare, DefaultBufferSize, DefaultIsAsync)
- { }
-
- public FileStream(string path, FileMode mode, FileAccess access, FileShare share) :
- this(path, mode, access, share, DefaultBufferSize, DefaultIsAsync)
- { }
-
- public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize) :
- this(path, mode, access, share, bufferSize, DefaultIsAsync)
- { }
-
- public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync) :
- this(path, mode, access, share, bufferSize, useAsync ? FileOptions.Asynchronous : FileOptions.None)
- { }
-
- public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
- {
- if (path == null)
- throw new ArgumentNullException(nameof(path), SR.ArgumentNull_Path);
- if (path.Length == 0)
- throw new ArgumentException(SR.Argument_EmptyPath, nameof(path));
-
- // don't include inheritable in our bounds check for share
- FileShare tempshare = share & ~FileShare.Inheritable;
- string? badArg = null;
-
- if (mode < FileMode.CreateNew || mode > FileMode.Append)
- badArg = nameof(mode);
- else if (access < FileAccess.Read || access > FileAccess.ReadWrite)
- badArg = nameof(access);
- else if (tempshare < FileShare.None || tempshare > (FileShare.ReadWrite | FileShare.Delete))
- badArg = nameof(share);
-
- if (badArg != null)
- throw new ArgumentOutOfRangeException(badArg, SR.ArgumentOutOfRange_Enum);
-
- // NOTE: any change to FileOptions enum needs to be matched here in the error validation
- if (options != FileOptions.None && (options & ~(FileOptions.WriteThrough | FileOptions.Asynchronous | FileOptions.RandomAccess | FileOptions.DeleteOnClose | FileOptions.SequentialScan | FileOptions.Encrypted | (FileOptions)0x20000000 /* NoBuffering */)) != 0)
- throw new ArgumentOutOfRangeException(nameof(options), SR.ArgumentOutOfRange_Enum);
-
- if (bufferSize <= 0)
- throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);
-
- // Write access validation
- if ((access & FileAccess.Write) == 0)
- {
- if (mode == FileMode.Truncate || mode == FileMode.CreateNew || mode == FileMode.Create || mode == FileMode.Append)
- {
- // No write access, mode and access disagree but flag access since mode comes first
- throw new ArgumentException(SR.Format(SR.Argument_InvalidFileModeAndAccessCombo, mode, access), nameof(access));
- }
- }
-
- if ((access & FileAccess.Read) != 0 && mode == FileMode.Append)
- throw new ArgumentException(SR.Argument_InvalidAppendMode, nameof(access));
-
- string fullPath = Path.GetFullPath(path);
-
- _path = fullPath;
- _access = access;
- _bufferLength = bufferSize;
-
- if ((options & FileOptions.Asynchronous) != 0)
- _useAsyncIO = true;
-
- if ((access & FileAccess.Write) == FileAccess.Write)
- {
- SerializationInfo.ThrowIfDeserializationInProgress("AllowFileWrites", ref s_cachedSerializationSwitch);
- }
-
- _fileHandle = OpenHandle(mode, share, options);
-
- try
- {
- Init(mode, share, path);
- }
- catch
- {
- // If anything goes wrong while setting up the stream, make sure we deterministically dispose
- // of the opened handle.
- _fileHandle.Dispose();
- _fileHandle = null!;
- throw;
- }
- }
-
- [Obsolete("This property has been deprecated. Please use FileStream's SafeFileHandle property instead. https://go.microsoft.com/fwlink/?linkid=14202")]
- public virtual IntPtr Handle => SafeFileHandle.DangerousGetHandle();
-
- public virtual void Lock(long position, long length)
- {
- if (position < 0 || length < 0)
- {
- throw new ArgumentOutOfRangeException(position < 0 ? nameof(position) : nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
-
- if (_fileHandle.IsClosed)
- {
- throw Error.GetFileNotOpen();
- }
-
- LockInternal(position, length);
- }
-
- public virtual void Unlock(long position, long length)
- {
- if (position < 0 || length < 0)
- {
- throw new ArgumentOutOfRangeException(position < 0 ? nameof(position) : nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
-
- if (_fileHandle.IsClosed)
- {
- throw Error.GetFileNotOpen();
- }
-
- UnlockInternal(position, length);
- }
-
- public override Task FlushAsync(CancellationToken cancellationToken)
- {
- // If we have been inherited into a subclass, the following implementation could be incorrect
- // since it does not call through to Flush() which a subclass might have overridden. To be safe
- // we will only use this implementation in cases where we know it is safe to do so,
- // and delegate to our base class (which will call into Flush) when we are not sure.
- if (GetType() != typeof(FileStream))
- return base.FlushAsync(cancellationToken);
-
- return FlushAsyncInternal(cancellationToken);
- }
-
- public override int Read(byte[] array, int offset, int count)
- {
- ValidateReadWriteArgs(array, offset, count);
- return _useAsyncIO ?
- ReadAsyncTask(array, offset, count, CancellationToken.None).GetAwaiter().GetResult() :
- ReadSpan(new Span<byte>(array, offset, count));
- }
-
- public override int Read(Span<byte> buffer)
- {
- if (GetType() == typeof(FileStream) && !_useAsyncIO)
- {
- if (_fileHandle.IsClosed)
- {
- throw Error.GetFileNotOpen();
- }
- return ReadSpan(buffer);
- }
- else
- {
- // This type is derived from FileStream and/or the stream is in async mode. If this is a
- // derived type, it may have overridden Read(byte[], int, int) prior to this Read(Span<byte>)
- // overload being introduced. In that case, this Read(Span<byte>) overload should use the behavior
- // of Read(byte[],int,int) overload. Or if the stream is in async mode, we can't call the
- // synchronous ReadSpan, so we similarly call the base Read, which will turn delegate to
- // Read(byte[],int,int), which will do the right thing if we're in async mode.
- return base.Read(buffer);
- }
- }
-
- public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
- {
- if (buffer == null)
- throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
- if (offset < 0)
- throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (count < 0)
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (buffer.Length - offset < count)
- throw new ArgumentException(SR.Argument_InvalidOffLen /*, no good single parameter name to pass*/);
-
- if (GetType() != typeof(FileStream))
- {
- // If we have been inherited into a subclass, the following implementation could be incorrect
- // since it does not call through to Read() which a subclass might have overridden.
- // To be safe we will only use this implementation in cases where we know it is safe to do so,
- // and delegate to our base class (which will call into Read/ReadAsync) when we are not sure.
- return base.ReadAsync(buffer, offset, count, cancellationToken);
- }
-
- if (cancellationToken.IsCancellationRequested)
- return Task.FromCanceled<int>(cancellationToken);
-
- if (IsClosed)
- throw Error.GetFileNotOpen();
-
- if (!_useAsyncIO)
- {
- // If we weren't opened for asynchronous I/O, we still call to the base implementation so that
- // Read is invoked asynchronously. But we can do so using the base Stream's internal helper
- // that bypasses delegating to BeginRead, since we already know this is FileStream rather
- // than something derived from it and what our BeginRead implementation is going to do.
- return (Task<int>)base.BeginReadInternal(buffer, offset, count, null, null, serializeAsynchronously: true, apm: false);
- }
-
- return ReadAsyncTask(buffer, offset, count, cancellationToken);
- }
-
- public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
- {
- if (GetType() != typeof(FileStream))
- {
- // If this isn't a concrete FileStream, a derived type may have overridden ReadAsync(byte[],...),
- // which was introduced first, so delegate to the base which will delegate to that.
- return base.ReadAsync(buffer, cancellationToken);
- }
-
- if (cancellationToken.IsCancellationRequested)
- {
- return new ValueTask<int>(Task.FromCanceled<int>(cancellationToken));
- }
-
- if (IsClosed)
- {
- throw Error.GetFileNotOpen();
- }
-
- if (!_useAsyncIO)
- {
- // If we weren't opened for asynchronous I/O, we still call to the base implementation so that
- // Read is invoked asynchronously. But if we have a byte[], we can do so using the base Stream's
- // internal helper that bypasses delegating to BeginRead, since we already know this is FileStream
- // rather than something derived from it and what our BeginRead implementation is going to do.
- return MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> segment) ?
- new ValueTask<int>((Task<int>)base.BeginReadInternal(segment.Array!, segment.Offset, segment.Count, null, null, serializeAsynchronously: true, apm: false)) :
- base.ReadAsync(buffer, cancellationToken);
- }
-
- Task<int>? t = ReadAsyncInternal(buffer, cancellationToken, out int synchronousResult);
- return t != null ?
- new ValueTask<int>(t) :
- new ValueTask<int>(synchronousResult);
- }
-
- private Task<int> ReadAsyncTask(byte[] array, int offset, int count, CancellationToken cancellationToken)
- {
- Task<int>? t = ReadAsyncInternal(new Memory<byte>(array, offset, count), cancellationToken, out int synchronousResult);
-
- if (t == null)
- {
- t = _lastSynchronouslyCompletedTask;
- Debug.Assert(t == null || t.IsCompletedSuccessfully, "Cached task should have completed successfully");
-
- if (t == null || t.Result != synchronousResult)
- {
- _lastSynchronouslyCompletedTask = t = Task.FromResult(synchronousResult);
- }
- }
-
- return t;
- }
-
- public override void Write(byte[] array, int offset, int count)
- {
- ValidateReadWriteArgs(array, offset, count);
- if (_useAsyncIO)
- {
- WriteAsyncInternal(new ReadOnlyMemory<byte>(array, offset, count), CancellationToken.None).GetAwaiter().GetResult();
- }
- else
- {
- WriteSpan(new ReadOnlySpan<byte>(array, offset, count));
- }
- }
-
- public override void Write(ReadOnlySpan<byte> buffer)
- {
- if (GetType() == typeof(FileStream) && !_useAsyncIO)
- {
- if (_fileHandle.IsClosed)
- {
- throw Error.GetFileNotOpen();
- }
- WriteSpan(buffer);
- }
- else
- {
- // This type is derived from FileStream and/or the stream is in async mode. If this is a
- // derived type, it may have overridden Write(byte[], int, int) prior to this Write(ReadOnlySpan<byte>)
- // overload being introduced. In that case, this Write(ReadOnlySpan<byte>) overload should use the behavior
- // of Write(byte[],int,int) overload. Or if the stream is in async mode, we can't call the
- // synchronous WriteSpan, so we similarly call the base Write, which will turn delegate to
- // Write(byte[],int,int), which will do the right thing if we're in async mode.
- base.Write(buffer);
- }
- }
-
- public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
- {
- if (buffer == null)
- throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
- if (offset < 0)
- throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (count < 0)
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (buffer.Length - offset < count)
- throw new ArgumentException(SR.Argument_InvalidOffLen /*, no good single parameter name to pass*/);
-
- if (GetType() != typeof(FileStream))
- {
- // If we have been inherited into a subclass, the following implementation could be incorrect
- // since it does not call through to Write() or WriteAsync() which a subclass might have overridden.
- // To be safe we will only use this implementation in cases where we know it is safe to do so,
- // and delegate to our base class (which will call into Write/WriteAsync) when we are not sure.
- return base.WriteAsync(buffer, offset, count, cancellationToken);
- }
-
- if (cancellationToken.IsCancellationRequested)
- return Task.FromCanceled(cancellationToken);
-
- if (IsClosed)
- throw Error.GetFileNotOpen();
-
- if (!_useAsyncIO)
- {
- // If we weren't opened for asynchronous I/O, we still call to the base implementation so that
- // Write is invoked asynchronously. But we can do so using the base Stream's internal helper
- // that bypasses delegating to BeginWrite, since we already know this is FileStream rather
- // than something derived from it and what our BeginWrite implementation is going to do.
- return (Task)base.BeginWriteInternal(buffer, offset, count, null, null, serializeAsynchronously: true, apm: false);
- }
-
- return WriteAsyncInternal(new ReadOnlyMemory<byte>(buffer, offset, count), cancellationToken).AsTask();
- }
-
- public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
- {
- if (GetType() != typeof(FileStream))
- {
- // If this isn't a concrete FileStream, a derived type may have overridden WriteAsync(byte[],...),
- // which was introduced first, so delegate to the base which will delegate to that.
- return base.WriteAsync(buffer, cancellationToken);
- }
-
- if (cancellationToken.IsCancellationRequested)
- {
- return new ValueTask(Task.FromCanceled<int>(cancellationToken));
- }
-
- if (IsClosed)
- {
- throw Error.GetFileNotOpen();
- }
-
- if (!_useAsyncIO)
- {
- // If we weren't opened for asynchronous I/O, we still call to the base implementation so that
- // Write is invoked asynchronously. But if we have a byte[], we can do so using the base Stream's
- // internal helper that bypasses delegating to BeginWrite, since we already know this is FileStream
- // rather than something derived from it and what our BeginWrite implementation is going to do.
- return MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> segment) ?
- new ValueTask((Task)BeginWriteInternal(segment.Array!, segment.Offset, segment.Count, null, null, serializeAsynchronously: true, apm: false)) :
- base.WriteAsync(buffer, cancellationToken);
- }
-
- return WriteAsyncInternal(buffer, cancellationToken);
- }
-
- /// <summary>
- /// Clears buffers for this stream and causes any buffered data to be written to the file.
- /// </summary>
- public override void Flush()
- {
- // Make sure that we call through the public virtual API
- Flush(flushToDisk: false);
- }
-
- /// <summary>
- /// Clears buffers for this stream, and if <param name="flushToDisk"/> is true,
- /// causes any buffered data to be written to the file.
- /// </summary>
- public virtual void Flush(bool flushToDisk)
- {
- if (IsClosed) throw Error.GetFileNotOpen();
-
- FlushInternalBuffer();
-
- if (flushToDisk && CanWrite)
- {
- FlushOSBuffer();
- }
- }
-
- /// <summary>Gets a value indicating whether the current stream supports reading.</summary>
- public override bool CanRead => !_fileHandle.IsClosed && (_access & FileAccess.Read) != 0;
-
- /// <summary>Gets a value indicating whether the current stream supports writing.</summary>
- public override bool CanWrite => !_fileHandle.IsClosed && (_access & FileAccess.Write) != 0;
-
- /// <summary>Validates arguments to Read and Write and throws resulting exceptions.</summary>
- /// <param name="array">The buffer to read from or write to.</param>
- /// <param name="offset">The zero-based offset into the array.</param>
- /// <param name="count">The maximum number of bytes to read or write.</param>
- private void ValidateReadWriteArgs(byte[] array, int offset, int count)
- {
- if (array == null)
- throw new ArgumentNullException(nameof(array), SR.ArgumentNull_Buffer);
- if (offset < 0)
- throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (count < 0)
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (array.Length - offset < count)
- throw new ArgumentException(SR.Argument_InvalidOffLen /*, no good single parameter name to pass*/);
- if (_fileHandle.IsClosed)
- throw Error.GetFileNotOpen();
- }
-
- /// <summary>Sets the length of this stream to the given value.</summary>
- /// <param name="value">The new length of the stream.</param>
- public override void SetLength(long value)
- {
- if (value < 0)
- throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (_fileHandle.IsClosed)
- throw Error.GetFileNotOpen();
- if (!CanSeek)
- throw Error.GetSeekNotSupported();
- if (!CanWrite)
- throw Error.GetWriteNotSupported();
-
- SetLengthInternal(value);
- }
-
- public virtual SafeFileHandle SafeFileHandle
- {
- get
- {
- Flush();
- _exposedHandle = true;
- return _fileHandle;
- }
- }
-
- /// <summary>Gets the path that was passed to the constructor.</summary>
- public virtual string Name => _path ?? SR.IO_UnknownFileName;
-
- /// <summary>Gets a value indicating whether the stream was opened for I/O to be performed synchronously or asynchronously.</summary>
- public virtual bool IsAsync => _useAsyncIO;
-
- /// <summary>Gets the length of the stream in bytes.</summary>
- public override long Length
- {
- get
- {
- if (_fileHandle.IsClosed) throw Error.GetFileNotOpen();
- if (!CanSeek) throw Error.GetSeekNotSupported();
- return GetLengthInternal();
- }
- }
-
- /// <summary>
- /// Verify that the actual position of the OS's handle equals what we expect it to.
- /// This will fail if someone else moved the UnixFileStream's handle or if
- /// our position updating code is incorrect.
- /// </summary>
- private void VerifyOSHandlePosition()
- {
- bool verifyPosition = _exposedHandle; // in release, only verify if we've given out the handle such that someone else could be manipulating it
-#if DEBUG
- verifyPosition = true; // in debug, always make sure our position matches what the OS says it should be
-#endif
- if (verifyPosition && CanSeek)
- {
- long oldPos = _filePosition; // SeekCore will override the current _position, so save it now
- long curPos = SeekCore(_fileHandle, 0, SeekOrigin.Current);
- if (oldPos != curPos)
- {
- // For reads, this is non-fatal but we still could have returned corrupted
- // data in some cases, so discard the internal buffer. For writes,
- // this is a problem; discard the buffer and error out.
- _readPos = _readLength = 0;
- if (_writePos > 0)
- {
- _writePos = 0;
- throw new IOException(SR.IO_FileStreamHandlePosition);
- }
- }
- }
- }
-
- /// <summary>Verifies that state relating to the read/write buffer is consistent.</summary>
- [Conditional("DEBUG")]
- private void AssertBufferInvariants()
- {
- // Read buffer values must be in range: 0 <= _bufferReadPos <= _bufferReadLength <= _bufferLength
- Debug.Assert(0 <= _readPos && _readPos <= _readLength && _readLength <= _bufferLength);
-
- // Write buffer values must be in range: 0 <= _bufferWritePos <= _bufferLength
- Debug.Assert(0 <= _writePos && _writePos <= _bufferLength);
-
- // Read buffering and write buffering can't both be active
- Debug.Assert((_readPos == 0 && _readLength == 0) || _writePos == 0);
- }
-
- /// <summary>Validates that we're ready to read from the stream.</summary>
- private void PrepareForReading()
- {
- if (_fileHandle.IsClosed)
- throw Error.GetFileNotOpen();
- if (_readLength == 0 && !CanRead)
- throw Error.GetReadNotSupported();
-
- AssertBufferInvariants();
- }
-
- /// <summary>Gets or sets the position within the current stream</summary>
- public override long Position
- {
- get
- {
- if (_fileHandle.IsClosed)
- throw Error.GetFileNotOpen();
-
- if (!CanSeek)
- throw Error.GetSeekNotSupported();
-
- AssertBufferInvariants();
- VerifyOSHandlePosition();
-
- // We may have read data into our buffer from the handle, such that the handle position
- // is artificially further along than the consumer's view of the stream's position.
- // Thus, when reading, our position is really starting from the handle position negatively
- // offset by the number of bytes in the buffer and positively offset by the number of
- // bytes into that buffer we've read. When writing, both the read length and position
- // must be zero, and our position is just the handle position offset positive by how many
- // bytes we've written into the buffer.
- return (_filePosition - _readLength) + _readPos + _writePos;
- }
- set
- {
- if (value < 0)
- throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum);
-
- Seek(value, SeekOrigin.Begin);
- }
- }
-
- internal virtual bool IsClosed => _fileHandle.IsClosed;
-
- private static bool IsIoRelatedException(Exception e) =>
- // These all derive from IOException
- // DirectoryNotFoundException
- // DriveNotFoundException
- // EndOfStreamException
- // FileLoadException
- // FileNotFoundException
- // PathTooLongException
- // PipeException
- e is IOException ||
- // Note that SecurityException is only thrown on runtimes that support CAS
- // e is SecurityException ||
- e is UnauthorizedAccessException ||
- e is NotSupportedException ||
- (e is ArgumentException && !(e is ArgumentNullException));
-
- /// <summary>
- /// Gets the array used for buffering reading and writing.
- /// If the array hasn't been allocated, this will lazily allocate it.
- /// </summary>
- /// <returns>The buffer.</returns>
- private byte[] GetBuffer()
- {
- Debug.Assert(_buffer == null || _buffer.Length == _bufferLength);
- if (_buffer == null)
- {
- _buffer = new byte[_bufferLength];
- OnBufferAllocated();
- }
-
- return _buffer;
- }
-
- partial void OnBufferAllocated();
-
- /// <summary>
- /// Flushes the internal read/write buffer for this stream. If write data has been buffered,
- /// that data is written out to the underlying file. Or if data has been buffered for
- /// reading from the stream, the data is dumped and our position in the underlying file
- /// is rewound as necessary. This does not flush the OS buffer.
- /// </summary>
- private void FlushInternalBuffer()
- {
- AssertBufferInvariants();
- if (_writePos > 0)
- {
- FlushWriteBuffer();
- }
- else if (_readPos < _readLength && CanSeek)
- {
- FlushReadBuffer();
- }
- }
-
- /// <summary>Dumps any read data in the buffer and rewinds our position in the stream, accordingly, as necessary.</summary>
- private void FlushReadBuffer()
- {
- // Reading is done by blocks from the file, but someone could read
- // 1 byte from the buffer then write. At that point, the OS's file
- // pointer is out of sync with the stream's position. All write
- // functions should call this function to preserve the position in the file.
-
- AssertBufferInvariants();
- Debug.Assert(_writePos == 0, "FileStream: Write buffer must be empty in FlushReadBuffer!");
-
- int rewind = _readPos - _readLength;
- if (rewind != 0)
- {
- Debug.Assert(CanSeek, "FileStream will lose buffered read data now.");
- SeekCore(_fileHandle, rewind, SeekOrigin.Current);
- }
- _readPos = _readLength = 0;
- }
-
- /// <summary>
- /// Reads a byte from the file stream. Returns the byte cast to an int
- /// or -1 if reading from the end of the stream.
- /// </summary>
- public override int ReadByte()
- {
- PrepareForReading();
-
- byte[] buffer = GetBuffer();
- if (_readPos == _readLength)
- {
- FlushWriteBuffer();
- _readLength = FillReadBufferForReadByte();
- _readPos = 0;
- if (_readLength == 0)
- {
- return -1;
- }
- }
-
- return buffer[_readPos++];
- }
-
- /// <summary>
- /// Writes a byte to the current position in the stream and advances the position
- /// within the stream by one byte.
- /// </summary>
- /// <param name="value">The byte to write to the stream.</param>
- public override void WriteByte(byte value)
- {
- PrepareForWriting();
-
- // Flush the write buffer if it's full
- if (_writePos == _bufferLength)
- FlushWriteBufferForWriteByte();
-
- // We now have space in the buffer. Store the byte.
- GetBuffer()[_writePos++] = value;
- }
-
- /// <summary>
- /// Validates that we're ready to write to the stream,
- /// including flushing a read buffer if necessary.
- /// </summary>
- private void PrepareForWriting()
- {
- if (_fileHandle.IsClosed)
- throw Error.GetFileNotOpen();
-
- // Make sure we're good to write. We only need to do this if there's nothing already
- // in our write buffer, since if there is something in the buffer, we've already done
- // this checking and flushing.
- if (_writePos == 0)
- {
- if (!CanWrite) throw Error.GetWriteNotSupported();
- FlushReadBuffer();
- Debug.Assert(_bufferLength > 0, "_bufferSize > 0");
- }
- }
-
- ~FileStream()
- {
- // Preserved for compatibility since FileStream has defined a
- // finalizer in past releases and derived classes may depend
- // on Dispose(false) call.
- Dispose(false);
- }
-
- public override IAsyncResult BeginRead(byte[] array, int offset, int numBytes, AsyncCallback callback, object? state)
- {
- if (array == null)
- throw new ArgumentNullException(nameof(array));
- if (offset < 0)
- throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (numBytes < 0)
- throw new ArgumentOutOfRangeException(nameof(numBytes), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (array.Length - offset < numBytes)
- throw new ArgumentException(SR.Argument_InvalidOffLen);
-
- if (IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed);
- if (!CanRead) throw new NotSupportedException(SR.NotSupported_UnreadableStream);
-
- if (!IsAsync)
- return base.BeginRead(array, offset, numBytes, callback, state);
- else
- return TaskToApm.Begin(ReadAsyncTask(array, offset, numBytes, CancellationToken.None), callback, state);
- }
-
- public override IAsyncResult BeginWrite(byte[] array, int offset, int numBytes, AsyncCallback callback, object? state)
- {
- if (array == null)
- throw new ArgumentNullException(nameof(array));
- if (offset < 0)
- throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (numBytes < 0)
- throw new ArgumentOutOfRangeException(nameof(numBytes), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (array.Length - offset < numBytes)
- throw new ArgumentException(SR.Argument_InvalidOffLen);
-
- if (IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed);
- if (!CanWrite) throw new NotSupportedException(SR.NotSupported_UnwritableStream);
-
- if (!IsAsync)
- return base.BeginWrite(array, offset, numBytes, callback, state);
- else
- return TaskToApm.Begin(WriteAsyncInternal(new ReadOnlyMemory<byte>(array, offset, numBytes), CancellationToken.None).AsTask(), callback, state);
- }
-
- public override int EndRead(IAsyncResult asyncResult)
- {
- if (asyncResult == null)
- throw new ArgumentNullException(nameof(asyncResult));
-
- if (!IsAsync)
- return base.EndRead(asyncResult);
- else
- return TaskToApm.End<int>(asyncResult);
- }
-
- public override void EndWrite(IAsyncResult asyncResult)
- {
- if (asyncResult == null)
- throw new ArgumentNullException(nameof(asyncResult));
-
- if (!IsAsync)
- base.EndWrite(asyncResult);
- else
- TaskToApm.End(asyncResult);
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/FileStreamCompletionSource.Win32.cs b/netcore/System.Private.CoreLib/shared/System/IO/FileStreamCompletionSource.Win32.cs
deleted file mode 100644
index 7345afec3f1..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/FileStreamCompletionSource.Win32.cs
+++ /dev/null
@@ -1,259 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Buffers;
-using System.Diagnostics;
-using System.Runtime.ExceptionServices;
-using System.Runtime.InteropServices;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace System.IO
-{
- public partial class FileStream : Stream
- {
- // This is an internal object extending TaskCompletionSource with fields
- // for all of the relevant data necessary to complete the IO operation.
- // This is used by IOCallback and all of the async methods.
- private unsafe class FileStreamCompletionSource : TaskCompletionSource<int>
- {
- private const long NoResult = 0;
- private const long ResultSuccess = (long)1 << 32;
- private const long ResultError = (long)2 << 32;
- private const long RegisteringCancellation = (long)4 << 32;
- private const long CompletedCallback = (long)8 << 32;
- private const ulong ResultMask = ((ulong)uint.MaxValue) << 32;
-
- private static Action<object?>? s_cancelCallback;
-
- private readonly FileStream _stream;
- private readonly int _numBufferedBytes;
- private CancellationTokenRegistration _cancellationRegistration;
-#if DEBUG
- private bool _cancellationHasBeenRegistered;
-#endif
- private NativeOverlapped* _overlapped; // Overlapped class responsible for operations in progress when an appdomain unload occurs
- private long _result; // Using long since this needs to be used in Interlocked APIs
-
- // Using RunContinuationsAsynchronously for compat reasons (old API used Task.Factory.StartNew for continuations)
- protected FileStreamCompletionSource(FileStream stream, int numBufferedBytes, byte[]? bytes)
- : base(TaskCreationOptions.RunContinuationsAsynchronously)
- {
- _numBufferedBytes = numBufferedBytes;
- _stream = stream;
- _result = NoResult;
-
- // Create the native overlapped. We try to use the preallocated overlapped if possible: it's possible if the byte
- // buffer is null (there's nothing to pin) or the same one that's associated with the preallocated overlapped (and
- // thus is already pinned) and if no one else is currently using the preallocated overlapped. This is the fast-path
- // for cases where the user-provided buffer is smaller than the FileStream's buffer (such that the FileStream's
- // buffer is used) and where operations on the FileStream are not being performed concurrently.
- Debug.Assert(bytes == null || ReferenceEquals(bytes, _stream._buffer));
-
- // The _preallocatedOverlapped is null if the internal buffer was never created, so we check for
- // a non-null bytes before using the stream's _preallocatedOverlapped
- _overlapped = bytes != null && _stream.CompareExchangeCurrentOverlappedOwner(this, null) == null ?
- _stream._fileHandle.ThreadPoolBinding!.AllocateNativeOverlapped(_stream._preallocatedOverlapped!) : // allocated when buffer was created, and buffer is non-null
- _stream._fileHandle.ThreadPoolBinding!.AllocateNativeOverlapped(s_ioCallback, this, bytes);
- Debug.Assert(_overlapped != null, "AllocateNativeOverlapped returned null");
- }
-
- internal NativeOverlapped* Overlapped => _overlapped;
-
- public void SetCompletedSynchronously(int numBytes)
- {
- ReleaseNativeResource();
- TrySetResult(numBytes + _numBufferedBytes);
- }
-
- public void RegisterForCancellation(CancellationToken cancellationToken)
- {
-#if DEBUG
- Debug.Assert(cancellationToken.CanBeCanceled);
- Debug.Assert(!_cancellationHasBeenRegistered, "Cannot register for cancellation twice");
- _cancellationHasBeenRegistered = true;
-#endif
-
- // Quick check to make sure the IO hasn't completed
- if (_overlapped != null)
- {
- Action<object?>? cancelCallback = s_cancelCallback ??= Cancel;
-
- // Register the cancellation only if the IO hasn't completed
- long packedResult = Interlocked.CompareExchange(ref _result, RegisteringCancellation, NoResult);
- if (packedResult == NoResult)
- {
- _cancellationRegistration = cancellationToken.UnsafeRegister(cancelCallback, this);
-
- // Switch the result, just in case IO completed while we were setting the registration
- packedResult = Interlocked.Exchange(ref _result, NoResult);
- }
- else if (packedResult != CompletedCallback)
- {
- // Failed to set the result, IO is in the process of completing
- // Attempt to take the packed result
- packedResult = Interlocked.Exchange(ref _result, NoResult);
- }
-
- // If we have a callback that needs to be completed
- if ((packedResult != NoResult) && (packedResult != CompletedCallback) && (packedResult != RegisteringCancellation))
- {
- CompleteCallback((ulong)packedResult);
- }
- }
- }
-
- internal virtual void ReleaseNativeResource()
- {
- // Ensure that cancellation has been completed and cleaned up.
- _cancellationRegistration.Dispose();
-
- // Free the overlapped.
- // NOTE: The cancellation must *NOT* be running at this point, or it may observe freed memory
- // (this is why we disposed the registration above).
- if (_overlapped != null)
- {
- _stream._fileHandle.ThreadPoolBinding!.FreeNativeOverlapped(_overlapped);
- _overlapped = null;
- }
-
- // Ensure we're no longer set as the current completion source (we may not have been to begin with).
- // Only one operation at a time is eligible to use the preallocated overlapped,
- _stream.CompareExchangeCurrentOverlappedOwner(null, this);
- }
-
- // When doing IO asynchronously (i.e. _isAsync==true), this callback is
- // called by a free thread in the threadpool when the IO operation
- // completes.
- internal static void IOCallback(uint errorCode, uint numBytes, NativeOverlapped* pOverlapped)
- {
- // Extract the completion source from the overlapped. The state in the overlapped
- // will either be a FileStream (in the case where the preallocated overlapped was used),
- // in which case the operation being completed is its _currentOverlappedOwner, or it'll
- // be directly the FileStreamCompletionSource that's completing (in the case where the preallocated
- // overlapped was already in use by another operation).
- object? state = ThreadPoolBoundHandle.GetNativeOverlappedState(pOverlapped);
- Debug.Assert(state is FileStream || state is FileStreamCompletionSource);
- FileStreamCompletionSource completionSource = state is FileStream fs ?
- fs._currentOverlappedOwner! : // must be owned
- (FileStreamCompletionSource)state!;
- Debug.Assert(completionSource != null);
- Debug.Assert(completionSource._overlapped == pOverlapped, "Overlaps don't match");
-
- // Handle reading from & writing to closed pipes. While I'm not sure
- // this is entirely necessary anymore, maybe it's possible for
- // an async read on a pipe to be issued and then the pipe is closed,
- // returning this error. This may very well be necessary.
- ulong packedResult;
- if (errorCode != 0 && errorCode != ERROR_BROKEN_PIPE && errorCode != ERROR_NO_DATA)
- {
- packedResult = ((ulong)ResultError | errorCode);
- }
- else
- {
- packedResult = ((ulong)ResultSuccess | numBytes);
- }
-
- // Stow the result so that other threads can observe it
- // And, if no other thread is registering cancellation, continue
- if (NoResult == Interlocked.Exchange(ref completionSource._result, (long)packedResult))
- {
- // Successfully set the state, attempt to take back the callback
- if (Interlocked.Exchange(ref completionSource._result, CompletedCallback) != NoResult)
- {
- // Successfully got the callback, finish the callback
- completionSource.CompleteCallback(packedResult);
- }
- // else: Some other thread stole the result, so now it is responsible to finish the callback
- }
- // else: Some other thread is registering a cancellation, so it *must* finish the callback
- }
-
- private void CompleteCallback(ulong packedResult)
- {
- // Free up the native resource and cancellation registration
- CancellationToken cancellationToken = _cancellationRegistration.Token; // access before disposing registration
- ReleaseNativeResource();
-
- // Unpack the result and send it to the user
- long result = (long)(packedResult & ResultMask);
- if (result == ResultError)
- {
- int errorCode = unchecked((int)(packedResult & uint.MaxValue));
- if (errorCode == Interop.Errors.ERROR_OPERATION_ABORTED)
- {
- TrySetCanceled(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true));
- }
- else
- {
- Exception e = Win32Marshal.GetExceptionForWin32Error(errorCode);
- e.SetCurrentStackTrace();
- TrySetException(e);
- }
- }
- else
- {
- Debug.Assert(result == ResultSuccess, "Unknown result");
- TrySetResult((int)(packedResult & uint.MaxValue) + _numBufferedBytes);
- }
- }
-
- private static void Cancel(object? state)
- {
- // WARNING: This may potentially be called under a lock (during cancellation registration)
-
- Debug.Assert(state is FileStreamCompletionSource, "Unknown state passed to cancellation");
- FileStreamCompletionSource completionSource = (FileStreamCompletionSource)state;
- Debug.Assert(completionSource._overlapped != null && !completionSource.Task.IsCompleted, "IO should not have completed yet");
-
- // If the handle is still valid, attempt to cancel the IO
- if (!completionSource._stream._fileHandle.IsInvalid &&
- !Interop.Kernel32.CancelIoEx(completionSource._stream._fileHandle, completionSource._overlapped))
- {
- int errorCode = Marshal.GetLastWin32Error();
-
- // ERROR_NOT_FOUND is returned if CancelIoEx cannot find the request to cancel.
- // This probably means that the IO operation has completed.
- if (errorCode != Interop.Errors.ERROR_NOT_FOUND)
- {
- throw Win32Marshal.GetExceptionForWin32Error(errorCode);
- }
- }
- }
-
- public static FileStreamCompletionSource Create(FileStream stream, int numBufferedBytesRead, ReadOnlyMemory<byte> memory)
- {
- // If the memory passed in is the stream's internal buffer, we can use the base FileStreamCompletionSource,
- // which has a PreAllocatedOverlapped with the memory already pinned. Otherwise, we use the derived
- // MemoryFileStreamCompletionSource, which Retains the memory, which will result in less pinning in the case
- // where the underlying memory is backed by pre-pinned buffers.
- return MemoryMarshal.TryGetArray(memory, out ArraySegment<byte> buffer) && ReferenceEquals(buffer.Array, stream._buffer) ?
- new FileStreamCompletionSource(stream, numBufferedBytesRead, buffer.Array) :
- new MemoryFileStreamCompletionSource(stream, numBufferedBytesRead, memory);
- }
- }
-
- /// <summary>
- /// Extends <see cref="FileStreamCompletionSource"/> with to support disposing of a
- /// <see cref="MemoryHandle"/> when the operation has completed. This should only be used
- /// when memory doesn't wrap a byte[].
- /// </summary>
- private sealed class MemoryFileStreamCompletionSource : FileStreamCompletionSource
- {
- private MemoryHandle _handle; // mutable struct; do not make this readonly
-
- internal MemoryFileStreamCompletionSource(FileStream stream, int numBufferedBytes, ReadOnlyMemory<byte> memory) :
- base(stream, numBufferedBytes, bytes: null) // this type handles the pinning, so null is passed for bytes
- {
- _handle = memory.Pin();
- }
-
- internal override void ReleaseNativeResource()
- {
- _handle.Dispose();
- base.ReleaseNativeResource();
- }
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/IOException.cs b/netcore/System.Private.CoreLib/shared/System/IO/IOException.cs
deleted file mode 100644
index a22cc3bc48f..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/IOException.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Runtime.Serialization;
-
-namespace System.IO
-{
- [Serializable]
- [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
- public class IOException : SystemException
- {
- public IOException()
- : base(SR.Arg_IOException)
- {
- HResult = HResults.COR_E_IO;
- }
-
- public IOException(string? message)
- : base(message)
- {
- HResult = HResults.COR_E_IO;
- }
-
- public IOException(string? message, int hresult)
- : base(message)
- {
- HResult = hresult;
- }
-
- public IOException(string? message, Exception? innerException)
- : base(message, innerException)
- {
- HResult = HResults.COR_E_IO;
- }
-
- protected IOException(SerializationInfo info, StreamingContext context) : base(info, context)
- {
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/MemoryStream.cs b/netcore/System.Private.CoreLib/shared/System/IO/MemoryStream.cs
deleted file mode 100644
index b8ff126e895..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/MemoryStream.cs
+++ /dev/null
@@ -1,868 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Buffers;
-using System.Diagnostics;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace System.IO
-{
- // A MemoryStream represents a Stream in memory (ie, it has no backing store).
- // This stream may reduce the need for temporary buffers and files in
- // an application.
- //
- // There are two ways to create a MemoryStream. You can initialize one
- // from an unsigned byte array, or you can create an empty one. Empty
- // memory streams are resizable, while ones created with a byte array provide
- // a stream "view" of the data.
- public class MemoryStream : Stream
- {
- private byte[] _buffer; // Either allocated internally or externally.
- private readonly int _origin; // For user-provided arrays, start at this origin
- private int _position; // read/write head.
- private int _length; // Number of bytes within the memory stream
- private int _capacity; // length of usable portion of buffer for stream
- // Note that _capacity == _buffer.Length for non-user-provided byte[]'s
-
- private bool _expandable; // User-provided buffers aren't expandable.
- private bool _writable; // Can user write to this stream?
- private readonly bool _exposable; // Whether the array can be returned to the user.
- private bool _isOpen; // Is this stream open or closed?
-
- private Task<int>? _lastReadTask; // The last successful task returned from ReadAsync
-
- private const int MemStreamMaxLength = int.MaxValue;
-
- public MemoryStream()
- : this(0)
- {
- }
-
- public MemoryStream(int capacity)
- {
- if (capacity < 0)
- throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_NegativeCapacity);
-
- _buffer = capacity != 0 ? new byte[capacity] : Array.Empty<byte>();
- _capacity = capacity;
- _expandable = true;
- _writable = true;
- _exposable = true;
- _origin = 0; // Must be 0 for byte[]'s created by MemoryStream
- _isOpen = true;
- }
-
- public MemoryStream(byte[] buffer)
- : this(buffer, true)
- {
- }
-
- public MemoryStream(byte[] buffer, bool writable)
- {
- if (buffer == null)
- throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
-
- _buffer = buffer;
- _length = _capacity = buffer.Length;
- _writable = writable;
- _exposable = false;
- _origin = 0;
- _isOpen = true;
- }
-
- public MemoryStream(byte[] buffer, int index, int count)
- : this(buffer, index, count, true, false)
- {
- }
-
- public MemoryStream(byte[] buffer, int index, int count, bool writable)
- : this(buffer, index, count, writable, false)
- {
- }
-
- public MemoryStream(byte[] buffer, int index, int count, bool writable, bool publiclyVisible)
- {
- if (buffer == null)
- throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
- if (index < 0)
- throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (count < 0)
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (buffer.Length - index < count)
- throw new ArgumentException(SR.Argument_InvalidOffLen);
-
- _buffer = buffer;
- _origin = _position = index;
- _length = _capacity = index + count;
- _writable = writable;
- _exposable = publiclyVisible; // Can TryGetBuffer/GetBuffer return the array?
- _expandable = false;
- _isOpen = true;
- }
-
- public override bool CanRead => _isOpen;
-
- public override bool CanSeek => _isOpen;
-
- public override bool CanWrite => _writable;
-
- private void EnsureNotClosed()
- {
- if (!_isOpen)
- throw Error.GetStreamIsClosed();
- }
-
- private void EnsureWriteable()
- {
- if (!CanWrite)
- throw Error.GetWriteNotSupported();
- }
-
- protected override void Dispose(bool disposing)
- {
- try
- {
- if (disposing)
- {
- _isOpen = false;
- _writable = false;
- _expandable = false;
- // Don't set buffer to null - allow TryGetBuffer, GetBuffer & ToArray to work.
- _lastReadTask = null;
- }
- }
- finally
- {
- // Call base.Close() to cleanup async IO resources
- base.Dispose(disposing);
- }
- }
-
- // returns a bool saying whether we allocated a new array.
- private bool EnsureCapacity(int value)
- {
- // Check for overflow
- if (value < 0)
- throw new IOException(SR.IO_StreamTooLong);
-
- if (value > _capacity)
- {
- int newCapacity = Math.Max(value, 256);
-
- // We are ok with this overflowing since the next statement will deal
- // with the cases where _capacity*2 overflows.
- if (newCapacity < _capacity * 2)
- {
- newCapacity = _capacity * 2;
- }
-
- // We want to expand the array up to Array.MaxByteArrayLength
- // And we want to give the user the value that they asked for
- if ((uint)(_capacity * 2) > Array.MaxByteArrayLength)
- {
- newCapacity = Math.Max(value, Array.MaxByteArrayLength);
- }
-
- Capacity = newCapacity;
- return true;
- }
- return false;
- }
-
- public override void Flush()
- {
- }
-
- public override Task FlushAsync(CancellationToken cancellationToken)
- {
- if (cancellationToken.IsCancellationRequested)
- return Task.FromCanceled(cancellationToken);
-
- try
- {
- Flush();
- return Task.CompletedTask;
- }
- catch (Exception ex)
- {
- return Task.FromException(ex);
- }
- }
-
- public virtual byte[] GetBuffer()
- {
- if (!_exposable)
- throw new UnauthorizedAccessException(SR.UnauthorizedAccess_MemStreamBuffer);
- return _buffer;
- }
-
- public virtual bool TryGetBuffer(out ArraySegment<byte> buffer)
- {
- if (!_exposable)
- {
- buffer = default;
- return false;
- }
-
- buffer = new ArraySegment<byte>(_buffer, offset: _origin, count: _length - _origin);
- return true;
- }
-
- // -------------- PERF: Internal functions for fast direct access of MemoryStream buffer (cf. BinaryReader for usage) ---------------
-
- // PERF: Internal sibling of GetBuffer, always returns a buffer (cf. GetBuffer())
- internal byte[] InternalGetBuffer()
- {
- return _buffer;
- }
-
- // PERF: True cursor position, we don't need _origin for direct access
- internal int InternalGetPosition()
- {
- return _position;
- }
-
- // PERF: Expose internal buffer for BinaryReader instead of going via the regular Stream interface which requires to copy the data out
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal ReadOnlySpan<byte> InternalReadSpan(int count)
- {
- EnsureNotClosed();
-
- int origPos = _position;
- int newPos = origPos + count;
-
- if ((uint)newPos > (uint)_length)
- {
- _position = _length;
- throw Error.GetEndOfFile();
- }
-
- var span = new ReadOnlySpan<byte>(_buffer, origPos, count);
- _position = newPos;
- return span;
- }
-
- // PERF: Get actual length of bytes available for read; do sanity checks; shift position - i.e. everything except actual copying bytes
- internal int InternalEmulateRead(int count)
- {
- EnsureNotClosed();
-
- int n = _length - _position;
- if (n > count)
- n = count;
- if (n < 0)
- n = 0;
-
- Debug.Assert(_position + n >= 0, "_position + n >= 0"); // len is less than 2^31 -1.
- _position += n;
- return n;
- }
-
- // Gets & sets the capacity (number of bytes allocated) for this stream.
- // The capacity cannot be set to a value less than the current length
- // of the stream.
- //
- public virtual int Capacity
- {
- get
- {
- EnsureNotClosed();
- return _capacity - _origin;
- }
- set
- {
- // Only update the capacity if the MS is expandable and the value is different than the current capacity.
- // Special behavior if the MS isn't expandable: we don't throw if value is the same as the current capacity
- if (value < Length)
- throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_SmallCapacity);
-
- EnsureNotClosed();
-
- if (!_expandable && (value != Capacity))
- throw new NotSupportedException(SR.NotSupported_MemStreamNotExpandable);
-
- // MemoryStream has this invariant: _origin > 0 => !expandable (see ctors)
- if (_expandable && value != _capacity)
- {
- if (value > 0)
- {
- byte[] newBuffer = new byte[value];
- if (_length > 0)
- {
- Buffer.BlockCopy(_buffer, 0, newBuffer, 0, _length);
- }
- _buffer = newBuffer;
- }
- else
- {
- _buffer = Array.Empty<byte>();
- }
- _capacity = value;
- }
- }
- }
-
- public override long Length
- {
- get
- {
- EnsureNotClosed();
- return _length - _origin;
- }
- }
-
- public override long Position
- {
- get
- {
- EnsureNotClosed();
- return _position - _origin;
- }
- set
- {
- if (value < 0)
- throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum);
-
- EnsureNotClosed();
-
- if (value > MemStreamMaxLength)
- throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_StreamLength);
- _position = _origin + (int)value;
- }
- }
-
- public override int Read(byte[] buffer, int offset, int count)
- {
- if (buffer == null)
- throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
- if (offset < 0)
- throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (count < 0)
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (buffer.Length - offset < count)
- throw new ArgumentException(SR.Argument_InvalidOffLen);
-
- EnsureNotClosed();
-
- int n = _length - _position;
- if (n > count)
- n = count;
- if (n <= 0)
- return 0;
-
- Debug.Assert(_position + n >= 0, "_position + n >= 0"); // len is less than 2^31 -1.
-
- if (n <= 8)
- {
- int byteCount = n;
- while (--byteCount >= 0)
- buffer[offset + byteCount] = _buffer[_position + byteCount];
- }
- else
- Buffer.BlockCopy(_buffer, _position, buffer, offset, n);
- _position += n;
-
- return n;
- }
-
- public override int Read(Span<byte> buffer)
- {
- if (GetType() != typeof(MemoryStream))
- {
- // MemoryStream is not sealed, and a derived type may have overridden Read(byte[], int, int) prior
- // to this Read(Span<byte>) overload being introduced. In that case, this Read(Span<byte>) overload
- // should use the behavior of Read(byte[],int,int) overload.
- return base.Read(buffer);
- }
-
- EnsureNotClosed();
-
- int n = Math.Min(_length - _position, buffer.Length);
- if (n <= 0)
- return 0;
-
- // TODO https://github.com/dotnet/coreclr/issues/15076:
- // Read(byte[], int, int) has an n <= 8 optimization, presumably based
- // on benchmarking. Determine if/where such a cut-off is here and add
- // an equivalent optimization if necessary.
- new Span<byte>(_buffer, _position, n).CopyTo(buffer);
-
- _position += n;
- return n;
- }
-
- public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
- {
- if (buffer == null)
- throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
- if (offset < 0)
- throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (count < 0)
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (buffer.Length - offset < count)
- throw new ArgumentException(SR.Argument_InvalidOffLen);
-
- // If cancellation was requested, bail early
- if (cancellationToken.IsCancellationRequested)
- return Task.FromCanceled<int>(cancellationToken);
-
- try
- {
- int n = Read(buffer, offset, count);
- Task<int>? t = _lastReadTask;
- Debug.Assert(t == null || t.Status == TaskStatus.RanToCompletion,
- "Expected that a stored last task completed successfully");
- return (t != null && t.Result == n) ? t : (_lastReadTask = Task.FromResult<int>(n));
- }
- catch (OperationCanceledException oce)
- {
- return Task.FromCanceled<int>(oce);
- }
- catch (Exception exception)
- {
- return Task.FromException<int>(exception);
- }
- }
-
- public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
- {
- if (cancellationToken.IsCancellationRequested)
- {
- return new ValueTask<int>(Task.FromCanceled<int>(cancellationToken));
- }
-
- try
- {
- // ReadAsync(Memory<byte>,...) needs to delegate to an existing virtual to do the work, in case an existing derived type
- // has changed or augmented the logic associated with reads. If the Memory wraps an array, we could delegate to
- // ReadAsync(byte[], ...), but that would defeat part of the purpose, as ReadAsync(byte[], ...) often needs to allocate
- // a Task<int> for the return value, so we want to delegate to one of the synchronous methods. We could always
- // delegate to the Read(Span<byte>) method, and that's the most efficient solution when dealing with a concrete
- // MemoryStream, but if we're dealing with a type derived from MemoryStream, Read(Span<byte>) will end up delegating
- // to Read(byte[], ...), which requires it to get a byte[] from ArrayPool and copy the data. So, we special-case the
- // very common case of the Memory<byte> wrapping an array: if it does, we delegate to Read(byte[], ...) with it,
- // as that will be efficient in both cases, and we fall back to Read(Span<byte>) if the Memory<byte> wrapped something
- // else; if this is a concrete MemoryStream, that'll be efficient, and only in the case where the Memory<byte> wrapped
- // something other than an array and this is a MemoryStream-derived type that doesn't override Read(Span<byte>) will
- // it then fall back to doing the ArrayPool/copy behavior.
- return new ValueTask<int>(
- MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> destinationArray) ?
- Read(destinationArray.Array!, destinationArray.Offset, destinationArray.Count) :
- Read(buffer.Span));
- }
- catch (OperationCanceledException oce)
- {
- return new ValueTask<int>(Task.FromCanceled<int>(oce));
- }
- catch (Exception exception)
- {
- return new ValueTask<int>(Task.FromException<int>(exception));
- }
- }
-
- public override int ReadByte()
- {
- EnsureNotClosed();
-
- if (_position >= _length)
- return -1;
-
- return _buffer[_position++];
- }
-
- public override void CopyTo(Stream destination, int bufferSize)
- {
- // Since we did not originally override this method, validate the arguments
- // the same way Stream does for back-compat.
- StreamHelpers.ValidateCopyToArgs(this, destination, bufferSize);
-
- // If we have been inherited into a subclass, the following implementation could be incorrect
- // since it does not call through to Read() which a subclass might have overridden.
- // To be safe we will only use this implementation in cases where we know it is safe to do so,
- // and delegate to our base class (which will call into Read) when we are not sure.
- if (GetType() != typeof(MemoryStream))
- {
- base.CopyTo(destination, bufferSize);
- return;
- }
-
- int originalPosition = _position;
-
- // Seek to the end of the MemoryStream.
- int remaining = InternalEmulateRead(_length - originalPosition);
-
- // If we were already at or past the end, there's no copying to do so just quit.
- if (remaining > 0)
- {
- // Call Write() on the other Stream, using our internal buffer and avoiding any
- // intermediary allocations.
- destination.Write(_buffer, originalPosition, remaining);
- }
- }
-
- public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
- {
- // This implementation offers better performance compared to the base class version.
-
- StreamHelpers.ValidateCopyToArgs(this, destination, bufferSize);
-
- // If we have been inherited into a subclass, the following implementation could be incorrect
- // since it does not call through to ReadAsync() which a subclass might have overridden.
- // To be safe we will only use this implementation in cases where we know it is safe to do so,
- // and delegate to our base class (which will call into ReadAsync) when we are not sure.
- if (GetType() != typeof(MemoryStream))
- return base.CopyToAsync(destination, bufferSize, cancellationToken);
-
- // If canceled - return fast:
- if (cancellationToken.IsCancellationRequested)
- return Task.FromCanceled(cancellationToken);
-
- // Avoid copying data from this buffer into a temp buffer:
- // (require that InternalEmulateRead does not throw,
- // otherwise it needs to be wrapped into try-catch-Task.FromException like memStrDest.Write below)
-
- int pos = _position;
- int n = InternalEmulateRead(_length - _position);
-
- // If we were already at or past the end, there's no copying to do so just quit.
- if (n == 0)
- return Task.CompletedTask;
-
- // If destination is not a memory stream, write there asynchronously:
- if (!(destination is MemoryStream memStrDest))
- return destination.WriteAsync(_buffer, pos, n, cancellationToken);
-
- try
- {
- // If destination is a MemoryStream, CopyTo synchronously:
- memStrDest.Write(_buffer, pos, n);
- return Task.CompletedTask;
- }
- catch (Exception ex)
- {
- return Task.FromException(ex);
- }
- }
-
- public override void CopyTo(ReadOnlySpanAction<byte, object?> callback, object? state, int bufferSize)
- {
- // If we have been inherited into a subclass, the following implementation could be incorrect
- // since it does not call through to Read() which a subclass might have overridden.
- // To be safe we will only use this implementation in cases where we know it is safe to do so,
- // and delegate to our base class (which will call into Read) when we are not sure.
- if (GetType() != typeof(MemoryStream))
- {
- base.CopyTo(callback, state, bufferSize);
- return;
- }
-
- StreamHelpers.ValidateCopyToArgs(this, callback, bufferSize);
-
- // Retrieve a span until the end of the MemoryStream.
- ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(_buffer, _position, _length - _position);
- _position = _length;
-
- // Invoke the callback, using our internal span and avoiding any
- // intermediary allocations.
- callback(span, state);
- }
-
- public override Task CopyToAsync(Func<ReadOnlyMemory<byte>, object?, CancellationToken, ValueTask> callback, object? state, int bufferSize, CancellationToken cancellationToken)
- {
- // If we have been inherited into a subclass, the following implementation could be incorrect
- // since it does not call through to ReadAsync() which a subclass might have overridden.
- // To be safe we will only use this implementation in cases where we know it is safe to do so,
- // and delegate to our base class (which will call into ReadAsync) when we are not sure.
- if (GetType() != typeof(MemoryStream))
- return base.CopyToAsync(callback, state, bufferSize, cancellationToken);
-
- StreamHelpers.ValidateCopyToArgs(this, callback, bufferSize);
-
- // If canceled - return fast:
- if (cancellationToken.IsCancellationRequested)
- return Task.FromCanceled(cancellationToken);
-
- // Avoid copying data from this buffer into a temp buffer
- ReadOnlyMemory<byte> memory = new ReadOnlyMemory<byte>(_buffer, _position, _length - _position);
- _position = _length;
-
- return callback(memory, state, cancellationToken).AsTask();
- }
-
- public override long Seek(long offset, SeekOrigin loc)
- {
- EnsureNotClosed();
-
- if (offset > MemStreamMaxLength)
- throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_StreamLength);
-
- switch (loc)
- {
- case SeekOrigin.Begin:
- {
- int tempPosition = unchecked(_origin + (int)offset);
- if (offset < 0 || tempPosition < _origin)
- throw new IOException(SR.IO_SeekBeforeBegin);
- _position = tempPosition;
- break;
- }
- case SeekOrigin.Current:
- {
- int tempPosition = unchecked(_position + (int)offset);
- if (unchecked(_position + offset) < _origin || tempPosition < _origin)
- throw new IOException(SR.IO_SeekBeforeBegin);
- _position = tempPosition;
- break;
- }
- case SeekOrigin.End:
- {
- int tempPosition = unchecked(_length + (int)offset);
- if (unchecked(_length + offset) < _origin || tempPosition < _origin)
- throw new IOException(SR.IO_SeekBeforeBegin);
- _position = tempPosition;
- break;
- }
- default:
- throw new ArgumentException(SR.Argument_InvalidSeekOrigin);
- }
-
- Debug.Assert(_position >= 0, "_position >= 0");
- return _position;
- }
-
- // Sets the length of the stream to a given value. The new
- // value must be nonnegative and less than the space remaining in
- // the array, int.MaxValue - origin
- // Origin is 0 in all cases other than a MemoryStream created on
- // top of an existing array and a specific starting offset was passed
- // into the MemoryStream constructor. The upper bounds prevents any
- // situations where a stream may be created on top of an array then
- // the stream is made longer than the maximum possible length of the
- // array (int.MaxValue).
- //
- public override void SetLength(long value)
- {
- if (value < 0 || value > int.MaxValue)
- throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_StreamLength);
-
- EnsureWriteable();
-
- // Origin wasn't publicly exposed above.
- Debug.Assert(MemStreamMaxLength == int.MaxValue); // Check parameter validation logic in this method if this fails.
- if (value > (int.MaxValue - _origin))
- throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_StreamLength);
-
- int newLength = _origin + (int)value;
- bool allocatedNewArray = EnsureCapacity(newLength);
- if (!allocatedNewArray && newLength > _length)
- Array.Clear(_buffer, _length, newLength - _length);
- _length = newLength;
- if (_position > newLength)
- _position = newLength;
- }
-
- public virtual byte[] ToArray()
- {
- int count = _length - _origin;
- if (count == 0)
- return Array.Empty<byte>();
- byte[] copy = new byte[count];
- Buffer.BlockCopy(_buffer, _origin, copy, 0, count);
- return copy;
- }
-
- public override void Write(byte[] buffer, int offset, int count)
- {
- if (buffer == null)
- throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
- if (offset < 0)
- throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (count < 0)
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (buffer.Length - offset < count)
- throw new ArgumentException(SR.Argument_InvalidOffLen);
-
- EnsureNotClosed();
- EnsureWriteable();
-
- int i = _position + count;
- // Check for overflow
- if (i < 0)
- throw new IOException(SR.IO_StreamTooLong);
-
- if (i > _length)
- {
- bool mustZero = _position > _length;
- if (i > _capacity)
- {
- bool allocatedNewArray = EnsureCapacity(i);
- if (allocatedNewArray)
- {
- mustZero = false;
- }
- }
- if (mustZero)
- {
- Array.Clear(_buffer, _length, i - _length);
- }
- _length = i;
- }
- if ((count <= 8) && (buffer != _buffer))
- {
- int byteCount = count;
- while (--byteCount >= 0)
- {
- _buffer[_position + byteCount] = buffer[offset + byteCount];
- }
- }
- else
- {
- Buffer.BlockCopy(buffer, offset, _buffer, _position, count);
- }
- _position = i;
- }
-
- public override void Write(ReadOnlySpan<byte> buffer)
- {
- if (GetType() != typeof(MemoryStream))
- {
- // MemoryStream is not sealed, and a derived type may have overridden Write(byte[], int, int) prior
- // to this Write(Span<byte>) overload being introduced. In that case, this Write(Span<byte>) overload
- // should use the behavior of Write(byte[],int,int) overload.
- base.Write(buffer);
- return;
- }
-
- EnsureNotClosed();
- EnsureWriteable();
-
- // Check for overflow
- int i = _position + buffer.Length;
- if (i < 0)
- throw new IOException(SR.IO_StreamTooLong);
-
- if (i > _length)
- {
- bool mustZero = _position > _length;
- if (i > _capacity)
- {
- bool allocatedNewArray = EnsureCapacity(i);
- if (allocatedNewArray)
- {
- mustZero = false;
- }
- }
- if (mustZero)
- {
- Array.Clear(_buffer, _length, i - _length);
- }
- _length = i;
- }
-
- buffer.CopyTo(new Span<byte>(_buffer, _position, buffer.Length));
- _position = i;
- }
-
- public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
- {
- if (buffer == null)
- throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
- if (offset < 0)
- throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (count < 0)
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (buffer.Length - offset < count)
- throw new ArgumentException(SR.Argument_InvalidOffLen);
-
- // If cancellation is already requested, bail early
- if (cancellationToken.IsCancellationRequested)
- return Task.FromCanceled(cancellationToken);
-
- try
- {
- Write(buffer, offset, count);
- return Task.CompletedTask;
- }
- catch (OperationCanceledException oce)
- {
- return Task.FromCanceled(oce);
- }
- catch (Exception exception)
- {
- return Task.FromException(exception);
- }
- }
-
- public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
- {
- if (cancellationToken.IsCancellationRequested)
- {
- return new ValueTask(Task.FromCanceled(cancellationToken));
- }
-
- try
- {
- // See corresponding comment in ReadAsync for why we don't just always use Write(ReadOnlySpan<byte>).
- // Unlike ReadAsync, we could delegate to WriteAsync(byte[], ...) here, but we don't for consistency.
- if (MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> sourceArray))
- {
- Write(sourceArray.Array!, sourceArray.Offset, sourceArray.Count);
- }
- else
- {
- Write(buffer.Span);
- }
- return default;
- }
- catch (OperationCanceledException oce)
- {
- return new ValueTask(Task.FromCanceled(oce));
- }
- catch (Exception exception)
- {
- return new ValueTask(Task.FromException(exception));
- }
- }
-
- public override void WriteByte(byte value)
- {
- EnsureNotClosed();
- EnsureWriteable();
-
- if (_position >= _length)
- {
- int newLength = _position + 1;
- bool mustZero = _position > _length;
- if (newLength >= _capacity)
- {
- bool allocatedNewArray = EnsureCapacity(newLength);
- if (allocatedNewArray)
- {
- mustZero = false;
- }
- }
- if (mustZero)
- {
- Array.Clear(_buffer, _length, _position - _length);
- }
- _length = newLength;
- }
- _buffer[_position++] = value;
- }
-
- // Writes this MemoryStream to another stream.
- public virtual void WriteTo(Stream stream)
- {
- if (stream == null)
- throw new ArgumentNullException(nameof(stream), SR.ArgumentNull_Stream);
-
- EnsureNotClosed();
-
- stream.Write(_buffer, _origin, _length - _origin);
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/Path.Unix.cs b/netcore/System.Private.CoreLib/shared/System/IO/Path.Unix.cs
deleted file mode 100644
index 487c880c8e0..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/Path.Unix.cs
+++ /dev/null
@@ -1,147 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-using System.Text;
-
-namespace System.IO
-{
- public static partial class Path
- {
- public static char[] GetInvalidFileNameChars() => new char[] { '\0', '/' };
-
- public static char[] GetInvalidPathChars() => new char[] { '\0' };
-
- // Expands the given path to a fully qualified path.
- public static string GetFullPath(string path)
- {
- if (path == null)
- throw new ArgumentNullException(nameof(path));
-
- if (path.Length == 0)
- throw new ArgumentException(SR.Arg_PathEmpty, nameof(path));
-
- if (path.Contains('\0'))
- throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path));
-
- // Expand with current directory if necessary
- if (!IsPathRooted(path))
- {
- path = Combine(Interop.Sys.GetCwd(), path);
- }
-
- // We would ideally use realpath to do this, but it resolves symlinks, requires that the file actually exist,
- // and turns it into a full path, which we only want if fullCheck is true.
- string collapsedString = PathInternal.RemoveRelativeSegments(path, PathInternal.GetRootLength(path));
-
- Debug.Assert(collapsedString.Length < path.Length || collapsedString.ToString() == path,
- "Either we've removed characters, or the string should be unmodified from the input path.");
-
- string result = collapsedString.Length == 0 ? PathInternal.DirectorySeparatorCharAsString : collapsedString;
-
- return result;
- }
-
- public static string GetFullPath(string path, string basePath)
- {
- if (path == null)
- throw new ArgumentNullException(nameof(path));
-
- if (basePath == null)
- throw new ArgumentNullException(nameof(basePath));
-
- if (!IsPathFullyQualified(basePath))
- throw new ArgumentException(SR.Arg_BasePathNotFullyQualified, nameof(basePath));
-
- if (basePath.Contains('\0') || path.Contains('\0'))
- throw new ArgumentException(SR.Argument_InvalidPathChars);
-
- if (IsPathFullyQualified(path))
- return GetFullPath(path);
-
- return GetFullPath(CombineInternal(basePath, path));
- }
-
- private static string RemoveLongPathPrefix(string path)
- {
- return path; // nop. There's nothing special about "long" paths on Unix.
- }
-
- public static string GetTempPath()
- {
- const string TempEnvVar = "TMPDIR";
- const string DefaultTempPath = "/tmp/";
-
- // Get the temp path from the TMPDIR environment variable.
- // If it's not set, just return the default path.
- // If it is, return it, ensuring it ends with a slash.
- string? path = Environment.GetEnvironmentVariable(TempEnvVar);
- return
- string.IsNullOrEmpty(path) ? DefaultTempPath :
- PathInternal.IsDirectorySeparator(path[path.Length - 1]) ? path :
- path + PathInternal.DirectorySeparatorChar;
- }
-
- public static string GetTempFileName()
- {
- const string Suffix = ".tmp";
- const int SuffixByteLength = 4;
-
- // mkstemps takes a char* and overwrites the XXXXXX with six characters
- // that'll result in a unique file name.
- string template = GetTempPath() + "tmpXXXXXX" + Suffix + "\0";
- byte[] name = Encoding.UTF8.GetBytes(template);
-
- // Create, open, and close the temp file.
- IntPtr fd = Interop.CheckIo(Interop.Sys.MksTemps(name, SuffixByteLength));
- Interop.Sys.Close(fd); // ignore any errors from close; nothing to do if cleanup isn't possible
-
- // 'name' is now the name of the file
- Debug.Assert(name[name.Length - 1] == '\0');
- return Encoding.UTF8.GetString(name, 0, name.Length - 1); // trim off the trailing '\0'
- }
-
- public static bool IsPathRooted(string? path)
- {
- if (path == null)
- return false;
-
- return IsPathRooted(path.AsSpan());
- }
-
- public static bool IsPathRooted(ReadOnlySpan<char> path)
- {
- return path.Length > 0 && path[0] == PathInternal.DirectorySeparatorChar;
- }
-
- /// <summary>
- /// Returns the path root or null if path is empty or null.
- /// </summary>
- public static string? GetPathRoot(string? path)
- {
- if (PathInternal.IsEffectivelyEmpty(path)) return null;
-
- return IsPathRooted(path) ? PathInternal.DirectorySeparatorCharAsString : string.Empty;
- }
-
- public static ReadOnlySpan<char> GetPathRoot(ReadOnlySpan<char> path)
- {
- return PathInternal.IsEffectivelyEmpty(path) && IsPathRooted(path) ? PathInternal.DirectorySeparatorCharAsString.AsSpan() : ReadOnlySpan<char>.Empty;
- }
-
- /// <summary>Gets whether the system is case-sensitive.</summary>
- internal static bool IsCaseSensitive
- {
- get
- {
- #if PLATFORM_OSX
- return false;
- #else
- return true;
- #endif
- }
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/Path.Windows.cs b/netcore/System.Private.CoreLib/shared/System/IO/Path.Windows.cs
deleted file mode 100644
index 1e2bf76b3b0..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/Path.Windows.cs
+++ /dev/null
@@ -1,286 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-#nullable enable
-using System.Diagnostics;
-using System.Text;
-
-#if MS_IO_REDIST
-using System;
-using System.IO;
-
-namespace Microsoft.IO
-#else
-namespace System.IO
-#endif
-{
- public static partial class Path
- {
- public static char[] GetInvalidFileNameChars() => new char[]
- {
- '\"', '<', '>', '|', '\0',
- (char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10,
- (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20,
- (char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30,
- (char)31, ':', '*', '?', '\\', '/'
- };
-
- public static char[] GetInvalidPathChars() => new char[]
- {
- '|', '\0',
- (char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10,
- (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20,
- (char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30,
- (char)31
- };
-
- // Expands the given path to a fully qualified path.
- public static string GetFullPath(string path)
- {
- if (path == null)
- throw new ArgumentNullException(nameof(path));
-
- // If the path would normalize to string empty, we'll consider it empty
- if (PathInternal.IsEffectivelyEmpty(path.AsSpan()))
- throw new ArgumentException(SR.Arg_PathEmpty, nameof(path));
-
- // Embedded null characters are the only invalid character case we trully care about.
- // This is because the nulls will signal the end of the string to Win32 and therefore have
- // unpredictable results.
- if (path.Contains('\0'))
- throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path));
-
- if (PathInternal.IsExtended(path.AsSpan()))
- {
- // \\?\ paths are considered normalized by definition. Windows doesn't normalize \\?\
- // paths and neither should we. Even if we wanted to GetFullPathName does not work
- // properly with device paths. If one wants to pass a \\?\ path through normalization
- // one can chop off the prefix, pass it to GetFullPath and add it again.
- return path;
- }
-
- return PathHelper.Normalize(path);
- }
-
- public static string GetFullPath(string path, string basePath)
- {
- if (path == null)
- throw new ArgumentNullException(nameof(path));
-
- if (basePath == null)
- throw new ArgumentNullException(nameof(basePath));
-
- if (!IsPathFullyQualified(basePath))
- throw new ArgumentException(SR.Arg_BasePathNotFullyQualified, nameof(basePath));
-
- if (basePath.Contains('\0') || path.Contains('\0'))
- throw new ArgumentException(SR.Argument_InvalidPathChars);
-
- if (IsPathFullyQualified(path))
- return GetFullPath(path);
-
- if (PathInternal.IsEffectivelyEmpty(path.AsSpan()))
- return basePath;
-
- int length = path.Length;
- string? combinedPath = null;
-
- if (length >= 1 && PathInternal.IsDirectorySeparator(path[0]))
- {
- // Path is current drive rooted i.e. starts with \:
- // "\Foo" and "C:\Bar" => "C:\Foo"
- // "\Foo" and "\\?\C:\Bar" => "\\?\C:\Foo"
- combinedPath = Join(GetPathRoot(basePath.AsSpan()), path.AsSpan(1)); // Cut the separator to ensure we don't end up with two separators when joining with the root.
- }
- else if (length >= 2 && PathInternal.IsValidDriveChar(path[0]) && path[1] == PathInternal.VolumeSeparatorChar)
- {
- // Drive relative paths
- Debug.Assert(length == 2 || !PathInternal.IsDirectorySeparator(path[2]));
-
- if (GetVolumeName(path.AsSpan()).EqualsOrdinal(GetVolumeName(basePath.AsSpan())))
- {
- // Matching root
- // "C:Foo" and "C:\Bar" => "C:\Bar\Foo"
- // "C:Foo" and "\\?\C:\Bar" => "\\?\C:\Bar\Foo"
- combinedPath = Join(basePath.AsSpan(), path.AsSpan(2));
- }
- else
- {
- // No matching root, root to specified drive
- // "D:Foo" and "C:\Bar" => "D:Foo"
- // "D:Foo" and "\\?\C:\Bar" => "\\?\D:\Foo"
- combinedPath = !PathInternal.IsDevice(basePath.AsSpan())
- ? path.Insert(2, @"\")
- : length == 2
- ? JoinInternal(basePath.AsSpan(0, 4), path.AsSpan(), @"\".AsSpan())
- : JoinInternal(basePath.AsSpan(0, 4), path.AsSpan(0, 2), @"\".AsSpan(), path.AsSpan(2));
- }
- }
- else
- {
- // "Simple" relative path
- // "Foo" and "C:\Bar" => "C:\Bar\Foo"
- // "Foo" and "\\?\C:\Bar" => "\\?\C:\Bar\Foo"
- combinedPath = JoinInternal(basePath.AsSpan(), path.AsSpan());
- }
-
- // Device paths are normalized by definition, so passing something of this format (i.e. \\?\C:\.\tmp, \\.\C:\foo)
- // to Windows APIs won't do anything by design. Additionally, GetFullPathName() in Windows doesn't root
- // them properly. As such we need to manually remove segments and not use GetFullPath().
-
- return PathInternal.IsDevice(combinedPath.AsSpan())
- ? PathInternal.RemoveRelativeSegments(combinedPath, PathInternal.GetRootLength(combinedPath.AsSpan()))
- : GetFullPath(combinedPath);
- }
-
- public static string GetTempPath()
- {
- var builder = new ValueStringBuilder(stackalloc char[PathInternal.MaxShortPath]);
-
- GetTempPath(ref builder);
-
- string path = PathHelper.Normalize(ref builder);
- builder.Dispose();
- return path;
- }
-
- private static void GetTempPath(ref ValueStringBuilder builder)
- {
- uint result;
- while ((result = Interop.Kernel32.GetTempPathW(builder.Capacity, ref builder.GetPinnableReference())) > builder.Capacity)
- {
- // Reported size is greater than the buffer size. Increase the capacity.
- builder.EnsureCapacity(checked((int)result));
- }
-
- if (result == 0)
- throw Win32Marshal.GetExceptionForLastWin32Error();
-
- builder.Length = (int)result;
- }
-
- // Returns a unique temporary file name, and creates a 0-byte file by that
- // name on disk.
- public static string GetTempFileName()
- {
- var tempPathBuilder = new ValueStringBuilder(stackalloc char[PathInternal.MaxShortPath]);
-
- GetTempPath(ref tempPathBuilder);
-
- var builder = new ValueStringBuilder(stackalloc char[PathInternal.MaxShortPath]);
-
- uint result = Interop.Kernel32.GetTempFileNameW(
- ref tempPathBuilder.GetPinnableReference(), "tmp", 0, ref builder.GetPinnableReference());
-
- tempPathBuilder.Dispose();
-
- if (result == 0)
- throw Win32Marshal.GetExceptionForLastWin32Error();
-
- builder.Length = builder.RawChars.IndexOf('\0');
-
- string path = PathHelper.Normalize(ref builder);
- builder.Dispose();
- return path;
- }
-
- // Tests if the given path contains a root. A path is considered rooted
- // if it starts with a backslash ("\") or a valid drive letter and a colon (":").
- public static bool IsPathRooted(string? path)
- {
- return path != null && IsPathRooted(path.AsSpan());
- }
-
- public static bool IsPathRooted(ReadOnlySpan<char> path)
- {
- int length = path.Length;
- return (length >= 1 && PathInternal.IsDirectorySeparator(path[0]))
- || (length >= 2 && PathInternal.IsValidDriveChar(path[0]) && path[1] == PathInternal.VolumeSeparatorChar);
- }
-
- // Returns the root portion of the given path. The resulting string
- // consists of those rightmost characters of the path that constitute the
- // root of the path. Possible patterns for the resulting string are: An
- // empty string (a relative path on the current drive), "\" (an absolute
- // path on the current drive), "X:" (a relative path on a given drive,
- // where X is the drive letter), "X:\" (an absolute path on a given drive),
- // and "\\server\share" (a UNC path for a given server and share name).
- // The resulting string is null if path is null. If the path is empty or
- // only contains whitespace characters an ArgumentException gets thrown.
- public static string? GetPathRoot(string? path)
- {
- if (PathInternal.IsEffectivelyEmpty(path.AsSpan()))
- return null;
-
- ReadOnlySpan<char> result = GetPathRoot(path.AsSpan());
- if (path!.Length == result.Length)
- return PathInternal.NormalizeDirectorySeparators(path);
-
- return PathInternal.NormalizeDirectorySeparators(result.ToString());
- }
-
- /// <remarks>
- /// Unlike the string overload, this method will not normalize directory separators.
- /// </remarks>
- public static ReadOnlySpan<char> GetPathRoot(ReadOnlySpan<char> path)
- {
- if (PathInternal.IsEffectivelyEmpty(path))
- return ReadOnlySpan<char>.Empty;
-
- int pathRoot = PathInternal.GetRootLength(path);
- return pathRoot <= 0 ? ReadOnlySpan<char>.Empty : path.Slice(0, pathRoot);
- }
-
- /// <summary>Gets whether the system is case-sensitive.</summary>
- internal static bool IsCaseSensitive => false;
-
- /// <summary>
- /// Returns the volume name for dos, UNC and device paths.
- /// </summary>
- internal static ReadOnlySpan<char> GetVolumeName(ReadOnlySpan<char> path)
- {
- // 3 cases: UNC ("\\server\share"), Device ("\\?\C:\"), or Dos ("C:\")
- ReadOnlySpan<char> root = GetPathRoot(path);
- if (root.Length == 0)
- return root;
-
- // Cut from "\\?\UNC\Server\Share" to "Server\Share"
- // Cut from "\\Server\Share" to "Server\Share"
- int startOffset = GetUncRootLength(path);
- if (startOffset == -1)
- {
- if (PathInternal.IsDevice(path))
- {
- startOffset = 4; // Cut from "\\?\C:\" to "C:"
- }
- else
- {
- startOffset = 0; // e.g. "C:"
- }
- }
-
- ReadOnlySpan<char> pathToTrim = root.Slice(startOffset);
- return Path.EndsInDirectorySeparator(pathToTrim) ? pathToTrim.Slice(0, pathToTrim.Length - 1) : pathToTrim;
- }
-
- /// <summary>
- /// Returns offset as -1 if the path is not in Unc format, otherwise returns the root length.
- /// </summary>
- /// <param name="path"></param>
- /// <returns></returns>
- internal static int GetUncRootLength(ReadOnlySpan<char> path)
- {
- bool isDevice = PathInternal.IsDevice(path);
-
- if (!isDevice && path.Slice(0, 2).EqualsOrdinal(@"\\".AsSpan()))
- return 2;
- else if (isDevice && path.Length >= 8
- && (path.Slice(0, 8).EqualsOrdinal(PathInternal.UncExtendedPathPrefix.AsSpan())
- || path.Slice(5, 4).EqualsOrdinal(@"UNC\".AsSpan())))
- return 8;
-
- return -1;
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/Path.cs b/netcore/System.Private.CoreLib/shared/System/IO/Path.cs
deleted file mode 100644
index b0cdad93bc7..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/Path.cs
+++ /dev/null
@@ -1,930 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-#nullable enable
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-using System.Runtime.InteropServices;
-using System.Text;
-
-#if MS_IO_REDIST
-using System;
-using System.IO;
-
-namespace Microsoft.IO
-#else
-namespace System.IO
-#endif
-{
- // Provides methods for processing file system strings in a cross-platform manner.
- // Most of the methods don't do a complete parsing (such as examining a UNC hostname),
- // but they will handle most string operations.
- public static partial class Path
- {
- // Public static readonly variant of the separators. The Path implementation itself is using
- // internal const variant of the separators for better performance.
- public static readonly char DirectorySeparatorChar = PathInternal.DirectorySeparatorChar;
- public static readonly char AltDirectorySeparatorChar = PathInternal.AltDirectorySeparatorChar;
- public static readonly char VolumeSeparatorChar = PathInternal.VolumeSeparatorChar;
- public static readonly char PathSeparator = PathInternal.PathSeparator;
-
- // For generating random file names
- // 8 random bytes provides 12 chars in our encoding for the 8.3 name.
- private const int KeyLength = 8;
-
- [Obsolete("Please use GetInvalidPathChars or GetInvalidFileNameChars instead.")]
- public static readonly char[] InvalidPathChars = GetInvalidPathChars();
-
- // Changes the extension of a file path. The path parameter
- // specifies a file path, and the extension parameter
- // specifies a file extension (with a leading period, such as
- // ".exe" or ".cs").
- //
- // The function returns a file path with the same root, directory, and base
- // name parts as path, but with the file extension changed to
- // the specified extension. If path is null, the function
- // returns null. If path does not contain a file extension,
- // the new file extension is appended to the path. If extension
- // is null, any existing extension is removed from path.
- [return: NotNullIfNotNull("path")]
- public static string? ChangeExtension(string? path, string? extension)
- {
- if (path == null)
- return null;
-
- int subLength = path.Length;
- if (subLength == 0)
- return string.Empty;
-
- for (int i = path.Length - 1; i >= 0; i--)
- {
- char ch = path[i];
-
- if (ch == '.')
- {
- subLength = i;
- break;
- }
-
- if (PathInternal.IsDirectorySeparator(ch))
- {
- break;
- }
- }
-
- if (extension == null)
- {
- return path.Substring(0, subLength);
- }
-
- ReadOnlySpan<char> subpath = path.AsSpan(0, subLength);
-#if MS_IO_REDIST
- return extension.Length != 0 && extension[0] == '.' ?
- StringExtensions.Concat(subpath, extension.AsSpan()) :
- StringExtensions.Concat(subpath, ".".AsSpan(), extension.AsSpan());
-#else
- return extension.StartsWith('.') ?
- string.Concat(subpath, extension) :
- string.Concat(subpath, ".", extension);
-#endif
- }
-
- /// <summary>
- /// Returns the directory portion of a file path. This method effectively
- /// removes the last segment of the given file path, i.e. it returns a
- /// string consisting of all characters up to but not including the last
- /// backslash ("\") in the file path. The returned value is null if the
- /// specified path is null, empty, or a root (such as "\", "C:", or
- /// "\\server\share").
- /// </summary>
- /// <remarks>
- /// Directory separators are normalized in the returned string.
- /// </remarks>
- public static string? GetDirectoryName(string? path)
- {
- if (path == null || PathInternal.IsEffectivelyEmpty(path.AsSpan()))
- return null;
-
- int end = GetDirectoryNameOffset(path.AsSpan());
- return end >= 0 ? PathInternal.NormalizeDirectorySeparators(path.Substring(0, end)) : null;
- }
-
- /// <summary>
- /// Returns the directory portion of a file path. The returned value is empty
- /// if the specified path is null, empty, or a root (such as "\", "C:", or
- /// "\\server\share").
- /// </summary>
- /// <remarks>
- /// Unlike the string overload, this method will not normalize directory separators.
- /// </remarks>
- public static ReadOnlySpan<char> GetDirectoryName(ReadOnlySpan<char> path)
- {
- if (PathInternal.IsEffectivelyEmpty(path))
- return ReadOnlySpan<char>.Empty;
-
- int end = GetDirectoryNameOffset(path);
- return end >= 0 ? path.Slice(0, end) : ReadOnlySpan<char>.Empty;
- }
-
- private static int GetDirectoryNameOffset(ReadOnlySpan<char> path)
- {
- int rootLength = PathInternal.GetRootLength(path);
- int end = path.Length;
- if (end <= rootLength)
- return -1;
-
- while (end > rootLength && !PathInternal.IsDirectorySeparator(path[--end])) ;
-
- // Trim off any remaining separators (to deal with C:\foo\\bar)
- while (end > rootLength && PathInternal.IsDirectorySeparator(path[end - 1]))
- end--;
-
- return end;
- }
-
- /// <summary>
- /// Returns the extension of the given path. The returned value includes the period (".") character of the
- /// extension except when you have a terminal period when you get string.Empty, such as ".exe" or ".cpp".
- /// The returned value is null if the given path is null or empty if the given path does not include an
- /// extension.
- /// </summary>
- [return: NotNullIfNotNull("path")]
- public static string? GetExtension(string? path)
- {
- if (path == null)
- return null;
-
- return GetExtension(path.AsSpan()).ToString();
- }
-
- /// <summary>
- /// Returns the extension of the given path.
- /// </summary>
- /// <remarks>
- /// The returned value is an empty ReadOnlySpan if the given path does not include an extension.
- /// </remarks>
- public static ReadOnlySpan<char> GetExtension(ReadOnlySpan<char> path)
- {
- int length = path.Length;
-
- for (int i = length - 1; i >= 0; i--)
- {
- char ch = path[i];
- if (ch == '.')
- {
- if (i != length - 1)
- return path.Slice(i, length - i);
- else
- return ReadOnlySpan<char>.Empty;
- }
- if (PathInternal.IsDirectorySeparator(ch))
- break;
- }
- return ReadOnlySpan<char>.Empty;
- }
-
- /// <summary>
- /// Returns the name and extension parts of the given path. The resulting string contains
- /// the characters of path that follow the last separator in path. The resulting string is
- /// null if path is null.
- /// </summary>
- [return: NotNullIfNotNull("path")]
- public static string? GetFileName(string? path)
- {
- if (path == null)
- return null;
-
- ReadOnlySpan<char> result = GetFileName(path.AsSpan());
- if (path.Length == result.Length)
- return path;
-
- return result.ToString();
- }
-
- /// <summary>
- /// The returned ReadOnlySpan contains the characters of the path that follows the last separator in path.
- /// </summary>
- public static ReadOnlySpan<char> GetFileName(ReadOnlySpan<char> path)
- {
- int root = GetPathRoot(path).Length;
-
- // We don't want to cut off "C:\file.txt:stream" (i.e. should be "file.txt:stream")
- // but we *do* want "C:Foo" => "Foo". This necessitates checking for the root.
-
- for (int i = path.Length; --i >= 0;)
- {
- if (i < root || PathInternal.IsDirectorySeparator(path[i]))
- return path.Slice(i + 1, path.Length - i - 1);
- }
-
- return path;
- }
-
- [return: NotNullIfNotNull("path")]
- public static string? GetFileNameWithoutExtension(string? path)
- {
- if (path == null)
- return null;
-
- ReadOnlySpan<char> result = GetFileNameWithoutExtension(path.AsSpan());
- if (path.Length == result.Length)
- return path;
-
- return result.ToString();
- }
-
- /// <summary>
- /// Returns the characters between the last separator and last (.) in the path.
- /// </summary>
- public static ReadOnlySpan<char> GetFileNameWithoutExtension(ReadOnlySpan<char> path)
- {
- ReadOnlySpan<char> fileName = GetFileName(path);
- int lastPeriod = fileName.LastIndexOf('.');
- return lastPeriod == -1 ?
- fileName : // No extension was found
- fileName.Slice(0, lastPeriod);
- }
-
- /// <summary>
- /// Returns a cryptographically strong random 8.3 string that can be
- /// used as either a folder name or a file name.
- /// </summary>
- public static unsafe string GetRandomFileName()
- {
- byte* pKey = stackalloc byte[KeyLength];
- Interop.GetRandomBytes(pKey, KeyLength);
-
-#if MS_IO_REDIST
- return StringExtensions.Create(
-#else
- return string.Create(
-#endif
- 12, (IntPtr)pKey, (span, key) => // 12 == 8 + 1 (for period) + 3
- Populate83FileNameFromRandomBytes((byte*)key, KeyLength, span));
- }
-
- /// <summary>
- /// Returns true if the path is fixed to a specific drive or UNC path. This method does no
- /// validation of the path (URIs will be returned as relative as a result).
- /// Returns false if the path specified is relative to the current drive or working directory.
- /// </summary>
- /// <remarks>
- /// Handles paths that use the alternate directory separator. It is a frequent mistake to
- /// assume that rooted paths <see cref="Path.IsPathRooted(string)"/> are not relative. This isn't the case.
- /// "C:a" is drive relative- meaning that it will be resolved against the current directory
- /// for C: (rooted, but relative). "C:\a" is rooted and not relative (the current directory
- /// will not be used to modify the path).
- /// </remarks>
- /// <exception cref="ArgumentNullException">
- /// Thrown if <paramref name="path"/> is null.
- /// </exception>
- public static bool IsPathFullyQualified(string path)
- {
- if (path == null)
- throw new ArgumentNullException(nameof(path));
-
- return IsPathFullyQualified(path.AsSpan());
- }
-
- public static bool IsPathFullyQualified(ReadOnlySpan<char> path)
- {
- return !PathInternal.IsPartiallyQualified(path);
- }
-
- /// <summary>
- /// Tests if a path's file name includes a file extension. A trailing period
- /// is not considered an extension.
- /// </summary>
- public static bool HasExtension(string? path)
- {
- if (path != null)
- {
- return HasExtension(path.AsSpan());
- }
- return false;
- }
-
- public static bool HasExtension(ReadOnlySpan<char> path)
- {
- for (int i = path.Length - 1; i >= 0; i--)
- {
- char ch = path[i];
- if (ch == '.')
- {
- return i != path.Length - 1;
- }
- if (PathInternal.IsDirectorySeparator(ch))
- break;
- }
- return false;
- }
-
- public static string Combine(string path1, string path2)
- {
- if (path1 == null || path2 == null)
- throw new ArgumentNullException((path1 == null) ? nameof(path1) : nameof(path2));
-
- return CombineInternal(path1, path2);
- }
-
- public static string Combine(string path1, string path2, string path3)
- {
- if (path1 == null || path2 == null || path3 == null)
- throw new ArgumentNullException((path1 == null) ? nameof(path1) : (path2 == null) ? nameof(path2) : nameof(path3));
-
- return CombineInternal(path1, path2, path3);
- }
-
- public static string Combine(string path1, string path2, string path3, string path4)
- {
- if (path1 == null || path2 == null || path3 == null || path4 == null)
- throw new ArgumentNullException((path1 == null) ? nameof(path1) : (path2 == null) ? nameof(path2) : (path3 == null) ? nameof(path3) : nameof(path4));
-
- return CombineInternal(path1, path2, path3, path4);
- }
-
- public static string Combine(params string[] paths)
- {
- if (paths == null)
- {
- throw new ArgumentNullException(nameof(paths));
- }
-
- int maxSize = 0;
- int firstComponent = 0;
-
- // We have two passes, the first calculates how large a buffer to allocate and does some precondition
- // checks on the paths passed in. The second actually does the combination.
-
- for (int i = 0; i < paths.Length; i++)
- {
- if (paths[i] == null)
- {
- throw new ArgumentNullException(nameof(paths));
- }
-
- if (paths[i].Length == 0)
- {
- continue;
- }
-
- if (IsPathRooted(paths[i]))
- {
- firstComponent = i;
- maxSize = paths[i].Length;
- }
- else
- {
- maxSize += paths[i].Length;
- }
-
- char ch = paths[i][paths[i].Length - 1];
- if (!PathInternal.IsDirectorySeparator(ch))
- maxSize++;
- }
-
- var builder = new ValueStringBuilder(stackalloc char[260]); // MaxShortPath on Windows
- builder.EnsureCapacity(maxSize);
-
- for (int i = firstComponent; i < paths.Length; i++)
- {
- if (paths[i].Length == 0)
- {
- continue;
- }
-
- if (builder.Length == 0)
- {
- builder.Append(paths[i]);
- }
- else
- {
- char ch = builder[builder.Length - 1];
- if (!PathInternal.IsDirectorySeparator(ch))
- {
- builder.Append(PathInternal.DirectorySeparatorChar);
- }
-
- builder.Append(paths[i]);
- }
- }
-
- return builder.ToString();
- }
-
- // Unlike Combine(), Join() methods do not consider rooting. They simply combine paths, ensuring that there
- // is a directory separator between them.
-
- public static string Join(ReadOnlySpan<char> path1, ReadOnlySpan<char> path2)
- {
- if (path1.Length == 0)
- return path2.ToString();
- if (path2.Length == 0)
- return path1.ToString();
-
- return JoinInternal(path1, path2);
- }
-
- public static string Join(ReadOnlySpan<char> path1, ReadOnlySpan<char> path2, ReadOnlySpan<char> path3)
- {
- if (path1.Length == 0)
- return Join(path2, path3);
-
- if (path2.Length == 0)
- return Join(path1, path3);
-
- if (path3.Length == 0)
- return Join(path1, path2);
-
- return JoinInternal(path1, path2, path3);
- }
-
- public static string Join(ReadOnlySpan<char> path1, ReadOnlySpan<char> path2, ReadOnlySpan<char> path3, ReadOnlySpan<char> path4)
- {
- if (path1.Length == 0)
- return Join(path2, path3, path4);
-
- if (path2.Length == 0)
- return Join(path1, path3, path4);
-
- if (path3.Length == 0)
- return Join(path1, path2, path4);
-
- if (path4.Length == 0)
- return Join(path1, path2, path3);
-
- return JoinInternal(path1, path2, path3, path4);
- }
-
- public static string Join(string? path1, string? path2)
- {
- return Join(path1.AsSpan(), path2.AsSpan());
- }
-
- public static string Join(string? path1, string? path2, string? path3)
- {
- return Join(path1.AsSpan(), path2.AsSpan(), path3.AsSpan());
- }
-
- public static string Join(string? path1, string? path2, string? path3, string? path4)
- {
- return Join(path1.AsSpan(), path2.AsSpan(), path3.AsSpan(), path4.AsSpan());
- }
-
- public static string Join(params string?[] paths)
- {
- if (paths == null)
- {
- throw new ArgumentNullException(nameof(paths));
- }
-
- if (paths.Length == 0)
- {
- return string.Empty;
- }
-
- int maxSize = 0;
- foreach (string? path in paths)
- {
- maxSize += path?.Length ?? 0;
- }
- maxSize += paths.Length - 1;
-
- var builder = new ValueStringBuilder(stackalloc char[260]); // MaxShortPath on Windows
- builder.EnsureCapacity(maxSize);
-
- for (int i = 0; i < paths.Length; i++)
- {
- string? path = paths[i];
- if (string.IsNullOrEmpty(path))
- {
- continue;
- }
-
- if (builder.Length == 0)
- {
- builder.Append(path);
- }
- else
- {
- if (!PathInternal.IsDirectorySeparator(builder[builder.Length - 1]) && !PathInternal.IsDirectorySeparator(path[0]))
- {
- builder.Append(PathInternal.DirectorySeparatorChar);
- }
-
- builder.Append(path);
- }
- }
-
- return builder.ToString();
- }
-
- public static bool TryJoin(ReadOnlySpan<char> path1, ReadOnlySpan<char> path2, Span<char> destination, out int charsWritten)
- {
- charsWritten = 0;
- if (path1.Length == 0 && path2.Length == 0)
- return true;
-
- if (path1.Length == 0 || path2.Length == 0)
- {
- ref ReadOnlySpan<char> pathToUse = ref path1.Length == 0 ? ref path2 : ref path1;
- if (destination.Length < pathToUse.Length)
- {
- return false;
- }
-
- pathToUse.CopyTo(destination);
- charsWritten = pathToUse.Length;
- return true;
- }
-
- bool needsSeparator = !(EndsInDirectorySeparator(path1) || PathInternal.StartsWithDirectorySeparator(path2));
- int charsNeeded = path1.Length + path2.Length + (needsSeparator ? 1 : 0);
- if (destination.Length < charsNeeded)
- return false;
-
- path1.CopyTo(destination);
- if (needsSeparator)
- destination[path1.Length] = DirectorySeparatorChar;
-
- path2.CopyTo(destination.Slice(path1.Length + (needsSeparator ? 1 : 0)));
-
- charsWritten = charsNeeded;
- return true;
- }
-
- public static bool TryJoin(ReadOnlySpan<char> path1, ReadOnlySpan<char> path2, ReadOnlySpan<char> path3, Span<char> destination, out int charsWritten)
- {
- charsWritten = 0;
- if (path1.Length == 0 && path2.Length == 0 && path3.Length == 0)
- return true;
-
- if (path1.Length == 0)
- return TryJoin(path2, path3, destination, out charsWritten);
- if (path2.Length == 0)
- return TryJoin(path1, path3, destination, out charsWritten);
- if (path3.Length == 0)
- return TryJoin(path1, path2, destination, out charsWritten);
-
- int neededSeparators = EndsInDirectorySeparator(path1) || PathInternal.StartsWithDirectorySeparator(path2) ? 0 : 1;
- bool needsSecondSeparator = !(EndsInDirectorySeparator(path2) || PathInternal.StartsWithDirectorySeparator(path3));
- if (needsSecondSeparator)
- neededSeparators++;
-
- int charsNeeded = path1.Length + path2.Length + path3.Length + neededSeparators;
- if (destination.Length < charsNeeded)
- return false;
-
- bool result = TryJoin(path1, path2, destination, out charsWritten);
- Debug.Assert(result, "should never fail joining first two paths");
-
- if (needsSecondSeparator)
- destination[charsWritten++] = DirectorySeparatorChar;
-
- path3.CopyTo(destination.Slice(charsWritten));
- charsWritten += path3.Length;
-
- return true;
- }
-
- private static string CombineInternal(string first, string second)
- {
- if (string.IsNullOrEmpty(first))
- return second;
-
- if (string.IsNullOrEmpty(second))
- return first;
-
- if (IsPathRooted(second.AsSpan()))
- return second;
-
- return JoinInternal(first.AsSpan(), second.AsSpan());
- }
-
- private static string CombineInternal(string first, string second, string third)
- {
- if (string.IsNullOrEmpty(first))
- return CombineInternal(second, third);
- if (string.IsNullOrEmpty(second))
- return CombineInternal(first, third);
- if (string.IsNullOrEmpty(third))
- return CombineInternal(first, second);
-
- if (IsPathRooted(third.AsSpan()))
- return third;
- if (IsPathRooted(second.AsSpan()))
- return CombineInternal(second, third);
-
- return JoinInternal(first.AsSpan(), second.AsSpan(), third.AsSpan());
- }
-
- private static string CombineInternal(string first, string second, string third, string fourth)
- {
- if (string.IsNullOrEmpty(first))
- return CombineInternal(second, third, fourth);
- if (string.IsNullOrEmpty(second))
- return CombineInternal(first, third, fourth);
- if (string.IsNullOrEmpty(third))
- return CombineInternal(first, second, fourth);
- if (string.IsNullOrEmpty(fourth))
- return CombineInternal(first, second, third);
-
- if (IsPathRooted(fourth.AsSpan()))
- return fourth;
- if (IsPathRooted(third.AsSpan()))
- return CombineInternal(third, fourth);
- if (IsPathRooted(second.AsSpan()))
- return CombineInternal(second, third, fourth);
-
- return JoinInternal(first.AsSpan(), second.AsSpan(), third.AsSpan(), fourth.AsSpan());
- }
-
- private static unsafe string JoinInternal(ReadOnlySpan<char> first, ReadOnlySpan<char> second)
- {
- Debug.Assert(first.Length > 0 && second.Length > 0, "should have dealt with empty paths");
-
- bool hasSeparator = PathInternal.IsDirectorySeparator(first[first.Length - 1])
- || PathInternal.IsDirectorySeparator(second[0]);
-
- fixed (char* f = &MemoryMarshal.GetReference(first), s = &MemoryMarshal.GetReference(second))
- {
-#if MS_IO_REDIST
- return StringExtensions.Create(
-#else
- return string.Create(
-#endif
- first.Length + second.Length + (hasSeparator ? 0 : 1),
- (First: (IntPtr)f, FirstLength: first.Length, Second: (IntPtr)s, SecondLength: second.Length, HasSeparator: hasSeparator),
- (destination, state) =>
- {
- new Span<char>((char*)state.First, state.FirstLength).CopyTo(destination);
- if (!state.HasSeparator)
- destination[state.FirstLength] = PathInternal.DirectorySeparatorChar;
- new Span<char>((char*)state.Second, state.SecondLength).CopyTo(destination.Slice(state.FirstLength + (state.HasSeparator ? 0 : 1)));
- });
- }
- }
-
- private static unsafe string JoinInternal(ReadOnlySpan<char> first, ReadOnlySpan<char> second, ReadOnlySpan<char> third)
- {
- Debug.Assert(first.Length > 0 && second.Length > 0 && third.Length > 0, "should have dealt with empty paths");
-
- bool firstHasSeparator = PathInternal.IsDirectorySeparator(first[first.Length - 1])
- || PathInternal.IsDirectorySeparator(second[0]);
- bool thirdHasSeparator = PathInternal.IsDirectorySeparator(second[second.Length - 1])
- || PathInternal.IsDirectorySeparator(third[0]);
-
- fixed (char* f = &MemoryMarshal.GetReference(first), s = &MemoryMarshal.GetReference(second), t = &MemoryMarshal.GetReference(third))
- {
-#if MS_IO_REDIST
- return StringExtensions.Create(
-#else
- return string.Create(
-#endif
- first.Length + second.Length + third.Length + (firstHasSeparator ? 0 : 1) + (thirdHasSeparator ? 0 : 1),
- (First: (IntPtr)f, FirstLength: first.Length, Second: (IntPtr)s, SecondLength: second.Length,
- Third: (IntPtr)t, ThirdLength: third.Length, FirstHasSeparator: firstHasSeparator, ThirdHasSeparator: thirdHasSeparator),
- (destination, state) =>
- {
- new Span<char>((char*)state.First, state.FirstLength).CopyTo(destination);
- if (!state.FirstHasSeparator)
- destination[state.FirstLength] = PathInternal.DirectorySeparatorChar;
- new Span<char>((char*)state.Second, state.SecondLength).CopyTo(destination.Slice(state.FirstLength + (state.FirstHasSeparator ? 0 : 1)));
- if (!state.ThirdHasSeparator)
- destination[destination.Length - state.ThirdLength - 1] = PathInternal.DirectorySeparatorChar;
- new Span<char>((char*)state.Third, state.ThirdLength).CopyTo(destination.Slice(destination.Length - state.ThirdLength));
- });
- }
- }
-
- private static unsafe string JoinInternal(ReadOnlySpan<char> first, ReadOnlySpan<char> second, ReadOnlySpan<char> third, ReadOnlySpan<char> fourth)
- {
- Debug.Assert(first.Length > 0 && second.Length > 0 && third.Length > 0 && fourth.Length > 0, "should have dealt with empty paths");
-
- bool firstHasSeparator = PathInternal.IsDirectorySeparator(first[first.Length - 1])
- || PathInternal.IsDirectorySeparator(second[0]);
- bool thirdHasSeparator = PathInternal.IsDirectorySeparator(second[second.Length - 1])
- || PathInternal.IsDirectorySeparator(third[0]);
- bool fourthHasSeparator = PathInternal.IsDirectorySeparator(third[third.Length - 1])
- || PathInternal.IsDirectorySeparator(fourth[0]);
-
- fixed (char* f = &MemoryMarshal.GetReference(first), s = &MemoryMarshal.GetReference(second), t = &MemoryMarshal.GetReference(third), u = &MemoryMarshal.GetReference(fourth))
- {
-#if MS_IO_REDIST
- return StringExtensions.Create(
-#else
- return string.Create(
-#endif
- first.Length + second.Length + third.Length + fourth.Length + (firstHasSeparator ? 0 : 1) + (thirdHasSeparator ? 0 : 1) + (fourthHasSeparator ? 0 : 1),
- (First: (IntPtr)f, FirstLength: first.Length, Second: (IntPtr)s, SecondLength: second.Length,
- Third: (IntPtr)t, ThirdLength: third.Length, Fourth: (IntPtr)u, FourthLength: fourth.Length,
- FirstHasSeparator: firstHasSeparator, ThirdHasSeparator: thirdHasSeparator, FourthHasSeparator: fourthHasSeparator),
- (destination, state) =>
- {
- new Span<char>((char*)state.First, state.FirstLength).CopyTo(destination);
- if (!state.FirstHasSeparator)
- destination[state.FirstLength] = PathInternal.DirectorySeparatorChar;
- new Span<char>((char*)state.Second, state.SecondLength).CopyTo(destination.Slice(state.FirstLength + (state.FirstHasSeparator ? 0 : 1)));
- if (!state.ThirdHasSeparator)
- destination[state.FirstLength + state.SecondLength + (state.FirstHasSeparator ? 0 : 1)] = PathInternal.DirectorySeparatorChar;
- new Span<char>((char*)state.Third, state.ThirdLength).CopyTo(destination.Slice(state.FirstLength + state.SecondLength + (state.FirstHasSeparator ? 0 : 1) + (state.ThirdHasSeparator ? 0 : 1)));
- if (!state.FourthHasSeparator)
- destination[destination.Length - state.FourthLength - 1] = PathInternal.DirectorySeparatorChar;
- new Span<char>((char*)state.Fourth, state.FourthLength).CopyTo(destination.Slice(destination.Length - state.FourthLength));
- });
- }
- }
-
- private static ReadOnlySpan<byte> Base32Char => new byte[32] { // uses C# compiler's optimization for static byte[] data
- (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', (byte)'h',
- (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', (byte)'o', (byte)'p',
- (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', (byte)'v', (byte)'w', (byte)'x',
- (byte)'y', (byte)'z', (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5' };
-
- private static unsafe void Populate83FileNameFromRandomBytes(byte* bytes, int byteCount, Span<char> chars)
- {
- // This method requires bytes of length 8 and chars of length 12.
- Debug.Assert(bytes != null);
- Debug.Assert(byteCount == 8, $"Unexpected {nameof(byteCount)}");
- Debug.Assert(chars.Length == 12, $"Unexpected {nameof(chars)}.Length");
-
- byte b0 = bytes[0];
- byte b1 = bytes[1];
- byte b2 = bytes[2];
- byte b3 = bytes[3];
- byte b4 = bytes[4];
-
- // write to chars[11] first in order to eliminate redundant bounds checks
- chars[11] = (char)Base32Char[bytes[7] & 0x1F];
-
- // Consume the 5 Least significant bits of the first 5 bytes
- chars[0] = (char)Base32Char[b0 & 0x1F];
- chars[1] = (char)Base32Char[b1 & 0x1F];
- chars[2] = (char)Base32Char[b2 & 0x1F];
- chars[3] = (char)Base32Char[b3 & 0x1F];
- chars[4] = (char)Base32Char[b4 & 0x1F];
-
- // Consume 3 MSB of b0, b1, MSB bits 6, 7 of b3, b4
- chars[5] = (char)Base32Char[
- ((b0 & 0xE0) >> 5) |
- ((b3 & 0x60) >> 2)];
-
- chars[6] = (char)Base32Char[
- ((b1 & 0xE0) >> 5) |
- ((b4 & 0x60) >> 2)];
-
- // Consume 3 MSB bits of b2, 1 MSB bit of b3, b4
- b2 >>= 5;
-
- Debug.Assert((b2 & 0xF8) == 0, "Unexpected set bits");
-
- if ((b3 & 0x80) != 0)
- b2 |= 0x08;
- if ((b4 & 0x80) != 0)
- b2 |= 0x10;
-
- chars[7] = (char)Base32Char[b2];
-
- // Set the file extension separator
- chars[8] = '.';
-
- // Consume the 5 Least significant bits of the remaining 3 bytes
- chars[9] = (char)Base32Char[bytes[5] & 0x1F];
- chars[10] = (char)Base32Char[bytes[6] & 0x1F];
- }
-
- /// <summary>
- /// Create a relative path from one path to another. Paths will be resolved before calculating the difference.
- /// Default path comparison for the active platform will be used (OrdinalIgnoreCase for Windows or Mac, Ordinal for Unix).
- /// </summary>
- /// <param name="relativeTo">The source path the output should be relative to. This path is always considered to be a directory.</param>
- /// <param name="path">The destination path.</param>
- /// <returns>The relative path or <paramref name="path"/> if the paths don't share the same root.</returns>
- /// <exception cref="ArgumentNullException">Thrown if <paramref name="relativeTo"/> or <paramref name="path"/> is <c>null</c> or an empty string.</exception>
- public static string GetRelativePath(string relativeTo, string path)
- {
- return GetRelativePath(relativeTo, path, StringComparison);
- }
-
- private static string GetRelativePath(string relativeTo, string path, StringComparison comparisonType)
- {
- if (relativeTo == null)
- throw new ArgumentNullException(nameof(relativeTo));
-
- if (PathInternal.IsEffectivelyEmpty(relativeTo.AsSpan()))
- throw new ArgumentException(SR.Arg_PathEmpty, nameof(relativeTo));
-
- if (path == null)
- throw new ArgumentNullException(nameof(path));
-
- if (PathInternal.IsEffectivelyEmpty(path.AsSpan()))
- throw new ArgumentException(SR.Arg_PathEmpty, nameof(path));
-
- Debug.Assert(comparisonType == StringComparison.Ordinal || comparisonType == StringComparison.OrdinalIgnoreCase);
-
- relativeTo = GetFullPath(relativeTo);
- path = GetFullPath(path);
-
- // Need to check if the roots are different- if they are we need to return the "to" path.
- if (!PathInternal.AreRootsEqual(relativeTo, path, comparisonType))
- return path;
-
- int commonLength = PathInternal.GetCommonPathLength(relativeTo, path, ignoreCase: comparisonType == StringComparison.OrdinalIgnoreCase);
-
- // If there is nothing in common they can't share the same root, return the "to" path as is.
- if (commonLength == 0)
- return path;
-
- // Trailing separators aren't significant for comparison
- int relativeToLength = relativeTo.Length;
- if (EndsInDirectorySeparator(relativeTo.AsSpan()))
- relativeToLength--;
-
- bool pathEndsInSeparator = EndsInDirectorySeparator(path.AsSpan());
- int pathLength = path.Length;
- if (pathEndsInSeparator)
- pathLength--;
-
- // If we have effectively the same path, return "."
- if (relativeToLength == pathLength && commonLength >= relativeToLength) return ".";
-
- // We have the same root, we need to calculate the difference now using the
- // common Length and Segment count past the length.
- //
- // Some examples:
- //
- // C:\Foo C:\Bar L3, S1 -> ..\Bar
- // C:\Foo C:\Foo\Bar L6, S0 -> Bar
- // C:\Foo\Bar C:\Bar\Bar L3, S2 -> ..\..\Bar\Bar
- // C:\Foo\Foo C:\Foo\Bar L7, S1 -> ..\Bar
-
- var sb = new ValueStringBuilder(stackalloc char[260]);
- sb.EnsureCapacity(Math.Max(relativeTo.Length, path.Length));
-
- // Add parent segments for segments past the common on the "from" path
- if (commonLength < relativeToLength)
- {
- sb.Append("..");
-
- for (int i = commonLength + 1; i < relativeToLength; i++)
- {
- if (PathInternal.IsDirectorySeparator(relativeTo[i]))
- {
- sb.Append(DirectorySeparatorChar);
- sb.Append("..");
- }
- }
- }
- else if (PathInternal.IsDirectorySeparator(path[commonLength]))
- {
- // No parent segments and we need to eat the initial separator
- // (C:\Foo C:\Foo\Bar case)
- commonLength++;
- }
-
- // Now add the rest of the "to" path, adding back the trailing separator
- int differenceLength = pathLength - commonLength;
- if (pathEndsInSeparator)
- differenceLength++;
-
- if (differenceLength > 0)
- {
- if (sb.Length > 0)
- {
- sb.Append(DirectorySeparatorChar);
- }
-
- sb.Append(path.AsSpan(commonLength, differenceLength));
- }
-
- return sb.ToString();
- }
-
- /// <summary>Returns a comparison that can be used to compare file and directory names for equality.</summary>
- internal static StringComparison StringComparison =>
- IsCaseSensitive ?
- StringComparison.Ordinal :
- StringComparison.OrdinalIgnoreCase;
-
- /// <summary>
- /// Trims one trailing directory separator beyond the root of the path.
- /// </summary>
- public static string TrimEndingDirectorySeparator(string path) => PathInternal.TrimEndingDirectorySeparator(path);
-
- /// <summary>
- /// Trims one trailing directory separator beyond the root of the path.
- /// </summary>
- public static ReadOnlySpan<char> TrimEndingDirectorySeparator(ReadOnlySpan<char> path) => PathInternal.TrimEndingDirectorySeparator(path);
-
- /// <summary>
- /// Returns true if the path ends in a directory separator.
- /// </summary>
- public static bool EndsInDirectorySeparator(ReadOnlySpan<char> path) => PathInternal.EndsInDirectorySeparator(path);
-
- /// <summary>
- /// Returns true if the path ends in a directory separator.
- /// </summary>
- public static bool EndsInDirectorySeparator(string path) => PathInternal.EndsInDirectorySeparator(path);
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/PathHelper.Windows.cs b/netcore/System.Private.CoreLib/shared/System/IO/PathHelper.Windows.cs
deleted file mode 100644
index abf5c346148..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/PathHelper.Windows.cs
+++ /dev/null
@@ -1,251 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-#nullable enable
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-using System.Text;
-
-namespace System.IO
-{
- /// <summary>
- /// Wrapper to help with path normalization.
- /// </summary>
- internal static class PathHelper
- {
- /// <summary>
- /// Normalize the given path.
- /// </summary>
- /// <remarks>
- /// Normalizes via Win32 GetFullPathName().
- /// </remarks>
- /// <param name="path">Path to normalize</param>
- /// <exception cref="PathTooLongException">Thrown if we have a string that is too large to fit into a UNICODE_STRING.</exception>
- /// <exception cref="IOException">Thrown if the path is empty.</exception>
- /// <returns>Normalized path</returns>
- internal static string Normalize(string path)
- {
- var builder = new ValueStringBuilder(stackalloc char[PathInternal.MaxShortPath]);
-
- // Get the full path
- GetFullPathName(path.AsSpan(), ref builder);
-
- // If we have the exact same string we were passed in, don't allocate another string.
- // TryExpandShortName does this input identity check.
- string result = builder.AsSpan().IndexOf('~') >= 0
- ? TryExpandShortFileName(ref builder, originalPath: path)
- : builder.AsSpan().Equals(path.AsSpan(), StringComparison.Ordinal) ? path : builder.ToString();
-
- // Clear the buffer
- builder.Dispose();
- return result;
- }
-
- /// <summary>
- /// Normalize the given path.
- /// </summary>
- /// <remarks>
- /// Exceptions are the same as the string overload.
- /// </remarks>
- internal static string Normalize(ref ValueStringBuilder path)
- {
- var builder = new ValueStringBuilder(stackalloc char[PathInternal.MaxShortPath]);
-
- // Get the full path
- GetFullPathName(path.AsSpan(terminate: true), ref builder);
-
- string result = builder.AsSpan().IndexOf('~') >= 0
- ? TryExpandShortFileName(ref builder, originalPath: null)
- : builder.ToString();
-
- // Clear the buffer
- builder.Dispose();
- return result;
- }
-
- /// <summary>
- /// Calls GetFullPathName on the given path.
- /// </summary>
- /// <param name="path">The path name. MUST be null terminated after the span.</param>
- /// <param name="builder">Builder that will store the result.</param>
- private static void GetFullPathName(ReadOnlySpan<char> path, ref ValueStringBuilder builder)
- {
- // If the string starts with an extended prefix we would need to remove it from the path before we call GetFullPathName as
- // it doesn't root extended paths correctly. We don't currently resolve extended paths, so we'll just assert here.
- Debug.Assert(PathInternal.IsPartiallyQualified(path) || !PathInternal.IsExtended(path));
-
- uint result;
- while ((result = Interop.Kernel32.GetFullPathNameW(ref MemoryMarshal.GetReference(path), (uint)builder.Capacity, ref builder.GetPinnableReference(), IntPtr.Zero)) > builder.Capacity)
- {
- // Reported size is greater than the buffer size. Increase the capacity.
- builder.EnsureCapacity(checked((int)result));
- }
-
- if (result == 0)
- {
- // Failure, get the error and throw
- int errorCode = Marshal.GetLastWin32Error();
- if (errorCode == 0)
- errorCode = Interop.Errors.ERROR_BAD_PATHNAME;
- throw Win32Marshal.GetExceptionForWin32Error(errorCode, path.ToString());
- }
-
- builder.Length = (int)result;
- }
-
- internal static int PrependDevicePathChars(ref ValueStringBuilder content, bool isDosUnc, ref ValueStringBuilder buffer)
- {
- int length = content.Length;
-
- length += isDosUnc
- ? PathInternal.UncExtendedPrefixLength - PathInternal.UncPrefixLength
- : PathInternal.DevicePrefixLength;
-
- buffer.EnsureCapacity(length + 1);
- buffer.Length = 0;
-
- if (isDosUnc)
- {
- // Is a \\Server\Share, put \\?\UNC\ in the front
- buffer.Append(PathInternal.UncExtendedPathPrefix);
-
- // Copy Server\Share\... over to the buffer
- buffer.Append(content.AsSpan(PathInternal.UncPrefixLength));
-
- // Return the prefix difference
- return PathInternal.UncExtendedPrefixLength - PathInternal.UncPrefixLength;
- }
- else
- {
- // Not an UNC, put the \\?\ prefix in front, then the original string
- buffer.Append(PathInternal.ExtendedPathPrefix);
- buffer.Append(content.AsSpan());
- return PathInternal.DevicePrefixLength;
- }
- }
-
- internal static string TryExpandShortFileName(ref ValueStringBuilder outputBuilder, string? originalPath)
- {
- // We guarantee we'll expand short names for paths that only partially exist. As such, we need to find the part of the path that actually does exist. To
- // avoid allocating a lot we'll create only one input array and modify the contents with embedded nulls.
-
- Debug.Assert(!PathInternal.IsPartiallyQualified(outputBuilder.AsSpan()), "should have resolved by now");
-
- // We'll have one of a few cases by now (the normalized path will have already:
- //
- // 1. Dos path (C:\)
- // 2. Dos UNC (\\Server\Share)
- // 3. Dos device path (\\.\C:\, \\?\C:\)
- //
- // We want to put the extended syntax on the front if it doesn't already have it (for long path support and speed), which may mean switching from \\.\.
- //
- // Note that we will never get \??\ here as GetFullPathName() does not recognize \??\ and will return it as C:\??\ (or whatever the current drive is).
-
- int rootLength = PathInternal.GetRootLength(outputBuilder.AsSpan());
- bool isDevice = PathInternal.IsDevice(outputBuilder.AsSpan());
-
- // As this is a corner case we're not going to add a stackalloc here to keep the stack pressure down.
- ValueStringBuilder inputBuilder = default;
-
- bool isDosUnc = false;
- int rootDifference = 0;
- bool wasDotDevice = false;
-
- // Add the extended prefix before expanding to allow growth over MAX_PATH
- if (isDevice)
- {
- // We have one of the following (\\?\ or \\.\)
- inputBuilder.Append(outputBuilder.AsSpan());
-
- if (outputBuilder[2] == '.')
- {
- wasDotDevice = true;
- inputBuilder[2] = '?';
- }
- }
- else
- {
- isDosUnc = !PathInternal.IsDevice(outputBuilder.AsSpan()) && outputBuilder.Length > 1 && outputBuilder[0] == '\\' && outputBuilder[1] == '\\';
- rootDifference = PrependDevicePathChars(ref outputBuilder, isDosUnc, ref inputBuilder);
- }
-
- rootLength += rootDifference;
- int inputLength = inputBuilder.Length;
-
- bool success = false;
- int foundIndex = inputBuilder.Length - 1;
-
- while (!success)
- {
- uint result = Interop.Kernel32.GetLongPathNameW(
- ref inputBuilder.GetPinnableReference(terminate: true), ref outputBuilder.GetPinnableReference(), (uint)outputBuilder.Capacity);
-
- // Replace any temporary null we added
- if (inputBuilder[foundIndex] == '\0') inputBuilder[foundIndex] = '\\';
-
- if (result == 0)
- {
- // Look to see if we couldn't find the file
- int error = Marshal.GetLastWin32Error();
- if (error != Interop.Errors.ERROR_FILE_NOT_FOUND && error != Interop.Errors.ERROR_PATH_NOT_FOUND)
- {
- // Some other failure, give up
- break;
- }
-
- // We couldn't find the path at the given index, start looking further back in the string.
- foundIndex--;
-
- for (; foundIndex > rootLength && inputBuilder[foundIndex] != '\\'; foundIndex--) ;
- if (foundIndex == rootLength)
- {
- // Can't trim the path back any further
- break;
- }
- else
- {
- // Temporarily set a null in the string to get Windows to look further up the path
- inputBuilder[foundIndex] = '\0';
- }
- }
- else if (result > outputBuilder.Capacity)
- {
- // Not enough space. The result count for this API does not include the null terminator.
- outputBuilder.EnsureCapacity(checked((int)result));
- }
- else
- {
- // Found the path
- success = true;
- outputBuilder.Length = checked((int)result);
- if (foundIndex < inputLength - 1)
- {
- // It was a partial find, put the non-existent part of the path back
- outputBuilder.Append(inputBuilder.AsSpan(foundIndex, inputBuilder.Length - foundIndex));
- }
- }
- }
-
- // If we were able to expand the path, use it, otherwise use the original full path result
- ref ValueStringBuilder builderToUse = ref (success ? ref outputBuilder : ref inputBuilder);
-
- // Switch back from \\?\ to \\.\ if necessary
- if (wasDotDevice)
- builderToUse[2] = '.';
-
- // Change from \\?\UNC\ to \\?\UN\\ if needed
- if (isDosUnc)
- builderToUse[PathInternal.UncExtendedPrefixLength - PathInternal.UncPrefixLength] = '\\';
-
- // Strip out any added characters at the front of the string
- ReadOnlySpan<char> output = builderToUse.AsSpan(rootDifference);
-
- string returnValue = ((originalPath != null) && output.Equals(originalPath.AsSpan(), StringComparison.Ordinal))
- ? originalPath : output.ToString();
-
- inputBuilder.Dispose();
- return returnValue;
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/PathInternal.Unix.cs b/netcore/System.Private.CoreLib/shared/System/IO/PathInternal.Unix.cs
deleted file mode 100644
index f9a018ae5e3..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/PathInternal.Unix.cs
+++ /dev/null
@@ -1,98 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-#nullable enable
-using System.Diagnostics;
-using System.Text;
-using System.Runtime.InteropServices;
-
-namespace System.IO
-{
- /// <summary>Contains internal path helpers that are shared between many projects.</summary>
- internal static partial class PathInternal
- {
- internal const char DirectorySeparatorChar = '/';
- internal const char AltDirectorySeparatorChar = '/';
- internal const char VolumeSeparatorChar = '/';
- internal const char PathSeparator = ':';
- internal const string DirectorySeparatorCharAsString = "/";
- internal const string ParentDirectoryPrefix = @"../";
-
- internal static int GetRootLength(ReadOnlySpan<char> path)
- {
- return path.Length > 0 && IsDirectorySeparator(path[0]) ? 1 : 0;
- }
-
- internal static bool IsDirectorySeparator(char c)
- {
- // The alternate directory separator char is the same as the directory separator,
- // so we only need to check one.
- Debug.Assert(DirectorySeparatorChar == AltDirectorySeparatorChar);
- return c == DirectorySeparatorChar;
- }
-
- /// <summary>
- /// Normalize separators in the given path. Compresses forward slash runs.
- /// </summary>
- internal static string NormalizeDirectorySeparators(string path)
- {
- if (string.IsNullOrEmpty(path))
- return path;
-
- // Make a pass to see if we need to normalize so we can potentially skip allocating
- bool normalized = true;
-
- for (int i = 0; i < path.Length; i++)
- {
- if (IsDirectorySeparator(path[i])
- && (i + 1 < path.Length && IsDirectorySeparator(path[i + 1])))
- {
- normalized = false;
- break;
- }
- }
-
- if (normalized)
- return path;
-
- StringBuilder builder = new StringBuilder(path.Length);
-
- for (int i = 0; i < path.Length; i++)
- {
- char current = path[i];
-
- // Skip if we have another separator following
- if (IsDirectorySeparator(current)
- && (i + 1 < path.Length && IsDirectorySeparator(path[i + 1])))
- continue;
-
- builder.Append(current);
- }
-
- return builder.ToString();
- }
-
- internal static bool IsPartiallyQualified(ReadOnlySpan<char> path)
- {
- // This is much simpler than Windows where paths can be rooted, but not fully qualified (such as Drive Relative)
- // As long as the path is rooted in Unix it doesn't use the current directory and therefore is fully qualified.
- return !Path.IsPathRooted(path);
- }
-
- /// <summary>
- /// Returns true if the path is effectively empty for the current OS.
- /// For unix, this is empty or null. For Windows, this is empty, null, or
- /// just spaces ((char)32).
- /// </summary>
- internal static bool IsEffectivelyEmpty(string? path)
- {
- return string.IsNullOrEmpty(path);
- }
-
- internal static bool IsEffectivelyEmpty(ReadOnlySpan<char> path)
- {
- return path.IsEmpty;
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/PathInternal.Windows.cs b/netcore/System.Private.CoreLib/shared/System/IO/PathInternal.Windows.cs
deleted file mode 100644
index 300d1265579..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/PathInternal.Windows.cs
+++ /dev/null
@@ -1,414 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-#nullable enable
-using System.Diagnostics.CodeAnalysis;
-using System.Runtime.CompilerServices;
-using System.Text;
-
-namespace System.IO
-{
- /// <summary>Contains internal path helpers that are shared between many projects.</summary>
- internal static partial class PathInternal
- {
- // All paths in Win32 ultimately end up becoming a path to a File object in the Windows object manager. Passed in paths get mapped through
- // DosDevice symbolic links in the object tree to actual File objects under \Devices. To illustrate, this is what happens with a typical
- // path "Foo" passed as a filename to any Win32 API:
- //
- // 1. "Foo" is recognized as a relative path and is appended to the current directory (say, "C:\" in our example)
- // 2. "C:\Foo" is prepended with the DosDevice namespace "\??\"
- // 3. CreateFile tries to create an object handle to the requested file "\??\C:\Foo"
- // 4. The Object Manager recognizes the DosDevices prefix and looks
- // a. First in the current session DosDevices ("\Sessions\1\DosDevices\" for example, mapped network drives go here)
- // b. If not found in the session, it looks in the Global DosDevices ("\GLOBAL??\")
- // 5. "C:" is found in DosDevices (in our case "\GLOBAL??\C:", which is a symbolic link to "\Device\HarddiskVolume6")
- // 6. The full path is now "\Device\HarddiskVolume6\Foo", "\Device\HarddiskVolume6" is a File object and parsing is handed off
- // to the registered parsing method for Files
- // 7. The registered open method for File objects is invoked to create the file handle which is then returned
- //
- // There are multiple ways to directly specify a DosDevices path. The final format of "\??\" is one way. It can also be specified
- // as "\\.\" (the most commonly documented way) and "\\?\". If the question mark syntax is used the path will skip normalization
- // (essentially GetFullPathName()) and path length checks.
-
- // Windows Kernel-Mode Object Manager
- // https://msdn.microsoft.com/en-us/library/windows/hardware/ff565763.aspx
- // https://channel9.msdn.com/Shows/Going+Deep/Windows-NT-Object-Manager
- //
- // Introduction to MS-DOS Device Names
- // https://msdn.microsoft.com/en-us/library/windows/hardware/ff548088.aspx
- //
- // Local and Global MS-DOS Device Names
- // https://msdn.microsoft.com/en-us/library/windows/hardware/ff554302.aspx
-
- internal const char DirectorySeparatorChar = '\\';
- internal const char AltDirectorySeparatorChar = '/';
- internal const char VolumeSeparatorChar = ':';
- internal const char PathSeparator = ';';
-
- internal const string DirectorySeparatorCharAsString = "\\";
-
- internal const string ExtendedPathPrefix = @"\\?\";
- internal const string UncPathPrefix = @"\\";
- internal const string UncExtendedPrefixToInsert = @"?\UNC\";
- internal const string UncExtendedPathPrefix = @"\\?\UNC\";
- internal const string DevicePathPrefix = @"\\.\";
- internal const string ParentDirectoryPrefix = @"..\";
-
- internal const int MaxShortPath = 260;
- internal const int MaxShortDirectoryPath = 248;
- // \\?\, \\.\, \??\
- internal const int DevicePrefixLength = 4;
- // \\
- internal const int UncPrefixLength = 2;
- // \\?\UNC\, \\.\UNC\
- internal const int UncExtendedPrefixLength = 8;
-
- /// <summary>
- /// Returns true if the given character is a valid drive letter
- /// </summary>
- internal static bool IsValidDriveChar(char value)
- {
- return (value >= 'A' && value <= 'Z') || (value >= 'a' && value <= 'z');
- }
-
- internal static bool EndsWithPeriodOrSpace(string? path)
- {
- if (string.IsNullOrEmpty(path))
- return false;
-
- char c = path[path.Length - 1];
- return c == ' ' || c == '.';
- }
-
- /// <summary>
- /// Adds the extended path prefix (\\?\) if not already a device path, IF the path is not relative,
- /// AND the path is more than 259 characters. (> MAX_PATH + null). This will also insert the extended
- /// prefix if the path ends with a period or a space. Trailing periods and spaces are normally eaten
- /// away from paths during normalization, but if we see such a path at this point it should be
- /// normalized and has retained the final characters. (Typically from one of the *Info classes)
- /// </summary>
- [return: NotNullIfNotNull("path")]
- internal static string? EnsureExtendedPrefixIfNeeded(string? path)
- {
- if (path != null && (path.Length >= MaxShortPath || EndsWithPeriodOrSpace(path)))
- {
- return EnsureExtendedPrefix(path);
- }
- else
- {
- return path;
- }
- }
-
- /// <summary>
- /// Adds the extended path prefix (\\?\) if not relative or already a device path.
- /// </summary>
- internal static string EnsureExtendedPrefix(string path)
- {
- // Putting the extended prefix on the path changes the processing of the path. It won't get normalized, which
- // means adding to relative paths will prevent them from getting the appropriate current directory inserted.
-
- // If it already has some variant of a device path (\??\, \\?\, \\.\, //./, etc.) we don't need to change it
- // as it is either correct or we will be changing the behavior. When/if Windows supports long paths implicitly
- // in the future we wouldn't want normalization to come back and break existing code.
-
- // In any case, all internal usages should be hitting normalize path (Path.GetFullPath) before they hit this
- // shimming method. (Or making a change that doesn't impact normalization, such as adding a filename to a
- // normalized base path.)
- if (IsPartiallyQualified(path.AsSpan()) || IsDevice(path.AsSpan()))
- return path;
-
- // Given \\server\share in longpath becomes \\?\UNC\server\share
- if (path.StartsWith(UncPathPrefix, StringComparison.OrdinalIgnoreCase))
- return path.Insert(2, UncExtendedPrefixToInsert);
-
- return ExtendedPathPrefix + path;
- }
-
- /// <summary>
- /// Returns true if the path uses any of the DOS device path syntaxes. ("\\.\", "\\?\", or "\??\")
- /// </summary>
- internal static bool IsDevice(ReadOnlySpan<char> path)
- {
- // If the path begins with any two separators is will be recognized and normalized and prepped with
- // "\??\" for internal usage correctly. "\??\" is recognized and handled, "/??/" is not.
- return IsExtended(path)
- ||
- (
- path.Length >= DevicePrefixLength
- && IsDirectorySeparator(path[0])
- && IsDirectorySeparator(path[1])
- && (path[2] == '.' || path[2] == '?')
- && IsDirectorySeparator(path[3])
- );
- }
-
- /// <summary>
- /// Returns true if the path is a device UNC (\\?\UNC\, \\.\UNC\)
- /// </summary>
- internal static bool IsDeviceUNC(ReadOnlySpan<char> path)
- {
- return path.Length >= UncExtendedPrefixLength
- && IsDevice(path)
- && IsDirectorySeparator(path[7])
- && path[4] == 'U'
- && path[5] == 'N'
- && path[6] == 'C';
- }
-
- /// <summary>
- /// Returns true if the path uses the canonical form of extended syntax ("\\?\" or "\??\"). If the
- /// path matches exactly (cannot use alternate directory separators) Windows will skip normalization
- /// and path length checks.
- /// </summary>
- internal static bool IsExtended(ReadOnlySpan<char> path)
- {
- // While paths like "//?/C:/" will work, they're treated the same as "\\.\" paths.
- // Skipping of normalization will *only* occur if back slashes ('\') are used.
- return path.Length >= DevicePrefixLength
- && path[0] == '\\'
- && (path[1] == '\\' || path[1] == '?')
- && path[2] == '?'
- && path[3] == '\\';
- }
-
- /// <summary>
- /// Check for known wildcard characters. '*' and '?' are the most common ones.
- /// </summary>
- internal static bool HasWildCardCharacters(ReadOnlySpan<char> path)
- {
- // Question mark is part of dos device syntax so we have to skip if we are
- int startIndex = IsDevice(path) ? ExtendedPathPrefix.Length : 0;
-
- // [MS - FSA] 2.1.4.4 Algorithm for Determining if a FileName Is in an Expression
- // https://msdn.microsoft.com/en-us/library/ff469270.aspx
- for (int i = startIndex; i < path.Length; i++)
- {
- char c = path[i];
- if (c <= '?') // fast path for common case - '?' is highest wildcard character
- {
- if (c == '\"' || c == '<' || c == '>' || c == '*' || c == '?')
- return true;
- }
- }
-
- return false;
- }
-
- /// <summary>
- /// Gets the length of the root of the path (drive, share, etc.).
- /// </summary>
- internal static int GetRootLength(ReadOnlySpan<char> path)
- {
- int pathLength = path.Length;
- int i = 0;
-
- bool deviceSyntax = IsDevice(path);
- bool deviceUnc = deviceSyntax && IsDeviceUNC(path);
-
- if ((!deviceSyntax || deviceUnc) && pathLength > 0 && IsDirectorySeparator(path[0]))
- {
- // UNC or simple rooted path (e.g. "\foo", NOT "\\?\C:\foo")
- if (deviceUnc || (pathLength > 1 && IsDirectorySeparator(path[1])))
- {
- // UNC (\\?\UNC\ or \\), scan past server\share
-
- // Start past the prefix ("\\" or "\\?\UNC\")
- i = deviceUnc ? UncExtendedPrefixLength : UncPrefixLength;
-
- // Skip two separators at most
- int n = 2;
- while (i < pathLength && (!IsDirectorySeparator(path[i]) || --n > 0))
- i++;
- }
- else
- {
- // Current drive rooted (e.g. "\foo")
- i = 1;
- }
- }
- else if (deviceSyntax)
- {
- // Device path (e.g. "\\?\.", "\\.\")
- // Skip any characters following the prefix that aren't a separator
- i = DevicePrefixLength;
- while (i < pathLength && !IsDirectorySeparator(path[i]))
- i++;
-
- // If there is another separator take it, as long as we have had at least one
- // non-separator after the prefix (e.g. don't take "\\?\\", but take "\\?\a\")
- if (i < pathLength && i > DevicePrefixLength && IsDirectorySeparator(path[i]))
- i++;
- }
- else if (pathLength >= 2
- && path[1] == VolumeSeparatorChar
- && IsValidDriveChar(path[0]))
- {
- // Valid drive specified path ("C:", "D:", etc.)
- i = 2;
-
- // If the colon is followed by a directory separator, move past it (e.g "C:\")
- if (pathLength > 2 && IsDirectorySeparator(path[2]))
- i++;
- }
-
- return i;
- }
-
- /// <summary>
- /// Returns true if the path specified is relative to the current drive or working directory.
- /// Returns false if the path is fixed to a specific drive or UNC path. This method does no
- /// validation of the path (URIs will be returned as relative as a result).
- /// </summary>
- /// <remarks>
- /// Handles paths that use the alternate directory separator. It is a frequent mistake to
- /// assume that rooted paths (Path.IsPathRooted) are not relative. This isn't the case.
- /// "C:a" is drive relative- meaning that it will be resolved against the current directory
- /// for C: (rooted, but relative). "C:\a" is rooted and not relative (the current directory
- /// will not be used to modify the path).
- /// </remarks>
- internal static bool IsPartiallyQualified(ReadOnlySpan<char> path)
- {
- if (path.Length < 2)
- {
- // It isn't fixed, it must be relative. There is no way to specify a fixed
- // path with one character (or less).
- return true;
- }
-
- if (IsDirectorySeparator(path[0]))
- {
- // There is no valid way to specify a relative path with two initial slashes or
- // \? as ? isn't valid for drive relative paths and \??\ is equivalent to \\?\
- return !(path[1] == '?' || IsDirectorySeparator(path[1]));
- }
-
- // The only way to specify a fixed path that doesn't begin with two slashes
- // is the drive, colon, slash format- i.e. C:\
- return !((path.Length >= 3)
- && (path[1] == VolumeSeparatorChar)
- && IsDirectorySeparator(path[2])
- // To match old behavior we'll check the drive character for validity as the path is technically
- // not qualified if you don't have a valid drive. "=:\" is the "=" file's default data stream.
- && IsValidDriveChar(path[0]));
- }
-
- /// <summary>
- /// True if the given character is a directory separator.
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal static bool IsDirectorySeparator(char c)
- {
- return c == DirectorySeparatorChar || c == AltDirectorySeparatorChar;
- }
-
- /// <summary>
- /// Normalize separators in the given path. Converts forward slashes into back slashes and compresses slash runs, keeping initial 2 if present.
- /// Also trims initial whitespace in front of "rooted" paths (see PathStartSkip).
- ///
- /// This effectively replicates the behavior of the legacy NormalizePath when it was called with fullCheck=false and expandShortpaths=false.
- /// The current NormalizePath gets directory separator normalization from Win32's GetFullPathName(), which will resolve relative paths and as
- /// such can't be used here (and is overkill for our uses).
- ///
- /// Like the current NormalizePath this will not try and analyze periods/spaces within directory segments.
- /// </summary>
- /// <remarks>
- /// The only callers that used to use Path.Normalize(fullCheck=false) were Path.GetDirectoryName() and Path.GetPathRoot(). Both usages do
- /// not need trimming of trailing whitespace here.
- ///
- /// GetPathRoot() could technically skip normalizing separators after the second segment- consider as a future optimization.
- ///
- /// For legacy desktop behavior with ExpandShortPaths:
- /// - It has no impact on GetPathRoot() so doesn't need consideration.
- /// - It could impact GetDirectoryName(), but only if the path isn't relative (C:\ or \\Server\Share).
- ///
- /// In the case of GetDirectoryName() the ExpandShortPaths behavior was undocumented and provided inconsistent results if the path was
- /// fixed/relative. For example: "C:\PROGRA~1\A.TXT" would return "C:\Program Files" while ".\PROGRA~1\A.TXT" would return ".\PROGRA~1". If you
- /// ultimately call GetFullPath() this doesn't matter, but if you don't or have any intermediate string handling could easily be tripped up by
- /// this undocumented behavior.
- ///
- /// We won't match this old behavior because:
- ///
- /// 1. It was undocumented
- /// 2. It was costly (extremely so if it actually contained '~')
- /// 3. Doesn't play nice with string logic
- /// 4. Isn't a cross-plat friendly concept/behavior
- /// </remarks>
- internal static string NormalizeDirectorySeparators(string path)
- {
- if (string.IsNullOrEmpty(path))
- return path;
-
- char current;
-
- // Make a pass to see if we need to normalize so we can potentially skip allocating
- bool normalized = true;
-
- for (int i = 0; i < path.Length; i++)
- {
- current = path[i];
- if (IsDirectorySeparator(current)
- && (current != DirectorySeparatorChar
- // Check for sequential separators past the first position (we need to keep initial two for UNC/extended)
- || (i > 0 && i + 1 < path.Length && IsDirectorySeparator(path[i + 1]))))
- {
- normalized = false;
- break;
- }
- }
-
- if (normalized)
- return path;
-
- var builder = new ValueStringBuilder(stackalloc char[MaxShortPath]);
-
- int start = 0;
- if (IsDirectorySeparator(path[start]))
- {
- start++;
- builder.Append(DirectorySeparatorChar);
- }
-
- for (int i = start; i < path.Length; i++)
- {
- current = path[i];
-
- // If we have a separator
- if (IsDirectorySeparator(current))
- {
- // If the next is a separator, skip adding this
- if (i + 1 < path.Length && IsDirectorySeparator(path[i + 1]))
- {
- continue;
- }
-
- // Ensure it is the primary separator
- current = DirectorySeparatorChar;
- }
-
- builder.Append(current);
- }
-
- return builder.ToString();
- }
-
- /// <summary>
- /// Returns true if the path is effectively empty for the current OS.
- /// For unix, this is empty or null. For Windows, this is empty, null, or
- /// just spaces ((char)32).
- /// </summary>
- internal static bool IsEffectivelyEmpty(ReadOnlySpan<char> path)
- {
- if (path.IsEmpty)
- return true;
-
- foreach (char c in path)
- {
- if (c != ' ')
- return false;
- }
- return true;
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/PathInternal.cs b/netcore/System.Private.CoreLib/shared/System/IO/PathInternal.cs
deleted file mode 100644
index dfea93658d3..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/PathInternal.cs
+++ /dev/null
@@ -1,248 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Diagnostics;
-using System.Text;
-
-namespace System.IO
-{
- /// <summary>Contains internal path helpers that are shared between many projects.</summary>
- internal static partial class PathInternal
- {
- /// <summary>
- /// Returns true if the path starts in a directory separator.
- /// </summary>
- internal static bool StartsWithDirectorySeparator(ReadOnlySpan<char> path) => path.Length > 0 && IsDirectorySeparator(path[0]);
-
-#if MS_IO_REDIST
- internal static string EnsureTrailingSeparator(string path)
- => EndsInDirectorySeparator(path) ? path : path + DirectorySeparatorCharAsString;
-#else
- internal static string EnsureTrailingSeparator(string path)
- => EndsInDirectorySeparator(path.AsSpan()) ? path : path + DirectorySeparatorCharAsString;
-#endif
-
- internal static bool IsRoot(ReadOnlySpan<char> path)
- => path.Length == GetRootLength(path);
-
- /// <summary>
- /// Get the common path length from the start of the string.
- /// </summary>
- internal static int GetCommonPathLength(string first, string second, bool ignoreCase)
- {
- int commonChars = EqualStartingCharacterCount(first, second, ignoreCase: ignoreCase);
-
- // If nothing matches
- if (commonChars == 0)
- return commonChars;
-
- // Or we're a full string and equal length or match to a separator
- if (commonChars == first.Length
- && (commonChars == second.Length || IsDirectorySeparator(second[commonChars])))
- return commonChars;
-
- if (commonChars == second.Length && IsDirectorySeparator(first[commonChars]))
- return commonChars;
-
- // It's possible we matched somewhere in the middle of a segment e.g. C:\Foodie and C:\Foobar.
- while (commonChars > 0 && !IsDirectorySeparator(first[commonChars - 1]))
- commonChars--;
-
- return commonChars;
- }
-
- /// <summary>
- /// Gets the count of common characters from the left optionally ignoring case
- /// </summary>
- internal static unsafe int EqualStartingCharacterCount(string first, string second, bool ignoreCase)
- {
- if (string.IsNullOrEmpty(first) || string.IsNullOrEmpty(second)) return 0;
-
- int commonChars = 0;
-
- fixed (char* f = first)
- fixed (char* s = second)
- {
- char* l = f;
- char* r = s;
- char* leftEnd = l + first.Length;
- char* rightEnd = r + second.Length;
-
- while (l != leftEnd && r != rightEnd
- && (*l == *r || (ignoreCase && char.ToUpperInvariant(*l) == char.ToUpperInvariant(*r))))
- {
- commonChars++;
- l++;
- r++;
- }
- }
-
- return commonChars;
- }
-
- /// <summary>
- /// Returns true if the two paths have the same root
- /// </summary>
- internal static bool AreRootsEqual(string first, string second, StringComparison comparisonType)
- {
- int firstRootLength = GetRootLength(first.AsSpan());
- int secondRootLength = GetRootLength(second.AsSpan());
-
- return firstRootLength == secondRootLength
- && string.Compare(
- strA: first,
- indexA: 0,
- strB: second,
- indexB: 0,
- length: firstRootLength,
- comparisonType: comparisonType) == 0;
- }
-
- /// <summary>
- /// Try to remove relative segments from the given path (without combining with a root).
- /// </summary>
- /// <param name="path">Input path</param>
- /// <param name="rootLength">The length of the root of the given path</param>
- internal static string RemoveRelativeSegments(string path, int rootLength)
- {
- var sb = new ValueStringBuilder(stackalloc char[260 /* PathInternal.MaxShortPath */]);
-
- if (RemoveRelativeSegments(path.AsSpan(), rootLength, ref sb))
- {
- path = sb.ToString();
- }
-
- sb.Dispose();
- return path;
- }
-
- /// <summary>
- /// Try to remove relative segments from the given path (without combining with a root).
- /// </summary>
- /// <param name="path">Input path</param>
- /// <param name="rootLength">The length of the root of the given path</param>
- /// <param name="sb">String builder that will store the result</param>
- /// <returns>"true" if the path was modified</returns>
- internal static bool RemoveRelativeSegments(ReadOnlySpan<char> path, int rootLength, ref ValueStringBuilder sb)
- {
- Debug.Assert(rootLength > 0);
- bool flippedSeparator = false;
-
- int skip = rootLength;
- // We treat "\.." , "\." and "\\" as a relative segment. We want to collapse the first separator past the root presuming
- // the root actually ends in a separator. Otherwise the first segment for RemoveRelativeSegments
- // in cases like "\\?\C:\.\" and "\\?\C:\..\", the first segment after the root will be ".\" and "..\" which is not considered as a relative segment and hence not be removed.
- if (PathInternal.IsDirectorySeparator(path[skip - 1]))
- skip--;
-
- // Remove "//", "/./", and "/../" from the path by copying each character to the output,
- // except the ones we're removing, such that the builder contains the normalized path
- // at the end.
- if (skip > 0)
- {
- sb.Append(path.Slice(0, skip));
- }
-
- for (int i = skip; i < path.Length; i++)
- {
- char c = path[i];
-
- if (PathInternal.IsDirectorySeparator(c) && i + 1 < path.Length)
- {
- // Skip this character if it's a directory separator and if the next character is, too,
- // e.g. "parent//child" => "parent/child"
- if (PathInternal.IsDirectorySeparator(path[i + 1]))
- {
- continue;
- }
-
- // Skip this character and the next if it's referring to the current directory,
- // e.g. "parent/./child" => "parent/child"
- if ((i + 2 == path.Length || PathInternal.IsDirectorySeparator(path[i + 2])) &&
- path[i + 1] == '.')
- {
- i++;
- continue;
- }
-
- // Skip this character and the next two if it's referring to the parent directory,
- // e.g. "parent/child/../grandchild" => "parent/grandchild"
- if (i + 2 < path.Length &&
- (i + 3 == path.Length || PathInternal.IsDirectorySeparator(path[i + 3])) &&
- path[i + 1] == '.' && path[i + 2] == '.')
- {
- // Unwind back to the last slash (and if there isn't one, clear out everything).
- int s;
- for (s = sb.Length - 1; s >= skip; s--)
- {
- if (PathInternal.IsDirectorySeparator(sb[s]))
- {
- sb.Length = (i + 3 >= path.Length && s == skip) ? s + 1 : s; // to avoid removing the complete "\tmp\" segment in cases like \\?\C:\tmp\..\, C:\tmp\..
- break;
- }
- }
- if (s < skip)
- {
- sb.Length = skip;
- }
-
- i += 2;
- continue;
- }
- }
-
- // Normalize the directory separator if needed
- if (c != PathInternal.DirectorySeparatorChar && c == PathInternal.AltDirectorySeparatorChar)
- {
- c = PathInternal.DirectorySeparatorChar;
- flippedSeparator = true;
- }
-
- sb.Append(c);
- }
-
- // If we haven't changed the source path, return the original
- if (!flippedSeparator && sb.Length == path.Length)
- {
- return false;
- }
-
- // We may have eaten the trailing separator from the root when we started and not replaced it
- if (skip != rootLength && sb.Length < rootLength)
- {
- sb.Append(path[rootLength - 1]);
- }
-
- return true;
- }
-
- /// <summary>
- /// Trims one trailing directory separator beyond the root of the path.
- /// </summary>
- internal static string TrimEndingDirectorySeparator(string path) =>
- EndsInDirectorySeparator(path) && !IsRoot(path.AsSpan()) ?
- path.Substring(0, path.Length - 1) :
- path;
-
- /// <summary>
- /// Returns true if the path ends in a directory separator.
- /// </summary>
- internal static bool EndsInDirectorySeparator(string path) =>
- !string.IsNullOrEmpty(path) && IsDirectorySeparator(path[path.Length - 1]);
-
- /// <summary>
- /// Trims one trailing directory separator beyond the root of the path.
- /// </summary>
- internal static ReadOnlySpan<char> TrimEndingDirectorySeparator(ReadOnlySpan<char> path) =>
- EndsInDirectorySeparator(path) && !IsRoot(path) ?
- path.Slice(0, path.Length - 1) :
- path;
-
- /// <summary>
- /// Returns true if the path ends in a directory separator.
- /// </summary>
- internal static bool EndsInDirectorySeparator(ReadOnlySpan<char> path) =>
- path.Length > 0 && IsDirectorySeparator(path[path.Length - 1]);
- }
-} \ No newline at end of file
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/PathTooLongException.cs b/netcore/System.Private.CoreLib/shared/System/IO/PathTooLongException.cs
deleted file mode 100644
index 3a69930a911..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/PathTooLongException.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Runtime.Serialization;
-
-namespace System.IO
-{
- [Serializable]
- [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
- public class PathTooLongException : IOException
- {
- public PathTooLongException()
- : base(SR.IO_PathTooLong)
- {
- HResult = HResults.COR_E_PATHTOOLONG;
- }
-
- public PathTooLongException(string? message)
- : base(message)
- {
- HResult = HResults.COR_E_PATHTOOLONG;
- }
-
- public PathTooLongException(string? message, Exception? innerException)
- : base(message, innerException)
- {
- HResult = HResults.COR_E_PATHTOOLONG;
- }
-
- protected PathTooLongException(SerializationInfo info, StreamingContext context)
- : base(info, context)
- {
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/PersistedFiles.Names.Unix.cs b/netcore/System.Private.CoreLib/shared/System/IO/PersistedFiles.Names.Unix.cs
deleted file mode 100644
index 8984f1aee32..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/PersistedFiles.Names.Unix.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-namespace System.IO
-{
- internal static partial class PersistedFiles
- {
- // Temporary data, /tmp/.dotnet/corefx
- // User-persisted data, ~/.dotnet/corefx/
- // System-persisted data, /etc/dotnet/corefx/
-
- internal const string TopLevelDirectory = "dotnet";
- internal const string TopLevelHiddenDirectory = "." + TopLevelDirectory;
- internal const string SecondLevelDirectory = "corefx";
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/PersistedFiles.Unix.cs b/netcore/System.Private.CoreLib/shared/System/IO/PersistedFiles.Unix.cs
deleted file mode 100644
index a610614f276..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/PersistedFiles.Unix.cs
+++ /dev/null
@@ -1,164 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-#nullable enable
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-
-namespace System.IO
-{
- internal static partial class PersistedFiles
- {
- private static string? s_userProductDirectory;
-
- /// <summary>
- /// Get the location of where to persist information for a particular aspect of the framework,
- /// such as "cryptography".
- /// </summary>
- /// <param name="featureName">The directory name for the feature</param>
- /// <returns>A path within the user's home directory for persisting data for the feature</returns>
- internal static string GetUserFeatureDirectory(string featureName)
- {
- if (s_userProductDirectory == null)
- {
- EnsureUserDirectories();
- }
-
- return Path.Combine(s_userProductDirectory!, featureName);
- }
-
- /// <summary>
- /// Get the location of where to persist information for a particular aspect of a feature of
- /// the framework, such as "x509stores" within "cryptography".
- /// </summary>
- /// <param name="featureName">The directory name for the feature</param>
- /// <param name="subFeatureName">The directory name for the sub-feature</param>
- /// <returns>A path within the user's home directory for persisting data for the sub-feature</returns>
- internal static string GetUserFeatureDirectory(string featureName, string subFeatureName)
- {
- if (s_userProductDirectory == null)
- {
- EnsureUserDirectories();
- }
-
- return Path.Combine(s_userProductDirectory!, featureName, subFeatureName);
- }
-
- /// <summary>
- /// Get the location of where to persist information for a particular aspect of the framework,
- /// with a lot of hierarchy, such as ["cryptography", "x509stores", "my"]
- /// </summary>
- /// <param name="featurePathParts">A non-empty set of directories to use for the storage hierarchy</param>
- /// <returns>A path within the user's home directory for persisting data for the feature</returns>
- internal static string GetUserFeatureDirectory(params string[] featurePathParts)
- {
- Debug.Assert(featurePathParts != null);
- Debug.Assert(featurePathParts.Length > 0);
-
- if (s_userProductDirectory == null)
- {
- EnsureUserDirectories();
- }
-
- return Path.Combine(s_userProductDirectory!, Path.Combine(featurePathParts));
- }
-
- private static void EnsureUserDirectories()
- {
- string? userHomeDirectory = GetHomeDirectory();
-
- if (string.IsNullOrEmpty(userHomeDirectory))
- {
- throw new InvalidOperationException(SR.PersistedFiles_NoHomeDirectory);
- }
-
- s_userProductDirectory = Path.Combine(
- userHomeDirectory,
- TopLevelHiddenDirectory,
- SecondLevelDirectory);
- }
-
- /// <summary>Gets the current user's home directory.</summary>
- /// <returns>The path to the home directory, or null if it could not be determined.</returns>
- internal static string? GetHomeDirectory()
- {
- // First try to get the user's home directory from the HOME environment variable.
- // This should work in most cases.
- string? userHomeDirectory = Environment.GetEnvironmentVariable("HOME");
- if (!string.IsNullOrEmpty(userHomeDirectory))
- return userHomeDirectory;
-
- // In initialization conditions, however, the "HOME" environment variable may
- // not yet be set. For such cases, consult with the password entry.
- unsafe
- {
- // First try with a buffer that should suffice for 99% of cases.
- // Note that, theoretically, userHomeDirectory may be null in the success case
- // if we simply couldn't find a home directory for the current user.
- // In that case, we pass back the null value and let the caller decide
- // what to do.
- const int BufLen = Interop.Sys.Passwd.InitialBufferSize;
- byte* stackBuf = stackalloc byte[BufLen];
- if (TryGetHomeDirectoryFromPasswd(stackBuf, BufLen, out userHomeDirectory))
- return userHomeDirectory;
-
- // Fallback to heap allocations if necessary, growing the buffer until
- // we succeed. TryGetHomeDirectory will throw if there's an unexpected error.
- int lastBufLen = BufLen;
- while (true)
- {
- lastBufLen *= 2;
- byte[] heapBuf = new byte[lastBufLen];
- fixed (byte* buf = &heapBuf[0])
- {
- if (TryGetHomeDirectoryFromPasswd(buf, heapBuf.Length, out userHomeDirectory))
- return userHomeDirectory;
- }
- }
- }
- }
-
- /// <summary>Wrapper for getpwuid_r.</summary>
- /// <param name="buf">The scratch buffer to pass into getpwuid_r.</param>
- /// <param name="bufLen">The length of <paramref name="buf"/>.</param>
- /// <param name="path">The resulting path; null if the user didn't have an entry.</param>
- /// <returns>true if the call was successful (path may still be null); false is a larger buffer is needed.</returns>
- private static unsafe bool TryGetHomeDirectoryFromPasswd(byte* buf, int bufLen, out string? path)
- {
- // Call getpwuid_r to get the passwd struct
- Interop.Sys.Passwd passwd;
- int error = Interop.Sys.GetPwUidR(Interop.Sys.GetEUid(), out passwd, buf, bufLen);
-
- // If the call succeeds, give back the home directory path retrieved
- if (error == 0)
- {
- Debug.Assert(passwd.HomeDirectory != null);
- path = Marshal.PtrToStringAnsi((IntPtr)passwd.HomeDirectory);
- return true;
- }
-
- // If the current user's entry could not be found, give back null
- // path, but still return true as false indicates the buffer was
- // too small.
- if (error == -1)
- {
- path = null;
- return true;
- }
-
- var errorInfo = new Interop.ErrorInfo(error);
-
- // If the call failed because the buffer was too small, return false to
- // indicate the caller should try again with a larger buffer.
- if (errorInfo.Error == Interop.Error.ERANGE)
- {
- path = null;
- return false;
- }
-
- // Otherwise, fail.
- throw new IOException(errorInfo.GetErrorMessage(), errorInfo.RawErrno);
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/PinnedBufferMemoryStream.cs b/netcore/System.Private.CoreLib/shared/System/IO/PinnedBufferMemoryStream.cs
deleted file mode 100644
index 9c002974786..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/PinnedBufferMemoryStream.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-/*============================================================
-**
-**
-**
-**
-**
-** Purpose: Pins a byte[], exposing it as an unmanaged memory
-** stream. Used in ResourceReader for corner cases.
-**
-**
-===========================================================*/
-
-using System.Runtime.InteropServices;
-using System.Diagnostics;
-
-namespace System.IO
-{
- internal sealed unsafe class PinnedBufferMemoryStream : UnmanagedMemoryStream
- {
- private readonly byte[] _array;
- private GCHandle _pinningHandle;
-
- internal PinnedBufferMemoryStream(byte[] array)
- {
- Debug.Assert(array != null, "Array can't be null");
-
- _array = array;
- _pinningHandle = GCHandle.Alloc(array, GCHandleType.Pinned);
- // Now the byte[] is pinned for the lifetime of this instance.
- // But I also need to get a pointer to that block of memory...
- int len = array.Length;
- fixed (byte* ptr = &MemoryMarshal.GetReference((Span<byte>)array))
- Initialize(ptr, len, len, FileAccess.Read);
- }
-
-#if !NETSTANDARD2_0
- public override int Read(Span<byte> buffer) => ReadCore(buffer);
-
- public override void Write(ReadOnlySpan<byte> buffer) => WriteCore(buffer);
-#endif
-
- ~PinnedBufferMemoryStream()
- {
- Dispose(false);
- }
-
- protected override void Dispose(bool disposing)
- {
- if (_pinningHandle.IsAllocated)
- {
- _pinningHandle.Free();
- }
-
- base.Dispose(disposing);
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/SeekOrigin.cs b/netcore/System.Private.CoreLib/shared/System/IO/SeekOrigin.cs
deleted file mode 100644
index 3798a0ce702..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/SeekOrigin.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-namespace System.IO
-{
- // Provides seek reference points. To seek to the end of a stream,
- // call stream.Seek(0, SeekOrigin.End).
- public enum SeekOrigin
- {
- // These constants match Win32's FILE_BEGIN, FILE_CURRENT, and FILE_END
- Begin = 0,
- Current = 1,
- End = 2,
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/Stream.cs b/netcore/System.Private.CoreLib/shared/System/IO/Stream.cs
deleted file mode 100644
index cf05e669ba3..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/Stream.cs
+++ /dev/null
@@ -1,1400 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-/*============================================================
-**
-**
-**
-**
-**
-** Purpose: Abstract base class for all Streams. Provides
-** default implementations of asynchronous reads & writes, in
-** terms of the synchronous reads & writes (and vice versa).
-**
-**
-===========================================================*/
-
-using System.Buffers;
-using System.Diagnostics;
-using System.Runtime.ExceptionServices;
-using System.Runtime.InteropServices;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace System.IO
-{
- public abstract partial class Stream : MarshalByRefObject, IDisposable, IAsyncDisposable
- {
- public static readonly Stream Null = new NullStream();
-
- // We pick a value that is the largest multiple of 4096 that is still smaller than the large object heap threshold (85K).
- // The CopyTo/CopyToAsync buffer is short-lived and is likely to be collected at Gen0, and it offers a significant
- // improvement in Copy performance.
- private const int DefaultCopyBufferSize = 81920;
-
- // To implement Async IO operations on streams that don't support async IO
-
- private ReadWriteTask? _activeReadWriteTask;
- private SemaphoreSlim? _asyncActiveSemaphore;
-
- internal SemaphoreSlim EnsureAsyncActiveSemaphoreInitialized()
- {
- // Lazily-initialize _asyncActiveSemaphore. As we're never accessing the SemaphoreSlim's
- // WaitHandle, we don't need to worry about Disposing it.
- return LazyInitializer.EnsureInitialized(ref _asyncActiveSemaphore, () => new SemaphoreSlim(1, 1));
- }
-
- public abstract bool CanRead
- {
- get;
- }
-
- // If CanSeek is false, Position, Seek, Length, and SetLength should throw.
- public abstract bool CanSeek
- {
- get;
- }
-
- public virtual bool CanTimeout => false;
-
- public abstract bool CanWrite
- {
- get;
- }
-
- public abstract long Length
- {
- get;
- }
-
- public abstract long Position
- {
- get;
- set;
- }
-
- public virtual int ReadTimeout
- {
- get => throw new InvalidOperationException(SR.InvalidOperation_TimeoutsNotSupported);
- set => throw new InvalidOperationException(SR.InvalidOperation_TimeoutsNotSupported);
- }
-
- public virtual int WriteTimeout
- {
- get => throw new InvalidOperationException(SR.InvalidOperation_TimeoutsNotSupported);
- set => throw new InvalidOperationException(SR.InvalidOperation_TimeoutsNotSupported);
- }
-
- public Task CopyToAsync(Stream destination)
- {
- int bufferSize = GetCopyBufferSize();
-
- return CopyToAsync(destination, bufferSize);
- }
-
- public Task CopyToAsync(Stream destination, int bufferSize)
- {
- return CopyToAsync(destination, bufferSize, CancellationToken.None);
- }
-
- public Task CopyToAsync(Stream destination, CancellationToken cancellationToken)
- {
- int bufferSize = GetCopyBufferSize();
-
- return CopyToAsync(destination, bufferSize, cancellationToken);
- }
-
- public virtual Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
- {
- StreamHelpers.ValidateCopyToArgs(this, destination, bufferSize);
-
- return CopyToAsyncInternal(destination, bufferSize, cancellationToken);
- }
-
- private async Task CopyToAsyncInternal(Stream destination, int bufferSize, CancellationToken cancellationToken)
- {
- byte[] buffer = ArrayPool<byte>.Shared.Rent(bufferSize);
- try
- {
- while (true)
- {
- int bytesRead = await ReadAsync(new Memory<byte>(buffer), cancellationToken).ConfigureAwait(false);
- if (bytesRead == 0) break;
- await destination.WriteAsync(new ReadOnlyMemory<byte>(buffer, 0, bytesRead), cancellationToken).ConfigureAwait(false);
- }
- }
- finally
- {
- ArrayPool<byte>.Shared.Return(buffer);
- }
- }
-
- // Reads the bytes from the current stream and writes the bytes to
- // the destination stream until all bytes are read, starting at
- // the current position.
- public void CopyTo(Stream destination)
- {
- int bufferSize = GetCopyBufferSize();
-
- CopyTo(destination, bufferSize);
- }
-
- public virtual void CopyTo(Stream destination, int bufferSize)
- {
- StreamHelpers.ValidateCopyToArgs(this, destination, bufferSize);
-
- byte[] buffer = ArrayPool<byte>.Shared.Rent(bufferSize);
- try
- {
- int read;
- while ((read = Read(buffer, 0, buffer.Length)) != 0)
- {
- destination.Write(buffer, 0, read);
- }
- }
- finally
- {
- ArrayPool<byte>.Shared.Return(buffer);
- }
- }
-
- private int GetCopyBufferSize()
- {
- int bufferSize = DefaultCopyBufferSize;
-
- if (CanSeek)
- {
- long length = Length;
- long position = Position;
- if (length <= position) // Handles negative overflows
- {
- // There are no bytes left in the stream to copy.
- // However, because CopyTo{Async} is virtual, we need to
- // ensure that any override is still invoked to provide its
- // own validation, so we use the smallest legal buffer size here.
- bufferSize = 1;
- }
- else
- {
- long remaining = length - position;
- if (remaining > 0)
- {
- // In the case of a positive overflow, stick to the default size
- bufferSize = (int)Math.Min(bufferSize, remaining);
- }
- }
- }
-
- return bufferSize;
- }
-
- public virtual void CopyTo(ReadOnlySpanAction<byte, object?> callback, object? state, int bufferSize)
- {
- if (callback == null) throw new ArgumentNullException(nameof(callback));
-
- CopyTo(new WriteCallbackStream(callback, state), bufferSize);
- }
-
- public virtual Task CopyToAsync(Func<ReadOnlyMemory<byte>, object?, CancellationToken, ValueTask> callback, object? state, int bufferSize, CancellationToken cancellationToken)
- {
- if (callback == null) throw new ArgumentNullException(nameof(callback));
-
- return CopyToAsync(new WriteCallbackStream(callback, state), bufferSize, cancellationToken);
- }
-
- private sealed class WriteCallbackStream : Stream
- {
- private readonly ReadOnlySpanAction<byte, object?>? _action;
- private readonly Func<ReadOnlyMemory<byte>, object?, CancellationToken, ValueTask>? _func;
- private readonly object? _state;
-
- public WriteCallbackStream(ReadOnlySpanAction<byte, object?> action, object? state)
- {
- _action = action;
- _state = state;
- }
-
- public WriteCallbackStream(Func<ReadOnlyMemory<byte>, object?, CancellationToken, ValueTask> func, object? state)
- {
- _func = func;
- _state = state;
- }
-
- public override void Write(byte[] buffer, int offset, int count)
- {
- Write(new ReadOnlySpan<byte>(buffer, offset, count));
- }
-
- public override void Write(ReadOnlySpan<byte> span)
- {
- if (_action != null)
- {
- _action(span, _state);
- return;
- }
-
- // In case a poorly implemented CopyToAsync(Stream, ...) method decides to call
- // the destination stream's Write rather than WriteAsync, we make it work, but this
- // does not need to be efficient.
- Debug.Assert(_func != null);
- _func(span.ToArray(), _state, CancellationToken.None).AsTask().GetAwaiter().GetResult();
-
- }
-
- public override Task WriteAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken)
- {
- return WriteAsync(new ReadOnlyMemory<byte>(buffer, offset, length), cancellationToken).AsTask();
- }
-
- public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
- {
- if (_func != null)
- {
- return _func(buffer, _state, cancellationToken);
- }
-
- // In case a poorly implemented CopyTo(Stream, ...) method decides to call
- // the destination stream's WriteAsync rather than Write, we make it work,
- // but this does not need to be efficient.
- Debug.Assert(_action != null);
- try
- {
- cancellationToken.ThrowIfCancellationRequested();
- _action(buffer.Span, _state);
- return default;
- }
- catch (Exception e)
- {
- return new ValueTask(Task.FromException(e));
- }
- }
-
- public override bool CanRead => false;
- public override bool CanSeek => false;
- public override bool CanWrite => true;
- public override void Flush() { }
- public override Task FlushAsync(CancellationToken token) => Task.CompletedTask;
- public override long Length => throw new NotSupportedException();
- public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
- public override int Read(byte[] buffer, int offset, int count) => throw new NotSupportedException();
- public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
- public override void SetLength(long value) => throw new NotSupportedException();
- }
-
- // 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 now.
- public virtual void Close()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- public void Dispose()
- {
- Close();
- }
-
- protected virtual void Dispose(bool disposing)
- {
- // Note: Never change this to call other virtual methods on Stream
- // like Write, since the state on subclasses has already been
- // torn down. This is the last code to run on cleanup for a stream.
- }
-
- public virtual ValueTask DisposeAsync()
- {
- try
- {
- Dispose();
- return default;
- }
- catch (Exception exc)
- {
- return new ValueTask(Task.FromException(exc));
- }
- }
-
- public abstract void Flush();
-
- public Task FlushAsync()
- {
- return FlushAsync(CancellationToken.None);
- }
-
- public virtual Task FlushAsync(CancellationToken cancellationToken)
- {
- return Task.Factory.StartNew(state => ((Stream)state!).Flush(), this,
- cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
- }
-
- [Obsolete("CreateWaitHandle will be removed eventually. Please use \"new ManualResetEvent(false)\" instead.")]
- protected virtual WaitHandle CreateWaitHandle()
- {
- return new ManualResetEvent(false);
- }
-
- public virtual IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object? state)
- {
- return BeginReadInternal(buffer, offset, count, callback, state, serializeAsynchronously: false, apm: true);
- }
-
- internal IAsyncResult BeginReadInternal(
- byte[] buffer, int offset, int count, AsyncCallback? callback, object? state,
- bool serializeAsynchronously, bool apm)
- {
- if (!CanRead) throw Error.GetReadNotSupported();
-
- // To avoid a race with a stream's position pointer & generating race conditions
- // with internal buffer indexes in our own streams that
- // don't natively support async IO operations when there are multiple
- // async requests outstanding, we will block the application's main
- // thread if it does a second IO request until the first one completes.
- SemaphoreSlim semaphore = EnsureAsyncActiveSemaphoreInitialized();
- Task? semaphoreTask = null;
- if (serializeAsynchronously)
- {
- semaphoreTask = semaphore.WaitAsync();
- }
- else
- {
- semaphore.Wait();
- }
-
- // Create the task to asynchronously do a Read. This task serves both
- // as the asynchronous work item and as the IAsyncResult returned to the user.
- var asyncResult = new ReadWriteTask(true /*isRead*/, apm, delegate
- {
- // The ReadWriteTask stores all of the parameters to pass to Read.
- // As we're currently inside of it, we can get the current task
- // and grab the parameters from it.
- var thisTask = Task.InternalCurrent as ReadWriteTask;
- Debug.Assert(thisTask != null && thisTask._stream != null && thisTask._buffer != null,
- "Inside ReadWriteTask, InternalCurrent should be the ReadWriteTask, and stream and buffer should be set");
-
- try
- {
- // Do the Read and return the number of bytes read
- return thisTask._stream.Read(thisTask._buffer, thisTask._offset, thisTask._count);
- }
- finally
- {
- // If this implementation is part of Begin/EndXx, then the EndXx method will handle
- // finishing the async operation. However, if this is part of XxAsync, then there won't
- // be an end method, and this task is responsible for cleaning up.
- if (!thisTask._apm)
- {
- thisTask._stream.FinishTrackingAsyncOperation();
- }
-
- thisTask.ClearBeginState(); // just to help alleviate some memory pressure
- }
- }, state, this, buffer, offset, count, callback);
-
- // Schedule it
- if (semaphoreTask != null)
- RunReadWriteTaskWhenReady(semaphoreTask, asyncResult);
- else
- RunReadWriteTask(asyncResult);
-
-
- return asyncResult; // return it
- }
-
- public virtual int EndRead(IAsyncResult asyncResult)
- {
- if (asyncResult == null)
- throw new ArgumentNullException(nameof(asyncResult));
-
- ReadWriteTask? readTask = _activeReadWriteTask;
-
- if (readTask == null)
- {
- throw new ArgumentException(SR.InvalidOperation_WrongAsyncResultOrEndReadCalledMultiple);
- }
- else if (readTask != asyncResult)
- {
- throw new InvalidOperationException(SR.InvalidOperation_WrongAsyncResultOrEndReadCalledMultiple);
- }
- else if (!readTask._isRead)
- {
- throw new ArgumentException(SR.InvalidOperation_WrongAsyncResultOrEndReadCalledMultiple);
- }
-
- try
- {
- return readTask.GetAwaiter().GetResult(); // block until completion, then get result / propagate any exception
- }
- finally
- {
- FinishTrackingAsyncOperation();
- }
- }
-
- public Task<int> ReadAsync(byte[] buffer, int offset, int count)
- {
- return ReadAsync(buffer, offset, count, CancellationToken.None);
- }
-
- public virtual Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
- {
- // If cancellation was requested, bail early with an already completed task.
- // Otherwise, return a task that represents the Begin/End methods.
- return cancellationToken.IsCancellationRequested
- ? Task.FromCanceled<int>(cancellationToken)
- : BeginEndReadAsync(buffer, offset, count);
- }
-
- public virtual ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
- {
- if (MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> array))
- {
- return new ValueTask<int>(ReadAsync(array.Array!, array.Offset, array.Count, cancellationToken));
- }
- else
- {
- byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
- return FinishReadAsync(ReadAsync(sharedBuffer, 0, buffer.Length, cancellationToken), sharedBuffer, buffer);
-
- static async ValueTask<int> FinishReadAsync(Task<int> readTask, byte[] localBuffer, Memory<byte> localDestination)
- {
- try
- {
- int result = await readTask.ConfigureAwait(false);
- new Span<byte>(localBuffer, 0, result).CopyTo(localDestination.Span);
- return result;
- }
- finally
- {
- ArrayPool<byte>.Shared.Return(localBuffer);
- }
- }
- }
- }
-
- private Task<int> BeginEndReadAsync(byte[] buffer, int offset, int count)
- {
- if (!HasOverriddenBeginEndRead())
- {
- // If the Stream does not override Begin/EndRead, then we can take an optimized path
- // that skips an extra layer of tasks / IAsyncResults.
- return (Task<int>)BeginReadInternal(buffer, offset, count, null, null, serializeAsynchronously: true, apm: false);
- }
-
- // Otherwise, we need to wrap calls to Begin/EndWrite to ensure we use the derived type's functionality.
- return TaskFactory<int>.FromAsyncTrim(
- this, new ReadWriteParameters { Buffer = buffer, Offset = offset, Count = count },
- (stream, args, callback, state) => stream.BeginRead(args.Buffer, args.Offset, args.Count, callback, state), // cached by compiler
- (stream, asyncResult) => stream.EndRead(asyncResult)); // cached by compiler
- }
-
- private struct ReadWriteParameters // struct for arguments to Read and Write calls
- {
- internal byte[] Buffer;
- internal int Offset;
- internal int Count;
- }
-
-
-
- public virtual IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object? state)
- {
- return BeginWriteInternal(buffer, offset, count, callback, state, serializeAsynchronously: false, apm: true);
- }
-
- internal IAsyncResult BeginWriteInternal(
- byte[] buffer, int offset, int count, AsyncCallback? callback, object? state,
- bool serializeAsynchronously, bool apm)
- {
- if (!CanWrite) throw Error.GetWriteNotSupported();
-
- // To avoid a race condition with a stream's position pointer & generating conditions
- // with internal buffer indexes in our own streams that
- // don't natively support async IO operations when there are multiple
- // async requests outstanding, we will block the application's main
- // thread if it does a second IO request until the first one completes.
- SemaphoreSlim semaphore = EnsureAsyncActiveSemaphoreInitialized();
- Task? semaphoreTask = null;
- if (serializeAsynchronously)
- {
- semaphoreTask = semaphore.WaitAsync(); // kick off the asynchronous wait, but don't block
- }
- else
- {
- semaphore.Wait(); // synchronously wait here
- }
-
- // Create the task to asynchronously do a Write. This task serves both
- // as the asynchronous work item and as the IAsyncResult returned to the user.
- var asyncResult = new ReadWriteTask(false /*isRead*/, apm, delegate
- {
- // The ReadWriteTask stores all of the parameters to pass to Write.
- // As we're currently inside of it, we can get the current task
- // and grab the parameters from it.
- var thisTask = Task.InternalCurrent as ReadWriteTask;
- Debug.Assert(thisTask != null && thisTask._stream != null && thisTask._buffer != null,
- "Inside ReadWriteTask, InternalCurrent should be the ReadWriteTask, and stream and buffer should be set");
-
- try
- {
- // Do the Write
- thisTask._stream.Write(thisTask._buffer, thisTask._offset, thisTask._count);
- return 0; // not used, but signature requires a value be returned
- }
- finally
- {
- // If this implementation is part of Begin/EndXx, then the EndXx method will handle
- // finishing the async operation. However, if this is part of XxAsync, then there won't
- // be an end method, and this task is responsible for cleaning up.
- if (!thisTask._apm)
- {
- thisTask._stream.FinishTrackingAsyncOperation();
- }
-
- thisTask.ClearBeginState(); // just to help alleviate some memory pressure
- }
- }, state, this, buffer, offset, count, callback);
-
- // Schedule it
- if (semaphoreTask != null)
- RunReadWriteTaskWhenReady(semaphoreTask, asyncResult);
- else
- RunReadWriteTask(asyncResult);
-
- return asyncResult; // return it
- }
-
- private void RunReadWriteTaskWhenReady(Task asyncWaiter, ReadWriteTask readWriteTask)
- {
- Debug.Assert(readWriteTask != null);
- Debug.Assert(asyncWaiter != null);
-
- // If the wait has already completed, run the task.
- if (asyncWaiter.IsCompleted)
- {
- Debug.Assert(asyncWaiter.IsCompletedSuccessfully, "The semaphore wait should always complete successfully.");
- RunReadWriteTask(readWriteTask);
- }
- else // Otherwise, wait for our turn, and then run the task.
- {
- asyncWaiter.ContinueWith((t, state) =>
- {
- Debug.Assert(t.IsCompletedSuccessfully, "The semaphore wait should always complete successfully.");
- var rwt = (ReadWriteTask)state!;
- Debug.Assert(rwt._stream != null);
- rwt._stream.RunReadWriteTask(rwt); // RunReadWriteTask(readWriteTask);
- }, readWriteTask, default, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
- }
- }
-
- private void RunReadWriteTask(ReadWriteTask readWriteTask)
- {
- Debug.Assert(readWriteTask != null);
- Debug.Assert(_activeReadWriteTask == null, "Expected no other readers or writers");
-
- // Schedule the task. ScheduleAndStart must happen after the write to _activeReadWriteTask to avoid a race.
- // Internally, we're able to directly call ScheduleAndStart rather than Start, avoiding
- // two interlocked operations. However, if ReadWriteTask is ever changed to use
- // a cancellation token, this should be changed to use Start.
- _activeReadWriteTask = readWriteTask; // store the task so that EndXx can validate it's given the right one
- readWriteTask.m_taskScheduler = TaskScheduler.Default;
- readWriteTask.ScheduleAndStart(needsProtection: false);
- }
-
- private void FinishTrackingAsyncOperation()
- {
- _activeReadWriteTask = null;
- Debug.Assert(_asyncActiveSemaphore != null, "Must have been initialized in order to get here.");
- _asyncActiveSemaphore.Release();
- }
-
- public virtual void EndWrite(IAsyncResult asyncResult)
- {
- if (asyncResult == null)
- throw new ArgumentNullException(nameof(asyncResult));
-
- ReadWriteTask? writeTask = _activeReadWriteTask;
- if (writeTask == null)
- {
- throw new ArgumentException(SR.InvalidOperation_WrongAsyncResultOrEndWriteCalledMultiple);
- }
- else if (writeTask != asyncResult)
- {
- throw new InvalidOperationException(SR.InvalidOperation_WrongAsyncResultOrEndWriteCalledMultiple);
- }
- else if (writeTask._isRead)
- {
- throw new ArgumentException(SR.InvalidOperation_WrongAsyncResultOrEndWriteCalledMultiple);
- }
-
- try
- {
- writeTask.GetAwaiter().GetResult(); // block until completion, then propagate any exceptions
- Debug.Assert(writeTask.Status == TaskStatus.RanToCompletion);
- }
- finally
- {
- FinishTrackingAsyncOperation();
- }
- }
-
- // Task used by BeginRead / BeginWrite to do Read / Write asynchronously.
- // A single instance of this task serves four purposes:
- // 1. The work item scheduled to run the Read / Write operation
- // 2. The state holding the arguments to be passed to Read / Write
- // 3. The IAsyncResult returned from BeginRead / BeginWrite
- // 4. The completion action that runs to invoke the user-provided callback.
- // This last item is a bit tricky. Before the AsyncCallback is invoked, the
- // IAsyncResult must have completed, so we can't just invoke the handler
- // from within the task, since it is the IAsyncResult, and thus it's not
- // yet completed. Instead, we use AddCompletionAction to install this
- // task as its own completion handler. That saves the need to allocate
- // a separate completion handler, it guarantees that the task will
- // have completed by the time the handler is invoked, and it allows
- // the handler to be invoked synchronously upon the completion of the
- // task. This all enables BeginRead / BeginWrite to be implemented
- // with a single allocation.
- private sealed class ReadWriteTask : Task<int>, ITaskCompletionAction
- {
- internal readonly bool _isRead;
- internal readonly bool _apm; // true if this is from Begin/EndXx; false if it's from XxAsync
- internal Stream? _stream;
- internal byte[]? _buffer;
- internal readonly int _offset;
- internal readonly int _count;
- private AsyncCallback? _callback;
- private ExecutionContext? _context;
-
- internal void ClearBeginState() // Used to allow the args to Read/Write to be made available for GC
- {
- _stream = null;
- _buffer = null;
- }
-
- public ReadWriteTask(
- bool isRead,
- bool apm,
- Func<object?, int> function, object? state,
- Stream stream, byte[] buffer, int offset, int count, AsyncCallback? callback) :
- base(function, state, CancellationToken.None, TaskCreationOptions.DenyChildAttach)
- {
- Debug.Assert(function != null);
- Debug.Assert(stream != null);
- Debug.Assert(buffer != null);
-
- // Store the arguments
- _isRead = isRead;
- _apm = apm;
- _stream = stream;
- _buffer = buffer;
- _offset = offset;
- _count = count;
-
- // If a callback was provided, we need to:
- // - Store the user-provided handler
- // - Capture an ExecutionContext under which to invoke the handler
- // - Add this task as its own completion handler so that the Invoke method
- // will run the callback when this task completes.
- if (callback != null)
- {
- _callback = callback;
- _context = ExecutionContext.Capture();
- base.AddCompletionAction(this);
- }
- }
-
- private static void InvokeAsyncCallback(object? completedTask)
- {
- Debug.Assert(completedTask is ReadWriteTask);
- var rwc = (ReadWriteTask)completedTask;
- AsyncCallback? callback = rwc._callback;
- Debug.Assert(callback != null);
- rwc._callback = null;
- callback(rwc);
- }
-
- private static ContextCallback? s_invokeAsyncCallback;
-
- void ITaskCompletionAction.Invoke(Task completingTask)
- {
- // Get the ExecutionContext. If there is none, just run the callback
- // directly, passing in the completed task as the IAsyncResult.
- // If there is one, process it with ExecutionContext.Run.
- ExecutionContext? context = _context;
- if (context == null)
- {
- AsyncCallback? callback = _callback;
- Debug.Assert(callback != null);
- _callback = null;
- callback(completingTask);
- }
- else
- {
- _context = null;
-
- ContextCallback? invokeAsyncCallback = s_invokeAsyncCallback ??= InvokeAsyncCallback;
-
- ExecutionContext.RunInternal(context, invokeAsyncCallback, this);
- }
- }
-
- bool ITaskCompletionAction.InvokeMayRunArbitraryCode => true;
- }
-
- public Task WriteAsync(byte[] buffer, int offset, int count)
- {
- return WriteAsync(buffer, offset, count, CancellationToken.None);
- }
-
- public virtual Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
- {
- // If cancellation was requested, bail early with an already completed task.
- // Otherwise, return a task that represents the Begin/End methods.
- return cancellationToken.IsCancellationRequested
- ? Task.FromCanceled(cancellationToken)
- : BeginEndWriteAsync(buffer, offset, count);
- }
-
- public virtual ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
- {
- if (MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> array))
- {
- return new ValueTask(WriteAsync(array.Array!, array.Offset, array.Count, cancellationToken));
- }
- else
- {
- byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
- buffer.Span.CopyTo(sharedBuffer);
- return new ValueTask(FinishWriteAsync(WriteAsync(sharedBuffer, 0, buffer.Length, cancellationToken), sharedBuffer));
- }
- }
-
- private async Task FinishWriteAsync(Task writeTask, byte[] localBuffer)
- {
- try
- {
- await writeTask.ConfigureAwait(false);
- }
- finally
- {
- ArrayPool<byte>.Shared.Return(localBuffer);
- }
- }
-
- private Task BeginEndWriteAsync(byte[] buffer, int offset, int count)
- {
- if (!HasOverriddenBeginEndWrite())
- {
- // If the Stream does not override Begin/EndWrite, then we can take an optimized path
- // that skips an extra layer of tasks / IAsyncResults.
- return (Task)BeginWriteInternal(buffer, offset, count, null, null, serializeAsynchronously: true, apm: false);
- }
-
- // Otherwise, we need to wrap calls to Begin/EndWrite to ensure we use the derived type's functionality.
- return TaskFactory<VoidTaskResult>.FromAsyncTrim(
- this, new ReadWriteParameters { Buffer = buffer, Offset = offset, Count = count },
- (stream, args, callback, state) => stream.BeginWrite(args.Buffer, args.Offset, args.Count, callback, state), // cached by compiler
- (stream, asyncResult) => // cached by compiler
- {
- stream.EndWrite(asyncResult);
- return default;
- });
- }
-
- public abstract long Seek(long offset, SeekOrigin origin);
-
- public abstract void SetLength(long value);
-
- public abstract int Read(byte[] buffer, int offset, int count);
-
- public virtual int Read(Span<byte> buffer)
- {
- byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
- try
- {
- int numRead = Read(sharedBuffer, 0, buffer.Length);
- if ((uint)numRead > (uint)buffer.Length)
- {
- throw new IOException(SR.IO_StreamTooLong);
- }
- new Span<byte>(sharedBuffer, 0, numRead).CopyTo(buffer);
- return numRead;
- }
- finally { ArrayPool<byte>.Shared.Return(sharedBuffer); }
- }
-
- // Reads one byte from the stream by calling Read(byte[], int, int).
- // Will return an unsigned byte cast to an int or -1 on end of stream.
- // This implementation does not perform well because it allocates a new
- // byte[] each time you call it, and should be overridden by any
- // subclass that maintains an internal buffer. Then, it can help perf
- // significantly for people who are reading one byte at a time.
- public virtual int ReadByte()
- {
- byte[] oneByteArray = new byte[1];
- int r = Read(oneByteArray, 0, 1);
- if (r == 0)
- return -1;
- return oneByteArray[0];
- }
-
- public abstract void Write(byte[] buffer, int offset, int count);
-
- public virtual void Write(ReadOnlySpan<byte> buffer)
- {
- byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
- try
- {
- buffer.CopyTo(sharedBuffer);
- Write(sharedBuffer, 0, buffer.Length);
- }
- finally { ArrayPool<byte>.Shared.Return(sharedBuffer); }
- }
-
- // Writes one byte from the stream by calling Write(byte[], int, int).
- // This implementation does not perform well because it allocates a new
- // byte[] each time you call it, and should be overridden by any
- // subclass that maintains an internal buffer. Then, it can help perf
- // significantly for people who are writing one byte at a time.
- public virtual void WriteByte(byte value)
- {
- byte[] oneByteArray = new byte[1];
- oneByteArray[0] = value;
- Write(oneByteArray, 0, 1);
- }
-
- public static Stream Synchronized(Stream stream)
- {
- if (stream == null)
- throw new ArgumentNullException(nameof(stream));
- if (stream is SyncStream)
- return stream;
-
- return new SyncStream(stream);
- }
-
- [Obsolete("Do not call or override this method.")]
- protected virtual void ObjectInvariant()
- {
- }
-
- internal IAsyncResult BlockingBeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object? state)
- {
- // To avoid a race with a stream's position pointer & generating conditions
- // with internal buffer indexes in our own streams that
- // don't natively support async IO operations when there are multiple
- // async requests outstanding, we will block the application's main
- // thread and do the IO synchronously.
- // This can't perform well - use a different approach.
- SynchronousAsyncResult asyncResult;
- try
- {
- int numRead = Read(buffer, offset, count);
- asyncResult = new SynchronousAsyncResult(numRead, state);
- }
- catch (IOException ex)
- {
- asyncResult = new SynchronousAsyncResult(ex, state, isWrite: false);
- }
-
- callback?.Invoke(asyncResult);
-
- return asyncResult;
- }
-
- internal static int BlockingEndRead(IAsyncResult asyncResult)
- {
- return SynchronousAsyncResult.EndRead(asyncResult);
- }
-
- internal IAsyncResult BlockingBeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object? state)
- {
- // To avoid a race condition with a stream's position pointer & generating conditions
- // with internal buffer indexes in our own streams that
- // don't natively support async IO operations when there are multiple
- // async requests outstanding, we will block the application's main
- // thread and do the IO synchronously.
- // This can't perform well - use a different approach.
- SynchronousAsyncResult asyncResult;
- try
- {
- Write(buffer, offset, count);
- asyncResult = new SynchronousAsyncResult(state);
- }
- catch (IOException ex)
- {
- asyncResult = new SynchronousAsyncResult(ex, state, isWrite: true);
- }
-
- callback?.Invoke(asyncResult);
-
- return asyncResult;
- }
-
- internal static void BlockingEndWrite(IAsyncResult asyncResult)
- {
- SynchronousAsyncResult.EndWrite(asyncResult);
- }
-
- private sealed class NullStream : Stream
- {
- private static readonly Task<int> s_zeroTask = Task.FromResult(0);
-
- internal NullStream() { }
-
- public override bool CanRead => true;
-
- public override bool CanWrite => true;
-
- public override bool CanSeek => true;
-
- public override long Length => 0;
-
- public override long Position
- {
- get => 0;
- set { }
- }
-
- public override void CopyTo(Stream destination, int bufferSize)
- {
- StreamHelpers.ValidateCopyToArgs(this, destination, bufferSize);
-
- // After we validate arguments this is a nop.
- }
-
- public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
- {
- // Validate arguments here for compat, since previously this method
- // was inherited from Stream (which did check its arguments).
- StreamHelpers.ValidateCopyToArgs(this, destination, bufferSize);
-
- return cancellationToken.IsCancellationRequested ?
- Task.FromCanceled(cancellationToken) :
- Task.CompletedTask;
- }
-
- public override void CopyTo(ReadOnlySpanAction<byte, object?> callback, object? state, int bufferSize)
- {
- StreamHelpers.ValidateCopyToArgs(this, callback, bufferSize);
-
- // After we validate arguments this is a nop.
- }
-
- public override Task CopyToAsync(Func<ReadOnlyMemory<byte>, object?, CancellationToken, ValueTask> callback, object? state, int bufferSize, CancellationToken cancellationToken)
- {
- StreamHelpers.ValidateCopyToArgs(this, callback, bufferSize);
-
- return cancellationToken.IsCancellationRequested ?
- Task.FromCanceled(cancellationToken) :
- Task.CompletedTask;
- }
-
- protected override void Dispose(bool disposing)
- {
- // Do nothing - we don't want NullStream singleton (static) to be closable
- }
-
- public override void Flush()
- {
- }
-
- public override Task FlushAsync(CancellationToken cancellationToken)
- {
- return cancellationToken.IsCancellationRequested ?
- Task.FromCanceled(cancellationToken) :
- Task.CompletedTask;
- }
-
- public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object? state)
- {
- if (!CanRead) throw Error.GetReadNotSupported();
-
- return BlockingBeginRead(buffer, offset, count, callback, state);
- }
-
- public override int EndRead(IAsyncResult asyncResult)
- {
- if (asyncResult == null)
- throw new ArgumentNullException(nameof(asyncResult));
-
- return BlockingEndRead(asyncResult);
- }
-
- public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object? state)
- {
- if (!CanWrite) throw Error.GetWriteNotSupported();
-
- return BlockingBeginWrite(buffer, offset, count, callback, state);
- }
-
- public override void EndWrite(IAsyncResult asyncResult)
- {
- if (asyncResult == null)
- throw new ArgumentNullException(nameof(asyncResult));
-
- BlockingEndWrite(asyncResult);
- }
-
- public override int Read(byte[] buffer, int offset, int count)
- {
- return 0;
- }
-
- public override int Read(Span<byte> buffer)
- {
- return 0;
- }
-
- public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
- {
- return s_zeroTask;
- }
-
- public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
- {
- return new ValueTask<int>(0);
- }
-
- public override int ReadByte()
- {
- return -1;
- }
-
- public override void Write(byte[] buffer, int offset, int count)
- {
- }
-
- public override void Write(ReadOnlySpan<byte> buffer)
- {
- }
-
- public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
- {
- return cancellationToken.IsCancellationRequested ?
- Task.FromCanceled(cancellationToken) :
- Task.CompletedTask;
- }
-
- public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
- {
- return cancellationToken.IsCancellationRequested ?
- new ValueTask(Task.FromCanceled(cancellationToken)) :
- default;
- }
-
- public override void WriteByte(byte value)
- {
- }
-
- public override long Seek(long offset, SeekOrigin origin)
- {
- return 0;
- }
-
- public override void SetLength(long length)
- {
- }
- }
-
-
- /// <summary>Used as the IAsyncResult object when using asynchronous IO methods on the base Stream class.</summary>
- private sealed class SynchronousAsyncResult : IAsyncResult
- {
- private readonly object? _stateObject;
- private readonly bool _isWrite;
- private ManualResetEvent? _waitHandle;
- private readonly ExceptionDispatchInfo? _exceptionInfo;
-
- private bool _endXxxCalled;
- private readonly int _bytesRead;
-
- internal SynchronousAsyncResult(int bytesRead, object? asyncStateObject)
- {
- _bytesRead = bytesRead;
- _stateObject = asyncStateObject;
- }
-
- internal SynchronousAsyncResult(object? asyncStateObject)
- {
- _stateObject = asyncStateObject;
- _isWrite = true;
- }
-
- internal SynchronousAsyncResult(Exception ex, object? asyncStateObject, bool isWrite)
- {
- _exceptionInfo = ExceptionDispatchInfo.Capture(ex);
- _stateObject = asyncStateObject;
- _isWrite = isWrite;
- }
-
- public bool IsCompleted => true;
-
- public WaitHandle AsyncWaitHandle =>
- LazyInitializer.EnsureInitialized(ref _waitHandle, () => new ManualResetEvent(true));
-
- public object? AsyncState => _stateObject;
-
- public bool CompletedSynchronously => true;
-
- internal void ThrowIfError()
- {
- if (_exceptionInfo != null)
- _exceptionInfo.Throw();
- }
-
- internal static int EndRead(IAsyncResult asyncResult)
- {
- if (!(asyncResult is SynchronousAsyncResult ar) || ar._isWrite)
- throw new ArgumentException(SR.Arg_WrongAsyncResult);
-
- if (ar._endXxxCalled)
- throw new ArgumentException(SR.InvalidOperation_EndReadCalledMultiple);
-
- ar._endXxxCalled = true;
-
- ar.ThrowIfError();
- return ar._bytesRead;
- }
-
- internal static void EndWrite(IAsyncResult asyncResult)
- {
- if (!(asyncResult is SynchronousAsyncResult ar) || !ar._isWrite)
- throw new ArgumentException(SR.Arg_WrongAsyncResult);
-
- if (ar._endXxxCalled)
- throw new ArgumentException(SR.InvalidOperation_EndWriteCalledMultiple);
-
- ar._endXxxCalled = true;
-
- ar.ThrowIfError();
- }
- } // class SynchronousAsyncResult
-
-
- // SyncStream is a wrapper around a stream that takes
- // a lock for every operation making it thread safe.
- private sealed class SyncStream : Stream, IDisposable
- {
- private readonly Stream _stream;
-
- internal SyncStream(Stream stream)
- {
- if (stream == null)
- throw new ArgumentNullException(nameof(stream));
- _stream = stream;
- }
-
- public override bool CanRead => _stream.CanRead;
-
- public override bool CanWrite => _stream.CanWrite;
-
- public override bool CanSeek => _stream.CanSeek;
-
- public override bool CanTimeout => _stream.CanTimeout;
-
- public override long Length
- {
- get
- {
- lock (_stream)
- {
- return _stream.Length;
- }
- }
- }
-
- public override long Position
- {
- get
- {
- lock (_stream)
- {
- return _stream.Position;
- }
- }
- set
- {
- lock (_stream)
- {
- _stream.Position = value;
- }
- }
- }
-
- public override int ReadTimeout
- {
- get => _stream.ReadTimeout;
- set => _stream.ReadTimeout = value;
- }
-
- public override int WriteTimeout
- {
- get => _stream.WriteTimeout;
- set => _stream.WriteTimeout = value;
- }
-
- // In the off chance that some wrapped stream has different
- // semantics for Close vs. Dispose, let's preserve that.
- public override void Close()
- {
- lock (_stream)
- {
- try
- {
- _stream.Close();
- }
- finally
- {
- base.Dispose(true);
- }
- }
- }
-
- protected override void Dispose(bool disposing)
- {
- lock (_stream)
- {
- try
- {
- // Explicitly pick up a potentially methodimpl'ed Dispose
- if (disposing)
- ((IDisposable)_stream).Dispose();
- }
- finally
- {
- base.Dispose(disposing);
- }
- }
- }
-
- public override ValueTask DisposeAsync()
- {
- lock (_stream)
- return _stream.DisposeAsync();
- }
-
- public override void Flush()
- {
- lock (_stream)
- _stream.Flush();
- }
-
- public override int Read(byte[] bytes, int offset, int count)
- {
- lock (_stream)
- return _stream.Read(bytes, offset, count);
- }
-
- public override int Read(Span<byte> buffer)
- {
- lock (_stream)
- return _stream.Read(buffer);
- }
-
- public override int ReadByte()
- {
- lock (_stream)
- return _stream.ReadByte();
- }
-
- public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object? state)
- {
-#if CORERT
- throw new NotImplementedException(); // TODO: https://github.com/dotnet/corert/issues/3251
-#else
- bool overridesBeginRead = _stream.HasOverriddenBeginEndRead();
-
- lock (_stream)
- {
- // If the Stream does have its own BeginRead implementation, then we must use that override.
- // If it doesn't, then we'll use the base implementation, but we'll make sure that the logic
- // which ensures only one asynchronous operation does so with an asynchronous wait rather
- // than a synchronous wait. A synchronous wait will result in a deadlock condition, because
- // the EndXx method for the outstanding async operation won't be able to acquire the lock on
- // _stream due to this call blocked while holding the lock.
- return overridesBeginRead ?
- _stream.BeginRead(buffer, offset, count, callback, state) :
- _stream.BeginReadInternal(buffer, offset, count, callback, state, serializeAsynchronously: true, apm: true);
- }
-#endif
- }
-
- public override int EndRead(IAsyncResult asyncResult)
- {
- if (asyncResult == null)
- throw new ArgumentNullException(nameof(asyncResult));
-
- lock (_stream)
- return _stream.EndRead(asyncResult);
- }
-
- public override long Seek(long offset, SeekOrigin origin)
- {
- lock (_stream)
- return _stream.Seek(offset, origin);
- }
-
- public override void SetLength(long length)
- {
- lock (_stream)
- _stream.SetLength(length);
- }
-
- public override void Write(byte[] bytes, int offset, int count)
- {
- lock (_stream)
- _stream.Write(bytes, offset, count);
- }
-
- public override void Write(ReadOnlySpan<byte> buffer)
- {
- lock (_stream)
- _stream.Write(buffer);
- }
-
- public override void WriteByte(byte b)
- {
- lock (_stream)
- _stream.WriteByte(b);
- }
-
- public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object? state)
- {
-#if CORERT
- throw new NotImplementedException(); // TODO: https://github.com/dotnet/corert/issues/3251
-#else
- bool overridesBeginWrite = _stream.HasOverriddenBeginEndWrite();
-
- lock (_stream)
- {
- // If the Stream does have its own BeginWrite implementation, then we must use that override.
- // If it doesn't, then we'll use the base implementation, but we'll make sure that the logic
- // which ensures only one asynchronous operation does so with an asynchronous wait rather
- // than a synchronous wait. A synchronous wait will result in a deadlock condition, because
- // the EndXx method for the outstanding async operation won't be able to acquire the lock on
- // _stream due to this call blocked while holding the lock.
- return overridesBeginWrite ?
- _stream.BeginWrite(buffer, offset, count, callback, state) :
- _stream.BeginWriteInternal(buffer, offset, count, callback, state, serializeAsynchronously: true, apm: true);
- }
-#endif
- }
-
- public override void EndWrite(IAsyncResult asyncResult)
- {
- if (asyncResult == null)
- throw new ArgumentNullException(nameof(asyncResult));
-
- lock (_stream)
- _stream.EndWrite(asyncResult);
- }
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/StreamHelpers.CopyValidation.cs b/netcore/System.Private.CoreLib/shared/System/IO/StreamHelpers.CopyValidation.cs
deleted file mode 100644
index 1d120e5a077..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/StreamHelpers.CopyValidation.cs
+++ /dev/null
@@ -1,70 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Buffers;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace System.IO
-{
- /// <summary>Provides methods to help in the implementation of Stream-derived types.</summary>
- internal static partial class StreamHelpers
- {
- /// <summary>Validate the arguments to CopyTo, as would Stream.CopyTo.</summary>
- public static void ValidateCopyToArgs(Stream source, Stream destination, int bufferSize)
- {
- if (destination == null)
- {
- throw new ArgumentNullException(nameof(destination));
- }
-
- if (bufferSize <= 0)
- {
- throw new ArgumentOutOfRangeException(nameof(bufferSize), bufferSize, SR.ArgumentOutOfRange_NeedPosNum);
- }
-
- bool sourceCanRead = source.CanRead;
- if (!sourceCanRead && !source.CanWrite)
- {
- throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed);
- }
-
- bool destinationCanWrite = destination.CanWrite;
- if (!destinationCanWrite && !destination.CanRead)
- {
- throw new ObjectDisposedException(nameof(destination), SR.ObjectDisposed_StreamClosed);
- }
-
- if (!sourceCanRead)
- {
- throw new NotSupportedException(SR.NotSupported_UnreadableStream);
- }
-
- if (!destinationCanWrite)
- {
- throw new NotSupportedException(SR.NotSupported_UnwritableStream);
- }
- }
-
- public static void ValidateCopyToArgs(Stream source, Delegate callback, int bufferSize)
- {
- if (callback == null)
- {
- throw new ArgumentNullException(nameof(callback));
- }
-
- if (bufferSize <= 0)
- {
- throw new ArgumentOutOfRangeException(nameof(bufferSize), bufferSize, SR.ArgumentOutOfRange_NeedPosNum);
- }
-
- if (!source.CanRead)
- {
- throw source.CanWrite ? (Exception)
- new NotSupportedException(SR.NotSupported_UnreadableStream) :
- new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed);
- }
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/StreamReader.cs b/netcore/System.Private.CoreLib/shared/System/IO/StreamReader.cs
deleted file mode 100644
index 5ce2d117a99..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/StreamReader.cs
+++ /dev/null
@@ -1,1347 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace System.IO
-{
- // This class implements a TextReader for reading characters to a Stream.
- // This is designed for character input in a particular Encoding,
- // whereas the Stream class is designed for byte input and output.
- public class StreamReader : TextReader
- {
- // StreamReader.Null is threadsafe.
- public static new readonly StreamReader Null = new NullStreamReader();
-
- // Using a 1K byte buffer and a 4K FileStream buffer works out pretty well
- // perf-wise. On even a 40 MB text file, any perf loss by using a 4K
- // buffer is negated by the win of allocating a smaller byte[], which
- // saves construction time. This does break adaptive buffering,
- // but this is slightly faster.
- private const int DefaultBufferSize = 1024; // Byte buffer size
- private const int DefaultFileStreamBufferSize = 4096;
- private const int MinBufferSize = 128;
-
- private readonly Stream _stream;
- private Encoding _encoding = null!; // only null in NullStreamReader where this is never used
- private Decoder _decoder = null!; // only null in NullStreamReader where this is never used
- private readonly byte[] _byteBuffer = null!; // only null in NullStreamReader where this is never used
- private char[] _charBuffer = null!; // only null in NullStreamReader where this is never used
- private int _charPos;
- private int _charLen;
- // Record the number of valid bytes in the byteBuffer, for a few checks.
- private int _byteLen;
- // This is used only for preamble detection
- private int _bytePos;
-
- // This is the maximum number of chars we can get from one call to
- // ReadBuffer. Used so ReadBuffer can tell when to copy data into
- // a user's char[] directly, instead of our internal char[].
- private int _maxCharsPerBuffer;
-
- /// <summary>True if the writer has been disposed; otherwise, false.</summary>
- private bool _disposed;
-
- // We will support looking for byte order marks in the stream and trying
- // to decide what the encoding might be from the byte order marks, IF they
- // exist. But that's all we'll do.
- private bool _detectEncoding;
-
- // Whether we must still check for the encoding's given preamble at the
- // beginning of this file.
- private bool _checkPreamble;
-
- // Whether the stream is most likely not going to give us back as much
- // data as we want the next time we call it. We must do the computation
- // before we do any byte order mark handling and save the result. Note
- // that we need this to allow users to handle streams used for an
- // interactive protocol, where they block waiting for the remote end
- // to send a response, like logging in on a Unix machine.
- private bool _isBlocked;
-
- // The intent of this field is to leave open the underlying stream when
- // disposing of this StreamReader. A name like _leaveOpen is better,
- // but this type is serializable, and this field's name was _closable.
- private readonly bool _closable; // Whether to close the underlying stream.
-
- // We don't guarantee thread safety on StreamReader, but we should at
- // least prevent users from trying to read anything while an Async
- // read from the same thread is in progress.
- private Task _asyncReadTask = Task.CompletedTask;
-
- private void CheckAsyncTaskInProgress()
- {
- // We are not locking the access to _asyncReadTask because this is not meant to guarantee thread safety.
- // We are simply trying to deter calling any Read APIs while an async Read from the same thread is in progress.
- if (!_asyncReadTask.IsCompleted)
- {
- ThrowAsyncIOInProgress();
- }
- }
-
- [DoesNotReturn]
- private static void ThrowAsyncIOInProgress() =>
- throw new InvalidOperationException(SR.InvalidOperation_AsyncIOInProgress);
-
- // StreamReader by default will ignore illegal UTF8 characters. We don't want to
- // throw here because we want to be able to read ill-formed data without choking.
- // The high level goal is to be tolerant of encoding errors when we read and very strict
- // when we write. Hence, default StreamWriter encoding will throw on error.
-
- private StreamReader()
- {
- Debug.Assert(this is NullStreamReader);
- _stream = Stream.Null;
- _closable = true;
- }
-
- public StreamReader(Stream stream)
- : this(stream, true)
- {
- }
-
- public StreamReader(Stream stream, bool detectEncodingFromByteOrderMarks)
- : this(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks, DefaultBufferSize, false)
- {
- }
-
- public StreamReader(Stream stream, Encoding encoding)
- : this(stream, encoding, true, DefaultBufferSize, false)
- {
- }
-
- public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks)
- : this(stream, encoding, detectEncodingFromByteOrderMarks, DefaultBufferSize, false)
- {
- }
-
- // Creates a new StreamReader for the given stream. The
- // character encoding is set by encoding and the buffer size,
- // in number of 16-bit characters, is set by bufferSize.
- //
- // Note that detectEncodingFromByteOrderMarks is a very
- // loose attempt at detecting the encoding by looking at the first
- // 3 bytes of the stream. It will recognize UTF-8, little endian
- // unicode, and big endian unicode text, but that's it. If neither
- // of those three match, it will use the Encoding you provided.
- //
- public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize)
- : this(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize, false)
- {
- }
-
- public StreamReader(Stream stream, Encoding? encoding = null, bool detectEncodingFromByteOrderMarks = true, int bufferSize = -1, bool leaveOpen = false)
- {
- if (stream == null)
- {
- throw new ArgumentNullException(nameof(stream));
- }
- if (encoding == null)
- {
- encoding = Encoding.UTF8;
- }
- if (!stream.CanRead)
- {
- throw new ArgumentException(SR.Argument_StreamNotReadable);
- }
- if (bufferSize == -1)
- {
- bufferSize = DefaultBufferSize;
- }
- else if (bufferSize <= 0)
- {
- throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);
- }
-
- _stream = stream;
- _encoding = encoding;
- _decoder = encoding.GetDecoder();
- if (bufferSize < MinBufferSize)
- {
- bufferSize = MinBufferSize;
- }
-
- _byteBuffer = new byte[bufferSize];
- _maxCharsPerBuffer = encoding.GetMaxCharCount(bufferSize);
- _charBuffer = new char[_maxCharsPerBuffer];
- _byteLen = 0;
- _bytePos = 0;
- _detectEncoding = detectEncodingFromByteOrderMarks;
- _checkPreamble = encoding.Preamble.Length > 0;
- _isBlocked = false;
- _closable = !leaveOpen;
- }
-
- public StreamReader(string path)
- : this(path, true)
- {
- }
-
- public StreamReader(string path, bool detectEncodingFromByteOrderMarks)
- : this(path, Encoding.UTF8, detectEncodingFromByteOrderMarks, DefaultBufferSize)
- {
- }
-
- public StreamReader(string path, Encoding encoding)
- : this(path, encoding, true, DefaultBufferSize)
- {
- }
-
- public StreamReader(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks)
- : this(path, encoding, detectEncodingFromByteOrderMarks, DefaultBufferSize)
- {
- }
-
- public StreamReader(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize) :
- this(ValidateArgsAndOpenPath(path, encoding, bufferSize), encoding, detectEncodingFromByteOrderMarks, bufferSize, leaveOpen: false)
- {
- }
-
- private static Stream ValidateArgsAndOpenPath(string path, Encoding encoding, int bufferSize)
- {
- if (path == null)
- throw new ArgumentNullException(nameof(path));
- if (encoding == null)
- throw new ArgumentNullException(nameof(encoding));
- if (path.Length == 0)
- throw new ArgumentException(SR.Argument_EmptyPath);
- if (bufferSize <= 0)
- throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);
-
- return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultFileStreamBufferSize, FileOptions.SequentialScan);
- }
-
- public override void Close()
- {
- Dispose(true);
- }
-
- protected override void Dispose(bool disposing)
- {
- if (_disposed)
- {
- return;
- }
- _disposed = true;
-
- // Dispose of our resources if this StreamReader is closable.
- if (_closable)
- {
- try
- {
- // Note that Stream.Close() can potentially throw here. So we need to
- // ensure cleaning up internal resources, inside the finally block.
- if (disposing)
- {
- _stream.Close();
- }
- }
- finally
- {
- _charPos = 0;
- _charLen = 0;
- base.Dispose(disposing);
- }
- }
- }
-
- public virtual Encoding CurrentEncoding => _encoding;
-
- public virtual Stream BaseStream => _stream;
-
- // DiscardBufferedData tells StreamReader to throw away its internal
- // buffer contents. This is useful if the user needs to seek on the
- // underlying stream to a known location then wants the StreamReader
- // to start reading from this new point. This method should be called
- // very sparingly, if ever, since it can lead to very poor performance.
- // However, it may be the only way of handling some scenarios where
- // users need to re-read the contents of a StreamReader a second time.
- public void DiscardBufferedData()
- {
- CheckAsyncTaskInProgress();
-
- _byteLen = 0;
- _charLen = 0;
- _charPos = 0;
- // in general we'd like to have an invariant that encoding isn't null. However,
- // for startup improvements for NullStreamReader, we want to delay load encoding.
- if (_encoding != null)
- {
- _decoder = _encoding.GetDecoder();
- }
- _isBlocked = false;
- }
-
- public bool EndOfStream
- {
- get
- {
- ThrowIfDisposed();
- CheckAsyncTaskInProgress();
-
- if (_charPos < _charLen)
- {
- return false;
- }
-
- // This may block on pipes!
- int numRead = ReadBuffer();
- return numRead == 0;
- }
- }
-
- public override int Peek()
- {
- ThrowIfDisposed();
- CheckAsyncTaskInProgress();
-
- if (_charPos == _charLen)
- {
- if (_isBlocked || ReadBuffer() == 0)
- {
- return -1;
- }
- }
- return _charBuffer[_charPos];
- }
-
- public override int Read()
- {
- ThrowIfDisposed();
- CheckAsyncTaskInProgress();
-
- if (_charPos == _charLen)
- {
- if (ReadBuffer() == 0)
- {
- return -1;
- }
- }
- int result = _charBuffer[_charPos];
- _charPos++;
- return result;
- }
-
- public override int Read(char[] buffer, int index, int count)
- {
- if (buffer == null)
- {
- throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
- }
- if (index < 0 || count < 0)
- {
- throw new ArgumentOutOfRangeException(index < 0 ? nameof(index) : nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (buffer.Length - index < count)
- {
- throw new ArgumentException(SR.Argument_InvalidOffLen);
- }
-
- return ReadSpan(new Span<char>(buffer, index, count));
- }
-
- public override int Read(Span<char> buffer) =>
- GetType() == typeof(StreamReader) ? ReadSpan(buffer) :
- base.Read(buffer); // Defer to Read(char[], ...) if a derived type may have previously overridden it
-
- private int ReadSpan(Span<char> buffer)
- {
- ThrowIfDisposed();
- CheckAsyncTaskInProgress();
-
- int charsRead = 0;
- // As a perf optimization, if we had exactly one buffer's worth of
- // data read in, let's try writing directly to the user's buffer.
- bool readToUserBuffer = false;
- int count = buffer.Length;
- while (count > 0)
- {
- int n = _charLen - _charPos;
- if (n == 0)
- {
- n = ReadBuffer(buffer.Slice(charsRead), out readToUserBuffer);
- }
- if (n == 0)
- {
- break; // We're at EOF
- }
- if (n > count)
- {
- n = count;
- }
- if (!readToUserBuffer)
- {
- new Span<char>(_charBuffer, _charPos, n).CopyTo(buffer.Slice(charsRead));
- _charPos += n;
- }
-
- charsRead += n;
- count -= n;
- // This function shouldn't block for an indefinite amount of time,
- // or reading from a network stream won't work right. If we got
- // fewer bytes than we requested, then we want to break right here.
- if (_isBlocked)
- {
- break;
- }
- }
-
- return charsRead;
- }
-
- public override string ReadToEnd()
- {
- ThrowIfDisposed();
- CheckAsyncTaskInProgress();
-
- // Call ReadBuffer, then pull data out of charBuffer.
- StringBuilder sb = new StringBuilder(_charLen - _charPos);
- do
- {
- sb.Append(_charBuffer, _charPos, _charLen - _charPos);
- _charPos = _charLen; // Note we consumed these characters
- ReadBuffer();
- } while (_charLen > 0);
- return sb.ToString();
- }
-
- public override int ReadBlock(char[] buffer, int index, int count)
- {
- if (buffer == null)
- {
- throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
- }
- if (index < 0 || count < 0)
- {
- throw new ArgumentOutOfRangeException(index < 0 ? nameof(index) : nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (buffer.Length - index < count)
- {
- throw new ArgumentException(SR.Argument_InvalidOffLen);
- }
- ThrowIfDisposed();
- CheckAsyncTaskInProgress();
-
- return base.ReadBlock(buffer, index, count);
- }
-
- public override int ReadBlock(Span<char> buffer)
- {
- if (GetType() != typeof(StreamReader))
- {
- // Defer to Read(char[], ...) if a derived type may have previously overridden it.
- return base.ReadBlock(buffer);
- }
-
- int i, n = 0;
- do
- {
- i = ReadSpan(buffer.Slice(n));
- n += i;
- } while (i > 0 && n < buffer.Length);
- return n;
- }
-
- // Trims n bytes from the front of the buffer.
- private void CompressBuffer(int n)
- {
- Debug.Assert(_byteLen >= n, "CompressBuffer was called with a number of bytes greater than the current buffer length. Are two threads using this StreamReader at the same time?");
- Buffer.BlockCopy(_byteBuffer, n, _byteBuffer, 0, _byteLen - n);
- _byteLen -= n;
- }
-
- private void DetectEncoding()
- {
- if (_byteLen < 2)
- {
- return;
- }
- _detectEncoding = false;
- bool changedEncoding = false;
- if (_byteBuffer[0] == 0xFE && _byteBuffer[1] == 0xFF)
- {
- // Big Endian Unicode
-
- _encoding = Encoding.BigEndianUnicode;
- CompressBuffer(2);
- changedEncoding = true;
- }
- else if (_byteBuffer[0] == 0xFF && _byteBuffer[1] == 0xFE)
- {
- // Little Endian Unicode, or possibly little endian UTF32
- if (_byteLen < 4 || _byteBuffer[2] != 0 || _byteBuffer[3] != 0)
- {
- _encoding = Encoding.Unicode;
- CompressBuffer(2);
- changedEncoding = true;
- }
- else
- {
- _encoding = Encoding.UTF32;
- CompressBuffer(4);
- changedEncoding = true;
- }
- }
- else if (_byteLen >= 3 && _byteBuffer[0] == 0xEF && _byteBuffer[1] == 0xBB && _byteBuffer[2] == 0xBF)
- {
- // UTF-8
- _encoding = Encoding.UTF8;
- CompressBuffer(3);
- changedEncoding = true;
- }
- else if (_byteLen >= 4 && _byteBuffer[0] == 0 && _byteBuffer[1] == 0 &&
- _byteBuffer[2] == 0xFE && _byteBuffer[3] == 0xFF)
- {
- // Big Endian UTF32
- _encoding = new UTF32Encoding(bigEndian: true, byteOrderMark: true);
- CompressBuffer(4);
- changedEncoding = true;
- }
- else if (_byteLen == 2)
- {
- _detectEncoding = true;
- }
- // Note: in the future, if we change this algorithm significantly,
- // we can support checking for the preamble of the given encoding.
-
- if (changedEncoding)
- {
- _decoder = _encoding.GetDecoder();
- int newMaxCharsPerBuffer = _encoding.GetMaxCharCount(_byteBuffer.Length);
- if (newMaxCharsPerBuffer > _maxCharsPerBuffer)
- {
- _charBuffer = new char[newMaxCharsPerBuffer];
- }
- _maxCharsPerBuffer = newMaxCharsPerBuffer;
- }
- }
-
- // Trims the preamble bytes from the byteBuffer. This routine can be called multiple times
- // and we will buffer the bytes read until the preamble is matched or we determine that
- // there is no match. If there is no match, every byte read previously will be available
- // for further consumption. If there is a match, we will compress the buffer for the
- // leading preamble bytes
- private bool IsPreamble()
- {
- if (!_checkPreamble)
- {
- return _checkPreamble;
- }
-
- ReadOnlySpan<byte> preamble = _encoding.Preamble;
-
- Debug.Assert(_bytePos <= preamble.Length, "_compressPreamble was called with the current bytePos greater than the preamble buffer length. Are two threads using this StreamReader at the same time?");
- int len = (_byteLen >= (preamble.Length)) ? (preamble.Length - _bytePos) : (_byteLen - _bytePos);
-
- for (int i = 0; i < len; i++, _bytePos++)
- {
- if (_byteBuffer[_bytePos] != preamble[_bytePos])
- {
- _bytePos = 0;
- _checkPreamble = false;
- break;
- }
- }
-
- Debug.Assert(_bytePos <= preamble.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?");
-
- if (_checkPreamble)
- {
- if (_bytePos == preamble.Length)
- {
- // We have a match
- CompressBuffer(preamble.Length);
- _bytePos = 0;
- _checkPreamble = false;
- _detectEncoding = false;
- }
- }
-
- return _checkPreamble;
- }
-
- internal virtual int ReadBuffer()
- {
- _charLen = 0;
- _charPos = 0;
-
- if (!_checkPreamble)
- {
- _byteLen = 0;
- }
-
- do
- {
- if (_checkPreamble)
- {
- Debug.Assert(_bytePos <= _encoding.Preamble.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?");
- int len = _stream.Read(_byteBuffer, _bytePos, _byteBuffer.Length - _bytePos);
- Debug.Assert(len >= 0, "Stream.Read returned a negative number! This is a bug in your stream class.");
-
- if (len == 0)
- {
- // EOF but we might have buffered bytes from previous
- // attempt to detect preamble that needs to be decoded now
- if (_byteLen > 0)
- {
- _charLen += _decoder.GetChars(_byteBuffer, 0, _byteLen, _charBuffer, _charLen);
- // Need to zero out the byteLen after we consume these bytes so that we don't keep infinitely hitting this code path
- _bytePos = _byteLen = 0;
- }
-
- return _charLen;
- }
-
- _byteLen += len;
- }
- else
- {
- Debug.Assert(_bytePos == 0, "bytePos can be non zero only when we are trying to _checkPreamble. Are two threads using this StreamReader at the same time?");
- _byteLen = _stream.Read(_byteBuffer, 0, _byteBuffer.Length);
- Debug.Assert(_byteLen >= 0, "Stream.Read returned a negative number! This is a bug in your stream class.");
-
- if (_byteLen == 0) // We're at EOF
- {
- return _charLen;
- }
- }
-
- // _isBlocked == whether we read fewer bytes than we asked for.
- // Note we must check it here because CompressBuffer or
- // DetectEncoding will change byteLen.
- _isBlocked = (_byteLen < _byteBuffer.Length);
-
- // Check for preamble before detect encoding. This is not to override the
- // user supplied Encoding for the one we implicitly detect. The user could
- // customize the encoding which we will loose, such as ThrowOnError on UTF8
- if (IsPreamble())
- {
- continue;
- }
-
- // If we're supposed to detect the encoding and haven't done so yet,
- // do it. Note this may need to be called more than once.
- if (_detectEncoding && _byteLen >= 2)
- {
- DetectEncoding();
- }
-
- _charLen += _decoder.GetChars(_byteBuffer, 0, _byteLen, _charBuffer, _charLen);
- } while (_charLen == 0);
- return _charLen;
- }
-
-
- // This version has a perf optimization to decode data DIRECTLY into the
- // user's buffer, bypassing StreamReader's own buffer.
- // This gives a > 20% perf improvement for our encodings across the board,
- // but only when asking for at least the number of characters that one
- // buffer's worth of bytes could produce.
- // This optimization, if run, will break SwitchEncoding, so we must not do
- // this on the first call to ReadBuffer.
- private int ReadBuffer(Span<char> userBuffer, out bool readToUserBuffer)
- {
- _charLen = 0;
- _charPos = 0;
-
- if (!_checkPreamble)
- {
- _byteLen = 0;
- }
-
- int charsRead = 0;
-
- // As a perf optimization, we can decode characters DIRECTLY into a
- // user's char[]. We absolutely must not write more characters
- // into the user's buffer than they asked for. Calculating
- // encoding.GetMaxCharCount(byteLen) each time is potentially very
- // expensive - instead, cache the number of chars a full buffer's
- // worth of data may produce. Yes, this makes the perf optimization
- // less aggressive, in that all reads that asked for fewer than AND
- // returned fewer than _maxCharsPerBuffer chars won't get the user
- // buffer optimization. This affects reads where the end of the
- // Stream comes in the middle somewhere, and when you ask for
- // fewer chars than your buffer could produce.
- readToUserBuffer = userBuffer.Length >= _maxCharsPerBuffer;
-
- do
- {
- Debug.Assert(charsRead == 0);
-
- if (_checkPreamble)
- {
- Debug.Assert(_bytePos <= _encoding.Preamble.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?");
- int len = _stream.Read(_byteBuffer, _bytePos, _byteBuffer.Length - _bytePos);
- Debug.Assert(len >= 0, "Stream.Read returned a negative number! This is a bug in your stream class.");
-
- if (len == 0)
- {
- // EOF but we might have buffered bytes from previous
- // attempt to detect preamble that needs to be decoded now
- if (_byteLen > 0)
- {
- if (readToUserBuffer)
- {
- charsRead = _decoder.GetChars(new ReadOnlySpan<byte>(_byteBuffer, 0, _byteLen), userBuffer.Slice(charsRead), flush: false);
- _charLen = 0; // StreamReader's buffer is empty.
- }
- else
- {
- charsRead = _decoder.GetChars(_byteBuffer, 0, _byteLen, _charBuffer, charsRead);
- _charLen += charsRead; // Number of chars in StreamReader's buffer.
- }
- }
-
- return charsRead;
- }
-
- _byteLen += len;
- }
- else
- {
- Debug.Assert(_bytePos == 0, "bytePos can be non zero only when we are trying to _checkPreamble. Are two threads using this StreamReader at the same time?");
-
- _byteLen = _stream.Read(_byteBuffer, 0, _byteBuffer.Length);
-
- Debug.Assert(_byteLen >= 0, "Stream.Read returned a negative number! This is a bug in your stream class.");
-
- if (_byteLen == 0) // EOF
- {
- break;
- }
- }
-
- // _isBlocked == whether we read fewer bytes than we asked for.
- // Note we must check it here because CompressBuffer or
- // DetectEncoding will change byteLen.
- _isBlocked = (_byteLen < _byteBuffer.Length);
-
- // Check for preamble before detect encoding. This is not to override the
- // user supplied Encoding for the one we implicitly detect. The user could
- // customize the encoding which we will loose, such as ThrowOnError on UTF8
- // Note: we don't need to recompute readToUserBuffer optimization as IsPreamble
- // doesn't change the encoding or affect _maxCharsPerBuffer
- if (IsPreamble())
- {
- continue;
- }
-
- // On the first call to ReadBuffer, if we're supposed to detect the encoding, do it.
- if (_detectEncoding && _byteLen >= 2)
- {
- DetectEncoding();
- // DetectEncoding changes some buffer state. Recompute this.
- readToUserBuffer = userBuffer.Length >= _maxCharsPerBuffer;
- }
-
- _charPos = 0;
- if (readToUserBuffer)
- {
- charsRead += _decoder.GetChars(new ReadOnlySpan<byte>(_byteBuffer, 0, _byteLen), userBuffer.Slice(charsRead), flush: false);
- _charLen = 0; // StreamReader's buffer is empty.
- }
- else
- {
- charsRead = _decoder.GetChars(_byteBuffer, 0, _byteLen, _charBuffer, charsRead);
- _charLen += charsRead; // Number of chars in StreamReader's buffer.
- }
- } while (charsRead == 0);
-
- _isBlocked &= charsRead < userBuffer.Length;
-
- return charsRead;
- }
-
-
- // Reads a line. 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. The resulting string does not
- // contain the terminating carriage return and/or line feed. The returned
- // value is null if the end of the input stream has been reached.
- //
- public override string? ReadLine()
- {
- ThrowIfDisposed();
- CheckAsyncTaskInProgress();
-
- if (_charPos == _charLen)
- {
- if (ReadBuffer() == 0)
- {
- return null;
- }
- }
-
- StringBuilder? sb = null;
- do
- {
- int i = _charPos;
- do
- {
- char ch = _charBuffer[i];
- // Note the following common line feed chars:
- // \n - UNIX \r\n - DOS \r - Mac
- if (ch == '\r' || ch == '\n')
- {
- string s;
- if (sb != null)
- {
- sb.Append(_charBuffer, _charPos, i - _charPos);
- s = sb.ToString();
- }
- else
- {
- s = new string(_charBuffer, _charPos, i - _charPos);
- }
- _charPos = i + 1;
- if (ch == '\r' && (_charPos < _charLen || ReadBuffer() > 0))
- {
- if (_charBuffer[_charPos] == '\n')
- {
- _charPos++;
- }
- }
- return s;
- }
- i++;
- } while (i < _charLen);
-
- i = _charLen - _charPos;
- sb ??= new StringBuilder(i + 80);
- sb.Append(_charBuffer, _charPos, i);
- } while (ReadBuffer() > 0);
- return sb.ToString();
- }
-
- public override Task<string?> ReadLineAsync()
- {
- // If we have been inherited into a subclass, the following implementation could be incorrect
- // since it does not call through to Read() which a subclass might have overridden.
- // To be safe we will only use this implementation in cases where we know it is safe to do so,
- // and delegate to our base class (which will call into Read) when we are not sure.
- if (GetType() != typeof(StreamReader))
- {
- return base.ReadLineAsync();
- }
-
- ThrowIfDisposed();
- CheckAsyncTaskInProgress();
-
- Task<string?> task = ReadLineAsyncInternal();
- _asyncReadTask = task;
-
- return task;
- }
-
- private async Task<string?> ReadLineAsyncInternal()
- {
- if (_charPos == _charLen && (await ReadBufferAsync(CancellationToken.None).ConfigureAwait(false)) == 0)
- {
- return null;
- }
-
- StringBuilder? sb = null;
-
- do
- {
- char[] tmpCharBuffer = _charBuffer;
- int tmpCharLen = _charLen;
- int tmpCharPos = _charPos;
- int i = tmpCharPos;
-
- do
- {
- char ch = tmpCharBuffer[i];
-
- // Note the following common line feed chars:
- // \n - UNIX \r\n - DOS \r - Mac
- if (ch == '\r' || ch == '\n')
- {
- string s;
-
- if (sb != null)
- {
- sb.Append(tmpCharBuffer, tmpCharPos, i - tmpCharPos);
- s = sb.ToString();
- }
- else
- {
- s = new string(tmpCharBuffer, tmpCharPos, i - tmpCharPos);
- }
-
- _charPos = tmpCharPos = i + 1;
-
- if (ch == '\r' && (tmpCharPos < tmpCharLen || (await ReadBufferAsync(CancellationToken.None).ConfigureAwait(false)) > 0))
- {
- tmpCharPos = _charPos;
- if (_charBuffer[tmpCharPos] == '\n')
- {
- _charPos = ++tmpCharPos;
- }
- }
-
- return s;
- }
-
- i++;
- } while (i < tmpCharLen);
-
- i = tmpCharLen - tmpCharPos;
- sb ??= new StringBuilder(i + 80);
- sb.Append(tmpCharBuffer, tmpCharPos, i);
- } while (await ReadBufferAsync(CancellationToken.None).ConfigureAwait(false) > 0);
-
- return sb.ToString();
- }
-
- public override Task<string> ReadToEndAsync()
- {
- // If we have been inherited into a subclass, the following implementation could be incorrect
- // since it does not call through to Read() which a subclass might have overridden.
- // To be safe we will only use this implementation in cases where we know it is safe to do so,
- // and delegate to our base class (which will call into Read) when we are not sure.
- if (GetType() != typeof(StreamReader))
- {
- return base.ReadToEndAsync();
- }
-
- ThrowIfDisposed();
- CheckAsyncTaskInProgress();
-
- Task<string> task = ReadToEndAsyncInternal();
- _asyncReadTask = task;
-
- return task;
- }
-
- private async Task<string> ReadToEndAsyncInternal()
- {
- // Call ReadBuffer, then pull data out of charBuffer.
- StringBuilder sb = new StringBuilder(_charLen - _charPos);
- do
- {
- int tmpCharPos = _charPos;
- sb.Append(_charBuffer, tmpCharPos, _charLen - tmpCharPos);
- _charPos = _charLen; // We consumed these characters
- await ReadBufferAsync(CancellationToken.None).ConfigureAwait(false);
- } while (_charLen > 0);
-
- return sb.ToString();
- }
-
- public override Task<int> ReadAsync(char[] buffer, int index, int count)
- {
- if (buffer == null)
- {
- throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
- }
- if (index < 0 || count < 0)
- {
- throw new ArgumentOutOfRangeException(index < 0 ? nameof(index) : nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (buffer.Length - index < count)
- {
- throw new ArgumentException(SR.Argument_InvalidOffLen);
- }
-
- // If we have been inherited into a subclass, the following implementation could be incorrect
- // since it does not call through to Read() which a subclass might have overridden.
- // To be safe we will only use this implementation in cases where we know it is safe to do so,
- // and delegate to our base class (which will call into Read) when we are not sure.
- if (GetType() != typeof(StreamReader))
- {
- return base.ReadAsync(buffer, index, count);
- }
-
- ThrowIfDisposed();
- CheckAsyncTaskInProgress();
-
- Task<int> task = ReadAsyncInternal(new Memory<char>(buffer, index, count), CancellationToken.None).AsTask();
- _asyncReadTask = task;
-
- return task;
- }
-
- public override ValueTask<int> ReadAsync(Memory<char> buffer, CancellationToken cancellationToken = default)
- {
- if (GetType() != typeof(StreamReader))
- {
- // Ensure we use existing overrides if a class already overrode existing overloads.
- return base.ReadAsync(buffer, cancellationToken);
- }
-
- ThrowIfDisposed();
- CheckAsyncTaskInProgress();
-
- if (cancellationToken.IsCancellationRequested)
- {
- return new ValueTask<int>(Task.FromCanceled<int>(cancellationToken));
- }
-
- return ReadAsyncInternal(buffer, cancellationToken);
- }
-
- internal override async ValueTask<int> ReadAsyncInternal(Memory<char> buffer, CancellationToken cancellationToken)
- {
- if (_charPos == _charLen && (await ReadBufferAsync(cancellationToken).ConfigureAwait(false)) == 0)
- {
- return 0;
- }
-
- int charsRead = 0;
-
- // As a perf optimization, if we had exactly one buffer's worth of
- // data read in, let's try writing directly to the user's buffer.
- bool readToUserBuffer = false;
-
- byte[] tmpByteBuffer = _byteBuffer;
- Stream tmpStream = _stream;
-
- int count = buffer.Length;
- while (count > 0)
- {
- // n is the characters available in _charBuffer
- int n = _charLen - _charPos;
-
- // charBuffer is empty, let's read from the stream
- if (n == 0)
- {
- _charLen = 0;
- _charPos = 0;
-
- if (!_checkPreamble)
- {
- _byteLen = 0;
- }
-
- readToUserBuffer = count >= _maxCharsPerBuffer;
-
- // We loop here so that we read in enough bytes to yield at least 1 char.
- // We break out of the loop if the stream is blocked (EOF is reached).
- do
- {
- Debug.Assert(n == 0);
-
- if (_checkPreamble)
- {
- Debug.Assert(_bytePos <= _encoding.Preamble.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?");
- int tmpBytePos = _bytePos;
- int len = await tmpStream.ReadAsync(new Memory<byte>(tmpByteBuffer, tmpBytePos, tmpByteBuffer.Length - tmpBytePos), cancellationToken).ConfigureAwait(false);
- Debug.Assert(len >= 0, "Stream.Read returned a negative number! This is a bug in your stream class.");
-
- if (len == 0)
- {
- // EOF but we might have buffered bytes from previous
- // attempts to detect preamble that needs to be decoded now
- if (_byteLen > 0)
- {
- if (readToUserBuffer)
- {
- n = _decoder.GetChars(new ReadOnlySpan<byte>(tmpByteBuffer, 0, _byteLen), buffer.Span.Slice(charsRead), flush: false);
- _charLen = 0; // StreamReader's buffer is empty.
- }
- else
- {
- n = _decoder.GetChars(tmpByteBuffer, 0, _byteLen, _charBuffer, 0);
- _charLen += n; // Number of chars in StreamReader's buffer.
- }
- }
-
- // How can part of the preamble yield any chars?
- Debug.Assert(n == 0);
-
- _isBlocked = true;
- break;
- }
- else
- {
- _byteLen += len;
- }
- }
- else
- {
- Debug.Assert(_bytePos == 0, "_bytePos can be non zero only when we are trying to _checkPreamble. Are two threads using this StreamReader at the same time?");
-
- _byteLen = await tmpStream.ReadAsync(new Memory<byte>(tmpByteBuffer), cancellationToken).ConfigureAwait(false);
-
- Debug.Assert(_byteLen >= 0, "Stream.Read returned a negative number! This is a bug in your stream class.");
-
- if (_byteLen == 0) // EOF
- {
- _isBlocked = true;
- break;
- }
- }
-
- // _isBlocked == whether we read fewer bytes than we asked for.
- // Note we must check it here because CompressBuffer or
- // DetectEncoding will change _byteLen.
- _isBlocked = (_byteLen < tmpByteBuffer.Length);
-
- // Check for preamble before detect encoding. This is not to override the
- // user supplied Encoding for the one we implicitly detect. The user could
- // customize the encoding which we will loose, such as ThrowOnError on UTF8
- // Note: we don't need to recompute readToUserBuffer optimization as IsPreamble
- // doesn't change the encoding or affect _maxCharsPerBuffer
- if (IsPreamble())
- {
- continue;
- }
-
- // On the first call to ReadBuffer, if we're supposed to detect the encoding, do it.
- if (_detectEncoding && _byteLen >= 2)
- {
- DetectEncoding();
- // DetectEncoding changes some buffer state. Recompute this.
- readToUserBuffer = count >= _maxCharsPerBuffer;
- }
-
- Debug.Assert(n == 0);
-
- _charPos = 0;
- if (readToUserBuffer)
- {
- n += _decoder.GetChars(new ReadOnlySpan<byte>(tmpByteBuffer, 0, _byteLen), buffer.Span.Slice(charsRead), flush: false);
-
- // Why did the bytes yield no chars?
- Debug.Assert(n > 0);
-
- _charLen = 0; // StreamReader's buffer is empty.
- }
- else
- {
- n = _decoder.GetChars(tmpByteBuffer, 0, _byteLen, _charBuffer, 0);
-
- // Why did the bytes yield no chars?
- Debug.Assert(n > 0);
-
- _charLen += n; // Number of chars in StreamReader's buffer.
- }
- } while (n == 0);
-
- if (n == 0)
- {
- break; // We're at EOF
- }
- } // if (n == 0)
-
- // Got more chars in charBuffer than the user requested
- if (n > count)
- {
- n = count;
- }
-
- if (!readToUserBuffer)
- {
- new Span<char>(_charBuffer, _charPos, n).CopyTo(buffer.Span.Slice(charsRead));
- _charPos += n;
- }
-
- charsRead += n;
- count -= n;
-
- // This function shouldn't block for an indefinite amount of time,
- // or reading from a network stream won't work right. If we got
- // fewer bytes than we requested, then we want to break right here.
- if (_isBlocked)
- {
- break;
- }
- } // while (count > 0)
-
- return charsRead;
- }
-
- public override Task<int> ReadBlockAsync(char[] buffer, int index, int count)
- {
- if (buffer == null)
- {
- throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
- }
- if (index < 0 || count < 0)
- {
- throw new ArgumentOutOfRangeException(index < 0 ? nameof(index) : nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (buffer.Length - index < count)
- {
- throw new ArgumentException(SR.Argument_InvalidOffLen);
- }
-
- // If we have been inherited into a subclass, the following implementation could be incorrect
- // since it does not call through to Read() which a subclass might have overridden.
- // To be safe we will only use this implementation in cases where we know it is safe to do so,
- // and delegate to our base class (which will call into Read) when we are not sure.
- if (GetType() != typeof(StreamReader))
- {
- return base.ReadBlockAsync(buffer, index, count);
- }
-
- ThrowIfDisposed();
- CheckAsyncTaskInProgress();
-
- Task<int> task = base.ReadBlockAsync(buffer, index, count);
- _asyncReadTask = task;
-
- return task;
- }
-
- public override ValueTask<int> ReadBlockAsync(Memory<char> buffer, CancellationToken cancellationToken = default)
- {
- if (GetType() != typeof(StreamReader))
- {
- // If a derived type may have overridden ReadBlockAsync(char[], ...) before this overload
- // was introduced, defer to it.
- return base.ReadBlockAsync(buffer, cancellationToken);
- }
-
- ThrowIfDisposed();
- CheckAsyncTaskInProgress();
-
- if (cancellationToken.IsCancellationRequested)
- {
- return new ValueTask<int>(Task.FromCanceled<int>(cancellationToken));
- }
-
- ValueTask<int> vt = ReadBlockAsyncInternal(buffer, cancellationToken);
- if (vt.IsCompletedSuccessfully)
- {
- return vt;
- }
-
- Task<int> t = vt.AsTask();
- _asyncReadTask = t;
- return new ValueTask<int>(t);
- }
-
- private async ValueTask<int> ReadBufferAsync(CancellationToken cancellationToken)
- {
- _charLen = 0;
- _charPos = 0;
- byte[] tmpByteBuffer = _byteBuffer;
- Stream tmpStream = _stream;
-
- if (!_checkPreamble)
- {
- _byteLen = 0;
- }
- do
- {
- if (_checkPreamble)
- {
- Debug.Assert(_bytePos <= _encoding.Preamble.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?");
- int tmpBytePos = _bytePos;
- int len = await tmpStream.ReadAsync(new Memory<byte>(tmpByteBuffer, tmpBytePos, tmpByteBuffer.Length - tmpBytePos), cancellationToken).ConfigureAwait(false);
- Debug.Assert(len >= 0, "Stream.Read returned a negative number! This is a bug in your stream class.");
-
- if (len == 0)
- {
- // EOF but we might have buffered bytes from previous
- // attempt to detect preamble that needs to be decoded now
- if (_byteLen > 0)
- {
- _charLen += _decoder.GetChars(tmpByteBuffer, 0, _byteLen, _charBuffer, _charLen);
- // Need to zero out the _byteLen after we consume these bytes so that we don't keep infinitely hitting this code path
- _bytePos = 0; _byteLen = 0;
- }
-
- return _charLen;
- }
-
- _byteLen += len;
- }
- else
- {
- Debug.Assert(_bytePos == 0, "_bytePos can be non zero only when we are trying to _checkPreamble. Are two threads using this StreamReader at the same time?");
- _byteLen = await tmpStream.ReadAsync(new Memory<byte>(tmpByteBuffer), cancellationToken).ConfigureAwait(false);
- Debug.Assert(_byteLen >= 0, "Stream.Read returned a negative number! Bug in stream class.");
-
- if (_byteLen == 0) // We're at EOF
- {
- return _charLen;
- }
- }
-
- // _isBlocked == whether we read fewer bytes than we asked for.
- // Note we must check it here because CompressBuffer or
- // DetectEncoding will change _byteLen.
- _isBlocked = (_byteLen < tmpByteBuffer.Length);
-
- // Check for preamble before detect encoding. This is not to override the
- // user supplied Encoding for the one we implicitly detect. The user could
- // customize the encoding which we will loose, such as ThrowOnError on UTF8
- if (IsPreamble())
- {
- continue;
- }
-
- // If we're supposed to detect the encoding and haven't done so yet,
- // do it. Note this may need to be called more than once.
- if (_detectEncoding && _byteLen >= 2)
- {
- DetectEncoding();
- }
-
- _charLen += _decoder.GetChars(tmpByteBuffer, 0, _byteLen, _charBuffer, _charLen);
- } while (_charLen == 0);
-
- return _charLen;
- }
-
- private void ThrowIfDisposed()
- {
- if (_disposed)
- {
- ThrowObjectDisposedException();
- }
-
- void ThrowObjectDisposedException() => throw new ObjectDisposedException(GetType().Name, SR.ObjectDisposed_ReaderClosed);
- }
-
- // No data, class doesn't need to be serializable.
- // Note this class is threadsafe.
- private sealed class NullStreamReader : StreamReader
- {
- public override Encoding CurrentEncoding => Encoding.Unicode;
-
- protected override void Dispose(bool disposing)
- {
- // Do nothing - this is essentially unclosable.
- }
-
- public override int Peek()
- {
- return -1;
- }
-
- public override int Read()
- {
- return -1;
- }
-
- public override int Read(char[] buffer, int index, int count)
- {
- return 0;
- }
-
- public override string? ReadLine()
- {
- return null;
- }
-
- public override string ReadToEnd()
- {
- return string.Empty;
- }
-
- internal override int ReadBuffer()
- {
- return 0;
- }
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/StreamWriter.cs b/netcore/System.Private.CoreLib/shared/System/IO/StreamWriter.cs
deleted file mode 100644
index 3987457bbfe..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/StreamWriter.cs
+++ /dev/null
@@ -1,1078 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace System.IO
-{
- // This class implements a TextWriter for writing characters to a Stream.
- // This is designed for character output in a particular Encoding,
- // whereas the Stream class is designed for byte input and output.
- public class StreamWriter : TextWriter
- {
- // For UTF-8, the values of 1K for the default buffer size and 4K for the
- // file stream buffer size are reasonable & give very reasonable
- // performance for in terms of construction time for the StreamWriter and
- // write perf. Note that for UTF-8, we end up allocating a 4K byte buffer,
- // which means we take advantage of adaptive buffering code.
- // The performance using UnicodeEncoding is acceptable.
- private const int DefaultBufferSize = 1024; // char[]
- private const int DefaultFileStreamBufferSize = 4096;
- private const int MinBufferSize = 128;
-
- // Bit bucket - Null has no backing store. Non closable.
- public static new readonly StreamWriter Null = new StreamWriter(Stream.Null, UTF8NoBOM, MinBufferSize, leaveOpen: true);
-
- private readonly Stream _stream;
- private readonly Encoding _encoding;
- private readonly Encoder _encoder;
- private readonly byte[] _byteBuffer;
- private readonly char[] _charBuffer;
- private int _charPos;
- private int _charLen;
- private bool _autoFlush;
- private bool _haveWrittenPreamble;
- private readonly bool _closable;
- private bool _disposed;
-
- // We don't guarantee thread safety on StreamWriter, but we should at
- // least prevent users from trying to write anything while an Async
- // write from the same thread is in progress.
- private Task _asyncWriteTask = Task.CompletedTask;
-
- private void CheckAsyncTaskInProgress()
- {
- // We are not locking the access to _asyncWriteTask because this is not meant to guarantee thread safety.
- // We are simply trying to deter calling any Write APIs while an async Write from the same thread is in progress.
- if (!_asyncWriteTask.IsCompleted)
- {
- ThrowAsyncIOInProgress();
- }
- }
-
- [DoesNotReturn]
- private static void ThrowAsyncIOInProgress() =>
- throw new InvalidOperationException(SR.InvalidOperation_AsyncIOInProgress);
-
- // The high level goal is to be tolerant of encoding errors when we read and very strict
- // when we write. Hence, default StreamWriter encoding will throw on encoding error.
- // Note: when StreamWriter throws on invalid encoding chars (for ex, high surrogate character
- // D800-DBFF without a following low surrogate character DC00-DFFF), it will cause the
- // internal StreamWriter's state to be irrecoverable as it would have buffered the
- // illegal chars and any subsequent call to Flush() would hit the encoding error again.
- // Even Close() will hit the exception as it would try to flush the unwritten data.
- // Maybe we can add a DiscardBufferedData() method to get out of such situation (like
- // StreamReader though for different reason). Either way, the buffered data will be lost!
- private static Encoding UTF8NoBOM => EncodingCache.UTF8NoBOM;
-
- public StreamWriter(Stream stream)
- : this(stream, UTF8NoBOM, DefaultBufferSize, false)
- {
- }
-
- public StreamWriter(Stream stream, Encoding encoding)
- : this(stream, encoding, DefaultBufferSize, false)
- {
- }
-
- // Creates a new StreamWriter for the given stream. The
- // character encoding is set by encoding and the buffer size,
- // in number of 16-bit characters, is set by bufferSize.
- //
- public StreamWriter(Stream stream, Encoding encoding, int bufferSize)
- : this(stream, encoding, bufferSize, false)
- {
- }
-
- public StreamWriter(Stream stream, Encoding? encoding = null, int bufferSize = -1, bool leaveOpen = false)
- : base(null) // Ask for CurrentCulture all the time
- {
- if (stream == null)
- {
- throw new ArgumentNullException(nameof(stream));
- }
- if (encoding == null)
- {
- encoding = UTF8NoBOM;
- }
- if (!stream.CanWrite)
- {
- throw new ArgumentException(SR.Argument_StreamNotWritable);
- }
- if (bufferSize == -1)
- {
- bufferSize = DefaultBufferSize;
- }
- else if (bufferSize <= 0)
- {
- throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);
- }
-
- _stream = stream;
- _encoding = encoding;
- _encoder = _encoding.GetEncoder();
- if (bufferSize < MinBufferSize)
- {
- bufferSize = MinBufferSize;
- }
-
- _charBuffer = new char[bufferSize];
- _byteBuffer = new byte[_encoding.GetMaxByteCount(bufferSize)];
- _charLen = bufferSize;
- // If we're appending to a Stream that already has data, don't write
- // the preamble.
- if (_stream.CanSeek && _stream.Position > 0)
- {
- _haveWrittenPreamble = true;
- }
-
- _closable = !leaveOpen;
- }
-
- public StreamWriter(string path)
- : this(path, false, UTF8NoBOM, DefaultBufferSize)
- {
- }
-
- public StreamWriter(string path, bool append)
- : this(path, append, UTF8NoBOM, DefaultBufferSize)
- {
- }
-
- public StreamWriter(string path, bool append, Encoding encoding)
- : this(path, append, encoding, DefaultBufferSize)
- {
- }
-
- public StreamWriter(string path, bool append, Encoding encoding, int bufferSize) :
- this(ValidateArgsAndOpenPath(path, append, encoding, bufferSize), encoding, bufferSize, leaveOpen: false)
- {
- }
-
- private static Stream ValidateArgsAndOpenPath(string path, bool append, Encoding encoding, int bufferSize)
- {
- if (path == null)
- throw new ArgumentNullException(nameof(path));
- if (encoding == null)
- throw new ArgumentNullException(nameof(encoding));
- if (path.Length == 0)
- throw new ArgumentException(SR.Argument_EmptyPath);
- if (bufferSize <= 0)
- throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);
-
- return new FileStream(path, append ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.Read, DefaultFileStreamBufferSize, FileOptions.SequentialScan);
- }
-
- public override void Close()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- protected override void Dispose(bool disposing)
- {
- try
- {
- // We need to flush any buffered data if we are being closed/disposed.
- // Also, we never close the handles for stdout & friends. So we can safely
- // write any buffered data to those streams even during finalization, which
- // is generally the right thing to do.
- if (!_disposed && disposing)
- {
- // Note: flush on the underlying stream can throw (ex., low disk space)
- CheckAsyncTaskInProgress();
- Flush(flushStream: true, flushEncoder: true);
- }
- }
- finally
- {
- CloseStreamFromDispose(disposing);
- }
- }
-
- private void CloseStreamFromDispose(bool disposing)
- {
- // Dispose of our resources if this StreamWriter is closable.
- if (_closable && !_disposed)
- {
- try
- {
- // Attempt to close the stream even if there was an IO error from Flushing.
- // Note that Stream.Close() can potentially throw here (may or may not be
- // due to the same Flush error). In this case, we still need to ensure
- // cleaning up internal resources, hence the finally block.
- if (disposing)
- {
- _stream.Close();
- }
- }
- finally
- {
- _disposed = true;
- _charLen = 0;
- base.Dispose(disposing);
- }
- }
- }
-
- public override ValueTask DisposeAsync() =>
- GetType() != typeof(StreamWriter) ?
- base.DisposeAsync() :
- DisposeAsyncCore();
-
- private async ValueTask DisposeAsyncCore()
- {
- // Same logic as in Dispose(), but with async flushing.
- Debug.Assert(GetType() == typeof(StreamWriter));
- try
- {
- if (!_disposed)
- {
- await FlushAsync().ConfigureAwait(false);
- }
- }
- finally
- {
- CloseStreamFromDispose(disposing: true);
- }
- GC.SuppressFinalize(this);
- }
-
- public override void Flush()
- {
- CheckAsyncTaskInProgress();
-
- Flush(true, true);
- }
-
- private void Flush(bool flushStream, bool flushEncoder)
- {
- // flushEncoder should be true at the end of the file and if
- // the user explicitly calls Flush (though not if AutoFlush is true).
- // This is required to flush any dangling characters from our UTF-7
- // and UTF-8 encoders.
- ThrowIfDisposed();
-
- // Perf boost for Flush on non-dirty writers.
- if (_charPos == 0 && !flushStream && !flushEncoder)
- {
- return;
- }
-
- if (!_haveWrittenPreamble)
- {
- _haveWrittenPreamble = true;
- ReadOnlySpan<byte> preamble = _encoding.Preamble;
- if (preamble.Length > 0)
- {
- _stream.Write(preamble);
- }
- }
-
- int count = _encoder.GetBytes(_charBuffer, 0, _charPos, _byteBuffer, 0, flushEncoder);
- _charPos = 0;
- if (count > 0)
- {
- _stream.Write(_byteBuffer, 0, count);
- }
- // By definition, calling Flush should flush the stream, but this is
- // only necessary if we passed in true for flushStream. The Web
- // Services guys have some perf tests where flushing needlessly hurts.
- if (flushStream)
- {
- _stream.Flush();
- }
- }
-
- public virtual bool AutoFlush
- {
- get => _autoFlush;
-
- set
- {
- CheckAsyncTaskInProgress();
-
- _autoFlush = value;
- if (value)
- {
- Flush(true, false);
- }
- }
- }
-
- public virtual Stream BaseStream => _stream;
-
- public override Encoding Encoding => _encoding;
-
- public override void Write(char value)
- {
- CheckAsyncTaskInProgress();
-
- if (_charPos == _charLen)
- {
- Flush(false, false);
- }
-
- _charBuffer[_charPos] = value;
- _charPos++;
- if (_autoFlush)
- {
- Flush(true, false);
- }
- }
-
- [MethodImpl(MethodImplOptions.NoInlining)] // prevent WriteSpan from bloating call sites
- public override void Write(char[]? buffer)
- {
- WriteSpan(buffer, appendNewLine: false);
- }
-
- [MethodImpl(MethodImplOptions.NoInlining)] // prevent WriteSpan from bloating call sites
- public override void Write(char[] buffer, int index, int count)
- {
- if (buffer == null)
- {
- throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
- }
- if (index < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (count < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (buffer.Length - index < count)
- {
- throw new ArgumentException(SR.Argument_InvalidOffLen);
- }
-
- WriteSpan(buffer.AsSpan(index, count), appendNewLine: false);
- }
-
- [MethodImpl(MethodImplOptions.NoInlining)] // prevent WriteSpan from bloating call sites
- public override void Write(ReadOnlySpan<char> buffer)
- {
- if (GetType() == typeof(StreamWriter))
- {
- WriteSpan(buffer, appendNewLine: false);
- }
- else
- {
- // If a derived class may have overridden existing Write behavior,
- // we need to make sure we use it.
- base.Write(buffer);
- }
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private unsafe void WriteSpan(ReadOnlySpan<char> buffer, bool appendNewLine)
- {
- CheckAsyncTaskInProgress();
-
- if (buffer.Length <= 4 && // Threshold of 4 chosen based on perf experimentation
- buffer.Length <= _charLen - _charPos)
- {
- // For very short buffers and when we don't need to worry about running out of space
- // in the char buffer, just copy the chars individually.
- for (int i = 0; i < buffer.Length; i++)
- {
- _charBuffer[_charPos++] = buffer[i];
- }
- }
- else
- {
- // For larger buffers or when we may run out of room in the internal char buffer, copy in chunks.
- // Use unsafe code until https://github.com/dotnet/coreclr/issues/13827 is addressed, as spans are
- // resulting in significant overhead (even when the if branch above is taken rather than this
- // else) due to temporaries that need to be cleared. Given the use of unsafe code, we also
- // make local copies of instance state to protect against potential concurrent misuse.
-
- ThrowIfDisposed();
- char[] charBuffer = _charBuffer;
-
- fixed (char* bufferPtr = &MemoryMarshal.GetReference(buffer))
- fixed (char* dstPtr = &charBuffer[0])
- {
- char* srcPtr = bufferPtr;
- int count = buffer.Length;
- int dstPos = _charPos; // use a local copy of _charPos for safety
- while (count > 0)
- {
- if (dstPos == charBuffer.Length)
- {
- Flush(false, false);
- dstPos = 0;
- }
-
- int n = Math.Min(charBuffer.Length - dstPos, count);
- int bytesToCopy = n * sizeof(char);
-
- Buffer.MemoryCopy(srcPtr, dstPtr + dstPos, bytesToCopy, bytesToCopy);
-
- _charPos += n;
- dstPos += n;
- srcPtr += n;
- count -= n;
- }
- }
- }
-
- if (appendNewLine)
- {
- char[] coreNewLine = CoreNewLine;
- for (int i = 0; i < coreNewLine.Length; i++) // Generally 1 (\n) or 2 (\r\n) iterations
- {
- if (_charPos == _charLen)
- {
- Flush(false, false);
- }
-
- _charBuffer[_charPos] = coreNewLine[i];
- _charPos++;
- }
- }
-
- if (_autoFlush)
- {
- Flush(true, false);
- }
- }
-
- [MethodImpl(MethodImplOptions.NoInlining)] // prevent WriteSpan from bloating call sites
- public override void Write(string? value)
- {
- WriteSpan(value, appendNewLine: false);
- }
-
- [MethodImpl(MethodImplOptions.NoInlining)] // prevent WriteSpan from bloating call sites
- public override void WriteLine(string? value)
- {
- CheckAsyncTaskInProgress();
- WriteSpan(value, appendNewLine: true);
- }
-
- [MethodImpl(MethodImplOptions.NoInlining)] // prevent WriteSpan from bloating call sites
- public override void WriteLine(ReadOnlySpan<char> value)
- {
- if (GetType() == typeof(StreamWriter))
- {
- CheckAsyncTaskInProgress();
- WriteSpan(value, appendNewLine: true);
- }
- else
- {
- // If a derived class may have overridden existing WriteLine behavior,
- // we need to make sure we use it.
- base.WriteLine(value);
- }
- }
-
- private void WriteFormatHelper(string format, ParamsArray args, bool appendNewLine)
- {
- StringBuilder sb =
- StringBuilderCache.Acquire((format?.Length ?? 0) + args.Length * 8)
- .AppendFormatHelper(null, format!, args); // AppendFormatHelper will appropriately throw ArgumentNullException for a null format
-
- StringBuilder.ChunkEnumerator chunks = sb.GetChunks();
-
- bool more = chunks.MoveNext();
- while (more)
- {
- ReadOnlySpan<char> current = chunks.Current.Span;
- more = chunks.MoveNext();
-
- // If final chunk, include the newline if needed
- WriteSpan(current, appendNewLine: more ? false : appendNewLine);
- }
-
- StringBuilderCache.Release(sb);
- }
-
- public override void Write(string format, object? arg0)
- {
- if (GetType() == typeof(StreamWriter))
- {
- WriteFormatHelper(format, new ParamsArray(arg0), appendNewLine: false);
- }
- else
- {
- base.Write(format, arg0);
- }
- }
-
- public override void Write(string format, object? arg0, object? arg1)
- {
- if (GetType() == typeof(StreamWriter))
- {
- WriteFormatHelper(format, new ParamsArray(arg0, arg1), appendNewLine: false);
- }
- else
- {
- base.Write(format, arg0, arg1);
- }
- }
-
- public override void Write(string format, object? arg0, object? arg1, object? arg2)
- {
- if (GetType() == typeof(StreamWriter))
- {
- WriteFormatHelper(format, new ParamsArray(arg0, arg1, arg2), appendNewLine: false);
- }
- else
- {
- base.Write(format, arg0, arg1, arg2);
- }
- }
-
- public override void Write(string format, params object?[] arg)
- {
- if (GetType() == typeof(StreamWriter))
- {
- if (arg == null)
- {
- throw new ArgumentNullException((format == null) ? nameof(format) : nameof(arg)); // same as base logic
- }
- WriteFormatHelper(format, new ParamsArray(arg), appendNewLine: false);
- }
- else
- {
- base.Write(format, arg);
- }
- }
-
- public override void WriteLine(string format, object? arg0)
- {
- if (GetType() == typeof(StreamWriter))
- {
- WriteFormatHelper(format, new ParamsArray(arg0), appendNewLine: true);
- }
- else
- {
- base.WriteLine(format, arg0);
- }
- }
-
- public override void WriteLine(string format, object? arg0, object? arg1)
- {
- if (GetType() == typeof(StreamWriter))
- {
- WriteFormatHelper(format, new ParamsArray(arg0, arg1), appendNewLine: true);
- }
- else
- {
- base.WriteLine(format, arg0, arg1);
- }
- }
-
- public override void WriteLine(string format, object? arg0, object? arg1, object? arg2)
- {
- if (GetType() == typeof(StreamWriter))
- {
- WriteFormatHelper(format, new ParamsArray(arg0, arg1, arg2), appendNewLine: true);
- }
- else
- {
- base.WriteLine(format, arg0, arg1, arg2);
- }
- }
-
- public override void WriteLine(string format, params object?[] arg)
- {
- if (GetType() == typeof(StreamWriter))
- {
- if (arg == null)
- {
- throw new ArgumentNullException(nameof(arg));
- }
- WriteFormatHelper(format, new ParamsArray(arg), appendNewLine: true);
- }
- else
- {
- base.WriteLine(format, arg);
- }
- }
-
- public override Task WriteAsync(char value)
- {
- // If we have been inherited into a subclass, the following implementation could be incorrect
- // since it does not call through to Write() which a subclass might have overridden.
- // To be safe we will only use this implementation in cases where we know it is safe to do so,
- // and delegate to our base class (which will call into Write) when we are not sure.
- if (GetType() != typeof(StreamWriter))
- {
- return base.WriteAsync(value);
- }
-
- ThrowIfDisposed();
- CheckAsyncTaskInProgress();
-
- Task task = WriteAsyncInternal(this, value, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: false);
- _asyncWriteTask = task;
-
- return task;
- }
-
- // We pass in private instance fields of this MarshalByRefObject-derived type as local params
- // to ensure performant access inside the state machine that corresponds this async method.
- // Fields that are written to must be assigned at the end of the method *and* before instance invocations.
- private static async Task WriteAsyncInternal(StreamWriter _this, char value,
- char[] charBuffer, int charPos, int charLen, char[] coreNewLine,
- bool autoFlush, bool appendNewLine)
- {
- if (charPos == charLen)
- {
- await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
- Debug.Assert(_this._charPos == 0);
- charPos = 0;
- }
-
- charBuffer[charPos] = value;
- charPos++;
-
- if (appendNewLine)
- {
- for (int i = 0; i < coreNewLine.Length; i++) // Expect 2 iterations, no point calling BlockCopy
- {
- if (charPos == charLen)
- {
- await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
- Debug.Assert(_this._charPos == 0);
- charPos = 0;
- }
-
- charBuffer[charPos] = coreNewLine[i];
- charPos++;
- }
- }
-
- if (autoFlush)
- {
- await _this.FlushAsyncInternal(true, false, charBuffer, charPos).ConfigureAwait(false);
- Debug.Assert(_this._charPos == 0);
- charPos = 0;
- }
-
- _this._charPos = charPos;
- }
-
- public override Task WriteAsync(string? value)
- {
- // If we have been inherited into a subclass, the following implementation could be incorrect
- // since it does not call through to Write() which a subclass might have overridden.
- // To be safe we will only use this implementation in cases where we know it is safe to do so,
- // and delegate to our base class (which will call into Write) when we are not sure.
- if (GetType() != typeof(StreamWriter))
- {
- return base.WriteAsync(value);
- }
-
- if (value != null)
- {
- ThrowIfDisposed();
- CheckAsyncTaskInProgress();
-
- Task task = WriteAsyncInternal(this, value, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: false);
- _asyncWriteTask = task;
-
- return task;
- }
- else
- {
- return Task.CompletedTask;
- }
- }
-
- // We pass in private instance fields of this MarshalByRefObject-derived type as local params
- // to ensure performant access inside the state machine that corresponds this async method.
- // Fields that are written to must be assigned at the end of the method *and* before instance invocations.
- private static async Task WriteAsyncInternal(StreamWriter _this, string value,
- char[] charBuffer, int charPos, int charLen, char[] coreNewLine,
- bool autoFlush, bool appendNewLine)
- {
- Debug.Assert(value != null);
-
- int count = value.Length;
- int index = 0;
-
- while (count > 0)
- {
- if (charPos == charLen)
- {
- await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
- Debug.Assert(_this._charPos == 0);
- charPos = 0;
- }
-
- int n = charLen - charPos;
- if (n > count)
- {
- n = count;
- }
-
- Debug.Assert(n > 0, "StreamWriter::Write(String) isn't making progress! This is most likely a race condition in user code.");
-
- value.CopyTo(index, charBuffer, charPos, n);
-
- charPos += n;
- index += n;
- count -= n;
- }
-
- if (appendNewLine)
- {
- for (int i = 0; i < coreNewLine.Length; i++) // Expect 2 iterations, no point calling BlockCopy
- {
- if (charPos == charLen)
- {
- await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
- Debug.Assert(_this._charPos == 0);
- charPos = 0;
- }
-
- charBuffer[charPos] = coreNewLine[i];
- charPos++;
- }
- }
-
- if (autoFlush)
- {
- await _this.FlushAsyncInternal(true, false, charBuffer, charPos).ConfigureAwait(false);
- Debug.Assert(_this._charPos == 0);
- charPos = 0;
- }
-
- _this._charPos = charPos;
- }
-
- public override Task WriteAsync(char[] buffer, int index, int count)
- {
- if (buffer == null)
- {
- throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
- }
- if (index < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (count < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (buffer.Length - index < count)
- {
- throw new ArgumentException(SR.Argument_InvalidOffLen);
- }
-
- // If we have been inherited into a subclass, the following implementation could be incorrect
- // since it does not call through to Write() which a subclass might have overridden.
- // To be safe we will only use this implementation in cases where we know it is safe to do so,
- // and delegate to our base class (which will call into Write) when we are not sure.
- if (GetType() != typeof(StreamWriter))
- {
- return base.WriteAsync(buffer, index, count);
- }
-
- ThrowIfDisposed();
- CheckAsyncTaskInProgress();
-
- Task task = WriteAsyncInternal(this, new ReadOnlyMemory<char>(buffer, index, count), _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: false, cancellationToken: default);
- _asyncWriteTask = task;
-
- return task;
- }
-
- public override Task WriteAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default)
- {
- if (GetType() != typeof(StreamWriter))
- {
- // If a derived type may have overridden existing WriteASync(char[], ...) behavior, make sure we use it.
- return base.WriteAsync(buffer, cancellationToken);
- }
-
- ThrowIfDisposed();
- CheckAsyncTaskInProgress();
-
- if (cancellationToken.IsCancellationRequested)
- {
- return Task.FromCanceled(cancellationToken);
- }
-
- Task task = WriteAsyncInternal(this, buffer, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: false, cancellationToken: cancellationToken);
- _asyncWriteTask = task;
- return task;
- }
-
- // We pass in private instance fields of this MarshalByRefObject-derived type as local params
- // to ensure performant access inside the state machine that corresponds this async method.
- // Fields that are written to must be assigned at the end of the method *and* before instance invocations.
- private static async Task WriteAsyncInternal(StreamWriter _this, ReadOnlyMemory<char> source,
- char[] charBuffer, int charPos, int charLen, char[] coreNewLine,
- bool autoFlush, bool appendNewLine, CancellationToken cancellationToken)
- {
- int copied = 0;
- while (copied < source.Length)
- {
- if (charPos == charLen)
- {
- await _this.FlushAsyncInternal(false, false, charBuffer, charPos, cancellationToken).ConfigureAwait(false);
- Debug.Assert(_this._charPos == 0);
- charPos = 0;
- }
-
- int n = Math.Min(charLen - charPos, source.Length - copied);
- Debug.Assert(n > 0, "StreamWriter::Write(char[], int, int) isn't making progress! This is most likely a race condition in user code.");
-
- source.Span.Slice(copied, n).CopyTo(new Span<char>(charBuffer, charPos, n));
- charPos += n;
- copied += n;
- }
-
- if (appendNewLine)
- {
- for (int i = 0; i < coreNewLine.Length; i++) // Expect 2 iterations, no point calling BlockCopy
- {
- if (charPos == charLen)
- {
- await _this.FlushAsyncInternal(false, false, charBuffer, charPos, cancellationToken).ConfigureAwait(false);
- Debug.Assert(_this._charPos == 0);
- charPos = 0;
- }
-
- charBuffer[charPos] = coreNewLine[i];
- charPos++;
- }
- }
-
- if (autoFlush)
- {
- await _this.FlushAsyncInternal(true, false, charBuffer, charPos, cancellationToken).ConfigureAwait(false);
- Debug.Assert(_this._charPos == 0);
- charPos = 0;
- }
-
- _this._charPos = charPos;
- }
-
- public override Task WriteLineAsync()
- {
- // If we have been inherited into a subclass, the following implementation could be incorrect
- // since it does not call through to Write() which a subclass might have overridden.
- // To be safe we will only use this implementation in cases where we know it is safe to do so,
- // and delegate to our base class (which will call into Write) when we are not sure.
- if (GetType() != typeof(StreamWriter))
- {
- return base.WriteLineAsync();
- }
-
- ThrowIfDisposed();
- CheckAsyncTaskInProgress();
-
- Task task = WriteAsyncInternal(this, ReadOnlyMemory<char>.Empty, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true, cancellationToken: default);
- _asyncWriteTask = task;
-
- return task;
- }
-
-
- public override Task WriteLineAsync(char value)
- {
- // If we have been inherited into a subclass, the following implementation could be incorrect
- // since it does not call through to Write() which a subclass might have overridden.
- // To be safe we will only use this implementation in cases where we know it is safe to do so,
- // and delegate to our base class (which will call into Write) when we are not sure.
- if (GetType() != typeof(StreamWriter))
- {
- return base.WriteLineAsync(value);
- }
-
- ThrowIfDisposed();
- CheckAsyncTaskInProgress();
-
- Task task = WriteAsyncInternal(this, value, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true);
- _asyncWriteTask = task;
-
- return task;
- }
-
-
- public override Task WriteLineAsync(string? value)
- {
- if (value == null)
- {
- return WriteLineAsync();
- }
-
- // If we have been inherited into a subclass, the following implementation could be incorrect
- // since it does not call through to Write() which a subclass might have overridden.
- // To be safe we will only use this implementation in cases where we know it is safe to do so,
- // and delegate to our base class (which will call into Write) when we are not sure.
- if (GetType() != typeof(StreamWriter))
- {
- return base.WriteLineAsync(value);
- }
-
- ThrowIfDisposed();
- CheckAsyncTaskInProgress();
-
- Task task = WriteAsyncInternal(this, value, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true);
- _asyncWriteTask = task;
-
- return task;
- }
-
-
- public override Task WriteLineAsync(char[] buffer, int index, int count)
- {
- if (buffer == null)
- {
- throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
- }
- if (index < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (count < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (buffer.Length - index < count)
- {
- throw new ArgumentException(SR.Argument_InvalidOffLen);
- }
-
- // If we have been inherited into a subclass, the following implementation could be incorrect
- // since it does not call through to Write() which a subclass might have overridden.
- // To be safe we will only use this implementation in cases where we know it is safe to do so,
- // and delegate to our base class (which will call into Write) when we are not sure.
- if (GetType() != typeof(StreamWriter))
- {
- return base.WriteLineAsync(buffer, index, count);
- }
-
- ThrowIfDisposed();
- CheckAsyncTaskInProgress();
-
- Task task = WriteAsyncInternal(this, new ReadOnlyMemory<char>(buffer, index, count), _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true, cancellationToken: default);
- _asyncWriteTask = task;
-
- return task;
- }
-
- public override Task WriteLineAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default)
- {
- if (GetType() != typeof(StreamWriter))
- {
- return base.WriteLineAsync(buffer, cancellationToken);
- }
-
- ThrowIfDisposed();
- CheckAsyncTaskInProgress();
-
- if (cancellationToken.IsCancellationRequested)
- {
- return Task.FromCanceled(cancellationToken);
- }
-
- Task task = WriteAsyncInternal(this, buffer, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true, cancellationToken: cancellationToken);
- _asyncWriteTask = task;
-
- return task;
- }
-
-
- public override Task FlushAsync()
- {
- // If we have been inherited into a subclass, the following implementation could be incorrect
- // since it does not call through to Flush() which a subclass might have overridden. To be safe
- // we will only use this implementation in cases where we know it is safe to do so,
- // and delegate to our base class (which will call into Flush) when we are not sure.
- if (GetType() != typeof(StreamWriter))
- {
- return base.FlushAsync();
- }
-
- // flushEncoder should be true at the end of the file and if
- // the user explicitly calls Flush (though not if AutoFlush is true).
- // This is required to flush any dangling characters from our UTF-7
- // and UTF-8 encoders.
- ThrowIfDisposed();
- CheckAsyncTaskInProgress();
-
- Task task = FlushAsyncInternal(true, true, _charBuffer, _charPos);
- _asyncWriteTask = task;
-
- return task;
- }
-
- private Task FlushAsyncInternal(bool flushStream, bool flushEncoder,
- char[] sCharBuffer, int sCharPos, CancellationToken cancellationToken = default)
- {
- if (cancellationToken.IsCancellationRequested)
- {
- return Task.FromCanceled(cancellationToken);
- }
-
- // Perf boost for Flush on non-dirty writers.
- if (sCharPos == 0 && !flushStream && !flushEncoder)
- {
- return Task.CompletedTask;
- }
-
- Task flushTask = FlushAsyncInternal(this, flushStream, flushEncoder, sCharBuffer, sCharPos, _haveWrittenPreamble,
- _encoding, _encoder, _byteBuffer, _stream, cancellationToken);
-
- _charPos = 0;
- return flushTask;
- }
-
-
- // We pass in private instance fields of this MarshalByRefObject-derived type as local params
- // to ensure performant access inside the state machine that corresponds this async method.
- private static async Task FlushAsyncInternal(StreamWriter _this, bool flushStream, bool flushEncoder,
- char[] charBuffer, int charPos, bool haveWrittenPreamble,
- Encoding encoding, Encoder encoder, byte[] byteBuffer, Stream stream, CancellationToken cancellationToken)
- {
- if (!haveWrittenPreamble)
- {
- _this._haveWrittenPreamble = true;
- byte[] preamble = encoding.GetPreamble();
- if (preamble.Length > 0)
- {
- await stream.WriteAsync(new ReadOnlyMemory<byte>(preamble), cancellationToken).ConfigureAwait(false);
- }
- }
-
- int count = encoder.GetBytes(charBuffer, 0, charPos, byteBuffer, 0, flushEncoder);
- if (count > 0)
- {
- await stream.WriteAsync(new ReadOnlyMemory<byte>(byteBuffer, 0, count), cancellationToken).ConfigureAwait(false);
- }
-
- // By definition, calling Flush should flush the stream, but this is
- // only necessary if we passed in true for flushStream. The Web
- // Services guys have some perf tests where flushing needlessly hurts.
- if (flushStream)
- {
- await stream.FlushAsync(cancellationToken).ConfigureAwait(false);
- }
- }
-
- private void ThrowIfDisposed()
- {
- if (_disposed)
- {
- ThrowObjectDisposedException();
- }
-
- void ThrowObjectDisposedException() => throw new ObjectDisposedException(GetType().Name, SR.ObjectDisposed_WriterClosed);
- }
- } // class StreamWriter
-} // namespace
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/TextReader.cs b/netcore/System.Private.CoreLib/shared/System/IO/TextReader.cs
deleted file mode 100644
index 50c4d77e03f..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/TextReader.cs
+++ /dev/null
@@ -1,406 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Buffers;
-
-namespace System.IO
-{
- // This abstract base class represents a reader that can read a sequential
- // stream of characters. This is not intended for reading bytes -
- // there are methods on the Stream class to read bytes.
- // A subclass must minimally implement the Peek() and Read() methods.
- //
- // This class is intended for character input, not bytes.
- // There are methods on the Stream class for reading bytes.
- public abstract partial class TextReader : MarshalByRefObject, IDisposable
- {
- public static readonly TextReader Null = new NullTextReader();
-
- protected TextReader() { }
-
- public virtual void Close()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- }
-
- // Returns the next available character without actually reading it from
- // the input stream. The current position of the TextReader is not changed by
- // this operation. The returned value is -1 if no further characters are
- // available.
- //
- // This default method simply returns -1.
- //
- public virtual int Peek()
- {
- return -1;
- }
-
- // Reads the next character from the input stream. The returned value is
- // -1 if no further characters are available.
- //
- // This default method simply returns -1.
- //
- public virtual int Read()
- {
- return -1;
- }
-
- // Reads a block of characters. This method will read up to
- // count characters from this TextReader into the
- // buffer character array starting at position
- // index. Returns the actual number of characters read.
- //
- public virtual int Read(char[] buffer, int index, int count)
- {
- if (buffer == null)
- {
- throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
- }
- if (index < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (count < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (buffer.Length - index < count)
- {
- throw new ArgumentException(SR.Argument_InvalidOffLen);
- }
-
- int n;
- for (n = 0; n < count; n++)
- {
- int ch = Read();
- if (ch == -1) break;
- buffer[index + n] = (char)ch;
- }
-
- return n;
- }
-
- // Reads a span of characters. This method will read up to
- // count characters from this TextReader into the
- // span of characters Returns the actual number of characters read.
- //
- public virtual int Read(Span<char> buffer)
- {
- char[] array = ArrayPool<char>.Shared.Rent(buffer.Length);
-
- try
- {
- int numRead = Read(array, 0, buffer.Length);
- if ((uint)numRead > (uint)buffer.Length)
- {
- throw new IOException(SR.IO_InvalidReadLength);
- }
- new Span<char>(array, 0, numRead).CopyTo(buffer);
- return numRead;
- }
- finally
- {
- ArrayPool<char>.Shared.Return(array);
- }
- }
-
- // Reads all characters from the current position to the end of the
- // TextReader, and returns them as one string.
- public virtual string ReadToEnd()
- {
- char[] chars = new char[4096];
- int len;
- StringBuilder sb = new StringBuilder(4096);
- while ((len = Read(chars, 0, chars.Length)) != 0)
- {
- sb.Append(chars, 0, len);
- }
- return sb.ToString();
- }
-
- // Blocking version of read. Returns only when count
- // characters have been read or the end of the file was reached.
- //
- public virtual int ReadBlock(char[] buffer, int index, int count)
- {
- int i, n = 0;
- do
- {
- n += (i = Read(buffer, index + n, count - n));
- } while (i > 0 && n < count);
- return n;
- }
-
- // Blocking version of read for span of characters. Returns only when count
- // characters have been read or the end of the file was reached.
- //
- public virtual int ReadBlock(Span<char> buffer)
- {
- char[] array = ArrayPool<char>.Shared.Rent(buffer.Length);
-
- try
- {
- int numRead = ReadBlock(array, 0, buffer.Length);
- if ((uint)numRead > (uint)buffer.Length)
- {
- throw new IOException(SR.IO_InvalidReadLength);
- }
- new Span<char>(array, 0, numRead).CopyTo(buffer);
- return numRead;
- }
- finally
- {
- ArrayPool<char>.Shared.Return(array);
- }
- }
-
- // Reads a line. 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. The resulting string does not
- // contain the terminating carriage return and/or line feed. The returned
- // value is null if the end of the input stream has been reached.
- //
- public virtual string? ReadLine()
- {
- StringBuilder sb = new StringBuilder();
- while (true)
- {
- int ch = Read();
- if (ch == -1) break;
- if (ch == '\r' || ch == '\n')
- {
- if (ch == '\r' && Peek() == '\n')
- {
- Read();
- }
-
- return sb.ToString();
- }
- sb.Append((char)ch);
- }
- if (sb.Length > 0)
- {
- return sb.ToString();
- }
-
- return null;
- }
-
- #region Task based Async APIs
- public virtual Task<string?> ReadLineAsync() =>
- Task<string?>.Factory.StartNew(state => ((TextReader)state!).ReadLine(), this,
- CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
-
- public virtual async Task<string> ReadToEndAsync()
- {
- var sb = new StringBuilder(4096);
- char[] chars = ArrayPool<char>.Shared.Rent(4096);
- try
- {
- int len;
- while ((len = await ReadAsyncInternal(chars, default).ConfigureAwait(false)) != 0)
- {
- sb.Append(chars, 0, len);
- }
- }
- finally
- {
- ArrayPool<char>.Shared.Return(chars);
- }
- return sb.ToString();
- }
-
- public virtual Task<int> ReadAsync(char[] buffer, int index, int count)
- {
- if (buffer == null)
- {
- throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
- }
- if (index < 0 || count < 0)
- {
- throw new ArgumentOutOfRangeException(index < 0 ? nameof(index) : nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (buffer.Length - index < count)
- {
- throw new ArgumentException(SR.Argument_InvalidOffLen);
- }
-
- return ReadAsyncInternal(new Memory<char>(buffer, index, count), default).AsTask();
- }
-
- public virtual ValueTask<int> ReadAsync(Memory<char> buffer, CancellationToken cancellationToken = default) =>
- new ValueTask<int>(MemoryMarshal.TryGetArray(buffer, out ArraySegment<char> array) ?
- ReadAsync(array.Array!, array.Offset, array.Count) :
- Task<int>.Factory.StartNew(state =>
- {
- var t = (Tuple<TextReader, Memory<char>>)state!;
- return t.Item1.Read(t.Item2.Span);
- }, Tuple.Create(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
-
- internal virtual ValueTask<int> ReadAsyncInternal(Memory<char> buffer, CancellationToken cancellationToken)
- {
- var tuple = new Tuple<TextReader, Memory<char>>(this, buffer);
- return new ValueTask<int>(Task<int>.Factory.StartNew(state =>
- {
- var t = (Tuple<TextReader, Memory<char>>)state!;
- return t.Item1.Read(t.Item2.Span);
- },
- tuple, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
- }
-
- public virtual Task<int> ReadBlockAsync(char[] buffer, int index, int count)
- {
- if (buffer == null)
- {
- throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
- }
- if (index < 0 || count < 0)
- {
- throw new ArgumentOutOfRangeException(index < 0 ? nameof(index) : nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (buffer.Length - index < count)
- {
- throw new ArgumentException(SR.Argument_InvalidOffLen);
- }
-
- return ReadBlockAsyncInternal(new Memory<char>(buffer, index, count), default).AsTask();
- }
-
- public virtual ValueTask<int> ReadBlockAsync(Memory<char> buffer, CancellationToken cancellationToken = default) =>
- new ValueTask<int>(MemoryMarshal.TryGetArray(buffer, out ArraySegment<char> array) ?
- ReadBlockAsync(array.Array!, array.Offset, array.Count) :
- Task<int>.Factory.StartNew(state =>
- {
- var t = (Tuple<TextReader, Memory<char>>)state!;
- return t.Item1.ReadBlock(t.Item2.Span);
- }, Tuple.Create(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
-
- internal async ValueTask<int> ReadBlockAsyncInternal(Memory<char> buffer, CancellationToken cancellationToken)
- {
- int n = 0, i;
- do
- {
- i = await ReadAsyncInternal(buffer.Slice(n), cancellationToken).ConfigureAwait(false);
- n += i;
- } while (i > 0 && n < buffer.Length);
-
- return n;
- }
- #endregion
-
- private sealed class NullTextReader : TextReader
- {
- public NullTextReader() { }
-
- public override int Read(char[] buffer, int index, int count)
- {
- return 0;
- }
-
- public override string? ReadLine()
- {
- return null;
- }
- }
-
- public static TextReader Synchronized(TextReader reader)
- {
- if (reader == null)
- throw new ArgumentNullException(nameof(reader));
-
- return reader is SyncTextReader ? reader : new SyncTextReader(reader);
- }
-
- internal sealed class SyncTextReader : TextReader
- {
- internal readonly TextReader _in;
-
- internal SyncTextReader(TextReader t)
- {
- _in = t;
- }
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void Close() => _in.Close();
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- protected override void Dispose(bool disposing)
- {
- // Explicitly pick up a potentially methodimpl'ed Dispose
- if (disposing)
- ((IDisposable)_in).Dispose();
- }
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override int Peek() => _in.Peek();
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override int Read() => _in.Read();
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override int Read(char[] buffer, int index, int count) => _in.Read(buffer, index, count);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override int ReadBlock(char[] buffer, int index, int count) => _in.ReadBlock(buffer, index, count);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override string? ReadLine() => _in.ReadLine();
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override string ReadToEnd() => _in.ReadToEnd();
-
- //
- // On SyncTextReader all APIs should run synchronously, even the async ones.
- //
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override Task<string?> ReadLineAsync() => Task.FromResult(ReadLine());
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override Task<string> ReadToEndAsync() => Task.FromResult(ReadToEnd());
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override Task<int> ReadBlockAsync(char[] buffer, int index, int count)
- {
- if (buffer == null)
- throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
- if (index < 0 || count < 0)
- throw new ArgumentOutOfRangeException(index < 0 ? nameof(index) : nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (buffer.Length - index < count)
- throw new ArgumentException(SR.Argument_InvalidOffLen);
-
- return Task.FromResult(ReadBlock(buffer, index, count));
- }
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override Task<int> ReadAsync(char[] buffer, int index, int count)
- {
- if (buffer == null)
- throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
- if (index < 0 || count < 0)
- throw new ArgumentOutOfRangeException(index < 0 ? nameof(index) : nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (buffer.Length - index < count)
- throw new ArgumentException(SR.Argument_InvalidOffLen);
-
- return Task.FromResult(Read(buffer, index, count));
- }
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/TextWriter.cs b/netcore/System.Private.CoreLib/shared/System/IO/TextWriter.cs
deleted file mode 100644
index 8263072b14e..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/TextWriter.cs
+++ /dev/null
@@ -1,1016 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Text;
-using System.Threading;
-using System.Globalization;
-using System.Threading.Tasks;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Buffers;
-using System.Diagnostics.CodeAnalysis;
-
-namespace System.IO
-{
- // This abstract base class represents a writer that can write a sequential
- // stream of characters. A subclass must minimally implement the
- // Write(char) method.
- //
- // This class is intended for character output, not bytes.
- // There are methods on the Stream class for writing bytes.
- public abstract partial class TextWriter : MarshalByRefObject, IDisposable, IAsyncDisposable
- {
- public static readonly TextWriter Null = new NullTextWriter();
-
- // We don't want to allocate on every TextWriter creation, so cache the char array.
- private static readonly char[] s_coreNewLine = Environment.NewLineConst.ToCharArray();
-
- /// <summary>
- /// This is the 'NewLine' property expressed as a char[].
- /// It is exposed to subclasses as a protected field for read-only
- /// purposes. You should only modify it by using the 'NewLine' property.
- /// In particular you should never modify the elements of the array
- /// as they are shared among many instances of TextWriter.
- /// </summary>
- protected char[] CoreNewLine = s_coreNewLine;
- private string CoreNewLineStr = Environment.NewLineConst;
-
- // Can be null - if so, ask for the Thread's CurrentCulture every time.
- private readonly IFormatProvider? _internalFormatProvider;
-
- protected TextWriter()
- {
- _internalFormatProvider = null; // Ask for CurrentCulture all the time.
- }
-
- protected TextWriter(IFormatProvider? formatProvider)
- {
- _internalFormatProvider = formatProvider;
- }
-
- public virtual IFormatProvider FormatProvider
- {
- get
- {
- if (_internalFormatProvider == null)
- {
- return CultureInfo.CurrentCulture;
- }
- else
- {
- return _internalFormatProvider;
- }
- }
- }
-
- public virtual void Close()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- }
-
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- public virtual ValueTask DisposeAsync()
- {
- try
- {
- Dispose();
- return default;
- }
- catch (Exception exc)
- {
- return new ValueTask(Task.FromException(exc));
- }
- }
-
- // Clears all buffers for this TextWriter and causes any buffered data to be
- // written to the underlying device. This default method is empty, but
- // descendant classes can override the method to provide the appropriate
- // functionality.
- public virtual void Flush()
- {
- }
-
- public abstract Encoding Encoding
- {
- get;
- }
-
- /// <summary>
- /// Returns the line terminator string used by this TextWriter. The default line
- /// terminator string is Environment.NewLine, which is platform specific.
- /// On Windows this is a carriage return followed by a line feed ("\r\n").
- /// On OSX and Linux this is a line feed ("\n").
- /// </summary>
- /// <remarks>
- /// The line terminator string is written to the text stream whenever one of the
- /// WriteLine methods are called. In order for text written by
- /// the TextWriter to be readable by a TextReader, only one of the following line
- /// terminator strings should be used: "\r", "\n", or "\r\n".
- /// </remarks>
- [AllowNull]
- public virtual string NewLine
- {
- get => CoreNewLineStr;
- set
- {
- if (value == null)
- {
- value = Environment.NewLineConst;
- }
-
- CoreNewLineStr = value;
- CoreNewLine = value.ToCharArray();
- }
- }
-
- // Writes a character to the text stream. This default method is empty,
- // but descendant classes can override the method to provide the
- // appropriate functionality.
- //
- public virtual void Write(char value)
- {
- }
-
- // Writes a character array to the text stream. This default method calls
- // Write(char) for each of the characters in the character array.
- // If the character array is null, nothing is written.
- //
- public virtual void Write(char[]? buffer)
- {
- if (buffer != null)
- {
- Write(buffer, 0, buffer.Length);
- }
- }
-
- // Writes a range of a character array to the text stream. This method will
- // write count characters of data into this TextWriter from the
- // buffer character array starting at position index.
- //
- public virtual void Write(char[] buffer, int index, int count)
- {
- if (buffer == null)
- {
- throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
- }
- if (index < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (count < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (buffer.Length - index < count)
- {
- throw new ArgumentException(SR.Argument_InvalidOffLen);
- }
-
- for (int i = 0; i < count; i++) Write(buffer[index + i]);
- }
-
- // Writes a span of characters to the text stream.
- //
- public virtual void Write(ReadOnlySpan<char> buffer)
- {
- char[] array = ArrayPool<char>.Shared.Rent(buffer.Length);
-
- try
- {
- buffer.CopyTo(new Span<char>(array));
- Write(array, 0, buffer.Length);
- }
- finally
- {
- ArrayPool<char>.Shared.Return(array);
- }
- }
-
- // Writes the text representation of a boolean to the text stream. This
- // method outputs either bool.TrueString or bool.FalseString.
- //
- public virtual void Write(bool value)
- {
- Write(value ? "True" : "False");
- }
-
- // Writes the text representation of an integer to the text stream. The
- // text representation of the given value is produced by calling the
- // int.ToString() method.
- //
- public virtual void Write(int value)
- {
- Write(value.ToString(FormatProvider));
- }
-
- // Writes the text representation of an integer to the text stream. The
- // text representation of the given value is produced by calling the
- // uint.ToString() method.
- //
- [CLSCompliant(false)]
- public virtual void Write(uint value)
- {
- Write(value.ToString(FormatProvider));
- }
-
- // Writes the text representation of a long to the text stream. The
- // text representation of the given value is produced by calling the
- // long.ToString() method.
- //
- public virtual void Write(long value)
- {
- Write(value.ToString(FormatProvider));
- }
-
- // Writes the text representation of an unsigned long to the text
- // stream. The text representation of the given value is produced
- // by calling the ulong.ToString() method.
- //
- [CLSCompliant(false)]
- public virtual void Write(ulong value)
- {
- Write(value.ToString(FormatProvider));
- }
-
- // Writes the text representation of a float to the text stream. The
- // text representation of the given value is produced by calling the
- // float.ToString(float) method.
- //
- public virtual void Write(float value)
- {
- Write(value.ToString(FormatProvider));
- }
-
- // Writes the text representation of a double to the text stream. The
- // text representation of the given value is produced by calling the
- // double.ToString(double) method.
- //
- public virtual void Write(double value)
- {
- Write(value.ToString(FormatProvider));
- }
-
- public virtual void Write(decimal value)
- {
- Write(value.ToString(FormatProvider));
- }
-
- // Writes a string to the text stream. If the given string is null, nothing
- // is written to the text stream.
- //
- public virtual void Write(string? value)
- {
- if (value != null)
- {
- Write(value.ToCharArray());
- }
- }
-
- // Writes the text representation of an object to the text stream. If the
- // given object is null, nothing is written to the text stream.
- // Otherwise, the object's ToString method is called to produce the
- // string representation, and the resulting string is then written to the
- // output stream.
- //
- public virtual void Write(object? value)
- {
- if (value != null)
- {
- if (value is IFormattable f)
- {
- Write(f.ToString(null, FormatProvider));
- }
- else
- Write(value.ToString());
- }
- }
-
- /// <summary>
- /// Equivalent to Write(stringBuilder.ToString()) however it uses the
- /// StringBuilder.GetChunks() method to avoid creating the intermediate string
- /// </summary>
- /// <param name="value">The string (as a StringBuilder) to write to the stream</param>
- public virtual void Write(StringBuilder? value)
- {
- if (value != null)
- {
- foreach (ReadOnlyMemory<char> chunk in value.GetChunks())
- Write(chunk.Span);
- }
- }
-
- // Writes out a formatted string. Uses the same semantics as
- // string.Format.
- //
- public virtual void Write(string format, object? arg0)
- {
- Write(string.Format(FormatProvider, format, arg0));
- }
-
- // Writes out a formatted string. Uses the same semantics as
- // string.Format.
- //
- public virtual void Write(string format, object? arg0, object? arg1)
- {
- Write(string.Format(FormatProvider, format, arg0, arg1));
- }
-
- // Writes out a formatted string. Uses the same semantics as
- // string.Format.
- //
- public virtual void Write(string format, object? arg0, object? arg1, object? arg2)
- {
- Write(string.Format(FormatProvider, format, arg0, arg1, arg2));
- }
-
- // Writes out a formatted string. Uses the same semantics as
- // string.Format.
- //
- public virtual void Write(string format, params object?[] arg)
- {
- Write(string.Format(FormatProvider, format, arg));
- }
-
- // Writes a line terminator to the text stream. The default line terminator
- // is Environment.NewLine, but this value can be changed by setting the NewLine property.
- //
- public virtual void WriteLine()
- {
- Write(CoreNewLine);
- }
-
- // Writes a character followed by a line terminator to the text stream.
- //
- public virtual void WriteLine(char value)
- {
- Write(value);
- WriteLine();
- }
-
- // Writes an array of characters followed by a line terminator to the text
- // stream.
- //
- public virtual void WriteLine(char[]? buffer)
- {
- Write(buffer);
- WriteLine();
- }
-
- // Writes a range of a character array followed by a line terminator to the
- // text stream.
- //
- public virtual void WriteLine(char[] buffer, int index, int count)
- {
- Write(buffer, index, count);
- WriteLine();
- }
-
- public virtual void WriteLine(ReadOnlySpan<char> buffer)
- {
- char[] array = ArrayPool<char>.Shared.Rent(buffer.Length);
-
- try
- {
- buffer.CopyTo(new Span<char>(array));
- WriteLine(array, 0, buffer.Length);
- }
- finally
- {
- ArrayPool<char>.Shared.Return(array);
- }
- }
-
- // Writes the text representation of a boolean followed by a line
- // terminator to the text stream.
- //
- public virtual void WriteLine(bool value)
- {
- Write(value);
- WriteLine();
- }
-
- // Writes the text representation of an integer followed by a line
- // terminator to the text stream.
- //
- public virtual void WriteLine(int value)
- {
- Write(value);
- WriteLine();
- }
-
- // Writes the text representation of an unsigned integer followed by
- // a line terminator to the text stream.
- //
- [CLSCompliant(false)]
- public virtual void WriteLine(uint value)
- {
- Write(value);
- WriteLine();
- }
-
- // Writes the text representation of a long followed by a line terminator
- // to the text stream.
- //
- public virtual void WriteLine(long value)
- {
- Write(value);
- WriteLine();
- }
-
- // Writes the text representation of an unsigned long followed by
- // a line terminator to the text stream.
- //
- [CLSCompliant(false)]
- public virtual void WriteLine(ulong value)
- {
- Write(value);
- WriteLine();
- }
-
- // Writes the text representation of a float followed by a line terminator
- // to the text stream.
- //
- public virtual void WriteLine(float value)
- {
- Write(value);
- WriteLine();
- }
-
- // Writes the text representation of a double followed by a line terminator
- // to the text stream.
- //
- public virtual void WriteLine(double value)
- {
- Write(value);
- WriteLine();
- }
-
- public virtual void WriteLine(decimal value)
- {
- Write(value);
- WriteLine();
- }
-
- // Writes a string followed by a line terminator to the text stream.
- //
- public virtual void WriteLine(string? value)
- {
- if (value != null)
- {
- Write(value);
- }
- Write(CoreNewLineStr);
- }
-
- /// <summary>
- /// Equivalent to WriteLine(stringBuilder.ToString()) however it uses the
- /// StringBuilder.GetChunks() method to avoid creating the intermediate string
- /// </summary>
- public virtual void WriteLine(StringBuilder? value)
- {
- Write(value);
- WriteLine();
- }
-
- // Writes the text representation of an object followed by a line
- // terminator to the text stream.
- //
- public virtual void WriteLine(object? value)
- {
- if (value == null)
- {
- WriteLine();
- }
- else
- {
- // Call WriteLine(value.ToString), not Write(Object), WriteLine().
- // This makes calls to WriteLine(Object) atomic.
- if (value is IFormattable f)
- {
- WriteLine(f.ToString(null, FormatProvider));
- }
- else
- {
- WriteLine(value.ToString());
- }
- }
- }
-
- // Writes out a formatted string and a new line. Uses the same
- // semantics as string.Format.
- //
- public virtual void WriteLine(string format, object? arg0)
- {
- WriteLine(string.Format(FormatProvider, format, arg0));
- }
-
- // Writes out a formatted string and a new line. Uses the same
- // semantics as string.Format.
- //
- public virtual void WriteLine(string format, object? arg0, object? arg1)
- {
- WriteLine(string.Format(FormatProvider, format, arg0, arg1));
- }
-
- // Writes out a formatted string and a new line. Uses the same
- // semantics as string.Format.
- //
- public virtual void WriteLine(string format, object? arg0, object? arg1, object? arg2)
- {
- WriteLine(string.Format(FormatProvider, format, arg0, arg1, arg2));
- }
-
- // Writes out a formatted string and a new line. Uses the same
- // semantics as string.Format.
- //
- public virtual void WriteLine(string format, params object?[] arg)
- {
- WriteLine(string.Format(FormatProvider, format, arg));
- }
-
- #region Task based Async APIs
- public virtual Task WriteAsync(char value)
- {
- var tuple = new Tuple<TextWriter, char>(this, value);
- return Task.Factory.StartNew(state =>
- {
- var t = (Tuple<TextWriter, char>)state!;
- t.Item1.Write(t.Item2);
- },
- tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
- }
-
- public virtual Task WriteAsync(string? value)
- {
- var tuple = new Tuple<TextWriter, string?>(this, value);
- return Task.Factory.StartNew(state =>
- {
- var t = (Tuple<TextWriter, string?>)state!;
- t.Item1.Write(t.Item2);
- },
- tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
- }
-
- /// <summary>
- /// Equivalent to WriteAsync(stringBuilder.ToString()) however it uses the
- /// StringBuilder.GetChunks() method to avoid creating the intermediate string
- /// </summary>
- /// <param name="value">The string (as a StringBuilder) to write to the stream</param>
- public virtual Task WriteAsync(StringBuilder? value, CancellationToken cancellationToken = default)
- {
- return
- cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) :
- value == null ? Task.CompletedTask :
- WriteAsyncCore(value, cancellationToken);
-
- async Task WriteAsyncCore(StringBuilder sb, CancellationToken ct)
- {
- foreach (ReadOnlyMemory<char> chunk in sb.GetChunks())
- {
- await WriteAsync(chunk, ct).ConfigureAwait(false);
- }
- }
- }
-
- public Task WriteAsync(char[]? buffer)
- {
- if (buffer == null)
- {
- return Task.CompletedTask;
- }
-
- return WriteAsync(buffer, 0, buffer.Length);
- }
-
- public virtual Task WriteAsync(char[] buffer, int index, int count)
- {
- var tuple = new Tuple<TextWriter, char[], int, int>(this, buffer, index, count);
- return Task.Factory.StartNew(state =>
- {
- var t = (Tuple<TextWriter, char[], int, int>)state!;
- t.Item1.Write(t.Item2, t.Item3, t.Item4);
- },
- tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
- }
-
- public virtual Task WriteAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default) =>
- cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) :
- MemoryMarshal.TryGetArray(buffer, out ArraySegment<char> array) ?
- WriteAsync(array.Array!, array.Offset, array.Count) :
- Task.Factory.StartNew(state =>
- {
- var t = (Tuple<TextWriter, ReadOnlyMemory<char>>)state!;
- t.Item1.Write(t.Item2.Span);
- }, Tuple.Create(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
-
- public virtual Task WriteLineAsync(char value)
- {
- var tuple = new Tuple<TextWriter, char>(this, value);
- return Task.Factory.StartNew(state =>
- {
- var t = (Tuple<TextWriter, char>)state!;
- t.Item1.WriteLine(t.Item2);
- },
- tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
- }
-
- public virtual Task WriteLineAsync(string? value)
- {
- var tuple = new Tuple<TextWriter, string?>(this, value);
- return Task.Factory.StartNew(state =>
- {
- var t = (Tuple<TextWriter, string?>)state!;
- t.Item1.WriteLine(t.Item2);
- },
- tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
- }
-
- /// <summary>
- /// Equivalent to WriteLineAsync(stringBuilder.ToString()) however it uses the
- /// StringBuilder.GetChunks() method to avoid creating the intermediate string
- /// </summary>
- /// <param name="value">The string (as a StringBuilder) to write to the stream</param>
- public virtual Task WriteLineAsync(StringBuilder? value, CancellationToken cancellationToken = default)
- {
- return
- cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) :
- value == null ? WriteAsync(CoreNewLine, cancellationToken) :
- WriteLineAsyncCore(value, cancellationToken);
-
- async Task WriteLineAsyncCore(StringBuilder sb, CancellationToken ct)
- {
- foreach (ReadOnlyMemory<char> chunk in sb.GetChunks())
- {
- await WriteAsync(chunk, ct).ConfigureAwait(false);
- }
- await WriteAsync(CoreNewLine, ct).ConfigureAwait(false);
- }
- }
-
- public Task WriteLineAsync(char[]? buffer)
- {
- if (buffer == null)
- {
- return WriteLineAsync();
- }
-
- return WriteLineAsync(buffer, 0, buffer.Length);
- }
-
- public virtual Task WriteLineAsync(char[] buffer, int index, int count)
- {
- var tuple = new Tuple<TextWriter, char[], int, int>(this, buffer, index, count);
- return Task.Factory.StartNew(state =>
- {
- var t = (Tuple<TextWriter, char[], int, int>)state!;
- t.Item1.WriteLine(t.Item2, t.Item3, t.Item4);
- },
- tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
- }
-
- public virtual Task WriteLineAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default) =>
- cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) :
- MemoryMarshal.TryGetArray(buffer, out ArraySegment<char> array) ?
- WriteLineAsync(array.Array!, array.Offset, array.Count) :
- Task.Factory.StartNew(state =>
- {
- var t = (Tuple<TextWriter, ReadOnlyMemory<char>>)state!;
- t.Item1.WriteLine(t.Item2.Span);
- }, Tuple.Create(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
-
- public virtual Task WriteLineAsync()
- {
- return WriteAsync(CoreNewLine);
- }
-
- public virtual Task FlushAsync()
- {
- return Task.Factory.StartNew(state => ((TextWriter)state!).Flush(), this,
- CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
- }
- #endregion
-
- private sealed class NullTextWriter : TextWriter
- {
- internal NullTextWriter() : base(CultureInfo.InvariantCulture)
- {
- }
-
- public override Encoding Encoding => Encoding.Unicode;
-
- public override void Write(char[] buffer, int index, int count)
- {
- }
-
- public override void Write(string? value)
- {
- }
-
- // Not strictly necessary, but for perf reasons
- public override void WriteLine()
- {
- }
-
- // Not strictly necessary, but for perf reasons
- public override void WriteLine(string? value)
- {
- }
-
- public override void WriteLine(object? value)
- {
- }
-
- public override void Write(char value)
- {
- }
- }
-
- public static TextWriter Synchronized(TextWriter writer)
- {
- if (writer == null)
- throw new ArgumentNullException(nameof(writer));
-
- return writer is SyncTextWriter ? writer : new SyncTextWriter(writer);
- }
-
- internal sealed class SyncTextWriter : TextWriter, IDisposable
- {
- private readonly TextWriter _out;
-
- internal SyncTextWriter(TextWriter t) : base(t.FormatProvider)
- {
- _out = t;
- }
-
- public override Encoding Encoding => _out.Encoding;
-
- public override IFormatProvider FormatProvider => _out.FormatProvider;
-
- [AllowNull]
- public override string NewLine
- {
- [MethodImpl(MethodImplOptions.Synchronized)]
- get => _out.NewLine;
- [MethodImpl(MethodImplOptions.Synchronized)]
- set => _out.NewLine = value;
- }
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void Close() => _out.Close();
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- protected override void Dispose(bool disposing)
- {
- // Explicitly pick up a potentially methodimpl'ed Dispose
- if (disposing)
- ((IDisposable)_out).Dispose();
- }
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void Flush() => _out.Flush();
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void Write(char value) => _out.Write(value);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void Write(char[]? buffer) => _out.Write(buffer);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void Write(char[] buffer, int index, int count) => _out.Write(buffer, index, count);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void Write(ReadOnlySpan<char> buffer) => _out.Write(buffer);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void Write(bool value) => _out.Write(value);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void Write(int value) => _out.Write(value);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void Write(uint value) => _out.Write(value);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void Write(long value) => _out.Write(value);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void Write(ulong value) => _out.Write(value);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void Write(float value) => _out.Write(value);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void Write(double value) => _out.Write(value);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void Write(decimal value) => _out.Write(value);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void Write(string? value) => _out.Write(value);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void Write(StringBuilder? value) => _out.Write(value);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void Write(object? value) => _out.Write(value);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void Write(string format, object? arg0) => _out.Write(format, arg0);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void Write(string format, object? arg0, object? arg1) => _out.Write(format, arg0, arg1);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void Write(string format, object? arg0, object? arg1, object? arg2) => _out.Write(format, arg0, arg1, arg2);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void Write(string format, object?[] arg) => _out.Write(format, arg);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void WriteLine() => _out.WriteLine();
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void WriteLine(char value) => _out.WriteLine(value);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void WriteLine(decimal value) => _out.WriteLine(value);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void WriteLine(char[]? buffer) => _out.WriteLine(buffer);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void WriteLine(char[] buffer, int index, int count) => _out.WriteLine(buffer, index, count);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void WriteLine(ReadOnlySpan<char> buffer) => _out.WriteLine(buffer);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void WriteLine(bool value) => _out.WriteLine(value);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void WriteLine(int value) => _out.WriteLine(value);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void WriteLine(uint value) => _out.WriteLine(value);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void WriteLine(long value) => _out.WriteLine(value);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void WriteLine(ulong value) => _out.WriteLine(value);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void WriteLine(float value) => _out.WriteLine(value);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void WriteLine(double value) => _out.WriteLine(value);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void WriteLine(string? value) => _out.WriteLine(value);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void WriteLine(StringBuilder? value) => _out.WriteLine(value);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void WriteLine(object? value) => _out.WriteLine(value);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void WriteLine(string format, object? arg0) => _out.WriteLine(format, arg0);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void WriteLine(string format, object? arg0, object? arg1) => _out.WriteLine(format, arg0, arg1);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void WriteLine(string format, object? arg0, object? arg1, object? arg2) => _out.WriteLine(format, arg0, arg1, arg2);
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override void WriteLine(string format, object?[] arg) => _out.WriteLine(format, arg);
-
- //
- // On SyncTextWriter all APIs should run synchronously, even the async ones.
- //
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override ValueTask DisposeAsync()
- {
- Dispose();
- return default;
- }
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override Task WriteAsync(char value)
- {
- Write(value);
- return Task.CompletedTask;
- }
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override Task WriteAsync(string? value)
- {
- Write(value);
- return Task.CompletedTask;
- }
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override Task WriteAsync(StringBuilder? value, CancellationToken cancellationToken = default)
- {
- if (cancellationToken.IsCancellationRequested)
- {
- return Task.FromCanceled(cancellationToken);
- }
-
- Write(value);
- return Task.CompletedTask;
- }
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override Task WriteAsync(char[] buffer, int index, int count)
- {
- Write(buffer, index, count);
- return Task.CompletedTask;
- }
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override Task WriteAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default)
- {
- if (cancellationToken.IsCancellationRequested)
- {
- return Task.FromCanceled(cancellationToken);
- }
-
- Write(buffer.Span);
- return Task.CompletedTask;
- }
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override Task WriteLineAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default)
- {
- if (cancellationToken.IsCancellationRequested)
- {
- return Task.FromCanceled(cancellationToken);
- }
-
- WriteLine(buffer.Span);
- return Task.CompletedTask;
- }
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override Task WriteLineAsync(char value)
- {
- WriteLine(value);
- return Task.CompletedTask;
- }
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override Task WriteLineAsync()
- {
- WriteLine();
- return Task.CompletedTask;
- }
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override Task WriteLineAsync(string? value)
- {
- WriteLine(value);
- return Task.CompletedTask;
- }
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override Task WriteLineAsync(StringBuilder? value, CancellationToken cancellationToken = default)
- {
- if (cancellationToken.IsCancellationRequested)
- {
- return Task.FromCanceled(cancellationToken);
- }
-
- WriteLine(value);
- return Task.CompletedTask;
- }
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override Task WriteLineAsync(char[] buffer, int index, int count)
- {
- WriteLine(buffer, index, count);
- return Task.CompletedTask;
- }
-
- [MethodImpl(MethodImplOptions.Synchronized)]
- public override Task FlushAsync()
- {
- Flush();
- return Task.CompletedTask;
- }
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryAccessor.cs b/netcore/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryAccessor.cs
deleted file mode 100644
index a2c6bd1764f..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryAccessor.cs
+++ /dev/null
@@ -1,668 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-/*============================================================
-**
-**
-**
-**
-** Purpose: Provides a fast, AV free, cross-language way of
-** accessing unmanaged memory in a random fashion.
-**
-**
-===========================================================*/
-
-using System.Runtime.InteropServices;
-using Internal.Runtime.CompilerServices;
-
-namespace System.IO
-{
- /// Perf notes: ReadXXX, WriteXXX (for basic types) acquire and release the
- /// SafeBuffer pointer rather than relying on generic Read(T) from SafeBuffer because
- /// this gives better throughput; benchmarks showed about 12-15% better.
- public class UnmanagedMemoryAccessor : IDisposable
- {
- private SafeBuffer _buffer = null!; // initialized in helper called by ctor
- private long _offset;
- private long _capacity;
- private FileAccess _access;
- private bool _isOpen;
- private bool _canRead;
- private bool _canWrite;
-
- protected UnmanagedMemoryAccessor()
- {
- _isOpen = false;
- }
-
- public UnmanagedMemoryAccessor(SafeBuffer buffer, long offset, long capacity)
- {
- Initialize(buffer, offset, capacity, FileAccess.Read);
- }
-
- public UnmanagedMemoryAccessor(SafeBuffer buffer, long offset, long capacity, FileAccess access)
- {
- Initialize(buffer, offset, capacity, access);
- }
-
- protected void Initialize(SafeBuffer buffer, long offset, long capacity, FileAccess access)
- {
- if (buffer == null)
- {
- throw new ArgumentNullException(nameof(buffer));
- }
- if (offset < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (capacity < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (buffer.ByteLength < (ulong)(offset + capacity))
- {
- throw new ArgumentException(SR.Argument_OffsetAndCapacityOutOfBounds);
- }
- if (access < FileAccess.Read || access > FileAccess.ReadWrite)
- {
- throw new ArgumentOutOfRangeException(nameof(access));
- }
-
- if (_isOpen)
- {
- throw new InvalidOperationException(SR.InvalidOperation_CalledTwice);
- }
-
- unsafe
- {
- byte* pointer = null;
-
- try
- {
- buffer.AcquirePointer(ref pointer);
- if (((byte*)((long)pointer + offset + capacity)) < pointer)
- {
- throw new ArgumentException(SR.Argument_UnmanagedMemAccessorWrapAround);
- }
- }
- finally
- {
- if (pointer != null)
- {
- buffer.ReleasePointer();
- }
- }
- }
-
- _offset = offset;
- _buffer = buffer;
- _capacity = capacity;
- _access = access;
- _isOpen = true;
- _canRead = (_access & FileAccess.Read) != 0;
- _canWrite = (_access & FileAccess.Write) != 0;
- }
-
- public long Capacity => _capacity;
-
- public bool CanRead => _isOpen && _canRead;
-
- public bool CanWrite => _isOpen && _canWrite;
-
- protected virtual void Dispose(bool disposing)
- {
- _isOpen = false;
- }
-
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- protected bool IsOpen => _isOpen;
-
- // ************** Read Methods ****************/
-
- public bool ReadBoolean(long position) => ReadByte(position) != 0;
-
- public byte ReadByte(long position)
- {
- EnsureSafeToRead(position, sizeof(byte));
-
- byte result;
- unsafe
- {
- byte* pointer = null;
-
- try
- {
- _buffer.AcquirePointer(ref pointer);
- result = *((byte*)(pointer + _offset + position));
- }
- finally
- {
- if (pointer != null)
- {
- _buffer.ReleasePointer();
- }
- }
- }
- return result;
- }
-
- public char ReadChar(long position) => unchecked((char)ReadInt16(position));
-
- public short ReadInt16(long position)
- {
- EnsureSafeToRead(position, sizeof(short));
-
- short result;
- unsafe
- {
- byte* pointer = null;
-
- try
- {
- _buffer.AcquirePointer(ref pointer);
- result = Unsafe.ReadUnaligned<short>(pointer + _offset + position);
- }
- finally
- {
- if (pointer != null)
- {
- _buffer.ReleasePointer();
- }
- }
- }
- return result;
- }
-
- public int ReadInt32(long position)
- {
- EnsureSafeToRead(position, sizeof(int));
-
- int result;
- unsafe
- {
- byte* pointer = null;
-
- try
- {
- _buffer.AcquirePointer(ref pointer);
- result = Unsafe.ReadUnaligned<int>(pointer + _offset + position);
- }
- finally
- {
- if (pointer != null)
- {
- _buffer.ReleasePointer();
- }
- }
- }
- return result;
- }
-
- public long ReadInt64(long position)
- {
- EnsureSafeToRead(position, sizeof(long));
-
- long result;
- unsafe
- {
- byte* pointer = null;
-
- try
- {
- _buffer.AcquirePointer(ref pointer);
- result = Unsafe.ReadUnaligned<long>(pointer + _offset + position);
- }
- finally
- {
- if (pointer != null)
- {
- _buffer.ReleasePointer();
- }
- }
- }
- return result;
- }
-
- public decimal ReadDecimal(long position)
- {
- const int ScaleMask = 0x00FF0000;
- const int SignMask = unchecked((int)0x80000000);
-
- EnsureSafeToRead(position, sizeof(decimal));
-
- int lo, mid, hi, flags;
-
- unsafe
- {
- byte* pointer = null;
- try
- {
- _buffer.AcquirePointer(ref pointer);
- pointer += (_offset + position);
-
- lo = Unsafe.ReadUnaligned<int>(pointer);
- mid = Unsafe.ReadUnaligned<int>(pointer + 4);
- hi = Unsafe.ReadUnaligned<int>(pointer + 8);
- flags = Unsafe.ReadUnaligned<int>(pointer + 12);
- }
- finally
- {
- if (pointer != null)
- {
- _buffer.ReleasePointer();
- }
- }
- }
-
- // Check for invalid Decimal values
- if (!((flags & ~(SignMask | ScaleMask)) == 0 && (flags & ScaleMask) <= (28 << 16)))
- {
- throw new ArgumentException(SR.Arg_BadDecimal); // Throw same Exception type as Decimal(int[]) ctor for compat
- }
-
- bool isNegative = (flags & SignMask) != 0;
- byte scale = (byte)(flags >> 16);
-
- return new decimal(lo, mid, hi, isNegative, scale);
- }
-
- public float ReadSingle(long position) => BitConverter.Int32BitsToSingle(ReadInt32(position));
-
- public double ReadDouble(long position) => BitConverter.Int64BitsToDouble(ReadInt64(position));
-
- [CLSCompliant(false)]
- public sbyte ReadSByte(long position) => unchecked((sbyte)ReadByte(position));
-
- [CLSCompliant(false)]
- public ushort ReadUInt16(long position) => unchecked((ushort)ReadInt16(position));
-
- [CLSCompliant(false)]
- public uint ReadUInt32(long position) => unchecked((uint)ReadInt32(position));
-
- [CLSCompliant(false)]
- public ulong ReadUInt64(long position) => unchecked((ulong)ReadInt64(position));
-
- // Reads a struct of type T from unmanaged memory, into the reference pointed to by ref value.
- // Note: this method is not safe, since it overwrites the contents of a structure, it can be
- // used to modify the private members of a struct.
- // This method is most performant when used with medium to large sized structs
- // (larger than 8 bytes -- though this is number is JIT and architecture dependent). As
- // such, it is best to use the ReadXXX methods for small standard types such as ints, longs,
- // bools, etc.
- public void Read<T>(long position, out T structure) where T : struct
- {
- if (position < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
-
- if (!_isOpen)
- {
- throw new ObjectDisposedException(nameof(UnmanagedMemoryAccessor), SR.ObjectDisposed_ViewAccessorClosed);
- }
- if (!_canRead)
- {
- throw new NotSupportedException(SR.NotSupported_Reading);
- }
-
- uint sizeOfT = SafeBuffer.SizeOf<T>();
- if (position > _capacity - sizeOfT)
- {
- if (position >= _capacity)
- {
- throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_PositionLessThanCapacityRequired);
- }
- else
- {
- throw new ArgumentException(SR.Format(SR.Argument_NotEnoughBytesToRead, typeof(T)), nameof(position));
- }
- }
-
- structure = _buffer.Read<T>((ulong)(_offset + position));
- }
-
- // Reads 'count' structs of type T from unmanaged memory, into 'array' starting at 'offset'.
- // Note: this method is not safe, since it overwrites the contents of structures, it can
- // be used to modify the private members of a struct.
- public int ReadArray<T>(long position, T[] array, int offset, int count) where T : struct
- {
- if (array == null)
- {
- throw new ArgumentNullException(nameof(array), SR.ArgumentNull_Buffer);
- }
- if (offset < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (count < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (array.Length - offset < count)
- {
- throw new ArgumentException(SR.Argument_InvalidOffLen);
- }
- if (!_isOpen)
- {
- throw new ObjectDisposedException(nameof(UnmanagedMemoryAccessor), SR.ObjectDisposed_ViewAccessorClosed);
- }
- if (!_canRead)
- {
- throw new NotSupportedException(SR.NotSupported_Reading);
- }
- if (position < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
-
- uint sizeOfT = SafeBuffer.AlignedSizeOf<T>();
-
- // only check position and ask for fewer Ts if count is too big
- if (position >= _capacity)
- {
- throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_PositionLessThanCapacityRequired);
- }
-
- int n = count;
- long spaceLeft = _capacity - position;
- if (spaceLeft < 0)
- {
- n = 0;
- }
- else
- {
- ulong spaceNeeded = (ulong)(sizeOfT * count);
- if ((ulong)spaceLeft < spaceNeeded)
- {
- n = (int)(spaceLeft / sizeOfT);
- }
- }
-
- _buffer.ReadArray<T>((ulong)(_offset + position), array, offset, n);
-
- return n;
- }
-
- // ************** Write Methods ****************/
-
- public void Write(long position, bool value) => Write(position, (byte)(value ? 1 : 0));
-
- public void Write(long position, byte value)
- {
- EnsureSafeToWrite(position, sizeof(byte));
-
- unsafe
- {
- byte* pointer = null;
-
- try
- {
- _buffer.AcquirePointer(ref pointer);
- *((byte*)(pointer + _offset + position)) = value;
- }
- finally
- {
- if (pointer != null)
- {
- _buffer.ReleasePointer();
- }
- }
- }
- }
-
- public void Write(long position, char value) => Write(position, unchecked((short)value));
-
- public void Write(long position, short value)
- {
- EnsureSafeToWrite(position, sizeof(short));
-
- unsafe
- {
- byte* pointer = null;
-
- try
- {
- _buffer.AcquirePointer(ref pointer);
- Unsafe.WriteUnaligned<short>(pointer + _offset + position, value);
- }
- finally
- {
- if (pointer != null)
- {
- _buffer.ReleasePointer();
- }
- }
- }
- }
-
- public void Write(long position, int value)
- {
- EnsureSafeToWrite(position, sizeof(int));
-
- unsafe
- {
- byte* pointer = null;
-
- try
- {
- _buffer.AcquirePointer(ref pointer);
- Unsafe.WriteUnaligned<int>(pointer + _offset + position, value);
- }
- finally
- {
- if (pointer != null)
- {
- _buffer.ReleasePointer();
- }
- }
- }
- }
-
- public void Write(long position, long value)
- {
- EnsureSafeToWrite(position, sizeof(long));
-
- unsafe
- {
- byte* pointer = null;
-
- try
- {
- _buffer.AcquirePointer(ref pointer);
- Unsafe.WriteUnaligned<long>(pointer + _offset + position, value);
- }
- finally
- {
- if (pointer != null)
- {
- _buffer.ReleasePointer();
- }
- }
- }
- }
-
- public void Write(long position, decimal value)
- {
- EnsureSafeToWrite(position, sizeof(decimal));
-
- unsafe
- {
- int* valuePtr = (int*)(&value);
- int flags = *valuePtr;
- int hi = *(valuePtr + 1);
- int lo = *(valuePtr + 2);
- int mid = *(valuePtr + 3);
-
- byte* pointer = null;
- try
- {
- _buffer.AcquirePointer(ref pointer);
- pointer += (_offset + position);
-
- Unsafe.WriteUnaligned<int>(pointer, lo);
- Unsafe.WriteUnaligned<int>(pointer + 4, mid);
- Unsafe.WriteUnaligned<int>(pointer + 8, hi);
- Unsafe.WriteUnaligned<int>(pointer + 12, flags);
- }
- finally
- {
- if (pointer != null)
- {
- _buffer.ReleasePointer();
- }
- }
- }
- }
-
- public void Write(long position, float value) => Write(position, BitConverter.SingleToInt32Bits(value));
-
- public void Write(long position, double value) => Write(position, BitConverter.DoubleToInt64Bits(value));
-
- [CLSCompliant(false)]
- public void Write(long position, sbyte value) => Write(position, unchecked((byte)value));
-
- [CLSCompliant(false)]
- public void Write(long position, ushort value) => Write(position, unchecked((short)value));
-
- [CLSCompliant(false)]
- public void Write(long position, uint value) => Write(position, unchecked((int)value));
-
- [CLSCompliant(false)]
- public void Write(long position, ulong value) => Write(position, unchecked((long)value));
-
- // Writes the struct pointed to by ref value into unmanaged memory. Note that this method
- // is most performant when used with medium to large sized structs (larger than 8 bytes
- // though this is number is JIT and architecture dependent). As such, it is best to use
- // the WriteX methods for small standard types such as ints, longs, bools, etc.
- public void Write<T>(long position, ref T structure) where T : struct
- {
- if (position < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (!_isOpen)
- {
- throw new ObjectDisposedException(nameof(UnmanagedMemoryAccessor), SR.ObjectDisposed_ViewAccessorClosed);
- }
- if (!_canWrite)
- {
- throw new NotSupportedException(SR.NotSupported_Writing);
- }
-
- uint sizeOfT = SafeBuffer.SizeOf<T>();
- if (position > _capacity - sizeOfT)
- {
- if (position >= _capacity)
- {
- throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_PositionLessThanCapacityRequired);
- }
- else
- {
- throw new ArgumentException(SR.Format(SR.Argument_NotEnoughBytesToWrite, typeof(T)), nameof(position));
- }
- }
-
- _buffer.Write<T>((ulong)(_offset + position), structure);
- }
-
- // Writes 'count' structs of type T from 'array' (starting at 'offset') into unmanaged memory.
- public void WriteArray<T>(long position, T[] array, int offset, int count) where T : struct
- {
- if (array == null)
- {
- throw new ArgumentNullException(nameof(array), SR.ArgumentNull_Buffer);
- }
- if (offset < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (count < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (array.Length - offset < count)
- {
- throw new ArgumentException(SR.Argument_InvalidOffLen);
- }
- if (position < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (position >= Capacity)
- {
- throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_PositionLessThanCapacityRequired);
- }
-
- if (!_isOpen)
- {
- throw new ObjectDisposedException(nameof(UnmanagedMemoryAccessor), SR.ObjectDisposed_ViewAccessorClosed);
- }
- if (!_canWrite)
- {
- throw new NotSupportedException(SR.NotSupported_Writing);
- }
-
- _buffer.WriteArray<T>((ulong)(_offset + position), array, offset, count);
- }
-
- private void EnsureSafeToRead(long position, int sizeOfType)
- {
- if (!_isOpen)
- {
- throw new ObjectDisposedException(nameof(UnmanagedMemoryAccessor), SR.ObjectDisposed_ViewAccessorClosed);
- }
- if (!_canRead)
- {
- throw new NotSupportedException(SR.NotSupported_Reading);
- }
- if (position < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (position > _capacity - sizeOfType)
- {
- if (position >= _capacity)
- {
- throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_PositionLessThanCapacityRequired);
- }
- else
- {
- throw new ArgumentException(SR.Argument_NotEnoughBytesToRead, nameof(position));
- }
- }
- }
-
- private void EnsureSafeToWrite(long position, int sizeOfType)
- {
- if (!_isOpen)
- {
- throw new ObjectDisposedException(nameof(UnmanagedMemoryAccessor), SR.ObjectDisposed_ViewAccessorClosed);
- }
- if (!_canWrite)
- {
- throw new NotSupportedException(SR.NotSupported_Writing);
- }
- if (position < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (position > _capacity - sizeOfType)
- {
- if (position >= _capacity)
- {
- throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_PositionLessThanCapacityRequired);
- }
- else
- {
- throw new ArgumentException(SR.Argument_NotEnoughBytesToWrite, nameof(position));
- }
- }
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStream.cs b/netcore/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStream.cs
deleted file mode 100644
index 4dbd34a95a7..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStream.cs
+++ /dev/null
@@ -1,951 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Buffers;
-using System.Diagnostics;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Threading;
-using System.Threading.Tasks;
-
-#pragma warning disable SA1121 // explicitly using type aliases instead of built-in types
-#if BIT64
-using nuint = System.UInt64;
-#else
-using nuint = System.UInt32;
-#endif
-
-namespace System.IO
-{
- /*
- * This class is used to access a contiguous block of memory, likely outside
- * the GC heap (or pinned in place in the GC heap, but a MemoryStream may
- * make more sense in those cases). It's great if you have a pointer and
- * a length for a section of memory mapped in by someone else and you don't
- * want to copy this into the GC heap. UnmanagedMemoryStream assumes these
- * two things:
- *
- * 1) All the memory in the specified block is readable or writable,
- * depending on the values you pass to the constructor.
- * 2) The lifetime of the block of memory is at least as long as the lifetime
- * of the UnmanagedMemoryStream.
- * 3) You clean up the memory when appropriate. The UnmanagedMemoryStream
- * currently will do NOTHING to free this memory.
- * 4) All calls to Write and WriteByte may not be threadsafe currently.
- *
- * It may become necessary to add in some sort of
- * DeallocationMode enum, specifying whether we unmap a section of memory,
- * call free, run a user-provided delegate to free the memory, etc.
- * We'll suggest user write a subclass of UnmanagedMemoryStream that uses
- * a SafeHandle subclass to hold onto the memory.
- *
- */
-
- /// <summary>
- /// Stream over a memory pointer or over a SafeBuffer
- /// </summary>
- public class UnmanagedMemoryStream : Stream
- {
- private SafeBuffer? _buffer;
- private unsafe byte* _mem;
- private long _length;
- private long _capacity;
- private long _position;
- private long _offset;
- private FileAccess _access;
- private bool _isOpen;
- private Task<int>? _lastReadTask; // The last successful task returned from ReadAsync
-
- /// <summary>
- /// Creates a closed stream.
- /// </summary>
- // Needed for subclasses that need to map a file, etc.
- protected UnmanagedMemoryStream()
- {
- unsafe
- {
- _mem = null;
- }
- _isOpen = false;
- }
-
- /// <summary>
- /// Creates a stream over a SafeBuffer.
- /// </summary>
- /// <param name="buffer"></param>
- /// <param name="offset"></param>
- /// <param name="length"></param>
- public UnmanagedMemoryStream(SafeBuffer buffer, long offset, long length)
- {
- Initialize(buffer, offset, length, FileAccess.Read);
- }
-
- /// <summary>
- /// Creates a stream over a SafeBuffer.
- /// </summary>
- public UnmanagedMemoryStream(SafeBuffer buffer, long offset, long length, FileAccess access)
- {
- Initialize(buffer, offset, length, access);
- }
-
- /// <summary>
- /// Subclasses must call this method (or the other overload) to properly initialize all instance fields.
- /// </summary>
- /// <param name="buffer"></param>
- /// <param name="offset"></param>
- /// <param name="length"></param>
- /// <param name="access"></param>
- protected void Initialize(SafeBuffer buffer, long offset, long length, FileAccess access)
- {
- if (buffer == null)
- {
- throw new ArgumentNullException(nameof(buffer));
- }
- if (offset < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (length < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (buffer.ByteLength < (ulong)(offset + length))
- {
- throw new ArgumentException(SR.Argument_InvalidSafeBufferOffLen);
- }
- if (access < FileAccess.Read || access > FileAccess.ReadWrite)
- {
- throw new ArgumentOutOfRangeException(nameof(access));
- }
-
- if (_isOpen)
- {
- throw new InvalidOperationException(SR.InvalidOperation_CalledTwice);
- }
-
- // check for wraparound
- unsafe
- {
- byte* pointer = null;
- RuntimeHelpers.PrepareConstrainedRegions();
- try
- {
- buffer.AcquirePointer(ref pointer);
- if ((pointer + offset + length) < pointer)
- {
- throw new ArgumentException(SR.ArgumentOutOfRange_UnmanagedMemStreamWrapAround);
- }
- }
- finally
- {
- if (pointer != null)
- {
- buffer.ReleasePointer();
- }
- }
- }
-
- _offset = offset;
- _buffer = buffer;
- _length = length;
- _capacity = length;
- _access = access;
- _isOpen = true;
- }
-
- /// <summary>
- /// Creates a stream over a byte*.
- /// </summary>
- [CLSCompliant(false)]
- public unsafe UnmanagedMemoryStream(byte* pointer, long length)
- {
- Initialize(pointer, length, length, FileAccess.Read);
- }
-
- /// <summary>
- /// Creates a stream over a byte*.
- /// </summary>
- [CLSCompliant(false)]
- public unsafe UnmanagedMemoryStream(byte* pointer, long length, long capacity, FileAccess access)
- {
- Initialize(pointer, length, capacity, access);
- }
-
- /// <summary>
- /// Subclasses must call this method (or the other overload) to properly initialize all instance fields.
- /// </summary>
- [CLSCompliant(false)]
- protected unsafe void Initialize(byte* pointer, long length, long capacity, FileAccess access)
- {
- if (pointer == null)
- throw new ArgumentNullException(nameof(pointer));
- if (length < 0 || capacity < 0)
- throw new ArgumentOutOfRangeException((length < 0) ? nameof(length) : nameof(capacity), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (length > capacity)
- throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_LengthGreaterThanCapacity);
- // Check for wraparound.
- if (((byte*)((long)pointer + capacity)) < pointer)
- throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_UnmanagedMemStreamWrapAround);
- if (access < FileAccess.Read || access > FileAccess.ReadWrite)
- throw new ArgumentOutOfRangeException(nameof(access), SR.ArgumentOutOfRange_Enum);
- if (_isOpen)
- throw new InvalidOperationException(SR.InvalidOperation_CalledTwice);
-
- _mem = pointer;
- _offset = 0;
- _length = length;
- _capacity = capacity;
- _access = access;
- _isOpen = true;
- }
-
- /// <summary>
- /// Returns true if the stream can be read; otherwise returns false.
- /// </summary>
- public override bool CanRead => _isOpen && (_access & FileAccess.Read) != 0;
-
- /// <summary>
- /// Returns true if the stream can seek; otherwise returns false.
- /// </summary>
- public override bool CanSeek => _isOpen;
-
- /// <summary>
- /// Returns true if the stream can be written to; otherwise returns false.
- /// </summary>
- public override bool CanWrite => _isOpen && (_access & FileAccess.Write) != 0;
-
- /// <summary>
- /// Calls the given callback with a span of the memory stream data
- /// </summary>
- /// <param name="callback">the callback to be called</param>
- /// <param name="state">A user-defined state, passed to the callback</param>
- /// <param name="bufferSize">the maximum size of the memory span</param>
- public override void CopyTo(ReadOnlySpanAction<byte, object?> callback, object? state, int bufferSize)
- {
- // If we have been inherited into a subclass, the following implementation could be incorrect
- // since it does not call through to Read() which a subclass might have overridden.
- // To be safe we will only use this implementation in cases where we know it is safe to do so,
- // and delegate to our base class (which will call into Read) when we are not sure.
- if (GetType() != typeof(UnmanagedMemoryStream))
- {
- base.CopyTo(callback, state, bufferSize);
- return;
- }
-
- if (callback == null) throw new ArgumentNullException(nameof(callback));
-
- EnsureNotClosed();
- EnsureReadable();
-
- // Use a local variable to avoid a race where another thread
- // changes our position after we decide we can read some bytes.
- long pos = Interlocked.Read(ref _position);
- long len = Interlocked.Read(ref _length);
- long n = len - pos;
- if (n <= 0)
- {
- return;
- }
-
- int nInt = (int)n; // Safe because n <= count, which is an Int32
- if (nInt < 0)
- {
- return; // _position could be beyond EOF
- }
-
- unsafe
- {
- if (_buffer != null)
- {
- byte* pointer = null;
-
- RuntimeHelpers.PrepareConstrainedRegions();
- try
- {
- _buffer.AcquirePointer(ref pointer);
- ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(pointer + pos + _offset, nInt);
- Interlocked.Exchange(ref _position, pos + n);
- callback(span, state);
- }
- finally
- {
- if (pointer != null)
- {
- _buffer.ReleasePointer();
- }
- }
- }
- else
- {
- ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(_mem + pos, nInt);
- Interlocked.Exchange(ref _position, pos + n);
- callback(span, state);
- }
- }
- }
-
- /// <summary>
- /// Closes the stream. The stream's memory needs to be dealt with separately.
- /// </summary>
- /// <param name="disposing"></param>
- protected override void Dispose(bool disposing)
- {
- _isOpen = false;
- unsafe { _mem = null; }
-
- // Stream allocates WaitHandles for async calls. So for correctness
- // call base.Dispose(disposing) for better perf, avoiding waiting
- // for the finalizers to run on those types.
- base.Dispose(disposing);
- }
-
- private void EnsureNotClosed()
- {
- if (!_isOpen)
- throw Error.GetStreamIsClosed();
- }
-
- private void EnsureReadable()
- {
- if (!CanRead)
- throw Error.GetReadNotSupported();
- }
-
- private void EnsureWriteable()
- {
- if (!CanWrite)
- throw Error.GetWriteNotSupported();
- }
-
- /// <summary>
- /// Since it's a memory stream, this method does nothing.
- /// </summary>
- public override void Flush()
- {
- EnsureNotClosed();
- }
-
- /// <summary>
- /// Since it's a memory stream, this method does nothing specific.
- /// </summary>
- /// <param name="cancellationToken"></param>
- /// <returns></returns>
- public override Task FlushAsync(CancellationToken cancellationToken)
- {
- if (cancellationToken.IsCancellationRequested)
- return Task.FromCanceled(cancellationToken);
-
- try
- {
- Flush();
- return Task.CompletedTask;
- }
- catch (Exception ex)
- {
- return Task.FromException(ex);
- }
- }
-
- /// <summary>
- /// Number of bytes in the stream.
- /// </summary>
- public override long Length
- {
- get
- {
- EnsureNotClosed();
- return Interlocked.Read(ref _length);
- }
- }
-
- /// <summary>
- /// Number of bytes that can be written to the stream.
- /// </summary>
- public long Capacity
- {
- get
- {
- EnsureNotClosed();
- return _capacity;
- }
- }
-
- /// <summary>
- /// ReadByte will read byte at the Position in the stream
- /// </summary>
- public override long Position
- {
- get
- {
- if (!CanSeek) throw Error.GetStreamIsClosed();
- return Interlocked.Read(ref _position);
- }
- set
- {
- if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (!CanSeek) throw Error.GetStreamIsClosed();
-
- Interlocked.Exchange(ref _position, value);
- }
- }
-
- /// <summary>
- /// Pointer to memory at the current Position in the stream.
- /// </summary>
- [CLSCompliant(false)]
- public unsafe byte* PositionPointer
- {
- get
- {
- if (_buffer != null)
- throw new NotSupportedException(SR.NotSupported_UmsSafeBuffer);
-
- EnsureNotClosed();
-
- // Use a temp to avoid a race
- long pos = Interlocked.Read(ref _position);
- if (pos > _capacity)
- throw new IndexOutOfRangeException(SR.IndexOutOfRange_UMSPosition);
- byte* ptr = _mem + pos;
- return ptr;
- }
- set
- {
- if (_buffer != null)
- throw new NotSupportedException(SR.NotSupported_UmsSafeBuffer);
-
- EnsureNotClosed();
-
- if (value < _mem)
- throw new IOException(SR.IO_SeekBeforeBegin);
- long newPosition = (long)value - (long)_mem;
- if (newPosition < 0)
- throw new ArgumentOutOfRangeException("offset", SR.ArgumentOutOfRange_UnmanagedMemStreamLength);
-
- Interlocked.Exchange(ref _position, newPosition);
- }
- }
-
- /// <summary>
- /// Reads bytes from stream and puts them into the buffer
- /// </summary>
- /// <param name="buffer">Buffer to read the bytes to.</param>
- /// <param name="offset">Starting index in the buffer.</param>
- /// <param name="count">Maximum number of bytes to read.</param>
- /// <returns>Number of bytes actually read.</returns>
- public override int Read(byte[] buffer, int offset, int count)
- {
- if (buffer == null)
- throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
- if (offset < 0)
- throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (count < 0)
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (buffer.Length - offset < count)
- throw new ArgumentException(SR.Argument_InvalidOffLen);
-
- return ReadCore(new Span<byte>(buffer, offset, count));
- }
-
- public override int Read(Span<byte> buffer)
- {
- if (GetType() == typeof(UnmanagedMemoryStream))
- {
- return ReadCore(buffer);
- }
- else
- {
- // UnmanagedMemoryStream is not sealed, and a derived type may have overridden Read(byte[], int, int) prior
- // to this Read(Span<byte>) overload being introduced. In that case, this Read(Span<byte>) overload
- // should use the behavior of Read(byte[],int,int) overload.
- return base.Read(buffer);
- }
- }
-
- internal int ReadCore(Span<byte> buffer)
- {
- EnsureNotClosed();
- EnsureReadable();
-
- // Use a local variable to avoid a race where another thread
- // changes our position after we decide we can read some bytes.
- long pos = Interlocked.Read(ref _position);
- long len = Interlocked.Read(ref _length);
- long n = Math.Min(len - pos, buffer.Length);
- if (n <= 0)
- {
- return 0;
- }
-
- int nInt = (int)n; // Safe because n <= count, which is an Int32
- if (nInt < 0)
- {
- return 0; // _position could be beyond EOF
- }
- Debug.Assert(pos + nInt >= 0, "_position + n >= 0"); // len is less than 2^63 -1.
-
- unsafe
- {
- fixed (byte* pBuffer = &MemoryMarshal.GetReference(buffer))
- {
- if (_buffer != null)
- {
- byte* pointer = null;
-
- RuntimeHelpers.PrepareConstrainedRegions();
- try
- {
- _buffer.AcquirePointer(ref pointer);
- Buffer.Memcpy(pBuffer, pointer + pos + _offset, nInt);
- }
- finally
- {
- if (pointer != null)
- {
- _buffer.ReleasePointer();
- }
- }
- }
- else
- {
- Buffer.Memcpy(pBuffer, _mem + pos, nInt);
- }
- }
- }
- Interlocked.Exchange(ref _position, pos + n);
- return nInt;
- }
-
- /// <summary>
- /// Reads bytes from stream and puts them into the buffer
- /// </summary>
- /// <param name="buffer">Buffer to read the bytes to.</param>
- /// <param name="offset">Starting index in the buffer.</param>
- /// <param name="count">Maximum number of bytes to read.</param>
- /// <param name="cancellationToken">Token that can be used to cancel this operation.</param>
- /// <returns>Task that can be used to access the number of bytes actually read.</returns>
- public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
- {
- if (buffer == null)
- throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
- if (offset < 0)
- throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (count < 0)
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (buffer.Length - offset < count)
- throw new ArgumentException(SR.Argument_InvalidOffLen);
-
- if (cancellationToken.IsCancellationRequested)
- return Task.FromCanceled<int>(cancellationToken);
-
- try
- {
- int n = Read(buffer, offset, count);
- Task<int>? t = _lastReadTask;
- return (t != null && t.Result == n) ? t : (_lastReadTask = Task.FromResult<int>(n));
- }
- catch (Exception ex)
- {
- Debug.Assert(!(ex is OperationCanceledException));
- return Task.FromException<int>(ex);
- }
- }
-
- /// <summary>
- /// Reads bytes from stream and puts them into the buffer
- /// </summary>
- /// <param name="buffer">Buffer to read the bytes to.</param>
- /// <param name="cancellationToken">Token that can be used to cancel this operation.</param>
- public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
- {
- if (cancellationToken.IsCancellationRequested)
- {
- return new ValueTask<int>(Task.FromCanceled<int>(cancellationToken));
- }
-
- try
- {
- // ReadAsync(Memory<byte>,...) needs to delegate to an existing virtual to do the work, in case an existing derived type
- // has changed or augmented the logic associated with reads. If the Memory wraps an array, we could delegate to
- // ReadAsync(byte[], ...), but that would defeat part of the purpose, as ReadAsync(byte[], ...) often needs to allocate
- // a Task<int> for the return value, so we want to delegate to one of the synchronous methods. We could always
- // delegate to the Read(Span<byte>) method, and that's the most efficient solution when dealing with a concrete
- // UnmanagedMemoryStream, but if we're dealing with a type derived from UnmanagedMemoryStream, Read(Span<byte>) will end up delegating
- // to Read(byte[], ...), which requires it to get a byte[] from ArrayPool and copy the data. So, we special-case the
- // very common case of the Memory<byte> wrapping an array: if it does, we delegate to Read(byte[], ...) with it,
- // as that will be efficient in both cases, and we fall back to Read(Span<byte>) if the Memory<byte> wrapped something
- // else; if this is a concrete UnmanagedMemoryStream, that'll be efficient, and only in the case where the Memory<byte> wrapped
- // something other than an array and this is an UnmanagedMemoryStream-derived type that doesn't override Read(Span<byte>) will
- // it then fall back to doing the ArrayPool/copy behavior.
- return new ValueTask<int>(
- MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> destinationArray) ?
- Read(destinationArray.Array!, destinationArray.Offset, destinationArray.Count) :
- Read(buffer.Span));
- }
- catch (Exception ex)
- {
- return new ValueTask<int>(Task.FromException<int>(ex));
- }
- }
-
- /// <summary>
- /// Returns the byte at the stream current Position and advances the Position.
- /// </summary>
- /// <returns></returns>
- public override int ReadByte()
- {
- EnsureNotClosed();
- EnsureReadable();
-
- long pos = Interlocked.Read(ref _position); // Use a local to avoid a race condition
- long len = Interlocked.Read(ref _length);
- if (pos >= len)
- return -1;
- Interlocked.Exchange(ref _position, pos + 1);
- int result;
- if (_buffer != null)
- {
- unsafe
- {
- byte* pointer = null;
- RuntimeHelpers.PrepareConstrainedRegions();
- try
- {
- _buffer.AcquirePointer(ref pointer);
- result = *(pointer + pos + _offset);
- }
- finally
- {
- if (pointer != null)
- {
- _buffer.ReleasePointer();
- }
- }
- }
- }
- else
- {
- unsafe
- {
- result = _mem[pos];
- }
- }
- return result;
- }
-
- /// <summary>
- /// Advanced the Position to specific location in the stream.
- /// </summary>
- /// <param name="offset">Offset from the loc parameter.</param>
- /// <param name="loc">Origin for the offset parameter.</param>
- /// <returns></returns>
- public override long Seek(long offset, SeekOrigin loc)
- {
- EnsureNotClosed();
-
- switch (loc)
- {
- case SeekOrigin.Begin:
- if (offset < 0)
- throw new IOException(SR.IO_SeekBeforeBegin);
- Interlocked.Exchange(ref _position, offset);
- break;
-
- case SeekOrigin.Current:
- long pos = Interlocked.Read(ref _position);
- if (offset + pos < 0)
- throw new IOException(SR.IO_SeekBeforeBegin);
- Interlocked.Exchange(ref _position, offset + pos);
- break;
-
- case SeekOrigin.End:
- long len = Interlocked.Read(ref _length);
- if (len + offset < 0)
- throw new IOException(SR.IO_SeekBeforeBegin);
- Interlocked.Exchange(ref _position, len + offset);
- break;
-
- default:
- throw new ArgumentException(SR.Argument_InvalidSeekOrigin);
- }
-
- long finalPos = Interlocked.Read(ref _position);
- Debug.Assert(finalPos >= 0, "_position >= 0");
- return finalPos;
- }
-
- /// <summary>
- /// Sets the Length of the stream.
- /// </summary>
- /// <param name="value"></param>
- public override void SetLength(long value)
- {
- if (value < 0)
- throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (_buffer != null)
- throw new NotSupportedException(SR.NotSupported_UmsSafeBuffer);
-
- EnsureNotClosed();
- EnsureWriteable();
-
- if (value > _capacity)
- throw new IOException(SR.IO_FixedCapacity);
-
- long pos = Interlocked.Read(ref _position);
- long len = Interlocked.Read(ref _length);
- if (value > len)
- {
- unsafe
- {
- Buffer.ZeroMemory(_mem + len, (nuint)(value - len));
- }
- }
- Interlocked.Exchange(ref _length, value);
- if (pos > value)
- {
- Interlocked.Exchange(ref _position, value);
- }
- }
-
- /// <summary>
- /// Writes buffer into the stream
- /// </summary>
- /// <param name="buffer">Buffer that will be written.</param>
- /// <param name="offset">Starting index in the buffer.</param>
- /// <param name="count">Number of bytes to write.</param>
- public override void Write(byte[] buffer, int offset, int count)
- {
- if (buffer == null)
- throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
- if (offset < 0)
- throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (count < 0)
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (buffer.Length - offset < count)
- throw new ArgumentException(SR.Argument_InvalidOffLen);
-
- WriteCore(new ReadOnlySpan<byte>(buffer, offset, count));
- }
-
- public override void Write(ReadOnlySpan<byte> buffer)
- {
- if (GetType() == typeof(UnmanagedMemoryStream))
- {
- WriteCore(buffer);
- }
- else
- {
- // UnmanagedMemoryStream is not sealed, and a derived type may have overridden Write(byte[], int, int) prior
- // to this Write(Span<byte>) overload being introduced. In that case, this Write(Span<byte>) overload
- // should use the behavior of Write(byte[],int,int) overload.
- base.Write(buffer);
- }
- }
-
- internal unsafe void WriteCore(ReadOnlySpan<byte> buffer)
- {
- EnsureNotClosed();
- EnsureWriteable();
-
- long pos = Interlocked.Read(ref _position); // Use a local to avoid a race condition
- long len = Interlocked.Read(ref _length);
- long n = pos + buffer.Length;
- // Check for overflow
- if (n < 0)
- {
- throw new IOException(SR.IO_StreamTooLong);
- }
-
- if (n > _capacity)
- {
- throw new NotSupportedException(SR.IO_FixedCapacity);
- }
-
- if (_buffer == null)
- {
- // Check to see whether we are now expanding the stream and must
- // zero any memory in the middle.
- if (pos > len)
- {
- Buffer.ZeroMemory(_mem + len, (nuint)(pos - len));
- }
-
- // set length after zeroing memory to avoid race condition of accessing unzeroed memory
- if (n > len)
- {
- Interlocked.Exchange(ref _length, n);
- }
- }
-
- fixed (byte* pBuffer = &MemoryMarshal.GetReference(buffer))
- {
- if (_buffer != null)
- {
- long bytesLeft = _capacity - pos;
- if (bytesLeft < buffer.Length)
- {
- throw new ArgumentException(SR.Arg_BufferTooSmall);
- }
-
- byte* pointer = null;
- RuntimeHelpers.PrepareConstrainedRegions();
- try
- {
- _buffer.AcquirePointer(ref pointer);
- Buffer.Memcpy(pointer + pos + _offset, pBuffer, buffer.Length);
- }
- finally
- {
- if (pointer != null)
- {
- _buffer.ReleasePointer();
- }
- }
- }
- else
- {
- Buffer.Memcpy(_mem + pos, pBuffer, buffer.Length);
- }
- }
-
- Interlocked.Exchange(ref _position, n);
- return;
- }
-
- /// <summary>
- /// Writes buffer into the stream. The operation completes synchronously.
- /// </summary>
- /// <param name="buffer">Buffer that will be written.</param>
- /// <param name="offset">Starting index in the buffer.</param>
- /// <param name="count">Number of bytes to write.</param>
- /// <param name="cancellationToken">Token that can be used to cancel the operation.</param>
- /// <returns>Task that can be awaited </returns>
- public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
- {
- if (buffer == null)
- throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
- if (offset < 0)
- throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (count < 0)
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (buffer.Length - offset < count)
- throw new ArgumentException(SR.Argument_InvalidOffLen);
-
- if (cancellationToken.IsCancellationRequested)
- return Task.FromCanceled(cancellationToken);
-
- try
- {
- Write(buffer, offset, count);
- return Task.CompletedTask;
- }
- catch (Exception ex)
- {
- Debug.Assert(!(ex is OperationCanceledException));
- return Task.FromException(ex);
- }
- }
-
- /// <summary>
- /// Writes buffer into the stream. The operation completes synchronously.
- /// </summary>
- /// <param name="buffer">Buffer that will be written.</param>
- /// <param name="cancellationToken">Token that can be used to cancel the operation.</param>
- public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
- {
- if (cancellationToken.IsCancellationRequested)
- {
- return new ValueTask(Task.FromCanceled(cancellationToken));
- }
-
- try
- {
- // See corresponding comment in ReadAsync for why we don't just always use Write(ReadOnlySpan<byte>).
- // Unlike ReadAsync, we could delegate to WriteAsync(byte[], ...) here, but we don't for consistency.
- if (MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> sourceArray))
- {
- Write(sourceArray.Array!, sourceArray.Offset, sourceArray.Count);
- }
- else
- {
- Write(buffer.Span);
- }
- return default;
- }
- catch (Exception ex)
- {
- return new ValueTask(Task.FromException(ex));
- }
- }
-
- /// <summary>
- /// Writes a byte to the stream and advances the current Position.
- /// </summary>
- /// <param name="value"></param>
- public override void WriteByte(byte value)
- {
- EnsureNotClosed();
- EnsureWriteable();
-
- long pos = Interlocked.Read(ref _position); // Use a local to avoid a race condition
- long len = Interlocked.Read(ref _length);
- long n = pos + 1;
- if (pos >= len)
- {
- // Check for overflow
- if (n < 0)
- throw new IOException(SR.IO_StreamTooLong);
-
- if (n > _capacity)
- throw new NotSupportedException(SR.IO_FixedCapacity);
-
- // Check to see whether we are now expanding the stream and must
- // zero any memory in the middle.
- // don't do if created from SafeBuffer
- if (_buffer == null)
- {
- if (pos > len)
- {
- unsafe
- {
- Buffer.ZeroMemory(_mem + len, (nuint)(pos - len));
- }
- }
-
- // set length after zeroing memory to avoid race condition of accessing unzeroed memory
- Interlocked.Exchange(ref _length, n);
- }
- }
-
- if (_buffer != null)
- {
- unsafe
- {
- byte* pointer = null;
- RuntimeHelpers.PrepareConstrainedRegions();
- try
- {
- _buffer.AcquirePointer(ref pointer);
- *(pointer + pos + _offset) = value;
- }
- finally
- {
- if (pointer != null)
- {
- _buffer.ReleasePointer();
- }
- }
- }
- }
- else
- {
- unsafe
- {
- _mem[pos] = value;
- }
- }
- Interlocked.Exchange(ref _position, n);
- }
- }
-}
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStreamWrapper.cs b/netcore/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStreamWrapper.cs
deleted file mode 100644
index e90c094df74..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStreamWrapper.cs
+++ /dev/null
@@ -1,195 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-/*============================================================
-**
-**
-**
-**
-** Purpose: Create a Memorystream over an UnmanagedMemoryStream
-**
-===========================================================*/
-
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace System.IO
-{
- // Needed for backwards compatibility with V1.x usages of the
- // ResourceManager, where a MemoryStream is now returned as an
- // UnmanagedMemoryStream from ResourceReader.
- internal sealed class UnmanagedMemoryStreamWrapper : MemoryStream
- {
- private readonly UnmanagedMemoryStream _unmanagedStream;
-
- internal UnmanagedMemoryStreamWrapper(UnmanagedMemoryStream stream)
- {
- _unmanagedStream = stream;
- }
-
- public override bool CanRead => _unmanagedStream.CanRead;
-
- public override bool CanSeek => _unmanagedStream.CanSeek;
-
- public override bool CanWrite => _unmanagedStream.CanWrite;
-
- protected override void Dispose(bool disposing)
- {
- try
- {
- if (disposing)
- _unmanagedStream.Dispose();
- }
- finally
- {
- base.Dispose(disposing);
- }
- }
-
- public override void Flush()
- {
- _unmanagedStream.Flush();
- }
-
- public override byte[] GetBuffer()
- {
- throw new UnauthorizedAccessException(SR.UnauthorizedAccess_MemStreamBuffer);
- }
-
- public override bool TryGetBuffer(out ArraySegment<byte> buffer)
- {
- buffer = default;
- return false;
- }
-
- public override int Capacity
- {
- get => (int)_unmanagedStream.Capacity;
- set => throw new IOException(SR.IO_FixedCapacity);
- }
-
- public override long Length => _unmanagedStream.Length;
-
- public override long Position
- {
- get => _unmanagedStream.Position;
- set => _unmanagedStream.Position = value;
- }
-
- public override int Read(byte[] buffer, int offset, int count)
- {
- return _unmanagedStream.Read(buffer, offset, count);
- }
-
- public override int Read(Span<byte> buffer)
- {
- return _unmanagedStream.Read(buffer);
- }
-
- public override int ReadByte()
- {
- return _unmanagedStream.ReadByte();
- }
-
- public override long Seek(long offset, SeekOrigin loc)
- {
- return _unmanagedStream.Seek(offset, loc);
- }
-
- public override unsafe byte[] ToArray()
- {
- byte[] buffer = new byte[_unmanagedStream.Length];
- _unmanagedStream.Read(buffer, 0, (int)_unmanagedStream.Length);
- return buffer;
- }
-
- public override void Write(byte[] buffer, int offset, int count)
- {
- _unmanagedStream.Write(buffer, offset, count);
- }
-
- public override void Write(ReadOnlySpan<byte> buffer)
- {
- _unmanagedStream.Write(buffer);
- }
-
- public override void WriteByte(byte value)
- {
- _unmanagedStream.WriteByte(value);
- }
-
- // Writes this MemoryStream to another stream.
- public override unsafe void WriteTo(Stream stream)
- {
- if (stream == null)
- throw new ArgumentNullException(nameof(stream), SR.ArgumentNull_Stream);
-
- byte[] buffer = ToArray();
-
- stream.Write(buffer, 0, buffer.Length);
- }
-
- public override void SetLength(long value)
- {
- // This was probably meant to call _unmanagedStream.SetLength(value), but it was forgotten in V.4.0.
- // Now this results in a call to the base which touches the underlying array which is never actually used.
- // We cannot fix it due to compat now, but we should fix this at the next SxS release oportunity.
- base.SetLength(value);
- }
-
-
- public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
- {
- // The parameter checks must be in sync with the base version:
- if (destination == null)
- throw new ArgumentNullException(nameof(destination));
-
- if (bufferSize <= 0)
- throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);
-
- if (!CanRead && !CanWrite)
- throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed);
-
- if (!destination.CanRead && !destination.CanWrite)
- throw new ObjectDisposedException(nameof(destination), SR.ObjectDisposed_StreamClosed);
-
- if (!CanRead)
- throw new NotSupportedException(SR.NotSupported_UnreadableStream);
-
- if (!destination.CanWrite)
- throw new NotSupportedException(SR.NotSupported_UnwritableStream);
-
-
- return _unmanagedStream.CopyToAsync(destination, bufferSize, cancellationToken);
- }
-
-
- public override Task FlushAsync(CancellationToken cancellationToken)
- {
- return _unmanagedStream.FlushAsync(cancellationToken);
- }
-
-
- public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
- {
- return _unmanagedStream.ReadAsync(buffer, offset, count, cancellationToken);
- }
-
- public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
- {
- return _unmanagedStream.ReadAsync(buffer, cancellationToken);
- }
-
-
- public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
- {
- return _unmanagedStream.WriteAsync(buffer, offset, count, cancellationToken);
- }
-
- public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
- {
- return _unmanagedStream.WriteAsync(buffer, cancellationToken);
- }
- } // class UnmanagedMemoryStreamWrapper
-} // namespace
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/Win32Marshal.cs b/netcore/System.Private.CoreLib/shared/System/IO/Win32Marshal.cs
deleted file mode 100644
index 35f7d2eefe9..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/Win32Marshal.cs
+++ /dev/null
@@ -1,101 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-#nullable enable
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-
-namespace System.IO
-{
- /// <summary>
- /// Provides static methods for converting from Win32 errors codes to exceptions, HRESULTS and error messages.
- /// </summary>
- internal static class Win32Marshal
- {
- /// <summary>
- /// Converts, resetting it, the last Win32 error into a corresponding <see cref="Exception"/> object, optionally
- /// including the specified path in the error message.
- /// </summary>
- internal static Exception GetExceptionForLastWin32Error(string? path = "")
- => GetExceptionForWin32Error(Marshal.GetLastWin32Error(), path);
-
- /// <summary>
- /// Converts the specified Win32 error into a corresponding <see cref="Exception"/> object, optionally
- /// including the specified path in the error message.
- /// </summary>
- internal static Exception GetExceptionForWin32Error(int errorCode, string? path = "")
- {
- // ERROR_SUCCESS gets thrown when another unexpected interop call was made before checking GetLastWin32Error().
- // Errors have to get retrieved as soon as possible after P/Invoking to avoid this.
- Debug.Assert(errorCode != Interop.Errors.ERROR_SUCCESS);
-
- switch (errorCode)
- {
- case Interop.Errors.ERROR_FILE_NOT_FOUND:
- return new FileNotFoundException(
- string.IsNullOrEmpty(path) ? SR.IO_FileNotFound : SR.Format(SR.IO_FileNotFound_FileName, path), path);
- case Interop.Errors.ERROR_PATH_NOT_FOUND:
- return new DirectoryNotFoundException(
- string.IsNullOrEmpty(path) ? SR.IO_PathNotFound_NoPathName : SR.Format(SR.IO_PathNotFound_Path, path));
- case Interop.Errors.ERROR_ACCESS_DENIED:
- return new UnauthorizedAccessException(
- string.IsNullOrEmpty(path) ? SR.UnauthorizedAccess_IODenied_NoPathName : SR.Format(SR.UnauthorizedAccess_IODenied_Path, path));
- case Interop.Errors.ERROR_ALREADY_EXISTS:
- if (string.IsNullOrEmpty(path))
- goto default;
- return new IOException(SR.Format(SR.IO_AlreadyExists_Name, path), MakeHRFromErrorCode(errorCode));
- case Interop.Errors.ERROR_FILENAME_EXCED_RANGE:
- return new PathTooLongException(
- string.IsNullOrEmpty(path) ? SR.IO_PathTooLong : SR.Format(SR.IO_PathTooLong_Path, path));
- case Interop.Errors.ERROR_SHARING_VIOLATION:
- return new IOException(
- string.IsNullOrEmpty(path) ? SR.IO_SharingViolation_NoFileName : SR.Format(SR.IO_SharingViolation_File, path),
- MakeHRFromErrorCode(errorCode));
- case Interop.Errors.ERROR_FILE_EXISTS:
- if (string.IsNullOrEmpty(path))
- goto default;
- return new IOException(SR.Format(SR.IO_FileExists_Name, path), MakeHRFromErrorCode(errorCode));
- case Interop.Errors.ERROR_OPERATION_ABORTED:
- return new OperationCanceledException();
- case Interop.Errors.ERROR_INVALID_PARAMETER:
- default:
- return new IOException(
- string.IsNullOrEmpty(path) ? GetMessage(errorCode) : $"{GetMessage(errorCode)} : '{path}'",
- MakeHRFromErrorCode(errorCode));
- }
- }
-
- /// <summary>
- /// If not already an HRESULT, returns an HRESULT for the specified Win32 error code.
- /// </summary>
- internal static int MakeHRFromErrorCode(int errorCode)
- {
- // Don't convert it if it is already an HRESULT
- if ((0xFFFF0000 & errorCode) != 0)
- return errorCode;
-
- return unchecked(((int)0x80070000) | errorCode);
- }
-
- /// <summary>
- /// Returns a Win32 error code for the specified HRESULT if it came from FACILITY_WIN32
- /// If not, returns the HRESULT unchanged
- /// </summary>
- internal static int TryMakeWin32ErrorCodeFromHR(int hr)
- {
- if ((0xFFFF0000 & hr) == 0x80070000)
- {
- // Win32 error, Win32Marshal.GetExceptionForWin32Error expects the Win32 format
- hr &= 0x0000FFFF;
- }
-
- return hr;
- }
-
- /// <summary>
- /// Returns a string message for the specified Win32 error code.
- /// </summary>
- internal static string GetMessage(int errorCode) => Interop.Kernel32.GetMessage(errorCode);
- }
-}