using Quasar.Common.Models; using System; using System.Collections; using System.Collections.Generic; using System.IO; namespace Quasar.Common.IO { public class FileSplit : IEnumerable, IDisposable { /// /// The maximum size per file chunk. /// public readonly int MaxChunkSize = 65535; /// /// The file path of the opened file. /// public string FilePath => _fileStream.Name; /// /// The file size of the opened file. /// public long FileSize => _fileStream.Length; /// /// The file stream of the opened file. /// private readonly FileStream _fileStream; /// /// Initializes a new instance of the class using the given file path and access mode. /// /// The path to the file to open. /// The file access mode for opening the file. Allowed are and . 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."); } } /// /// Writes a chunk to the file. In other words. /// /// public void WriteChunk(FileChunk chunk) { _fileStream.Seek(chunk.Offset, SeekOrigin.Begin); _fileStream.Write(chunk.Data, 0, chunk.Data.Length); } /// /// Reads a chunk of the file. /// /// Offset of the file, must be a multiple of for proper reconstruction. /// The read file chunk at the given offset. /// /// The returned file chunk can be smaller than iff the /// remaining file size from the offset is smaller than , /// then the remaining file size is used. /// 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 }; } /// /// Returns an enumerator that iterates through the file chunks. /// /// An object that can be used to iterate through the file chunks. public IEnumerator GetEnumerator() { for (long currentChunk = 0; currentChunk <= _fileStream.Length / MaxChunkSize; currentChunk++) { yield return ReadChunk(currentChunk * MaxChunkSize); } } /// IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } protected virtual void Dispose(bool disposing) { if (disposing) { _fileStream.Dispose(); } } /// /// Disposes all managed and unmanaged resources associated with this class. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } } }