public void ApplyChanges()
{
if (_readOnly)
throw new OperationCanceledException("File is in read-only mode");
// This method is implemented to efficiently save the changes to the same file stream opened for reading.
// Saving to a separate file would be a much simpler implementation.
// Firstly, extend the file length (if necessary) to ensure that there is enough disk space.
if (_totalLength > _stream.Length)
{
_stream.SetLength(_totalLength);
}
// Secondly, shift around any file sections that have moved.
long dataOffset = 0;
for (DataBlock block = _dataMap.FirstBlock; block != null; block = block.NextBlock)
{
FileDataBlock fileBlock = block as FileDataBlock;
if (fileBlock != null && fileBlock.FileOffset != dataOffset)
{
MoveFileBlock(fileBlock, dataOffset);
}
dataOffset += block.Length;
}
// Next, write in-memory changes.
dataOffset = 0;
for (DataBlock block = _dataMap.FirstBlock; block != null; block = block.NextBlock)
{
MemoryDataBlock memoryBlock = block as MemoryDataBlock;
if (memoryBlock != null)
{
_stream.Position = dataOffset;
for (int memoryOffset = 0; memoryOffset < memoryBlock.Length; memoryOffset += COPY_BLOCK_SIZE)
{
_stream.Write(memoryBlock.Data, memoryOffset, (int)Math.Min(COPY_BLOCK_SIZE, memoryBlock.Length - memoryOffset));
}
}
dataOffset += block.Length;
}
// Finally, if the file has shortened, truncate the stream.
_stream.SetLength(_totalLength);
ReInitialize();
}