コード例 #1
0
    protected async Task <List <string> > ReadLinesInternalAsync(Func <MemoryStream, object?, bool>?finishDeterminer = null, CancellationToken cancel = default)
    {
        this.Sock.Stream.ReadTimeout = Settings.RecvTimeout;

        BinaryLineReaderOption opt = new BinaryLineReaderOption(finishDeterminer, (ms, p, c) =>
        {
            string previewLine = MemoryToString(ms);

            string tmp      = "";
            string?hostname = this.Settings.TargetHostName._Split(StringSplitOptions.RemoveEmptyEntries, ".").FirstOrDefault();
            if (hostname._IsFilled())
            {
                tmp = hostname + ":";
            }

            if (Settings.PrintDebug)
            {
                Con.WriteInfo($"[{tmp}RX] {previewLine}");
            }
        });

        RefBool isEof = new RefBool();

        var result = await Reader.ReadLinesUntilEofAsync(isEof, cancel : cancel, option : opt);

        if (result == null || isEof)
        {
            // 期待されたプロンプト等が現われず EOF に達してしまったので、途中のごみは出さずに切断されたことにする
            await this.Sock.DisconnectAsync();

            throw new DisconnectedException();
        }

        return(result._ToStringList(this.Settings.Encoding));
    }
コード例 #2
0
 public static string RegisterRefBool(RefBool r)
 {
     return(RegisterCallback(() =>
     {
         return r.Value;
     }));
 }
コード例 #3
0
        public IEnumerator StopAllCoroutines_SpecificOwnerArg_StopsAllCoroutinesCorrectly()
        {
            OwnerOfRoutine ownerA        = new OwnerOfRoutine();
            RefBool        wasCancelledA = new RefBool();
            RefBool        wasCancelledB = new RefBool();

            // Test coroutines will be stopped. Check in 1 second for expected results. Test coroutines should finish by then, successfully or not.
            DateTime     then          = DateTime.Now;
            const double secondsToWait = 1;

            TestContext.WriteLine("Starting SimpleIteratorToBeStoppedB and ownerA.SimpleIteratorToBeStoppedA routines.\nWill automatically attempt to stop routines belonging to ownerA after a frame.");
            // All the following coroutines will be attempted to be cancelled after 1 frame, in the line after the following `yield return null`.
            EditorCoroutineService.StartCoroutine(ownerA, "SimpleIteratorToBeStoppedA", new object[] { wasCancelledA });
            EditorCoroutineService.StartCoroutine(SimpleIteratorToBeStoppedB(wasCancelledB));
            yield return(null);

            EditorCoroutineService.StopAllCoroutines(ownerA);

            double deltaSeconds = (DateTime.Now - then).TotalSeconds;

            while (deltaSeconds < secondsToWait)
            {
                yield return(null);

                deltaSeconds = (DateTime.Now - then).TotalSeconds;
            }

            Assert.IsTrue(wasCancelledA.Value && !wasCancelledB.Value, "EditorCoroutines belonging to a specific owner were not stopped correctly.");
        }
コード例 #4
0
        private IEnumerator SimpleIterator(RefBool isTestComplete)
        {
            TestContext.WriteLine("In SimpleIterator routine. Starting.");
            yield return(null);

            isTestComplete.Value = true;
            TestContext.WriteLine("In SimpleIterator routine. Finished.");
        }
コード例 #5
0
        private IEnumerator ReturnAfterSecondsToBeStopped(float secondsToWait, RefBool isTestComplete)
        {
            TestContext.WriteLine($"In ReturnAfterSecondsToBeStopped routine. Starting. Will automatically finish in {secondsToWait} seconds.");
            yield return(new WaitForSeconds(secondsToWait));

            isTestComplete.Value = true;
            TestContext.WriteLine("In ReturnAfterSecondsToBeStopped routine. Finished.");
        }
コード例 #6
0
        private IEnumerator ReturnAfterSeconds(float seconds, RefBool isTestComplete)
        {
            TestContext.WriteLine("In ReturnAfterSeconds routine. Starting.");
            yield return(new WaitForSeconds(seconds));

            isTestComplete.Value = true;
            TestContext.WriteLine($"In ReturnAfterSeconds routine. Finished after waiting for {seconds} seconds.");
        }
