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)); }
public static string RegisterRefBool(RefBool r) { return(RegisterCallback(() => { return r.Value; })); }
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."); }
private IEnumerator SimpleIterator(RefBool isTestComplete) { TestContext.WriteLine("In SimpleIterator routine. Starting."); yield return(null); isTestComplete.Value = true; TestContext.WriteLine("In SimpleIterator routine. Finished."); }
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."); }
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."); }
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); }
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."); }
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); }
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}."); }
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."); }
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."); }
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); } }
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."); }
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."); }
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."); }
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."); }
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."); }
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."); }
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."); }
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); }
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."); }
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."); }
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(); } } } }
// ランダムに配列した各テストを 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); } }
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); }
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); } }
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); }