Browse Source

update tmds.dbus.protocol

feature/linux-automation
Jumar Macato 2 years ago
parent
commit
ceb4554b0a
  1. 15
      src/Linux/Tmds.DBus.Protocol/Connection.cs
  2. 9
      src/Linux/Tmds.DBus.Protocol/DBusConnection.cs
  3. 9
      src/Linux/Tmds.DBus.Protocol/IMethodHandlerDictionary.cs
  4. 60
      src/Linux/Tmds.DBus.Protocol/MessageWriter.Basic.cs
  5. 9
      src/Linux/Tmds.DBus.Protocol/MessageWriter.cs
  6. 40
      src/Linux/Tmds.DBus.Protocol/Netstandard2_1Extensions.cs
  7. 137
      src/Linux/Tmds.DBus.Protocol/PathNodeDictionary.cs
  8. 144
      src/Linux/Tmds.DBus.Protocol/Polyfill/NullableAttributes.cs
  9. 1
      src/Linux/Tmds.DBus.Protocol/SocketExtensions.cs
  10. 1
      src/Linux/Tmds.DBus.Protocol/Variant.cs

15
src/Linux/Tmds.DBus.Protocol/Connection.cs

@ -249,12 +249,19 @@ public partial class Connection : IDisposable
}
public void AddMethodHandler(IMethodHandler methodHandler)
=> AddMethodHandlers([ methodHandler ]);
=> UpdateMethodHandlers((dictionary, handler) => dictionary.AddMethodHandler(handler), methodHandler);
public void AddMethodHandlers(IReadOnlyList<IMethodHandler> methodHandlers)
{
GetConnection().AddMethodHandlers(methodHandlers);
}
=> UpdateMethodHandlers((dictionary, handlers) => dictionary.AddMethodHandlers(handlers), methodHandlers);
public void RemoveMethodHandler(string path)
=> UpdateMethodHandlers((dictionary, path) => dictionary.RemoveMethodHandler(path), path);
public void RemoveMethodHandlers(IEnumerable<string> paths)
=> UpdateMethodHandlers((dictionary, paths) => dictionary.RemoveMethodHandlers(paths), paths);
private void UpdateMethodHandlers<T>(Action<IMethodHandlerDictionary, T> update, T state)
=> GetConnection().UpdateMethodHandlers(update, state);
private static Connection CreateConnection(ref Connection? field, string? address)
{

9
src/Linux/Tmds.DBus.Protocol/DBusConnection.cs

@ -490,16 +490,11 @@ class DBusConnection : IDisposable
_currentSynchronizationContext = null;
}
public void AddMethodHandlers(IReadOnlyList<IMethodHandler> methodHandlers)
public void UpdateMethodHandlers<T>(Action<IMethodHandlerDictionary, T> update, T state)
{
lock (_gate)
{
if (_state == ConnectionState.Disconnected)
{
return;
}
_pathNodes.AddMethodHandlers(methodHandlers);
update(_pathNodes, state);
}
}

9
src/Linux/Tmds.DBus.Protocol/IMethodHandlerDictionary.cs

@ -0,0 +1,9 @@
namespace Tmds.DBus.Protocol;
interface IMethodHandlerDictionary
{
void AddMethodHandlers(IReadOnlyList<IMethodHandler> methodHandlers);
void AddMethodHandler(IMethodHandler methodHandler);
void RemoveMethodHandler(string path);
void RemoveMethodHandlers(IEnumerable<string> paths);
}

60
src/Linux/Tmds.DBus.Protocol/MessageWriter.Basic.cs