コード例 #7
0
 public MonitorActivator(int dueTime, bool isActive = false, int waitTime = 10_000)
 {
     IsActive = new RefBool(isActive);
     Timer    = new Timer(s => IsActive.Value = true,
                          null,
                          dueTime,
                          waitTime);
 }
コード例 #8
0
        public IEnumerator StartCoroutine_IEnumeratorArg_StartsCorrectly()
        {
            RefBool isTestComplete = new RefBool();

            EditorCoroutineService.StartCoroutine(SimpleIterator(isTestComplete));
            yield return(null);

            Assert.IsTrue(isTestComplete.Value, "EditorCoroutine was not started correctly via IEnumerator arg.");
        }
コード例 #9
0
    public void GlobalLockTest()
    {
        GlobalLock k = new GlobalLock("test_lock1");

        Event   startEvent = new Event(true);
        Event   event2     = new Event(true);
        Event   event3     = new Event(true);
        RefBool fail       = new RefBool();

        var thread1 = ThreadObj.Start(x =>
        {
            try
            {
                startEvent.Wait();

                using (k.Lock())
                {
                    event2.Set();

                    if (event3.Wait(100))
                    {
                        fail.Set(true);
                    }
                }
            }
            catch
            {
                fail.Set(true);
            }
        });

        var thread2 = ThreadObj.Start(x =>
        {
            try
            {
                startEvent.Wait();

                event2.Wait();

                using (k.Lock())
                {
                    event3.Set();
                }
            }
            catch
            {
                fail.Set(true);
            }
        });

        startEvent.Set();

        Assert.True(thread1.WaitForEnd(5000));
        Assert.True(thread2.WaitForEnd(5000));

        Assert.False(fail.Value);
    }
コード例 #10
0
        public IEnumerator StartCoroutine_MethodNameArg_StartsCorrectly()
        {
            RefBool      isTestComplete = new RefBool();
            object       owner          = this;
            const string methodName     = "SimpleIterator";

            EditorCoroutineService.StartCoroutine(owner, methodName, new object[] { isTestComplete });
            yield return(null);

            Assert.IsTrue(isTestComplete.Value, $"EditorCoroutine was not started correctly via method name {methodName}.");
        }
コード例 #11
0
        private IEnumerator SimpleIteratorToBeStoppedB(RefBool wasCancelled)
        {
            TestContext.WriteLine("In SimpleIteratorToBeStoppedB routine. Starting.");
            yield return(null);

            TestContext.WriteLine("In SimpleIteratorToBeStoppedB routine. This routine may be stopped now. If so, this should be the last log from this method.");
            wasCancelled.Value = true;
            yield return(null);

            wasCancelled.Value = false;
            TestContext.WriteLine("In SimpleIteratorToBeStoppedB routine. Finished. This line should not be logged if the coroutine was stopped successfully.");
        }
コード例 #12
0
        public IEnumerator StartCoroutine_IndependentCoroutinesInParallel_ReturnInCorrectOrder()
        {
            RefBool isTestComplete = new RefBool();

            _parallelRoutinesTracker = new Queue <char> ();

            EditorCoroutineService.StartCoroutine(ReturnAfterRoutinesRunInParallel(isTestComplete));
            while (!isTestComplete.Value)
            {
                yield return(null);
            }

            Assert.AreEqual(0, _parallelRoutinesTracker.Count, "EditorCoroutines started in parallel were not run in the correct order.");
        }
コード例 #13
0
        private IEnumerator ReturnAfterCustomYieldInstructionSucceeds(RefBool isTestComplete)
        {
            TestContext.WriteLine("In ReturnAfterCustomYieldInstructionSucceeds routine. Starting.");
            EditorCoroutineService.StartCoroutine(SetIndexAfterSeconds(2f));
            yield return(new CustomWaitWhile(IsIndexStillZero));

            TestContext.WriteLine("In ReturnAfterCustomYieldInstructionSucceeds routine. Finished.");
            isTestComplete.Value = true;

            bool IsIndexStillZero()
            {
                return(_customIndex == 0);
            }
        }
コード例 #14
0
        public IEnumerator StartCoroutine_CustomYieldInstruction_ReturnsCorrectly()
        {
            RefBool isTestComplete = new RefBool();

            _customIndex = 0;

            EditorCoroutineService.StartCoroutine(ReturnAfterCustomYieldInstructionSucceeds(isTestComplete));
            while (!isTestComplete.Value)
            {
                yield return(null);
            }

            Assert.AreEqual(1, _customIndex, "EditorCoroutine yielding on a CustomYieldInstruction was not returned correctly.");
        }
