// 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;
}
}
}
}