@ -2,6 +2,8 @@ namespace Tmds.DBus.Protocol;
public ref partial struct MessageWriter
{
private const int MaxSizeHint = 4096;
public void WriteBool(bool value) => WriteUInt32(value ? 1u : 0u);
public void WriteByte(byte value) => WritePrimitiveCore<byte>(value, DBusType.Byte);
@ -170,33 +172,63 @@ public ref partial struct MessageWriter
private int WriteRaw(ReadOnlySpan<byte> data)
{
int length = data.Length;
var dst = GetSpan(length);
data.CopyTo(dst);
Advance(length);
return length;
int totalLength = data.Length;
if (totalLength <= MaxSizeHint)
{
var dst = GetSpan(totalLength);
data.CopyTo(dst);
Advance(totalLength);
return totalLength;
}
else
{
while (!data.IsEmpty)
{
var dst = GetSpan(1);
int length = Math.Min(data.Length, dst.Length);
data.Slice(0, length).CopyTo(dst);
Advance(length);
data = data.Slice(length);
}
return totalLength;
}
}
private int WriteRaw(string data)
{
#if NETSTANDARD2_1_OR_GREATER || NET
// To use the IBufferWriter we need to flush the Span.
// Avoid it when we're writing small strings.
if (data.Length <= 2048)
const int MaxUtf8BytesPerChar = 3;
if (data.Length <= MaxSizeHint / MaxUtf8BytesPerChar)
{
ReadOnlySpan<char> chars = data.AsSpan();
int byteCount = Encoding.UTF8.GetByteCount(chars);
var dst = GetSpan(byteCount);
byteCount = Encoding.UTF8.GetBytes(data, dst);
byteCount = Encoding.UTF8.GetBytes(data.AsSpan(), dst);
Advance(byteCount);
return byteCount;
}
else
#endif
{
int length = (int)Encoding.UTF8.GetBytes(data.AsSpan(), Writer);
_offset += length;
return length;
ReadOnlySpan<char> chars = data.AsSpan();
Encoder encoder = Encoding.UTF8.GetEncoder();
int totalLength = 0;
do
{
Debug.Assert(!chars.IsEmpty);
var dst = GetSpan(MaxUtf8BytesPerChar);
encoder.Convert(chars, dst, flush: true, out int charsUsed, out int bytesUsed, out bool completed);
Advance(bytesUsed);
totalLength += bytesUsed;
if (completed)
{
return totalLength;
}
chars = chars.Slice(charsUsed);
} while (true);
}
}
}

9
src/Linux/Tmds.DBus.Protocol/MessageWriter.cs

