public static async Task <long> CopyBetweenFileBaseAsync(FileBase src, FileBase dest, CopyFileParams?param = null, ProgressReporterBase?reporter = null, long estimatedSize = -1, CancellationToken cancel = default, RefBool?readErrorIgnored = null, Ref <uint>?srcZipCrc = null, long truncateSize = -1) { if (param == null) { param = new CopyFileParams(); } if (reporter == null) { reporter = new NullProgressReporter(null); } if (readErrorIgnored == null) { readErrorIgnored = new RefBool(); } if (srcZipCrc == null) { srcZipCrc = new Ref <uint>(); } if (estimatedSize < 0) { estimatedSize = src.Size; } if (truncateSize >= 0) { estimatedSize = Math.Min(estimatedSize, truncateSize); } ZipCrc32 srcCrc = new ZipCrc32(); readErrorIgnored.Set(false); checked { long currentPosition = 0; if (param.IgnoreReadError) { long fileSize = src.Size; int errorCounter = 0; if (truncateSize >= 0) { fileSize = truncateSize; // Truncate } // Ignore read error mode using (MemoryHelper.FastAllocMemoryWithUsing(param.IgnoreReadErrorSectorSize, out Memory <byte> buffer)) { for (long pos = 0; pos < fileSize; pos += param.IgnoreReadErrorSectorSize) { int size = (int)Math.Min(param.IgnoreReadErrorSectorSize, (fileSize - pos)); Memory <byte> buffer2 = buffer.Slice(0, size); int readSize = 0; try { //if (pos >= 10000000 && pos <= (10000000 + 100000)) throw new ApplicationException("*err*"); readSize = await src.ReadRandomAsync(pos, buffer2, cancel); } catch (Exception ex) { errorCounter++; if (errorCounter >= 100) { // Skip error display if (errorCounter == 100) { Con.WriteError($"The read error counter of the file \"{src.FileParams.Path}\" exceeds 100. No more errors will be reported."); } } else { // Display the error Con.WriteError($"Ignoring the read error at the offset {pos} of the file \"{src.FileParams.Path}\". Error: {ex.Message}"); } readErrorIgnored.Set(true); } if (readSize >= 1) { await dest.WriteRandomAsync(pos, buffer2.Slice(0, readSize), cancel); } currentPosition = pos + readSize; reporter.ReportProgress(new ProgressData(currentPosition, fileSize)); } } } else if (param.AsyncCopy == false) { // Normal copy using (MemoryHelper.FastAllocMemoryWithUsing(param.BufferSize, out Memory <byte> buffer)) { while (true) { Memory <byte> thisTimeBuffer = buffer; if (truncateSize >= 0) { // Truncate long remainSize = Math.Max(truncateSize - currentPosition, 0); if (thisTimeBuffer.Length > remainSize) { thisTimeBuffer = thisTimeBuffer.Slice(0, (int)remainSize); } if (remainSize == 0) { break; } } int readSize = await src.ReadAsync(thisTimeBuffer, cancel); Debug.Assert(readSize <= thisTimeBuffer.Length); if (readSize <= 0) { break; } ReadOnlyMemory <byte> sliced = thisTimeBuffer.Slice(0, readSize); if (param.Flags.Bit(FileFlags.CopyFile_Verify)) { srcCrc.Append(sliced.Span); } await dest.WriteAsync(sliced, cancel); currentPosition += readSize; reporter.ReportProgress(new ProgressData(currentPosition, estimatedSize)); } } } else { // Async copy using (MemoryHelper.FastAllocMemoryWithUsing(param.BufferSize, out Memory <byte> buffer1)) { using (MemoryHelper.FastAllocMemoryWithUsing(param.BufferSize, out Memory <byte> buffer2)) { Task?lastWriteTask = null; int number = 0; int writeSize = 0; long currentReadPosition = 0; Memory <byte>[] buffers = new Memory <byte>[2] { buffer1, buffer2 }; while (true) { Memory <byte> buffer = buffers[(number++) % 2]; Memory <byte> thisTimeBuffer = buffer; if (truncateSize >= 0) { // Truncate long remainSize = Math.Max(truncateSize - currentReadPosition, 0); if (thisTimeBuffer.Length > remainSize) { thisTimeBuffer = thisTimeBuffer.Slice(0, (int)remainSize); } } int readSize = await src.ReadAsync(thisTimeBuffer, cancel); Debug.Assert(readSize <= buffer.Length); if (lastWriteTask != null) { await lastWriteTask; currentPosition += writeSize; reporter.ReportProgress(new ProgressData(currentPosition, estimatedSize)); } if (readSize <= 0) { break; } currentReadPosition += readSize; writeSize = readSize; ReadOnlyMemory <byte> sliced = buffer.Slice(0, writeSize); if (param.Flags.Bit(FileFlags.CopyFile_Verify)) { srcCrc.Append(sliced.Span); } lastWriteTask = dest.WriteAsync(sliced, cancel); } reporter.ReportProgress(new ProgressData(currentPosition, estimatedSize)); } } } srcZipCrc.Set(srcCrc.Value); return(currentPosition); } }