253 lines
5.5 KiB
C#
253 lines
5.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.IO;
|
|
|
|
|
|
namespace skyscraper8.SophtainerIO
|
|
{
|
|
public delegate void FileStreamPartSwitchDelegate(int readSoFar, int currentOffset, int remaining);
|
|
|
|
internal class SplitFileStream : Stream
|
|
{
|
|
private string baseFileName;
|
|
private uint partSize;
|
|
private int currentPart;
|
|
private FileStream currentPartStream;
|
|
public event FileStreamPartSwitchDelegate PartChangedDuringRead;
|
|
public SplitFileStream(string basename, uint partSize = 4294967295)
|
|
{
|
|
this.baseFileName = basename;
|
|
this.partSize = partSize;
|
|
this.currentPart = -1;
|
|
AcquireCurrentPart();
|
|
}
|
|
|
|
private int CountParts()
|
|
{
|
|
int result = 0;
|
|
while (true)
|
|
{
|
|
string filename = baseFileName + "." + (66000 + result++);
|
|
if (!File.Exists(filename))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return result - 1;
|
|
}
|
|
|
|
private bool AcquireCurrentPart()
|
|
{
|
|
long requiredPart = Position;
|
|
requiredPart /= partSize;
|
|
if (currentPartStream != null)
|
|
currentPartStream.Position = internalPosition % partSize;
|
|
if (requiredPart == currentPart)
|
|
return false;
|
|
|
|
if (currentPartStream != null)
|
|
{
|
|
currentPartStream.Flush();
|
|
currentPartStream.Close();
|
|
currentPartStream.Dispose();
|
|
}
|
|
|
|
string filename = baseFileName + "." + (66000 + requiredPart).ToString();
|
|
currentPartStream = File.Open(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
|
|
currentPartStream.Position = internalPosition % partSize;
|
|
int oldPart = currentPart;
|
|
currentPart = (int)requiredPart;
|
|
return true;
|
|
}
|
|
|
|
public override bool CanRead
|
|
{
|
|
get
|
|
{
|
|
AcquireCurrentPart();
|
|
return currentPartStream.CanRead;
|
|
}
|
|
}
|
|
|
|
public override bool CanSeek
|
|
{
|
|
get
|
|
{
|
|
AcquireCurrentPart();
|
|
return currentPartStream.CanSeek;
|
|
}
|
|
}
|
|
|
|
public override bool CanWrite
|
|
{
|
|
get
|
|
{
|
|
AcquireCurrentPart();
|
|
return currentPartStream.CanWrite;
|
|
}
|
|
}
|
|
|
|
public override long Length
|
|
{
|
|
get
|
|
{
|
|
int totalParts = CountParts();
|
|
if (totalParts == 1)
|
|
{
|
|
AcquireCurrentPart();
|
|
return currentPartStream.Length;
|
|
}
|
|
|
|
if (totalParts == currentPart)
|
|
{
|
|
long result = 0;
|
|
for (int i = 0; i < totalParts - 1;i++)
|
|
{
|
|
string filename = baseFileName + (66000 + i);
|
|
FileInfo currentFilePart = new FileInfo(filename);
|
|
result += currentFilePart.Length;
|
|
}
|
|
result += currentPartStream.Length;
|
|
return result;
|
|
}
|
|
|
|
long result2 = 0;
|
|
for (int i = 0; i < totalParts; i++)
|
|
{
|
|
string filename2 = baseFileName + "." + (66000 + i);
|
|
FileInfo currentFilePart = new FileInfo(filename2);
|
|
result2 += currentFilePart.Length;
|
|
}
|
|
return result2;
|
|
}
|
|
}
|
|
|
|
private long internalPosition;
|
|
public override long Position
|
|
{
|
|
get
|
|
{
|
|
return internalPosition;
|
|
}
|
|
set
|
|
{
|
|
if (value == 0)
|
|
{
|
|
if (currentPartStream != null)
|
|
{
|
|
currentPartStream.Flush();
|
|
currentPartStream.Close();
|
|
}
|
|
currentPartStream = null;
|
|
currentPart = -1;
|
|
internalPosition = 0;
|
|
return;
|
|
}
|
|
|
|
long maxValue = Length;
|
|
if (value > Length)
|
|
{
|
|
throw new ArgumentOutOfRangeException();
|
|
}
|
|
internalPosition = value;
|
|
}
|
|
}
|
|
|
|
public override void Flush()
|
|
{
|
|
if (currentPartStream != null)
|
|
{
|
|
currentPartStream.Flush();
|
|
}
|
|
}
|
|
|
|
public override int Read(byte[] buffer, int offset, int count)
|
|
{
|
|
int overallResult = 0;
|
|
while (count > 0)
|
|
{
|
|
if (AcquireCurrentPart())
|
|
{
|
|
if (overallResult > 0)
|
|
{
|
|
if (PartChangedDuringRead != null)
|
|
{
|
|
PartChangedDuringRead(overallResult, offset, count);
|
|
}
|
|
}
|
|
}
|
|
long currentPartOffset = currentPartStream.Position;
|
|
long currentPartRemaining = partSize - currentPartOffset;
|
|
long readAmount = Math.Min(currentPartRemaining, count);
|
|
int result = currentPartStream.Read(buffer, offset, (int)readAmount);
|
|
if (result == 0)
|
|
break;
|
|
count -= result;
|
|
offset += result;
|
|
overallResult += result;
|
|
internalPosition += result;
|
|
}
|
|
return overallResult;
|
|
}
|
|
|
|
public override long Seek(long offset, SeekOrigin origin)
|
|
{
|
|
switch(origin)
|
|
{
|
|
case SeekOrigin.Begin:
|
|
Position = offset;
|
|
break;
|
|
case SeekOrigin.Current:
|
|
Position += offset;
|
|
break;
|
|
case SeekOrigin.End:
|
|
long maxPos = Length;
|
|
Position = maxPos + offset;
|
|
break;
|
|
default:
|
|
throw new NotImplementedException(origin.ToString());
|
|
}
|
|
return Position;
|
|
}
|
|
|
|
public override void SetLength(long value)
|
|
{
|
|
long currentLength = Length;
|
|
if (value > currentLength)
|
|
{
|
|
Seek(currentLength, SeekOrigin.Begin);
|
|
long neededToWrite = value - currentLength;
|
|
byte[] blankBuffer = new byte[4096];
|
|
while (neededToWrite > 0)
|
|
{
|
|
long partSize = Math.Min(blankBuffer.Length, neededToWrite);
|
|
Write(blankBuffer, 0, (int)partSize);
|
|
neededToWrite -= partSize;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw new NotSupportedException("shrink");
|
|
}
|
|
}
|
|
|
|
public override void Write(byte[] buffer, int offset, int count)
|
|
{
|
|
while (count > 0)
|
|
{
|
|
AcquireCurrentPart();
|
|
long currentPartOffset = currentPartStream.Position;
|
|
long currentPartRemaining = partSize - currentPartOffset;
|
|
long operationSize = Math.Min(count, currentPartRemaining);
|
|
currentPartStream.Write(buffer, offset, (int)operationSize);
|
|
offset += (int)operationSize;
|
|
count -= (int)operationSize;
|
|
internalPosition += operationSize;
|
|
}
|
|
}
|
|
}
|
|
}
|