Add StreamFile. Make sure LocalFile closes the base file

This commit is contained in:
Alex Barney 2019-01-09 19:47:06 -06:00
parent 01a3bef903
commit 3db2c81b77
9 changed files with 157 additions and 25 deletions

View file

@ -44,7 +44,8 @@ namespace LibHac.IO
private long GetAesXtsFileSize(string path)
{
IFile file = BaseFileSystem.OpenFile(path, OpenMode.Read);
using (IFile file = BaseFileSystem.OpenFile(path, OpenMode.Read))
{
var buffer = new byte[8];
file.Read(buffer, 0);
@ -55,3 +56,4 @@ namespace LibHac.IO
}
}
}
}

View file

@ -28,7 +28,7 @@ namespace LibHac.IO
public void CreateDirectory(string path)
{
throw new NotImplementedException();
BaseFileSystem.CreateDirectory(path);
}
public void CreateFile(string path, long size)
@ -38,12 +38,12 @@ namespace LibHac.IO
public void DeleteDirectory(string path)
{
throw new NotImplementedException();
BaseFileSystem.DeleteDirectory(path);
}
public void DeleteFile(string path)
{
throw new NotImplementedException();
BaseFileSystem.DeleteFile(path);
}
public IDirectory OpenDirectory(string path, OpenDirectoryMode mode)
@ -77,17 +77,17 @@ namespace LibHac.IO
public bool DirectoryExists(string path)
{
throw new NotImplementedException();
return BaseFileSystem.DirectoryExists(path);
}
public bool FileExists(string path)
{
throw new NotImplementedException();
return BaseFileSystem.FileExists(path);
}
public void Commit()
{
throw new NotImplementedException();
BaseFileSystem.Commit();
}
}
}

View file

@ -9,7 +9,7 @@ namespace LibHac.IO
private IFile[] Sources { get; }
private long SplitFileSize { get; }
public ConcatenationFile(IList<IFile> sources, long splitFileSize, OpenMode mode)
internal ConcatenationFile(IList<IFile> sources, long splitFileSize, OpenMode mode)
{
Sources = sources.ToArray();
SplitFileSize = splitFileSize;
@ -22,6 +22,8 @@ namespace LibHac.IO
throw new ArgumentException($"Source file must have size {splitFileSize}");
}
}
ToDispose.AddRange(Sources);
}
public override int Read(Span<byte> destination, long offset)

View file

