From 0969a1839c946dcebadce61fd70d5dd44c81c34d Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Wed, 13 Nov 2024 15:55:35 +0100 Subject: [PATCH] VariantValue: support constructing variant values. The VariantValue type was created as a representation of a variant that is read from the bus. For writing variants, a separate type ('Variant') exists. This PR makes it possible to compose variant values using VariantValue. These values can then be written to the bus. The UX of VariantValue when dealing with composite values (Dictionaries, Arrays of Arrays/Dictionaries/Structs) is clunkier than Variant (which offers a strongly-typed API). Such composite values are not expected to be the common case. The benefit of using VariantValue over Variant is that values read from the bus as VariantValue can now be also written back. --- docs/protocol.md | 50 +- src/Tmds.DBus.Protocol/ObjectPath.cs | 19 +- src/Tmds.DBus.Protocol/ProtocolConstants.cs | 7 + src/Tmds.DBus.Protocol/Reader.Variant.cs | 20 +- src/Tmds.DBus.Protocol/Signature.cs | 16 + src/Tmds.DBus.Protocol/Variant.cs | 3 + src/Tmds.DBus.Protocol/VariantValue.cs | 845 ++++++++++++++---- src/Tmds.DBus.Tool/ProtocolGenerator.cs | 2 +- test/Tmds.DBus.Protocol.Tests/ReaderTests.cs | 88 +- .../VariantValueTests.cs | 291 ++++-- 10 files changed, 1028 insertions(+), 313 deletions(-) diff --git a/docs/protocol.md b/docs/protocol.md index 3fdbf618..ac413ae7 100644 --- a/docs/protocol.md +++ b/docs/protocol.md @@ -281,28 +281,48 @@ Note that `VariantValue` is a small struct, there is no need to pass it by refer ### Writing a variant -For writing variants, the value must be stored in a `Variant` struct and passed to `Writer.WriteVariant(Variant)`. +For writing variants, the value must be stored in a `VariantValue` struct and passed to `Writer.WriteVariant(VariantValue)`. -Basic types have implicit conversion to `Variant`. +Simple types have implicit conversion to `Variant`. ```cs -Variant v1 = (byte)1; -Variant v2 = "string"; -Variant v3 = new ObjectPath("/path"); +VariantValue v1 = (byte)1; +VariantValue v2 = "string"; +VariantValue v3 = new ObjectPath("/path"); ``` -For composite variant values, the libraries `Struct`/`Array`/`Dict` classes must be used. +They can also be constructed using a static method: + ```cs -Variant v4 = Struct.Create((byte)1, Struct.Create("string", "string")); -Variant v5 = new Dict() -{ - { 1, Struct.Create(1, 2) }, - { 2, "string" }, -}; -Variant v6 = new Array() { 1, 2 }; +VariantValue v1 = VariantValue.Byte(1); +VariantValue v2 = VariantValue.String("string"); +VariantValue v3 = VariantValue.ObjectPath("/path"); +``` + +Structs can be created using the static `Struct` method: + +```cs +VariantValue v1 = VariantValue.Struct("string", 5); ``` -As shown in the previous examples, the composite types support nesting. +Arrays can be created using the static `Array` method. -Note that the `Variant` struct is a small struct, there is no need to pass it by reference. +For simple types, the C# array can be passed as the argument: +```cs +VariantValue v = VariantValue.Array(new int[] { 1, 2, 3 }) +``` +For arrays that hold other arrays, dictionaries, or structs, the item signature must be specified and then the items as a `VariantValue[]`: + +```cs +// Array that holds structs of (byte, string). +VariantValue v = VariantValue.Array("(ys)"u8, new [] { VariantValue.Struct((byte)1, "one"), VariantValue.Struct((byte)1, "two") }); +``` + +For dictionaries, the `Dictionary` method can be used. It accepts the key type, the value signature, and then the pairs as a `KeyValuePair[]`. + +```cs +// This example shows how to convert a strongly typed .NET Dictionary to a VariantValue. +Dictionary dict = ...; +VariantValue v = VariantValue.Dictionary(DBusType.Byte, "i"u8, dict.Select(pair => KeyValuePair.Create((VariantValue)pair.Key, (VariantValue)pair.Value)).ToArray()); +``` diff --git a/src/Tmds.DBus.Protocol/ObjectPath.cs b/src/Tmds.DBus.Protocol/ObjectPath.cs index 51753ea1..a500bc26 100644 --- a/src/Tmds.DBus.Protocol/ObjectPath.cs +++ b/src/Tmds.DBus.Protocol/ObjectPath.cs @@ -4,7 +4,24 @@ public struct ObjectPath { private string _value; - public ObjectPath(string value) => _value = value; + public ObjectPath(string value) + { + _value = value; + ThrowIfEmpty(); + } + + internal void ThrowIfEmpty() + { + if (_value is null || _value.Length == 0) + { + ThrowEmptyException(); + } + } + + private void ThrowEmptyException() + { + throw new ArgumentException($"{nameof(ObjectPath)} is empty."); + } public override string ToString() => _value ?? ""; diff --git a/src/Tmds.DBus.Protocol/ProtocolConstants.cs b/src/Tmds.DBus.Protocol/ProtocolConstants.cs index 52862734..085795ff 100644 --- a/src/Tmds.DBus.Protocol/ProtocolConstants.cs +++ b/src/Tmds.DBus.Protocol/ProtocolConstants.cs @@ -20,6 +20,13 @@ static class ProtocolConstants public static ReadOnlySpan SignatureSignature => new byte[] { (byte)'g' }; public static ReadOnlySpan VariantSignature => new byte[] { (byte)'v' }; + private static ReadOnlySpan SingleTypes => new byte[] { (byte)'y', (byte)'b', (byte)'n', (byte)'q', (byte)'i', (byte)'u', (byte)'x', (byte)'t', (byte)'d', (byte)'h', (byte)'s', (byte)'o', (byte)'g', (byte)'v' }; + + public static bool IsSingleCompleteType(byte b) + { + return SingleTypes.IndexOf(b) != -1; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetTypeAlignment(DBusType type) diff --git a/src/Tmds.DBus.Protocol/Reader.Variant.cs b/src/Tmds.DBus.Protocol/Reader.Variant.cs index a8945655..6d295ebf 100644 --- a/src/Tmds.DBus.Protocol/Reader.Variant.cs +++ b/src/Tmds.DBus.Protocol/Reader.Variant.cs @@ -81,7 +81,7 @@ private VariantValue ReadTypeAsVariantValue(DBusType type, ReadOnlySpan in items.Add(new KeyValuePair(key, value)); } ReadOnlySpan valueSignature = itemSignature.Slice(2, itemSignature.Length - 3); - return new VariantValue(ToVariantValueType(keyType), ToVariantValueType(valueType), VariantValue.GetSignatureObject(items.Count, valueSignature), items.ToArray(), nesting); + return new VariantValue(ToVariantValueType(keyType, keyInnerSignature), ToVariantValueType(valueType, valueInnerSignature), VariantValue.GetSignatureObject(items.Count, valueSignature), items.ToArray(), nesting); } else { @@ -136,7 +136,7 @@ private VariantValue ReadTypeAsVariantValue(DBusType type, ReadOnlySpan in : ReadTypeAsVariantValue(type, innerSignature, nesting: 0); items.Add(value); } - return new VariantValue(ToVariantValueType(type), VariantValue.GetSignatureObject(items.Count, itemSignature), items.ToArray(), nesting); + return new VariantValue(ToVariantValueType(type, innerSignature), VariantValue.GetSignatureObject(items.Count, itemSignature), items.ToArray(), nesting); } } case DBusType.Struct: @@ -148,11 +148,6 @@ private VariantValue ReadTypeAsVariantValue(DBusType type, ReadOnlySpan in int i = 0; while (sigReader.TryRead(out type, out innerSignature)) { - if (i > VariantValue.MaxStructFields) - { - VariantValue.ThrowMaxStructFieldsExceeded(); - } - variantMask <<= 1; VariantValue value; if (type == DBusType.Variant) { @@ -182,6 +177,13 @@ private void ThrowInvalidSignature(string message) throw new ProtocolException(message); } - private static VariantValueType ToVariantValueType(DBusType type) - => (VariantValueType)type; + private static VariantValueType ToVariantValueType(DBusType type, ReadOnlySpan innerSignature) + { + VariantValueType rv = (VariantValueType)type; + if (rv == VariantValueType.Array && innerSignature[0] == (byte)DBusType.DictEntry) + { + rv = VariantValueType.Dictionary; + } + return rv; + } } diff --git a/src/Tmds.DBus.Protocol/Signature.cs b/src/Tmds.DBus.Protocol/Signature.cs index dfe7f6a5..8b734df1 100644 --- a/src/Tmds.DBus.Protocol/Signature.cs +++ b/src/Tmds.DBus.Protocol/Signature.cs @@ -4,6 +4,22 @@ public struct Signature { private byte[]? _value; + // note: C# compiler treats these as static data. + public static ReadOnlySpan Byte => new byte[] { (byte)'y' }; + public static ReadOnlySpan Boolean => new byte[] { (byte)'b' }; + public static ReadOnlySpan Int16 => new byte[] { (byte)'n' }; + public static ReadOnlySpan UInt16 => new byte[] { (byte)'q' }; + public static ReadOnlySpan Int32 => new byte[] { (byte)'i' }; + public static ReadOnlySpan UInt32 => new byte[] { (byte)'u' }; + public static ReadOnlySpan Int64 => new byte[] { (byte)'x' }; + public static ReadOnlySpan UInt64 => new byte[] { (byte)'t' }; + public static ReadOnlySpan Double => new byte[] { (byte)'d' }; + public static ReadOnlySpan UnixFd => new byte[] { (byte)'h' }; + public static ReadOnlySpan String => new byte[] { (byte)'s' }; + public static ReadOnlySpan ObjectPath => new byte[] { (byte)'o' }; + public static ReadOnlySpan Sig => new byte[] { (byte)'g' }; // Name can not be the same as enclosing type. + public static ReadOnlySpan Variant => new byte[] { (byte)'v' }; + internal byte[] Data => _value ?? Array.Empty(); [Obsolete("Use the constructor that accepts a ReadOnlySpan.")] diff --git a/src/Tmds.DBus.Protocol/Variant.cs b/src/Tmds.DBus.Protocol/Variant.cs index 3c584333..c9e87166 100644 --- a/src/Tmds.DBus.Protocol/Variant.cs +++ b/src/Tmds.DBus.Protocol/Variant.cs @@ -4,6 +4,9 @@ namespace Tmds.DBus.Protocol; // DynamicallyAccessedMemberTypes.PublicParameterlessConstructor. #pragma warning disable IL2091 +#if !DEBUG +[Obsolete($"{nameof(Variant)} will be removed. Use the {nameof(VariantValue)} type instead.")] +#endif public readonly struct Variant { private static readonly object Int64Type = DBusType.Int64; diff --git a/src/Tmds.DBus.Protocol/VariantValue.cs b/src/Tmds.DBus.Protocol/VariantValue.cs index d0b7ce24..cec129b2 100644 --- a/src/Tmds.DBus.Protocol/VariantValue.cs +++ b/src/Tmds.DBus.Protocol/VariantValue.cs @@ -17,9 +17,10 @@ namespace Tmds.DBus.Protocol; private const int DictionaryKeyTypeShift = 8 * 5; private const int DictionaryValueTypeShift = 8 * 4; private const long StripMetadataMask = 0xffffffffL; + private const long MetadataMask = ~StripMetadataMask; private const long StructVariantMask = 0xffffL; private const int StructVariantMaskShift = 8 * 4; - internal const int MaxStructFields = 2 * 8; + private const int MaxStructFields = 2 * 8; private bool UnsafeIsStructFieldVariant(int index) => (_l & (1L << (index + StructVariantMaskShift))) != 0; @@ -48,15 +49,15 @@ public TypeData(long l) public readonly long L { get; } } - private const long Int64 = ((long)VariantValueType.Int64) << TypeShift; - private const long UInt64 = ((long)VariantValueType.UInt64) << TypeShift; - private const long Double = ((long)VariantValueType.Double) << TypeShift; - private const long VariantOfInt64 = (1L << NestingShift) | Int64; - private const long VariantOfUInt64 = (1L << NestingShift) | UInt64; - private const long VariantOfDouble = (1L << NestingShift) | Double; - private static readonly object Int64TypeDescriptor = new TypeData(Int64); - private static readonly object UInt64TypeDescriptor = new TypeData(UInt64); - private static readonly object DoubleTypeDescriptor = new TypeData(Double); + private const long L_Int64 = ((long)VariantValueType.Int64) << TypeShift; + private const long L_UInt64 = ((long)VariantValueType.UInt64) << TypeShift; + private const long L_Double = ((long)VariantValueType.Double) << TypeShift; + private const long VariantOfInt64 = (1L << NestingShift) | L_Int64; + private const long VariantOfUInt64 = (1L << NestingShift) | L_UInt64; + private const long VariantOfDouble = (1L << NestingShift) | L_Double; + private static readonly object Int64TypeDescriptor = new TypeData(L_Int64); + private static readonly object UInt64TypeDescriptor = new TypeData(L_UInt64); + private static readonly object DoubleTypeDescriptor = new TypeData(L_Double); private static readonly object VariantOfInt64TypeDescriptor = new TypeData(VariantOfInt64); private static readonly object VariantOfUInt64TypeDescriptor = new TypeData(VariantOfUInt64); private static readonly object VariantOfDoubleTypeDescriptor = new TypeData(VariantOfDouble); @@ -77,9 +78,9 @@ private static object UpdateNesting(TypeData metadata, int change) l = UpdateNesting(l, change); return l switch { - Int64 => Int64TypeDescriptor, - UInt64 => UInt64TypeDescriptor, - Double => DoubleTypeDescriptor, + L_Int64 => Int64TypeDescriptor, + L_UInt64 => UInt64TypeDescriptor, + L_Double => DoubleTypeDescriptor, VariantOfInt64 => VariantOfInt64TypeDescriptor, VariantOfUInt64 => VariantOfUInt64TypeDescriptor, VariantOfDouble => VariantOfDoubleTypeDescriptor, @@ -107,57 +108,36 @@ private VariantValue(long l, object? o) _l = l; _o = o; } - internal VariantValue(byte value) : - this(value, nesting: 0) - { } internal VariantValue(byte value, byte nesting) { _l = value | GetTypeMetadata(VariantValueType.Byte, nesting); _o = null; } - internal VariantValue(bool value) : - this(value, nesting: 0) - { } internal VariantValue(bool value, byte nesting) { _l = (value ? 1L : 0) | GetTypeMetadata(VariantValueType.Bool, nesting); _o = null; } - internal VariantValue(short value) : - this(value, nesting: 0) - { } internal VariantValue(short value, byte nesting) { _l = (ushort)value | GetTypeMetadata(VariantValueType.Int16, nesting); _o = null; } - internal VariantValue(ushort value) : - this(value, nesting: 0) - { } internal VariantValue(ushort value, byte nesting) { _l = value | GetTypeMetadata(VariantValueType.UInt16, nesting); _o = null; } - internal VariantValue(int value) : - this(value, nesting: 0) - { } internal VariantValue(int value, byte nesting) { _l = (uint)value | GetTypeMetadata(VariantValueType.Int32, nesting); _o = null; } - internal VariantValue(uint value) : - this(value, nesting: 0) - { } internal VariantValue(uint value, byte nesting) { _l = value | GetTypeMetadata(VariantValueType.UInt32, nesting); _o = null; } - internal VariantValue(long value) : - this(value, nesting: 0) - { } internal VariantValue(long value, byte nesting) { _l = value; @@ -168,9 +148,6 @@ internal VariantValue(long value, byte nesting) _ => new TypeData(GetTypeMetadata(VariantValueType.Int64, nesting)) }; } - internal VariantValue(ulong value) : - this(value, nesting: 0) - { } internal VariantValue(ulong value, byte nesting) { _l = (long)value; @@ -181,9 +158,6 @@ internal VariantValue(ulong value, byte nesting) _ => new TypeData(GetTypeMetadata(VariantValueType.UInt64, nesting)) }; } - internal unsafe VariantValue(double value) : - this(value, nesting: 0) - { } internal unsafe VariantValue(double value, byte nesting) { _l = *(long*)&value; @@ -194,140 +168,75 @@ internal unsafe VariantValue(double value, byte nesting) _ => new TypeData(GetTypeMetadata(VariantValueType.Double, nesting)) }; } - internal VariantValue(string value) : - this(value, nesting: 0) - { } internal VariantValue(string value, byte nesting) { + ThrowIfNull(value, nameof(value)); _l = GetTypeMetadata(VariantValueType.String, nesting); - _o = value ?? throw new ArgumentNullException(nameof(value)); + _o = value; } - internal VariantValue(ObjectPath value) : - this(value, nesting: 0) - { } internal VariantValue(ObjectPath value, byte nesting) { + value.ThrowIfEmpty(); _l = GetTypeMetadata(VariantValueType.ObjectPath, nesting); - string s = value.ToString(); - if (s.Length == 0) - { - throw new ArgumentException(nameof(value)); - } - _o = s; + _o = value.ToString(); } - internal VariantValue(Signature value) : - this(value, nesting: 0) - { } internal VariantValue(Signature value, byte nesting) { _l = GetTypeMetadata(VariantValueType.Signature, nesting); - byte[] data = value.Data; - if (data.Length == 0) - { - throw new ArgumentException(nameof(value)); - } - _o = data; + _o = value.Data;; } // Array internal VariantValue(VariantValueType itemType, object? itemSignature, VariantValue[] items, byte nesting) { - Debug.Assert( - itemType != VariantValueType.Byte && - itemType != VariantValueType.Int16 && - itemType != VariantValueType.UInt16 && - itemType != VariantValueType.Int32 && - itemType != VariantValueType.UInt32 && - itemType != VariantValueType.Int64 && - itemType != VariantValueType.UInt64 && - itemType != VariantValueType.Double - ); int count = items.Length; _l = GetArrayTypeMetadata(itemType, count, nesting); _o = count == 0 ? itemSignature : items; - if (_o?.GetType() == typeof(int)) - { - throw new Exception(); - } } - // For testing - internal VariantValue(VariantValueType itemType, VariantValue[] items, object? itemSignature = null) : - this(itemType, itemSignature, items, nesting: 0) - { } - internal VariantValue(string[] items) : - this(items, nesting: 0) - { } internal VariantValue(string[] items, byte nesting) { _l = GetArrayTypeMetadata(VariantValueType.String, items.Length, nesting); _o = items; } - internal VariantValue(ObjectPath[] items) : - this(items, nesting: 0) - { } internal VariantValue(ObjectPath[] items, byte nesting) { _l = GetArrayTypeMetadata(VariantValueType.ObjectPath, items.Length, nesting); _o = items; } - internal VariantValue(byte[] items) : - this(items, nesting: 0) - { } internal VariantValue(byte[] items, byte nesting) { _l = GetArrayTypeMetadata(VariantValueType.Byte, items.Length, nesting); _o = items; } - internal VariantValue(short[] items) : - this(items, nesting: 0) - { } internal VariantValue(short[] items, byte nesting) { _l = GetArrayTypeMetadata(VariantValueType.Int16, items.Length, nesting); _o = items; } - internal VariantValue(ushort[] items) : - this(items, nesting: 0) - { } internal VariantValue(ushort[] items, byte nesting) { _l = GetArrayTypeMetadata(VariantValueType.UInt16, items.Length, nesting); _o = items; } - internal VariantValue(int[] items) : - this(items, nesting: 0) - { } internal VariantValue(int[] items, byte nesting) { _l = GetArrayTypeMetadata(VariantValueType.Int32, items.Length, nesting); _o = items; } - internal VariantValue(uint[] items) : - this(items, nesting: 0) - { } internal VariantValue(uint[] items, byte nesting) { _l = GetArrayTypeMetadata(VariantValueType.UInt32, items.Length, nesting); _o = items; } - internal VariantValue(long[] items) : - this(items, nesting: 0) - { } internal VariantValue(long[] items, byte nesting) { _l = GetArrayTypeMetadata(VariantValueType.Int64, items.Length, nesting); _o = items; } - internal VariantValue(ulong[] items) : - this(items, nesting: 0) - { } internal VariantValue(ulong[] items, byte nesting) { _l = GetArrayTypeMetadata(VariantValueType.UInt64, items.Length, nesting); _o = items; } - internal VariantValue(double[] items) : - this(items, nesting: 0) - { } internal VariantValue(double[] items, byte nesting) { _l = GetArrayTypeMetadata(VariantValueType.Double, items.Length, nesting); @@ -340,39 +249,34 @@ internal VariantValue(VariantValueType keyType, VariantValueType valueType, obje _l = GetDictionaryTypeMetadata(keyType, valueType, count, nesting); _o = count == 0 ? valueSignature : pairs; } - // For testing - internal VariantValue(VariantValueType keyType, VariantValueType valueType, KeyValuePair[] pairs, object? valueSignature = null) : - this(keyType, valueType, valueSignature, pairs, nesting: 0) - { } // Struct - internal VariantValue(VariantValue[] fields) : - this(fields, nesting: 0) - { } - internal VariantValue(VariantValue[] fields, byte nesting) + private static VariantValue StructCore(VariantValue[] fields, byte nesting) { long variantMask = 0; for (int i = 0; i < fields.Length; i++) { - if (i > VariantValue.MaxStructFields) - { - ThrowMaxStructFieldsExceeded(); - } VariantValue value = fields[i]; + value.ThrowIfInvalid(); + if (value.Type == VariantValueType.Variant) { variantMask |= (1L << i); fields[i] = value.GetVariantValue(); } } - _l = GetStructMetadata(variantMask, fields.Length, nesting); - _o = fields; + + return new VariantValue(variantMask, fields, nesting: 0); } - internal static void ThrowMaxStructFieldsExceeded() + private static void ThrowMaxStructFieldsExceeded() { throw new NotSupportedException($"Struct types {VariantValue.MaxStructFields}+ fields are not supported."); } internal VariantValue(long variantMask, VariantValue[] fields, byte nesting) { + if (fields.Length > MaxStructFields) + { + ThrowMaxStructFieldsExceeded(); + } _l = GetStructMetadata(variantMask, fields.Length, nesting); _o = fields; } @@ -385,20 +289,6 @@ internal VariantValue(UnixFdCollection? fdCollection, int index, byte nesting) _l = (long)index | GetTypeMetadata(VariantValueType.UnixFd, nesting); _o = fdCollection; } - // Variant - internal static VariantValue CreateVariant(VariantValue value) - { - object? o = value._o; - long l = value._l; - if (o is TypeData td) - { - return new VariantValue(l, UpdateNesting(td, +1)); - } - else - { - return new VariantValue(UpdateNesting(l, +1), o); - } - } public VariantValue GetVariantValue() { @@ -656,7 +546,7 @@ public Dictionary GetDictionary if (UnsafeCount == 0) { - return Array.Empty(); + return System.Array.Empty(); } // Return the array by reference when we can. @@ -698,6 +588,10 @@ public Dictionary GetDictionary { return (T[])(object)(_o as string[])!; } + else if (typeof(T) == typeof(ObjectPath)) + { + return (T[])(object)(_o as ObjectPath[])!; + } else { var items = (_o as VariantValue[])!.AsSpan(); @@ -827,25 +721,25 @@ public VariantValue GetItem(int i) switch (UnsafeDetermineInnerType(ArrayItemTypeShift)) { case VariantValueType.Byte: - return new VariantValue((_o as byte[])![i]); + return Byte((_o as byte[])![i]); case VariantValueType.Int16: - return new VariantValue((_o as short[])![i]); + return Int16((_o as short[])![i]); case VariantValueType.UInt16: - return new VariantValue((_o as ushort[])![i]); + return UInt16((_o as ushort[])![i]); case VariantValueType.Int32: - return new VariantValue((_o as int[])![i]); + return Int32((_o as int[])![i]); case VariantValueType.UInt32: - return new VariantValue((_o as uint[])![i]); + return UInt32((_o as uint[])![i]); case VariantValueType.Int64: - return new VariantValue((_o as long[])![i]); + return Int64((_o as long[])![i]); case VariantValueType.UInt64: - return new VariantValue((_o as ulong[])![i]); + return UInt64((_o as ulong[])![i]); case VariantValueType.Double: - return new VariantValue((_o as double[])![i]); + return Double((_o as double[])![i]); case VariantValueType.String: - return new VariantValue((_o as string[])![i]); + return String((_o as string[])![i]); case VariantValueType.ObjectPath: - return new VariantValue((_o as ObjectPath[])![i]); + return ObjectPath((_o as ObjectPath[])![i]); } } @@ -873,29 +767,607 @@ public KeyValuePair GetDictionaryEntry(int i) // implicit conversion to VariantValue for basic D-Bus types (except Unix_FD). public static implicit operator VariantValue(byte value) - => new VariantValue(value); + => Byte(value); public static implicit operator VariantValue(bool value) - => new VariantValue(value); + => Bool(value); public static implicit operator VariantValue(short value) - => new VariantValue(value); + => Int16(value); public static implicit operator VariantValue(ushort value) - => new VariantValue(value); + => UInt16(value); public static implicit operator VariantValue(int value) - => new VariantValue(value); + => Int32(value); public static implicit operator VariantValue(uint value) - => new VariantValue(value); + => UInt32(value); public static implicit operator VariantValue(long value) - => new VariantValue(value); + => Int64(value); public static implicit operator VariantValue(ulong value) - => new VariantValue(value); + => UInt64(value); public static implicit operator VariantValue(double value) - => new VariantValue(value); + => Double(value); public static implicit operator VariantValue(string value) - => new VariantValue(value); + => String(value); public static implicit operator VariantValue(ObjectPath value) - => new VariantValue(value); + => ObjectPath(value); public static implicit operator VariantValue(Signature value) - => new VariantValue(value); + => Signature(value); + + public static VariantValue Byte(byte value) => new VariantValue(value, nesting: 0); + public static VariantValue Bool(bool value) => new VariantValue(value, nesting: 0); + public static VariantValue Int16(short value) => new VariantValue(value, nesting: 0); + public static VariantValue UInt16(ushort value) => new VariantValue(value, nesting: 0); + public static VariantValue Int32(int value) => new VariantValue(value, nesting: 0); + public static VariantValue UInt32(uint value) => new VariantValue(value, nesting: 0); + public static VariantValue Int64(long value) => new VariantValue(value, nesting: 0); + public static VariantValue UInt64(ulong value) => new VariantValue(value, nesting: 0); + public static VariantValue Double(double value) => new VariantValue(value, nesting: 0); + public static VariantValue ObjectPath(ObjectPath value) => new VariantValue(value, nesting: 0); + public static VariantValue Signature(Signature value) => new VariantValue(value, nesting: 0); + public static VariantValue String(string value) + { + ThrowIfNull(value, nameof(value)); + return new VariantValue(value, nesting: 0); + } + public static VariantValue UnixFd(SafeHandle handle) + { + ThrowIfNull(handle, nameof(handle)); + var fds = new UnixFdCollection(isRawHandleCollection: false); + fds.AddHandle(handle); + return new VariantValue(fds, index: 0); + } + public static VariantValue Variant(VariantValue value) + { + value.ThrowIfInvalid(); + + object? o = value._o; + long l = value._l; + if (o is TypeData td) + { + return new VariantValue(l, UpdateNesting(td, +1)); + } + else + { + return new VariantValue(UpdateNesting(l, +1), o); + } + } + public static VariantValue Struct(VariantValue item1) + => StructCore(new[] { item1 }, nesting: 0); + public static VariantValue Struct(VariantValue item1, VariantValue item2) + => StructCore(new[] { item1, item2 }, nesting: 0); + public static VariantValue Struct(VariantValue item1, VariantValue item2, VariantValue item3) + => StructCore(new[] { item1, item2, item3 }, nesting: 0); + public static VariantValue Struct(VariantValue item1, VariantValue item2, VariantValue item3, VariantValue item4) + => StructCore(new[] { item1, item2, item3, item4 }, nesting: 0); + public static VariantValue Struct(VariantValue item1, VariantValue item2, VariantValue item3, VariantValue item4, VariantValue item5) + => StructCore(new[] { item1, item2, item3, item4, item5 }, nesting: 0); + public static VariantValue Struct(VariantValue item1, VariantValue item2, VariantValue item3, VariantValue item4, VariantValue item5, VariantValue item6) + => StructCore(new[] { item1, item2, item3, item4, item5, item6 }, nesting: 0); + public static VariantValue Struct(VariantValue item1, VariantValue item2, VariantValue item3, VariantValue item4, VariantValue item5, VariantValue item6, VariantValue item7) + => StructCore(new[] { item1, item2, item3, item4, item5, item6, item7 }, nesting: 0); + public static VariantValue Struct(VariantValue item1, VariantValue item2, VariantValue item3, VariantValue item4, VariantValue item5, VariantValue item6, VariantValue item7, VariantValue item8) + => StructCore(new[] { item1, item2, item3, item4, item5, item6, item7, item8 }, nesting: 0); + public static VariantValue Struct(VariantValue item1, VariantValue item2, VariantValue item3, VariantValue item4, VariantValue item5, VariantValue item6, VariantValue item7, VariantValue item8, VariantValue item9) + => StructCore(new[] { item1, item2, item3, item4, item5, item6, item7, item8, item9 }, nesting: 0); + public static VariantValue Struct(VariantValue item1, VariantValue item2, VariantValue item3, VariantValue item4, VariantValue item5, VariantValue item6, VariantValue item7, VariantValue item8, VariantValue item9, VariantValue item10) + => StructCore(new[] { item1, item2, item3, item4, item5, item6, item7, item8, item9, item10 }, nesting: 0); + + public static VariantValue Array(byte[] items) + { + ThrowIfNull(items, nameof(items)); + return new VariantValue(items, nesting: 0); + } + public static VariantValue Array(short[] items) + { + ThrowIfNull(items, nameof(items)); + return new VariantValue(items, nesting: 0); + } + public static VariantValue Array(ushort[] items) + { + ThrowIfNull(items, nameof(items)); + return new VariantValue(items, nesting: 0); + } + public static VariantValue Array(int[] items) + { + ThrowIfNull(items, nameof(items)); + return new VariantValue(items, nesting: 0); + } + public static VariantValue Array(uint[] items) + { + ThrowIfNull(items, nameof(items)); + return new VariantValue(items, nesting: 0); + } + public static VariantValue Array(long[] items) + { + ThrowIfNull(items, nameof(items)); + return new VariantValue(items, nesting: 0); + } + public static VariantValue Array(ulong[] items) + { + ThrowIfNull(items, nameof(items)); + return new VariantValue(items, nesting: 0); + } + public static VariantValue Array(double[] items) + { + ThrowIfNull(items, nameof(items)); + return new VariantValue(items, nesting: 0); + } + public static VariantValue Array(ObjectPath[] items) + { + ThrowIfNull(items, nameof(items)); + return new VariantValue(items, nesting: 0); + } + public static VariantValue Array(string[] items) + { + ThrowIfNull(items, nameof(items)); + if (System.Array.IndexOf(items, null) != -1) + { + ThrowArgumentNull(nameof(items)); + } + return new VariantValue(items, nesting: 0); + } + public static VariantValue Array(SafeHandle[] items) + { + ThrowIfNull(items, nameof(items)); + if (System.Array.IndexOf(items, null) != -1) + { + ThrowArgumentNull(nameof(items)); + } + VariantValue[] values = new VariantValue[items.Length]; + var fds = new UnixFdCollection(isRawHandleCollection: false); + for (int i = 0; i < items.Length; i++) + { + SafeHandle handle = items[i]; + fds.AddHandle(handle); + values[i] = new VariantValue(fds, i); + } + return new VariantValue(VariantValueType.UnixFd, itemSignature: null, values, nesting: 0); + } + public static VariantValue Array(Signature[] items) + { + ThrowIfNull(items, nameof(items)); + VariantValue[] values = new VariantValue[items.Length]; + for (int i = 0; i < items.Length; i++) + { + values[i] = Signature(items[i]); + } + return new VariantValue(VariantValueType.Signature, itemSignature: null, values, nesting: 0); + } + public static VariantValue Array(bool[] items) + { + ThrowIfNull(items, nameof(items)); + VariantValue[] values = new VariantValue[items.Length]; + for (int i = 0; i < items.Length; i++) + { + values[i] = Bool(items[i]); + } + return new VariantValue(VariantValueType.Bool, itemSignature: null, values, nesting: 0); + } + public static VariantValue Array(ReadOnlySpan itemSignature, VariantValue[] items) + { + ThrowIfNull(items, nameof(items)); + ThrowIfSignatureEmpty(itemSignature, nameof(itemSignature)); + + VariantValueType firstType = DetermineType(itemSignature); + + if (firstType != VariantValueType.Array + && firstType != VariantValueType.Struct + && firstType != VariantValueType.Dictionary + && firstType != VariantValueType.Variant) + { + throw new ArgumentException($"Unsupported item type: {firstType}. Use a type-safe overload for the item type."); + } + + int count = items.Length; + if (count == 0) + { + ThrowIfSignatureNotSingleComplete(itemSignature, nameof(itemSignature)); + } + else + { + SignatureCheck sigCheck = new(itemSignature); + foreach (var item in items) + { + sigCheck.ThrowIfNoMatch(item); + } + } + + return new VariantValue(firstType, itemSignature: GetSignatureObject(count, itemSignature), items, nesting: 0); + } + + private static void ThrowIfSignatureEmpty(ReadOnlySpan signature, string paramName) + { + if (signature.IsEmpty) + { + ThrowSignatureEmpty(paramName); + } + } + + private static void ThrowIfSignatureNotSingleComplete(ReadOnlySpan signature, string paramName) + { + Debug.Assert(signature.Length > 0); + if (!IsSingleOrComplete(signature)) + { + ThrowSignatureIsNotSingleOrComplete(signature, paramName); + } + } + + private static bool IsSingleOrComplete(ReadOnlySpan signature) + { + Debug.Assert(signature.Length > 0); + if (signature.Length == 1) + { + return ProtocolConstants.IsSingleCompleteType(signature[0]); + } + else + { + DBusType type = (DBusType)signature[0]; + switch (type) + { + case DBusType.Array: + if (signature.Length == 1) + { + return false; + } + if (signature[1] == (byte)DBusType.DictEntry) + { + if (signature[signature.Length - 1] != (byte)'}' || signature.Length < 5) + { + return false; + } + return IsSingleOrComplete(signature.Slice(2, 1)) && IsSingleOrComplete(signature.Slice(3, signature.Length - 4)); + } + else + { + return IsSingleOrComplete(signature.Slice(1)); + } + case DBusType.Struct: + if (signature[signature.Length - 1] != (byte)')' || signature.Length < 3) + { + return false; + } + signature = signature.Slice(1, signature.Length - 2); + while (TryReadSingleCompleteType(ref signature, out ReadOnlySpan itemSignature)) + { + if (!IsSingleOrComplete(itemSignature)) + { + return false; + } + } + return true; + default: + return false; + + } + } + } + + private static void ThrowSignatureEmpty(string paramName) + { + throw new ArgumentException("Signature is empty.", paramName); + } + + private static void ThrowSignatureIsNotSingleOrComplete(ReadOnlySpan signature, string paramName) + { + throw new ArgumentException($"Signature '{Encoding.UTF8.GetString(signature)}' is not a single complete type.", paramName); + } + + public static VariantValue Dictionary(DBusType keyType, ReadOnlySpan valueSignature, KeyValuePair[] items) + { + ThrowIfNull(items, nameof(items)); + ThrowIfSignatureEmpty(valueSignature, nameof(valueSignature)); + + ReadOnlySpan keySignature = [ (byte)keyType ]; + + int count = items.Length; + if (count == 0) + { + ThrowIfSignatureNotSingleComplete(keySignature, nameof(keyType)); + ThrowIfSignatureNotSingleComplete(valueSignature, nameof(valueSignature)); + } + else + { + SignatureCheck keySigCheck = new(keySignature); + SignatureCheck valueSigCheck = new(valueSignature); + foreach (var item in items) + { + keySigCheck.ThrowIfNoMatch(item.Key); + valueSigCheck.ThrowIfNoMatch(item.Value); + } + } + + return new VariantValue((VariantValueType)keyType, DetermineType(valueSignature), GetSignatureObject(count, valueSignature), items, nesting: 0); + } + + internal static VariantValueType DetermineType(ReadOnlySpan signature) + { + VariantValueType type = (VariantValueType)signature[0]; + if (type == VariantValueType.Array && (DBusType)signature[1] == DBusType.DictEntry) + { + type = VariantValueType.Dictionary; + } + return type; + } + + readonly ref struct SignatureCheck + { + private readonly ReadOnlySpan _signature; + private readonly object? _typeDef; + private readonly long _typeL; + private readonly bool _extendedCheck; + + public SignatureCheck(ReadOnlySpan signature) + { + Debug.Assert(signature.Length > 0); + + VariantValueType signatureType = (VariantValueType)signature[0]; + + _signature = signature; + _typeDef = null; + _extendedCheck = false; + + if (signatureType == VariantValueType.Array) + { + if (signature.Length > 1) + { + if (signature[1] == (byte)DBusType.DictEntry) + { + if (signature.Length >= 5 && + signature[signature.Length - 1] == (byte)'}') + { + VariantValueType keyType = (VariantValueType)signature[2]; + VariantValueType valueType = (VariantValueType)signature[3]; + _extendedCheck = valueType == VariantValueType.Array || valueType == VariantValueType.Struct; + if (valueType == VariantValueType.Array && signature[4] == '{') // a{xa{ + { + valueType = VariantValueType.Dictionary; + } + + _typeL = (((long)VariantValueType.Dictionary) << TypeShift) | + (((long)keyType) << DictionaryKeyTypeShift) | + (((long)valueType) << DictionaryValueTypeShift); + return; + } + } + else + { + VariantValueType itemType = (VariantValueType)signature[1]; + _extendedCheck = itemType == VariantValueType.Array || itemType == VariantValueType.Struct; + if (itemType == VariantValueType.Array) + { + if (signature.Length < 3) + { + ThrowInvalidSignature(signature); + } + if (signature[2] == '{') // aa{ + { + itemType = VariantValueType.Dictionary; + } + } + + _typeL = (((long)VariantValueType.Array) << TypeShift) | + (((long)itemType) << ArrayItemTypeShift); + return; + } + } + } + else if (signatureType == VariantValueType.Struct) + { + if (signature[signature.Length - 1] == (byte)')') + { + _typeL = (((long)VariantValueType.Struct) << TypeShift) | + (((long)GetStructVariantMask(signature)) << StructVariantMaskShift); + _extendedCheck = true; + return; + } + } + else if (signature.Length == 1) + { + switch (signatureType) + { + case VariantValueType.Invalid: + ThrowInvalidSignature(signature); + return; + case VariantValueType.Variant: + _typeL = 0; + return; + case VariantValueType.Int64: + _typeDef = Int64TypeDescriptor; + _typeL = 0; + return; + case VariantValueType.UInt64: + _typeDef = UInt64TypeDescriptor; + _typeL = 0; + return; + case VariantValueType.Double: + _typeDef = DoubleTypeDescriptor; + _typeL = 0; + return; + default: + _typeL = ((long)signatureType) << TypeShift; + return; + } + } + + ThrowInvalidSignature(signature); + } + + private static long GetStructVariantMask(ReadOnlySpan signature) + { + signature = signature.Slice(1, signature.Length - 1); + int i = 0; + long mask = 0; + while (TryReadSingleCompleteType(ref signature, out ReadOnlySpan itemSignature)) + { + if (itemSignature.SequenceEqual(Tmds.DBus.Protocol.Signature.Variant)) + { + mask |= 1L << i; + } + i++; + } + return mask; + } + + private void ThrowInvalidSignature(ReadOnlySpan signature) + { + throw new ArgumentException($"Invalid signature: '{Encoding.UTF8.GetString(signature)}'", nameof(signature)); + } + + public void ThrowIfNoMatch(VariantValue vv) + { + if (_typeDef is not null) + { + if (object.ReferenceEquals(vv._o, _typeDef)) + { + return; + } + } + else if (_typeL != 0) + { + if ((vv._l & MetadataMask) == _typeL) + { + if (_extendedCheck) + { + ExtendedCheck(vv); + } + return; + } + } + else + { + // signature is Variant, just ensure the value is valid. + vv.ThrowIfInvalid(); + return; + } + + ThrowValueSignatureMismatch(vv, _signature); + } + + private static void ThrowValueSignatureMismatch(VariantValue vv, ReadOnlySpan signature) + { + throw new ArgumentException($"Value {vv} does not match signature: '{Encoding.UTF8.GetString(signature)}'"); + } + + private void ExtendedCheck(VariantValue vv) + { + // We've already verified type matches the signature. + VariantValueType type = vv.UnsafeDetermineInnerType(TypeShift); + if (type == VariantValueType.Array) + { + ReadOnlySpan itemSignature = _signature.Slice(1); + if (vv.UnsafeCount == 0) + { + if (itemSignature.SequenceEqual((vv._o as byte[])!)) + { + return; + } + } + else + { + VariantValue item = (vv._o as VariantValue[])![0]; + SignatureCheck sigCheck = new(itemSignature); + sigCheck.ThrowIfNoMatch(item); + return; + } + } + else if (type == VariantValueType.Dictionary) + { + // We've already verified the key type matches. + ReadOnlySpan valueSignature = _signature.Slice(3, _signature.Length - 4); + if (vv.UnsafeCount == 0) + { + if (valueSignature.SequenceEqual((vv._o as byte[])!)) + { + return; + } + } + else + { + KeyValuePair pair = (vv._o as KeyValuePair[])![0]; + SignatureCheck sigCheck = new(valueSignature); + sigCheck.ThrowIfNoMatch(pair.Value); + return; + } + } + else if (type == VariantValueType.Struct) + { + // We've already verified the variant mask matches. + ReadOnlySpan signature = _signature.Slice(1, _signature.Length - 2); + var items = (vv._o as VariantValue[])!; + int mask = (int)(vv._l >> StructVariantMaskShift); + foreach (var item in items) + { + if (!TryReadSingleCompleteType(ref signature, out ReadOnlySpan itemSignature)) + { + goto NoMatch; + } + if ((mask & 1) == 0) + { + SignatureCheck sigCheck = new(itemSignature); + sigCheck.ThrowIfNoMatch(item); + } + mask >>= 1; + } + if (signature.Length == 0) + { + return; + } + } + + NoMatch: + ThrowValueSignatureMismatch(vv, _signature); + } + } + + // The signature returned in itemSignature is best-effort. It may be an invalid signature. + private static bool TryReadSingleCompleteType(ref ReadOnlySpan signature, out ReadOnlySpan itemSignature) + { + ReadOnlySpan s = signature; + + int length = 0; + while (s.Length > 0 && s[0] == (byte)DBusType.Array) + { + s = s.Slice(1); + length++; + } + + if (s.Length > 0) + { + DBusType type = (DBusType)s[0]; + length++; + if (type == DBusType.Struct || type == DBusType.DictEntry) + { + s = s.Slice(1); + byte startChar = (byte)type; + byte endChar = type == DBusType.Struct ? (byte)')' : (byte)'}'; + int count = 1; + do + { + int offset = s.IndexOfAny(startChar, endChar); + if (offset == -1) + { + // Signature is invalid. Return the whole thing. + length = signature.Length; + break; + } + + if (s[offset] == startChar) + { + count++; + } + else + { + count--; + } + + length += offset + 1; + s = s.Slice(offset + 1); + } while (count > 0); + } + } + + itemSignature = signature.Slice(0, length); + signature = signature.Slice(length); + return length != 0; + } public VariantValueType ItemType => DetermineInnerType(VariantValueType.Array, ArrayItemTypeShift); @@ -906,7 +1378,8 @@ public VariantValueType KeyType public VariantValueType ValueType => DetermineInnerType(VariantValueType.Dictionary, DictionaryValueTypeShift); - public VariantValueType GetStructFieldType(int index) + // For Testing + internal VariantValueType GetStructFieldType(int index) { EnsureTypeIs(VariantValueType.Struct); if (index < 0 || index > UnsafeCount) @@ -920,6 +1393,25 @@ public VariantValueType GetStructFieldType(int index) return ((VariantValue[])_o!)[index].Type; } + private void ThrowIfInvalid() + { + if (_l == 0 && _o is null) + { + ThrowArgumentNull("value"); + } + } + + private static void ThrowIfNull(object? argument, string paramName) + { + if (argument is null) + { + ThrowArgumentNull(paramName); + } + } + + internal static void ThrowArgumentNull(string paramName) => + throw new ArgumentNullException(paramName); + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void EnsureTypeIs(VariantValueType expected) => EnsureTypeIs(Type, expected); @@ -936,7 +1428,7 @@ private static void EnsureTypeIs(VariantValueType actual, VariantValueType expec [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void EnsureTypeIs(VariantValueType actual, VariantValueType[] expected) { - if (Array.IndexOf(expected, actual) == -1) + if (System.Array.IndexOf(expected, actual) == -1) { ThrowCannotRetrieveAs(actual, expected); } @@ -1019,13 +1511,12 @@ public string ToString(bool includeTypeSuffix) case VariantValueType.Double: return $"{UnsafeGetDouble()}{TypeSuffix(includeTypeSuffix, type, nesting)}"; case VariantValueType.String: - return $"{UnsafeGetString()}{TypeSuffix(includeTypeSuffix, type, nesting)}"; case VariantValueType.ObjectPath: return $"{UnsafeGetString()}{TypeSuffix(includeTypeSuffix, type, nesting)}"; case VariantValueType.Signature: return $"{UnsafeGetSignature()}{TypeSuffix(includeTypeSuffix, type, nesting)}"; case VariantValueType.Struct: - var values = (_o as VariantValue[]) ?? Array.Empty(); + var values = (_o as VariantValue[])!; return $"({string.Join(", ", values.Select(v => v.ToString(includeTypeSuffix: false)))}){TypeSuffix(includeTypeSuffix, type, nesting)}"; default: return $"{TypeString(type, nesting)}"; @@ -1041,7 +1532,7 @@ string TypeString(VariantValueType type, byte nesting) if (type == VariantValueType.Struct) { var _this = this; - var values = (_o as VariantValue[]) ?? Array.Empty(); + var values = (_o as VariantValue[])!; suffix = $"<{string.Join(", ", values.Select((v, idx) => _this.UnsafeIsStructFieldVariant(idx) ? VariantValueType.Variant : v.Type))}>"; } else if (type == VariantValueType.Array) @@ -1117,13 +1608,10 @@ public bool Equals(VariantValue other) } // For testing. - internal string Signature + internal string GetDBusSignature() { - get - { - Span span = stackalloc byte[ProtocolConstants.MaxSignatureLength]; - return Encoding.UTF8.GetString(GetSignature(Type, span)); - } + Span span = stackalloc byte[ProtocolConstants.MaxSignatureLength]; + return Encoding.UTF8.GetString(GetSignature(Type, span)); } internal void WriteVariantTo(ref MessageWriter writer) @@ -1185,7 +1673,11 @@ private void WriteValueTo(ref MessageWriter writer, int nestingOffset) writer.WriteSignature(UnsafeGetSignature()); break; case VariantValueType.UnixFd: - writer.WriteHandle(UnsafeReadHandle() ?? throw new ArgumentNullException("SafeHandle unavailable.")); + SafeHandle? handle = UnsafeReadHandle(); + if (handle is null) + { + throw new InvalidOperationException("Handle already read"); + } break; case VariantValueType.Array: WriteArrayTo(ref writer); @@ -1383,12 +1875,13 @@ bool TryAppendSignatureFromMetadata(Span signature, ref int length, Varian { return false; } + } - static bool IsSimpleSignature(VariantValueType type) + // Signature consists of a single character. + private static bool IsSimpleSignature(VariantValueType type) => type != VariantValueType.Array && type != VariantValueType.Dictionary && type != VariantValueType.Struct; - } private static void ThrowTypeInvalid() { diff --git a/src/Tmds.DBus.Tool/ProtocolGenerator.cs b/src/Tmds.DBus.Tool/ProtocolGenerator.cs index 39975840..9bbbd5e5 100644 --- a/src/Tmds.DBus.Tool/ProtocolGenerator.cs +++ b/src/Tmds.DBus.Tool/ProtocolGenerator.cs @@ -1010,7 +1010,7 @@ private static string GetDotnetType(DBusType type, ReadOnlySpan innerSigna case DBusType.Signature: return "Signature"; case DBusType.Variant: - return readNotWrite ? "VariantValue" : "Variant"; + return "VariantValue"; case DBusType.UnixFd: return typeof(SafeHandle).FullName; diff --git a/test/Tmds.DBus.Protocol.Tests/ReaderTests.cs b/test/Tmds.DBus.Protocol.Tests/ReaderTests.cs index 31976e8e..be33655e 100644 --- a/test/Tmds.DBus.Protocol.Tests/ReaderTests.cs +++ b/test/Tmds.DBus.Protocol.Tests/ReaderTests.cs @@ -150,7 +150,7 @@ public void ReadHandleRaw() public bool Equals(VariantValue lhs, VariantValue other) { - if (lhs.Signature != other.Signature) + if (lhs.GetDBusSignature() != other.GetDBusSignature()) { return false; } @@ -249,142 +249,142 @@ public static IEnumerable ReadVariantValueTestData { get { - VariantValue myDictionary = new VariantValue(VariantValueType.Byte, VariantValueType.String, + VariantValue myDictionary = VariantValue.Dictionary(DBusType.Byte, Signature.String, new[] { - KeyValuePair.Create(new VariantValue((byte)1), new VariantValue("one")), - KeyValuePair.Create(new VariantValue((byte)2), new VariantValue("two")), + KeyValuePair.Create(VariantValue.Byte(1), VariantValue.String("one")), + KeyValuePair.Create(VariantValue.Byte(2), VariantValue.String("two")), }); - VariantValue stringVariantDictionary = new VariantValue(VariantValueType.String, VariantValueType.Variant, + VariantValue stringVariantDictionary = VariantValue.Dictionary(DBusType.String, Signature.Variant, new[] { - KeyValuePair.Create(new VariantValue("one"), new VariantValue(1)), - KeyValuePair.Create(new VariantValue("two"), VariantValue.CreateVariant(new VariantValue(2))), + KeyValuePair.Create(VariantValue.String("one"), VariantValue.Int32(1)), + KeyValuePair.Create(VariantValue.String("two"), VariantValue.Variant(VariantValue.Int32(2))), }); return new[] { - new object[] {new VariantValue(true), + new object[] {(VariantValue)(true), new byte[] {1, 98, 0, 0, 0, 0, 0, 1}, new byte[] {1, 98, 0, 0, 1, 0, 0, 0}}, - new object[] {new VariantValue((byte)5), + new object[] {(VariantValue)((byte)5), new byte[] {1, 121, 0, 5}, new byte[] {1, 121, 0, 5}}, - new object[] {new VariantValue((short)0x0102), + new object[] {(VariantValue)((short)0x0102), new byte[] {1, 110, 0, 0, 1, 2}, new byte[] {1, 110, 0, 0, 2, 1}}, - new object[] {new VariantValue(0x01020304), + new object[] {(VariantValue)(0x01020304), new byte[] {1, 105, 0, 0, 1, 2, 3, 4}, new byte[] {1, 105, 0, 0, 4, 3, 2, 1}}, - new object[] {new VariantValue(0x0102030405060708), + new object[] {(VariantValue)(0x0102030405060708), new byte[] {1, 120, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8}, new byte[] {1, 120, 0, 0, 0, 0, 0, 0, 8, 7, 6, 5, 4, 3, 2, 1}}, - new object[] {new VariantValue(1.0), + new object[] {(VariantValue)(1.0), new byte[] {1, 100, 0, 0, 0, 0, 0, 0, 63, 240, 0, 0, 0, 0, 0, 0}, new byte[] {1, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 63}}, - new object[] {new VariantValue((ushort)0x0102), + new object[] {(VariantValue)((ushort)0x0102), new byte[] {1, 113, 0, 0, 1, 2}, new byte[] {1, 113, 0, 0, 2, 1}}, - new object[] {new VariantValue((uint)0x01020304), + new object[] {(VariantValue)((uint)0x01020304), new byte[] {1, 117, 0, 0, 1, 2, 3, 4}, new byte[] {1, 117, 0, 0, 4, 3, 2, 1}}, - new object[] {new VariantValue((ulong)0x0102030405060708), + new object[] {(VariantValue)((ulong)0x0102030405060708), new byte[] {1, 116, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8}, new byte[] {1, 116, 0, 0, 0, 0, 0, 0, 8, 7, 6, 5, 4, 3, 2, 1}}, - new object[] {new VariantValue("hw"), + new object[] {(VariantValue)("hw"), new byte[] {1, 115, 0, 0, 0, 0, 0, 2, 104, 119, 0}, new byte[] {1, 115, 0, 0, 2, 0, 0, 0, 104, 119, 0}}, - new object[] {new VariantValue(new ObjectPath("/a/b")), + new object[] {(VariantValue)(new ObjectPath("/a/b")), new byte[] {1, 111, 0, 0, 0, 0, 0, 4, 47, 97, 47, 98, 0}, new byte[] {1, 111, 0, 0, 4, 0, 0, 0, 47, 97, 47, 98, 0}}, - new object[] {new VariantValue(new Signature("sis"u8)), + new object[] {(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 object[] {VariantValue.Array(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 object[] {VariantValue.Array(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}}, - new object[] {new VariantValue(new VariantValue[] { new VariantValue(1L), new VariantValue("hw") }), + new object[] {VariantValue.Struct(1L, "hw"), new byte[] {4, 40, 120, 115, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 104, 119, 0}, new byte[] {4, 40, 120, 115, 41, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 104, 119, 0}}, new object[] {myDictionary, new byte[] {5, 97, 123, 121, 115, 125, 0, 0, 0, 0, 0, 28, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 3, 111, 110, 101, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 3, 116, 119, 111, 0}, new byte[] {5, 97, 123, 121, 115, 125, 0, 0, 28, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 111, 110, 101, 0, 0, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 116, 119, 111, 0}}, - new object[] {new VariantValue(new VariantValue[] { new VariantValue((byte)1), new VariantValue((byte)2), new VariantValue((byte)3), new VariantValue((byte)4), new VariantValue((byte)5), new VariantValue((byte)6), new VariantValue((byte)7), new VariantValue((byte)8) }), + new object[] {VariantValue.Struct((byte)1, (byte)2, (byte)3, (byte)4, (byte)5, (byte)6, (byte)7, (byte)8), new byte[] {10, 40, 121, 121, 121, 121, 121, 121, 121, 121, 41, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8}, new byte[] {10, 40, 121, 121, 121, 121, 121, 121, 121, 121, 41, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8}}, // Above values wrapped in a Variant. - new object[] {VariantValue.CreateVariant(new VariantValue(true)), + new object[] {VariantValue.Variant((VariantValue)(true)), new byte[] {1, 118, 0, 1, 98, 0, 0, 0, 0, 0, 0, 1}, new byte[] {1, 118, 0, 1, 98, 0, 0, 0, 1, 0, 0, 0}}, - new object[] {VariantValue.CreateVariant(new VariantValue((byte)5)), + new object[] {VariantValue.Variant((VariantValue)((byte)5)), new byte[] {1, 118, 0, 1, 121, 0, 5}, new byte[] {1, 118, 0, 1, 121, 0, 5}}, - new object[] {VariantValue.CreateVariant(new VariantValue((short)0x0102)), + new object[] {VariantValue.Variant((VariantValue)((short)0x0102)), new byte[] {1, 118, 0, 1, 110, 0, 1, 2}, new byte[] {1, 118, 0, 1, 110, 0, 2, 1}}, - new object[] {VariantValue.CreateVariant(new VariantValue(0x01020304)), + new object[] {VariantValue.Variant((VariantValue)(0x01020304)), new byte[] {1, 118, 0, 1, 105, 0, 0, 0, 1, 2, 3, 4}, new byte[] {1, 118, 0, 1, 105, 0, 0, 0, 4, 3, 2, 1}}, - new object[] {VariantValue.CreateVariant(new VariantValue(0x0102030405060708)), + new object[] {VariantValue.Variant((VariantValue)(0x0102030405060708)), new byte[] {1, 118, 0, 1, 120, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8}, new byte[] {1, 118, 0, 1, 120, 0, 0, 0, 8, 7, 6, 5, 4, 3, 2, 1}}, - new object[] {VariantValue.CreateVariant(new VariantValue(1.0)), + new object[] {VariantValue.Variant((VariantValue)(1.0)), new byte[] {1, 118, 0, 1, 100, 0, 0, 0, 63, 240, 0, 0, 0, 0, 0, 0}, new byte[] {1, 118, 0, 1, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 63}}, - new object[] {VariantValue.CreateVariant(new VariantValue((ushort)0x0102)), + new object[] {VariantValue.Variant((VariantValue)((ushort)0x0102)), new byte[] {1, 118, 0, 1, 113, 0, 1, 2}, new byte[] {1, 118, 0, 1, 113, 0, 2, 1}}, - new object[] {VariantValue.CreateVariant(new VariantValue((uint)0x01020304)), + new object[] {VariantValue.Variant((VariantValue)((uint)0x01020304)), new byte[] {1, 118, 0, 1, 117, 0, 0, 0, 1, 2, 3, 4}, new byte[] {1, 118, 0, 1, 117, 0, 0, 0, 4, 3, 2, 1}}, - new object[] {VariantValue.CreateVariant(new VariantValue((ulong)0x0102030405060708)), + new object[] {VariantValue.Variant((VariantValue)((ulong)0x0102030405060708)), new byte[] {1, 118, 0, 1, 116, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8}, new byte[] {1, 118, 0, 1, 116, 0, 0, 0, 8, 7, 6, 5, 4, 3, 2, 1}}, - new object[] {VariantValue.CreateVariant(new VariantValue("hw")), + new object[] {VariantValue.Variant((VariantValue)("hw")), new byte[] {1, 118, 0, 1, 115, 0, 0, 0, 0, 0, 0, 2, 104, 119, 0}, new byte[] {1, 118, 0, 1, 115, 0, 0, 0, 2, 0, 0, 0, 104, 119, 0}}, - new object[] {VariantValue.CreateVariant(new VariantValue(new ObjectPath("/a/b"))), + new object[] {VariantValue.Variant((VariantValue)(new ObjectPath("/a/b"))), new byte[] {1, 118, 0, 1, 111, 0, 0, 0, 0, 0, 0, 4, 47, 97, 47, 98, 0}, new byte[] {1, 118, 0, 1, 111, 0, 0, 0, 4, 0, 0, 0, 47, 97, 47, 98, 0}}, - new object[] {VariantValue.CreateVariant(new VariantValue(new Signature("sis"u8))), + new object[] {VariantValue.Variant((VariantValue)(new Signature("sis"u8))), new byte[] {1, 118, 0, 1, 103, 0, 3, 115, 105, 115, 0}, new byte[] {1, 118, 0, 1, 103, 0, 3, 115, 105, 115, 0}}, - new object[] {VariantValue.CreateVariant(new VariantValue(new long[] { 1, 2})), + new object[] {VariantValue.Variant(VariantValue.Array(new long[] { 1, 2})), new byte[] {1, 118, 0, 2, 97, 120, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2}, new byte[] {1, 118, 0, 2, 97, 120, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0}}, - new object[] {VariantValue.CreateVariant(new VariantValue(new VariantValue[] { new VariantValue(1L), new VariantValue("hw") })), + new object[] {VariantValue.Variant(VariantValue.Struct(1L, "hw")), new byte[] {1, 118, 0, 4, 40, 120, 115, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 104, 119, 0}, new byte[] {1, 118, 0, 4, 40, 120, 115, 41, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 104, 119, 0}}, - new object[] {VariantValue.CreateVariant(myDictionary), + new object[] {VariantValue.Variant(myDictionary), new byte[] {1, 118, 0, 5, 97, 123, 121, 115, 125, 0, 0, 0, 0, 0, 0, 28, 1, 0, 0, 0, 0, 0, 0, 3, 111, 110, 101, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 3, 116, 119, 111, 0}, new byte[] {1, 118, 0, 5, 97, 123, 121, 115, 125, 0, 0, 0, 28, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 111, 110, 101, 0, 0, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 116, 119, 111, 0}}, - new object[] {VariantValue.CreateVariant(new VariantValue(new VariantValue[] { new VariantValue((byte)1), new VariantValue((byte)2), new VariantValue((byte)3), new VariantValue((byte)4), new VariantValue((byte)5), new VariantValue((byte)6), new VariantValue((byte)7), new VariantValue((byte)8) })), + new object[] {VariantValue.Variant(VariantValue.Struct((byte)1, (byte)2, (byte)3, (byte)4, (byte)5, (byte)6, (byte)7, (byte)8)), new byte[] {1, 118, 0, 10, 40, 121, 121, 121, 121, 121, 121, 121, 121, 41, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8}, new byte[] {1, 118, 0, 10, 40, 121, 121, 121, 121, 121, 121, 121, 121, 41, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8}}, // Nested variants. // v -> (v) / v -> i - new object[] {VariantValue.CreateVariant(new VariantValue(new VariantValue[] { VariantValue.CreateVariant(new VariantValue(1)) })), + new object[] {VariantValue.Variant(VariantValue.Struct(VariantValue.Variant(1))), new byte[] {1, 118, 0, 3, 40, 118, 41, 0, 1, 105, 0, 0, 0, 0, 0, 1}, new byte[] {1, 118, 0, 3, 40, 118, 41, 0, 1, 105, 0, 0, 1, 0, 0, 0}}, // v -> (v) / v -> v / v -> i - new object[] {VariantValue.CreateVariant(new VariantValue(new VariantValue[] { VariantValue.CreateVariant(VariantValue.CreateVariant(new VariantValue(1))) })), + new object[] {VariantValue.Variant(VariantValue.Struct(VariantValue.Variant(VariantValue.Variant(1)))), new byte[] {1, 118, 0, 3, 40, 118, 41, 0, 1, 118, 0, 1, 105, 0, 0, 0, 0, 0, 0, 1}, new byte[] {1, 118, 0, 3, 40, 118, 41, 0, 1, 118, 0, 1, 105, 0, 0, 0, 1, 0, 0, 0}}, // v -> av / v -> i - new object[] {VariantValue.CreateVariant(new VariantValue(VariantValueType.Variant, new VariantValue[] { 1 })), + new object[] {VariantValue.Variant(VariantValue.Array(Signature.Variant, [ 1 ])), new byte[] {1, 118, 0, 2, 97, 118, 0, 0, 0, 0, 0, 8, 1, 105, 0, 0, 0, 0, 0, 1}, 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 object[] {VariantValue.Variant(VariantValue.Array(Signature.Variant, new VariantValue[] { VariantValue.Variant(1) })), 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 - new object[] {VariantValue.CreateVariant(stringVariantDictionary), + new object[] {VariantValue.Variant(stringVariantDictionary), new byte[] {1, 118, 0, 5, 97, 123, 115, 118, 125, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, 3, 111, 110, 101, 0, 1, 105, 0, 0, 0, 0, 0, 1, 0, 0, 0, 3, 116, 119, 111, 0, 1, 118, 0, 1, 105, 0, 0, 0, 0, 0, 0, 2}, new byte[] {1, 118, 0, 5, 97, 123, 115, 118, 125, 0, 0, 0, 36, 0, 0, 0, 3, 0, 0, 0, 111, 110, 101, 0, 1, 105, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 116, 119, 111, 0, 1, 118, 0, 1, 105, 0, 0, 0, 2, 0, 0, 0}}, diff --git a/test/Tmds.DBus.Protocol.Tests/VariantValueTests.cs b/test/Tmds.DBus.Protocol.Tests/VariantValueTests.cs index 5ec2d4b5..eee30dab 100644 --- a/test/Tmds.DBus.Protocol.Tests/VariantValueTests.cs +++ b/test/Tmds.DBus.Protocol.Tests/VariantValueTests.cs @@ -17,9 +17,9 @@ public class VariantValueTests [InlineData(0, 1)] [InlineData(1, 1)] [InlineData(byte.MaxValue, 1)] - public void Byte(Byte value, byte nesting) + public void Byte(byte value, byte nesting) { - VariantValue vv = nesting > 0 ? new(value, nesting) : new(value); + VariantValue vv = Nest(VariantValue.Byte(value), nesting); UnwrapVariant(ref vv, nesting); Assert.Equal(value, vv.GetByte()); @@ -28,7 +28,7 @@ public void Byte(Byte value, byte nesting) Assert.Equal(VariantValueType.Invalid, vv.ItemType); Assert.Equal(VariantValueType.Invalid, vv.KeyType); Assert.Equal(VariantValueType.Invalid, vv.ValueType); - Assert.Equal("y", vv.Signature); + Assert.Equal("y", vv.GetDBusSignature()); Assert.Equal(-1, vv.Count); } @@ -40,7 +40,7 @@ public void Byte(Byte value, byte nesting) [InlineData(false, 1)] public void Bool(bool value, byte nesting) { - VariantValue vv = nesting > 0 ? new(value, nesting) : new(value); + VariantValue vv = Nest(VariantValue.Bool(value), nesting); UnwrapVariant(ref vv, nesting); Assert.Equal(value, vv.GetBool()); @@ -49,7 +49,7 @@ public void Bool(bool value, byte nesting) Assert.Equal(VariantValueType.Invalid, vv.ItemType); Assert.Equal(VariantValueType.Invalid, vv.KeyType); Assert.Equal(VariantValueType.Invalid, vv.ValueType); - Assert.Equal("b", vv.Signature); + Assert.Equal("b", vv.GetDBusSignature()); Assert.Equal(-1, vv.Count); } @@ -67,7 +67,7 @@ public void Bool(bool value, byte nesting) [InlineData(short.MaxValue, 1)] public void Int16(Int16 value, byte nesting) { - VariantValue vv = nesting > 0 ? new(value, nesting) : new(value); + VariantValue vv = Nest(VariantValue.Int16(value), nesting); UnwrapVariant(ref vv, nesting); Assert.Equal(value, vv.GetInt16()); @@ -76,7 +76,7 @@ public void Int16(Int16 value, byte nesting) Assert.Equal(VariantValueType.Invalid, vv.ItemType); Assert.Equal(VariantValueType.Invalid, vv.KeyType); Assert.Equal(VariantValueType.Invalid, vv.ValueType); - Assert.Equal("n", vv.Signature); + Assert.Equal("n", vv.GetDBusSignature()); Assert.Equal(-1, vv.Count); } @@ -92,7 +92,7 @@ public void Int16(Int16 value, byte nesting) [InlineData(ushort.MaxValue, 1)] public void UInt16(UInt16 value, byte nesting) { - VariantValue vv = nesting > 0 ? new(value, nesting) : new(value); + VariantValue vv = Nest(VariantValue.UInt16(value), nesting); UnwrapVariant(ref vv, nesting); Assert.Equal(value, vv.GetUInt16()); @@ -101,7 +101,7 @@ public void UInt16(UInt16 value, byte nesting) Assert.Equal(VariantValueType.Invalid, vv.ItemType); Assert.Equal(VariantValueType.Invalid, vv.KeyType); Assert.Equal(VariantValueType.Invalid, vv.ValueType); - Assert.Equal("q", vv.Signature); + Assert.Equal("q", vv.GetDBusSignature()); Assert.Equal(-1, vv.Count); } @@ -119,7 +119,7 @@ public void UInt16(UInt16 value, byte nesting) [InlineData(int.MaxValue, 1)] public void Int32(Int32 value, byte nesting) { - VariantValue vv = nesting > 0 ? new(value, nesting) : new(value); + VariantValue vv = Nest(VariantValue.Int32(value), nesting); UnwrapVariant(ref vv, nesting); Assert.Equal(value, vv.GetInt32()); @@ -128,7 +128,7 @@ public void Int32(Int32 value, byte nesting) Assert.Equal(VariantValueType.Invalid, vv.ItemType); Assert.Equal(VariantValueType.Invalid, vv.KeyType); Assert.Equal(VariantValueType.Invalid, vv.ValueType); - Assert.Equal("i", vv.Signature); + Assert.Equal("i", vv.GetDBusSignature()); Assert.Equal(-1, vv.Count); } @@ -144,7 +144,7 @@ public void Int32(Int32 value, byte nesting) [InlineData(uint.MaxValue, 1)] public void UInt32(UInt32 value, byte nesting) { - VariantValue vv = nesting > 0 ? new(value, nesting) : new(value); + VariantValue vv = Nest(VariantValue.UInt32(value), nesting); UnwrapVariant(ref vv, nesting); Assert.Equal(value, vv.GetUInt32()); @@ -153,7 +153,7 @@ public void UInt32(UInt32 value, byte nesting) Assert.Equal(VariantValueType.Invalid, vv.ItemType); Assert.Equal(VariantValueType.Invalid, vv.KeyType); Assert.Equal(VariantValueType.Invalid, vv.ValueType); - Assert.Equal("u", vv.Signature); + Assert.Equal("u", vv.GetDBusSignature()); Assert.Equal(-1, vv.Count); } @@ -176,7 +176,7 @@ public void UInt32(UInt32 value, byte nesting) [InlineData(long.MaxValue, 3)] public void Int64(Int64 value, byte nesting) { - VariantValue vv = nesting > 0 ? new(value, nesting) : new(value); + VariantValue vv = Nest(VariantValue.Int64(value), nesting); UnwrapVariant(ref vv, nesting); Assert.Equal(value, vv.GetInt64()); @@ -185,7 +185,7 @@ public void Int64(Int64 value, byte nesting) Assert.Equal(VariantValueType.Invalid, vv.ItemType); Assert.Equal(VariantValueType.Invalid, vv.KeyType); Assert.Equal(VariantValueType.Invalid, vv.ValueType); - Assert.Equal("x", vv.Signature); + Assert.Equal("x", vv.GetDBusSignature()); Assert.Equal(-1, vv.Count); } @@ -205,7 +205,7 @@ public void Int64(Int64 value, byte nesting) [InlineData(ulong.MaxValue, 3)] public void UInt64(UInt64 value, byte nesting) { - VariantValue vv = nesting > 0 ? new(value, nesting) : new(value); + VariantValue vv = Nest(VariantValue.UInt64(value), nesting); UnwrapVariant(ref vv, nesting); Assert.Equal(value, vv.GetUInt64()); @@ -214,7 +214,7 @@ public void UInt64(UInt64 value, byte nesting) Assert.Equal(VariantValueType.Invalid, vv.ItemType); Assert.Equal(VariantValueType.Invalid, vv.KeyType); Assert.Equal(VariantValueType.Invalid, vv.ValueType); - Assert.Equal("t", vv.Signature); + Assert.Equal("t", vv.GetDBusSignature()); Assert.Equal(-1, vv.Count); } @@ -237,7 +237,7 @@ public void UInt64(UInt64 value, byte nesting) [InlineData(double.MaxValue, 3)] public void Double(Double value, byte nesting) { - VariantValue vv = nesting > 0 ? new(value, nesting) : new(value); + VariantValue vv = Nest(VariantValue.Double(value), nesting); UnwrapVariant(ref vv, nesting); Assert.Equal(value, vv.GetDouble()); @@ -246,7 +246,7 @@ public void Double(Double value, byte nesting) Assert.Equal(VariantValueType.Invalid, vv.ItemType); Assert.Equal(VariantValueType.Invalid, vv.KeyType); Assert.Equal(VariantValueType.Invalid, vv.ValueType); - Assert.Equal("d", vv.Signature); + Assert.Equal("d", vv.GetDBusSignature()); Assert.Equal(-1, vv.Count); } @@ -256,7 +256,7 @@ public void Double(Double value, byte nesting) [InlineData("test", 1)] public void String(String value, byte nesting) { - VariantValue vv = nesting > 0 ? new(value, nesting) : new(value); + VariantValue vv = Nest(VariantValue.String(value), nesting); UnwrapVariant(ref vv, nesting); Assert.Equal(value, vv.GetString()); @@ -265,7 +265,7 @@ public void String(String value, byte nesting) Assert.Equal(VariantValueType.Invalid, vv.ItemType); Assert.Equal(VariantValueType.Invalid, vv.KeyType); Assert.Equal(VariantValueType.Invalid, vv.ValueType); - Assert.Equal("s", vv.Signature); + Assert.Equal("s", vv.GetDBusSignature()); Assert.Equal(-1, vv.Count); } @@ -276,7 +276,7 @@ public void String(String value, byte nesting) public void ObjectPath(string s, byte nesting) { ObjectPath value = new ObjectPath(s); - VariantValue vv = nesting > 0 ? new(value, nesting) : new(value); + VariantValue vv = Nest(VariantValue.ObjectPath(value), nesting); UnwrapVariant(ref vv, nesting); Assert.Equal(s, vv.GetObjectPath()); @@ -285,7 +285,7 @@ public void ObjectPath(string s, byte nesting) Assert.Equal(VariantValueType.Invalid, vv.ItemType); Assert.Equal(VariantValueType.Invalid, vv.KeyType); Assert.Equal(VariantValueType.Invalid, vv.ValueType); - Assert.Equal("o", vv.Signature); + Assert.Equal("o", vv.GetDBusSignature()); Assert.Equal(-1, vv.Count); } @@ -295,8 +295,8 @@ public void ObjectPath(string s, byte nesting) [InlineData("sis", 1)] public void Signature(string s, byte nesting) { - Signature value = new Signature(Encoding.UTF8.GetBytes(s)); - VariantValue vv = nesting > 0 ? new(value, nesting) : new(value); + Signature value = new Signature(Encoding.UTF8.GetBytes(s).AsSpan()); + VariantValue vv = Nest(VariantValue.Signature(value), nesting); UnwrapVariant(ref vv, nesting); Assert.Equal(s, vv.GetSignature().ToString()); @@ -305,7 +305,7 @@ public void Signature(string s, byte nesting) Assert.Equal(VariantValueType.Invalid, vv.ItemType); Assert.Equal(VariantValueType.Invalid, vv.KeyType); Assert.Equal(VariantValueType.Invalid, vv.ValueType); - Assert.Equal("g", vv.Signature); + Assert.Equal("g", vv.GetDBusSignature()); Assert.Equal(-1, vv.Count); } @@ -315,15 +315,14 @@ public void Signature(string s, byte nesting) [InlineData(1)] public void Array(byte nesting) { - VariantValue vv = nesting > 0 ? new VariantValue(new string[] { "1", "2" }, nesting) - : new VariantValue(new string[] { "1", "2" }); + VariantValue vv = Nest(VariantValue.Array(new string[] { "1", "2" }), nesting); UnwrapVariant(ref vv, nesting); Assert.Equal(VariantValueType.Array, vv.Type); Assert.Equal(VariantValueType.String, vv.ItemType); Assert.Equal(VariantValueType.Invalid, vv.KeyType); Assert.Equal(VariantValueType.Invalid, vv.ValueType); - Assert.Equal("as", vv.Signature); + Assert.Equal("as", vv.GetDBusSignature()); Assert.Equal(2, vv.Count); @@ -338,15 +337,14 @@ public void Array(byte nesting) [InlineData(1)] public void ArrayOfByte(byte nesting) { - VariantValue vv = nesting > 0 ? new VariantValue(new byte[] { 1, 2 }, nesting) - : new VariantValue(new byte[] { 1, 2 }); + VariantValue vv = Nest(VariantValue.Array(new byte[] { 1, 2 }), nesting); UnwrapVariant(ref vv, nesting); Assert.Equal(VariantValueType.Array, vv.Type); Assert.Equal(VariantValueType.Byte, vv.ItemType); Assert.Equal(VariantValueType.Invalid, vv.KeyType); Assert.Equal(VariantValueType.Invalid, vv.ValueType); - Assert.Equal("ay", vv.Signature); + Assert.Equal("ay", vv.GetDBusSignature()); Assert.Equal(2, vv.Count); @@ -361,15 +359,14 @@ public void ArrayOfByte(byte nesting) [InlineData(1)] public void ArrayOfInt16(byte nesting) { - VariantValue vv = nesting > 0 ? new VariantValue(new short[] { 1, 2 }, nesting) - : new VariantValue(new short[] { 1, 2 }); + VariantValue vv = Nest(VariantValue.Array(new short[] { 1, 2 }), nesting); UnwrapVariant(ref vv, nesting); Assert.Equal(VariantValueType.Array, vv.Type); Assert.Equal(VariantValueType.Int16, vv.ItemType); Assert.Equal(VariantValueType.Invalid, vv.KeyType); Assert.Equal(VariantValueType.Invalid, vv.ValueType); - Assert.Equal("an", vv.Signature); + Assert.Equal("an", vv.GetDBusSignature()); Assert.Equal(2, vv.Count); @@ -384,15 +381,14 @@ public void ArrayOfInt16(byte nesting) [InlineData(1)] public void ArrayOfUInt16(byte nesting) { - VariantValue vv = nesting > 0 ? new VariantValue(new ushort[] { 1, 2 }, nesting) - : new VariantValue(new ushort[] { 1, 2 }); + VariantValue vv = Nest(VariantValue.Array(new ushort[] { 1, 2 }), nesting); UnwrapVariant(ref vv, nesting); Assert.Equal(VariantValueType.Array, vv.Type); Assert.Equal(VariantValueType.UInt16, vv.ItemType); Assert.Equal(VariantValueType.Invalid, vv.KeyType); Assert.Equal(VariantValueType.Invalid, vv.ValueType); - Assert.Equal("aq", vv.Signature); + Assert.Equal("aq", vv.GetDBusSignature()); Assert.Equal(2, vv.Count); @@ -407,15 +403,14 @@ public void ArrayOfUInt16(byte nesting) [InlineData(1)] public void ArrayOfInt32(byte nesting) { - VariantValue vv = nesting > 0 ? new VariantValue(new int[] { 1, 2 }, nesting) - : new VariantValue(new int[] { 1, 2 }); + VariantValue vv = Nest(VariantValue.Array(new int[] { 1, 2 }), nesting); UnwrapVariant(ref vv, nesting); Assert.Equal(VariantValueType.Array, vv.Type); Assert.Equal(VariantValueType.Int32, vv.ItemType); Assert.Equal(VariantValueType.Invalid, vv.KeyType); Assert.Equal(VariantValueType.Invalid, vv.ValueType); - Assert.Equal("ai", vv.Signature); + Assert.Equal("ai", vv.GetDBusSignature()); Assert.Equal(2, vv.Count); @@ -430,15 +425,14 @@ public void ArrayOfInt32(byte nesting) [InlineData(1)] public void ArrayOfUInt32(byte nesting) { - VariantValue vv = nesting > 0 ? new VariantValue(new uint[] { 1, 2 }, nesting) - : new VariantValue(new uint[] { 1, 2 }); + VariantValue vv = Nest(VariantValue.Array(new uint[] { 1, 2 }), nesting); UnwrapVariant(ref vv, nesting); Assert.Equal(VariantValueType.Array, vv.Type); Assert.Equal(VariantValueType.UInt32, vv.ItemType); Assert.Equal(VariantValueType.Invalid, vv.KeyType); Assert.Equal(VariantValueType.Invalid, vv.ValueType); - Assert.Equal("au", vv.Signature); + Assert.Equal("au", vv.GetDBusSignature()); Assert.Equal(2, vv.Count); @@ -453,15 +447,14 @@ public void ArrayOfUInt32(byte nesting) [InlineData(1)] public void ArrayOfInt64(byte nesting) { - VariantValue vv = nesting > 0 ? new VariantValue(new long[] { 1, 2 }, nesting) - : new VariantValue(new long[] { 1, 2 });; + VariantValue vv = Nest(VariantValue.Array(new long[] { 1, 2 }), nesting); UnwrapVariant(ref vv, nesting); Assert.Equal(VariantValueType.Array, vv.Type); Assert.Equal(VariantValueType.Int64, vv.ItemType); Assert.Equal(VariantValueType.Invalid, vv.KeyType); Assert.Equal(VariantValueType.Invalid, vv.ValueType); - Assert.Equal("ax", vv.Signature); + Assert.Equal("ax", vv.GetDBusSignature()); Assert.Equal(2, vv.Count); @@ -476,15 +469,14 @@ public void ArrayOfInt64(byte nesting) [InlineData(1)] public void ArrayOfUInt64(byte nesting) { - VariantValue vv = nesting > 0 ? new VariantValue(new ulong[] { 1, 2 }, nesting) - : new VariantValue(new ulong[] { 1, 2 }); + VariantValue vv = Nest(VariantValue.Array(new ulong[] { 1, 2 }), nesting); UnwrapVariant(ref vv, nesting); Assert.Equal(VariantValueType.Array, vv.Type); Assert.Equal(VariantValueType.UInt64, vv.ItemType); Assert.Equal(VariantValueType.Invalid, vv.KeyType); Assert.Equal(VariantValueType.Invalid, vv.ValueType); - Assert.Equal("at", vv.Signature); + Assert.Equal("at", vv.GetDBusSignature()); Assert.Equal(2, vv.Count); @@ -502,15 +494,14 @@ public void ArrayOfDouble(byte nesting) double d1 = Math.PI; double d2 = Math.E; - VariantValue vv = nesting > 0 ? new VariantValue(new double[] { d1, d2 }, nesting) - : new VariantValue(new double[] { d1, d2 }); + VariantValue vv = Nest(VariantValue.Array(new [] { d1, d2 }), nesting); UnwrapVariant(ref vv, nesting); Assert.Equal(VariantValueType.Array, vv.Type); Assert.Equal(VariantValueType.Double, vv.ItemType); Assert.Equal(VariantValueType.Invalid, vv.KeyType); Assert.Equal(VariantValueType.Invalid, vv.ValueType); - Assert.Equal("ad", vv.Signature); + Assert.Equal("ad", vv.GetDBusSignature()); Assert.Equal(2, vv.Count); @@ -520,21 +511,174 @@ public void ArrayOfDouble(byte nesting) Assert.Equal(new double[] { d1, d2 }, vv.GetArray()); } + [Fact] + public void ArrayOfArrayOfInt() + { + VariantValue vv = VariantValue.Array("ai"u8, new [] { VariantValue.Array(new int[] { 5, 8 }) }); + + Assert.Equal(VariantValueType.Array, vv.Type); + Assert.Equal(VariantValueType.Array, vv.ItemType); + Assert.Equal(VariantValueType.Invalid, vv.KeyType); + Assert.Equal(VariantValueType.Invalid, vv.ValueType); + Assert.Equal("aai", vv.GetDBusSignature()); + + Assert.Equal(1, vv.Count); + + vv = vv.GetItem(0); + + Assert.Equal(VariantValueType.Array, vv.Type); + Assert.Equal(VariantValueType.Int32, vv.ItemType); + Assert.Equal(VariantValueType.Invalid, vv.KeyType); + Assert.Equal(VariantValueType.Invalid, vv.ValueType); + Assert.Equal("ai", vv.GetDBusSignature()); + + Assert.Equal(2, vv.Count); + + Assert.Equal(5, vv.GetItem(0).GetInt32()); + Assert.Equal(8, vv.GetItem(1).GetInt32()); + + Assert.Equal(new int[] { 5, 8 }, vv.GetArray()); + } + + [Fact] + public void ArrayOfStructWithVariantFields() + { + VariantValue vv = VariantValue.Array("(yvns)"u8, new [] { VariantValue.Struct(VariantValue.Byte(1), VariantValue.Variant(VariantValue.Int32(2)), VariantValue.Int16(3), VariantValue.String("string")) }); + + Assert.Equal(VariantValueType.Array, vv.Type); + Assert.Equal(VariantValueType.Struct, vv.ItemType); + Assert.Equal(VariantValueType.Invalid, vv.KeyType); + Assert.Equal(VariantValueType.Invalid, vv.ValueType); + Assert.Equal("a(yvns)", vv.GetDBusSignature()); + + Assert.Equal(1, vv.Count); + + vv = vv.GetItem(0); + + Assert.Equal(VariantValueType.Struct, vv.Type); + Assert.Equal(VariantValueType.Invalid, vv.ItemType); + Assert.Equal(VariantValueType.Invalid, vv.KeyType); + Assert.Equal(VariantValueType.Invalid, vv.ValueType); + Assert.Equal("(yvns)", vv.GetDBusSignature()); // Variant field is reported as variant. + + Assert.Equal(4, vv.Count); + + Assert.Equal(1, vv.GetItem(0).GetByte()); + Assert.Equal(2, vv.GetItem(1).GetInt32()); // Variant field value is unwrapped. + Assert.Equal(3, vv.GetItem(2).GetInt16()); + Assert.Equal("string", vv.GetItem(3).GetString()); + } + + [Fact] + public void ArrayOfNestings() + { + var item1 = KeyValuePair.Create(VariantValue.Byte(1), VariantValue.Struct(VariantValue.String("one"))); + VariantValue vv = VariantValue.Array("((y)a{y(s)}a(i))"u8, new [] { + VariantValue.Struct( + VariantValue.Struct(VariantValue.Byte(1)), + VariantValue.Dictionary(DBusType.Byte, "(s)"u8, new[] { item1 }), + VariantValue.Array("(i)"u8, new[] { VariantValue.Struct(1) })) }); + + Assert.Equal(VariantValueType.Array, vv.Type); + Assert.Equal(VariantValueType.Struct, vv.ItemType); + Assert.Equal(VariantValueType.Invalid, vv.KeyType); + Assert.Equal(VariantValueType.Invalid, vv.ValueType); + Assert.Equal("a((y)a{y(s)}a(i))", vv.GetDBusSignature()); + + Assert.Equal(1, vv.Count); + } + + [Fact] + public void ArrayOfEmptyNestings() + { + VariantValue vv = VariantValue.Array("((y)a{y(s)}a(i))"u8, new [] { + VariantValue.Struct( + VariantValue.Struct(VariantValue.Byte(1)), + VariantValue.Dictionary(DBusType.Byte, "(s)"u8, []), + VariantValue.Array("(i)"u8, [ ])) }); + + Assert.Equal(VariantValueType.Array, vv.Type); + Assert.Equal(VariantValueType.Struct, vv.ItemType); + Assert.Equal(VariantValueType.Invalid, vv.KeyType); + Assert.Equal(VariantValueType.Invalid, vv.ValueType); + Assert.Equal("a((y)a{y(s)}a(i))", vv.GetDBusSignature()); + + Assert.Equal(1, vv.Count); + } + + [Fact] + public void ArrayOfDictionary() + { + var item1 = KeyValuePair.Create(VariantValue.Byte(1), VariantValue.String("one")); + var item2 = KeyValuePair.Create(VariantValue.Byte(2), VariantValue.String("two")); + VariantValue dict = VariantValue.Dictionary(DBusType.Byte, Tmds.DBus.Protocol.Signature.String, new[] { item1, item2 }); + + VariantValue vv = VariantValue.Array("a{ys}"u8, new [] { dict }); + + Assert.Equal(VariantValueType.Array, vv.Type); + Assert.Equal(VariantValueType.Dictionary, vv.ItemType); + Assert.Equal(VariantValueType.Invalid, vv.KeyType); + Assert.Equal(VariantValueType.Invalid, vv.ValueType); + Assert.Equal("aa{ys}", vv.GetDBusSignature()); + + Assert.Equal(1, vv.Count); + } + + [Fact] + public void EmptyArrayOfArrayOfInt() + { + VariantValue vv = VariantValue.Array("ai"u8, []); + + Assert.Equal(VariantValueType.Array, vv.Type); + Assert.Equal(VariantValueType.Array, vv.ItemType); + Assert.Equal(VariantValueType.Invalid, vv.KeyType); + Assert.Equal(VariantValueType.Invalid, vv.ValueType); + Assert.Equal("aai", vv.GetDBusSignature()); + + Assert.Equal(0, vv.Count); + } + + [Fact] + public void EmptyArrayOfStructWithVariantFields() + { + VariantValue vv = VariantValue.Array("(yvns)"u8, []); + + Assert.Equal(VariantValueType.Array, vv.Type); + Assert.Equal(VariantValueType.Struct, vv.ItemType); + Assert.Equal(VariantValueType.Invalid, vv.KeyType); + Assert.Equal(VariantValueType.Invalid, vv.ValueType); + Assert.Equal("a(yvns)", vv.GetDBusSignature()); + + Assert.Equal(0, vv.Count); + } + + [Fact] + public void EmptyArrayOfDictionary() + { + VariantValue vv = VariantValue.Array("a{ys}"u8, []); + + Assert.Equal(VariantValueType.Array, vv.Type); + Assert.Equal(VariantValueType.Dictionary, vv.ItemType); + Assert.Equal(VariantValueType.Invalid, vv.KeyType); + Assert.Equal(VariantValueType.Invalid, vv.ValueType); + Assert.Equal("aa{ys}", vv.GetDBusSignature()); + + Assert.Equal(0, vv.Count); + } + [Theory] [InlineData(0)] [InlineData(1)] public void Struct(byte nesting) { - VariantValue vv = nesting > 0 - ? new VariantValue(new VariantValue[] { new VariantValue((byte)1), new VariantValue("string") }, nesting) - : new VariantValue(new VariantValue[] { new VariantValue((byte)1), new VariantValue("string") }); + VariantValue vv = Nest(VariantValue.Struct(VariantValue.Byte(1), VariantValue.String("string")), nesting); UnwrapVariant(ref vv, nesting); Assert.Equal(VariantValueType.Struct, vv.Type); Assert.Equal(VariantValueType.Invalid, vv.ItemType); Assert.Equal(VariantValueType.Invalid, vv.KeyType); Assert.Equal(VariantValueType.Invalid, vv.ValueType); - Assert.Equal("(ys)", vv.Signature); + Assert.Equal("(ys)", vv.GetDBusSignature()); Assert.Equal(2, vv.Count); @@ -545,13 +689,13 @@ public void Struct(byte nesting) [Fact] public void StructWithVariantFields() { - VariantValue vv = new VariantValue(new VariantValue[] { new VariantValue((byte)1), VariantValue.CreateVariant(2), new VariantValue((short)3), new VariantValue("string") }); + VariantValue vv = VariantValue.Struct(VariantValue.Byte(1), VariantValue.Variant(VariantValue.Int32(2)), VariantValue.Int16(3), VariantValue.String("string")); Assert.Equal(VariantValueType.Struct, vv.Type); Assert.Equal(VariantValueType.Invalid, vv.ItemType); Assert.Equal(VariantValueType.Invalid, vv.KeyType); Assert.Equal(VariantValueType.Invalid, vv.ValueType); - Assert.Equal("(yvns)", vv.Signature); // Variant field is reported as variant. + Assert.Equal("(yvns)", vv.GetDBusSignature()); // Variant field is reported as variant. Assert.Equal(4, vv.Count); @@ -566,18 +710,16 @@ public void StructWithVariantFields() [InlineData(1)] public void Dictionary(byte nesting) { - var item1 = KeyValuePair.Create(new VariantValue((byte)1), new VariantValue("one")); - var item2 = KeyValuePair.Create(new VariantValue((byte)2), new VariantValue("two")); - VariantValue vv = nesting > 0 - ? new VariantValue(VariantValueType.Byte, VariantValueType.String, valueSignature: null, new[] { item1, item2 }, nesting) - : new VariantValue(VariantValueType.Byte, VariantValueType.String, new[] { item1, item2 }); + var item1 = KeyValuePair.Create(VariantValue.Byte(1), VariantValue.String("one")); + var item2 = KeyValuePair.Create(VariantValue.Byte(2), VariantValue.String("two")); + VariantValue vv = Nest(VariantValue.Dictionary(DBusType.Byte, Tmds.DBus.Protocol.Signature.String, new[] { item1, item2 }), nesting); UnwrapVariant(ref vv, nesting); Assert.Equal(VariantValueType.Dictionary, vv.Type); Assert.Equal(VariantValueType.Invalid, vv.ItemType); Assert.Equal(VariantValueType.Byte, vv.KeyType); Assert.Equal(VariantValueType.String, vv.ValueType); - Assert.Equal("a{ys}", vv.Signature); + Assert.Equal("a{ys}", vv.GetDBusSignature()); Assert.Equal(2, vv.Count); @@ -593,6 +735,12 @@ public void Dictionary(byte nesting) Assert.Equal(new Dictionary() { { 1, "one"}, { 2, "two" } }, dict); } + [Fact] + public void TypeMismatchThrows() + { + Assert.Throws(() => VariantValue.Array("ai"u8, new [] { VariantValue.Array(new byte[] { 5, 8 }) })); + } + [Theory] [InlineData(0)] [InlineData(1)] @@ -617,11 +765,20 @@ public void UnixFd(byte nesting) Assert.Equal(VariantValueType.Invalid, vv.ItemType); Assert.Equal(VariantValueType.Invalid, vv.KeyType); Assert.Equal(VariantValueType.Invalid, vv.ValueType); - Assert.Equal("h", vv.Signature); + Assert.Equal("h", vv.GetDBusSignature()); Assert.Equal(-1, vv.Count); } + private static VariantValue Nest(VariantValue vv, byte nesting) + { + for (int i = 0; i < nesting; i++) + { + vv = VariantValue.Variant(vv); + } + return vv; + } + private static void UnwrapVariant(ref VariantValue vv, byte nesting) { for (int i = 0; i < nesting; i++) @@ -630,7 +787,7 @@ private static void UnwrapVariant(ref VariantValue vv, byte nesting) Assert.Equal(VariantValueType.Invalid, vv.ItemType); Assert.Equal(VariantValueType.Invalid, vv.KeyType); Assert.Equal(VariantValueType.Invalid, vv.ValueType); - Assert.Equal("v", vv.Signature); + Assert.Equal("v", vv.GetDBusSignature()); vv = vv.GetVariantValue(); }