Files
sharpcompress/src/SharpCompress/IO/SourceStream.cs

243 lines
6.4 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SharpCompress.Readers;
2022-12-20 15:06:44 +00:00
namespace SharpCompress.IO;
public class SourceStream : Stream
{
2022-12-20 15:06:44 +00:00
private long _prevSize;
2023-03-21 13:41:36 +00:00
private readonly List<FileInfo> _files;
private readonly List<Stream> _streams;
private readonly Func<int, FileInfo?> _getFilePart;
private readonly Func<int, Stream?> _getStreamPart;
2022-12-20 15:06:44 +00:00
private int _stream;
public SourceStream(FileInfo file, Func<int, FileInfo?> getPart, ReaderOptions options)
: this(null, null, file, getPart, options) { }
public SourceStream(Stream stream, Func<int, Stream?> getPart, ReaderOptions options)
: this(stream, getPart, null, null, options) { }
private SourceStream(
Stream? stream,
Func<int, Stream?>? getStreamPart,
FileInfo? file,
Func<int, FileInfo?>? getFilePart,
ReaderOptions options
)
{
2022-12-20 15:06:44 +00:00
ReaderOptions = options;
_files = new List<FileInfo>();
_streams = new List<Stream>();
IsFileMode = file != null;
IsVolumes = false;
2022-12-20 15:06:44 +00:00
if (!IsFileMode)
{
_streams.Add(stream!);
_getStreamPart = getStreamPart!;
2023-03-21 13:41:36 +00:00
_getFilePart = _ => null!;
if (stream is FileStream fileStream)
{
2023-03-21 13:41:36 +00:00
_files.Add(new FileInfo(fileStream.Name));
}
}
2022-12-20 15:06:44 +00:00
else
{
2022-12-20 15:06:44 +00:00
_files.Add(file!);
_streams.Add(_files[0].OpenRead());
_getFilePart = getFilePart!;
2023-03-21 13:41:36 +00:00
_getStreamPart = _ => null!;
}
2022-12-20 15:06:44 +00:00
_stream = 0;
_prevSize = 0;
}
2022-12-20 15:06:44 +00:00
public void LoadAllParts()
{
2022-12-20 15:14:22 +00:00
for (var i = 1; SetStream(i); i++) { }
2022-12-20 15:06:44 +00:00
SetStream(0);
}
2022-12-20 15:06:44 +00:00
public bool IsVolumes { get; set; }
2022-12-20 15:06:44 +00:00
public ReaderOptions ReaderOptions { get; }
public bool IsFileMode { get; }
2022-12-20 15:06:44 +00:00
public IEnumerable<FileInfo> Files => _files;
public IEnumerable<Stream> Streams => _streams;
2022-12-20 13:45:47 +00:00
2022-12-20 15:06:44 +00:00
private Stream Current => _streams[_stream];
public bool LoadStream(int index) //ensure all parts to id are loaded
{
while (_streams.Count <= index)
{
2022-12-20 15:06:44 +00:00
if (IsFileMode)
{
2022-12-20 15:06:44 +00:00
var f = _getFilePart(_streams.Count);
if (f == null)
{
2022-12-20 15:06:44 +00:00
_stream = _streams.Count - 1;
return false;
}
2022-12-20 15:06:44 +00:00
//throw new Exception($"File part {idx} not available.");
_files.Add(f);
_streams.Add(_files.Last().OpenRead());
}
else
{
var s = _getStreamPart(_streams.Count);
if (s == null)
{
2022-12-20 15:06:44 +00:00
_stream = _streams.Count - 1;
return false;
}
//throw new Exception($"Stream part {idx} not available.");
_streams.Add(s);
2023-03-21 13:41:36 +00:00
if (s is FileStream stream)
2022-12-20 15:06:44 +00:00
{
2023-03-21 13:41:36 +00:00
_files.Add(new FileInfo(stream.Name));
}
}
}
2022-12-20 15:06:44 +00:00
return true;
}
2022-12-20 13:45:47 +00:00
2022-12-20 15:06:44 +00:00
public bool SetStream(int idx) //allow caller to switch part in multipart
{
if (LoadStream(idx))
{
2022-12-20 15:06:44 +00:00
_stream = idx;
}
2022-12-20 15:06:44 +00:00
return _stream == idx;
}
2022-12-20 15:06:44 +00:00
public override bool CanRead => true;
2022-12-20 15:06:44 +00:00
public override bool CanSeek => true;
2022-12-20 15:06:44 +00:00
public override bool CanWrite => false;
2023-03-21 13:41:36 +00:00
public override long Length => !IsVolumes ? _streams.Sum(a => a.Length) : Current.Length;
2022-12-20 15:06:44 +00:00
public override long Position
{
2023-03-21 13:41:36 +00:00
get => _prevSize + Current.Position; //_prevSize is 0 for multi-volume
2022-12-20 15:06:44 +00:00
set => Seek(value, SeekOrigin.Begin);
}
2022-12-20 15:20:49 +00:00
public override void Flush() => Current.Flush();
2022-12-20 15:06:44 +00:00
public override int Read(byte[] buffer, int offset, int count)
{
if (count <= 0)
{
2022-12-20 15:06:44 +00:00
return 0;
}
2022-12-20 15:06:44 +00:00
var total = count;
var r = -1;
2022-12-20 15:06:44 +00:00
while (count != 0 && r != 0)
{
r = Current.Read(
buffer,
offset,
(int)Math.Min(count, Current.Length - Current.Position)
);
count -= r;
offset += r;
if (!IsVolumes && count != 0 && Current.Position == Current.Length)
{
2022-12-20 15:06:44 +00:00
var length = Current.Length;
// Load next file if present
if (!SetStream(_stream + 1))
{
2022-12-20 15:06:44 +00:00
break;
}
2022-12-20 15:06:44 +00:00
// Current stream switched
// Add length of previous stream
_prevSize += length;
Current.Seek(0, SeekOrigin.Begin);
2023-03-21 13:14:08 +00:00
r = -1; //BugFix: reset to allow loop if count is still not 0 - was breaking split zipx (lzma xz etc)
2022-12-20 15:06:44 +00:00
}
}
2022-12-20 15:06:44 +00:00
return total - count;
}
public override long Seek(long offset, SeekOrigin origin)
{
var pos = Position;
switch (origin)
{
2022-12-20 15:06:44 +00:00
case SeekOrigin.Begin:
pos = offset;
break;
case SeekOrigin.Current:
pos += offset;
break;
case SeekOrigin.End:
pos = Length + offset;
break;
}
2022-12-20 15:06:44 +00:00
_prevSize = 0;
if (!IsVolumes)
{
SetStream(0);
while (_prevSize + Current.Length < pos)
{
2022-12-20 15:06:44 +00:00
_prevSize += Current.Length;
SetStream(_stream + 1);
}
}
2022-12-20 15:06:44 +00:00
if (pos != _prevSize + Current.Position)
{
2022-12-20 15:06:44 +00:00
Current.Seek(pos - _prevSize, SeekOrigin.Begin);
}
2022-12-20 15:06:44 +00:00
return pos;
}
2022-12-20 15:20:49 +00:00
public override void SetLength(long value) => throw new NotImplementedException();
2022-12-20 15:20:49 +00:00
public override void Write(byte[] buffer, int offset, int count) =>
2022-12-20 15:06:44 +00:00
throw new NotImplementedException();
public override void Close()
{
if (IsFileMode || !ReaderOptions.LeaveStreamOpen) //close if file mode or options specify it
{
2022-12-20 15:06:44 +00:00
foreach (var stream in _streams)
{
2022-12-20 15:06:44 +00:00
try
{
2023-03-21 13:41:36 +00:00
stream.Dispose();
}
catch
{
// ignored
}
}
2022-12-20 15:06:44 +00:00
_streams.Clear();
_files.Clear();
}
2022-12-20 15:06:44 +00:00
}
2022-12-20 15:06:44 +00:00
protected override void Dispose(bool disposing)
{
Close();
base.Dispose(disposing);
}
}