Example #1
0
        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);
            }
        }