1
// Copyright 2006 Alp Toker <alp@atoker.com>
2
// This software is made available under the MIT License
3
// See COPYING for details
8
using System.Collections.Generic;
9
//TODO: Reflection should be done at a higher level than this class
10
using System.Reflection;
14
//maybe this should be nullable?
17
//TODO: this class needs some work
18
//Data should probably include the null terminator
20
public static readonly Signature Empty = new Signature (String.Empty);
21
public static readonly Signature ByteSig = Allocate (DType.Byte);
22
public static readonly Signature UInt16Sig = Allocate (DType.UInt16);
23
public static readonly Signature UInt32Sig = Allocate (DType.UInt32);
24
public static readonly Signature StringSig = Allocate (DType.String);
25
public static readonly Signature ObjectPathSig = Allocate (DType.ObjectPath);
26
public static readonly Signature SignatureSig = Allocate (DType.Signature);
27
public static readonly Signature VariantSig = Allocate (DType.Variant);
29
public static bool operator == (Signature a, Signature b)
40
if (a.data.Length != b.data.Length)
43
for (int i = 0 ; i != a.data.Length ; i++)
44
if (a.data[i] != b.data[i])
50
public static bool operator != (Signature a, Signature b)
55
public override bool Equals (object o)
60
if (!(o is Signature))
63
return this == (Signature)o;
66
public override int GetHashCode ()
68
// TODO: Avoid string conversion
69
return Value.GetHashCode ();
72
public static Signature operator + (Signature s1, Signature s2)
74
return Concat (s1, s2);
77
public static Signature Concat (Signature s1, Signature s2)
79
if (s1.data == null && s2.data == null)
80
return Signature.Empty;
88
if (s1.Length + s2.Length == 0)
89
return Signature.Empty;
91
byte[] data = new byte[s1.data.Length + s2.data.Length];
92
s1.data.CopyTo (data, 0);
93
s2.data.CopyTo (data, s1.data.Length);
94
return Signature.Take (data);
97
public Signature (string value)
99
if (value.Length == 0) {
100
this.data = Empty.data;
104
if (value.Length == 1) {
105
this.data = DataForDType ((DType)value[0]);
109
this.data = Encoding.ASCII.GetBytes (value);
112
internal static Signature Take (byte[] value)
116
if (value.Length == 0) {
117
sig.data = Empty.data;
121
if (value.Length == 1) {
122
sig.data = DataForDType ((DType)value[0]);
130
static byte[] DataForDType (DType value)
132
// Reduce heap allocations.
133
// For now, we only cache the most common protocol signatures.
138
return UInt16Sig.data;
140
return UInt32Sig.data;
142
return StringSig.data;
143
case DType.ObjectPath:
144
return ObjectPathSig.data;
145
case DType.Signature:
146
return SignatureSig.data;
148
return VariantSig.data;
150
return new byte[] {(byte)value};
154
private static Signature Allocate (DType value)
157
sig.data = new byte[] {(byte)value};
161
internal Signature (DType value)
163
this.data = DataForDType (value);
166
internal Signature (DType[] value)
168
if (value.Length == 0) {
169
this.data = Empty.data;
173
if (value.Length == 1) {
174
this.data = DataForDType (value[0]);
178
this.data = new byte[value.Length];
180
for (int i = 0 ; i != value.Length ; i++)
181
this.data[i] = (byte)value[i];
186
//TODO: this should be private, but MessageWriter and Monitor still use it
188
public byte[] GetBuffer ()
193
internal DType this[int index]
196
return (DType)data[index];
211
//FIXME: hack to handle bad case when Data is null
215
return Encoding.ASCII.GetString (data);
219
public override string ToString ()
224
StringBuilder sb = new StringBuilder ();
226
foreach (DType t in data) {
227
//we shouldn't rely on object mapping here, but it's an easy way to get string representations for now
228
Type type = DTypeToType (t);
230
sb.Append (type.Name);
233
if (!Char.IsControl (c))
236
sb.Append (@"\" + (int)c);
241
return sb.ToString ();
245
public Signature MakeArraySignature ()
247
return new Signature (DType.Array) + this;
250
public static Signature MakeStruct (params Signature[] elems)
252
Signature sig = Signature.Empty;
254
sig += new Signature (DType.StructBegin);
256
foreach (Signature elem in elems)
259
sig += new Signature (DType.StructEnd);
264
public static Signature MakeDictEntry (Signature keyType, Signature valueType)
266
Signature sig = Signature.Empty;
268
sig += new Signature (DType.DictEntryBegin);
273
sig += new Signature (DType.DictEntryEnd);
278
public static Signature MakeDict (Signature keyType, Signature valueType)
280
return MakeDictEntry (keyType, valueType).MakeArraySignature ();
286
if (data.Length == 0)
289
return Protocol.GetAlignment (this[0]);
293
static int GetSize (DType dtype)
310
case DType.Single: //Not yet supported!
316
case DType.ObjectPath:
317
case DType.Signature:
319
case DType.StructBegin:
321
case DType.DictEntryBegin:
325
throw new Exception ("Cannot determine size of " + dtype);
329
public bool GetFixedSize (ref int size)
334
if (data.Length == 0)
338
size = Protocol.Padded (size, Alignment);
340
if (data.Length == 1) {
341
int valueSize = GetSize (this[0]);
351
foreach (Signature sig in GetParts ())
352
if (!sig.GetFixedSize (ref size))
357
if (IsArray || IsDict)
361
foreach (Signature sig in GetFieldSignatures ())
362
if (!sig.GetFixedSize (ref size))
368
throw new Exception ();
371
public bool IsFixedSize
374
if (data.Length == 0)
377
if (data.Length == 1) {
378
int size = GetSize (this[0]);
383
foreach (Signature sig in GetParts ())
384
if (!sig.IsFixedSize)
389
if (IsArray || IsDict)
393
foreach (Signature sig in GetFieldSignatures ())
394
if (!sig.IsFixedSize)
400
throw new Exception ();
404
//TODO: complete this
405
public bool IsPrimitive
408
if (data.Length != 1)
411
if (this[0] == DType.Variant)
414
if (this[0] == DType.Invalid)
427
if (this[0] != DType.StructBegin)
430
// FIXME: Incorrect! What if this is in fact a Structlike starting and finishing with structs?
431
if (this[Length - 1] != DType.StructEnd)
438
public bool IsDictEntry
444
if (this[0] != DType.DictEntryBegin)
447
// FIXME: Incorrect! What if this is in fact a Structlike starting and finishing with structs?
448
if (this[Length - 1] != DType.DictEntryEnd)
455
public bool IsStructlike
484
if (this[1] != DType.DictEntryBegin)
497
if (this[0] != DType.Array)
504
public Signature GetElementSignature ()
507
throw new Exception ("Cannot get the element signature of a non-array (signature was '" + this + "')");
511
// throw new NotSupportedException ("Parsing dict signature is not supported (signature was '" + this + "')");
515
return GetNextSignature (ref pos);
518
public Type[] ToTypes ()
520
// TODO: Find a way to avoid these null checks everywhere.
522
return Type.EmptyTypes;
524
List<Type> types = new List<Type> ();
525
for (int i = 0 ; i != data.Length ; types.Add (ToType (ref i)));
526
return types.ToArray ();
529
public Type ToType ()
532
Type ret = ToType (ref pos);
533
if (pos != data.Length)
534
throw new Exception ("Signature '" + Value + "' is not a single complete type");
538
internal static DType TypeCodeToDType (TypeCode typeCode)
543
return DType.Invalid;
544
case TypeCode.Object:
545
return DType.Invalid;
546
case TypeCode.DBNull:
547
return DType.Invalid;
548
case TypeCode.Boolean:
549
return DType.Boolean;
558
case TypeCode.UInt16:
562
case TypeCode.UInt32:
566
case TypeCode.UInt64:
568
case TypeCode.Single:
570
case TypeCode.Double:
572
case TypeCode.Decimal:
573
return DType.Invalid;
574
case TypeCode.DateTime:
575
return DType.Invalid;
576
case TypeCode.String:
579
return DType.Invalid;
583
//FIXME: this method is bad, get rid of it
584
internal static DType TypeToDType (Type type)
586
if (type == typeof (void))
587
return DType.Invalid;
589
if (type == typeof (string))
592
if (type == typeof (ObjectPath))
593
return DType.ObjectPath;
595
if (type == typeof (Signature))
596
return DType.Signature;
598
if (type == typeof (object))
599
return DType.Variant;
601
if (type.IsPrimitive)
602
return TypeCodeToDType (Type.GetTypeCode (type));
605
return TypeToDType (Enum.GetUnderlyingType (type));
611
//if (type.UnderlyingSystemType != null)
612
// return TypeToDType (type.UnderlyingSystemType);
613
if (Mapper.IsPublic (type))
614
return DType.ObjectPath;
616
if (!type.IsPrimitive && !type.IsEnum)
619
//TODO: maybe throw an exception here
620
return DType.Invalid;
624
public static DType TypeToDType (Type type)
627
return DType.Invalid;
628
else if (type == typeof (byte))
630
else if (type == typeof (bool))
631
return DType.Boolean;
632
else if (type == typeof (short))
634
else if (type == typeof (ushort))
636
else if (type == typeof (int))
638
else if (type == typeof (uint))
640
else if (type == typeof (long))
642
else if (type == typeof (ulong))
644
else if (type == typeof (float)) //not supported by libdbus at time of writing
646
else if (type == typeof (double))
648
else if (type == typeof (string))
650
else if (type == typeof (ObjectPath))
651
return DType.ObjectPath;
652
else if (type == typeof (Signature))
653
return DType.Signature;
655
return DType.Invalid;
659
public IEnumerable<Signature> GetFieldSignatures ()
661
if (this == Signature.Empty || this[0] != DType.StructBegin)
662
throw new Exception ("Not a struct");
664
for (int pos = 1 ; pos < data.Length - 1 ;)
665
yield return GetNextSignature (ref pos);
668
public void GetDictEntrySignatures (out Signature sigKey, out Signature sigValue)
670
if (this == Signature.Empty || this[0] != DType.DictEntryBegin)
671
throw new Exception ("Not a DictEntry");
674
sigKey = GetNextSignature (ref pos);
675
sigValue = GetNextSignature (ref pos);
678
public IEnumerable<Signature> GetParts ()
682
for (int pos = 0 ; pos < data.Length ;) {
683
yield return GetNextSignature (ref pos);
687
public Signature GetNextSignature (ref int pos)
690
return Signature.Empty;
692
DType dtype = (DType)data[pos++];
695
//case DType.Invalid:
696
// return typeof (void);
698
//peek to see if this is in fact a dictionary
699
if ((DType)data[pos] == DType.DictEntryBegin) {
702
Signature keyType = GetNextSignature (ref pos);
703
Signature valueType = GetNextSignature (ref pos);
706
return Signature.MakeDict (keyType, valueType);
708
Signature elementType = GetNextSignature (ref pos);
709
return elementType.MakeArraySignature ();
711
//case DType.DictEntryBegin: // FIXME: DictEntries should be handled separately.
712
case DType.StructBegin:
713
//List<Signature> fieldTypes = new List<Signature> ();
714
Signature fieldsSig = Signature.Empty;
715
while ((DType)data[pos] != DType.StructEnd)
716
fieldsSig += GetNextSignature (ref pos);
719
return Signature.MakeStruct (fieldsSig);
721
case DType.DictEntryBegin:
722
Signature sigKey = GetNextSignature (ref pos);
723
Signature sigValue = GetNextSignature (ref pos);
726
return Signature.MakeDictEntry (sigKey, sigValue);
728
return new Signature (dtype);
732
public Type ToType (ref int pos)
734
// TODO: Find a way to avoid these null checks everywhere.
736
return typeof (void);
738
DType dtype = (DType)data[pos++];
742
return typeof (void);
744
return typeof (byte);
746
return typeof (bool);
748
return typeof (short);
750
return typeof (ushort);
754
return typeof (uint);
756
return typeof (long);
758
return typeof (ulong);
759
case DType.Single: ////not supported by libdbus at time of writing
760
return typeof (float);
762
return typeof (double);
764
return typeof (string);
765
case DType.ObjectPath:
766
return typeof (ObjectPath);
767
case DType.Signature:
768
return typeof (Signature);
770
//peek to see if this is in fact a dictionary
771
if ((DType)data[pos] == DType.DictEntryBegin) {
774
Type keyType = ToType (ref pos);
775
Type valueType = ToType (ref pos);
778
//return typeof (IDictionary<,>).MakeGenericType (new Type[] {keyType, valueType});
779
//workaround for Mono bug #81035 (memory leak)
780
return Mapper.GetGenericType (typeof (IDictionary<,>), new Type[] {keyType, valueType});
782
return ToType (ref pos).MakeArrayType ();
785
return typeof (ValueType);
786
case DType.DictEntry:
787
return typeof (System.Collections.Generic.KeyValuePair<,>);
789
return typeof (object);
791
throw new NotSupportedException ("Parsing or converting this signature is not yet supported (signature was '" + this + "'), at DType." + dtype);
795
public static Signature GetSig (object[] objs)
797
return GetSig (Type.GetTypeArray (objs));
800
public static Signature GetSig (Type[] types)
803
throw new ArgumentNullException ("types");
805
Signature sig = Signature.Empty;
807
foreach (Type type in types)
808
sig += GetSig (type);
813
public static Signature GetSig (Type type)
816
throw new ArgumentNullException ("type");
818
//this is inelegant, but works for now
819
if (type == typeof (Signature))
820
return new Signature (DType.Signature);
822
if (type == typeof (ObjectPath))
823
return new Signature (DType.ObjectPath);
825
if (type == typeof (void))
826
return Signature.Empty;
828
if (type == typeof (string))
829
return new Signature (DType.String);
831
if (type == typeof (object))
832
return new Signature (DType.Variant);
835
return GetSig (type.GetElementType ()).MakeArraySignature ();
837
if (type.IsGenericType && (type.GetGenericTypeDefinition () == typeof (IDictionary<,>) || type.GetGenericTypeDefinition () == typeof (Dictionary<,>))) {
839
Type[] genArgs = type.GetGenericArguments ();
840
return Signature.MakeDict (GetSig (genArgs[0]), GetSig (genArgs[1]));
843
if (Mapper.IsPublic (type)) {
844
return new Signature (DType.ObjectPath);
847
if (!type.IsPrimitive && !type.IsEnum) {
848
Signature sig = Signature.Empty;
850
foreach (FieldInfo fi in type.GetFields (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
851
sig += GetSig (fi.FieldType);
853
return Signature.MakeStruct (sig);
856
DType dtype = Signature.TypeToDType (type);
857
return new Signature (dtype);
869
Invalid = (byte)'\0',
879
Single = (byte)'f', //This is not yet supported!
882
ObjectPath = (byte)'o',
883
Signature = (byte)'g',
886
[Obsolete ("Not in protocol")]
888
[Obsolete ("Not in protocol")]
889
DictEntry = (byte)'e',
892
StructBegin = (byte)'(',
893
StructEnd = (byte)')',
894
DictEntryBegin = (byte)'{',
895
DictEntryEnd = (byte)'}',