Skip to content

Commit

Permalink
MessageWriter: support writing VariantValue.
Browse files Browse the repository at this point in the history
  • Loading branch information
tmds committed Nov 10, 2024
1 parent 84a32de commit 6f835ec
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 69 deletions.
16 changes: 8 additions & 8 deletions src/Tmds.DBus.Protocol/MessageWriter.Basic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ public ref partial struct MessageWriter

public void WriteDouble(double value) => WritePrimitiveCore<double>(value, DBusType.Double);

public void WriteString(ReadOnlySpan<byte> value) => WriteStringCore(value);
public void WriteString(scoped ReadOnlySpan<byte> value) => WriteStringCore(value);

public void WriteString(string value) => WriteStringCore(value);

public void WriteSignature(Signature value)
=> WriteSignature(value.Data);

public void WriteSignature(ReadOnlySpan<byte> value)
public void WriteSignature(scoped ReadOnlySpan<byte> value)
{
int length = value.Length;
WriteByte((byte)length);
Expand All @@ -48,7 +48,7 @@ public void WriteSignature(string s)
WriteByte(0);
}

public void WriteObjectPath(ReadOnlySpan<byte> value) => WriteStringCore(value);
public void WriteObjectPath(scoped ReadOnlySpan<byte> value) => WriteStringCore(value);

public void WriteObjectPath(string value) => WriteStringCore(value);

Expand Down Expand Up @@ -108,19 +108,19 @@ public void WriteVariantDouble(double value)
WriteDouble(value);
}

public void WriteVariantString(ReadOnlySpan<byte> value)
public void WriteVariantString(scoped ReadOnlySpan<byte> value)
{
WriteSignature(ProtocolConstants.StringSignature);
WriteString(value);
}

public void WriteVariantSignature(ReadOnlySpan<byte> value)
public void WriteVariantSignature(scoped ReadOnlySpan<byte> value)
{
WriteSignature(ProtocolConstants.SignatureSignature);
WriteSignature(value);
}

public void WriteVariantObjectPath(ReadOnlySpan<byte> value)
public void WriteVariantObjectPath(scoped ReadOnlySpan<byte> value)
{
WriteSignature(ProtocolConstants.ObjectPathSignature);
WriteObjectPath(value);
Expand All @@ -144,7 +144,7 @@ public void WriteVariantObjectPath(string value)
WriteObjectPath(value);
}

private void WriteStringCore(ReadOnlySpan<byte> span)
private void WriteStringCore(scoped ReadOnlySpan<byte> span)
{
int length = span.Length;
WriteUInt32((uint)length);
Expand Down Expand Up @@ -174,7 +174,7 @@ private void WritePrimitiveCore<T>(T value, DBusType type)
Advance(length);
}

private int WriteRaw(ReadOnlySpan<byte> data)
private int WriteRaw(scoped ReadOnlySpan<byte> data)
{
int totalLength = data.Length;
if (totalLength <= MaxSizeHint)
Expand Down
5 changes: 5 additions & 0 deletions src/Tmds.DBus.Protocol/MessageWriter.Variant.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,9 @@ public void WriteVariant(Variant value)
{
value.WriteTo(ref this);
}

public void WriteVariant(VariantValue value)
{
value.WriteVariantTo(ref this);
}
}
1 change: 1 addition & 0 deletions src/Tmds.DBus.Protocol/ProtocolConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ static class ProtocolConstants
public static ReadOnlySpan<byte> StringSignature => new byte[] { (byte)'s' };
public static ReadOnlySpan<byte> ObjectPathSignature => new byte[] { (byte)'o' };
public static ReadOnlySpan<byte> SignatureSignature => new byte[] { (byte)'g' };
public static ReadOnlySpan<byte> VariantSignature => new byte[] { (byte)'v' };


[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down
4 changes: 2 additions & 2 deletions src/Tmds.DBus.Protocol/TypeModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -218,12 +218,12 @@ private static void EnsureSupportedVariantType(Type type)
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<byte> GetSignature<T>(scoped Span<byte> buffer)
public static ReadOnlySpan<byte> GetSignature<T>(Span<byte> buffer)
{
Debug.Assert(buffer.Length >= ProtocolConstants.MaxSignatureLength);

int bytesWritten = AppendTypeSignature(typeof(T), buffer);
return buffer.Slice(0, bytesWritten).ToArray();
return buffer.Slice(0, bytesWritten);
}

private static int AppendTypeSignature(Type type, Span<byte> signature)
Expand Down
180 changes: 175 additions & 5 deletions src/Tmds.DBus.Protocol/VariantValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1122,22 +1122,192 @@ internal string Signature
get
{
Span<byte> span = stackalloc byte[ProtocolConstants.MaxSignatureLength];
return Encoding.UTF8.GetString(GetSignature(span));
return Encoding.UTF8.GetString(GetSignature(Type, span));
}
}

internal void WriteVariantTo(ref MessageWriter writer)
{
WriteValueTo(ref writer, nestingOffset: +1);
}