コード例 #15
0
        private IEnumerator ReturnAfterRoutinesRunInParallel(RefBool isTestComplete)
        {
            const float secondsToWaitInEachRoutine = 2f;
            const float secondRoutineStartDelay    = 0.5f;

            TestContext.WriteLine("In ReturnAfterRoutinesRunInParallel routine. Starting.");
            _parallelRoutinesTracker.Enqueue('a');
            EditorCoroutineService.StartCoroutine(ReturnAfterSecondsA(secondsToWaitInEachRoutine));
            TestContext.WriteLine($"In ReturnAfterRoutinesRunInParallel routine. Waiting for {secondRoutineStartDelay} seconds.");
            yield return(new WaitForSeconds(secondRoutineStartDelay));

            _parallelRoutinesTracker.Enqueue('b');
            EditorCoroutineService.StartCoroutine(ReturnAfterSecondsB(secondsToWaitInEachRoutine, isTestComplete));
            TestContext.WriteLine("In ReturnAfterRoutinesRunInParallel routine. Finished, though an EditorCoroutine started in parallel may still be running.");
        }
コード例 #16
0
        public IEnumerator StartCoroutine_YieldWaitForAsyncOperation_ReturnsCorrectly()
        {
            RefBool isTestComplete = new RefBool();

            _webRequestData = null;
            const string uri = "http://httpbin.org/get";

            EditorCoroutineService.StartCoroutine(ReturnAfterWebRequest(uri, isTestComplete));
            while (!isTestComplete.Value)
            {
                yield return(null);
            }

            Assert.IsNotNull(_webRequestData,
                             $"WebRequest EditorCoroutine did not return any data.\nThis may be an issue with the URI {uri} itself, but for now, this test has failed.");
        }
コード例 #17
0
        private IEnumerator ReturnAfterSecondsB(float secondsToWait, RefBool isTestComplete)
        {
            TestContext.WriteLine($"In ReturnAfterSecondsB routine. Starting. Will automatically finish in {secondsToWait} seconds.");
            yield return(new WaitForSeconds(secondsToWait));

            if (_parallelRoutinesTracker.Peek() == 'b')
            {
                _parallelRoutinesTracker.Dequeue();
            }
            else
            {
                throw new Exception("Parallel EditorCoroutine B is run in wrong order.");
            }

            isTestComplete.Value = true;
            TestContext.WriteLine("In ReturnAfterSecondsB routine. Finished.");
        }
コード例 #18
0
        private IEnumerator ReturnAfterWebRequest(string uri, RefBool isTestComplete)
        {
            TestContext.WriteLine("In ReturnAfterWebRequest routine. Starting.");
            UnityWebRequest www = UnityWebRequest.Get(uri);

            yield return(www.SendWebRequest());

            if (www.isNetworkError || www.isHttpError)
            {
                TestContext.WriteLine($"Request has returned with error:\n{www.error}");
                throw new Exception(www.error);
            }

            TestContext.WriteLine($"Request has returned successfully with text:\n{www.downloadHandler.text}");
            _webRequestData      = www.downloadHandler.data;
            isTestComplete.Value = true;
            TestContext.WriteLine("In ReturnAfterWebRequest routine. Finished.");
        }
コード例 #19
0
        public IEnumerator StartCoroutine_YieldWaitForNestedCoroutines_ReturnsInCorrectOrder()
        {
            RefBool isTestComplete = new RefBool();

            _nestedRoutinesTracker = new Stack <int> ();
            for (int i = 0; i < 3; i++)
            {
                _nestedRoutinesTracker.Push(i);
            }

            EditorCoroutineService.StartCoroutine(ReturnAfterNestedRoutines(isTestComplete));
            while (!isTestComplete.Value)
            {
                yield return(null);
            }

            Assert.AreEqual(0, _nestedRoutinesTracker.Count, "Nested EditorCoroutines were not run in the correct order.");
        }
