diff --git a/Directory.Build.targets b/Directory.Build.targets
index 66fbd114..b191f782 100644
--- a/Directory.Build.targets
+++ b/Directory.Build.targets
@@ -1,6 +1,6 @@
-
+
@@ -8,4 +8,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/lib/ShortDev.Microsoft.ConnectedDevices.NearShare/CdpFileProvider.cs b/lib/ShortDev.Microsoft.ConnectedDevices.NearShare/CdpFileProvider.cs
index 3386a6cb..dc9c18a3 100644
--- a/lib/ShortDev.Microsoft.ConnectedDevices.NearShare/CdpFileProvider.cs
+++ b/lib/ShortDev.Microsoft.ConnectedDevices.NearShare/CdpFileProvider.cs
@@ -1,5 +1,4 @@
-using System.IO;
-using System.Text;
+using System.Text;
namespace ShortDev.Microsoft.ConnectedDevices.NearShare;
@@ -43,12 +42,10 @@ public static CdpFileProvider FromStream(string fileName, Stream stream)
public ulong FileSize
=> (ulong)_buffer.Length;
- public ReadOnlySpan ReadBlob(ulong start, uint length)
+ public void ReadBlob(ulong start, Span buffer)
{
- Span buffer = new byte[length];
_buffer.Position = (long)start;
_buffer.Read(buffer);
- return buffer;
}
public void Dispose()
diff --git a/lib/ShortDev.Microsoft.ConnectedDevices.NearShare/Messages/FetchDataResponse.cs b/lib/ShortDev.Microsoft.ConnectedDevices.NearShare/Messages/FetchDataResponse.cs
new file mode 100644
index 00000000..e03b1270
--- /dev/null
+++ b/lib/ShortDev.Microsoft.ConnectedDevices.NearShare/Messages/FetchDataResponse.cs
@@ -0,0 +1,47 @@
+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)
+ {
+ CompactBinaryBondWriter bondWriter = new(writer.Buffer);
+
+ bondWriter.WriteFieldBegin(Bond.BondDataType.BT_MAP, 1);
+ bondWriter.WriteContainerBegin(count: 4, Bond.BondDataType.BT_WSTRING, Bond.BondDataType.BT_STRUCT);
+
+ WritePropertyBegin(ref bondWriter, "ControlMessage", PropertyType.PropertyType_UInt32);
+ bondWriter.WriteFieldBegin(Bond.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.WriteUInt32(contentId);
+ bondWriter.WriteStructEnd();
+
+ WritePropertyBegin(ref bondWriter, "BlobPosition", PropertyType.PropertyType_UInt64);
+ bondWriter.WriteFieldBegin(Bond.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);
+
+ blob = writer.Buffer.GetSpan(length)[..length];
+ writer.Buffer.Advance(length);
+
+ bondWriter.WriteStructEnd();
+
+ bondWriter.WriteStructEnd();
+ }
+
+ static void WritePropertyBegin(ref CompactBinaryBondWriter writer, string name, PropertyType type)
+ {
+ writer.WriteWString(name);
+
+ writer.WriteFieldBegin(Bond.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 ab301968..05f30746 100644
--- a/lib/ShortDev.Microsoft.ConnectedDevices.NearShare/NearShareSender.cs
+++ b/lib/ShortDev.Microsoft.ConnectedDevices.NearShare/NearShareSender.cs
@@ -4,6 +4,8 @@
using ShortDev.Microsoft.ConnectedDevices.NearShare.Apps;
using ShortDev.Microsoft.ConnectedDevices.NearShare.Messages;
using ShortDev.Microsoft.ConnectedDevices.Serialization;
+using System.Buffers;
+using System.Diagnostics;
namespace ShortDev.Microsoft.ConnectedDevices.NearShare;
@@ -176,7 +178,13 @@ 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");
- var blob = fileProvider.ReadBlob(start, length);
+ Channel.SendBinaryMessage(writer =>
+ {
+ FetchDataResponse.Write(writer, contentId, start, (int)length, out var blob);
+ Debug.Assert(blob.Length == length);
+
+ fileProvider.ReadBlob(start, blob);
+ }, header.MessageId);
_fileProgress?.Report(new()
{
@@ -184,13 +192,6 @@ void HandleDataRequest(BinaryMsgHeader header, ValueSet payload)
TotalBytes = _bytesToSend,
TotalFiles = (uint)_files.Count
});
-
- ValueSet response = new();
- response.Add("ControlMessage", (uint)NearShareControlMsgType.FetchDataResponse);
- response.Add("ContentId", contentId);
- response.Add("BlobPosition", start);
- response.Add("DataBlob", blob.ToArray().ToList()); // ToDo: Remove allocation
- SendValueSet(response, header.MessageId);
}
}
}
diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Encryption/CdpCryptor.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Encryption/CdpCryptor.cs
index bb269928..e52df167 100644
--- a/lib/ShortDev.Microsoft.ConnectedDevices/Encryption/CdpCryptor.cs
+++ b/lib/ShortDev.Microsoft.ConnectedDevices/Encryption/CdpCryptor.cs
@@ -1,5 +1,8 @@
using ShortDev.Microsoft.ConnectedDevices.Exceptions;
using ShortDev.Microsoft.ConnectedDevices.Messages;
+using ShortDev.Microsoft.ConnectedDevices.Transports;
+using System.Buffers;
+using System.Buffers.Binary;
using System.Diagnostics;
using System.Security.Cryptography;
@@ -27,16 +30,13 @@ void GenerateIV(CommonHeader header, Span destination)
{
Debug.Assert(destination.Length == Constants.IVSize);
- var aes = _ivAes;
+ Span raw = stackalloc byte[Constants.IVSize];
+ BinaryPrimitives.WriteUInt64BigEndian(raw[..8], header.SessionId);
+ BinaryPrimitives.WriteUInt32BigEndian(raw[8..12], header.SequenceNumber);
+ BinaryPrimitives.WriteUInt16BigEndian(raw[12..14], header.FragmentIndex);
+ BinaryPrimitives.WriteUInt16BigEndian(raw[14..16], header.FragmentCount);
- EndianWriter writer = new(Endianness.BigEndian, Constants.IVSize);
-
- writer.Write(header.SessionId);
- writer.Write(header.SequenceNumber);
- writer.Write(header.FragmentIndex);
- writer.Write(header.FragmentCount);
-
- int bytesWritten = aes.EncryptCbc(writer.Buffer.AsSpan(), _ivData, destination, PaddingMode.None);
+ int bytesWritten = _ivAes.EncryptCbc(raw, _ivData, destination, PaddingMode.None);
Debug.Assert(bytesWritten == destination.Length);
}
@@ -86,36 +86,53 @@ void VerifyHMac(CommonHeader header, ReadOnlySpan payload, ReadOnlySpan payloadBuffer)
+ public void EncryptMessage(IFragmentSender sender, CommonHeader header, ReadOnlySpan payloadBuffer)
{
- EndianWriter msgWriter = new(Endianness.BigEndian);
+ // Prepend payload with length
+ ReadOnlySpan finalPayload;
+ {
+ EndianWriter payloadWriter = new(Endianness.BigEndian);
+ payloadWriter.Write((uint)payloadBuffer.Length);
+ payloadWriter.Write(payloadBuffer);
- Span iv = stackalloc byte[Constants.IVSize];
- GenerateIV(header, iv);
+ finalPayload = payloadWriter.Buffer.AsSpan();
+ }
+
+ // Encrypt
+ var msgWriter = Encrypt(header, finalPayload);
- EndianWriter payloadWriter = new(Endianness.BigEndian);
- payloadWriter.Write((uint)payloadBuffer.Length);
- payloadWriter.Write(payloadBuffer);
+ // HMAC
+ {
+ var msgBuffer = msgWriter.Buffer.AsWriteableSpan();
+ Span hmac = stackalloc byte[Constants.HMacSize];
+ ComputeHmac(msgBuffer, hmac);
+ CommonHeader.ModifyMessageLength(msgBuffer, +Constants.HMacSize);
+ msgWriter.Write(hmac);
+ }
- var buffer = payloadWriter.Buffer.AsSpan();
+ sender.SendFragment(msgWriter.Buffer.AsSpan());
+ }
+
+ EndianWriter Encrypt(CommonHeader header, ReadOnlySpan buffer)
+ {
// 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 encryptedPayload = _aes.EncryptCbc(buffer, iv, paddingMode);
+ var encryptedPayloadLength = _aes.GetCiphertextLengthCbc(buffer.Length, paddingMode);
+ // Write header
+ EndianWriter writer = new(Endianness.BigEndian);
header.Flags |= MessageFlags.SessionEncrypted | MessageFlags.HasHMAC;
- header.SetPayloadLength(encryptedPayload.Length);
- header.Write(msgWriter);
-
- msgWriter.Write(encryptedPayload);
+ header.SetPayloadLength(encryptedPayloadLength);
+ header.Write(writer);
- var msgBuffer = msgWriter.Buffer.AsWriteableSpan();
+ Span iv = stackalloc byte[Constants.IVSize];
+ GenerateIV(header, iv);
- Span hmac = stackalloc byte[Constants.HMacSize];
- ComputeHmac(msgBuffer, hmac);
- CommonHeader.ModifyMessageLength(msgBuffer, +Constants.HMacSize);
+ // Encrypt and write to msgWriter
+ _aes.EncryptCbc(buffer, iv, writer.Buffer.GetSpan(encryptedPayloadLength), paddingMode);
+ writer.Buffer.Advance(encryptedPayloadLength);
- writer.Write(msgBuffer);
- writer.Write(hmac);
+ return writer;
}
public void Read(ref EndianReader reader, CommonHeader header)
diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Extensions.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Extensions.cs
index 9038d37b..aee17b87 100644
--- a/lib/ShortDev.Microsoft.ConnectedDevices/Extensions.cs
+++ b/lib/ShortDev.Microsoft.ConnectedDevices/Extensions.cs
@@ -69,6 +69,8 @@ public readonly struct ArrayPoolToken(ArrayPool pool, int capacity) : IDis
private readonly int _capacity = capacity;
private readonly T[] _array = pool.Rent(capacity);
+ public T[] ArrayUnsafe => _array;
+
public Memory Memory => _array.AsMemory()[0.._capacity];
public Span Span => Memory.Span;
diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Session/BinaryMsgHeader.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Session/BinaryMsgHeader.cs
index cbee6a4e..3816f874 100644
--- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Session/BinaryMsgHeader.cs
+++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Session/BinaryMsgHeader.cs
@@ -4,11 +4,11 @@
/// cdp.dll!cdp::BinaryFragmenter::GetMessageFragments
///
///
-public sealed class BinaryMsgHeader : ICdpHeader
+public readonly struct BinaryMsgHeader() : ICdpHeader
{
- public uint FragmentCount { get; set; } = 1;
- public uint FragmentIndex { get; set; } = 0;
- public required uint MessageId { get; set; }
+ 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)
=> new()
diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Serialization/BondWriter.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Serialization/BondWriter.cs
new file mode 100644
index 00000000..1908ebea
--- /dev/null
+++ b/lib/ShortDev.Microsoft.ConnectedDevices/Serialization/BondWriter.cs
@@ -0,0 +1,590 @@
+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/Transports/CdpSocket.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Transports/CdpSocket.cs
index 10b99bc7..8ae04a84 100644
--- a/lib/ShortDev.Microsoft.ConnectedDevices/Transports/CdpSocket.cs
+++ b/lib/ShortDev.Microsoft.ConnectedDevices/Transports/CdpSocket.cs
@@ -12,11 +12,8 @@ public sealed class CdpSocket : IFragmentSender, IDisposable
public void SendFragment(ReadOnlySpan fragment)
{
- lock (OutputStream)
- {
- OutputStream.Write(fragment);
- OutputStream.Flush();
- }
+ OutputStream.Write(fragment);
+ OutputStream.Flush();
}
public bool IsClosed { get; private set; }
diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Transports/MessageFragmenter.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Transports/MessageFragmenter.cs
index 3b7928bb..79400bac 100644
--- a/lib/ShortDev.Microsoft.ConnectedDevices/Transports/MessageFragmenter.cs
+++ b/lib/ShortDev.Microsoft.ConnectedDevices/Transports/MessageFragmenter.cs
@@ -35,19 +35,18 @@ static void SendFragment(this IFragmentSender sender, CommonHeader header, ReadO
{
Debug.Assert(payload.Length <= DefaultMessageFragmentSize);
- EndianWriter writer = new(Endianness.BigEndian);
if (cryptor != null)
{
- cryptor.EncryptMessage(writer, header, payload);
+ cryptor.EncryptMessage(sender, header, payload);
}
else
{
+ EndianWriter writer = new(Endianness.BigEndian);
header.SetPayloadLength(payload.Length);
header.Write(writer);
writer.Write(payload);
+ sender.SendFragment(writer.Buffer.AsSpan());
}
-
- sender.SendFragment(writer.Buffer.AsSpan());
}
}
diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Transports/Network/NetworkTransport.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Transports/Network/NetworkTransport.cs
index 431ff5fd..c0d2a6ca 100644
--- a/lib/ShortDev.Microsoft.ConnectedDevices/Transports/Network/NetworkTransport.cs
+++ b/lib/ShortDev.Microsoft.ConnectedDevices/Transports/Network/NetworkTransport.cs
@@ -29,6 +29,7 @@ public async Task Listen(CancellationToken cancellationToken)
if (client.Client.RemoteEndPoint is not IPEndPoint endPoint)
return;
+ client.NoDelay = true;
var stream = client.GetStream();
DeviceConnected?.Invoke(this, new()
{
@@ -52,6 +53,7 @@ public async Task ConnectAsync(EndpointInfo endpoint)
// ToDo: If the windows machine tries to connect back it uses the port assigned here not 5040!!
TcpClient client = new();
await client.ConnectAsync(endpoint.ToIPEndPoint()).ConfigureAwait(false);
+ client.NoDelay = true;
return new()
{
Endpoint = endpoint,
diff --git a/tests/ShortDev.Microsoft.ConnectedDevices.Test/CryptorTest.cs b/tests/ShortDev.Microsoft.ConnectedDevices.Test/CryptorTest.cs
index f57ddd0e..bdad91b6 100644
--- a/tests/ShortDev.Microsoft.ConnectedDevices.Test/CryptorTest.cs
+++ b/tests/ShortDev.Microsoft.ConnectedDevices.Test/CryptorTest.cs
@@ -1,5 +1,6 @@
using ShortDev.Microsoft.ConnectedDevices.Encryption;
using ShortDev.Microsoft.ConnectedDevices.Messages;
+using ShortDev.Microsoft.ConnectedDevices.Transports;
using System.Security.Cryptography;
namespace ShortDev.Microsoft.ConnectedDevices.Test;
@@ -17,10 +18,11 @@ public void Decrypt_ShouldYieldSameAsEncrypt()
var header = TestValueGenerator.RandomValue();
ReadOnlySpan payload = TestValueGenerator.RandomValue();
- EndianWriter writer = new(Endianness.BigEndian);
- cryptor.EncryptMessage(writer, header, payload);
+ FragmentSenderSpy fragmentSender = new();
+ cryptor.EncryptMessage(fragmentSender, header, payload);
+ Assert.NotNull(fragmentSender.Fragment);
- EndianReader reader = new(Endianness.BigEndian, writer.Buffer.AsSpan());
+ EndianReader reader = new(Endianness.BigEndian, fragmentSender.Fragment.Value.Span);
header = CommonHeader.Parse(ref reader);
var readerContent = reader.ReadToEnd();
@@ -32,4 +34,15 @@ public void Decrypt_ShouldYieldSameAsEncrypt()
Assert.True(payload.SequenceEqual(decrypted[sizeof(uint)..]));
}
+
+ sealed class FragmentSenderSpy : IFragmentSender
+ {
+ public ReadOnlyMemory? Fragment { get; private set; }
+ public void SendFragment(ReadOnlySpan fragment)
+ {
+ Assert.Null(Fragment);
+
+ Fragment = fragment.ToArray();
+ }
+ }
}
diff --git a/tests/ShortDev.Microsoft.ConnectedDevices.Test/SerializationTest.cs b/tests/ShortDev.Microsoft.ConnectedDevices.Test/SerializationTest.cs
index 4318a158..20794147 100644
--- a/tests/ShortDev.Microsoft.ConnectedDevices.Test/SerializationTest.cs
+++ b/tests/ShortDev.Microsoft.ConnectedDevices.Test/SerializationTest.cs
@@ -1,5 +1,8 @@
using ShortDev.Microsoft.ConnectedDevices.Encryption;
using ShortDev.Microsoft.ConnectedDevices.Messages;
+using ShortDev.Microsoft.ConnectedDevices.NearShare.Messages;
+using ShortDev.Microsoft.ConnectedDevices.Serialization;
+using System.Diagnostics;
using Xunit.Abstractions;
namespace ShortDev.Microsoft.ConnectedDevices.Test;
@@ -73,4 +76,24 @@ static void TestRun(Endianness endianness) where T : ICdpSerializable
Assert.True(writtenMemory1.Span.SequenceEqual(writtenMemory2.Span));
}
}
+
+ [Fact]
+ public void ValueSet()
+ {
+ ValueSet response = new();
+ response.Add("ControlMessage", (uint)NearShareControlMsgType.FetchDataResponse);
+ response.Add("ContentId", (uint)1);
+ response.Add("BlobPosition", (ulong)2);
+ response.Add("DataBlob", (List)[42]);
+
+ EndianWriter writer1 = new(Endianness.BigEndian);
+ response.Write(writer1);
+
+ EndianWriter writer2 = new(Endianness.BigEndian);
+ FetchDataResponse.Write(writer2, 1, 2, length: 1, out var blob);
+ blob[0] = 42;
+
+ Assert.Equal(1, blob.Length);
+ Assert.Equal(writer1.Buffer.ToArray(), writer2.Buffer.ToArray());
+ }
}
\ 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 f928ad2f..15344e24 100644
--- a/tests/ShortDev.Microsoft.ConnectedDevices.Test/ShortDev.Microsoft.ConnectedDevices.Test.csproj
+++ b/tests/ShortDev.Microsoft.ConnectedDevices.Test/ShortDev.Microsoft.ConnectedDevices.Test.csproj
@@ -20,6 +20,7 @@
+