Quasar/Quasar.Common/IO/FileSplit.cs

125 lines
4.2 KiB
C#

using Quasar.Common.Models;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
namespace Quasar.Common.IO
{
public class FileSplit : IEnumerable<FileChunk>, IDisposable
{
/// <summary>
/// The maximum size per file chunk.
/// </summary>
public readonly int MaxChunkSize = 65535;
/// <summary>
/// The file path of the opened file.
/// </summary>
public string FilePath => _fileStream.Name;
/// <summary>
/// The file size of the opened file.
/// </summary>
public long FileSize => _fileStream.Length;
/// <summary>
/// The file stream of the opened file.
/// </summary>
private readonly FileStream _fileStream;
/// <summary>
/// Initializes a new instance of the <see cref="FileSplit"/> class using the given file path and access mode.
/// </summary>
/// <param name="filePath">The path to the file to open.</param>
/// <param name="fileAccess">The file access mode for opening the file. Allowed are <see cref="FileAccess.Read"/> and <see cref="FileAccess.Write"/>.</param>
public FileSplit(string filePath, FileAccess fileAccess)
{
switch (fileAccess)
{
case FileAccess.Read:
_fileStream = File.OpenRead(filePath);
break;
case FileAccess.Write:
_fileStream = File.OpenWrite(filePath);
break;
default:
throw new ArgumentException($"{nameof(fileAccess)} must be either Read or Write.");
}
}
/// <summary>
/// Writes a chunk to the file. In other words.
/// </summary>
/// <param name="chunk"></param>
public void WriteChunk(FileChunk chunk)
{
_fileStream.Seek(chunk.Offset, SeekOrigin.Begin);
_fileStream.Write(chunk.Data, 0, chunk.Data.Length);
}
/// <summary>
/// Reads a chunk of the file.
/// </summary>
/// <param name="offset">Offset of the file, must be a multiple of <see cref="MaxChunkSize"/> for proper reconstruction.</param>
/// <returns>The read file chunk at the given offset.</returns>
/// <remarks>
/// The returned file chunk can be smaller than <see cref="MaxChunkSize"/> iff the
/// remaining file size from the offset is smaller than <see cref="MaxChunkSize"/>,
/// then the remaining file size is used.
/// </remarks>
public FileChunk ReadChunk(long offset)
{
_fileStream.Seek(offset, SeekOrigin.Begin);
long chunkSize = _fileStream.Length - _fileStream.Position < MaxChunkSize
? _fileStream.Length - _fileStream.Position
: MaxChunkSize;
var chunkData = new byte[chunkSize];
_fileStream.Read(chunkData, 0, chunkData.Length);
return new FileChunk
{
Data = chunkData,
Offset = _fileStream.Position - chunkData.Length
};
}
/// <summary>
/// Returns an enumerator that iterates through the file chunks.
/// </summary>
/// <returns>An <see cref="IEnumerator"/> object that can be used to iterate through the file chunks.</returns>
public IEnumerator<FileChunk> GetEnumerator()
{
for (long currentChunk = 0; currentChunk <= _fileStream.Length / MaxChunkSize; currentChunk++)
{
yield return ReadChunk(currentChunk * MaxChunkSize);
}
}
/// <inheritdoc />
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_fileStream.Dispose();
}
}
/// <summary>
/// Disposes all managed and unmanaged resources associated with this class.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}