diff --git a/.editorconfig b/.editorconfig index f333e941..1dd14cd2 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,6 +1,6 @@ [*] charset=utf-8 -end_of_line=lf +end_of_line=crlf trim_trailing_whitespace=true insert_final_newline=true indent_style=space diff --git a/BenchmarkDotNet.Artifacts/results/Benchmarks.BufferWriting-report.html b/BenchmarkDotNet.Artifacts/results/Benchmarks.BufferWriting-report.html index 0ebb6522..08da10cd 100644 --- a/BenchmarkDotNet.Artifacts/results/Benchmarks.BufferWriting-report.html +++ b/BenchmarkDotNet.Artifacts/results/Benchmarks.BufferWriting-report.html @@ -3,12 +3,12 @@ Benchmarks.BufferWriting-20201110-192311 - - diff --git a/BenchmarkDotNet.Artifacts/results/Benchmarks.ImmutableDictionaryLookup-report.html b/BenchmarkDotNet.Artifacts/results/Benchmarks.ImmutableDictionaryLookup-report.html index 521c4b53..c1f1315e 100644 --- a/BenchmarkDotNet.Artifacts/results/Benchmarks.ImmutableDictionaryLookup-report.html +++ b/BenchmarkDotNet.Artifacts/results/Benchmarks.ImmutableDictionaryLookup-report.html @@ -3,12 +3,12 @@ Benchmarks.ImmutableDictionaryLookup-20200714-200711 - - diff --git a/BenchmarkDotNet.Artifacts/results/Benchmarks.LockOverhead-report.html b/BenchmarkDotNet.Artifacts/results/Benchmarks.LockOverhead-report.html index 5993f663..99f74fae 100644 --- a/BenchmarkDotNet.Artifacts/results/Benchmarks.LockOverhead-report.html +++ b/BenchmarkDotNet.Artifacts/results/Benchmarks.LockOverhead-report.html @@ -3,12 +3,12 @@ Benchmarks.LockOverhead-20200825-220153 - - diff --git a/BenchmarkDotNet.Artifacts/results/Benchmarks.MaxValueToDepth-report.html b/BenchmarkDotNet.Artifacts/results/Benchmarks.MaxValueToDepth-report.html index a0b205ca..107ea109 100644 --- a/BenchmarkDotNet.Artifacts/results/Benchmarks.MaxValueToDepth-report.html +++ b/BenchmarkDotNet.Artifacts/results/Benchmarks.MaxValueToDepth-report.html @@ -3,12 +3,12 @@ Benchmarks.MaxValueToDepth-20200823-173129 - - diff --git a/BenchmarkDotNet.Artifacts/results/Benchmarks.MemoryCopy-report.html b/BenchmarkDotNet.Artifacts/results/Benchmarks.MemoryCopy-report.html index 8496b82e..a32e8a26 100644 --- a/BenchmarkDotNet.Artifacts/results/Benchmarks.MemoryCopy-report.html +++ b/BenchmarkDotNet.Artifacts/results/Benchmarks.MemoryCopy-report.html @@ -3,12 +3,12 @@ Benchmarks.MemoryCopy-20200823-155139 - - diff --git a/BenchmarkDotNet.Artifacts/results/Benchmarks.PlainVsCursorStruct-report.html b/BenchmarkDotNet.Artifacts/results/Benchmarks.PlainVsCursorStruct-report.html index e2ff147c..d012c142 100644 --- a/BenchmarkDotNet.Artifacts/results/Benchmarks.PlainVsCursorStruct-report.html +++ b/BenchmarkDotNet.Artifacts/results/Benchmarks.PlainVsCursorStruct-report.html @@ -3,12 +3,12 @@ Benchmarks.PlainVsCursorStruct-20200726-153143 - - diff --git a/BenchmarkDotNet.Artifacts/results/Benchmarks.PropertiesVsFields-report.html b/BenchmarkDotNet.Artifacts/results/Benchmarks.PropertiesVsFields-report.html index eb579d99..c669b29c 100644 --- a/BenchmarkDotNet.Artifacts/results/Benchmarks.PropertiesVsFields-report.html +++ b/BenchmarkDotNet.Artifacts/results/Benchmarks.PropertiesVsFields-report.html @@ -3,12 +3,12 @@ Benchmarks.PropertiesVsFields-20200825-210502 - - diff --git a/MarcusW.VncClient.sln.DotSettings b/MarcusW.VncClient.sln.DotSettings index 567ad340..5009dcdc 100644 --- a/MarcusW.VncClient.sln.DotSettings +++ b/MarcusW.VncClient.sln.DotSettings @@ -1,2 +1,7 @@  - True \ No newline at end of file + True + + + + + \ No newline at end of file diff --git a/benchmarks/Benchmarks/Benchmarks.csproj b/benchmarks/Benchmarks/Benchmarks.csproj index e03b1a38..196412ad 100644 --- a/benchmarks/Benchmarks/Benchmarks.csproj +++ b/benchmarks/Benchmarks/Benchmarks.csproj @@ -1,13 +1,11 @@ - - - netcoreapp3.1 - Exe - true - - - - - - - + + net8.0 + Exe + true + enable + + + + + \ No newline at end of file diff --git a/benchmarks/Benchmarks/BufferWriting.cs b/benchmarks/Benchmarks/BufferWriting.cs index 123ac622..78cf8d27 100644 --- a/benchmarks/Benchmarks/BufferWriting.cs +++ b/benchmarks/Benchmarks/BufferWriting.cs @@ -2,95 +2,94 @@ using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; -namespace Benchmarks +namespace Benchmarks; + +public class BufferWriting { - public class BufferWriting + private readonly byte[] _buffer = new byte[1920 * 1080 * 4]; + + [Benchmark] + public void ArrayIndexer() { - private readonly byte[] _buffer = new byte[1920 * 1080 * 4]; + for (var i = 0; i < _buffer.Length; i += 4) + SetPixelArrayIndexer(_buffer, i, 0xffffffff); + } - [Benchmark] - public void ArrayIndexer() + [Benchmark] + public unsafe void Pointer() + { + fixed (byte* ptr = &_buffer[0]) { - for (int i = 0; i < _buffer.Length; i += 4) - SetPixelArrayIndexer(_buffer, i, 0xffffffff); + for (var i = 0; i < _buffer.Length; i += 4) + SetPixelPointer(ptr + i, 0xffffffff); } + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void SetPixelArrayIndexer(byte[] buffer, int i, uint color) + [Benchmark] + public unsafe void PointerMemcpy() + { + fixed (byte* ptr = &_buffer[0]) { - buffer[i] = (byte)(color & 0xff); - buffer[i + 1] = (byte)((color >> 8) & 0xff); - buffer[i + 2] = (byte)((color >> 16) & 0xff); - buffer[i + 3] = (byte)((color >> 24) & 0xff); + for (var i = 0; i < _buffer.Length; i += 4) + SetPixelPointerMemcpy(ptr + i, 0xffffffff); } + } - [Benchmark] - public void Span() + [Benchmark] + public unsafe void PointerReinterpretCast() + { + fixed (byte* ptr = &_buffer[0]) { - Span buffer = _buffer; - - for (int i = 0; i < _buffer.Length; i += 4) - SetPixelSpan(buffer.Slice(i, 4), 0xffffffff); + for (var i = 0; i < _buffer.Length; i += 4) + SetPixelPointerReinterpretCast(ptr + i, 0xffffffff); } + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void SetPixelSpan(in Span span, uint color) - { - span[0] = (byte)(color & 0xff); - span[1] = (byte)((color >> 8) & 0xff); - span[2] = (byte)((color >> 16) & 0xff); - span[3] = (byte)((color >> 24) & 0xff); - } + [Benchmark] + public void Span() + { + Span buffer = _buffer; - [Benchmark] - public unsafe void Pointer() - { - fixed (byte* ptr = &_buffer[0]) - { - for (int i = 0; i < _buffer.Length; i += 4) - SetPixelPointer(ptr + i, 0xffffffff); - } - } + for (var i = 0; i < _buffer.Length; i += 4) + SetPixelSpan(buffer.Slice(i, 4), 0xffffffff); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe void SetPixelPointer(byte* ptr, uint color) - { - *ptr++ = (byte)(color & 0xff); - *ptr++ = (byte)((color >> 8) & 0xff); - *ptr++ = (byte)((color >> 16) & 0xff); - *ptr = (byte)((color >> 24) & 0xff); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void SetPixelArrayIndexer(byte[] buffer, int i, uint color) + { + buffer[i] = (byte)(color & 0xff); + buffer[i + 1] = (byte)((color >> 8) & 0xff); + buffer[i + 2] = (byte)((color >> 16) & 0xff); + buffer[i + 3] = (byte)((color >> 24) & 0xff); + } - [Benchmark] - public unsafe void PointerReinterpretCast() - { - fixed (byte* ptr = &_buffer[0]) - { - for (int i = 0; i < _buffer.Length; i += 4) - SetPixelPointerReinterpretCast(ptr + i, 0xffffffff); - } - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe void SetPixelPointer(byte* ptr, uint color) + { + *ptr++ = (byte)(color & 0xff); + *ptr++ = (byte)((color >> 8) & 0xff); + *ptr++ = (byte)((color >> 16) & 0xff); + *ptr = (byte)((color >> 24) & 0xff); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe void SetPixelPointerReinterpretCast(byte* ptr, uint color) - { - *(uint*)ptr = color; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe void SetPixelPointerMemcpy(byte* ptr, uint color) + { + Unsafe.CopyBlock(ptr, &color, sizeof(uint)); + } - [Benchmark] - public unsafe void PointerMemcpy() - { - fixed (byte* ptr = &_buffer[0]) - { - for (int i = 0; i < _buffer.Length; i += 4) - SetPixelPointerMemcpy(ptr + i, 0xffffffff); - } - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe void SetPixelPointerReinterpretCast(byte* ptr, uint color) + { + *(uint*)ptr = color; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe void SetPixelPointerMemcpy(byte* ptr, uint color) - { - Unsafe.CopyBlock(ptr, &color, sizeof(uint)); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void SetPixelSpan(in Span span, uint color) + { + span[0] = (byte)(color & 0xff); + span[1] = (byte)((color >> 8) & 0xff); + span[2] = (byte)((color >> 16) & 0xff); + span[3] = (byte)((color >> 24) & 0xff); } } diff --git a/benchmarks/Benchmarks/ImmutableDictionaryLookup.cs b/benchmarks/Benchmarks/ImmutableDictionaryLookup.cs index e36674ba..97739d0c 100644 --- a/benchmarks/Benchmarks/ImmutableDictionaryLookup.cs +++ b/benchmarks/Benchmarks/ImmutableDictionaryLookup.cs @@ -1,42 +1,33 @@ -using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using BenchmarkDotNet.Attributes; -namespace Benchmarks +namespace Benchmarks; + +public class ImmutableDictionaryLookup { - public class ImmutableDictionaryLookup - { - private const int Index = 500; - private readonly IImmutableDictionary _dictionary= Enumerable.Range(0, 1000).ToImmutableDictionary(i => i, i => new object()); + private const int Index = 500; - [Benchmark] - public object Indexer() - { - if (!_dictionary.ContainsKey(Index)) - return null; - return _dictionary[Index]; - } + private readonly IImmutableDictionary _dictionary = + Enumerable.Range(0, 1000).ToImmutableDictionary(i => i, _ => new object()); - [Benchmark] - public object TryGet() + [Benchmark] + public object? Indexer() => CollectionExtensions.GetValueOrDefault(_dictionary, Index); + + [Benchmark] + public object? TryCatch() + { + try { - if (!_dictionary.TryGetValue(Index, out object value)) - return null; - return value; + return _dictionary[Index]; } - - [Benchmark] - public object TryCatch() + catch { - try - { - return _dictionary[Index]; - } - catch - { - return null; - } + return null; } } + + [Benchmark] + public object? TryGet() => CollectionExtensions.GetValueOrDefault(_dictionary, Index); } diff --git a/benchmarks/Benchmarks/LockOverhead.cs b/benchmarks/Benchmarks/LockOverhead.cs index ac21609c..5dacfd11 100644 --- a/benchmarks/Benchmarks/LockOverhead.cs +++ b/benchmarks/Benchmarks/LockOverhead.cs @@ -1,23 +1,22 @@ using BenchmarkDotNet.Attributes; -namespace Benchmarks +namespace Benchmarks; + +public class LockOverhead { - public class LockOverhead - { - private int _value; - private object _lock = new object(); + private readonly object _lock = new(); + private int _value; - [Benchmark] - public void IncreaseWithoutLock() - { + [Benchmark] + public void IncreaseWithLock() + { + lock (_lock) _value++; - } + } - [Benchmark] - public void IncreaseWithLock() - { - lock (_lock) - _value++; - } + [Benchmark] + public void IncreaseWithoutLock() + { + _value++; } } diff --git a/benchmarks/Benchmarks/MaxValueToDepth.cs b/benchmarks/Benchmarks/MaxValueToDepth.cs index 3dc54fa8..e3941fa0 100644 --- a/benchmarks/Benchmarks/MaxValueToDepth.cs +++ b/benchmarks/Benchmarks/MaxValueToDepth.cs @@ -1,27 +1,26 @@ using System.Runtime.Intrinsics.X86; using BenchmarkDotNet.Attributes; -namespace Benchmarks +namespace Benchmarks; + +public class MaxValueToDepth { - public class MaxValueToDepth - { - private const int MaxValue = 255; + private const int MaxValue = 255; - [Benchmark] - public uint WhileLoop() - { - uint val = MaxValue; - uint depth = 0; - while (val != 0) - { - depth++; - val >>= 1; - } + [Benchmark] + public uint PopCount() => Popcnt.PopCount(MaxValue); - return depth; + [Benchmark] + public uint WhileLoop() + { + uint val = MaxValue; + uint depth = 0; + while (val != 0) + { + depth++; + val >>= 1; } - [Benchmark] - public uint PopCount() => Popcnt.PopCount(MaxValue); + return depth; } } diff --git a/benchmarks/Benchmarks/MemoryCopy.cs b/benchmarks/Benchmarks/MemoryCopy.cs index c5acb586..893bbc20 100644 --- a/benchmarks/Benchmarks/MemoryCopy.cs +++ b/benchmarks/Benchmarks/MemoryCopy.cs @@ -1,48 +1,46 @@ using System; -using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using BenchmarkDotNet.Attributes; -namespace Benchmarks +namespace Benchmarks; + +public unsafe class MemoryCopy { - public unsafe class MemoryCopy - { - private readonly byte* _srcPtr; - private readonly byte* _dstPtr; + private readonly byte* _dstPtr; + private readonly byte* _srcPtr; - public MemoryCopy() - { - _srcPtr = (byte*)Marshal.AllocHGlobal(4); - _dstPtr = (byte*)Marshal.AllocHGlobal(4); - } + public MemoryCopy() + { + _srcPtr = (byte*)Marshal.AllocHGlobal(4); + _dstPtr = (byte*)Marshal.AllocHGlobal(4); + } - ~MemoryCopy() - { - Marshal.FreeHGlobal((IntPtr)_srcPtr); - Marshal.FreeHGlobal((IntPtr)_dstPtr); - } + [Benchmark] + public void AssigningValues() + { + *_dstPtr = *_srcPtr; + *(_dstPtr + 1) = *(_srcPtr + 1); + *(_dstPtr + 2) = *(_srcPtr + 2); + *(_dstPtr + 3) = *(_srcPtr + 3); + } - [Benchmark] - public void MemCpy() - { - Unsafe.CopyBlock(_dstPtr, _srcPtr, 4); - } + [Benchmark] + public void MemCpy() + { + Unsafe.CopyBlock(_dstPtr, _srcPtr, 4); + } - [Benchmark] - public void AssigningValues() - { - *_dstPtr = *_srcPtr; - *(_dstPtr + 1) = *(_srcPtr + 1); - *(_dstPtr + 2) = *(_srcPtr + 2); - *(_dstPtr + 3) = *(_srcPtr + 3); - } + [Benchmark] + public void ReinterpretCast() + { + var val = Unsafe.AsRef(_srcPtr); + Unsafe.Write(_dstPtr, val); + } - [Benchmark] - public void ReinterpretCast() - { - uint val = Unsafe.AsRef(_srcPtr); - Unsafe.Write(_dstPtr, val); - } + ~MemoryCopy() + { + Marshal.FreeHGlobal((IntPtr)_srcPtr); + Marshal.FreeHGlobal((IntPtr)_dstPtr); } } diff --git a/benchmarks/Benchmarks/PlainVsCursorStruct.cs b/benchmarks/Benchmarks/PlainVsCursorStruct.cs index 37a70179..2c6d67d9 100644 --- a/benchmarks/Benchmarks/PlainVsCursorStruct.cs +++ b/benchmarks/Benchmarks/PlainVsCursorStruct.cs @@ -1,109 +1,98 @@ using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; -namespace Benchmarks +namespace Benchmarks; + +public class PlainVsCursorStruct { - public class PlainVsCursorStruct - { - private const int Pixels = 1920 * 1080; - private readonly byte[] _buffer = new byte[Pixels * 4]; + private const int Pixels = 1920 * 1080; + private readonly byte[] _buffer = new byte[Pixels * 4]; - [Benchmark] - public unsafe void Plain() + [Benchmark] + public unsafe void Plain() + { + fixed (byte* ptr = &_buffer[0]) { - fixed (byte* ptr = &_buffer[0]) - { - for (int i = 0; i < _buffer.Length; i += 4) - SetColor(i, ptr + i); - } + for (var i = 0; i < _buffer.Length; i += 4) + SetColor(i, ptr + i); } + } - [Benchmark] - public unsafe void WithCursor() + [Benchmark] + public unsafe void WithCursor() + { + fixed (byte* ptr = &_buffer[0]) { - fixed (byte* ptr = &_buffer[0]) - { - var cursor = new BufferCursor(ptr); - for (int i = 0; i < Pixels; i++) - cursor.SetNextPixel(i); - } + var cursor = new BufferCursor(ptr); + for (var i = 0; i < Pixels; i++) + cursor.SetNextPixel(i); } + } - [Benchmark] - public unsafe void WithCursorNotInlined() + [Benchmark] + public unsafe void WithCursorNotInlined() + { + fixed (byte* ptr = &_buffer[0]) { - fixed (byte* ptr = &_buffer[0]) - { - var cursor = new BufferCursorNotInlined(ptr); - for (int i = 0; i < Pixels; i++) - cursor.SetNextPixel(i); - } + var cursor = new BufferCursorNotInlined(ptr); + for (var i = 0; i < Pixels; i++) + cursor.SetNextPixel(i); } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe void SetColor(int i, byte* position) + { + // Some random operations + *position++ = (byte)(i & 0xff); + *position++ = (byte)((i >> 8) & 0xff); + *position++ = (byte)((i >> 16) & 0xff); + *position = (byte)((i >> 24) & 0xff); + } + + private readonly unsafe struct BufferCursor(byte* ptr) + { + private readonly byte* _ptr = ptr; [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe void SetColor(int i, byte* position) + public readonly void SetNextPixel(int i) { + MoveNext(); + // Some random operations - *position++ = (byte)(i & 0xff); - *position++ = (byte)((i >> 8) & 0xff); - *position++ = (byte)((i >> 16) & 0xff); - *position = (byte)((i >> 24) & 0xff); + *_ptr = (byte)(i & 0xff); + *(_ptr + 1) = (byte)((i >> 8) & 0xff); + *(_ptr + 2) = (byte)((i >> 16) & 0xff); + *(_ptr + 3) = (byte)((i >> 24) & 0xff); } - private unsafe struct BufferCursor + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly void MoveNext() { - private byte* _ptr; - - public BufferCursor(byte* ptr) - { - _ptr = ptr; - } + *_ptr += 4; + } + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetNextPixel(int i) - { - MoveNext(); + private readonly unsafe struct BufferCursorNotInlined(byte* ptr) + { + private readonly byte* _ptr = ptr; - // Some random operations - *_ptr = (byte)(i & 0xff); - *(_ptr + 1) = (byte)((i >> 8) & 0xff); - *(_ptr + 2) = (byte)((i >> 16) & 0xff); - *(_ptr + 3) = (byte)((i >> 24) & 0xff); - } + [MethodImpl(MethodImplOptions.NoInlining)] + public readonly void SetNextPixel(int i) + { + MoveNext(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void MoveNext() - { - *_ptr += 4; - } + // Some random operations + *_ptr = (byte)(i & 0xff); + *(_ptr + 1) = (byte)((i >> 8) & 0xff); + *(_ptr + 2) = (byte)((i >> 16) & 0xff); + *(_ptr + 3) = (byte)((i >> 24) & 0xff); } - private unsafe struct BufferCursorNotInlined + [MethodImpl(MethodImplOptions.NoInlining)] + public readonly void MoveNext() { - private byte* _ptr; - - public BufferCursorNotInlined(byte* ptr) - { - _ptr = ptr; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public void SetNextPixel(int i) - { - MoveNext(); - - // Some random operations - *_ptr = (byte)(i & 0xff); - *(_ptr + 1) = (byte)((i >> 8) & 0xff); - *(_ptr + 2) = (byte)((i >> 16) & 0xff); - *(_ptr + 3) = (byte)((i >> 24) & 0xff); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public void MoveNext() - { - *_ptr += 4; - } + *_ptr += 4; } } } diff --git a/benchmarks/Benchmarks/Program.cs b/benchmarks/Benchmarks/Program.cs index 3fe08a35..dcc45980 100644 --- a/benchmarks/Benchmarks/Program.cs +++ b/benchmarks/Benchmarks/Program.cs @@ -1,12 +1,11 @@ using BenchmarkDotNet.Running; -namespace Benchmarks +namespace Benchmarks; + +public static class Program { - public static class Program + public static void Main(string[] args) { - public static void Main(string[] args) - { - BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); - } + BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); } } diff --git a/benchmarks/Benchmarks/PropertiesVsFields.cs b/benchmarks/Benchmarks/PropertiesVsFields.cs index f34a6bec..0a780904 100644 --- a/benchmarks/Benchmarks/PropertiesVsFields.cs +++ b/benchmarks/Benchmarks/PropertiesVsFields.cs @@ -1,43 +1,29 @@ using BenchmarkDotNet.Attributes; -namespace Benchmarks -{ - public class PropertiesVsFields - { - private StructWithProperties _structWithProperties = new StructWithProperties(100,200); - private StructWithFields _structWithFields = new StructWithFields(100,200); - - public struct StructWithProperties - { - public int A { get; } - - public int B { get; } +namespace Benchmarks; - public StructWithProperties(int a, int b) - { - A = a; - B = b; - } - } +public class PropertiesVsFields +{ + private readonly StructWithFields _structWithFields = new(100, 200); + private readonly StructWithProperties _structWithProperties = new(100, 200); - public struct StructWithFields - { - public int A; + [Benchmark] + public int MultipliedFields() => _structWithFields.A * _structWithFields.B; - public int B; + [Benchmark] + public int MultipliedProperties() => _structWithProperties.A * _structWithProperties.B; - public StructWithFields(int a, int b) - { - A = a; - B = b; - } - } + public readonly struct StructWithProperties(int a, int b) + { + public int A { get; } = a; + public int B { get; } = b; + } - [Benchmark] - public int MultipliedProperties() => _structWithProperties.A * _structWithProperties.B; + public struct StructWithFields(int a, int b) + { + public readonly int A = a; - [Benchmark] - public int MultipliedFields() => _structWithFields.A * _structWithFields.B; + public readonly int B = b; } } diff --git a/samples/AvaloniaVncClient/App.xaml b/samples/AvaloniaVncClient/App.xaml index 78b40ff3..a05231fe 100644 --- a/samples/AvaloniaVncClient/App.xaml +++ b/samples/AvaloniaVncClient/App.xaml @@ -7,7 +7,6 @@ - - + diff --git a/samples/AvaloniaVncClient/App.xaml.cs b/samples/AvaloniaVncClient/App.xaml.cs index 431535cf..1f31b239 100644 --- a/samples/AvaloniaVncClient/App.xaml.cs +++ b/samples/AvaloniaVncClient/App.xaml.cs @@ -6,29 +6,28 @@ using AvaloniaVncClient.Views; using Splat; -namespace AvaloniaVncClient +namespace AvaloniaVncClient; + +public class App : Application { - public class App : Application + public override void Initialize() { - public override void Initialize() - { - AvaloniaXamlLoader.Load(this); + AvaloniaXamlLoader.Load(this); - // Register dependencies - Locator.CurrentMutable.RegisterLazySingleton(() => new ConnectionManager()); - Locator.CurrentMutable.RegisterLazySingleton(() => new InteractiveAuthenticationHandler()); - } + // Register dependencies + Locator.CurrentMutable.RegisterLazySingleton(() => new ConnectionManager()); + Locator.CurrentMutable.RegisterLazySingleton(() => new InteractiveAuthenticationHandler()); + } - public override void OnFrameworkInitializationCompleted() + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - desktop.MainWindow = new MainWindow { - DataContext = new MainWindowViewModel() - }; - } - - base.OnFrameworkInitializationCompleted(); + desktop.MainWindow = new MainWindow { + DataContext = new MainWindowViewModel(), + }; } + + base.OnFrameworkInitializationCompleted(); } } diff --git a/samples/AvaloniaVncClient/AvaloniaVncClient.csproj b/samples/AvaloniaVncClient/AvaloniaVncClient.csproj index 36a77e27..aad540ed 100644 --- a/samples/AvaloniaVncClient/AvaloniaVncClient.csproj +++ b/samples/AvaloniaVncClient/AvaloniaVncClient.csproj @@ -1,27 +1,27 @@  - - WinExe - netcoreapp3.1 - enable - - - - - %(Filename) - - - Designer - - - - - - - - - - - - - - + + WinExe + net8.0-windows + enable + + + + + %(Filename) + + + Designer + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/AvaloniaVncClient/Program.cs b/samples/AvaloniaVncClient/Program.cs index 1159de7d..00c035d2 100644 --- a/samples/AvaloniaVncClient/Program.cs +++ b/samples/AvaloniaVncClient/Program.cs @@ -3,26 +3,25 @@ using Avalonia.Logging; using Avalonia.ReactiveUI; -namespace AvaloniaVncClient +namespace AvaloniaVncClient; + +public static class Program { - public static class Program + // Avalonia configuration, don't remove; also used by visual designer. + public static AppBuilder BuildAvaloniaApp() { - // Initialization code. Don't use any Avalonia, third-party APIs or any - // SynchronizationContext-reliant code before AppMain is called: things aren't initialized - // yet and stuff might break. - public static void Main(string[] args) - => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args, ShutdownMode.OnMainWindowClose); - - // Avalonia configuration, don't remove; also used by visual designer. - public static AppBuilder BuildAvaloniaApp() - { #if DEBUG - LogEventLevel logLevel = LogEventLevel.Debug; + var logLevel = LogEventLevel.Debug; #else - LogEventLevel logLevel = LogEventLevel.Warning; + var logLevel = LogEventLevel.Warning; #endif - return AppBuilder.Configure().UsePlatformDetect().LogToTrace(logLevel).UseReactiveUI(); - } + return AppBuilder.Configure().UsePlatformDetect().LogToTrace(logLevel).UseReactiveUI(); } + + // Initialization code. Don't use any Avalonia, third-party APIs or any + // SynchronizationContext-reliant code before AppMain is called: things aren't initialized + // yet and stuff might break. + public static void Main(string[] args) + => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args, ShutdownMode.OnMainWindowClose); } diff --git a/samples/AvaloniaVncClient/Services/ConnectionManager.cs b/samples/AvaloniaVncClient/Services/ConnectionManager.cs index ad5f4e38..44cb1331 100644 --- a/samples/AvaloniaVncClient/Services/ConnectionManager.cs +++ b/samples/AvaloniaVncClient/Services/ConnectionManager.cs @@ -3,38 +3,37 @@ using System.Threading.Tasks; using MarcusW.VncClient; using MarcusW.VncClient.Avalonia.Adapters.Logging; -using MarcusW.VncClient.Rendering; using Microsoft.Extensions.Logging; using Splat; -namespace AvaloniaVncClient.Services +namespace AvaloniaVncClient.Services; + +public class ConnectionManager { - public class ConnectionManager - { - private readonly InteractiveAuthenticationHandler _interactiveAuthenticationHandler; + private readonly InteractiveAuthenticationHandler _interactiveAuthenticationHandler; - private readonly VncClient _vncClient; + private readonly VncClient _vncClient; - public ConnectionManager(InteractiveAuthenticationHandler? interactiveAuthenticationHandler = null) - { - _interactiveAuthenticationHandler = interactiveAuthenticationHandler ?? Locator.Current.GetService() - ?? throw new ArgumentNullException(nameof(interactiveAuthenticationHandler)); + public ConnectionManager(InteractiveAuthenticationHandler? interactiveAuthenticationHandler = null) + { + _interactiveAuthenticationHandler = interactiveAuthenticationHandler + ?? Locator.Current.GetService() + ?? throw new ArgumentNullException(nameof(interactiveAuthenticationHandler)); - // Create and populate default logger factory for logging to Avalonia logging sinks - var loggerFactory = new LoggerFactory(); - loggerFactory.AddProvider(new AvaloniaLoggerProvider()); + // Create and populate default logger factory for logging to Avalonia logging sinks + var loggerFactory = new LoggerFactory(); + loggerFactory.AddProvider(new AvaloniaLoggerProvider()); - _vncClient = new VncClient(loggerFactory); - } + _vncClient = new(loggerFactory); + } - public Task ConnectAsync(ConnectParameters parameters, CancellationToken cancellationToken = default) - { - parameters.AuthenticationHandler = _interactiveAuthenticationHandler; + public Task ConnectAsync(ConnectParameters parameters, CancellationToken cancellationToken = default) + { + parameters.AuthenticationHandler = _interactiveAuthenticationHandler; - // Uncomment for debugging/visualization purposes - //parameters.RenderFlags |= RenderFlags.VisualizeRectangles; + // Uncomment for debugging/visualization purposes + //parameters.RenderFlags |= RenderFlags.VisualizeRectangles; - return _vncClient.ConnectAsync(parameters, cancellationToken); - } + return _vncClient.ConnectAsync(parameters, cancellationToken); } } diff --git a/samples/AvaloniaVncClient/Services/InteractiveAuthenticationHandler.cs b/samples/AvaloniaVncClient/Services/InteractiveAuthenticationHandler.cs index eb938b8f..e10dc738 100644 --- a/samples/AvaloniaVncClient/Services/InteractiveAuthenticationHandler.cs +++ b/samples/AvaloniaVncClient/Services/InteractiveAuthenticationHandler.cs @@ -8,28 +8,29 @@ using MarcusW.VncClient.Security; using ReactiveUI; -namespace AvaloniaVncClient.Services +namespace AvaloniaVncClient.Services; + +public class InteractiveAuthenticationHandler : IAuthenticationHandler { - public class InteractiveAuthenticationHandler : IAuthenticationHandler - { - public Interaction EnterPasswordInteraction { get; } = new Interaction(); + public Interaction EnterPasswordInteraction { get; } = new(); - /// - public async Task ProvideAuthenticationInputAsync(RfbConnection connection, ISecurityType securityType, IAuthenticationInputRequest request) - where TInput : class, IAuthenticationInput + /// + public async Task ProvideAuthenticationInputAsync(RfbConnection connection, + ISecurityType securityType, IAuthenticationInputRequest request) + where TInput : class, IAuthenticationInput + { + if (typeof(TInput) != typeof(PasswordAuthenticationInput)) { - if (typeof(TInput) == typeof(PasswordAuthenticationInput)) - { - string? password = await Dispatcher.UIThread.InvokeAsync(async () => await EnterPasswordInteraction.Handle(Unit.Default)).ConfigureAwait(false); + throw new InvalidOperationException( + "The authentication input request is not supported by the interactive authentication handler."); + } - // TODO: Implement canceling of authentication input requests instead of passing an empty password! - if (password == null) - password = string.Empty; + string password = await Dispatcher.UIThread + .InvokeAsync(async () => await EnterPasswordInteraction.Handle(Unit.Default)).ConfigureAwait(false) - return (TInput)Convert.ChangeType(new PasswordAuthenticationInput(password), typeof(TInput)); - } + // TODO: Implement canceling of authentication input requests instead of passing an empty password! + ?? string.Empty; - throw new InvalidOperationException("The authentication input request is not supported by the interactive authentication handler."); - } + return (TInput)Convert.ChangeType(new PasswordAuthenticationInput(password), typeof(TInput)); } } diff --git a/samples/AvaloniaVncClient/ViewLocator.cs b/samples/AvaloniaVncClient/ViewLocator.cs index 7fa8017d..3909e1ed 100644 --- a/samples/AvaloniaVncClient/ViewLocator.cs +++ b/samples/AvaloniaVncClient/ViewLocator.cs @@ -3,25 +3,28 @@ using Avalonia.Controls.Templates; using AvaloniaVncClient.ViewModels; -namespace AvaloniaVncClient +namespace AvaloniaVncClient; + +public class ViewLocator : IDataTemplate { - public class ViewLocator : IDataTemplate - { - public bool SupportsRecycling => false; + public bool SupportsRecycling => false; - public IControl Build(object data) + public Control Build(object? data) + { + string? viewName = data?.GetType().FullName?.Replace("ViewModel", "View"); + if (viewName == null) { - var viewName = data.GetType().FullName?.Replace("ViewModel", "View"); - if (viewName == null) - return new TextBlock { Text = "Not Found" }; - - var viewType = Type.GetType(viewName); - if (viewType == null) - return new TextBlock { Text = "Not Found: " + viewName }; + return new TextBlock { Text = "Not Found" }; + } - return (Control)Activator.CreateInstance(viewType)!; + var viewType = Type.GetType(viewName); + if (viewType == null) + { + return new TextBlock { Text = "Not Found: " + viewName }; } - public bool Match(object data) => data is ViewModelBase; + return (Control)Activator.CreateInstance(viewType)!; } + + public bool Match(object? data) => data is ViewModelBase; } diff --git a/samples/AvaloniaVncClient/ViewModels/MainWindowViewModel.cs b/samples/AvaloniaVncClient/ViewModels/MainWindowViewModel.cs index b7bbd1e5..a378241b 100644 --- a/samples/AvaloniaVncClient/ViewModels/MainWindowViewModel.cs +++ b/samples/AvaloniaVncClient/ViewModels/MainWindowViewModel.cs @@ -1,6 +1,4 @@ -using System; -using System.Linq; -using System.Net; +using System; using System.Reactive; using System.Threading; using System.Threading.Tasks; @@ -8,96 +6,103 @@ using MarcusW.VncClient; using MarcusW.VncClient.Protocol.Implementation; using MarcusW.VncClient.Protocol.Implementation.Services.Transports; -using MarcusW.VncClient.Rendering; using ReactiveUI; using Splat; -namespace AvaloniaVncClient.ViewModels +namespace AvaloniaVncClient.ViewModels; + +public class MainWindowViewModel : ViewModelBase { - public class MainWindowViewModel : ViewModelBase - { - private readonly ConnectionManager _connectionManager; + private readonly ConnectionManager _connectionManager; - private string _host = "fedora-vm"; - private int _port = 5901; - private RfbConnection? _rfbConnection; - private string? _errorMessage; + private readonly ObservableAsPropertyHelper _parametersValidProperty; + private string? _errorMessage; - private readonly ObservableAsPropertyHelper _parametersValidProperty; + private string _host = "fedora-vm"; + private int _port = 5901; + private RfbConnection? _rfbConnection; - public InteractiveAuthenticationHandler InteractiveAuthenticationHandler { get; } + public MainWindowViewModel(ConnectionManager? connectionManager = null, + InteractiveAuthenticationHandler? interactiveAuthenticationHandler = null) + { + _connectionManager = connectionManager ?? Locator.Current.GetService() + ?? throw new ArgumentNullException(nameof(connectionManager)); + InteractiveAuthenticationHandler = interactiveAuthenticationHandler + ?? Locator.Current.GetService() + ?? throw new ArgumentNullException(nameof(interactiveAuthenticationHandler)); + + IObservable parametersValid = this.WhenAnyValue(vm => vm.Host, vm => vm.Port, (host, port) => { + // Is it an IP Address or a valid DNS/hostname? + if (Uri.CheckHostName(host) == UriHostNameType.Unknown) + { + return false; + } - public bool IsTightAvailable => DefaultImplementation.IsTightAvailable; + // Is the port valid? + return port is >= 0 and <= 65535; + }); + _parametersValidProperty = parametersValid.ToProperty(this, nameof(ParametersValid)); - public string Host - { - get => _host; - set => this.RaiseAndSetIfChanged(ref _host, value); - } + ConnectCommand = ReactiveCommand.CreateFromTask(ConnectAsync, parametersValid); + } - public int Port - { - get => _port; - set => this.RaiseAndSetIfChanged(ref _port, value); - } + public InteractiveAuthenticationHandler InteractiveAuthenticationHandler { get; } - // TODO: Add a way to close existing connections. Maybe a list of multiple connections (shown as tabs)? - public RfbConnection? RfbConnection - { - get => _rfbConnection; - private set => this.RaiseAndSetIfChanged(ref _rfbConnection, value); - } +#pragma warning disable CA1822 - public string? ErrorMessage - { - get => _errorMessage; - set => this.RaiseAndSetIfChanged(ref _errorMessage, value); - } + // ReSharper disable once MemberCanBeMadeStatic.Global + public bool IsTightAvailable => DefaultImplementation.IsTightAvailable; +#pragma warning restore CA1822 - public ReactiveCommand ConnectCommand { get; } + public string Host + { + get => _host; + set => this.RaiseAndSetIfChanged(ref _host, value); + } - public bool ParametersValid => _parametersValidProperty.Value; + public int Port + { + get => _port; + set => this.RaiseAndSetIfChanged(ref _port, value); + } - public MainWindowViewModel(ConnectionManager? connectionManager = null, InteractiveAuthenticationHandler? interactiveAuthenticationHandler = null) - { - _connectionManager = connectionManager ?? Locator.Current.GetService() ?? throw new ArgumentNullException(nameof(connectionManager)); - InteractiveAuthenticationHandler = interactiveAuthenticationHandler ?? Locator.Current.GetService() - ?? throw new ArgumentNullException(nameof(interactiveAuthenticationHandler)); + // TODO: Add a way to close existing connections. Maybe a list of multiple connections (shown as tabs)? + public RfbConnection? RfbConnection + { + get => _rfbConnection; + private set => this.RaiseAndSetIfChanged(ref _rfbConnection, value); + } - IObservable parametersValid = this.WhenAnyValue(vm => vm.Host, vm => vm.Port, (host, port) => { - // Is it an IP Address or a valid DNS/hostname? - if (Uri.CheckHostName(host) == UriHostNameType.Unknown) - return false; + public string? ErrorMessage + { + get => _errorMessage; + set => this.RaiseAndSetIfChanged(ref _errorMessage, value); + } - // Is the port valid? - return port >= 0 && port <= 65535; - }); - _parametersValidProperty = parametersValid.ToProperty(this, nameof(ParametersValid)); + public ReactiveCommand ConnectCommand { get; } - ConnectCommand = ReactiveCommand.CreateFromTask(ConnectAsync, parametersValid); - } + public bool ParametersValid => _parametersValidProperty.Value; - private async Task ConnectAsync(CancellationToken cancellationToken = default) + private async Task ConnectAsync(CancellationToken cancellationToken = default) + { + try { - try - { - // TODO: Configure connect parameters - var parameters = new ConnectParameters { - TransportParameters = new TcpTransportParameters { - Host = Host, - Port = Port - } - }; - - // Try to connect and set the connection - RfbConnection = await _connectionManager.ConnectAsync(parameters, cancellationToken).ConfigureAwait(true); - - ErrorMessage = null; - } - catch (Exception exception) - { - ErrorMessage = exception.Message; - } + // TODO: Configure connect parameters + var parameters = new ConnectParameters { + TransportParameters = new TcpTransportParameters { + Host = Host, + Port = Port, + }, + }; + + // Try to connect and set the connection + RfbConnection = await _connectionManager.ConnectAsync(parameters, cancellationToken).ConfigureAwait(true); + + ErrorMessage = null; + } + catch (Exception exception) + { + ErrorMessage = exception.Message; } } } diff --git a/samples/AvaloniaVncClient/ViewModels/ViewModelBase.cs b/samples/AvaloniaVncClient/ViewModels/ViewModelBase.cs index f801d1f7..4111dbdf 100644 --- a/samples/AvaloniaVncClient/ViewModels/ViewModelBase.cs +++ b/samples/AvaloniaVncClient/ViewModels/ViewModelBase.cs @@ -1,6 +1,5 @@ using ReactiveUI; -namespace AvaloniaVncClient.ViewModels -{ - public class ViewModelBase : ReactiveObject { } -} +namespace AvaloniaVncClient.ViewModels; + +public class ViewModelBase : ReactiveObject { } diff --git a/samples/AvaloniaVncClient/Views/Dialogs/EnterPasswordDialog.xaml.cs b/samples/AvaloniaVncClient/Views/Dialogs/EnterPasswordDialog.xaml.cs index 7974948b..1491a305 100644 --- a/samples/AvaloniaVncClient/Views/Dialogs/EnterPasswordDialog.xaml.cs +++ b/samples/AvaloniaVncClient/Views/Dialogs/EnterPasswordDialog.xaml.cs @@ -1,32 +1,30 @@ -using Avalonia; using Avalonia.Controls; using Avalonia.Interactivity; using Avalonia.Markup.Xaml; -namespace AvaloniaVncClient.Views.Dialogs +namespace AvaloniaVncClient.Views.Dialogs; + +public class EnterPasswordDialog : Window { - public class EnterPasswordDialog : Window + public EnterPasswordDialog() { - private TextBox PasswordTextBox => this.FindControl("PasswordTextBox"); + InitializeComponent(); + } - public EnterPasswordDialog() - { - InitializeComponent(); - } + private TextBox PasswordTextBox => this.FindControl("PasswordTextBox")!; - private void InitializeComponent() - { - AvaloniaXamlLoader.Load(this); - } + public void OnCancelClick(object? sender, RoutedEventArgs e) + { + Close(null); + } - public void OnCancelClick(object sender, RoutedEventArgs e) - { - Close(null); - } + public void OnOkClick(object? sender, RoutedEventArgs e) + { + Close(PasswordTextBox.Text); + } - public void OnOkClick(object sender, RoutedEventArgs e) - { - Close(PasswordTextBox.Text); - } + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); } } diff --git a/samples/AvaloniaVncClient/Views/MainWindow.xaml b/samples/AvaloniaVncClient/Views/MainWindow.xaml index 55a77273..c8602b03 100644 --- a/samples/AvaloniaVncClient/Views/MainWindow.xaml +++ b/samples/AvaloniaVncClient/Views/MainWindow.xaml @@ -1,3 +1,4 @@ + - - - + + @@ -32,7 +36,8 @@ - + - + @@ -58,7 +64,7 @@ - + @@ -71,11 +77,16 @@ - - -