private void WriteValueTo(ref MessageWriter writer, int nestingOffset)
{
(VariantValueType type, int nesting) = DetermineTypeAndNesting();

nesting += nestingOffset;
while (nesting > 1)
{
writer.WriteSignature(ProtocolConstants.VariantSignature);
nesting--;
}
if (nesting == 1)
{
WriteSignatureTo(type, ref writer);
}

switch (type)
{
case VariantValueType.Byte:
writer.WriteByte(UnsafeGetByte());
break;
case VariantValueType.Bool:
writer.WriteBool(UnsafeGetBool());
break;
case VariantValueType.Int16:
writer.WriteInt16(UnsafeGetInt16());
break;
case VariantValueType.UInt16:
writer.WriteUInt16(UnsafeGetUInt16());
break;
case VariantValueType.Int32:
writer.WriteInt32(UnsafeGetInt32());
break;
case VariantValueType.UInt32:
writer.WriteUInt32(UnsafeGetUInt32());
break;
case VariantValueType.Int64:
writer.WriteInt64(UnsafeGetInt64());
break;
case VariantValueType.UInt64:
writer.WriteUInt64(UnsafeGetUInt64());
break;
case VariantValueType.Double:
writer.WriteDouble(UnsafeGetDouble());
break;
case VariantValueType.String:
writer.WriteString(UnsafeGetString());
break;
case VariantValueType.ObjectPath:
writer.WriteObjectPath(UnsafeGetString());
break;
case VariantValueType.Signature:
writer.WriteSignature(UnsafeGetSignature());
break;
case VariantValueType.UnixFd:
writer.WriteHandle(UnsafeReadHandle<Microsoft.Win32.SafeHandles.SafeFileHandle>() ?? throw new ArgumentNullException("SafeHandle unavailable."));
break;
case VariantValueType.Array:
WriteArrayTo(ref writer);
break;
case VariantValueType.Struct:
WriteStructTo(ref writer);
break;
case VariantValueType.Dictionary:
WriteDictionaryTo(ref writer);
break;
default:
throw new ArgumentException($"VariantValueType: {type}");
}
}

private void WriteStructTo(ref MessageWriter writer)
{
writer.WriteStructureStart();
var items = (_o as VariantValue[])!;
int mask = (int)(_l >> StructVariantMaskShift);
foreach (var item in items)
{
item.WriteValueTo(ref writer, mask & 1);
mask >>= 1;
}
}

private void WriteDictionaryTo(ref MessageWriter writer)
{
ArrayStart arrayStart = writer.WriteDictionaryStart();
if (UnsafeCount > 0)
{
DBusType keyType = ToDBusType(UnsafeDetermineInnerType(DictionaryKeyTypeShift));
DBusType valueType = ToDBusType(UnsafeDetermineInnerType(DictionaryValueTypeShift));
int keyNestingOffset = keyType == DBusType.Variant ? 1 : 0;
int valueNestingOffset = valueType == DBusType.Variant ? 1 : 0;

var pairs = (_o as KeyValuePair<VariantValue, VariantValue>[])!;
foreach (var pair in pairs)
{
writer.WriteDictionaryEntryStart();
pair.Key.WriteValueTo(ref writer, keyNestingOffset);
pair.Value.WriteValueTo(ref writer, valueNestingOffset);
}
}
writer.WriteDictionaryEnd(arrayStart);
}

private void WriteArrayTo(ref MessageWriter writer)
{
DBusType itemType = ToDBusType(UnsafeDetermineInnerType(ArrayItemTypeShift));
switch (itemType)
{
case DBusType.Byte:
writer.WriteArray((_o as byte[])!);
return;
case DBusType.Int16:
writer.WriteArray((_o as short[])!);
return;
case DBusType.UInt16:
writer.WriteArray((_o as ushort[])!);
return;
case DBusType.Int32:
writer.WriteArray((_o as int[])!);
return;
case DBusType.UInt32:
writer.WriteArray((_o as uint[])!);
return;
case DBusType.Int64:
writer.WriteArray((_o as long[])!);
return;
case DBusType.UInt64:
writer.WriteArray((_o as ulong[])!);
return;
case DBusType.Double:
writer.WriteArray((_o as double[])!);
return;
case DBusType.String:
writer.WriteArray((_o as string[])!);
return;
case DBusType.ObjectPath:
writer.WriteArray((_o as ObjectPath[])!);
return;
}

ArrayStart arrayStart = writer.WriteArrayStart(itemType);
var items = _o as VariantValue[];
if (items is not null)
{
int nestingOffset = itemType == DBusType.Variant ? 1 : 0;
foreach (var item in items)
{
item.WriteValueTo(ref writer, nestingOffset);
}
}
writer.WriteArrayEnd(arrayStart);
}

private static DBusType ToDBusType(VariantValueType type)
=> (DBusType)type;

