Remove IFile.Mode and create a new FileBase class

This commit is contained in:
Alex Barney 2019-09-21 23:38:47 -05:00
parent 22940f20e5
commit 6a449b7da1
15 changed files with 211 additions and 77 deletions

View File

@ -1,5 +1,4 @@
using System;
using LibHac.FsSystem;
namespace LibHac.Fs.Accessors
{
@ -11,10 +10,6 @@ namespace LibHac.Fs.Accessors
public WriteState WriteState { get; private set; }
public OpenMode OpenMode { get; }
// Todo: Consider removing Mode from interface because OpenMode is in FileAccessor
// Todo: Set WriteState to Error based on returned results
OpenMode IFile.Mode => OpenMode;
public FileAccessor(IFile baseFile, FileSystemAccessor parent, OpenMode mode)
{
File = baseFile;

131
src/LibHac/Fs/FileBase2.cs Normal file
View File

@ -0,0 +1,131 @@
using System;
using System.Threading;
namespace LibHac.Fs
{
public abstract class FileBase2 : IFile
{
// 0 = not disposed; 1 = disposed
private int _disposedState;
private bool IsDisposed => _disposedState != 0;
public abstract Result ReadImpl(out long bytesRead, long offset, Span<byte> destination, ReadOption options);
public abstract Result WriteImpl(long offset, ReadOnlySpan<byte> source, WriteOption options);
public abstract Result FlushImpl();
public abstract Result GetSizeImpl(out long size);
public abstract Result SetSizeImpl(long size);
public Result Read(out long bytesRead, long offset, Span<byte> destination, ReadOption options)
{
bytesRead = default;
if (IsDisposed) return ResultFs.PreconditionViolation.Log();
if (destination.Length == 0) return Result.Success;
if (offset < 0) return ResultFs.ValueOutOfRange.Log();
return ReadImpl(out bytesRead, offset, destination, options);
}
public Result Write(long offset, ReadOnlySpan<byte> source, WriteOption options)
{
if (IsDisposed) return ResultFs.PreconditionViolation.Log();
if (source.Length == 0)
{
if (options.HasFlag(WriteOption.Flush))
{
return Flush();
}
return Result.Success;
}
if (offset < 0) return ResultFs.ValueOutOfRange.Log();
return WriteImpl(offset, source, options);
}
public Result Flush()
{
if (IsDisposed) return ResultFs.PreconditionViolation.Log();
return FlushImpl();
}
public Result GetSize(out long size)
{
size = 0;
if (IsDisposed) return ResultFs.PreconditionViolation.Log();
return GetSizeImpl(out size);
}
public Result SetSize(long size)
{
if (IsDisposed) return ResultFs.PreconditionViolation.Log();
if (size < 0) return ResultFs.ValueOutOfRange.Log();
return SetSizeImpl(size);
}
public void Dispose()
{
// Make sure Dispose is only called once
if (Interlocked.CompareExchange(ref _disposedState, 1, 0) == 0)
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
protected virtual void Dispose(bool disposing) { }
protected Result ValidateReadParams(out long bytesToRead, long offset, int size, OpenMode openMode)
{
bytesToRead = default;
if (!openMode.HasFlag(OpenMode.Read))
{
return ResultFs.InvalidOpenModeForRead.Log();
}
Result rc = GetSize(out long fileSize);
if (rc.IsFailure()) return rc;
if (offset > fileSize)
{
return ResultFs.ValueOutOfRange.Log();
}
bytesToRead = Math.Min(fileSize - offset, size);
return Result.Success;
}
protected Result ValidateWriteParams(long offset, int size, OpenMode openMode, out bool isResizeNeeded)
{
isResizeNeeded = false;
if (!openMode.HasFlag(OpenMode.Write))
{
return ResultFs.InvalidOpenModeForWrite.Log();
}
Result rc = GetSize(out long fileSize);
if (rc.IsFailure()) return rc;
if (offset + size > fileSize)
{
isResizeNeeded = true;
if (!openMode.HasFlag(OpenMode.AllowAppend))
{
return ResultFs.AllowAppendRequiredForImplicitExtension.Log();
}
}
return Result.Success;
}
}
}

View File

@ -1,5 +1,4 @@
using System;
using LibHac.FsSystem;
namespace LibHac.Fs
{

View File

@ -1,5 +1,3 @@
using LibHac.FsSystem;
namespace LibHac.Fs
{
public partial class FileSystemClient

View File

@ -1,4 +1,6 @@
namespace LibHac.Fs
using System;
namespace LibHac.Fs
{
public enum BisPartitionId
{
@ -92,4 +94,29 @@
Nand = 0,
SdCard = 1
}
/// <summary>
/// Specifies which operations are available on an <see cref="IFile"/>.
/// </summary>
[Flags]
public enum OpenMode
{
Read = 1,
Write = 2,
AllowAppend = 4,
ReadWrite = Read | Write
}
[Flags]
public enum ReadOption
{
None = 0
}
[Flags]
public enum WriteOption
{
None = 0,
Flush = 1
}
}

View File

@ -1,5 +1,4 @@
using System;
using LibHac.FsSystem;
namespace LibHac.Fs
{
@ -19,11 +18,6 @@ namespace LibHac.Fs
/// the file will be expanded so that it is large enough to contain the written data.</remarks>
public interface IFile : IDisposable
{
/// <summary>
/// The permissions mode for the current file.
/// </summary>
OpenMode Mode { get; }
/// <summary>
/// Reads a sequence of bytes from the current <see cref="IFile"/>.
/// </summary>

View File

@ -1,7 +1,6 @@
using System;
using LibHac.Common;
using LibHac.Fs;
using LibHac.FsSystem;
using LibHac.FsSystem.Save;
namespace LibHac.FsService.Creators

View File

@ -3,57 +3,49 @@ using LibHac.Fs;
namespace LibHac.FsSystem
{
public class DirectorySaveDataFile : FileBase
public class DirectorySaveDataFile : FileBase2
{
private IFile BaseFile { get; }
private DirectorySaveDataFileSystem ParentFs { get; }
private object DisposeLocker { get; } = new object();
private OpenMode Mode { get; }
public DirectorySaveDataFile(DirectorySaveDataFileSystem parentFs, IFile baseFile)
public DirectorySaveDataFile(DirectorySaveDataFileSystem parentFs, IFile baseFile, OpenMode mode)
{
ParentFs = parentFs;
BaseFile = baseFile;
Mode = BaseFile.Mode;
ToDispose.Add(BaseFile);
Mode = mode;
}
public override Result Read(out long bytesRead, long offset, Span<byte> destination, ReadOption options)
public override Result ReadImpl(out long bytesRead, long offset, Span<byte> destination, ReadOption options)
{
return BaseFile.Read(out bytesRead, offset, destination, options);
}
public override Result Write(long offset, ReadOnlySpan<byte> source, WriteOption options)
public override Result WriteImpl(long offset, ReadOnlySpan<byte> source, WriteOption options)
{
return BaseFile.Write(offset, source, options);
}
public override Result Flush()
public override Result FlushImpl()
{
return BaseFile.Flush();
}
public override Result GetSize(out long size)
public override Result GetSizeImpl(out long size)
{
return BaseFile.GetSize(out size);
}
public override Result SetSize(long size)
public override Result SetSizeImpl(long size)
{
return BaseFile.SetSize(size);
}
protected override void Dispose(bool disposing)
{
lock (DisposeLocker)
if (Mode.HasFlag(OpenMode.Write))
{
if (IsDisposed) return;
base.Dispose(disposing);
if (Mode.HasFlag(OpenMode.Write))
{
ParentFs.NotifyCloseWritableFile();
}
ParentFs.NotifyCloseWritableFile();
}
}
}

View File

@ -114,7 +114,7 @@ namespace LibHac.FsSystem
Result rc = BaseFs.OpenFile(out IFile baseFile, fullPath, mode);
if (rc.IsFailure()) return rc;
file = new DirectorySaveDataFile(this, baseFile);
file = new DirectorySaveDataFile(this, baseFile, mode);
if (mode.HasFlag(OpenMode.Write))
{

View File

@ -83,29 +83,4 @@ namespace LibHac.FsSystem
IsDisposed = true;
}
}
/// <summary>
/// Specifies which operations are available on an <see cref="IFile"/>.
/// </summary>
[Flags]
public enum OpenMode
{
Read = 1,
Write = 2,
AllowAppend = 4,
ReadWrite = Read | Write
}
[Flags]
public enum ReadOption
{
None = 0
}
[Flags]
public enum WriteOption
{
None = 0,
Flush = 1
}
}

View File

@ -152,7 +152,7 @@ namespace LibHac.FsSystem
public static IStorage AsStorage(this IFile file) => new FileStorage(file);
public static Stream AsStream(this IFile file) => new NxFileStream(file, true);
public static Stream AsStream(this IFile file, bool keepOpen) => new NxFileStream(file, keepOpen);
public static Stream AsStream(this IFile file, OpenMode mode, bool keepOpen) => new NxFileStream(file, mode, keepOpen);
public static IFile AsIFile(this Stream stream, OpenMode mode) => new StreamFile(stream, mode);

View File

@ -4,49 +4,51 @@ using LibHac.Fs;
namespace LibHac.FsSystem
{
public class LocalFile : FileBase
public class LocalFile : FileBase2
{
private const int ErrorHandleDiskFull = unchecked((int)0x80070027);
private const int ErrorDiskFull = unchecked((int)0x80070070);
private FileStream Stream { get; }
private StreamFile File { get; }
private OpenMode Mode { get; }
public LocalFile(string path, OpenMode mode)
{
Mode = mode;
Stream = OpenFile(path, mode);
File = new StreamFile(Stream, mode);
ToDispose.Add(File);
ToDispose.Add(Stream);
}
public override Result Read(out long bytesRead, long offset, Span<byte> destination, ReadOption options)
public override Result ReadImpl(out long bytesRead, long offset, Span<byte> destination, ReadOption options)
{
int toRead = ValidateReadParamsAndGetSize(destination, offset);
bytesRead = 0;
return File.Read(out bytesRead, offset, destination.Slice(0, toRead), options);
Result rc = ValidateReadParams(out long toRead, offset, destination.Length, Mode);
if (rc.IsFailure()) return rc;
return File.Read(out bytesRead, offset, destination.Slice(0, (int)toRead), options);
}
public override Result Write(long offset, ReadOnlySpan<byte> source, WriteOption options)
public override Result WriteImpl(long offset, ReadOnlySpan<byte> source, WriteOption options)
{
ValidateWriteParams(source, offset);
Result rc = ValidateWriteParams(offset, source.Length, Mode, out _);
if (rc.IsFailure()) return rc;
return File.Write(offset, source, options);
}
public override Result Flush()
public override Result FlushImpl()
{
return File.Flush();
}
public override Result GetSize(out long size)
public override Result GetSizeImpl(out long size)
{
return File.GetSize(out size);
}
public override Result SetSize(long size)
public override Result SetSizeImpl(long size)
{
try
{
@ -60,6 +62,16 @@ namespace LibHac.FsSystem
return Result.Success;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
File?.Dispose();
}
Stream?.Dispose();
}
private static FileAccess GetFileAccess(OpenMode mode)
{
// FileAccess and OpenMode have the same flags

View File

@ -8,11 +8,15 @@ namespace LibHac.FsSystem
{
private IFile BaseFile { get; }
private bool LeaveOpen { get; }
private OpenMode Mode { get; }
private long _length;
public NxFileStream(IFile baseFile, bool leaveOpen)
public NxFileStream(IFile baseFile, bool leaveOpen) : this(baseFile, OpenMode.ReadWrite, leaveOpen) { }
public NxFileStream(IFile baseFile, OpenMode mode, bool leaveOpen)
{
BaseFile = baseFile;
Mode = mode;
LeaveOpen = leaveOpen;
baseFile.GetSize(out _length).ThrowIfFailure();
@ -63,9 +67,9 @@ namespace LibHac.FsSystem
BaseFile.GetSize(out _length).ThrowIfFailure();
}
public override bool CanRead => BaseFile.Mode.HasFlag(OpenMode.Read);
public override bool CanRead => Mode.HasFlag(OpenMode.Read);
public override bool CanSeek => true;
public override bool CanWrite => BaseFile.Mode.HasFlag(OpenMode.Write);
public override bool CanWrite => Mode.HasFlag(OpenMode.Write);
public override long Length => _length;
public override long Position { get; set; }

View File

@ -1,5 +1,6 @@
using System;
using System.IO;
using LibHac.Fs;
namespace LibHac.FsSystem.Save
{

View File

@ -1,8 +1,10 @@
using System;
using System.Diagnostics;
namespace LibHac
{
[Serializable]
[DebuggerDisplay("{ToString()}")]
public struct Result : IEquatable<Result>
{
public readonly int Value;
@ -43,6 +45,11 @@ namespace LibHac
return this;
}
public override string ToString()
{
return IsSuccess() ? "Success" : ErrorCode;
}
public override bool Equals(object obj)
{
return obj is Result result && Equals(result);