Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 140 additions & 0 deletions src/libraries/Common/src/System/IO/File.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Buffers;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;

#if SYSTEM_PRIVATE_CORELIB
namespace Internal.IO
#elif MS_IO_REDIST
namespace Microsoft.IO
#else
namespace System.IO
#endif
{
#if SYSTEM_PRIVATE_CORELIB
internal
#else
public
#endif
static partial class File
{
// Tests if a file exists. The result is true if the file
// given by the specified path exists; otherwise, the result is
// false. Note that if path describes a directory,
// Exists will return true.
public static bool Exists([NotNullWhen(true)] string? path)
{
try
{
if (path == null)
return false;
if (path.Length == 0)
return false;

path = Path.GetFullPath(path);

// After normalizing, check whether path ends in directory separator.
// Otherwise, FillAttributeInfo removes it and we may return a false positive.
// GetFullPath should never return null
Debug.Assert(path != null, "File.Exists: GetFullPath returned null");
if (path.Length > 0 && PathInternal.IsDirectorySeparator(path[path.Length - 1]))
{
return false;
}

return FileSystem.FileExists(path);
}
catch (ArgumentException) { }
catch (IOException) { }
catch (UnauthorizedAccessException) { }

return false;
}

public static byte[] ReadAllBytes(string path)
{
// bufferSize == 1 used to avoid unnecessary buffer in FileStream
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 1))
{
long fileLength = fs.Length;
if (fileLength > int.MaxValue)
{
throw new IOException(SR.IO_FileTooLong2GB);
}
else if (fileLength == 0)
{
#if !MS_IO_REDIST
// Some file systems (e.g. procfs on Linux) return 0 for length even when there's content.
// Thus we need to assume 0 doesn't mean empty.
return ReadAllBytesUnknownLength(fs);
#endif
}

int index = 0;
int count = (int)fileLength;
byte[] bytes = new byte[count];
while (count > 0)
{
int n = fs.Read(bytes, index, count);
if (n == 0)
ThrowEndOfFile();
index += n;
count -= n;
}
return bytes;
}

static void ThrowEndOfFile() => throw new EndOfStreamException(SR.IO_EOF_ReadBeyondEOF);
}

#if !MS_IO_REDIST
private static byte[] ReadAllBytesUnknownLength(FileStream fs)
{
byte[]? rentedArray = null;
Span<byte> buffer = stackalloc byte[512];
try
{
int bytesRead = 0;
while (true)
{
if (bytesRead == buffer.Length)
{
uint newLength = (uint)buffer.Length * 2;
if (newLength > Array.MaxLength)
{
newLength = (uint)Math.Max(Array.MaxLength, buffer.Length + 1);
}

byte[] tmp = ArrayPool<byte>.Shared.Rent((int)newLength);
buffer.CopyTo(tmp);
if (rentedArray != null)
{
ArrayPool<byte>.Shared.Return(rentedArray);
}
buffer = rentedArray = tmp;
}

Debug.Assert(bytesRead < buffer.Length);
int n = fs.Read(buffer.Slice(bytesRead));
if (n == 0)
{
return buffer.Slice(0, bytesRead).ToArray();
}
bytesRead += n;
}
}
finally
{
if (rentedArray != null)
{
ArrayPool<byte>.Shared.Return(rentedArray);
}
}
}
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
using System.IO;
using System.Text;

#if MS_IO_REDIST
#if SYSTEM_PRIVATE_CORELIB
namespace Internal.IO
#elif MS_IO_REDIST
namespace Microsoft.IO
#else
namespace System.IO
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics;
using System.IO;

