Completely changed ForcedSeekStream workings to use a backing

file. The previous one worked with small files but failed
	miserably with big ones.
This commit is contained in:
2017-06-07 17:08:46 +01:00
parent 6b3c824f3e
commit 2f6714d8a0

View File

@@ -42,10 +42,11 @@ namespace DiscImageChef.Filters
public class ForcedSeekStream<T> : Stream where T : Stream public class ForcedSeekStream<T> : Stream where T : Stream
{ {
T baseStream; T baseStream;
long currentPosition;
object[] parameters; object[] parameters;
long streamLength; long streamLength;
const int bufferLen = 1048576; const int bufferLen = 1048576;
FileStream backStream;
string backFile;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="T:DiscImageChef.Filters.ForcedSeekStream`1"/> class. /// Initializes a new instance of the <see cref="T:DiscImageChef.Filters.ForcedSeekStream`1"/> class.
@@ -55,8 +56,10 @@ namespace DiscImageChef.Filters
public ForcedSeekStream(long length, params object[] args) public ForcedSeekStream(long length, params object[] args)
{ {
parameters = args; parameters = args;
Rewind();
streamLength = length; streamLength = length;
baseStream = (T)Activator.CreateInstance(typeof(T), parameters);
backFile = Path.GetTempFileName();
backStream = new FileStream(backFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
} }
/// <summary> /// <summary>
@@ -66,17 +69,10 @@ namespace DiscImageChef.Filters
public ForcedSeekStream(params object[] args) public ForcedSeekStream(params object[] args)
{ {
parameters = args; parameters = args;
Rewind();
streamLength = baseStream.Length;
}
/// <summary>
/// Rewinds the stream to start
/// </summary>
public void Rewind()
{
baseStream = (T)Activator.CreateInstance(typeof(T), parameters); baseStream = (T)Activator.CreateInstance(typeof(T), parameters);
currentPosition = 0; backFile = Path.GetTempFileName();
backStream = new FileStream(backFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
CalculateLength();
} }
/// <summary> /// <summary>
@@ -87,18 +83,17 @@ namespace DiscImageChef.Filters
/// <returns>The length.</returns> /// <returns>The length.</returns>
public void CalculateLength() public void CalculateLength()
{ {
long count = 0;
int read; int read;
Rewind();
do do
{ {
byte[] buffer = new byte[bufferLen]; byte[] buffer = new byte[bufferLen];
read = baseStream.Read(buffer, 0, bufferLen); read = baseStream.Read(buffer, 0, bufferLen);
count += read; backStream.Write(buffer, 0, read);
} }
while(read == bufferLen); while(read == bufferLen);
streamLength = count; streamLength = backStream.Length;
backStream.Position = 0;
} }
public override bool CanRead public override bool CanRead
@@ -129,7 +124,7 @@ namespace DiscImageChef.Filters
{ {
get get
{ {
return baseStream.Length; return streamLength;
} }
} }
@@ -137,34 +132,43 @@ namespace DiscImageChef.Filters
{ {
get get
{ {
return currentPosition; return backStream.Position;
} }
set set
{ {
if(value == currentPosition) SetPosition(value);
return;
if(value < currentPosition)
Rewind();
int fullBufferReads = (int)(value / bufferLen);
int restToRead = (int)(value % bufferLen);
byte[] buffer;
for(int i = 0; i < fullBufferReads; i++)
{
buffer = new byte[bufferLen];
baseStream.Read(buffer, 0, bufferLen);
}
buffer = new byte[restToRead];
baseStream.Read(buffer, 0, restToRead);
currentPosition = value;
} }
} }
void SetPosition(long position)
{
if(position == backStream.Position)
return;
if(position < backStream.Length)
{
backStream.Position = position;
return;
}
long toposition = position - backStream.Position;
int fullBufferReads = (int)(toposition / bufferLen);
int restToRead = (int)(toposition % bufferLen);
byte[] buffer;
for(int i = 0; i < fullBufferReads; i++)
{
buffer = new byte[bufferLen];
baseStream.Read(buffer, 0, bufferLen);
backStream.Write(buffer, 0, bufferLen);
}
buffer = new byte[restToRead];
baseStream.Read(buffer, 0, restToRead);
backStream.Write(buffer, 0, restToRead);
}
public override void Flush() public override void Flush()
{ {
baseStream.Flush(); baseStream.Flush();
@@ -172,22 +176,24 @@ namespace DiscImageChef.Filters
public override int Read(byte[] buffer, int offset, int count) public override int Read(byte[] buffer, int offset, int count)
{ {
int read = baseStream.Read(buffer, offset, count); if(backStream.Position + count > backStream.Length)
{
SetPosition(backStream.Position + count);
SetPosition(backStream.Position - count);
}
currentPosition += read; return backStream.Read(buffer, offset, count);
return read;
} }
public override int ReadByte() public override int ReadByte()
{ {
int byt = baseStream.ReadByte(); if(backStream.Position + 1 > backStream.Length)
{
SetPosition(backStream.Position + 1);
SetPosition(backStream.Position - 1);
}
// Because -1 equals end of stream so we cannot go farther return backStream.ReadByte();
if(byt > 0)
currentPosition++;
return byt;
} }
public override long Seek(long offset, SeekOrigin origin) public override long Seek(long offset, SeekOrigin origin)
@@ -197,21 +203,21 @@ namespace DiscImageChef.Filters
case SeekOrigin.Begin: case SeekOrigin.Begin:
if(offset < 0) if(offset < 0)
throw new IOException("Cannot seek before stream start."); throw new IOException("Cannot seek before stream start.");
Position = offset; SetPosition(offset);
break; break;
case SeekOrigin.End: case SeekOrigin.End:
if(offset > 0) if(offset > 0)
throw new IOException("Cannot seek after stream end."); throw new IOException("Cannot seek after stream end.");
if(streamLength == 0) if(streamLength == 0)
CalculateLength(); CalculateLength();
Position = streamLength + offset; SetPosition(streamLength + offset);
break; break;
default: default:
Position = currentPosition + offset; SetPosition(backStream.Position + offset);
break; break;
} }
return currentPosition; return backStream.Position;
} }
public override void SetLength(long value) public override void SetLength(long value)
@@ -223,6 +229,12 @@ namespace DiscImageChef.Filters
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
public override void Close()
{
backStream.Close();
File.Delete(backFile);
}
} }
} }