// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.IO; using System.Text; using EpicGames.Core; namespace EpicGames.UHT.Utils { /// /// Interface used to read/write files /// public interface IUhtFileManager { /// /// Return the full file path for a partial path /// /// The partial file path /// The full file path public string GetFullFilePath(string filePath); /// /// Read the given source file /// /// File path /// Read fragment information /// True if the file was read public bool ReadSource(string filePath, out UhtSourceFragment fragment); /// /// Read the given source file /// /// File path /// Buffer containing the read data or null if not found. The returned buffer must be returned to the cache via a call to UhtBuffer.Return [Obsolete("Use the new ReadOutput with UhtPoolBuffer")] public UhtBuffer? ReadOutput(string filePath); /// /// Read the given source file /// /// File path /// Read output /// True if the file was read public bool ReadOutput(string filePath, out UhtPoolBuffer output); /// /// Write the given contents to the file /// /// Path to write to /// Contents to write /// True if the source was written public bool WriteOutput(string filePath, ReadOnlySpan contents); /// /// Rename the given file /// /// Old file path name /// New file path name /// True if the file was renamed public bool RenameOutput(string oldFilePath, string newFilePath); } /// /// Implementation of a file manager that reads/writes from disk /// public class UhtStdFileManager : IUhtFileManager { /// /// Construct a new file manager /// public UhtStdFileManager() { } /// public string GetFullFilePath(string filePath) { return filePath; } /// public bool ReadSource(string filePath, out UhtSourceFragment fragment) { if (ReadFile(filePath, out StringView data)) { fragment = new UhtSourceFragment { SourceFile = null, FilePath = filePath, LineNumber = 0, Data = data }; return true; } fragment = new UhtSourceFragment { SourceFile = null, FilePath = String.Empty, LineNumber = 0, Data = new StringView() }; return false; } /// [Obsolete("Use the new ReadOutput with UhtPoolBuffer")] public UhtBuffer? ReadOutput(string filePath) { // Exceptions are very expensive. Don't bother trying to open the file if it doesn't exist if (!File.Exists(filePath)) { return null; } try { using FileStream fs = new(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 1, FileOptions.SequentialScan); using UhtBorrowByteBuffer byteBuffer = new((int)fs.Length); Span byteSpan = byteBuffer.Buffer.Memory.Span; int readLength = fs.Read(byteSpan); byteSpan = byteSpan[..readLength]; Encoding encoding = FileUtils.GetEncoding(byteSpan, out int skipBytes); byteSpan = byteSpan[skipBytes..]; int charCount = encoding.GetCharCount(byteBuffer.Buffer.Memory.Span); UhtBuffer initialBuffer = UhtBuffer.Borrow(charCount); encoding.GetChars(byteBuffer.Buffer.Memory.Span, initialBuffer.Memory.Span); return initialBuffer; } catch (IOException) { return null; } } /// public bool ReadOutput(string filePath, out UhtPoolBuffer output) { // Exceptions are very expensive. Don't bother trying to open the file if it doesn't exist if (!File.Exists(filePath)) { output = default; return false; } try { using FileStream fs = new(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 1, FileOptions.SequentialScan); using UhtRentedPoolBuffer byteBuffer = new((int)fs.Length); Span byteSpan = byteBuffer.Buffer.Memory.Span; int readLength = fs.Read(byteSpan); byteSpan = byteSpan[..readLength]; Encoding encoding = FileUtils.GetEncoding(byteSpan, out int skipBytes); byteSpan = byteSpan[skipBytes..]; int charCount = encoding.GetCharCount(byteBuffer.Buffer.Memory.Span); output = UhtPoolBuffers.Rent(charCount); encoding.GetChars(byteBuffer.Buffer.Memory.Span, output.Memory.Span); return true; } catch (IOException) { output = default; return false; } } /// public bool WriteOutput(string filePath, ReadOnlySpan contents) { try { string? fileDirectory = Path.GetDirectoryName(filePath); if (!String.IsNullOrEmpty(fileDirectory)) { Directory.CreateDirectory(fileDirectory); } using StreamWriter writer = new(filePath, false, new UTF8Encoding(false, true), 16 * 1024); writer.Write(contents); return true; } catch (Exception) { return false; } } /// public bool RenameOutput(string oldFilePath, string newFilePath) { try { File.Move(oldFilePath, newFilePath, true); return true; } catch (Exception) { return false; } } /// /// Read the given source file /// /// Full file path /// Contents of the file /// True if the file was read, false if not private static bool ReadFile(string filePath, out StringView contents) { // Exceptions are very expensive. Don't bother trying to open the file if it doesn't exist if (!File.Exists(filePath)) { contents = new StringView(); return false; } try { using FileStream fs = new(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 1, FileOptions.SequentialScan); using UhtRentedPoolBuffer byteBuffer = new((int)fs.Length); Span byteSpan = byteBuffer.Buffer.Memory.Span; int readLength = fs.Read(byteSpan); byteSpan = byteSpan[..readLength]; Encoding encoding = FileUtils.GetEncoding(byteSpan, out int skipBytes); byteSpan = byteSpan[skipBytes..]; int charCount = encoding.GetCharCount(byteSpan); char[] initialBuffer = new char[charCount]; encoding.GetChars(byteSpan, initialBuffer); contents = new StringView(new ReadOnlyMemory(initialBuffer, 0, charCount)); return true; } catch (IOException) { contents = new StringView(); return false; } } } }