#if SYSTEM_PRIVATE_CORELIB
namespace Internal.IO
#else
namespace System.IO
#endif
{
/// <summary>Provides an implementation of FileSystem for Unix systems.</summary>
internal static partial class FileSystem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@
Link="Common\System\IO\DisableMediaInsertionPrompt.cs" />
<Compile Include="$(CoreLibSharedDir)System\IO\DriveInfoInternal.Windows.cs"
Link="Common\System\IO\DriveInfoInternal.Windows.cs" />
<Compile Include="$(CommonPath)System\IO\File.cs"
Link="Common\System\IO\File.cs" />
<Compile Include="$(CoreLibSharedDir)System\IO\Path.cs"
Link="System\IO\Path.cs" />
<Compile Include="$(CoreLibSharedDir)System\IO\Path.Windows.cs"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
</ItemGroup>
<!-- All targets files -->
<ItemGroup>
<Compile Include="$(CommonPath)System\IO\File.cs"
Link="Common\System\IO\File.cs" />
<Compile Include="$(CoreLibSharedDir)System\IO\PathInternal.cs"
Link="System\IO\PathInternal.cs" />
<Compile Include="$(CommonPath)System\Text\ValueStringBuilder.cs"
Expand Down Expand Up @@ -218,10 +220,11 @@
Link="Common\Interop\Unix\Interop.UTimensat.cs" />
<Compile Include="$(CommonPath)System\Text\ValueUtf8Converter.cs"
Link="Common\System\Text\ValueUtf8Converter.cs" />
<Compile Include="$(CommonPath)System\IO\FileSystem.Exists.Unix.cs"
Link="Common\System\IO\FileSystem.Exists.Unix.cs" />
<Compile Include="System\IO\Enumeration\FileSystemEntry.Unix.cs" />
<Compile Include="System\IO\Enumeration\FileSystemEnumerator.Unix.cs" />
<Compile Include="System\IO\FileStatus.Unix.cs" />
<Compile Include="System\IO\FileSystem.Exists.Unix.cs" />
<Compile Include="System\IO\FileSystem.Unix.cs" />
<Compile Include="System\IO\FileSystemInfo.Unix.cs" />
</ItemGroup>
Expand Down
115 changes: 1 addition & 114 deletions src/libraries/System.IO.FileSystem/src/System/IO/File.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace System.IO
{
// Class for creating FileStream objects, and some basic file management
// routines such as Delete, etc.
public static class File
public static partial class File
{
private const int MaxByteArrayLength = 0x7FFFFFC7;
private static Encoding? s_UTF8NoBOM;
Expand Down Expand Up @@ -111,39 +111,6 @@ public static void Delete(string path)
FileSystem.DeleteFile(Path.GetFullPath(path));
}

// Tests whether a file exists. The result is true if the file
// given by the specified path exists; otherwise, the result is
// false. Note that if path describes a directory,
// Exists will return true.
public static bool Exists([NotNullWhen(true)] string? path)
{
try
{
if (path == null)
return false;
if (path.Length == 0)
return false;

path = Path.GetFullPath(path);

// After normalizing, check whether path ends in directory separator.
// Otherwise, FillAttributeInfo removes it and we may return a false positive.
// GetFullPath should never return null
Debug.Assert(path != null, "File.Exists: GetFullPath returned null");
if (path.Length > 0 && PathInternal.IsDirectorySeparator(path[path.Length - 1]))
{
return false;
}

return FileSystem.FileExists(path);
}
catch (ArgumentException) { }
catch (IOException) { }
catch (UnauthorizedAccessException) { }

return false;
}

public static FileStream Open(string path, FileMode mode)
{
return Open(path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), FileShare.None);
Expand Down Expand Up @@ -326,86 +293,6 @@ public static void WriteAllText(string path, string? contents, Encoding encoding
}
}

public static byte[] ReadAllBytes(string path)
{
// bufferSize == 1 used to avoid unnecessary buffer in FileStream
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 1, FileOptions.SequentialScan))
{
long fileLength = fs.Length;
if (fileLength > int.MaxValue)
{
throw new IOException(SR.IO_FileTooLong2GB);
}
else if (fileLength == 0)
{
#if !MS_IO_REDIST
// Some file systems (e.g. procfs on Linux) return 0 for length even when there's content.
// Thus we need to assume 0 doesn't mean empty.
return ReadAllBytesUnknownLength(fs);
#endif
}

int index = 0;
int count = (int)fileLength;
byte[] bytes = new byte[count];
while (count > 0)
{
int n = fs.Read(bytes, index, count);
if (n == 0)
throw Error.GetEndOfFile();
index += n;
count -= n;
}
return bytes;
}
}

#if !MS_IO_REDIST
private static byte[] ReadAllBytesUnknownLength(FileStream fs)
{
byte[]? rentedArray = null;
Span<byte> buffer = stackalloc byte[512];
try
{
int bytesRead = 0;
while (true)
{
if (bytesRead == buffer.Length)
{
uint newLength = (uint)buffer.Length * 2;
if (newLength > MaxByteArrayLength)
{
newLength = (uint)Math.Max(MaxByteArrayLength, buffer.Length + 1);
}

byte[] tmp = ArrayPool<byte>.Shared.Rent((int)newLength);
buffer.CopyTo(tmp);
if (rentedArray != null)
{
ArrayPool<byte>.Shared.Return(rentedArray);
}
buffer = rentedArray = tmp;
}

Debug.Assert(bytesRead < buffer.Length);
int n = fs.Read(buffer.Slice(bytesRead));
if (n == 0)
{
return buffer.Slice(0, bytesRead).ToArray();
}
bytesRead += n;
}
}
finally
{
if (rentedArray != null)
{
ArrayPool<byte>.Shared.Return(rentedArray);
}
}
}
#endif

public static void WriteAllBytes(string path, byte[] bytes)
{
if (path == null)
Expand Down
24 changes: 0 additions & 24 deletions src/libraries/System.Private.CoreLib/src/Internal/IO/File.Unix.cs

This file was deleted.

Loading