@ -54,15 +54,6 @@ public ref partial struct MessageWriter
return message;
}
private IBufferWriter<byte> Writer
{
get
{
Flush();
return _data;
}
}
internal MessageWriter(MessageBufferPool messagePool, uint serial)
{
_message = messagePool.Rent();

40
src/Linux/Tmds.DBus.Protocol/Netstandard2_1Extensions.cs

@ -25,46 +25,6 @@ static partial class NetstandardExtensions
return null!;
}
public static long GetBytes(this Encoding encoding, ReadOnlySpan<char> chars, IBufferWriter<byte> writer)
{
if (chars.Length <= MaxInputElementsPerIteration)
{
int byteCount = encoding.GetByteCount(chars);
Span<byte> scratchBuffer = writer.GetSpan(byteCount);
int actualBytesWritten = encoding.GetBytes(chars, scratchBuffer);
writer.Advance(actualBytesWritten);
return actualBytesWritten;
}
else
{
Convert(encoding.GetEncoder(), chars, writer, flush: true, out long totalBytesWritten, out bool completed);
return totalBytesWritten;
}
}
public static void Convert(this Encoder encoder, ReadOnlySpan<char> chars, IBufferWriter<byte> writer, bool flush, out long bytesUsed, out bool completed)
{
long totalBytesWritten = 0;
do
{
int byteCountForThisSlice = (chars.Length <= MaxInputElementsPerIteration)
? encoder.GetByteCount(chars, flush)
: encoder.GetByteCount(chars.Slice(0, MaxInputElementsPerIteration), flush: false);
Span<byte> scratchBuffer = writer.GetSpan(byteCountForThisSlice);
encoder.Convert(chars, scratchBuffer, flush, out int charsUsedJustNow, out int bytesWrittenJustNow, out completed);
chars = chars.Slice(charsUsedJustNow);
writer.Advance(bytesWrittenJustNow);
totalBytesWritten += bytesWrittenJustNow;
} while (!chars.IsEmpty);
bytesUsed = totalBytesWritten;
}
public static async Task ConnectAsync(this Socket socket, EndPoint remoteEP, CancellationToken cancellationToken)
{
using var ctr = cancellationToken.Register(state => ((Socket)state!).Dispose(), socket, useSynchronizationContext: false);

137
src/Linux/Tmds.DBus.Protocol/PathNodeDictionary.cs

@ -74,8 +74,19 @@ sealed class PathNode
}
}
sealed class PathNodeDictionary : Dictionary<string, PathNode>
sealed class PathNodeDictionary : IMethodHandlerDictionary
{
private readonly Dictionary<string, PathNode> _dictionary = new();
public bool TryGetValue(string path, [NotNullWhen(true)]out PathNode? pathNode)
=> _dictionary.TryGetValue(path, out pathNode);
// For tests:
public PathNode this[string path]
=> _dictionary[path];
public int Count
=> _dictionary.Count;
public void AddMethodHandlers(IReadOnlyList<IMethodHandler> methodHandlers)
{
if (methodHandlers is null)
@ -89,22 +100,8 @@ sealed class PathNodeDictionary : Dictionary<string, PathNode>
for (int i = 0; i < methodHandlers.Count; i++)
{
IMethodHandler methodHandler = methodHandlers[i] ?? throw new ArgumentNullException("methodHandler");
string path = methodHandler.Path ?? throw new ArgumentNullException(nameof(methodHandler.Path));
// Validate the path starts with '/' and has no empty sections.
// GetParentPath relies on this.
if (path[0] != '/' || path.IndexOf("//", StringComparison.Ordinal) != -1)
{
throw new FormatException($"The path '{path}' is not valid.");
}
PathNode node = GetOrCreateNode(path);
if (node.MethodHandler is not null)
{
throw new InvalidOperationException($"A method handler is already registered for the path '{path}'.");
}
node.MethodHandler = methodHandler;
AddMethodHandler(methodHandler);
registeredCount++;
}
@ -121,7 +118,7 @@ sealed class PathNodeDictionary : Dictionary<string, PathNode>
private PathNode GetOrCreateNode(string path)
{
#if NET6_0_OR_GREATER
ref PathNode? node = ref CollectionsMarshal.GetValueRefOrAddDefault(this, path, out bool exists);
ref PathNode? node = ref CollectionsMarshal.GetValueRefOrAddDefault(_dictionary, path, out bool exists);
if (exists)
{
return node!;
@ -129,12 +126,12 @@ sealed class PathNodeDictionary : Dictionary<string, PathNode>
PathNode newNode = new PathNode();
node = newNode;
#else
if (this.TryGetValue(path, out PathNode? node))
if (_dictionary.TryGetValue(path, out PathNode? node))
{
return node;
}
PathNode newNode = new PathNode();
Add(path, newNode);
_dictionary.Add(path, newNode);
#endif
string? parentPath = GetParentPath(path);
if (parentPath is not null)
@ -178,7 +175,7 @@ sealed class PathNodeDictionary : Dictionary<string, PathNode>
for (int i = 0; i < count; i++)
{
string path = methodHandlers[i].Path;
if (this.Remove(path, out PathNode? node))
if (_dictionary.Remove(path, out PathNode? node))
{
nodes[j++] = (path, node);
node.MethodHandler = null;
@ -206,52 +203,104 @@ sealed class PathNodeDictionary : Dictionary<string, PathNode>
for (int i = 0; i < count; i++)
{
var node = nodes[i];
this[node.Path] = node.Node;
_dictionary[node.Path] = node.Node;
}
}
void RemoveFromParent(string path, PathNode node)
private void RemoveFromParent(string path, PathNode node)
{
PathNode? parent = node.Parent;
if (parent is null)
{
PathNode? parent = node.Parent;
if (parent is null)
return;
}
Debug.Assert(parent.ChildNameCount >= 1, "node is expected to be a known child");
if (parent.ChildNameCount == 1) // We're the only child.
{
if (parent.MethodHandler is not null)
{
return;
// Parent is still needed for the MethodHandler.
parent.ClearChildNames();
}
Debug.Assert(parent.ChildNameCount >= 1, "node is expected to be a known child");
if (parent.ChildNameCount == 1) // We're the only child.
else
{
if (parent.MethodHandler is not null)
{
// Parent is still needed for the MethodHandler.
parent.ClearChildNames();
}
else
{
// Suppress netstandard2.0 nullability warnings around NetstandardExtensions.Remove.
#if NETSTANDARD2_0
#pragma warning disable CS8620
#pragma warning disable CS8604
#endif
// Parent is no longer needed.
string parentPath = GetParentPath(path)!;
Debug.Assert(parentPath is not null);
this.Remove(parentPath, out PathNode? parentNode);
Debug.Assert(parentNode is not null);
RemoveFromParent(parentPath, parentNode);
// Parent is no longer needed.
string parentPath = GetParentPath(path)!;
Debug.Assert(parentPath is not null);
_dictionary.Remove(parentPath, out PathNode? parentNode);
Debug.Assert(parentNode is not null);
RemoveFromParent(parentPath, parentNode);
#if NETSTANDARD2_0
#pragma warning restore CS8620
#pragma warning restore CS8604
#endif
}
}
}
else
{
string childName = GetChildName(path);
parent.RemoveChildName(childName);
}
}
public void AddMethodHandler(IMethodHandler methodHandler)
{
string path = methodHandler.Path ?? throw new ArgumentNullException(nameof(methodHandler.Path));
// Validate the path starts with '/' and has no empty sections.
// GetParentPath relies on this.
if (path[0] != '/' || path.IndexOf("//", StringComparison.Ordinal) != -1)
{
throw new FormatException($"The path '{path}' is not valid.");
}
PathNode node = GetOrCreateNode(path);
if (node.MethodHandler is not null)
{
throw new InvalidOperationException($"A method handler is already registered for the path '{path}'.");
}
node.MethodHandler = methodHandler;
}
public void RemoveMethodHandler(string path)
{
if (path is null)
{
throw new ArgumentNullException(nameof(path));
}
if (_dictionary.Remove(path, out PathNode? node))
{
if (node.ChildNameCount > 0)
{
// Node is still needed for its children.
node.MethodHandler = null;
_dictionary.Add(path, node);
}
else
{
string childName = GetChildName(path);
parent.RemoveChildName(childName);
RemoveFromParent(path, node);
}
}
}
public void RemoveMethodHandlers(IEnumerable<string> paths)
{
if (paths is null)
{
throw new ArgumentNullException(nameof(paths));
}
foreach (var path in paths)
{
RemoveMethodHandler(path);
}
}
private static readonly RemoveKeyComparer RemoveKeyComparerInstance = new();
sealed class RemoveKeyComparer : IComparer<(string Path, PathNode Node)>

144
src/Linux/Tmds.DBus.Protocol/Polyfill/NullableAttributes.cs

@ -0,0 +1,144 @@
namespace System.Diagnostics.CodeAnalysis
{
#if NETSTANDARD2_0
/// <summary>Specifies that null is allowed as an input even if the corresponding type disallows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)]
internal sealed class AllowNullAttribute : Attribute { }
/// <summary>Specifies that null is disallowed as an input even if the corresponding type allows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)]
internal sealed class DisallowNullAttribute : Attribute { }
/// <summary>Specifies that an output may be null even if the corresponding type disallows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
internal sealed class MaybeNullAttribute : Attribute { }
/// <summary>Specifies that an output will not be null even if the corresponding type allows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
internal sealed class NotNullAttribute : Attribute { }
/// <summary>Specifies that when a method returns <see cref="ReturnValue"/>, the parameter may be null even if the corresponding type disallows it.</summary>
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
internal sealed class MaybeNullWhenAttribute : Attribute
{
/// <summary>Initializes the attribute with the specified return value condition.</summary>
/// <param name="returnValue">
/// The return value condition. If the method returns this value, the associated parameter may be null.
/// </param>
public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
/// <summary>Gets the return value condition.</summary>
public bool ReturnValue { get; }
}
/// <summary>Specifies that when a method returns <see cref="ReturnValue"/>, the parameter will not be null even if the corresponding type allows it.</summary>
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
internal sealed class NotNullWhenAttribute : Attribute
{
/// <summary>Initializes the attribute with the specified return value condition.</summary>
/// <param name="returnValue">
/// The return value condition. If the method returns this value, the associated parameter will not be null.
/// </param>
public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
/// <summary>Gets the return value condition.</summary>
public bool ReturnValue { get; }
}
/// <summary>Specifies that the output will be non-null if the named parameter is non-null.</summary>
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)]
internal sealed class NotNullIfNotNullAttribute : Attribute
{
/// <summary>Initializes the attribute with the associated parameter name.</summary>
/// <param name="parameterName">
/// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null.
/// </param>
public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName;
/// <summary>Gets the associated parameter name.</summary>
public string ParameterName { get; }
}
/// <summary>Applied to a method that will never return under any circumstance.</summary>
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
internal sealed class DoesNotReturnAttribute : Attribute { }
/// <summary>Specifies that the method will not return if the associated Boolean parameter is passed the specified value.</summary>
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
internal sealed class DoesNotReturnIfAttribute : Attribute
{
/// <summary>Initializes the attribute with the specified parameter value.</summary>
/// <param name="parameterValue">
/// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to
/// the associated parameter matches this value.
/// </param>
public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue;
/// <summary>Gets the condition parameter value.</summary>
public bool ParameterValue { get; }
}
#endif
#if !NETCOREAPP || NETCOREAPP3_1
/// <summary>Specifies that the method or property will ensure that the listed field and property members have not-null values.</summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
internal sealed class MemberNotNullAttribute : Attribute
{
/// <summary>Initializes the attribute with a field or property member.</summary>
/// <param name="member">
/// The field or property member that is promised to be not-null.
/// </param>
public MemberNotNullAttribute(string member) => Members = new[] { member };
/// <summary>Initializes the attribute with the list of field and property members.</summary>
/// <param name="members">
/// The list of field and property members that are promised to be not-null.
/// </param>
public MemberNotNullAttribute(params string[] members) => Members = members;
/// <summary>Gets field or property member names.</summary>
public string[] Members { get; }
}
/// <summary>Specifies that the method or property will ensure that the listed field and property members have not-null values when returning with the specified return value condition.</summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
internal sealed class MemberNotNullWhenAttribute : Attribute
{
/// <summary>Initializes the attribute with the specified return value condition and a field or property member.</summary>
/// <param name="returnValue">
/// The return value condition. If the method returns this value, the associated parameter will not be null.
/// </param>
/// <param name="member">
/// The field or property member that is promised to be not-null.
/// </param>
public MemberNotNullWhenAttribute(bool returnValue, string member)
{
ReturnValue = returnValue;
Members = new[] { member };
}
/// <summary>Initializes the attribute with the specified return value condition and list of field and property members.</summary>
/// <param name="returnValue">
/// The return value condition. If the method returns this value, the associated parameter will not be null.
/// </param>
/// <param name="members">
/// The list of field and property members that are promised to be not-null.
/// </param>
public MemberNotNullWhenAttribute(bool returnValue, params string[] members)
{
ReturnValue = returnValue;
Members = members;
}
/// <summary>Gets the return value condition.</summary>
public bool ReturnValue { get; }
/// <summary>Gets field or property member names.</summary>
public string[] Members { get; }
}
#endif
}

1
src/Linux/Tmds.DBus.Protocol/SocketExtensions.cs

@ -190,7 +190,6 @@ static class SocketExtensions
const int SOL_SOCKET = 1;
const int EINTR = 4;
// const int EBADF = 9;
static readonly int EAGAIN = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 35 : 11;
const int SCM_RIGHTS = 1;

1
src/Linux/Tmds.DBus.Protocol/Variant.cs

@ -13,7 +13,6 @@ public readonly struct Variant
private readonly long _l;
private const int TypeShift = 8 * 7;
// private const int SignatureFirstShift = 8 * 6;
private const long StripTypeMask = ~(0xffL << TypeShift);
private DBusType Type

Loading…
Cancel
Save