Migrate existing code
This commit is contained in:
parent
384ff4a6f3
commit
9b49f880a2
30 changed files with 1789 additions and 5 deletions
113
System/IO/Paths.cs
Normal file
113
System/IO/Paths.cs
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
#if !(LINUX || MACOS || WINDOWS)
|
||||
#define TARGET_ANY
|
||||
#endif
|
||||
|
||||
#if TARGET_ANY || LINUX
|
||||
#define TARGET_LINUX
|
||||
#endif
|
||||
#if TARGET_ANY || MACOS
|
||||
#define TARGET_MACOS
|
||||
#endif
|
||||
#if TARGET_ANY || WINDOWS
|
||||
#define TARGET_WINDOWS
|
||||
#endif
|
||||
|
||||
using System.Buffers;
|
||||
|
||||
using CommunityToolkit.HighPerformance.Buffers;
|
||||
|
||||
namespace System.IO;
|
||||
|
||||
public static class PathEx
|
||||
{
|
||||
public static string ExpandPath(string path)
|
||||
{
|
||||
#if TARGET_ANY
|
||||
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||
{
|
||||
#endif
|
||||
#if TARGET_LINUX || TARGET_MACOS
|
||||
return ExpandPathUnixImpl(path);
|
||||
#endif
|
||||
#if TARGET_ANY
|
||||
}
|
||||
else if (OperatingSystem.IsWindows())
|
||||
{
|
||||
#endif
|
||||
#if TARGET_WINDOWS
|
||||
return ExpandPathWindowsImpl(path);
|
||||
#endif
|
||||
#if TARGET_ANY
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if TARGET_LINUX || TARGET_MACOS
|
||||
private static string ExpandPathUnixImpl(string path)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
var reader = new SequenceReader<char>(new(path.AsMemory()));
|
||||
if (!reader.TryReadTo(out ReadOnlySpan<char> read, '$'))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
using ArrayPoolBufferWriter<char> result = new();
|
||||
while (true)
|
||||
{
|
||||
result.Write(read);
|
||||
|
||||
int skip = 0;
|
||||
if (reader.UnreadSpan[0] == '{' && reader.UnreadSpan.IndexOf('}') is not -1 and int s)
|
||||
{
|
||||
read = reader.UnreadSpan[1..s];
|
||||
skip = s + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
var propertyReader = new SequenceReader<char>(reader.UnreadSequence);
|
||||
while (!propertyReader.End && propertyReader.UnreadSpan[0] is char c && (c == '_' || char.IsAsciiLetterOrDigit(c)))
|
||||
{
|
||||
propertyReader.Advance(1);
|
||||
}
|
||||
|
||||
read = reader.UnreadSpan[..(int)propertyReader.Consumed];
|
||||
skip = read.Length;
|
||||
}
|
||||
|
||||
if (skip != 0 && Environment.GetEnvironmentVariable(read.ToString()) is { } env)
|
||||
{
|
||||
result.Write(env);
|
||||
reader.Advance(skip);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Write(['$']);
|
||||
}
|
||||
|
||||
if (!reader.TryReadTo(out read, '$'))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
result.Write(reader.UnreadSpan);
|
||||
return result.WrittenSpan.ToString();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TARGET_WINDOWS
|
||||
private static string ExpandPathWindowsImpl(string path)
|
||||
{
|
||||
return Environment.ExpandEnvironmentVariables(path);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
67
System/Threading/Tasks/AsyncWaitHandle.cs
Normal file
67
System/Threading/Tasks/AsyncWaitHandle.cs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
namespace System.Threading.Tasks;
|
||||
|
||||
public static class AsyncWaitHandle
|
||||
{
|
||||
public static Task WaitOneAsync(this WaitHandle waitHandle, CancellationToken cancellationToken = default)
|
||||
{
|
||||
WaitOneAsyncState state = new(waitHandle, cancellationToken);
|
||||
return state.WaitOneAsync();
|
||||
}
|
||||
|
||||
private class WaitOneAsyncState : IDisposable
|
||||
{
|
||||
private readonly CancellationTokenRegistration _cancellationTokenRegistration;
|
||||
private readonly RegisteredWaitHandle _registeredWaitHandle;
|
||||
private readonly TaskCompletionSource _tcs;
|
||||
private bool _disposed;
|
||||
|
||||
public WaitOneAsyncState(WaitHandle waitHandle, CancellationToken cancellationToken)
|
||||
{
|
||||
_tcs = new();
|
||||
_cancellationTokenRegistration = cancellationToken.Register((state, token) => ((WaitOneAsyncState)state!).Canceled(token), this);
|
||||
_registeredWaitHandle = ThreadPool.RegisterWaitForSingleObject(waitHandle, (state, timeout) => ((WaitOneAsyncState)state!).Signaled(timeout), this, Timeout.Infinite, true);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_registeredWaitHandle.Unregister(default);
|
||||
_cancellationTokenRegistration.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
public Task WaitOneAsync()
|
||||
{
|
||||
const TaskContinuationOptions options = TaskContinuationOptions.AttachedToParent | TaskContinuationOptions.ExecuteSynchronously;
|
||||
return _tcs.Task.ContinueWith((upstream, state) => ((WaitOneAsyncState)state!).Continuation(upstream), this, options).Unwrap();
|
||||
}
|
||||
|
||||
private void Canceled(CancellationToken token)
|
||||
{
|
||||
_tcs.SetCanceled(token);
|
||||
}
|
||||
|
||||
private Task Continuation(Task task)
|
||||
{
|
||||
Dispose();
|
||||
return task;
|
||||
}
|
||||
|
||||
private void Signaled(bool timeout)
|
||||
{
|
||||
if (timeout)
|
||||
{
|
||||
Canceled(CancellationToken.None);
|
||||
}
|
||||
else
|
||||
{
|
||||
_tcs.SetResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue