diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 1510ed02..e18a47db 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -42,7 +42,11 @@ jobs: echo "~/.dotnet/tools" >> $GITHUB_PATH - name: Build docs - run: docfx docfx.json + run: | + dotnet --version + whereis docfx + docfx --version + docfx docfx.json - name: Upload artifact uses: actions/upload-pages-artifact@v3 diff --git a/Directory.Build.targets b/Directory.Build.targets index b191f782..7cc09f18 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -1,14 +1,4 @@ - - - - - - - - - - diff --git a/NearShare.slnx b/NearShare.slnx index 5980d523..19f555d4 100644 --- a/NearShare.slnx +++ b/NearShare.slnx @@ -15,6 +15,7 @@ + diff --git a/lib/ShortDev.Microsoft.ConnectedDevices.NearShare/Messages/FetchDataResponse.cs b/lib/ShortDev.Microsoft.ConnectedDevices.NearShare/Messages/FetchDataResponse.cs index e03b1270..ae578d6f 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices.NearShare/Messages/FetchDataResponse.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices.NearShare/Messages/FetchDataResponse.cs @@ -1,47 +1,48 @@ -using ShortDev.Microsoft.ConnectedDevices.Serialization; +using ShortDev.IO.Bond; +using ShortDev.Microsoft.ConnectedDevices.Serialization; namespace ShortDev.Microsoft.ConnectedDevices.NearShare.Messages; internal static class FetchDataResponse { - public static void Write(EndianWriter writer, uint contentId, ulong start, int length, out Span blob) + public static void Write(ref TWriter writer, uint contentId, ulong start, int length, out Span blob) where TWriter : struct, IEndianWriter, allows ref struct { - CompactBinaryBondWriter bondWriter = new(writer.Buffer); + CompactBinaryWriter bondWriter = new(ref writer); - bondWriter.WriteFieldBegin(Bond.BondDataType.BT_MAP, 1); - bondWriter.WriteContainerBegin(count: 4, Bond.BondDataType.BT_WSTRING, Bond.BondDataType.BT_STRUCT); + bondWriter.WriteFieldBegin(BondDataType.BT_MAP, 1); + bondWriter.WriteContainerBegin(count: 4, BondDataType.BT_WSTRING, BondDataType.BT_STRUCT); WritePropertyBegin(ref bondWriter, "ControlMessage", PropertyType.PropertyType_UInt32); - bondWriter.WriteFieldBegin(Bond.BondDataType.BT_UINT32, 104); + bondWriter.WriteFieldBegin(BondDataType.BT_UINT32, 104); bondWriter.WriteUInt32((uint)NearShareControlMsgType.FetchDataResponse); bondWriter.WriteStructEnd(); WritePropertyBegin(ref bondWriter, "ContentId", PropertyType.PropertyType_UInt32); - bondWriter.WriteFieldBegin(Bond.BondDataType.BT_UINT32, 104); + bondWriter.WriteFieldBegin(BondDataType.BT_UINT32, 104); bondWriter.WriteUInt32(contentId); bondWriter.WriteStructEnd(); WritePropertyBegin(ref bondWriter, "BlobPosition", PropertyType.PropertyType_UInt64); - bondWriter.WriteFieldBegin(Bond.BondDataType.BT_UINT64, 106); + bondWriter.WriteFieldBegin(BondDataType.BT_UINT64, 106); bondWriter.WriteUInt64(start); bondWriter.WriteStructEnd(); WritePropertyBegin(ref bondWriter, "DataBlob", PropertyType.PropertyType_UInt8Array); - bondWriter.WriteFieldBegin(Bond.BondDataType.BT_LIST, 200); - bondWriter.WriteContainerBegin(length, Bond.BondDataType.BT_UINT8); + bondWriter.WriteFieldBegin(BondDataType.BT_LIST, 200); + bondWriter.WriteContainerBegin(length, BondDataType.BT_UINT8); - blob = writer.Buffer.GetSpan(length)[..length]; - writer.Buffer.Advance(length); + blob = writer.GetSpan(length)[..length]; + writer.Advance(length); bondWriter.WriteStructEnd(); bondWriter.WriteStructEnd(); } - static void WritePropertyBegin(ref CompactBinaryBondWriter writer, string name, PropertyType type) + static void WritePropertyBegin(ref CompactBinaryWriter writer, string name, PropertyType type) where TWriter : struct, IEndianWriter, allows ref struct { writer.WriteWString(name); - writer.WriteFieldBegin(Bond.BondDataType.BT_INT32, 0); + writer.WriteFieldBegin(BondDataType.BT_INT32, 0); writer.WriteInt32((int)type); } } diff --git a/lib/ShortDev.Microsoft.ConnectedDevices.NearShare/NearShareSender.cs b/lib/ShortDev.Microsoft.ConnectedDevices.NearShare/NearShareSender.cs index c1c8f5fc..2eff8012 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices.NearShare/NearShareSender.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices.NearShare/NearShareSender.cs @@ -1,4 +1,5 @@ -using ShortDev.Microsoft.ConnectedDevices.Exceptions; +using ShortDev.IO.ValueStream; +using ShortDev.Microsoft.ConnectedDevices.Exceptions; using ShortDev.Microsoft.ConnectedDevices.Messages; using ShortDev.Microsoft.ConnectedDevices.Messages.Session; using ShortDev.Microsoft.ConnectedDevices.NearShare.Apps; @@ -186,9 +187,9 @@ void HandleDataRequest(BinaryMsgHeader header, ValueSet payload) var length = payload.Get("BlobSize"); var fileProvider = _files?[(int)contentId] ?? throw new NullReferenceException("Could not access files to transfer"); - Channel.SendBinaryMessage(writer => + Channel.SendBinaryMessage((ref EndianWriter writer) => { - FetchDataResponse.Write(writer, contentId, start, (int)length, out var blob); + FetchDataResponse.Write(ref writer, contentId, start, (int)length, out var blob); Debug.Assert(blob.Length == length); fileProvider.ReadBlob(start, blob); diff --git a/lib/ShortDev.Microsoft.ConnectedDevices.NearShare/ShortDev.Microsoft.ConnectedDevices.NearShare.csproj b/lib/ShortDev.Microsoft.ConnectedDevices.NearShare/ShortDev.Microsoft.ConnectedDevices.NearShare.csproj index 3a3bb996..85561d53 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices.NearShare/ShortDev.Microsoft.ConnectedDevices.NearShare.csproj +++ b/lib/ShortDev.Microsoft.ConnectedDevices.NearShare/ShortDev.Microsoft.ConnectedDevices.NearShare.csproj @@ -8,4 +8,10 @@ + + + + + + diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/CdpChannel.cs b/lib/ShortDev.Microsoft.ConnectedDevices/CdpChannel.cs index 1a80f6a9..6ff5856d 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/CdpChannel.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/CdpChannel.cs @@ -3,6 +3,7 @@ using ShortDev.Microsoft.ConnectedDevices.Messages.Session; using ShortDev.Microsoft.ConnectedDevices.Session.Channels; using ShortDev.Microsoft.ConnectedDevices.Transports; +using System.Buffers; namespace ShortDev.Microsoft.ConnectedDevices; @@ -49,7 +50,7 @@ private CdpChannel(ChannelHandler handler, ulong channelId, CdpSocket socket, Cd /// public ulong ChannelId { get; } - public void SendBinaryMessage(BodyCallback bodyCallback, uint msgId, List? headers = null) + public void SendBinaryMessage(BodyCallback bodyCallback, uint msgId) { CommonHeader header = new() { @@ -57,17 +58,22 @@ public void SendBinaryMessage(BodyCallback bodyCallback, uint msgId, List ConnectClientAsync(ConnectedDevicesPlatfo #endregion #region SendMessage - public void SendMessage(CdpSocket socket, CommonHeader header, EndianWriter payloadWriter, bool supplyRequestId = false) - => SendMessage(socket, header, payloadWriter.Buffer.AsSpan(), supplyRequestId); - uint _sequenceNumber = 0; ulong _requestId = 0; internal CdpCryptor? Cryptor { get; set; } + + public void SendMessage( + CdpSocket socket, + CommonHeader header, in TMessageHeader messageHeader, in TMessage message, + bool supplyRequestId = false + ) where TMessageHeader : IBinaryWritable where TMessage : IBinaryWritable + { + SendMessage(socket, ref header, messageHeader, message, supplyRequestId: supplyRequestId); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public void SendMessage( + CdpSocket socket, + ref CommonHeader header, in TMessageHeader messageHeader, in TMessage message, + bool supplyRequestId = false + ) where TMessageHeader : IBinaryWritable where TMessage : IBinaryWritable + { + var bufferSize = EndianWriter.CalcBinarySize(messageHeader) + EndianWriter.CalcBinarySize(message); + var writer = EndianWriter.Create(Endianness.BigEndian, ConnectedDevicesPlatform.MemoryPool, initialCapacity: (int)bufferSize); + try + { + messageHeader.Write(ref writer); + message.Write(ref writer); + SendMessage(socket, header, writer.Stream.WrittenSpan, supplyRequestId); + } + finally + { + writer.Dispose(); + } + } + public void SendMessage(CdpSocket socket, CommonHeader header, ReadOnlySpan payload, bool supplyRequestId = false) { if (header.Type == MessageType.Session && Cryptor == null) @@ -121,11 +150,11 @@ public void SendMessage(CdpSocket socket, CommonHeader header, ReadOnlySpan _msgRegistry = new(); - void HandleSession(CommonHeader header, ref EndianReader reader) + void HandleSession(CommonHeader header, ref HeapEndianReader reader) { CdpMessage msg = _msgRegistry.GetOrAdd(header.SequenceNumber, id => new(header)); - msg.AddFragment(reader.ReadToEnd()); // ToDo: Reduce allocations + msg.AddFragment(reader.Stream.ReadSlice((int)(reader.Stream.Length - reader.Stream.Position))); if (msg.IsComplete) { diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/ConnectedDevicesPlatform.MessageLoop.cs b/lib/ShortDev.Microsoft.ConnectedDevices/ConnectedDevicesPlatform.MessageLoop.cs index 79e2f8ed..34aa9dad 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/ConnectedDevicesPlatform.MessageLoop.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/ConnectedDevicesPlatform.MessageLoop.cs @@ -1,26 +1,26 @@ -using ShortDev.Microsoft.ConnectedDevices.Messages; +using ShortDev.IO.Buffers; +using ShortDev.IO.ValueStream; +using ShortDev.Microsoft.ConnectedDevices.Messages; using ShortDev.Microsoft.ConnectedDevices.Transports; -using System.Buffers; namespace ShortDev.Microsoft.ConnectedDevices; partial class ConnectedDevicesPlatform { - static readonly ArrayPool _messagePool = ArrayPool.Create(); private void ReceiveLoop(CdpSocket socket) { RegisterKnownSocket(socket); - Task.Run(() => + Task.Factory.StartNew(() => { - EndianReader streamReader = new(Endianness.BigEndian, socket.InputStream); + var streamReader = EndianReader.FromStream(Endianness.BigEndian, socket.InputStream); using (socket) { ReceiveLoop(socket, ref streamReader); } - }); + }, TaskCreationOptions.LongRunning); } - void ReceiveLoop(CdpSocket socket, ref EndianReader streamReader) + void ReceiveLoop(CdpSocket socket, ref EndianReader streamReader) { do { @@ -38,13 +38,13 @@ void ReceiveLoop(CdpSocket socket, ref EndianReader streamReader) header ); - using var payload = _messagePool.RentToken(header.PayloadSize); + using var payload = MemoryPool.RentMemory(header.PayloadSize); streamReader.ReadBytes(payload.Span); if (socket.IsClosed) return; - EndianReader reader = new(Endianness.BigEndian, payload.Span); + var reader = EndianReader.FromMemory(Endianness.BigEndian, payload); session.HandleMessage(socket, header, ref reader); } catch (IOException) diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/ConnectedDevicesPlatform.Utils.cs b/lib/ShortDev.Microsoft.ConnectedDevices/ConnectedDevicesPlatform.Utils.cs index b85a3e6f..c6c8038d 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/ConnectedDevicesPlatform.Utils.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/ConnectedDevicesPlatform.Utils.cs @@ -1,4 +1,5 @@ using ShortDev.Microsoft.ConnectedDevices.Encryption; +using System.Buffers; using System.Diagnostics.CodeAnalysis; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; @@ -13,4 +14,6 @@ public static X509Certificate2 CreateDeviceCertificate([NotNull] CdpEncryptionPa CertificateRequest certRequest = new("CN=Ms-Cdp", key, HashAlgorithmName.SHA256); return certRequest.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(5)); } + + public static ArrayPool MemoryPool { get; } = ArrayPool.Create(); } diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/DeviceIdHash.cs b/lib/ShortDev.Microsoft.ConnectedDevices/DeviceIdHash.cs new file mode 100644 index 00000000..b1e70971 --- /dev/null +++ b/lib/ShortDev.Microsoft.ConnectedDevices/DeviceIdHash.cs @@ -0,0 +1,19 @@ +using System.Runtime.CompilerServices; + +namespace ShortDev.Microsoft.ConnectedDevices; + +[InlineArray(32)] +public struct DeviceIdHash : IBinaryWritable, IBinaryParsable +{ + public byte _element0; + + public readonly void Write(ref TWriter writer) where TWriter : struct, IEndianWriter, allows ref struct + => writer.Write((ReadOnlySpan)this); + + public static DeviceIdHash Parse(ref TReader reader) where TReader : struct, IEndianReader, allows ref struct + { + DeviceIdHash result = default; + reader.ReadBytes(result); + return result; + } +} diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Encryption/CdpCryptor.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Encryption/CdpCryptor.cs index e581840d..9e73419a 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Encryption/CdpCryptor.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Encryption/CdpCryptor.cs @@ -1,8 +1,11 @@ -using ShortDev.Microsoft.ConnectedDevices.Exceptions; +using ShortDev.IO.Buffers; +using ShortDev.IO.ValueStream; +using ShortDev.Microsoft.ConnectedDevices.Exceptions; using ShortDev.Microsoft.ConnectedDevices.Messages; using ShortDev.Microsoft.ConnectedDevices.Transports; using System.Buffers.Binary; using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Security.Cryptography; namespace ShortDev.Microsoft.ConnectedDevices.Encryption; @@ -25,6 +28,8 @@ public CdpCryptor(byte[] sharedSecret) _hmac = sharedSecret.AsMemory()[^32..^0]; } + [SkipLocalsInit] + [MethodImpl(MethodImplOptions.AggressiveOptimization)] void GenerateIV(CommonHeader header, Span destination) { Debug.Assert(destination.Length == Constants.IVSize); @@ -49,17 +54,20 @@ void ComputeHmac(ReadOnlySpan buffer, Span destination) Debug.Assert(bytesWritten == destination.Length); } - public ReadOnlyMemory DecryptMessage(CommonHeader header, ReadOnlySpan payload, ReadOnlySpan hmac) + public PooledMemory DecryptMessage(CommonHeader header, ReadOnlySpan payload, ReadOnlySpan hmac) { VerifyHMac(header, payload, hmac); Span iv = stackalloc byte[Constants.IVSize]; GenerateIV(header, iv); - byte[] decryptedPayload = _aes.DecryptCbc(payload, iv, PaddingMode.None); + var decryptedBufferSize = _aes.GetCiphertextLengthCbc(payload.Length, PaddingMode.None); - var payloadSize = BinaryPrimitives.ReadUInt32BigEndian(decryptedPayload.AsSpan()[..sizeof(uint)]); - return decryptedPayload.AsMemory(sizeof(uint), (int)payloadSize); + var decryptedPayload = ConnectedDevicesPlatform.MemoryPool.RentMemory(decryptedBufferSize); + _aes.DecryptCbc(payload, iv, decryptedPayload.Span, PaddingMode.None); + + var payloadSize = BinaryPrimitives.ReadUInt32BigEndian(decryptedPayload.Span[..sizeof(uint)]); + return decryptedPayload.Slice(sizeof(uint), (int)payloadSize); } void VerifyHMac(CommonHeader header, ReadOnlySpan payload, ReadOnlySpan hmac) @@ -67,75 +75,85 @@ void VerifyHMac(CommonHeader header, ReadOnlySpan payload, ReadOnlySpan expectedHMac = stackalloc byte[Constants.HMacSize]; - ComputeHmac(buffer, expectedHMac); - if (!hmac.SequenceEqual(expectedHMac)) - throw new CdpSecurityException("Invalid hmac!"); + Span expectedHMac = stackalloc byte[Constants.HMacSize]; + ComputeHmac(buffer, expectedHMac); + if (!hmac.SequenceEqual(expectedHMac)) + throw new CdpSecurityException("Invalid hmac!"); + } + finally + { + writer.Dispose(); + } } public void EncryptMessage(IFragmentSender sender, CommonHeader header, ReadOnlySpan payloadBuffer) { - // Prepend payload with length - ReadOnlySpan finalPayload; + var msgWriter = EndianWriter.Create(Endianness.BigEndian, ConnectedDevicesPlatform.MemoryPool); + try { - EndianWriter payloadWriter = new(Endianness.BigEndian); - payloadWriter.Write((uint)payloadBuffer.Length); - payloadWriter.Write(payloadBuffer); - - finalPayload = payloadWriter.Buffer.AsSpan(); + using (var payloadWriter = EndianWriter.Create(Endianness.BigEndian, ConnectedDevicesPlatform.MemoryPool, payloadBuffer.Length + sizeof(uint))) + { + // Prepend payload with length + payloadWriter.Write((uint)payloadBuffer.Length); + payloadWriter.Write(payloadBuffer); + + // Encrypt + Encrypt(ref msgWriter, header, payloadWriter.Stream.WrittenSpan); + } + + // HMAC + { + var msgBuffer = msgWriter.Stream.WrittenSpan; + Span hmac = stackalloc byte[Constants.HMacSize]; + ComputeHmac(msgBuffer, hmac); + CommonHeader.ModifyMessageLength(msgBuffer.AsSpanUnsafe(), +Constants.HMacSize); + msgWriter.Write(hmac); + } + + sender.SendFragment(msgWriter.Stream.WrittenSpan); } - - // Encrypt - var msgWriter = Encrypt(header, finalPayload); - - // HMAC + finally { - var msgBuffer = msgWriter.Buffer.AsWriteableSpan(); - Span hmac = stackalloc byte[Constants.HMacSize]; - ComputeHmac(msgBuffer, hmac); - CommonHeader.ModifyMessageLength(msgBuffer, +Constants.HMacSize); - msgWriter.Write(hmac); + msgWriter.Dispose(); } - - sender.SendFragment(msgWriter.Buffer.AsSpan()); } - EndianWriter Encrypt(CommonHeader header, ReadOnlySpan buffer) + [SkipLocalsInit] + void Encrypt(ref TWriter writer, CommonHeader header, ReadOnlySpan buffer) where TWriter : struct, IEndianWriter, allows ref struct { // If payload size is an exact multiple of block length (16 bytes) no padding is applied PaddingMode paddingMode = buffer.Length % 16 == 0 ? PaddingMode.None : PaddingMode.PKCS7; var encryptedPayloadLength = _aes.GetCiphertextLengthCbc(buffer.Length, paddingMode); // Write header - EndianWriter writer = new(Endianness.BigEndian); header.Flags |= MessageFlags.SessionEncrypted | MessageFlags.HasHMAC; header.SetPayloadLength(encryptedPayloadLength); - header.Write(writer); + header.Write(ref writer); Span iv = stackalloc byte[Constants.IVSize]; GenerateIV(header, iv); // Encrypt and write to msgWriter - _aes.EncryptCbc(buffer, iv, writer.Buffer.GetSpan(encryptedPayloadLength), paddingMode); - writer.Buffer.Advance(encryptedPayloadLength); - - return writer; + _aes.EncryptCbc(buffer, iv, writer.GetSpan(encryptedPayloadLength), paddingMode); + writer.Advance(encryptedPayloadLength); } - public void Read(ref EndianReader reader, CommonHeader header) + public DisposeToken Read(ref HeapEndianReader reader, CommonHeader header) { if (!header.HasFlag(MessageFlags.SessionEncrypted)) - return; + return default; int payloadSize = header.PayloadSize; if (header.HasFlag(MessageFlags.HasHMAC)) @@ -143,7 +161,7 @@ public void Read(ref EndianReader reader, CommonHeader header) payloadSize -= Constants.HMacSize; } - var encryptedPayload = reader.ReadBytes(payloadSize); + var encryptedPayload = reader.Stream.ReadSlice(payloadSize); scoped Span hmac = []; if (header.HasFlag(MessageFlags.HasHMAC)) @@ -153,7 +171,9 @@ public void Read(ref EndianReader reader, CommonHeader header) } var decryptedPayload = DecryptMessage(header, encryptedPayload, hmac); - reader = new(Endianness.BigEndian, decryptedPayload.Span); + reader = EndianReader.FromMemory(Endianness.BigEndian, decryptedPayload); + + return decryptedPayload; } public void Dispose() diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Encryption/CdpEncryptionInfo.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Encryption/CdpEncryptionInfo.cs index 297f4416..f83797b5 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Encryption/CdpEncryptionInfo.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Encryption/CdpEncryptionInfo.cs @@ -19,15 +19,15 @@ public static CdpEncryptionInfo Create(CdpEncryptionParams encryptionParams) EncryptionParams = encryptionParams }; - public static CdpEncryptionInfo FromRemote(byte[] publicX, byte[] publicY, CdpNonce nonce, CdpEncryptionParams encryptionParams) + public static CdpEncryptionInfo FromRemote(ReadOnlyMemory publicX, ReadOnlyMemory publicY, CdpNonce nonce, CdpEncryptionParams encryptionParams) { var diffieHellman = ECDiffieHellman.Create(new ECParameters() { Curve = encryptionParams.Curve, Q = new ECPoint() { - X = publicX, - Y = publicY + X = publicX.ToArray(), + Y = publicY.ToArray() } }); return new() diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Extensions.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Extensions.cs index 6d6be3dc..12d4261f 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Extensions.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Extensions.cs @@ -1,6 +1,10 @@ -using System.Buffers; +using ShortDev.IO.Buffers; +using System.Buffers; using System.Diagnostics.CodeAnalysis; using System.Net.NetworkInformation; +using System.Runtime.CompilerServices; +using System.Security.Cryptography.X509Certificates; +using System.Text; namespace ShortDev.Microsoft.ConnectedDevices; public static class Extensions @@ -63,21 +67,40 @@ public static void DisposeAll([NotNull] this IEnumerable disposables) wher throw new AggregateException(exceptions); } - public readonly struct ArrayPoolToken(ArrayPool pool, int capacity) : IDisposable + public static ReadOnlyMemory ReadBytesWithLength(this ref TReader reader) where TReader : struct, IEndianReader, allows ref struct { - private readonly ArrayPool? _pool = pool; - private readonly int _capacity = capacity; - private readonly T[] _array = pool.Rent(capacity); + var length = reader.ReadUInt16(); + byte[] buffer = new byte[length]; + reader.ReadBytes(buffer); + return buffer; + } - public T[] ArrayUnsafe => _array; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static X509Certificate2 ReadCert(this ref TReader reader) where TReader : struct, IEndianReader, allows ref struct + { + var length = reader.ReadUInt16(); - public Memory Memory => _array.AsMemory()[0.._capacity]; - public Span Span => Memory.Span; + using var buffer = ArrayPool.Shared.RentMemory(length); + reader.ReadBytes(buffer.Span); - public void Dispose() - => _pool?.Return(_array); + return X509CertificateLoader.LoadCertificate(buffer.Span); } - public static ArrayPoolToken RentToken(this ArrayPool pool, int capacity) - => new(pool, capacity); + [SkipLocalsInit] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PhysicalAddress ReadPhysicalAddress(this ref TReader reader) where TReader : struct, IEndianReader, allows ref struct + { + byte[] buffer = new byte[6]; + reader.ReadBytes(buffer); + buffer.AsSpan().Reverse(); + return new PhysicalAddress(buffer); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Write(this ref TWriter writer, PhysicalAddress address) where TWriter : struct, IEndianWriter, allows ref struct + { + var bytes = address.GetAddressBytes(); + bytes.AsSpan().Reverse(); + writer.Write(bytes); + } } diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/GlobalUsings.cs b/lib/ShortDev.Microsoft.ConnectedDevices/GlobalUsings.cs new file mode 100644 index 00000000..2952b7e4 --- /dev/null +++ b/lib/ShortDev.Microsoft.ConnectedDevices/GlobalUsings.cs @@ -0,0 +1 @@ +global using HeapEndianReader = ShortDev.IO.Input.EndianReader; diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/AdditionalHeader.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/AdditionalHeader.cs index 45d1987a..37117696 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/AdditionalHeader.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/AdditionalHeader.cs @@ -10,7 +10,7 @@ namespace ShortDev.Microsoft.ConnectedDevices.Messages; /// /// /// -public record AdditionalHeader(AdditionalHeaderType Type, ReadOnlyMemory Value) +public readonly record struct AdditionalHeader(AdditionalHeaderType Type, ReadOnlyMemory Value) { public static AdditionalHeader CreateCorrelationHeader() => FromCorrelationVector(new CorrelationVectorV1()); diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/BodyCallback.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/BodyCallback.cs index d4af1540..94fb2b6b 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/BodyCallback.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/BodyCallback.cs @@ -1,3 +1,5 @@ -namespace ShortDev.Microsoft.ConnectedDevices.Messages; +using ShortDev.IO.ValueStream; -public delegate void BodyCallback(EndianWriter writer); +namespace ShortDev.Microsoft.ConnectedDevices.Messages; + +public delegate void BodyCallback(ref EndianWriter writer); diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/CdpMessage.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/CdpMessage.cs index e8cf9de4..d70d03bb 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/CdpMessage.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/CdpMessage.cs @@ -1,27 +1,21 @@ -using ShortDev.Microsoft.ConnectedDevices.Messages.Session; +using ShortDev.IO.ValueStream; +using ShortDev.Microsoft.ConnectedDevices.Messages.Session; using ShortDev.Microsoft.ConnectedDevices.Serialization; using ShortDev.Microsoft.ConnectedDevices.Transports; namespace ShortDev.Microsoft.ConnectedDevices.Messages; -public sealed class CdpMessage +public sealed class CdpMessage(CommonHeader header) { - readonly EndianBuffer _buffer; - - public CdpMessage(CommonHeader header) + readonly HeapOutputStream _buffer = new() { - Header = header; - _buffer = new(header.FragmentCount * MessageFragmenter.DefaultMessageFragmentSize); - } - - public CdpMessage(CommonHeader header, byte[] payload) - { - Header = header; - _buffer = new(payload); - _receivedFragmentCount = header.FragmentCount; - } + Writer = new() + { + Capacity = header.FragmentCount * MessageFragmenter.DefaultMessageFragmentSize + } + }; - public CommonHeader Header { get; } + public CommonHeader Header { get; } = header; public uint Id => Header.SequenceNumber; @@ -41,14 +35,14 @@ public void AddFragment(ReadOnlySpan fragment) } #endregion - public void Read(out EndianReader reader) + public void Read(out HeapEndianReader reader) { ThrowIfNotCompleted(); - reader = new(Endianness.BigEndian, _buffer.AsSpan()); + reader = EndianReader.FromMemory(Endianness.BigEndian, _buffer.WrittenMemory); } - public void ReadBinary(out EndianReader reader, out BinaryMsgHeader header) + public void ReadBinary(out HeapEndianReader reader, out BinaryMsgHeader header) { ThrowIfNotCompleted(); @@ -60,7 +54,7 @@ public void ReadBinary(out ValueSet payload, out BinaryMsgHeader header) { ThrowIfNotCompleted(); - ReadBinary(out EndianReader reader, out header); + ReadBinary(out HeapEndianReader reader, out header); payload = ValueSet.Parse(ref reader); } diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/CommonHeaders.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/CommonHeaders.cs index 948c0a6a..8b77db49 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/CommonHeaders.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/CommonHeaders.cs @@ -7,16 +7,16 @@ namespace ShortDev.Microsoft.ConnectedDevices.Messages; /// /// The is common for all Messages. /// -public sealed class CommonHeader : ICdpHeader +public sealed class CommonHeader : IBinaryWritable, IBinaryParsable { - public static CommonHeader Parse(ref EndianReader reader) + public static CommonHeader Parse(ref TReader reader) where TReader : struct, IEndianReader, allows ref struct { if (!TryParse(ref reader, out var result, out var ex)) throw ex; return result; } - public static bool TryParse(ref EndianReader reader, [MaybeNullWhen(false)] out CommonHeader result, [MaybeNullWhen(true)] out Exception ex) + public static bool TryParse(ref TReader reader, [MaybeNullWhen(false)] out CommonHeader result, [MaybeNullWhen(true)] out Exception ex) where TReader : IEndianReader, allows ref struct { result = new(); var sig = reader.ReadUInt16(); @@ -27,14 +27,14 @@ public static bool TryParse(ref EndianReader reader, [MaybeNullWhen(false)] out } result.MessageLength = reader.ReadUInt16(); - result.Version = reader.ReadByte(); + result.Version = reader.ReadUInt8(); if (result.Version != Constants.ProtocolVersion) { ex = new InvalidDataException($"Wrong version. Got \"{result.Version}\", expected \"{Constants.ProtocolVersion}\""); return false; } - result.Type = (MessageType)reader.ReadByte(); + result.Type = (MessageType)reader.ReadUInt8(); result.Flags = (MessageFlags)reader.ReadInt16(); result.SequenceNumber = reader.ReadUInt32(); result.RequestID = reader.ReadUInt64(); @@ -48,14 +48,15 @@ public static bool TryParse(ref EndianReader reader, [MaybeNullWhen(false)] out List additionalHeaders = []; while (true) { - nextHeaderType = (AdditionalHeaderType)reader.ReadByte(); - nextHeaderSize = reader.ReadByte(); + nextHeaderType = (AdditionalHeaderType)reader.ReadUInt8(); + nextHeaderSize = reader.ReadUInt8(); if (nextHeaderType == AdditionalHeaderType.None) break; - var value = reader.ReadBytes(nextHeaderSize); - additionalHeaders.Add(new(nextHeaderType, value.ToArray())); + byte[] value = new byte[nextHeaderSize]; + reader.ReadBytes(value); + additionalHeaders.Add(new(nextHeaderType, value)); } if (nextHeaderSize != 0) @@ -70,7 +71,8 @@ public static bool TryParse(ref EndianReader reader, [MaybeNullWhen(false)] out return true; } - public void Write(EndianWriter writer) + + public void Write(ref TWriter writer) where TWriter : struct, IEndianWriter, allows ref struct { writer.Write(Constants.Signature); writer.Write(MessageLength); @@ -245,6 +247,6 @@ public void SetReplyToId(ulong requestId) if (header == null) return null; - return BinaryPrimitives.ReadUInt64LittleEndian(header.Value.Span); + return BinaryPrimitives.ReadUInt64LittleEndian(header.Value.Value.Span); } } diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/Authentication/AuthenticationPayload.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/Authentication/AuthenticationPayload.cs index a239512b..b9b167cc 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/Authentication/AuthenticationPayload.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/Authentication/AuthenticationPayload.cs @@ -1,4 +1,5 @@ -using ShortDev.Microsoft.ConnectedDevices.Encryption; +using ShortDev.IO.ValueStream; +using ShortDev.Microsoft.ConnectedDevices.Encryption; using ShortDev.Microsoft.ConnectedDevices.Exceptions; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; @@ -8,15 +9,13 @@ namespace ShortDev.Microsoft.ConnectedDevices.Messages.Connection.Authentication /// /// For all authentication, devices send their device / user certificate, which is self-signed. /// -public sealed class AuthenticationPayload : ICdpPayload +public readonly record struct AuthenticationPayload : IBinaryWritable, IBinaryParsable { - private AuthenticationPayload() { } - - public static AuthenticationPayload Parse(ref EndianReader reader) + public static AuthenticationPayload Parse(ref TReader reader) where TReader : struct, IEndianReader, allows ref struct => new() { - Certificate = X509CertificateLoader.LoadCertificate(reader.ReadBytesWithLength()), - SignedThumbprint = reader.ReadBytesWithLength().ToArray() + Certificate = reader.ReadCert(), + SignedThumbprint = reader.ReadBytesWithLength() }; /// @@ -40,12 +39,12 @@ public static AuthenticationPayload Create(X509Certificate2 cert, CdpNonce hostN /// /// A signed Device Cert Thumbprint. /// - public required byte[] SignedThumbprint { get; init; } + public required ReadOnlyMemory SignedThumbprint { get; init; } - public void Write(EndianWriter writer) + public void Write(ref TWriter writer) where TWriter : struct, IEndianWriter, allows ref struct { writer.WriteWithLength(Certificate.Export(X509ContentType.Cert)); - writer.WriteWithLength(SignedThumbprint); + writer.WriteWithLength(SignedThumbprint.Span); } /// @@ -59,29 +58,33 @@ public bool VerifyThumbprint(CdpNonce hostNonce, CdpNonce clientNonce) { var publicKey = Certificate.GetECDsaPublicKey() ?? throw new CdpSecurityException("Invalid certificate!"); - var merged = MergeNoncesWithCertificate(Certificate, hostNonce, clientNonce).Span; - return publicKey.VerifyData(merged, SignedThumbprint, thumbprintHashType); + // Use little-endian here! + using var nonceWriter = EndianWriter.Create(Endianness.LittleEndian, ConnectedDevicesPlatform.MemoryPool); + MergeNoncesWithCertificate(nonceWriter, Certificate, hostNonce, clientNonce); + + return publicKey.VerifyData(nonceWriter.Stream.WrittenSpan, SignedThumbprint.Span, thumbprintHashType); } #region Thumbprint Api static readonly HashAlgorithmName thumbprintHashType = HashAlgorithmName.SHA256; - static ReadOnlyMemory MergeNoncesWithCertificate(X509Certificate2 cert, CdpNonce hostNonce, CdpNonce clientNonce) + static void MergeNoncesWithCertificate(EndianWriter writer, X509Certificate2 cert, CdpNonce hostNonce, CdpNonce clientNonce) { byte[] certData = cert.Export(X509ContentType.Cert); - EndianWriter writer = new(Endianness.LittleEndian); // Use little-endian here! writer.Write(hostNonce.Value); writer.Write(clientNonce.Value); writer.Write(certData); - return writer.Buffer.AsMemory(); } static byte[] CreateSignedThumbprint(X509Certificate2 cert, CdpNonce hostNonce, CdpNonce clientNonce) { - var data = MergeNoncesWithCertificate(cert, hostNonce, clientNonce); + // Use little-endian here! + using var nonceWriter = EndianWriter.Create(Endianness.LittleEndian, ConnectedDevicesPlatform.MemoryPool); + MergeNoncesWithCertificate(nonceWriter, cert, hostNonce, clientNonce); + var privateKey = cert.GetECDsaPrivateKey() ?? throw new ArgumentException("No ECDsa private key!", nameof(cert)); - return privateKey.SignData(data.Span, thumbprintHashType); + return privateKey.SignData(nonceWriter.Stream.WrittenSpan, thumbprintHashType); } #endregion } diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/ConnectionHeader.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/ConnectionHeader.cs index ab946750..b32ad5bc 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/ConnectionHeader.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/ConnectionHeader.cs @@ -3,28 +3,28 @@ /// /// The is common for all Connection Messages. /// -public sealed class ConnectionHeader : ICdpHeader +public readonly record struct ConnectionHeader : IBinaryWritable, IBinaryParsable { /// /// Indicates the current connection type. /// - public required ConnectionType MessageType { get; set; } + public required ConnectionType MessageType { get; init; } /// /// Displays the types of available connections. /// - public required ConnectionMode ConnectionMode { get; set; } + public required ConnectionMode ConnectionMode { get; init; } - public static ConnectionHeader Parse(ref EndianReader reader) + public static ConnectionHeader Parse(ref TReader reader) where TReader : struct, IEndianReader, allows ref struct { return new() { ConnectionMode = (ConnectionMode)reader.ReadInt16(), - MessageType = (ConnectionType)reader.ReadByte() + MessageType = (ConnectionType)reader.ReadUInt8() }; } - public void Write(EndianWriter writer) + public void Write(ref TWriter writer) where TWriter : struct, IEndianWriter, allows ref struct { writer.Write((short)ConnectionMode); writer.Write((byte)MessageType); diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/ConnectionRequest.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/ConnectionRequest.cs index fffa2af7..0af53905 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/ConnectionRequest.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/ConnectionRequest.cs @@ -6,50 +6,50 @@ namespace ShortDev.Microsoft.ConnectedDevices.Messages.Connection; /// /// Client initiates a connection request with a host device. /// -public sealed class ConnectionRequest : ICdpPayload +public readonly record struct ConnectionRequest : IBinaryWritable, IBinaryParsable { /// /// The type of elliptical curve used. /// - public required CurveType CurveType { get; set; } + public required CurveType CurveType { get; init; } /// /// The expected size of HMAC. /// - public required ushort HmacSize { get; set; } + public required ushort HmacSize { get; init; } /// /// Random values /// - public required CdpNonce Nonce { get; set; } + public required CdpNonce Nonce { get; init; } /// /// The maximum size of a single message fragment.
/// (Fixed Value of ). ///
- public required uint MessageFragmentSize { get; set; } + public required uint MessageFragmentSize { get; init; } /// /// A fixed-length key. /// This is the X component of the key.
/// (See ) ///
- public required byte[] PublicKeyX { get; set; } + public required ReadOnlyMemory PublicKeyX { get; init; } /// /// A fixed-length key. /// This is the Y component of the key.
/// (See ) ///
- public required byte[] PublicKeyY { get; set; } + public required ReadOnlyMemory PublicKeyY { get; init; } - public static ConnectionRequest Parse(ref EndianReader reader) + public static ConnectionRequest Parse(ref TReader reader) where TReader : struct, IEndianReader, allows ref struct => new() { - CurveType = (CurveType)reader.ReadByte(), + CurveType = (CurveType)reader.ReadUInt8(), HmacSize = reader.ReadUInt16(), Nonce = new(reader.ReadUInt64()), MessageFragmentSize = reader.ReadUInt32(), - PublicKeyX = reader.ReadBytesWithLength().ToArray(), - PublicKeyY = reader.ReadBytesWithLength().ToArray() + PublicKeyX = reader.ReadBytesWithLength(), + PublicKeyY = reader.ReadBytesWithLength() }; - public void Write(EndianWriter writer) + public void Write(ref TWriter writer) where TWriter : struct, IEndianWriter, allows ref struct { writer.Write((byte)CurveType); writer.Write((ushort)HmacSize); @@ -57,8 +57,8 @@ public void Write(EndianWriter writer) writer.Write((uint)MessageFragmentSize); writer.Write((ushort)PublicKeyX.Length); - writer.Write(PublicKeyX); + writer.Write(PublicKeyX.Span); writer.Write((ushort)PublicKeyY.Length); - writer.Write(PublicKeyY); + writer.Write(PublicKeyY.Span); } } \ No newline at end of file diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/ConnectionResponse.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/ConnectionResponse.cs index f9a7f47b..dadbbed5 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/ConnectionResponse.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/ConnectionResponse.cs @@ -7,53 +7,53 @@ namespace ShortDev.Microsoft.ConnectedDevices.Messages.Connection; /// The host responds with a connection response message including device information.
/// Only the Result is sent if the Result is anything other than . ///
-public sealed class ConnectionResponse : ICdpPayload +public readonly record struct ConnectionResponse : IBinaryWritable, IBinaryParsable { /// /// The result of the connection request. /// - public required ConnectionResult Result { get; set; } + public required ConnectionResult Result { get; init; } /// /// The expected size of HMAC. /// - public required ushort HmacSize { get; set; } + public required ushort HmacSize { get; init; } /// /// Random values. /// - public required CdpNonce Nonce { get; set; } + public required CdpNonce Nonce { get; init; } /// /// The maximum size of a single message fragment.
/// (Fixed Value of ). ///
- public required uint MessageFragmentSize { get; set; } + public required uint MessageFragmentSize { get; init; } /// /// A fixed-length key that is based on the from , which is sent only if the connection is successful.
/// This is the X component of the key.
/// (See ) ///
- public required byte[] PublicKeyX { get; set; } + public required ReadOnlyMemory PublicKeyX { get; init; } /// /// A fixed-length key that is based on the from , which is sent only if the connection is successful.
/// This is the Y component of the key. /// (See ) ///
- public required byte[] PublicKeyY { get; set; } + public required ReadOnlyMemory PublicKeyY { get; init; } - public static ConnectionResponse Parse(ref EndianReader reader) + public static ConnectionResponse Parse(ref TReader reader) where TReader : struct, IEndianReader, allows ref struct { - var result = (ConnectionResult)reader.ReadByte(); + var result = (ConnectionResult)reader.ReadUInt8(); return new() { Result = result, HmacSize = reader.ReadUInt16(), Nonce = new(reader.ReadUInt64()), MessageFragmentSize = reader.ReadUInt32(), - PublicKeyX = reader.ReadBytesWithLength().ToArray(), - PublicKeyY = reader.ReadBytesWithLength().ToArray() + PublicKeyX = reader.ReadBytesWithLength(), + PublicKeyY = reader.ReadBytesWithLength() }; } - public void Write(EndianWriter writer) + public void Write(ref TWriter writer) where TWriter : struct, IEndianWriter, allows ref struct { writer.Write((byte)Result); writer.Write((ushort)HmacSize); @@ -61,8 +61,8 @@ public void Write(EndianWriter writer) writer.Write((uint)MessageFragmentSize); writer.Write((ushort)PublicKeyX.Length); - writer.Write(PublicKeyX); + writer.Write(PublicKeyX.Span); writer.Write((ushort)PublicKeyY.Length); - writer.Write(PublicKeyY); + writer.Write(PublicKeyY.Span); } } diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/DeviceInfo/DeviceInfoMessage.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/DeviceInfo/DeviceInfoMessage.cs index 642b1094..5b56c0be 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/DeviceInfo/DeviceInfoMessage.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/DeviceInfo/DeviceInfoMessage.cs @@ -6,9 +6,9 @@ namespace ShortDev.Microsoft.ConnectedDevices.Messages.Connection.DeviceInfo; /// /// This message requests information from the device. /// -public sealed class DeviceInfoMessage : ICdpPayload +public readonly record struct DeviceInfoMessage : IBinaryWritable, IBinaryParsable { - public static DeviceInfoMessage Parse(ref EndianReader reader) + public static DeviceInfoMessage Parse(ref TReader reader) where TReader : struct, IEndianReader, allows ref struct => new() { DeviceInfo = JsonSerializer.Deserialize(reader.ReadStringWithLength()) ?? throw new CdpProtocolException("Invalid device info") @@ -19,7 +19,7 @@ public static DeviceInfoMessage Parse(ref EndianReader reader) ///
public required CdpDeviceInfo DeviceInfo { get; init; } - public void Write(EndianWriter writer) + public void Write(ref TWriter writer) where TWriter : struct, IEndianWriter, allows ref struct { writer.WriteWithLength(JsonSerializer.Serialize(DeviceInfo)); } diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/TransportUpgrade/EndpointMetadata.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/TransportUpgrade/EndpointMetadata.cs index 44bbcd53..c028378d 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/TransportUpgrade/EndpointMetadata.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/TransportUpgrade/EndpointMetadata.cs @@ -2,20 +2,22 @@ namespace ShortDev.Microsoft.ConnectedDevices.Messages.Connection.TransportUpgrade; -public record class EndpointMetadata(CdpTransportType Type, byte[] Data) : ICdpPayload, ICdpArraySerializable +public readonly record struct EndpointMetadata(CdpTransportType Type, byte[] Data) : IBinaryWritable, IBinaryParsable { public static EndpointMetadata Tcp { get; } = new(CdpTransportType.Tcp, []); public static EndpointMetadata WifiDirect { get; } = new(CdpTransportType.WifiDirect, []); - public static EndpointMetadata Parse(ref EndianReader reader) + public static EndpointMetadata Parse(ref TReader reader) where TReader : struct, IEndianReader, allows ref struct { var type = (CdpTransportType)reader.ReadUInt16(); var length = reader.ReadInt32(); - var data = reader.ReadBytes(length); - return new(type, data.ToArray()); + + byte[] data = new byte[length]; + reader.ReadBytes(data); + return new(type, data); } - public static IReadOnlyList ParseArray(ref EndianReader reader) + public static IReadOnlyList ParseArray(ref TReader reader) where TReader : struct, IEndianReader, allows ref struct { var arrayLength = reader.ReadUInt16(); var endpoints = new EndpointMetadata[arrayLength]; @@ -24,17 +26,25 @@ public static IReadOnlyList ParseArray(ref EndianReader reader return endpoints; } - public void Write(EndianWriter writer) + public void Write(ref TWriter writer) where TWriter : struct, IEndianWriter, allows ref struct { writer.Write((ushort)Type); writer.Write((uint)Data.Length); writer.Write(Data); } - public static void WriteArray(EndianWriter writer, IReadOnlyList endpoints) + public static void WriteArray(ref TWriter writer, IReadOnlyList endpoints) where TWriter : struct, IEndianWriter, allows ref struct { writer.Write((ushort)endpoints.Count); foreach (var endpoint in endpoints) - endpoint.Write(writer); + endpoint.Write(ref writer); } } + +public readonly record struct EndpointMetadataArray(IReadOnlyList Endpoints) : IBinaryWritable, IBinaryParsable +{ + public void Write(ref TWriter writer) where TWriter : struct, IEndianWriter, allows ref struct + => EndpointMetadata.WriteArray(ref writer, Endpoints); + public static EndpointMetadataArray Parse(ref TReader reader) where TReader : struct, IEndianReader, allows ref struct + => new(EndpointMetadata.ParseArray(ref reader)); +} diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/TransportUpgrade/UpgradeIdPayload.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/TransportUpgrade/UpgradeIdPayload.cs index e1a43291..8cd57695 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/TransportUpgrade/UpgradeIdPayload.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/TransportUpgrade/UpgradeIdPayload.cs @@ -4,18 +4,18 @@ /// This message transports the details of the upgrade.
/// (See and ) /// -public sealed class UpgradeIdPayload : ICdpPayload +public readonly record struct UpgradeIdPayload : IBinaryWritable, IBinaryParsable { public required Guid UpgradeId { get; init; } - public static UpgradeIdPayload Parse(ref EndianReader reader) + public static UpgradeIdPayload Parse(ref TReader reader) where TReader : struct, IEndianReader, allows ref struct => new() { UpgradeId = reader.ReadGuid() }; - public void Write(EndianWriter writer) + public void Write(ref TWriter writer) where TWriter : struct, IEndianWriter, allows ref struct { - writer.Write(UpgradeId.ToByteArray()); + writer.Write(UpgradeId); } } diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/TransportUpgrade/UpgradeRequest.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/TransportUpgrade/UpgradeRequest.cs index f1d11ecc..d1402294 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/TransportUpgrade/UpgradeRequest.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/TransportUpgrade/UpgradeRequest.cs @@ -3,7 +3,7 @@ /// /// This message transports the upgrade request. /// -public sealed class UpgradeRequest : ICdpPayload +public readonly record struct UpgradeRequest : IBinaryWritable, IBinaryParsable { /// /// A random GUID identifying this upgrade process across transports. @@ -11,16 +11,16 @@ public sealed class UpgradeRequest : ICdpPayload public required Guid UpgradeId { get; init; } public required IReadOnlyList Endpoints { get; init; } - public static UpgradeRequest Parse(ref EndianReader reader) + public static UpgradeRequest Parse(ref TReader reader) where TReader : struct, IEndianReader, allows ref struct => new() { UpgradeId = reader.ReadGuid(), Endpoints = EndpointMetadata.ParseArray(ref reader) }; - public void Write(EndianWriter writer) + public void Write(ref TWriter writer) where TWriter : struct, IEndianWriter, allows ref struct { - writer.Write(UpgradeId.ToByteArray()); - EndpointMetadata.WriteArray(writer, Endpoints); + writer.Write(UpgradeId); + EndpointMetadata.WriteArray(ref writer, Endpoints); } } \ No newline at end of file diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/TransportUpgrade/UpgradeResponse.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/TransportUpgrade/UpgradeResponse.cs index 8f04d512..838fa0ed 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/TransportUpgrade/UpgradeResponse.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/TransportUpgrade/UpgradeResponse.cs @@ -5,7 +5,7 @@ namespace ShortDev.Microsoft.ConnectedDevices.Messages.Connection.TransportUpgra /// /// This message transports the upgrade response. /// -public sealed class UpgradeResponse : ICdpPayload +public readonly record struct UpgradeResponse : IBinaryWritable, IBinaryParsable { /// /// A length-prefixed list of endpoint structures (see following) that are provided by each transport on the host device. @@ -13,7 +13,7 @@ public sealed class UpgradeResponse : ICdpPayload public required IReadOnlyList Endpoints { get; init; } public required IReadOnlyList MetaData { get; init; } - public static UpgradeResponse Parse(ref EndianReader reader) + public static UpgradeResponse Parse(ref TReader reader) where TReader : struct, IEndianReader, allows ref struct { var length = reader.ReadUInt16(); var endpoints = new EndpointInfo[length]; @@ -35,7 +35,7 @@ public static UpgradeResponse Parse(ref EndianReader reader) }; } - public void Write(EndianWriter writer) + public void Write(ref TWriter writer) where TWriter : struct, IEndianWriter, allows ref struct { writer.Write((ushort)Endpoints.Count); foreach (var endpoint in Endpoints) @@ -45,6 +45,6 @@ public void Write(EndianWriter writer) writer.Write((ushort)endpoint.TransportType); } - EndpointMetadata.WriteArray(writer, MetaData); + EndpointMetadata.WriteArray(ref writer, MetaData); } } diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Control/ControlHeader.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Control/ControlHeader.cs index 6ed8b731..74da603f 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Control/ControlHeader.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Control/ControlHeader.cs @@ -1,18 +1,18 @@ namespace ShortDev.Microsoft.ConnectedDevices.Messages.Control; -public sealed class ControlHeader : ICdpHeader +public readonly record struct ControlHeader : IBinaryWritable, IBinaryParsable { - public required ControlMessageType MessageType { get; set; } + public required ControlMessageType MessageType { get; init; } - public static ControlHeader Parse(ref EndianReader reader) + public static ControlHeader Parse(ref TReader reader) where TReader : struct, IEndianReader, allows ref struct { return new() { - MessageType = (ControlMessageType)reader.ReadByte() + MessageType = (ControlMessageType)reader.ReadUInt8() }; } - public void Write(EndianWriter writer) + public void Write(ref TWriter writer) where TWriter : struct, IEndianWriter, allows ref struct { writer.Write((byte)MessageType); } diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Control/StartChannelRequest.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Control/StartChannelRequest.cs index 7bbafecd..b5b6b9e6 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Control/StartChannelRequest.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Control/StartChannelRequest.cs @@ -1,8 +1,8 @@ namespace ShortDev.Microsoft.ConnectedDevices.Messages.Control; -public sealed class StartChannelRequest : ICdpPayload +public readonly record struct StartChannelRequest() : IBinaryWritable, IBinaryParsable { - public static StartChannelRequest Parse(ref EndianReader reader) + public static StartChannelRequest Parse(ref TReader reader) where TReader : struct, IEndianReader, allows ref struct => new() { Id = reader.ReadStringWithLength(), @@ -16,7 +16,7 @@ public static StartChannelRequest Parse(ref EndianReader reader) public short Reserved1 { get; init; } public string Reserved2 { get; init; } = string.Empty; - public void Write(EndianWriter writer) + public void Write(ref TWriter writer) where TWriter : struct, IEndianWriter, allows ref struct { writer.WriteWithLength(Id); writer.WriteWithLength(Name); diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Control/StartChannelResponse.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Control/StartChannelResponse.cs index b82dec9f..d7063e4e 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Control/StartChannelResponse.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Control/StartChannelResponse.cs @@ -2,12 +2,12 @@ namespace ShortDev.Microsoft.ConnectedDevices.Messages.Control; -public sealed class StartChannelResponse : ICdpPayload +public readonly record struct StartChannelResponse : IBinaryWritable, IBinaryParsable { - public static StartChannelResponse Parse(ref EndianReader reader) + public static StartChannelResponse Parse(ref TReader reader) where TReader : struct, IEndianReader, allows ref struct => new() { - Result = (ChannelResult)reader.ReadByte(), + Result = (ChannelResult)reader.ReadUInt8(), ChannelId = reader.ReadUInt64() }; @@ -20,7 +20,7 @@ public void ThrowOnError() throw new CdpProtocolException($"Could not create channel. {Result}"); } - public void Write(EndianWriter writer) + public void Write(ref TWriter writer) where TWriter : struct, IEndianWriter, allows ref struct { writer.Write((byte)Result); writer.Write(ChannelId); diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Discovery/DiscoveryHeader.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Discovery/DiscoveryHeader.cs index 1ea762a6..2f3fbe11 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Discovery/DiscoveryHeader.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Discovery/DiscoveryHeader.cs @@ -1,17 +1,17 @@ namespace ShortDev.Microsoft.ConnectedDevices.Messages.Discovery; -public sealed class DiscoveryHeader : ICdpHeader +public readonly record struct DiscoveryHeader : IBinaryWritable, IBinaryParsable { - public static DiscoveryHeader Parse(ref EndianReader reader) + public static DiscoveryHeader Parse(ref TReader reader) where TReader : struct, IEndianReader, allows ref struct => new() { - Type = (DiscoveryType)reader.ReadByte() + Type = (DiscoveryType)reader.ReadUInt8() }; - public void Write(EndianWriter writer) + public void Write(ref TWriter writer) where TWriter : struct, IEndianWriter, allows ref struct { writer.Write((byte)Type); } - public required DiscoveryType Type { get; set; } + public required DiscoveryType Type { get; init; } } diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Discovery/PresenceResponse.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Discovery/PresenceResponse.cs index 83a9c292..2bc0ae13 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Discovery/PresenceResponse.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Discovery/PresenceResponse.cs @@ -1,9 +1,10 @@ using ShortDev.Microsoft.ConnectedDevices.Messages.Connection; +using System.Diagnostics; using System.Security.Cryptography; namespace ShortDev.Microsoft.ConnectedDevices.Messages.Discovery; -public class PresenceResponse : ICdpPayload +public readonly record struct PresenceResponse : IBinaryWritable, IBinaryParsable { public required ConnectionMode ConnectionMode { get; init; } @@ -13,25 +14,25 @@ public class PresenceResponse : ICdpPayload public required int DeviceIdSalt { get; init; } - public required byte[] DeviceIdHash { get; init; } + public required DeviceIdHash DeviceIdHash { get; init; } - public static PresenceResponse Parse(ref EndianReader reader) + public static PresenceResponse Parse(ref TReader reader) where TReader : struct, IEndianReader, allows ref struct => new() { ConnectionMode = (ConnectionMode)reader.ReadInt16(), DeviceType = (DeviceType)reader.ReadInt16(), DeviceName = reader.ReadStringWithLength(), DeviceIdSalt = reader.ReadInt32(), - DeviceIdHash = reader.ReadBytes(32).ToArray() + DeviceIdHash = reader.Read() }; - public void Write(EndianWriter writer) + public void Write(ref TWriter writer) where TWriter : struct, IEndianWriter, allows ref struct { writer.Write((short)ConnectionMode); writer.Write((short)DeviceType); writer.WriteWithLength(DeviceName); writer.Write((int)DeviceIdSalt); - writer.Write(DeviceIdHash); + writer.Write(DeviceIdHash); } static readonly HashAlgorithm _hashAlgorithm = SHA256.Create(); @@ -39,15 +40,16 @@ public static PresenceResponse Create(LocalDeviceInfo deviceInfo, ConnectionMode { var salt = RandomNumberGenerator.GetInt32(int.MaxValue); - using MemoryStream stream = new(); - EndianWriter writer = new(Endianness.LittleEndian); + using var writer = EndianWriter.Create(Endianness.LittleEndian, ConnectedDevicesPlatform.MemoryPool); // ToDo: Wrong writer.Write(salt); writer.Write(deviceInfo.GetDeduplicationHint()); - writer.CopyTo(stream); - var hash = _hashAlgorithm.ComputeHash(stream); + Debug.Assert(_hashAlgorithm.HashSize / 8 == 32); + + DeviceIdHash hash = default; + _hashAlgorithm.TryComputeHash(writer.Stream.WrittenSpan, hash, out _); return new() { diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/EmptyMessage.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/EmptyMessage.cs new file mode 100644 index 00000000..7600f96e --- /dev/null +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/EmptyMessage.cs @@ -0,0 +1,12 @@ +namespace ShortDev.Microsoft.ConnectedDevices.Messages; + +internal readonly record struct EmptyMessage : IBinaryParsable, IBinaryWritable +{ + public readonly void Write(ref TWriter writer) where TWriter : struct, IEndianWriter, allows ref struct + { + // No content to write + } + + public static EmptyMessage Parse(ref TReader reader) where TReader : struct, IEndianReader, allows ref struct + => default; +} diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/HResultPayload.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/HResultPayload.cs index dbcdbeca..8aaaa476 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/HResultPayload.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/HResultPayload.cs @@ -3,9 +3,9 @@ /// /// General payload to represent a HRESULT (). /// -public sealed class HResultPayload : ICdpPayload +public readonly record struct HResultPayload : IBinaryWritable, IBinaryParsable { - public static HResultPayload Parse(ref EndianReader reader) + public static HResultPayload Parse(ref TReader reader) where TReader : struct, IEndianReader, allows ref struct => new() { HResult = reader.ReadInt32() @@ -13,7 +13,7 @@ public static HResultPayload Parse(ref EndianReader reader) public required int HResult { get; init; } - public void Write(EndianWriter writer) + public void Write(ref TWriter writer) where TWriter : struct, IEndianWriter, allows ref struct { writer.Write(HResult); } diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/ICdpHeader.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/ICdpHeader.cs deleted file mode 100644 index 05810e68..00000000 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/ICdpHeader.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace ShortDev.Microsoft.ConnectedDevices.Messages; - -public interface ICdpHeader : ICdpSerializable where T : ICdpHeader { } \ No newline at end of file diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/ICdpPayload.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/ICdpPayload.cs deleted file mode 100644 index 75fd4866..00000000 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/ICdpPayload.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace ShortDev.Microsoft.ConnectedDevices.Messages; - -public interface ICdpPayload : ICdpSerializable where T : ICdpPayload { } \ No newline at end of file diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/ICdpSerializable.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/ICdpSerializable.cs deleted file mode 100644 index 9642636f..00000000 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/ICdpSerializable.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace ShortDev.Microsoft.ConnectedDevices.Messages; - -public interface ICdpSerializable : ICdpWriteable where T : ICdpSerializable -{ - static abstract T Parse(ref EndianReader reader); - public static bool TryParse(ref EndianReader reader, out T? result, out Exception? error) - => throw new NotImplementedException(); -} - -public interface ICdpArraySerializable where T : ICdpArraySerializable -{ - static abstract IReadOnlyList ParseArray(ref EndianReader reader); - static abstract void WriteArray(EndianWriter writer, IReadOnlyList array); -} - -public interface ICdpWriteable -{ - void Write(EndianWriter writer); -} diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/PeerCapabilities.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/PeerCapabilities.cs index 0dc84e5e..5b8d4d43 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/PeerCapabilities.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/PeerCapabilities.cs @@ -1,6 +1,4 @@ -using System; - -namespace ShortDev.Microsoft.ConnectedDevices.Messages; +namespace ShortDev.Microsoft.ConnectedDevices.Messages; [Flags] public enum PeerCapabilities : ulong diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/ResultPayload.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/ResultPayload.cs index 1a73aff0..61ec9c15 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/ResultPayload.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/ResultPayload.cs @@ -7,17 +7,17 @@ namespace ShortDev.Microsoft.ConnectedDevices.Messages; ///
/// (e.g. ) ///
-public sealed class ResultPayload : ICdpPayload +public readonly record struct ResultPayload : IBinaryWritable, IBinaryParsable { - public static ResultPayload Parse(ref EndianReader reader) + public static ResultPayload Parse(ref TReader reader) where TReader : struct, IEndianReader, allows ref struct => new() { - Result = (CdpResult)reader.ReadByte() + Result = (CdpResult)reader.ReadUInt8() }; public required CdpResult Result { get; init; } - public void Write(EndianWriter writer) + public void Write(ref TWriter writer) where TWriter : struct, IEndianWriter, allows ref struct { writer.Write((byte)Result); } diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Session/AppControl/AppControlHeader.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Session/AppControl/AppControlHeader.cs index 1fab8dbe..af8c0eea 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Session/AppControl/AppControlHeader.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Session/AppControl/AppControlHeader.cs @@ -1,18 +1,18 @@ namespace ShortDev.Microsoft.ConnectedDevices.Messages.Session.AppControl; -public sealed class AppControlHeader : ICdpHeader +public readonly record struct AppControlHeader : IBinaryWritable, IBinaryParsable { - public required AppControlType MessageType { get; set; } + public required AppControlType MessageType { get; init; } - public static AppControlHeader Parse(ref EndianReader reader) + public static AppControlHeader Parse(ref TReader reader) where TReader : struct, IEndianReader, allows ref struct { return new() { - MessageType = (AppControlType)reader.ReadByte() + MessageType = (AppControlType)reader.ReadUInt8() }; } - public void Write(EndianWriter writer) + public void Write(ref TWriter writer) where TWriter : struct, IEndianWriter, allows ref struct { writer.Write((byte)MessageType); } diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Session/AppControl/LaunchUriForTargetRequest.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Session/AppControl/LaunchUriForTargetRequest.cs index cf44fa05..bffd276d 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Session/AppControl/LaunchUriForTargetRequest.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Session/AppControl/LaunchUriForTargetRequest.cs @@ -1,8 +1,8 @@ namespace ShortDev.Microsoft.ConnectedDevices.Messages.Session.AppControl; -public sealed class LaunchUriForTargetRequest : ICdpPayload +public readonly record struct LaunchUriForTargetRequest() : IBinaryWritable, IBinaryParsable { - public static LaunchUriForTargetRequest Parse(ref EndianReader reader) + public static LaunchUriForTargetRequest Parse(ref TReader reader) where TReader : struct, IEndianReader, allows ref struct => new() { Uri = reader.ReadStringWithLength(), @@ -13,7 +13,7 @@ public static LaunchUriForTargetRequest Parse(ref EndianReader reader) AlternateId = reader.ReadStringWithLength(), TitleId = reader.ReadInt32(), FacadeName = reader.ReadStringWithLength(), - InputData = reader.ReadBytesWithLength().ToArray() + InputData = reader.ReadBytesWithLength() }; /// @@ -50,9 +50,9 @@ public static LaunchUriForTargetRequest Parse(ref EndianReader reader) /// BOND.NET serialized data that is passed as a value set to the app launched by the call.
/// (Optional) ///
- public byte[] InputData { get; init; } = []; + public ReadOnlyMemory InputData { get; init; } = default; - public void Write(EndianWriter writer) + public void Write(ref TWriter writer) where TWriter : struct, IEndianWriter, allows ref struct { writer.WriteWithLength(Uri); writer.Write((short)LaunchLocation); @@ -62,6 +62,6 @@ public void Write(EndianWriter writer) writer.WriteWithLength(AlternateId); writer.Write(TitleId); writer.WriteWithLength(FacadeName); - writer.WriteWithLength(InputData); + writer.WriteWithLength(InputData.Span); } } diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Session/AppControl/LaunchUriRequest.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Session/AppControl/LaunchUriRequest.cs index 64d69aad..8ccb896d 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Session/AppControl/LaunchUriRequest.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Session/AppControl/LaunchUriRequest.cs @@ -1,8 +1,8 @@ namespace ShortDev.Microsoft.ConnectedDevices.Messages.Session.AppControl; -public sealed class LaunchUriRequest : ICdpPayload +public readonly record struct LaunchUriRequest() : IBinaryWritable, IBinaryParsable { - public static LaunchUriRequest Parse(ref EndianReader reader) + public static LaunchUriRequest Parse(ref TReader reader) where TReader : struct, IEndianReader, allows ref struct => new() { Uri = reader.ReadStringWithLength(), @@ -27,7 +27,7 @@ public static LaunchUriRequest Parse(ref EndianReader reader) ///
public string InputData { get; init; } = string.Empty; - public void Write(EndianWriter writer) + public void Write(ref TWriter writer) where TWriter : struct, IEndianWriter, allows ref struct { writer.WriteWithLength(Uri); writer.Write((short)LaunchLocation); diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Session/AppControl/LaunchUriResult.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Session/AppControl/LaunchUriResult.cs index 716808db..c69f827e 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Session/AppControl/LaunchUriResult.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Session/AppControl/LaunchUriResult.cs @@ -1,8 +1,8 @@ namespace ShortDev.Microsoft.ConnectedDevices.Messages.Session.AppControl; -public sealed class LaunchUriResult : ICdpPayload +public readonly record struct LaunchUriResult : IBinaryWritable, IBinaryParsable { - public static LaunchUriResult Parse(ref EndianReader reader) + public static LaunchUriResult Parse(ref TReader reader) where TReader : struct, IEndianReader, allows ref struct => new() { Result = reader.ReadInt32(), @@ -20,7 +20,7 @@ public static LaunchUriResult Parse(ref EndianReader reader) /// public required int ResponseID { get; init; } - public void Write(EndianWriter writer) + public void Write(ref TWriter writer) where TWriter : struct, IEndianWriter, allows ref struct { writer.Write(Result); writer.Write(ResponseID); diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Session/BinaryMsgHeader.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Session/BinaryMsgHeader.cs index 3816f874..5468216e 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Session/BinaryMsgHeader.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Session/BinaryMsgHeader.cs @@ -4,13 +4,13 @@ /// cdp.dll!cdp::BinaryFragmenter::GetMessageFragments
/// /// -public readonly struct BinaryMsgHeader() : ICdpHeader +public readonly record struct BinaryMsgHeader() : IBinaryWritable, IBinaryParsable { public uint FragmentCount { get; init; } = 1; public uint FragmentIndex { get; init; } = 0; public required uint MessageId { get; init; } - public static BinaryMsgHeader Parse(ref EndianReader reader) + public static BinaryMsgHeader Parse(ref TReader reader) where TReader : struct, IEndianReader, allows ref struct => new() { FragmentCount = reader.ReadUInt32(), @@ -18,7 +18,7 @@ public static BinaryMsgHeader Parse(ref EndianReader reader) MessageId = reader.ReadUInt32(), }; - public void Write(EndianWriter writer) + public void Write(ref TWriter writer) where TWriter : struct, IEndianWriter, allows ref struct { writer.Write(FragmentCount); writer.Write(FragmentIndex); diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Session/DisconnectMessage.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Session/DisconnectMessage.cs index d3246cb1..bf7545b2 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Session/DisconnectMessage.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Session/DisconnectMessage.cs @@ -4,20 +4,20 @@ /// The Disconnect message is an optional message sent by a client or host used to inform the other device to disconnect the connected session.
/// The is sent to identify the session to be disconnected. /// -public sealed class DisconnectMessage : ICdpPayload +public readonly record struct DisconnectMessage : IBinaryParsable { /// /// ID representing the session. /// public required ulong SessionId { get; init; } - public static DisconnectMessage Parse(ref EndianReader reader) + public static DisconnectMessage Parse(ref TReader reader) where TReader : struct, IEndianReader, allows ref struct => new() { SessionId = reader.ReadUInt64() }; - public void Write(EndianWriter writer) + public void Write(ref TWriter writer) where TWriter : struct, IEndianWriter, allows ref struct { writer.Write(SessionId); } diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Serialization/BondWriter.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Serialization/BondWriter.cs deleted file mode 100644 index 1908ebea..00000000 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Serialization/BondWriter.cs +++ /dev/null @@ -1,590 +0,0 @@ -using Bond; -using System.Runtime.CompilerServices; -using System.Text; - -namespace ShortDev.Microsoft.ConnectedDevices.Serialization; -public readonly ref struct CompactBinaryBondWriter(EndianBuffer buffer) -{ - const ushort Magic = 16963; - - readonly ushort version = 1; - readonly EndianWriter output = new(Endianness.LittleEndian, buffer); - - // - // Summary: - // Write protocol magic number and version - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteVersion() - { - output.Write((ushort)16963); - output.Write((ushort)version); - } - - // - // Summary: - // End writing a struct - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteStructEnd() - { - output.Write((byte)0); - } - - // - // Summary: - // End writing a base struct - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteBaseEnd() - { - output.Write((byte)1); - } - - // - // Summary: - // Start writing a field - // - // Parameters: - // type: - // Type of the field - // - // id: - // Identifier of the field - // - // metadata: - // Metadata of the field - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteFieldBegin(BondDataType type, ushort id) - { - if (id <= 5) - { - output.Write((byte)((uint)type | (uint)(id << 5))); - return; - } - - if (id <= 255) - { - output.Write((ushort)((uint)type | (uint)(id << 8) | 0xC0u)); - return; - } - - output.Write((byte)(type | (BondDataType)224)); - output.Write(id); - } - - // - // Summary: - // Start writing a list or set container - // - // Parameters: - // count: - // Number of elements in the container - // - // elementType: - // Type of the elements - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteContainerBegin(int count, BondDataType elementType) - { - if (2 == version && count < 7) - { - output.Write((byte)((uint)elementType | (uint)(count + 1 << 5))); - return; - } - - output.Write((byte)elementType); - IntegerHelper.WriteVarUInt32(output, (uint)count); - } - - // - // Summary: - // Start writing a map container - // - // Parameters: - // count: - // Number of elements in the container - // - // keyType: - // Type of the keys - // - // valueType: - // Type of the values - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteContainerBegin(int count, BondDataType keyType, BondDataType valueType) - { - output.Write((byte)keyType); - output.Write((byte)valueType); - IntegerHelper.WriteVarUInt32(output, (uint)count); - } - - // - // Summary: - // Write array of bytes verbatim - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteBytes(ReadOnlySpan data) - { - output.Write(data); - } - - // - // Summary: - // Write an UInt8 - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteUInt8(byte value) - { - output.Write(value); - } - - // - // Summary: - // Write an UInt16 - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteUInt16(ushort value) - { - IntegerHelper.WriteVarUInt16(output, value); - } - - // - // Summary: - // Write an UInt16 - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteUInt32(uint value) - { - IntegerHelper.WriteVarUInt32(output, value); - } - - // - // Summary: - // Write an UInt64 - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteUInt64(ulong value) - { - IntegerHelper.WriteVarUInt64(output, value); - } - - // - // Summary: - // Write an Int8 - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteInt8(sbyte value) - { - output.Write((byte)value); - } - - // - // Summary: - // Write an Int16 - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteInt16(short value) - { - IntegerHelper.WriteVarUInt16(output, IntegerHelper.EncodeZigzag16(value)); - } - - // - // Summary: - // Write an Int32 - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteInt32(int value) - { - IntegerHelper.WriteVarUInt32(output, IntegerHelper.EncodeZigzag32(value)); - } - - // - // Summary: - // Write an Int64 - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteInt64(long value) - { - IntegerHelper.WriteVarUInt64(output, IntegerHelper.EncodeZigzag64(value)); - } - - // - // Summary: - // Write a float - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteFloat(float value) - { - output.Write(value); - } - - // - // Summary: - // Write a double - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteDouble(double value) - { - output.Write(value); - } - - // - // Summary: - // Write a bool - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteBool(bool value) - { - output.Write((byte)(value ? 1u : 0u)); - } - - // - // Summary: - // Write a UTF-8 string - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteString(string value) - { - if (value.Length == 0) - { - WriteUInt32(0u); - return; - } - - int byteCount = Encoding.UTF8.GetByteCount(value); - WriteUInt32((uint)byteCount); - // output.WriteString(Encoding.UTF8, value, byteCount); - output.Write(Encoding.UTF8.GetBytes(value)); - } - - // - // Summary: - // Write a UTF-16 string - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteWString(string value) - { - if (value.Length == 0) - { - WriteUInt32(0u); - return; - } - - int size = checked(value.Length * 2); - WriteUInt32((uint)value.Length); - // output.WriteString(Encoding.Unicode, value, size); - output.Write(Encoding.Unicode.GetBytes(value)); - } - - internal static class IntegerHelper - { - public const int MaxBytesVarInt16 = 3; - - public const int MaxBytesVarInt32 = 5; - - public const int MaxBytesVarInt64 = 10; - - public static int GetVarUInt16Length(ushort value) - { - if (value < 128) - { - return 1; - } - - if (value < 16384) - { - return 2; - } - - return 3; - } - - public static void WriteVarUInt16(in EndianWriter writer, ushort value) - { - if (value >= 128) - { - writer.Write((byte)(value | 0x80u)); - value >>= 7; - if (value >= 128) - { - writer.Write((byte)(value | 0x80u)); - value >>= 7; - } - } - - writer.Write((byte)value); - } - - public static int GetVarUInt32Length(uint value) - { - if (value < 128) - { - return 1; - } - - if (value < 16384) - { - return 2; - } - - if (value < 2097152) - { - return 3; - } - - if (value < 268435456) - { - return 4; - } - - return 5; - } - - public static void WriteVarUInt32(in EndianWriter writer, uint value) - { - if (value >= 128) - { - writer.Write((byte)(value | 0x80u)); - value >>= 7; - if (value >= 128) - { - writer.Write((byte)(value | 0x80u)); - value >>= 7; - if (value >= 128) - { - writer.Write((byte)(value | 0x80u)); - value >>= 7; - if (value >= 128) - { - writer.Write((byte)(value | 0x80u)); - value >>= 7; - } - } - } - } - - writer.Write((byte)value); - } - - public static int GetVarUInt64Length(ulong value) - { - if (value < 128) - { - return 1; - } - - if (value < 16384) - { - return 2; - } - - if (value < 2097152) - { - return 3; - } - - if (value < 268435456) - { - return 4; - } - - if (value < 34359738368L) - { - return 5; - } - - if (value < 4398046511104L) - { - return 6; - } - - if (value < 562949953421312L) - { - return 7; - } - - if (value < 72057594037927936L) - { - return 8; - } - - if (value < 9223372036854775808uL) - { - return 9; - } - - return 10; - } - - public static void WriteVarUInt64(in EndianWriter writer, ulong value) - { - if (value >= 128) - { - writer.Write((byte)(value | 0x80)); - value >>= 7; - if (value >= 128) - { - writer.Write((byte)(value | 0x80)); - value >>= 7; - if (value >= 128) - { - writer.Write((byte)(value | 0x80)); - value >>= 7; - if (value >= 128) - { - writer.Write((byte)(value | 0x80)); - value >>= 7; - if (value >= 128) - { - writer.Write((byte)(value | 0x80)); - value >>= 7; - if (value >= 128) - { - writer.Write((byte)(value | 0x80)); - value >>= 7; - if (value >= 128) - { - writer.Write((byte)(value | 0x80)); - value >>= 7; - if (value >= 128) - { - writer.Write((byte)(value | 0x80)); - value >>= 7; - if (value >= 128) - { - writer.Write((byte)(value | 0x80)); - value >>= 7; - } - } - } - } - } - } - } - } - } - - writer.Write((byte)value); - } - - public static ushort DecodeVarUInt16(byte[] data, ref int index) - { - int num = index; - uint num2 = data[num++]; - if (128 <= num2) - { - uint num3 = data[num++]; - num2 = (num2 & 0x7Fu) | ((num3 & 0x7F) << 7); - if (128 <= num3) - { - num3 = data[num++]; - num2 |= num3 << 14; - } - } - - index = num; - return (ushort)num2; - } - - public static uint DecodeVarUInt32(byte[] data, ref int index) - { - int num = index; - uint num2 = data[num++]; - if (128 <= num2) - { - uint num3 = data[num++]; - num2 = (num2 & 0x7Fu) | ((num3 & 0x7F) << 7); - if (128 <= num3) - { - num3 = data[num++]; - num2 |= (num3 & 0x7F) << 14; - if (128 <= num3) - { - num3 = data[num++]; - num2 |= (num3 & 0x7F) << 21; - if (128 <= num3) - { - num3 = data[num++]; - num2 |= num3 << 28; - } - } - } - } - - index = num; - return num2; - } - - public static ulong DecodeVarUInt64(byte[] data, ref int index) - { - int num = index; - ulong num2 = data[num++]; - if (128 <= num2) - { - ulong num3 = data[num++]; - num2 = (num2 & 0x7F) | ((num3 & 0x7F) << 7); - if (128 <= num3) - { - num3 = data[num++]; - num2 |= (num3 & 0x7F) << 14; - if (128 <= num3) - { - num3 = data[num++]; - num2 |= (num3 & 0x7F) << 21; - if (128 <= num3) - { - num3 = data[num++]; - num2 |= (num3 & 0x7F) << 28; - if (128 <= num3) - { - num3 = data[num++]; - num2 |= (num3 & 0x7F) << 35; - if (128 <= num3) - { - num3 = data[num++]; - num2 |= (num3 & 0x7F) << 42; - if (128 <= num3) - { - num3 = data[num++]; - num2 |= (num3 & 0x7F) << 49; - if (128 <= num3) - { - num3 = data[num++]; - num2 |= num3 << 56; - if (128 <= num3) - { - num++; - } - } - } - } - } - } - } - } - } - - index = num; - return num2; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ushort EncodeZigzag16(short value) - { - return (ushort)((value << 1) ^ (value >> 15)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint EncodeZigzag32(int value) - { - return (uint)((value << 1) ^ (value >> 31)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong EncodeZigzag64(long value) - { - return (ulong)((value << 1) ^ (value >> 63)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static short DecodeZigzag16(ushort value) - { - return (short)((value >> 1) ^ -(value & 1)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int DecodeZigzag32(uint value) - { - return (int)((value >> 1) ^ (0L - (long)(value & 1))); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long DecodeZigzag64(ulong value) - { - return (long)((value >> 1) ^ (0L - (value & 1))); - } - } -} - diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Serialization/ValueSet.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Serialization/ValueSet.cs index cb35c12f..cc0a3220 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Serialization/ValueSet.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Serialization/ValueSet.cs @@ -1,21 +1,21 @@ using Bond; using Bond.IO.Unsafe; using Bond.Protocols; -using ShortDev.Microsoft.ConnectedDevices.Messages; namespace ShortDev.Microsoft.ConnectedDevices.Serialization; -public partial class ValueSet : ICdpPayload +public partial class ValueSet : IBinaryWritable { - public static ValueSet Parse(ref EndianReader reader) + public static ValueSet Parse(ref HeapEndianReader reader) { // ToDo: We really should not re-allocated here!! - var data = reader.ReadToEnd().ToArray(); + byte[] data = new byte[reader.Stream.Length - reader.Stream.Position]; + reader.ReadBytes(data); CompactBinaryReader bondReader = new(new(data)); return Deserialize.From(bondReader); } - public void Write(EndianWriter writer) + public void Write(ref TWriter writer) where TWriter : struct, IEndianWriter, allows ref struct { OutputBuffer buffer = new(); CompactBinaryWriter bondWriter = new(buffer); diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Session/Channels/ChannelHandler.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Session/Channels/ChannelHandler.cs index 38fef94c..3f6bafe5 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Session/Channels/ChannelHandler.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Session/Channels/ChannelHandler.cs @@ -11,7 +11,7 @@ internal abstract class ChannelHandler(CdpSession session) : IDisposable public CdpSession Session { get; } = session; - public void HandleControl(CdpSocket socket, CommonHeader header, ref EndianReader reader) + public void HandleControl(CdpSocket socket, CommonHeader header, ref HeapEndianReader reader) { var controlHeader = ControlHeader.Parse(ref reader); _logger.ReceivedControlMessage( @@ -23,7 +23,7 @@ public void HandleControl(CdpSocket socket, CommonHeader header, ref EndianReade HandleMessageInternal(socket, header, controlHeader, ref reader); } - protected abstract void HandleMessageInternal(CdpSocket socket, CommonHeader header, ControlHeader controlHeader, ref EndianReader reader); + protected abstract void HandleMessageInternal(CdpSocket socket, CommonHeader header, ControlHeader controlHeader, ref HeapEndianReader reader); #region Registry protected readonly AutoKeyRegistry _channelRegistry = []; diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Session/Channels/ClientChannelHandler.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Session/Channels/ClientChannelHandler.cs index 32be8193..584f4533 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Session/Channels/ClientChannelHandler.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Session/Channels/ClientChannelHandler.cs @@ -5,7 +5,7 @@ namespace ShortDev.Microsoft.ConnectedDevices.Session.Channels; internal sealed class ClientChannelHandler(CdpSession session) : ChannelHandler(session) { - protected override void HandleMessageInternal(CdpSocket socket, CommonHeader header, ControlHeader controlHeader, ref EndianReader reader) + protected override void HandleMessageInternal(CdpSocket socket, CommonHeader header, ControlHeader controlHeader, ref HeapEndianReader reader) { if (controlHeader.MessageType != ControlMessageType.StartChannelResponse) return; @@ -50,22 +50,24 @@ public async Task CreateChannelAsync(string appId, string appName, C ulong SendChannelRequest(CdpSocket socket, string appId, string appName) { - EndianWriter writer = new(Endianness.BigEndian); - new ControlHeader() - { - MessageType = ControlMessageType.StartChannelRequest - }.Write(writer); - new StartChannelRequest() - { - Id = appId, - Name = appName - }.Write(writer); - CommonHeader header = new() { Type = MessageType.Control }; - Session.SendMessage(socket, header, writer, supplyRequestId: true); + Session.SendMessage( + socket, + ref header, + new ControlHeader() + { + MessageType = ControlMessageType.StartChannelRequest + }, + new StartChannelRequest() + { + Id = appId, + Name = appName + }, + supplyRequestId: true + ); return header.RequestID; } diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Session/Channels/HostChannelHandler.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Session/Channels/HostChannelHandler.cs index 2f0e36ad..5b6e263e 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Session/Channels/HostChannelHandler.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Session/Channels/HostChannelHandler.cs @@ -5,7 +5,7 @@ namespace ShortDev.Microsoft.ConnectedDevices.Session.Channels; internal sealed class HostChannelHandler(CdpSession session) : ChannelHandler(session) { - protected override void HandleMessageInternal(CdpSocket socket, CommonHeader header, ControlHeader controlHeader, ref EndianReader reader) + protected override void HandleMessageInternal(CdpSocket socket, CommonHeader header, ControlHeader controlHeader, ref HeapEndianReader reader) { if (controlHeader.MessageType != ControlMessageType.StartChannelRequest) return; @@ -22,18 +22,19 @@ protected override void HandleMessageInternal(CdpSocket socket, CommonHeader hea _channelRegistry.Create(channelId => CdpChannel.CreateServerChannel(this, socket, request, channelId), out var channelId); - EndianWriter writer = new(Endianness.BigEndian); - new ControlHeader() - { - MessageType = ControlMessageType.StartChannelResponse - }.Write(writer); - new StartChannelResponse() - { - Result = ChannelResult.Success, - ChannelId = channelId - }.Write(writer); - header.Flags = 0; - Session.SendMessage(socket, header, writer); + Session.SendMessage( + socket, + ref header, + new ControlHeader() + { + MessageType = ControlMessageType.StartChannelResponse + }, + new StartChannelResponse() + { + Result = ChannelResult.Success, + ChannelId = channelId + } + ); } } diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Session/Connection/ClientConnectHandler.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Session/Connection/ClientConnectHandler.cs index c67571c4..cc3d33b8 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Session/Connection/ClientConnectHandler.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Session/Connection/ClientConnectHandler.cs @@ -33,30 +33,30 @@ public async Task ConnectAsync(CdpSocket socket, bool upgradeSupported = true, C } }; - EndianWriter writer = new(Endianness.BigEndian); - new ConnectionHeader() - { - ConnectionMode = ConnectionMode.Proximal, - MessageType = ConnectionType.ConnectRequest - }.Write(writer); - var publicKey = _localEncryption.PublicKey; - new ConnectionRequest() - { - CurveType = CurveType.CT_NIST_P256_KDF_SHA512, - HmacSize = Constants.HMacSize, - MessageFragmentSize = MessageFragmenter.DefaultMessageFragmentSize, - Nonce = _localEncryption.Nonce, - PublicKeyX = publicKey.X!, - PublicKeyY = publicKey.Y! - }.Write(writer); - - _session.SendMessage(socket, header, writer); + _session.SendMessage( + socket, + ref header, + new ConnectionHeader() + { + ConnectionMode = ConnectionMode.Proximal, + MessageType = ConnectionType.ConnectRequest + }, + new ConnectionRequest() + { + CurveType = CurveType.CT_NIST_P256_KDF_SHA512, + HmacSize = Constants.HMacSize, + MessageFragmentSize = MessageFragmenter.DefaultMessageFragmentSize, + Nonce = _localEncryption.Nonce, + PublicKeyX = publicKey.X!, + PublicKeyY = publicKey.Y! + } + ); await _promise; } - protected override void HandleMessageInternal(CdpSocket socket, CommonHeader header, ConnectionHeader connectionHeader, ref EndianReader reader) + protected override void HandleMessageInternal(CdpSocket socket, CommonHeader header, ConnectionHeader connectionHeader, ref HeapEndianReader reader) { if (_promise?.CancellationToken.IsCancellationRequested == true) return; @@ -93,7 +93,7 @@ protected override void HandleMessageInternal(CdpSocket socket, CommonHeader hea } } - void HandleConnectResponse(CommonHeader header, ref EndianReader reader, CdpSocket socket) + void HandleConnectResponse(CommonHeader header, ref HeapEndianReader reader, CdpSocket socket) { _session.HostCapabilities = (PeerCapabilities)(header.TryGetHeader(AdditionalHeaderType.PeerCapabilities)?.AsUInt64() ?? 0); @@ -103,22 +103,23 @@ void HandleConnectResponse(CommonHeader header, ref EndianReader reader, CdpSock var secret = _localEncryption.GenerateSharedSecret(_remoteEncryption); _session.Cryptor = new(secret); - EndianWriter writer = new(Endianness.BigEndian); - new ConnectionHeader() - { - ConnectionMode = ConnectionMode.Proximal, - MessageType = ConnectionType.DeviceAuthRequest - }.Write(writer); - AuthenticationPayload.Create( - _session.Platform.DeviceInfo.DeviceCertificate!, // ToDo: User cert - hostNonce: _remoteEncryption!.Nonce, clientNonce: _localEncryption.Nonce - ).Write(writer); - header.Flags = 0; - _session.SendMessage(socket, header, writer); + _session.SendMessage( + socket, + ref header, + new ConnectionHeader() + { + ConnectionMode = ConnectionMode.Proximal, + MessageType = ConnectionType.DeviceAuthRequest + }, + AuthenticationPayload.Create( + _session.Platform.DeviceInfo.DeviceCertificate!, // ToDo: User cert + hostNonce: _remoteEncryption!.Nonce, clientNonce: _localEncryption.Nonce + ) + ); } - void HandleAuthResponse(CommonHeader header, ref EndianReader reader, CdpSocket socket, ConnectionType connectionType) + void HandleAuthResponse(CommonHeader header, ref HeapEndianReader reader, CdpSocket socket, ConnectionType connectionType) { var authRequest = AuthenticationPayload.Parse(ref reader); if (!authRequest.VerifyThumbprint(hostNonce: _remoteEncryption!.Nonce, clientNonce: _localEncryption.Nonce)) @@ -150,15 +151,17 @@ async void PrepareSession(CdpSocket socket) try { - EndianWriter writer = new(Endianness.BigEndian); - new ConnectionHeader() - { - ConnectionMode = ConnectionMode.Proximal, - MessageType = ConnectionType.AuthDoneRequest - }.Write(writer); - header.Flags = 0; - _session.SendMessage(socket, header, writer); + _session.SendMessage( + socket, + ref header, + new ConnectionHeader() + { + ConnectionMode = ConnectionMode.Proximal, + MessageType = ConnectionType.AuthDoneRequest + }, + new EmptyMessage() + ); } catch (Exception ex) { @@ -169,44 +172,42 @@ async void PrepareSession(CdpSocket socket) void HandleDeviceAuthResponse(CdpSocket socket, CommonHeader header) { - EndianWriter writer = new(Endianness.BigEndian); - new ConnectionHeader() - { - ConnectionMode = ConnectionMode.Proximal, - MessageType = ConnectionType.UserDeviceAuthRequest - }.Write(writer); - AuthenticationPayload.Create( - _session.Platform.DeviceInfo.DeviceCertificate!, // ToDo: User cert - hostNonce: _remoteEncryption!.Nonce, clientNonce: _localEncryption.Nonce - ).Write(writer); - header.Flags = 0; - _session.SendMessage(socket, header, writer); + _session.SendMessage( + socket, + ref header, + new ConnectionHeader() + { + ConnectionMode = ConnectionMode.Proximal, + MessageType = ConnectionType.UserDeviceAuthRequest + }, + AuthenticationPayload.Create( + _session.Platform.DeviceInfo.DeviceCertificate!, // ToDo: User cert + hostNonce: _remoteEncryption!.Nonce, clientNonce: _localEncryption.Nonce + ) + ); } - void HandleAuthDoneResponse(CdpSocket socket, ref EndianReader reader) + void HandleAuthDoneResponse(CdpSocket socket, ref HeapEndianReader reader) { var msg = ResultPayload.Parse(ref reader); msg.ThrowOnError(); - EndianWriter writer = new(Endianness.BigEndian); - new ConnectionHeader() - { - ConnectionMode = ConnectionMode.Proximal, - MessageType = ConnectionType.DeviceInfoMessage - }.Write(writer); - new DeviceInfoMessage() - { - DeviceInfo = _session.Platform.GetCdpDeviceInfo() - }.Write(writer); - _session.SendMessage( socket, new CommonHeader() { Type = MessageType.Connect }, - writer + new ConnectionHeader() + { + ConnectionMode = ConnectionMode.Proximal, + MessageType = ConnectionType.DeviceInfoMessage + }, + new DeviceInfoMessage() + { + DeviceInfo = _session.Platform.GetCdpDeviceInfo() + } ); _promise?.TrySetResult(); diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Session/Connection/ConnectHandler.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Session/Connection/ConnectHandler.cs index 11f07edb..2d299e19 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Session/Connection/ConnectHandler.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Session/Connection/ConnectHandler.cs @@ -16,7 +16,7 @@ internal abstract class ConnectHandler(CdpSession session, UpgradeHandler upgrad internal UpgradeHandler UpgradeHandler { get; } = upgradeHandler; - public void HandleConnect(CdpSocket socket, CommonHeader header, ref EndianReader reader) + public void HandleConnect(CdpSocket socket, CommonHeader header, ref HeapEndianReader reader) { ConnectionHeader connectionHeader = ConnectionHeader.Parse(ref reader); _logger.ReceivedConnectMessage( @@ -34,25 +34,27 @@ public void HandleConnect(CdpSocket socket, CommonHeader header, ref EndianReade HandleMessageInternal(socket, header, connectionHeader, ref reader); } - protected abstract void HandleMessageInternal(CdpSocket socket, CommonHeader header, ConnectionHeader connectionHeader, ref EndianReader reader); + protected abstract void HandleMessageInternal(CdpSocket socket, CommonHeader header, ConnectionHeader connectionHeader, ref HeapEndianReader reader); public CdpDeviceInfo? DeviceInfo { get; private set; } - protected void HandleDeviceInfoMessage(CommonHeader header, ref EndianReader reader, CdpSocket socket) + protected void HandleDeviceInfoMessage(CommonHeader header, ref HeapEndianReader reader, CdpSocket socket) { var msg = DeviceInfoMessage.Parse(ref reader); _logger.ReceivedDeviceInfo(msg.DeviceInfo); DeviceInfo = msg.DeviceInfo; - EndianWriter writer = new(Endianness.BigEndian); - new ConnectionHeader() - { - ConnectionMode = ConnectionMode.Proximal, - MessageType = ConnectionType.DeviceInfoResponseMessage // Ack - }.Write(writer); - header.Flags = 0; - _session.SendMessage(socket, header, writer); + _session.SendMessage( + socket, + ref header, + new ConnectionHeader() + { + ConnectionMode = ConnectionMode.Proximal, + MessageType = ConnectionType.DeviceInfoResponseMessage // Ack + }, + new EmptyMessage() + ); } public static ConnectHandler Create(CdpSession session, EndpointInfo initialEndpoint) diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Session/Connection/HostConnectHandler.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Session/Connection/HostConnectHandler.cs index a2106624..e0939a7b 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Session/Connection/HostConnectHandler.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Session/Connection/HostConnectHandler.cs @@ -9,7 +9,7 @@ namespace ShortDev.Microsoft.ConnectedDevices.Session.Connection; internal sealed class HostConnectHandler(CdpSession session, HostUpgradeHandler upgradeHandler) : ConnectHandler(session, upgradeHandler) { - protected override void HandleMessageInternal(CdpSocket socket, CommonHeader header, ConnectionHeader connectionHeader, ref EndianReader reader) + protected override void HandleMessageInternal(CdpSocket socket, CommonHeader header, ConnectionHeader connectionHeader, ref HeapEndianReader reader) { if (connectionHeader.MessageType == ConnectionType.ConnectRequest) { @@ -43,73 +43,75 @@ protected override void HandleMessageInternal(CdpSocket socket, CommonHeader hea } } - void HandleConnectRequest(CommonHeader header, ref EndianReader reader, CdpSocket socket) + void HandleConnectRequest(CommonHeader header, ref HeapEndianReader reader, CdpSocket socket) { _session.ClientCapabilities = (PeerCapabilities)(header.TryGetHeader(AdditionalHeaderType.PeerCapabilities)?.AsUInt64() ?? 0); var connectionRequest = ConnectionRequest.Parse(ref reader); _remoteEncryption = CdpEncryptionInfo.FromRemote(connectionRequest.PublicKeyX, connectionRequest.PublicKeyY, connectionRequest.Nonce, CdpEncryptionParams.Default); - EndianWriter writer = new(Endianness.BigEndian); - new ConnectionHeader() - { - ConnectionMode = ConnectionMode.Proximal, - MessageType = ConnectionType.ConnectResponse - }.Write(writer); - var publicKey = _localEncryption.PublicKey; - new ConnectionResponse() - { - Result = ConnectionResult.Pending, - HmacSize = connectionRequest.HmacSize, - MessageFragmentSize = connectionRequest.MessageFragmentSize, - Nonce = _localEncryption.Nonce, - PublicKeyX = publicKey.X!, - PublicKeyY = publicKey.Y! - }.Write(writer); - - _session.SendMessage(socket, header, writer); + _session.SendMessage( + socket, + ref header, + new ConnectionHeader() + { + ConnectionMode = ConnectionMode.Proximal, + MessageType = ConnectionType.ConnectResponse + }, + new ConnectionResponse() + { + Result = ConnectionResult.Pending, + HmacSize = connectionRequest.HmacSize, + MessageFragmentSize = connectionRequest.MessageFragmentSize, + Nonce = _localEncryption.Nonce, + PublicKeyX = publicKey.X!, + PublicKeyY = publicKey.Y! + } + ); // We have to set cryptor after we send the message because it would be encrypted otherwise var secret = _localEncryption.GenerateSharedSecret(_remoteEncryption); _session.Cryptor = new(secret); } - void HandleAuthRequest(CommonHeader header, ref EndianReader reader, CdpSocket socket, ConnectionType connectionType) + void HandleAuthRequest(CommonHeader header, ref HeapEndianReader reader, CdpSocket socket, ConnectionType connectionType) { var authRequest = AuthenticationPayload.Parse(ref reader); if (!authRequest.VerifyThumbprint(hostNonce: _localEncryption.Nonce, clientNonce: _remoteEncryption!.Nonce)) throw new CdpSecurityException("Invalid thumbprint"); - EndianWriter writer = new(); - new ConnectionHeader() - { - ConnectionMode = ConnectionMode.Proximal, - MessageType = connectionType == ConnectionType.DeviceAuthRequest ? ConnectionType.DeviceAuthResponse : ConnectionType.UserDeviceAuthResponse - }.Write(writer); - AuthenticationPayload.Create( - _session.Platform.DeviceInfo.DeviceCertificate, // ToDo: User cert - hostNonce: _localEncryption.Nonce, clientNonce: _remoteEncryption!.Nonce - ).Write(writer); - header.Flags = 0; - _session.SendMessage(socket, header, writer); + _session.SendMessage( + socket, + ref header, + new ConnectionHeader() + { + ConnectionMode = ConnectionMode.Proximal, + MessageType = connectionType == ConnectionType.DeviceAuthRequest ? ConnectionType.DeviceAuthResponse : ConnectionType.UserDeviceAuthResponse + }, + AuthenticationPayload.Create( + _session.Platform.DeviceInfo.DeviceCertificate, // ToDo: User cert + hostNonce: _localEncryption.Nonce, clientNonce: _remoteEncryption!.Nonce + ) + ); } void HandleAuthDoneRequest(CommonHeader header, CdpSocket socket) { - EndianWriter writer = new(Endianness.BigEndian); - new ConnectionHeader() - { - ConnectionMode = ConnectionMode.Proximal, - MessageType = ConnectionType.AuthDoneRespone // Ack - }.Write(writer); - new ResultPayload() - { - Result = CdpResult.Success - }.Write(writer); - header.Flags = 0; - _session.SendMessage(socket, header, writer); + _session.SendMessage( + socket, + ref header, + new ConnectionHeader() + { + ConnectionMode = ConnectionMode.Proximal, + MessageType = ConnectionType.AuthDoneRespone // Ack + }, + new ResultPayload() + { + Result = CdpResult.Success + } + ); } } diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Session/Upgrade/ClientUpgradeHandler.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Session/Upgrade/ClientUpgradeHandler.cs index ce354501..d57ab690 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Session/Upgrade/ClientUpgradeHandler.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Session/Upgrade/ClientUpgradeHandler.cs @@ -11,7 +11,7 @@ internal sealed class ClientUpgradeHandler(CdpSession session, EndpointInfo init { private readonly ILogger _logger = session.Platform.CreateLogger(); - protected override bool TryHandleConnectInternal(CdpSocket socket, ConnectionHeader connectionHeader, ref EndianReader reader) + protected override bool TryHandleConnectInternal(CdpSocket socket, ConnectionHeader connectionHeader, ref HeapEndianReader reader) { if (!IsSocketAllowed(socket)) return false; @@ -49,25 +49,23 @@ public async ValueTask UpgradeAsync(CdpSocket oldSocket) { _logger.SendingUpgradeRequest(_currentUpgrade.Id, UpgradeEndpoints); - CommonHeader header = new() - { - Type = MessageType.Connect - }; - - EndianWriter writer = new(Endianness.BigEndian); - new ConnectionHeader() - { - ConnectionMode = ConnectionMode.Proximal, - MessageType = ConnectionType.UpgradeRequest - }.Write(writer); - - new UpgradeRequest() - { - UpgradeId = _currentUpgrade.Id, - Endpoints = UpgradeEndpoints - }.Write(writer); - - _session.SendMessage(oldSocket, header, writer); + _session.SendMessage( + oldSocket, + new CommonHeader() + { + Type = MessageType.Connect + }, + new ConnectionHeader() + { + ConnectionMode = ConnectionMode.Proximal, + MessageType = ConnectionType.UpgradeRequest + }, + new UpgradeRequest() + { + UpgradeId = _currentUpgrade.Id, + Endpoints = UpgradeEndpoints + } + ); return await _currentUpgrade; } @@ -77,7 +75,7 @@ public async ValueTask UpgradeAsync(CdpSocket oldSocket) } } - void HandleUpgradeResponse(CdpSocket oldSocket, ref EndianReader reader) + void HandleUpgradeResponse(CdpSocket oldSocket, ref HeapEndianReader reader) { if (_currentUpgrade == null) return; @@ -117,21 +115,19 @@ async void FindNewEndpoint() void SendUpgradFinalization(CdpSocket socket) { - EndianWriter writer = new(Endianness.BigEndian); - new ConnectionHeader() - { - ConnectionMode = ConnectionMode.Proximal, - MessageType = ConnectionType.UpgradeFinalization - }.Write(writer); - EndpointMetadata.WriteArray(writer, - [ - EndpointMetadata.Tcp - ]); - - _session.SendMessage(socket, new() - { - Type = MessageType.Connect, - }, writer); + _session.SendMessage( + socket, + new CommonHeader() + { + Type = MessageType.Connect, + }, + new ConnectionHeader() + { + ConnectionMode = ConnectionMode.Proximal, + MessageType = ConnectionType.UpgradeFinalization + }, + new EndpointMetadataArray([EndpointMetadata.Tcp]) + ); } void HandleUpgradeFinalizationResponse() @@ -147,24 +143,25 @@ void HandleUpgradeFinalizationResponse() _allowedAddresses.Add(_currentUpgrade.NewSocket.Endpoint.Address); // Request transport permission for new socket - EndianWriter writer = new(Endianness.BigEndian); - new ConnectionHeader() - { - ConnectionMode = ConnectionMode.Proximal, - MessageType = ConnectionType.TransportRequest - }.Write(writer); - new UpgradeIdPayload() - { - UpgradeId = _currentUpgrade.Id - }.Write(writer); - - _session.SendMessage(_currentUpgrade.NewSocket, new() - { - Type = MessageType.Connect, - }, writer); + _session.SendMessage( + _currentUpgrade.NewSocket, + new CommonHeader() + { + Type = MessageType.Connect, + }, + new ConnectionHeader() + { + ConnectionMode = ConnectionMode.Proximal, + MessageType = ConnectionType.TransportRequest + }, + new UpgradeIdPayload() + { + UpgradeId = _currentUpgrade.Id + } + ); } - void HandleUpgradeFailure(ref EndianReader reader) + void HandleUpgradeFailure(ref HeapEndianReader reader) { var msg = HResultPayload.Parse(ref reader); @@ -173,7 +170,7 @@ void HandleUpgradeFailure(ref EndianReader reader) ); } - void HandleTransportConfirmation(CdpSocket socket, ref EndianReader reader) + void HandleTransportConfirmation(CdpSocket socket, ref HeapEndianReader reader) { var msg = UpgradeIdPayload.Parse(ref reader); diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Session/Upgrade/HostUpgradeHandler.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Session/Upgrade/HostUpgradeHandler.cs index 5d7f15c1..539c0e75 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Session/Upgrade/HostUpgradeHandler.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Session/Upgrade/HostUpgradeHandler.cs @@ -11,7 +11,7 @@ internal sealed class HostUpgradeHandler(CdpSession session, EndpointInfo initia { readonly ILogger _logger = session.Platform.CreateLogger(); - protected override bool TryHandleConnectInternal(CdpSocket socket, ConnectionHeader connectionHeader, ref EndianReader reader) + protected override bool TryHandleConnectInternal(CdpSocket socket, ConnectionHeader connectionHeader, ref HeapEndianReader reader) { // This part needs to be always accessible! // This is used to validate @@ -43,7 +43,7 @@ protected override bool TryHandleConnectInternal(CdpSocket socket, ConnectionHea } readonly SynchronizedList _upgradeIds = []; - void HandleTransportRequest(CdpSocket socket, ref EndianReader reader) + void HandleTransportRequest(CdpSocket socket, ref HeapEndianReader reader) { var msg = UpgradeIdPayload.Parse(ref reader); @@ -64,25 +64,24 @@ void HandleTransportRequest(CdpSocket socket, ref EndianReader reader) allowed ? "succeeded" : "failed" ); - CommonHeader header = new() - { - Type = MessageType.Connect - }; - - EndianWriter writer = new(Endianness.BigEndian); - new ConnectionHeader() - { - ConnectionMode = ConnectionMode.Proximal, - MessageType = allowed ? ConnectionType.TransportConfirmation : ConnectionType.UpgradeFailure - }.Write(writer); - msg.Write(writer); - - _session.SendMessage(socket, header, writer); + _session.SendMessage( + socket, + new CommonHeader() + { + Type = MessageType.Connect + }, + new ConnectionHeader() + { + ConnectionMode = ConnectionMode.Proximal, + MessageType = allowed ? ConnectionType.TransportConfirmation : ConnectionType.UpgradeFailure + }, + msg + ); RemoteEndpoint = socket.Endpoint; } - void HandleUpgradeRequest(CdpSocket socket, ref EndianReader reader) + void HandleUpgradeRequest(CdpSocket socket, ref HeapEndianReader reader) { var msg = UpgradeRequest.Parse(ref reader); _logger.UpgradeRequest( @@ -99,65 +98,66 @@ void HandleUpgradeRequest(CdpSocket socket, ref EndianReader reader) var localIp = networkTransport?.Handler.TryGetLocalIp(); if (networkTransport == null || localIp == null) { - EndianWriter writer = new(Endianness.BigEndian); - new ConnectionHeader() - { - ConnectionMode = ConnectionMode.Proximal, - MessageType = ConnectionType.UpgradeFailure - }.Write(writer); - new HResultPayload() - { - HResult = -1 - }.Write(writer); - - _session.SendMessage(socket, header, writer); - return; + _session.SendMessage( + socket, + header, + new ConnectionHeader() + { + ConnectionMode = ConnectionMode.Proximal, + MessageType = ConnectionType.UpgradeFailure + }, + new HResultPayload() + { + HResult = -1 + } + ); } - - _upgradeIds.Add(msg.UpgradeId); - + else { - EndianWriter writer = new(Endianness.BigEndian); - new ConnectionHeader() - { - ConnectionMode = ConnectionMode.Proximal, - MessageType = ConnectionType.UpgradeResponse - }.Write(writer); - new UpgradeResponse() - { - Endpoints = - [ - EndpointInfo.FromTcp(localIp, networkTransport.TcpPort) - ], - MetaData = - [ - EndpointMetadata.Tcp - ] - }.Write(writer); - - _session.SendMessage(socket, header, writer); + _upgradeIds.Add(msg.UpgradeId); + + _session.SendMessage( + socket, + ref header, + new ConnectionHeader() + { + ConnectionMode = ConnectionMode.Proximal, + MessageType = ConnectionType.UpgradeResponse + }, + new UpgradeResponse() + { + Endpoints = + [ + EndpointInfo.FromTcp(localIp, networkTransport.TcpPort) + ], + MetaData = + [ + EndpointMetadata.Tcp + ] + } + ); } } - void HandleUpgradeFinalization(CdpSocket socket, ref EndianReader reader) + void HandleUpgradeFinalization(CdpSocket socket, ref HeapEndianReader reader) { var msg = EndpointMetadata.ParseArray(ref reader); _logger.UpgradeFinalization( msg.Select((x) => x.Type) ); - CommonHeader header = new() - { - Type = MessageType.Connect - }; - - EndianWriter writer = new(Endianness.BigEndian); - new ConnectionHeader() - { - ConnectionMode = ConnectionMode.Proximal, - MessageType = ConnectionType.UpgradeFinalizationResponse - }.Write(writer); - - _session.SendMessage(socket, header, writer); + _session.SendMessage( + socket, + new CommonHeader() + { + Type = MessageType.Connect + }, + new ConnectionHeader() + { + ConnectionMode = ConnectionMode.Proximal, + MessageType = ConnectionType.UpgradeFinalizationResponse + }, + new EmptyMessage() + ); } } diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Session/Upgrade/UpgradeHandler.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Session/Upgrade/UpgradeHandler.cs index 0436100d..3ec6471b 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Session/Upgrade/UpgradeHandler.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Session/Upgrade/UpgradeHandler.cs @@ -29,8 +29,8 @@ protected set public bool IsUpgradeSupported => (/* ToDo: header131Value & */ _session.ClientCapabilities & _session.HostCapabilities & PeerCapabilities.UpgradeSupport) != 0; - public bool TryHandleConnect(CdpSocket socket, ConnectionHeader connectionHeader, ref EndianReader reader) + public bool TryHandleConnect(CdpSocket socket, ConnectionHeader connectionHeader, ref HeapEndianReader reader) => TryHandleConnectInternal(socket, connectionHeader, ref reader); - protected abstract bool TryHandleConnectInternal(CdpSocket socket, ConnectionHeader connectionHeader, ref EndianReader reader); + protected abstract bool TryHandleConnectInternal(CdpSocket socket, ConnectionHeader connectionHeader, ref HeapEndianReader reader); } diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/ShortDev.Microsoft.ConnectedDevices.csproj b/lib/ShortDev.Microsoft.ConnectedDevices/ShortDev.Microsoft.ConnectedDevices.csproj index 33dc3555..c074be3c 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/ShortDev.Microsoft.ConnectedDevices.csproj +++ b/lib/ShortDev.Microsoft.ConnectedDevices/ShortDev.Microsoft.ConnectedDevices.csproj @@ -11,6 +11,13 @@ + + + + + + +
diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/SpeedMeassure.cs b/lib/ShortDev.Microsoft.ConnectedDevices/SpeedMeassure.cs new file mode 100644 index 00000000..e09e2286 --- /dev/null +++ b/lib/ShortDev.Microsoft.ConnectedDevices/SpeedMeassure.cs @@ -0,0 +1,14 @@ +using System.Diagnostics; + +namespace ShortDev.Microsoft.ConnectedDevices; + +public ref struct SpeedMeassure(uint length) : IDisposable +{ + readonly long _start = Stopwatch.GetTimestamp(); + + public readonly void Dispose() + { + var deltaTime = Stopwatch.GetElapsedTime(_start); + Debug.Print($"Speed: {Math.Round(length / 1_000.0 / deltaTime.TotalSeconds)} kByte/s; dT: {deltaTime}"); + } +} diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Transports/Bluetooth/BLeBeacon.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Transports/Bluetooth/BLeBeacon.cs index 63ffe903..edf45787 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Transports/Bluetooth/BLeBeacon.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Transports/Bluetooth/BLeBeacon.cs @@ -20,15 +20,15 @@ public static bool TryParse(byte[] beaconData, [MaybeNullWhen(false)] out BLeBea if (beaconData == null) return false; - EndianReader reader = new(Endianness.BigEndian, beaconData); + var reader = EndianReader.FromMemory(Endianness.BigEndian, beaconData); - var scenarioType = (ScenarioType)reader.ReadByte(); + var scenarioType = (ScenarioType)reader.ReadUInt8(); if (scenarioType != ScenarioType.Bluetooth) return false; - var deviceType = (DeviceType)reader.ReadByte(); + var deviceType = (DeviceType)reader.ReadUInt8(); - var versionAndFlags = reader.ReadByte(); + var versionAndFlags = reader.ReadUInt8(); if (versionAndFlags >> 5 != 1) return false; // wrong version @@ -37,15 +37,15 @@ public static bool TryParse(byte[] beaconData, [MaybeNullWhen(false)] out BLeBea return false; // wrong flags /* deviceStatus */ - _ = (ExtendedDeviceStatus)reader.ReadByte(); + _ = (ExtendedDeviceStatus)reader.ReadUInt8(); if (flags != BeaconFlags.Public) return false; data = new( deviceType, - new PhysicalAddress(BinaryConvert.ToReversed(reader.ReadBytes(6))), - Encoding.UTF8.GetString(reader.ReadToEnd()) + reader.ReadPhysicalAddress(), + reader.ReadString((int)(reader.Stream.Length - reader.Stream.Position - 1)) ); return true; @@ -53,24 +53,31 @@ public static bool TryParse(byte[] beaconData, [MaybeNullWhen(false)] out BLeBea public byte[] ToArray() { - EndianWriter writer = new(Endianness.LittleEndian); - writer.Write((byte)ScenarioType.Bluetooth); - writer.Write((byte)DeviceType); - - byte versionAndFlags = (byte)BeaconFlags.Public; - versionAndFlags |= Constants.BLeBeaconVersion << 5; - writer.Write(versionAndFlags); - - var deviceStatus = ExtendedDeviceStatus.RemoteSessionsNotHosted | ExtendedDeviceStatus.NearShareAuthPolicyPermissive; - writer.Write((byte)deviceStatus); - - writer.Write(BinaryConvert.ToReversed(MacAddress.GetAddressBytes())); - - // ToDo: Don't crop characters wider that 2 bytes! - ReadOnlySpan deviceNameBuffer = Encoding.UTF8.GetBytes(DeviceName); - var deviceNameLength = Math.Min(deviceNameBuffer.Length, Constants.BLeBeaconDeviceNameMaxByteLength); - writer.Write(deviceNameBuffer[..deviceNameLength]); - - return writer.Buffer.ToArray(); + var writer = EndianWriter.Create(Endianness.LittleEndian, ConnectedDevicesPlatform.MemoryPool); + try + { + writer.Write((byte)ScenarioType.Bluetooth); + writer.Write((byte)DeviceType); + + byte versionAndFlags = (byte)BeaconFlags.Public; + versionAndFlags |= Constants.BLeBeaconVersion << 5; + writer.Write(versionAndFlags); + + var deviceStatus = ExtendedDeviceStatus.RemoteSessionsNotHosted | ExtendedDeviceStatus.NearShareAuthPolicyPermissive; + writer.Write((byte)deviceStatus); + + writer.Write(MacAddress); + + // ToDo: Don't crop characters wider that 2 bytes! + ReadOnlySpan deviceNameBuffer = Encoding.UTF8.GetBytes(DeviceName); + var deviceNameLength = Math.Min(deviceNameBuffer.Length, Constants.BLeBeaconDeviceNameMaxByteLength); + writer.Write(deviceNameBuffer[..deviceNameLength]); + + return writer.Stream.WrittenSpan.ToArray(); + } + finally + { + writer.Dispose(); + } } } diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Transports/CdpSocket.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Transports/CdpSocket.cs index 8ae04a84..15ea2987 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Transports/CdpSocket.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Transports/CdpSocket.cs @@ -16,6 +16,16 @@ public void SendFragment(ReadOnlySpan fragment) OutputStream.Flush(); } + public void SendFragment(ReadOnlySpan header, ReadOnlySpan payload) + { + lock (OutputStream) + { + OutputStream.Write(header); + OutputStream.Write(payload); + OutputStream.Flush(); + } + } + public bool IsClosed { get; private set; } public Action? Close { private get; set; } diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Transports/MessageFragmenter.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Transports/MessageFragmenter.cs index 79400bac..edb27c26 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Transports/MessageFragmenter.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Transports/MessageFragmenter.cs @@ -1,4 +1,5 @@ -using ShortDev.Microsoft.ConnectedDevices.Encryption; +using ShortDev.IO.Input; +using ShortDev.Microsoft.ConnectedDevices.Encryption; using ShortDev.Microsoft.ConnectedDevices.Messages; using System.Diagnostics; @@ -41,11 +42,17 @@ static void SendFragment(this IFragmentSender sender, CommonHeader header, ReadO } else { - EndianWriter writer = new(Endianness.BigEndian); - header.SetPayloadLength(payload.Length); - header.Write(writer); - writer.Write(payload); - sender.SendFragment(writer.Buffer.AsSpan()); + var headerWriter = EndianWriter.Create(Endianness.BigEndian, ConnectedDevicesPlatform.MemoryPool); + try + { + header.SetPayloadLength(payload.Length); + header.Write(ref headerWriter); + sender.SendFragment(headerWriter.Stream.WrittenSpan, payload); + } + finally + { + headerWriter.Dispose(); + } } } } @@ -55,5 +62,11 @@ public interface IFragmentSender /// /// Sends a binary fragment. /// - void SendFragment(ReadOnlySpan fragment); + void SendFragment(ReadOnlySpan message); + + /// + /// Sends a binary fragment. + /// + void SendFragment(ReadOnlySpan header, ReadOnlySpan payload) + => SendFragment([.. header, .. payload]); } diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Transports/Network/NetworkTransport.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Transports/Network/NetworkTransport.cs index 6042757b..50f3ecdc 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Transports/Network/NetworkTransport.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Transports/Network/NetworkTransport.cs @@ -107,7 +107,7 @@ public async Task Advertise(LocalDeviceInfo deviceInfo, CancellationToken cancel DiscoveryMessageReceived -= OnMessage; } - void OnMessage(IPAddress address, DiscoveryHeader header, EndianReader reader) + void OnMessage(IPAddress address, DiscoveryHeader header, ref HeapEndianReader reader) { if (header.Type != DiscoveryType.PresenceRequest) return; @@ -143,7 +143,7 @@ async Task RunPresenceSendLoop() } } - void OnMessage(IPAddress address, DiscoveryHeader header, EndianReader reader) + void OnMessage(IPAddress address, DiscoveryHeader header, ref HeapEndianReader reader) { if (header.Type != DiscoveryType.PresenceResponse) return; @@ -160,7 +160,7 @@ void OnMessage(IPAddress address, DiscoveryHeader header, EndianReader reader) } } - delegate void DiscoveryMessageReceivedHandler(IPAddress address, DiscoveryHeader header, EndianReader reader); + delegate void DiscoveryMessageReceivedHandler(IPAddress address, DiscoveryHeader header, ref HeapEndianReader reader); event DiscoveryMessageReceivedHandler? DiscoveryMessageReceived; bool _isListening; @@ -194,12 +194,12 @@ await Task.Run(async () => void HandleMsg(UdpReceiveResult result) { - EndianReader reader = new(Endianness.BigEndian, result.Buffer); + var reader = EndianReader.FromMemory(Endianness.BigEndian, result.Buffer); if (!CommonHeader.TryParse(ref reader, out var headers, out _) || headers.Type != MessageType.Discovery) return; DiscoveryHeader discoveryHeaders = DiscoveryHeader.Parse(ref reader); - DiscoveryMessageReceived?.Invoke(result.RemoteEndPoint.Address, discoveryHeaders, reader); + DiscoveryMessageReceived?.Invoke(result.RemoteEndPoint.Address, discoveryHeaders, ref reader); } } #endregion @@ -211,14 +211,21 @@ void SendPresenceRequest() Type = MessageType.Discovery, }; - EndianWriter payloadWriter = new(Endianness.BigEndian); - new DiscoveryHeader() + var payloadWriter = EndianWriter.Create(Endianness.BigEndian, ConnectedDevicesPlatform.MemoryPool); + try { - Type = DiscoveryType.PresenceRequest - }.Write(payloadWriter); + new DiscoveryHeader() + { + Type = DiscoveryType.PresenceRequest + }.Write(ref payloadWriter); - new UdpFragmentSender(_udpclient, new IPEndPoint(IPAddress.Broadcast, UdpPort)) - .SendMessage(header, payloadWriter.Buffer.AsSpan()); + new UdpFragmentSender(_udpclient, new IPEndPoint(IPAddress.Broadcast, UdpPort)) + .SendMessage(header, payloadWriter.Stream.WrittenSpan); + } + finally + { + payloadWriter.Dispose(); + } } void SendPresenceResponse(IPAddress device, PresenceResponse response) @@ -228,15 +235,22 @@ void SendPresenceResponse(IPAddress device, PresenceResponse response) Type = MessageType.Discovery }; - EndianWriter payloadWriter = new(Endianness.BigEndian); - new DiscoveryHeader() + var payloadWriter = EndianWriter.Create(Endianness.BigEndian, ConnectedDevicesPlatform.MemoryPool); + try { - Type = DiscoveryType.PresenceResponse - }.Write(payloadWriter); - response.Write(payloadWriter); + new DiscoveryHeader() + { + Type = DiscoveryType.PresenceResponse + }.Write(ref payloadWriter); + response.Write(ref payloadWriter); - new UdpFragmentSender(_udpclient, new IPEndPoint(device, UdpPort)) - .SendMessage(header, payloadWriter.Buffer.AsSpan()); + new UdpFragmentSender(_udpclient, new IPEndPoint(device, UdpPort)) + .SendMessage(header, payloadWriter.Stream.WrittenSpan); + } + finally + { + payloadWriter.Dispose(); + } } sealed class UdpFragmentSender(UdpClient client, IPEndPoint receiver) : IFragmentSender diff --git a/tests/ShortDev.Microsoft.ConnectedDevices.Benchmarks/GlobalUsings.cs b/tests/ShortDev.Microsoft.ConnectedDevices.Benchmarks/GlobalUsings.cs new file mode 100644 index 00000000..baf2d972 --- /dev/null +++ b/tests/ShortDev.Microsoft.ConnectedDevices.Benchmarks/GlobalUsings.cs @@ -0,0 +1 @@ +global using BenchmarkDotNet.Attributes; diff --git a/tests/ShortDev.Microsoft.ConnectedDevices.Benchmarks/Program.cs b/tests/ShortDev.Microsoft.ConnectedDevices.Benchmarks/Program.cs new file mode 100644 index 00000000..05b2cd65 --- /dev/null +++ b/tests/ShortDev.Microsoft.ConnectedDevices.Benchmarks/Program.cs @@ -0,0 +1,5 @@ +using BenchmarkDotNet.Running; +using System.Reflection; + +BenchmarkSwitcher.FromAssembly(Assembly.GetExecutingAssembly()) + .RunAllJoined(args: args); diff --git a/tests/ShortDev.Microsoft.ConnectedDevices.Benchmarks/ShortDev.Microsoft.ConnectedDevices.Benchmarks.csproj b/tests/ShortDev.Microsoft.ConnectedDevices.Benchmarks/ShortDev.Microsoft.ConnectedDevices.Benchmarks.csproj new file mode 100644 index 00000000..aae7a51c --- /dev/null +++ b/tests/ShortDev.Microsoft.ConnectedDevices.Benchmarks/ShortDev.Microsoft.ConnectedDevices.Benchmarks.csproj @@ -0,0 +1,19 @@ + + + + Exe + net9.0 + enable + enable + ShortDev.Microsoft.ConnectedDevices + + + + + + + + + + + diff --git a/tests/ShortDev.Microsoft.ConnectedDevices.Benchmarks/Transports/CryptorBenchmark.cs b/tests/ShortDev.Microsoft.ConnectedDevices.Benchmarks/Transports/CryptorBenchmark.cs new file mode 100644 index 00000000..61d100f4 --- /dev/null +++ b/tests/ShortDev.Microsoft.ConnectedDevices.Benchmarks/Transports/CryptorBenchmark.cs @@ -0,0 +1,36 @@ +using ShortDev.Microsoft.ConnectedDevices.Encryption; +using ShortDev.Microsoft.ConnectedDevices.Messages; +using System.Security.Cryptography; + +namespace ShortDev.Microsoft.ConnectedDevices.Transports; + +public class CryptorBenchmark +{ + readonly CommonHeader _header = new(); + readonly Sender _sender = new(); + readonly CdpCryptor _cryptor = new(RandomNumberGenerator.GetBytes(64)); + + [Params(100, 500, MessageFragmenter.DefaultMessageFragmentSize, 20_000)] + public int BufferSize { get; set; } + + byte[] _buffer = []; + + [GlobalSetup] + public void Setup() + { + _buffer = RandomNumberGenerator.GetBytes(BufferSize); + } + + [Benchmark] + public void Speed() + { + _cryptor.EncryptMessage(_sender, _header, _buffer); + } + + sealed class Sender : IFragmentSender + { + public void SendFragment(ReadOnlySpan message) { } + + public void SendFragment(ReadOnlySpan header, ReadOnlySpan payload) { } + } +} diff --git a/tests/ShortDev.Microsoft.ConnectedDevices.Test/CryptorTest.cs b/tests/ShortDev.Microsoft.ConnectedDevices.Test/CryptorTest.cs index 851dc74f..9381595c 100644 --- a/tests/ShortDev.Microsoft.ConnectedDevices.Test/CryptorTest.cs +++ b/tests/ShortDev.Microsoft.ConnectedDevices.Test/CryptorTest.cs @@ -22,10 +22,10 @@ public void Decrypt_ShouldYieldSameAsEncrypt() cryptor.EncryptMessage(fragmentSender, header, payload); Assert.NotNull(fragmentSender.Fragment); - EndianReader reader = new(Endianness.BigEndian, fragmentSender.Fragment.Value.Span); + var reader = EndianReader.FromSpan(Endianness.BigEndian, fragmentSender.Fragment.Value.Span); header = CommonHeader.Parse(ref reader); - var readerContent = reader.ReadToEnd(); + var readerContent = fragmentSender.Fragment.Value.Span[(int)reader.Stream.Position..]; var encryptedPayload = readerContent[..^Constants.HMacSize]; var hmac = readerContent[^Constants.HMacSize..]; diff --git a/tests/ShortDev.Microsoft.ConnectedDevices.Test/Messages/CommonHeaderTest.cs b/tests/ShortDev.Microsoft.ConnectedDevices.Test/Messages/CommonHeaderTest.cs index 44168726..c53a90a5 100644 --- a/tests/ShortDev.Microsoft.ConnectedDevices.Test/Messages/CommonHeaderTest.cs +++ b/tests/ShortDev.Microsoft.ConnectedDevices.Test/Messages/CommonHeaderTest.cs @@ -1,4 +1,5 @@ using ShortDev.Microsoft.ConnectedDevices.Messages; +using System.Buffers; namespace ShortDev.Microsoft.ConnectedDevices.Test.Messages; @@ -7,36 +8,48 @@ public class CommonHeaderTest [Fact] public void CalcSize_YieldsCorrectResult_WhenNoHeaders() { - EndianWriter writer = new(Endianness.BigEndian); - - CommonHeader header = new(); - header.Write(writer); - var expected = writer.Buffer.Size; + var writer = EndianWriter.Create(Endianness.BigEndian, ArrayPool.Shared); + try + { + CommonHeader header = new(); + header.Write(ref writer); + var expected = writer.Stream.WrittenSpan.Length; - var actual = header.CalcSize(); + var actual = header.CalcSize(); - Assert.Equal(expected, actual); + Assert.Equal(expected, actual); + } + finally + { + writer.Dispose(); + } } [Fact] public void CalcSize_YieldsCorrectResult_WhenWithHeaders() { - EndianWriter writer = new(Endianness.BigEndian); - - CommonHeader header = new() + var writer = EndianWriter.Create(Endianness.BigEndian, ArrayPool.Shared); + try { - Type = MessageType.Connect, - AdditionalHeaders = { + CommonHeader header = new() + { + Type = MessageType.Connect, + AdditionalHeaders = { AdditionalHeader.FromUInt32(AdditionalHeaderType.Header129, 0x70_00_00_03), AdditionalHeader.FromUInt64(AdditionalHeaderType.PeerCapabilities, (ulong)PeerCapabilities.All), AdditionalHeader.FromUInt64(AdditionalHeaderType.Header131, 6u) } - }; - header.Write(writer); - var expected = writer.Buffer.Size; + }; + header.Write(ref writer); + var expected = writer.Stream.WrittenSpan.Length; - var actual = header.CalcSize(); + var actual = header.CalcSize(); - Assert.Equal(expected, actual); + Assert.Equal(expected, actual); + } + finally + { + writer.Dispose(); + } } } diff --git a/tests/ShortDev.Microsoft.ConnectedDevices.Test/SerializationTest.cs b/tests/ShortDev.Microsoft.ConnectedDevices.Test/SerializationTest.cs index e7f91ba5..d8502a1e 100644 --- a/tests/ShortDev.Microsoft.ConnectedDevices.Test/SerializationTest.cs +++ b/tests/ShortDev.Microsoft.ConnectedDevices.Test/SerializationTest.cs @@ -1,7 +1,9 @@ +using ShortDev.IO.Output; using ShortDev.Microsoft.ConnectedDevices.Encryption; using ShortDev.Microsoft.ConnectedDevices.Messages; using ShortDev.Microsoft.ConnectedDevices.NearShare.Messages; using ShortDev.Microsoft.ConnectedDevices.Serialization; +using System.Buffers; namespace ShortDev.Microsoft.ConnectedDevices.Test; @@ -20,9 +22,7 @@ public static IEnumerable> GenerateMsgTypes() foreach (var type in assembly.DefinedTypes) { if ( - IsOk(typeof(ICdpHeader<>), type) || - IsOk(typeof(ICdpPayload<>), type) && - type.Name != "PresenceResponse" + IsOk(typeof(IBinaryWritable), type) && IsOk(typeof(IBinaryParsable<>), type) ) yield return type; } @@ -31,7 +31,9 @@ static bool IsOk(Type TInterface, Type TClass) { foreach (var item in TClass.GetInterfaces()) { - if (item.IsGenericType && item.GetGenericTypeDefinition() == TInterface) + if (item == TInterface) + return true; + else if (item.IsGenericType && item.GetGenericTypeDefinition() == TInterface) return true; } return false; @@ -49,7 +51,7 @@ public void ParseMessage_ShouldYieldSameAsWritten(Type type) genericMethod.Invoke(null, [Endianness.LittleEndian]); genericMethod.Invoke(null, [Endianness.BigEndian]); - static void TestRun(Endianness endianness) where T : ICdpSerializable + static void TestRun(Endianness endianness) where T : IBinaryWritable, IBinaryParsable { Type type = typeof(T); @@ -57,21 +59,35 @@ static void TestRun(Endianness endianness) where T : ICdpSerializable var instance = TestValueGenerator.RandomValue(); // write - 1st pass - EndianWriter writer = new(endianness); - instance.Write(writer); - var writtenMemory1 = writer.Buffer.AsMemory(); - - // parse - EndianReader reader = new(endianness, writtenMemory1.Span); - var parsedObject = T.Parse(ref reader); - - // write - 2nd pass - writer = new(endianness); - parsedObject.Write(writer); - var writtenMemory2 = writer.Buffer.AsMemory(); - - // assert - Assert.True(writtenMemory1.Span.SequenceEqual(writtenMemory2.Span)); + var writer = EndianWriter.Create(endianness, ArrayPool.Shared); + try + { + instance.Write(ref writer); + var writtenMemory1 = writer.Stream.WrittenMemory; + + // parse + var reader = EndianReader.FromSpan(endianness, writtenMemory1.Span); + var parsedObject = T.Parse(ref reader); + + // write - 2nd pass + var writer2 = EndianWriter.Create(endianness, ArrayPool.Shared); + try + { + parsedObject.Write(ref writer2); + var writtenMemory2 = writer2.Stream.WrittenMemory; + + // assert + Assert.True(writtenMemory1.Span.SequenceEqual(writtenMemory2.Span)); + } + finally + { + writer2.Dispose(); + } + } + finally + { + writer.Dispose(); + } } } @@ -84,14 +100,28 @@ public void ValueSet() response.Add("BlobPosition", (ulong)2); response.Add("DataBlob", (List)[42]); - EndianWriter writer1 = new(Endianness.BigEndian); - response.Write(writer1); + var writer1 = EndianWriter.Create(Endianness.BigEndian, ArrayPool.Shared); + try + { + response.Write(ref writer1); - EndianWriter writer2 = new(Endianness.BigEndian); - FetchDataResponse.Write(writer2, 1, 2, length: 1, out var blob); - blob[0] = 42; + var writer2 = EndianWriter.Create(Endianness.BigEndian, ArrayPool.Shared); + try + { + FetchDataResponse.Write(ref writer2, 1, 2, length: 1, out var blob); + blob[0] = 42; - Assert.Equal(1, blob.Length); - Assert.Equal(writer1.Buffer.ToArray(), writer2.Buffer.ToArray()); + Assert.Equal(1, blob.Length); + Assert.Equal(writer1.Stream.WrittenMemory, writer2.Stream.WrittenMemory); + } + finally + { + writer2.Dispose(); + } + } + finally + { + writer1.Dispose(); + } } } \ No newline at end of file diff --git a/tests/ShortDev.Microsoft.ConnectedDevices.Test/ShortDev.Microsoft.ConnectedDevices.Test.csproj b/tests/ShortDev.Microsoft.ConnectedDevices.Test/ShortDev.Microsoft.ConnectedDevices.Test.csproj index 93dc3f19..00c0b1b0 100644 --- a/tests/ShortDev.Microsoft.ConnectedDevices.Test/ShortDev.Microsoft.ConnectedDevices.Test.csproj +++ b/tests/ShortDev.Microsoft.ConnectedDevices.Test/ShortDev.Microsoft.ConnectedDevices.Test.csproj @@ -24,4 +24,10 @@ + + + + + +