コード例 #20
0
        public IEnumerator StartCoroutine_YieldWaitForSeconds_SuccessAfterSeconds()
        {
            RefBool     isTestComplete = new RefBool();
            DateTime    then           = DateTime.Now;
            const float secondsToWait  = 2f;

            EditorCoroutineService.StartCoroutine(ReturnAfterSeconds(secondsToWait, isTestComplete));
            while (!isTestComplete.Value)
            {
                yield return(null);
            }

            double deltaSeconds = (DateTime.Now - then).TotalSeconds;

            Assert.AreEqual(secondsToWait,
                            deltaSeconds,
                            0.024,  // A little over a single frame margin of error at 60 fps, which Unity Editor _definitely_ runs at. /s
                            $"WaitForSeconds EditorCoroutine returned in {deltaSeconds}, when expected to return in {secondsToWait} seconds.");
        }
コード例 #21
0
    public async Task <List <string>?> GetHostNameListAsync(IPAddress?ip, Ref <DnsAdditionalResults>?additional = null, CancellationToken cancel = default, bool noCache = false)
    {
        if (noCache)
        {
            return(await GetHostNameListCoreAsync(ip, additional, cancel));
        }

        if (ip == null)
        {
            return(null);
        }

        var ipType = ip._GetIPAddressType();

        if (ipType.Bit(IPAddressType.Loopback))
        {
            return("localhost"._SingleList());
        }

        string ipStr = ip.ToString();

        RefBool found = new RefBool();

        ReverseLookupCacheItem?item = await this.ReverseLookupCache.GetOrCreateAsync(ipStr, async ipStr =>
        {
            Ref <DnsAdditionalResults> additionals = new Ref <DnsAdditionalResults>();

            List <string>?value = await GetHostNameListCoreAsync(ip, additionals, cancel);

            return(new ReverseLookupCacheItem(value, additionals.Value ?? new DnsAdditionalResults(true, false)));
        }, found);

        if (item == null)
        {
            additional?.Set(new DnsAdditionalResults(true, false, false));
            return(null);
        }

        additional?.Set(item.AdditionalResults.Clone(true));

        return(item.Value);
    }
コード例 #22
0
        private IEnumerator ReturnAfterNestedRoutines(RefBool isTestComplete)
        {
            const float secondsToWait = 1f;

            TestContext.WriteLine("In parent routine. Starting.");
            yield return(new WaitForSeconds(secondsToWait));

            yield return(EditorCoroutineService.StartCoroutine(ReturnAfterNestedRoutinesDepthOne()));

            if (_nestedRoutinesTracker.Count == 1)
            {
                _nestedRoutinesTracker.Pop();
            }
            else
            {
                throw new Exception("Parent routine is run in wrong order.");
            }

            isTestComplete.Value = true;
            TestContext.WriteLine("In parent routine. Finished.");
        }
コード例 #23
0
        public IEnumerator StopCoroutine_MethodName_StopsCorrectly()
        {
            RefBool isTestComplete = new RefBool();
            RefBool wasCancelled   = new RefBool();

            // Test coroutine will be stopped. Check in 1 second for expected results. Test coroutine should finish by then, successfully or not.
            EditorCoroutineService.StartCoroutine(ReturnAfterSecondsToBeStopped(1f, isTestComplete));

            TestContext.WriteLine("Starting SimpleIteratorToBeStopped routine.\nWill automatically attempt to stop it after a frame.");
            // This coroutine will be attempted to be cancelled after 1 frame, in the line after the following `yield return null`.
            EditorCoroutineService.StartCoroutine(this, "SimpleIteratorToBeStopped", new object[] { wasCancelled });
            yield return(null);

            EditorCoroutineService.StopCoroutine(this, "SimpleIteratorToBeStopped");

            while (!isTestComplete.Value)
            {
                yield return(null);
            }

            Assert.IsTrue(wasCancelled.Value, "EditorCoroutine was not stopped correctly via Method Name arg.");
        }
