mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Speed up DirLoader.DirLoader on Windows.
New FileHelper.TryOpenFileRead that doesn't throw if the file doesn't exist. Also used it in a couple other spots.
This commit is contained in:
@@ -105,11 +105,11 @@ internal sealed partial class StatusHost
|
||||
private Task<bool> SourceAczDictionaryViaFile(AssetPass pass, IPackageLogger logger)
|
||||
{
|
||||
var path = PathHelpers.ExecutableRelativeFile("Content.Client.zip");
|
||||
if (!File.Exists(path))
|
||||
if (!FileHelper.TryOpenFileRead(path, out var fileStream))
|
||||
return Task.FromResult(false);
|
||||
|
||||
_aczSawmill.Info($"StatusHost found client zip: {path}");
|
||||
using var zip = new ZipArchive(File.OpenRead(path), ZipArchiveMode.Read, leaveOpen: false);
|
||||
using var zip = new ZipArchive(fileStream, ZipArchiveMode.Read, leaveOpen: false);
|
||||
SourceAczDictionaryViaZipStream(zip, pass, logger);
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
@@ -895,12 +895,10 @@ namespace Robust.Shared.ContentPack
|
||||
{
|
||||
var path = Path.Combine(diskLoadPath, dllName);
|
||||
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
if (!FileHelper.TryOpenFileRead(path, out var fileStream))
|
||||
continue;
|
||||
}
|
||||
|
||||
return ModLoader.MakePEReader(File.OpenRead(path));
|
||||
return ModLoader.MakePEReader(fileStream);
|
||||
}
|
||||
|
||||
foreach (var resLoadPath in _resLoadPaths)
|
||||
|
||||
@@ -46,16 +46,11 @@ namespace Robust.Shared.ContentPack
|
||||
public bool TryGetFile(ResPath relPath, [NotNullWhen(true)] out Stream? stream)
|
||||
{
|
||||
var path = GetPath(relPath);
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
stream = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
CheckPathCasing(relPath);
|
||||
|
||||
stream = File.OpenRead(path);
|
||||
return true;
|
||||
var ret = FileHelper.TryOpenFileRead(path, out var fStream);
|
||||
stream = fStream;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public bool FileExists(ResPath relPath)
|
||||
|
||||
70
Robust.Shared/Utility/FileHelper.cs
Normal file
70
Robust.Shared/Utility/FileHelper.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Robust.Shared.Utility;
|
||||
|
||||
internal static class FileHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Try to open a file for reading. If the file does not exist, the operation fails without exception.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This API is not atomic and can thus be vulnerable to TOCTOU attacks. Don't use it if that's relevant.
|
||||
/// </remarks>
|
||||
/// <param name="path">The path to try to open.</param>
|
||||
/// <param name="stream">The resulting file stream.</param>
|
||||
/// <returns>True if the file existed and was opened.</returns>
|
||||
public static bool TryOpenFileRead(string path, [NotNullWhen(true)] out FileStream? stream)
|
||||
{
|
||||
// On Windows, the separate File.Exists() call alone adds a ton of weight.
|
||||
// The alternative however (opening the file and catching the error) is extremely slow because of .NET exceptions.
|
||||
// So we manually call the windows API and make the file handle from that. Problem solved!
|
||||
if (OperatingSystem.IsWindows())
|
||||
return TryGetFileWindows(path, out stream);
|
||||
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
stream = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
stream = File.OpenRead(path);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static unsafe bool TryGetFileWindows(string path, [NotNullWhen(true)] out FileStream? stream)
|
||||
{
|
||||
HANDLE file;
|
||||
fixed (char* pPath = path)
|
||||
{
|
||||
file = Windows.CreateFileW(
|
||||
(ushort*)pPath,
|
||||
Windows.GENERIC_READ,
|
||||
FILE.FILE_SHARE_READ,
|
||||
null,
|
||||
OPEN.OPEN_EXISTING,
|
||||
FILE.FILE_ATTRIBUTE_NORMAL,
|
||||
HANDLE.NULL);
|
||||
}
|
||||
|
||||
if (file == HANDLE.INVALID_VALUE)
|
||||
{
|
||||
var lastError = Marshal.GetLastWin32Error();
|
||||
if (lastError is ERROR.ERROR_FILE_NOT_FOUND or ERROR.ERROR_PATH_NOT_FOUND)
|
||||
{
|
||||
stream = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
Marshal.ThrowExceptionForHR(Windows.HRESULT_FROM_WIN32(lastError));
|
||||
}
|
||||
|
||||
var sf = new SafeFileHandle(file, ownsHandle: true);
|
||||
stream = new FileStream(sf, FileAccess.Read);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user