private void WriteSignatureTo(VariantValueType type, ref MessageWriter writer)
{
Span<byte> span = stackalloc byte[ProtocolConstants.MaxSignatureLength];
ReadOnlySpan<byte> signature = GetSignature(type, span);
writer.WriteSignature(signature);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ReadOnlySpan<byte> GetSignature(scoped Span<byte> buffer)
private ReadOnlySpan<byte> GetSignature(VariantValueType type, Span<byte> buffer)
{
Debug.Assert(buffer.Length >= ProtocolConstants.MaxSignatureLength);

int bytesWritten = AppendTypeSignature(buffer);
return buffer.Slice(0, bytesWritten).ToArray();
int bytesWritten = AppendTypeSignature(type, buffer);
return buffer.Slice(0, bytesWritten);
}

private int AppendTypeSignature(Span<byte> signature)
=> AppendTypeSignature(Type, signature);

private int AppendTypeSignature(VariantValueType type, Span<byte> signature)
{
VariantValueType type = Type;
switch (type)
{
case VariantValueType.Invalid:
Expand Down
19 changes: 9 additions & 10 deletions test/Tmds.DBus.Protocol.Tests/ReaderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -178,15 +178,11 @@ public bool Equals(VariantValue lhs, VariantValue other)
}
for (int i = 0; i < lhs.Count; i++)
{
if (!lhs.GetItem(i).Equals(other.GetItem(i)))
if (!Equals(lhs.GetItem(i), other.GetItem(i)))
{
return false;
}
}
if (lhs.Count == 0 && lhs.ItemType != other.ItemType)
{
return false;
}
return true;
case VariantValueType.Struct:
if (lhs.Count != other.Count)
Expand Down Expand Up @@ -226,7 +222,7 @@ public bool Equals(VariantValue lhs, VariantValue other)
for (int i = 0; i < lhs.Count; i++)
{
var pair1 = lhs.GetDictionaryEntry(i);
var pair2 = lhs.GetDictionaryEntry(i);
var pair2 = other.GetDictionaryEntry(i);
if (!Equals(pair1.Key, pair2.Key) || !Equals(pair1.Value, pair2.Value))
{
return false;
Expand Down Expand Up @@ -256,8 +252,8 @@ public static IEnumerable<object[]> ReadVariantValueTestData
VariantValue myDictionary = new VariantValue(VariantValueType.Byte, VariantValueType.String,
new[]
{
KeyValuePair.Create(new VariantValue(1), new VariantValue("one")),
KeyValuePair.Create(new VariantValue(1), new VariantValue("two")),
KeyValuePair.Create(new VariantValue((byte)1), new VariantValue("one")),
KeyValuePair.Create(new VariantValue((byte)2), new VariantValue("two")),
});
VariantValue stringVariantDictionary = new VariantValue(VariantValueType.String, VariantValueType.Variant,
new[]
Expand Down Expand Up @@ -303,6 +299,9 @@ public static IEnumerable<object[]> ReadVariantValueTestData
new object[] {new VariantValue(new Signature("sis"u8)),
new byte[] {1, 103, 0, 3, 115, 105, 115, 0},
new byte[] {1, 103, 0, 3, 115, 105, 115, 0}},
new object[] {new VariantValue(new byte[] { }),
new byte[] {2, 97, 121, 0, 0, 0, 0, 0},
new byte[] {2, 97, 121, 0, 0, 0, 0, 0}},
new object[] {new VariantValue(new long[] { 1, 2}),
new byte[] {2, 97, 120, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2},
new byte[] {2, 97, 120, 0, 16, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0}},
Expand Down Expand Up @@ -380,8 +379,8 @@ public static IEnumerable<object[]> ReadVariantValueTestData
new byte[] {1, 118, 0, 2, 97, 118, 0, 0, 8, 0, 0, 0, 1, 105, 0, 0, 1, 0, 0, 0}},
// v -> av / v -> v / v -> i
new object[] {VariantValue.CreateVariant(new VariantValue(VariantValueType.Variant, new VariantValue[] { VariantValue.CreateVariant(1) })),
new byte[] {1, 118, 0, 2, 97, 118, 0, 0, 0, 0, 0, 9, 1, 118, 0, 1, 105, 0, 0, 0, 0, 0, 0, 1},
new byte[] {1, 118, 0, 2, 97, 118, 0, 0, 9, 0, 0, 0, 1, 118, 0, 1, 105, 0, 0, 0, 1, 0, 0, 0}},
new byte[] {1, 118, 0, 2, 97, 118, 0, 0, 0, 0, 0, 12, 1, 118, 0, 1, 105, 0, 0, 0, 0, 0, 0, 1},
new byte[] {1, 118, 0, 2, 97, 118, 0, 0, 12, 0, 0, 0, 1, 118, 0, 1, 105, 0, 0, 0, 1, 0, 0, 0}},
// v -> a{sv}
// 0: v -> i
// 1: v -> v / v -> i
Expand Down
Loading

0 comments on commit 6f835ec

Please sign in to comment.