コード例 #24
0
        public static async Task CopyFileAsync(FileSystem srcFileSystem, string srcPath, FileSystem destFileSystem, string destPath,
                                               CopyFileParams?param = null, object?state = null, CancellationToken cancel = default, RefBool?readErrorIgnored = null)
        {
            if (readErrorIgnored == null)
            {
                readErrorIgnored = new RefBool(false);
            }

            readErrorIgnored.Set(false);

            if (param == null)
            {
                param = new CopyFileParams();
            }

            srcPath = await srcFileSystem.NormalizePathAsync(srcPath, cancel : cancel);

            destPath = await destFileSystem.NormalizePathAsync(destPath, cancel : cancel);

            if (srcFileSystem == destFileSystem)
            {
                if (srcFileSystem.PathParser.PathStringComparer.Equals(srcPath, destPath))
                {
                    throw new FileException(destPath, "Both source and destination is the same file.");
                }
            }

            using (ProgressReporterBase reporter = param.ProgressReporterFactory.CreateNewReporter($"CopyFile '{srcFileSystem.PathParser.GetFileName(srcPath)}'", state))
            {
                using (var srcFile = await srcFileSystem.OpenAsync(srcPath, flags: param.Flags, cancel: cancel))
                {
                    try
                    {
                        FileMetadata srcFileMetadata = await srcFileSystem.GetFileMetadataAsync(srcPath, param.MetadataCopier.OptimizedMetadataGetFlags, cancel);

                        bool destFileExists = await destFileSystem.IsFileExistsAsync(destPath, cancel);

                        using (var destFile = await destFileSystem.CreateAsync(destPath, flags: param.Flags, doNotOverwrite: !param.Overwrite, cancel: cancel))
                        {
                            try
                            {
                                reporter.ReportProgress(new ProgressData(0, srcFileMetadata.Size));

                                Ref <uint> srcZipCrc = new Ref <uint>();

                                long copiedSize = await CopyBetweenFileBaseAsync(srcFile, destFile, param, reporter, srcFileMetadata.Size, cancel, readErrorIgnored, srcZipCrc);

                                if (param.Flags.Bit(FileFlags.CopyFile_Verify) && param.IgnoreReadError == false)
                                {
                                    // Verify を実施する
                                    // キャッシュを無効にするため、一度ファイルを閉じて再度開く

                                    await destFile.CloseAsync();

                                    using (var destFile2 = await destFileSystem.OpenAsync(destPath, flags: param.Flags, cancel: cancel))
                                    {
                                        uint destZipCrc = await CalcZipCrc32HandleAsync(destFile2, param, cancel);

                                        if (srcZipCrc.Value != destZipCrc)
                                        {
                                            // なんと Verify に失敗したぞ
                                            throw new CoresException($"CopyFile_Verify error. Src file: '{srcPath}', Dest file: '{destPath}', srcCrc: {srcZipCrc.Value}, destCrc = {destZipCrc}");
                                        }
                                    }
                                }

                                reporter.ReportProgress(new ProgressData(copiedSize, copiedSize, true));

                                await destFile.CloseAsync();

                                try
                                {
                                    await destFileSystem.SetFileMetadataAsync(destPath, param.MetadataCopier.Copy(srcFileMetadata), cancel);
                                }
                                catch (Exception ex)
                                {
                                    Con.WriteDebug($"CopyFileAsync: '{destPath}': SetFileMetadataAsync failed. Error: {ex.Message}");
                                }
                            }
                            catch
                            {
                                if (destFileExists == false)
                                {
                                    try
                                    {
                                        await destFile.CloseAsync();
                                    }
                                    catch { }

                                    try
                                    {
                                        await destFileSystem.DeleteFileAsync(destPath);
                                    }
                                    catch { }
                                }

                                throw;
                            }
                            finally
                            {
                                await destFile.CloseAsync();
                            }
                        }
                    }
                    finally
                    {
                        await srcFile.CloseAsync();
                    }
                }
            }
        }
コード例 #25
0
    // ランダムに配列した各テストを 1 秒間隔で順に実行していき、1 つでも成功したら抜ける
    // 1 つでも成功した場合は true、成功するまでにネットワークの状態が変化した場合は false を返す
    async Task <bool> StartEveryTestAsync(CancellationToken cancel, AsyncAutoResetEvent?networkChangedEvent)
    {
        int startNetworkVersion = (networkChangedEvent == null) ? 0 : TcpIp.GetHostInfo(false).InfoVersion;

        CancellationTokenSource cts = new CancellationTokenSource();

        await using (TaskUtil.CreateCombinedCancellationToken(out CancellationToken opCancel, cancel, cts.Token))
        {
            // テストをシャッフルする
            var shuffledTests = Options.GetTestItemList()._Shuffle();

            List <Task <bool> > runningTaskList = new List <Task <bool> >();

            RefBool retBool = new RefBool();

            // シャッフルしたテストを実行する
            int num = 0;
            foreach (var test in shuffledTests)
            {
                Task <bool> t = AsyncAwait(async() =>
                {
                    //Con.WriteDebug($"{test} - Waiting {num} secs...");

                    if (await opCancel._WaitUntilCanceledAsync(1000 * num))
                    {
                        // キャンセルされた
                        return(false);
                    }

                    int numRetry = 0;

                    while (true)
                    {
                        if (opCancel.IsCancellationRequested)
                        {
                            return(false);
                        }

                        bool ret = await PerformSingleTestAsync(test, opCancel);

                        //Con.WriteDebug($"{test} - {ret}");

                        if (ret)
                        {
                            // 成功
                            retBool.Set(true);

                            // 自分自信のほか、他のタスクもすべてキャンセルする
                            cts._TryCancelNoBlock();
                            return(true);
                        }

                        // 再試行まで待機
                        numRetry++;

                        int retryInterval = Util.GenRandIntervalWithRetry(Options.RetryIntervalMin, numRetry, Options.RetryIntervalMax);

                        if (test is InternetCheckerPingTestItem)
                        {
                            retryInterval = Util.GenRandIntervalWithRetry(Options.RetryIntervalMin, numRetry, Options.RetryIntervalMaxPing);
                        }

                        await TaskUtil.WaitObjectsAsync(cancels: opCancel._SingleArray(), events: networkChangedEvent._SingleArray(),
                                                        timeout: retryInterval);

                        if (opCancel.IsCancellationRequested)
                        {
                            // キャンセルされた
                            return(false);
                        }

                        if (startNetworkVersion != 0)
                        {
                            int currentNetworkVersion = BackgroundState <PalHostNetInfo> .Current.Version;

                            if (startNetworkVersion != currentNetworkVersion)
                            {
                                // ネットワーク状態が変化した
                                // 自分自身のほか、他のタスクもすべてキャンセルする
                                cts._TryCancelNoBlock();
                                return(false);
                            }
                        }
                    }
                });

                runningTaskList.Add(t);

                num++;
            }

            // 実行中のテストすべてを待機する
            await Task.WhenAll(runningTaskList);

            return(retBool);
        }
    }
コード例 #26
0
    public void SingleInstanceTest()
    {
        Event   startEvent = new Event(true);
        RefBool fail       = new RefBool();
        Event   event2     = new Event(true);
        Event   event3     = new Event(true);
        Event   event4     = new Event(true);
        Event   event5     = new Event(true);
        Event   event6     = new Event(true);

        var thread1 = ThreadObj.Start(x =>
        {
            try
            {
                startEvent.Wait();

                using (var si2 = new SingleInstance("si_test"))
                {
                    event2.Set();

                    event3.Wait();
                }

                event4.Set();

                event5.Wait();

                var si = SingleInstance.TryGet("si_test");
                if (si != null)
                {
                    fail.Set(true);
                }

                event6.Set();
            }
            catch
            {
                fail.Set(true);
            }
        });

        var thread2 = ThreadObj.Start(x =>
        {
            try
            {
                startEvent.Wait();

                event2.Wait();

                var si = SingleInstance.TryGet("si_test");
                if (si != null)
                {
                    fail.Set(true);
                }

                event3.Set();

                event4.Wait();

                si = SingleInstance.TryGet("si_test");
                if (si == null)
                {
                    fail.Set(true);
                }

                event5.Set();

                event6.Wait();

                si._DisposeSafe();
            }
            catch
            {
                fail.Set(true);
            }
        });

        startEvent.Set();

        Assert.True(thread1.WaitForEnd(5000));
        Assert.True(thread2.WaitForEnd(5000));

        Assert.False(fail.Value);
    }
コード例 #27
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);
            }
        }
