Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions Directory.Build.targets
Original file line number Diff line number Diff line change
@@ -1,14 +1,4 @@
<Project>
<ItemGroup Label="Global Dependencies">
<PackageReference Include="ShortDev.IO" Version="0.1.4" />
</ItemGroup>

<ItemGroup Label="Global Usings">
<Using Include="ShortDev.IO" />
<Using Include="ShortDev.IO.Input" />
<Using Include="ShortDev.IO.Output" />
</ItemGroup>

<ItemGroup Label="Tests">
<InternalsVisibleTo Include="ShortDev.Microsoft.ConnectedDevices.Test" />
</ItemGroup>
Expand Down
1 change: 1 addition & 0 deletions NearShare.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<Project Path="lib/ShortDev.Microsoft.ConnectedDevices/ShortDev.Microsoft.ConnectedDevices.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/ShortDev.Microsoft.ConnectedDevices.Benchmarks/ShortDev.Microsoft.ConnectedDevices.Benchmarks.csproj" />
<Project Path="tests/ShortDev.Microsoft.ConnectedDevices.Test/ShortDev.Microsoft.ConnectedDevices.Test.csproj" />
</Folder>
<Folder Name="/utils/">
Expand Down
Original file line number Diff line number Diff line change
@@ -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<byte> blob)
public static void Write<TWriter>(ref TWriter writer, uint contentId, ulong start, int length, out Span<byte> blob) where TWriter : struct, IEndianWriter, allows ref struct
{
CompactBinaryBondWriter bondWriter = new(writer.Buffer);
CompactBinaryWriter<TWriter> 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<TWriter>(ref CompactBinaryWriter<TWriter> 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);
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -186,9 +187,9 @@ void HandleDataRequest(BinaryMsgHeader header, ValueSet payload)
var length = payload.Get<uint>("BlobSize");

var fileProvider = _files?[(int)contentId] ?? throw new NullReferenceException("Could not access files to transfer");
Channel.SendBinaryMessage(writer =>
Channel.SendBinaryMessage((ref EndianWriter<HeapOutputStream> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,10 @@
<ProjectReference Include="..\ShortDev.Microsoft.ConnectedDevices\ShortDev.Microsoft.ConnectedDevices.csproj" />
</ItemGroup>

<ItemGroup Label="Global Usings">
<Using Include="ShortDev.IO" />
<Using Include="ShortDev.IO.Input" />
<Using Include="ShortDev.IO.Output" />
</ItemGroup>

</Project>
26 changes: 16 additions & 10 deletions lib/ShortDev.Microsoft.ConnectedDevices/CdpChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -49,28 +50,33 @@
/// </summary>
public ulong ChannelId { get; }

public void SendBinaryMessage(BodyCallback bodyCallback, uint msgId, List<AdditionalHeader>? headers = null)
public void SendBinaryMessage(BodyCallback bodyCallback, uint msgId)
{
CommonHeader header = new()
{
Type = MessageType.Session,
ChannelId = ChannelId
};

if (headers != null)
header.AdditionalHeaders = headers;

EndianWriter writer = new(Endianness.BigEndian);
new BinaryMsgHeader()
var writer = EndianWriter.Create(Endianness.BigEndian, ConnectedDevicesPlatform.MemoryPool);
try
{
MessageId = msgId
}.Write(writer);
bodyCallback(writer);
new BinaryMsgHeader()
{
MessageId = msgId
}.Write(ref writer);
bodyCallback(ref writer);

Session.SendMessage(Socket, header, writer);
using SpeedMeassure speedMeassure = new((uint)writer.Stream.WrittenSpan.Length);
Session.SendMessage(Socket, header, writer.Stream.WrittenSpan);
}
finally
{
writer.Dispose();
}
}

void IDisposable.Dispose()

Check warning on line 79 in lib/ShortDev.Microsoft.ConnectedDevices/CdpChannel.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Rename 'CdpChannel.System.IDisposable.Dispose' to 'Dispose' and ensure that it is declared as public and sealed (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1063)

Check warning on line 79 in lib/ShortDev.Microsoft.ConnectedDevices/CdpChannel.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Ensure that 'CdpChannel.System.IDisposable.Dispose' is declared as public and sealed (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1063)
=> Dispose();

public void Dispose(bool closeSession = false, bool closeSocket = false)
Expand Down
43 changes: 36 additions & 7 deletions lib/ShortDev.Microsoft.ConnectedDevices/CdpSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using ShortDev.Microsoft.ConnectedDevices.Session.Connection;
using ShortDev.Microsoft.ConnectedDevices.Transports;
using System.Collections.Concurrent;
using System.ComponentModel;

namespace ShortDev.Microsoft.ConnectedDevices;

Expand Down Expand Up @@ -92,12 +93,40 @@ internal static async Task<CdpSession> 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<TMessageHeader, TMessage>(
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<TMessageHeader, TMessage>(
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<byte> payload, bool supplyRequestId = false)
{
if (header.Type == MessageType.Session && Cryptor == null)
Expand All @@ -121,11 +150,11 @@ public void SendMessage(CdpSocket socket, CommonHeader header, ReadOnlySpan<byte

#region HandleMessages
bool _connectionEstablished = false;
internal void HandleMessage(CdpSocket socket, CommonHeader header, ref EndianReader reader)
internal void HandleMessage(CdpSocket socket, CommonHeader header, ref HeapEndianReader reader)
{
ThrowIfDisposed();

Cryptor?.Read(ref reader, header);
using var disposeToken = Cryptor?.Read(ref reader, header) ?? default;
header.CorrectClientSessionBit();

if (header.Type == MessageType.Connect)
Expand Down Expand Up @@ -158,10 +187,10 @@ internal void HandleMessage(CdpSocket socket, CommonHeader header, ref EndianRea
}

readonly ConcurrentDictionary<uint, CdpMessage> _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)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -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<byte> _messagePool = ArrayPool<byte>.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<StreamWrapperStream> streamReader)
{
do
{
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<byte> MemoryPool { get; } = ArrayPool<byte>.Create();
}
19 changes: 19 additions & 0 deletions lib/ShortDev.Microsoft.ConnectedDevices/DeviceIdHash.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Runtime.CompilerServices;

namespace ShortDev.Microsoft.ConnectedDevices;

[InlineArray(32)]
public struct DeviceIdHash : IBinaryWritable, IBinaryParsable<DeviceIdHash>
{
public byte _element0;

public readonly void Write<TWriter>(ref TWriter writer) where TWriter : struct, IEndianWriter, allows ref struct
=> writer.Write((ReadOnlySpan<byte>)this);

public static DeviceIdHash Parse<TReader>(ref TReader reader) where TReader : struct, IEndianReader, allows ref struct
{
DeviceIdHash result = default;
reader.ReadBytes(result);
return result;
}
}
Loading
Loading