@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
namespace LibHac.IO
{
public abstract class FileBase : IFile
{
private bool _isDisposed;
protected List<IDisposable> ToDispose { get; } = new List<IDisposable>();
public abstract int Read(Span<byte> destination, long offset);
public abstract void Write(ReadOnlySpan<byte> source, long offset);
@ -12,7 +14,7 @@ namespace LibHac.IO
public abstract long GetSize();
public abstract void SetSize(long size);
protected OpenMode Mode { get; set; }
public OpenMode Mode { get; protected set; }
protected int ValidateReadParamsAndGetSize(ReadOnlySpan<byte> span, long offset)
{
@ -66,6 +68,11 @@ namespace LibHac.IO
if (disposing)
{
Flush();
foreach (IDisposable item in ToDispose)
{
item?.Dispose();
}
}
_isDisposed = true;

View file

@ -98,8 +98,10 @@ namespace LibHac.IO
}
}
public static Stream AsStream(this IFile storage) => new NxFileStream(storage, true);
public static Stream AsStream(this IFile storage, bool keepOpen) => new NxFileStream(storage, keepOpen);
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 IFile AsIFile(this Stream stream, OpenMode mode) => new StreamFile(stream, mode);
public static int GetEntryCount(this IFileSystem fs, OpenDirectoryMode mode)
{

View file

@ -4,6 +4,7 @@ namespace LibHac.IO
{
public interface IFile : IDisposable
{
OpenMode Mode { get; }
int Read(Span<byte> destination, long offset);
void Write(ReadOnlySpan<byte> source, long offset);
void Flush();

View file

@ -6,20 +6,25 @@ namespace LibHac.IO
public class LocalFile : FileBase
{
private string Path { get; }
private StreamStorage Storage { get; }
private FileStream Stream { get; }
private StreamFile File { get; }
public LocalFile(string path, OpenMode mode)
{
Path = path;
Mode = mode;
Storage = new StreamStorage(new FileStream(Path, FileMode.Open), false);
Stream = new FileStream(Path, FileMode.Open, GetFileAccess(mode));
File = new StreamFile(Stream, mode);
ToDispose.Add(File);
ToDispose.Add(Stream);
}
public override int Read(Span<byte> destination, long offset)
{
int toRead = ValidateReadParamsAndGetSize(destination, offset);
Storage.Read(destination.Slice(0, toRead), offset);
File.Read(destination.Slice(0, toRead), offset);
return toRead;
}
@ -28,22 +33,28 @@ namespace LibHac.IO
{
ValidateWriteParams(source, offset);
Storage.Write(source, offset);
File.Write(source, offset);
}
public override void Flush()
{
Storage.Flush();
File.Flush();
}
public override long GetSize()
{
return Storage.Length;
return File.GetSize();
}
public override void SetSize(long size)
{
throw new NotImplementedException();
File.SetSize(size);
}
private static FileAccess GetFileAccess(OpenMode mode)
{
// FileAccess and OpenMode have the same flags
return (FileAccess)(mode & OpenMode.ReadWrite);
}
}
}

View file

@ -60,9 +60,9 @@ namespace LibHac.IO
}
// todo access
public override bool CanRead => true;
public override bool CanRead => BaseFile.Mode.HasFlag(OpenMode.Read);
public override bool CanSeek => true;
public override bool CanWrite => true;
public override bool CanWrite => BaseFile.Mode.HasFlag(OpenMode.Write);
public override long Length { get; }
public override long Position { get; set; }

107
src/LibHac/IO/StreamFile.cs Normal file
View file

@ -0,0 +1,107 @@
using System;
using System.IO;
#if !STREAM_SPAN
using System.Buffers;
#endif
namespace LibHac.IO
{
/// <summary>
/// Provides an <see cref="IFile"/> interface for interacting with a <see cref="Stream"/>
/// </summary>
public class StreamFile : FileBase
{
private Stream BaseStream { get; }
private object Locker { get; } = new object();
public StreamFile(Stream baseStream, OpenMode mode)
{
BaseStream = baseStream;
Mode = mode;
}
public override int Read(Span<byte> destination, long offset)
{
#if STREAM_SPAN
lock (Locker)
{
if (BaseStream.Position != offset)
{
BaseStream.Position = offset;
}
return BaseStream.Read(destination);
}
#else
byte[] buffer = ArrayPool<byte>.Shared.Rent(destination.Length);
try
{
int bytesRead;
lock (Locker)
{
if (BaseStream.Position != offset)
{
BaseStream.Position = offset;
}
bytesRead = BaseStream.Read(buffer, 0, destination.Length);
}
new Span<byte>(buffer, 0, destination.Length).CopyTo(destination);
return bytesRead;
}
finally { ArrayPool<byte>.Shared.Return(buffer); }
#endif
}
public override void Write(ReadOnlySpan<byte> source, long offset)
{
#if STREAM_SPAN
lock (Locker)
{
BaseStream.Position = offset;
BaseStream.Write(source);
}
#else
byte[] buffer = ArrayPool<byte>.Shared.Rent(source.Length);
try
{
source.CopyTo(buffer);
lock (Locker)
{
BaseStream.Position = offset;
BaseStream.Write(buffer, 0, source.Length);
}
}
finally { ArrayPool<byte>.Shared.Return(buffer); }
#endif
}
public override void Flush()
{
lock (Locker)
{
BaseStream.Flush();
}
}
public override long GetSize()
{
lock (Locker)
{
return BaseStream.Length;
}
}
public override void SetSize(long size)
{
lock (Locker)
{
BaseStream.SetLength(size);
}
}
}
}