コード例 #28
0
        public static async Task <CopyDirectoryStatus> CopyDirAsync(FileSystem srcFileSystem, string srcPath, FileSystem destFileSystem, string destPath,
                                                                    CopyDirectoryParams?param = null, object?state = null, CopyDirectoryStatus?statusObject = null, CancellationToken cancel = default)
        {
            CopyDirectoryStatus status = statusObject ?? new CopyDirectoryStatus();

            status.Clear();

            status.State     = state;
            status.StartTick = Tick64.Now;

            if (param == null)
            {
                param = new CopyDirectoryParams();
            }

            srcPath = await srcFileSystem.NormalizePathAsync(srcPath, cancel : cancel);

            destPath = await destFileSystem.NormalizePathAsync(destPath, cancel : cancel);

            if (srcFileSystem == destFileSystem)
            {
                if (srcFileSystem.PathParser.PathStringComparer.Equals(srcPath, destPath))
                {
                    throw new FileException(destPath, "Both source and destination is the same directory.");
                }
            }

            using (ProgressReporterBase dirReporter = param.EntireProgressReporterFactory.CreateNewReporter($"CopyDir '{srcFileSystem.PathParser.GetFileName(srcPath)}'", state))
            {
                DirectoryWalker walker  = new DirectoryWalker(srcFileSystem);
                bool            walkRet = await walker.WalkDirectoryAsync(srcPath,
                                                                          async (dirInfo, entries, c) =>
                {
                    c.ThrowIfCancellationRequested();

                    foreach (FileSystemEntity entity in entries)
                    {
                        c.ThrowIfCancellationRequested();

                        try
                        {
                            if (await param.ProgressCallbackProc(status, entity) == false)
                            {
                                throw new OperationCanceledException($"Copying of the file '{entity.FullPath}' is cancaled by the user.");
                            }

                            string entryName = entity.Name;
                            if (entity.IsCurrentDirectory)
                            {
                                entryName = "";
                            }

                            string srcFullPath  = srcFileSystem.PathParser.Combine(srcPath, dirInfo.RelativePath, entryName);
                            string destFullPath = destFileSystem.PathParser.Combine(destPath, srcFileSystem.PathParser.ConvertDirectorySeparatorToOtherSystem(dirInfo.RelativePath, destFileSystem.PathParser), entryName);

                            if (entity.IsDirectory == false)
                            {
                                // Copy a file
                                lock (status.LockObj)
                                    status.NumFilesTotal++;

                                FileMetadataGetFlags metadataGetFlags = FileMetadataCopier.CalcOptimizedMetadataGetFlags(param.FileMetadataCopier.Mode | FileMetadataCopyMode.Attributes);
                                FileMetadata srcFileMetadata          = await srcFileSystem.GetFileMetadataAsync(srcFullPath, metadataGetFlags, cancel);
                                FileFlags copyFileAdditionalFlags     = FileFlags.None;

                                lock (status.LockObj)
                                    status.SizeTotal += srcFileMetadata.Size;

                                if (param.CopyDirFlags.Bit(CopyDirectoryFlags.CopyFileCompressionFlag))
                                {
                                    if (srcFileMetadata.Attributes is FileAttributes attr)
                                    {
                                        if (attr.Bit(FileAttributes.Compressed))
                                        {
                                            copyFileAdditionalFlags |= FileFlags.OnCreateSetCompressionFlag;
                                        }
                                        else
                                        {
                                            copyFileAdditionalFlags |= FileFlags.OnCreateRemoveCompressionFlag;
                                        }
                                    }
                                }

                                if (param.CopyDirFlags.Bit(CopyDirectoryFlags.CopyFileSparseFlag))
                                {
                                    if (srcFileMetadata.Attributes is FileAttributes attr)
                                    {
                                        if (attr.Bit(FileAttributes.SparseFile))
                                        {
                                            copyFileAdditionalFlags |= FileFlags.SparseFile;
                                        }
                                    }
                                }

                                var copyFileParam = param.GenerateCopyFileParams(copyFileAdditionalFlags);

                                RefBool ignoredReadError = new RefBool(false);

                                await CopyFileAsync(srcFileSystem, srcFullPath, destFileSystem, destFullPath, copyFileParam, state, cancel, ignoredReadError);

                                lock (status.LockObj)
                                {
                                    status.NumFilesOk++;
                                    status.SizeOk += srcFileMetadata.Size;

                                    if (ignoredReadError)
                                    {
                                        status.IgnoreReadErrorFileNameList.Add(srcFullPath);
                                    }
                                }
                            }
                            else
                            {
                                // Make a directory
                                lock (status.LockObj)
                                {
                                    status.NumDirectoriesTotal++;
                                }

                                FileMetadataGetFlags metadataGetFlags = FileMetadataCopier.CalcOptimizedMetadataGetFlags(param.DirectoryMetadataCopier.Mode | FileMetadataCopyMode.Attributes);
                                FileMetadata srcDirMetadata           = await srcFileSystem.GetDirectoryMetadataAsync(srcFullPath, metadataGetFlags, cancel);
                                FileFlags copyDirFlags = FileFlags.None;

                                if (param.CopyDirFlags.Bit(CopyDirectoryFlags.BackupMode))
                                {
                                    copyDirFlags |= FileFlags.BackupMode;
                                }

                                if (param.CopyDirFlags.Bit(CopyDirectoryFlags.CopyDirectoryCompressionFlag))
                                {
                                    if (srcDirMetadata.Attributes is FileAttributes attr)
                                    {
                                        if (attr.Bit(FileAttributes.Compressed))
                                        {
                                            copyDirFlags |= FileFlags.OnCreateSetCompressionFlag;
                                        }
                                        else
                                        {
                                            copyDirFlags |= FileFlags.OnCreateRemoveCompressionFlag;
                                        }
                                    }
                                }

                                await destFileSystem.CreateDirectoryAsync(destFullPath, copyDirFlags, cancel);

                                FileMetadata dstDirMetadata = param.DirectoryMetadataCopier.Copy(srcDirMetadata);

                                if (param.CopyDirFlags.Bit(CopyDirectoryFlags.SetAclProtectionFlagOnRootDir))
                                {
                                    if (dirInfo.IsRoot)
                                    {
                                        if (dstDirMetadata.Security != null)
                                        {
                                            if (dstDirMetadata.Security.Acl != null)
                                            {
                                                dstDirMetadata.Security.Acl.Win32AclSddl = "!" + dstDirMetadata.Security.Acl.Win32AclSddl;
                                            }

                                            if (dstDirMetadata.Security.Audit != null)
                                            {
                                                dstDirMetadata.Security.Audit.Win32AuditSddl = "!" + dstDirMetadata.Security.Audit.Win32AuditSddl;
                                            }
                                        }
                                    }
                                }

                                await destFileSystem.SetDirectoryMetadataAsync(destFullPath, dstDirMetadata, cancel);

                                lock (status.LockObj)
                                    status.NumDirectoriesOk++;
                            }
                        }
                        catch (Exception ex)
                        {
                            if (await param.ExceptionCallbackProc(status, entity, ex) == false)
                            {
                                throw ex;
                            }
                        }
                    }

                    return(true);
                },
                                                                          async (dirInfo, entries, c) =>
                {
                    c.ThrowIfCancellationRequested();
                    foreach (FileSystemEntity entity in entries)
                    {
                        c.ThrowIfCancellationRequested();
                        string entryName = entity.Name;
                        if (entity.IsCurrentDirectory)
                        {
                            entryName = "";
                        }

                        string srcFullPath  = srcFileSystem.PathParser.Combine(srcPath, dirInfo.RelativePath, entryName);
                        string destFullPath = destFileSystem.PathParser.Combine(destPath, srcFileSystem.PathParser.ConvertDirectorySeparatorToOtherSystem(dirInfo.RelativePath, destFileSystem.PathParser), entryName);

                        if (entity.IsDirectory)
                        {
                            // Update the directory LastWriteTime metadata after placing all inside files into the directory
                            if (param.DirectoryMetadataCopier.Mode.BitAny(FileMetadataCopyMode.TimeAll))
                            {
                                FileMetadataGetFlags metadataGetFlags = FileMetadataCopier.CalcOptimizedMetadataGetFlags(param.DirectoryMetadataCopier.Mode & (FileMetadataCopyMode.TimeAll));
                                FileMetadata srcDirMetadata           = await srcFileSystem.GetDirectoryMetadataAsync(srcFullPath, metadataGetFlags, cancel);
                                FileMetadata dstDirMetadata           = param.DirectoryMetadataCopier.Copy(srcDirMetadata);
                                await destFileSystem.SetDirectoryMetadataAsync(destFullPath, dstDirMetadata, cancel);
                            }
                        }
                    }
                    return(true);
                },
                                                                          async (dirInfo, exception, c) =>
                {
                    c.ThrowIfCancellationRequested();
                    if (await param.ExceptionCallbackProc(status, dirInfo.Entity, exception) == false)
                    {
                        throw exception;
                    }
                    return(true);
                },
                                                                          param.CopyDirFlags.Bit(CopyDirectoryFlags.Recursive),
                                                                          cancel
                                                                          );
            }

            status.EndTick = Tick64.Now;

            return(status);
        }