1
// Copyright 2006 Alp Toker <alp@atoker.com>
2
// This software is made available under the MIT License
3
// See COPYING for details
7
using System.Collections.Generic;
9
using System.Reflection;
13
partial class MessageReader
15
protected EndianFlag endianness;
16
//protected byte[] data;
18
//TODO: this should be uint or long to handle long messages
19
//internal int pos = 0;
21
protected Message message;
23
public MessageReader (EndianFlag endianness, byte[] data)
26
// throw new ArgumentNullException ("data");
30
this.endianness = endianness;
31
this.IsNativeEndian = endianness == Connection.NativeEndianness;
35
public MessageReader (Message message) : this (message.Header.Endianness, message.Body)
38
throw new ArgumentNullException ("message");
40
this.message = message;
43
public readonly bool IsNativeEndian;
45
public object ReadValue (Type type)
47
if (type == typeof (void))
51
return ReadArray (type.GetElementType ());
52
} else if (type == typeof (ObjectPath)) {
53
return ReadObjectPath ();
54
} else if (type == typeof (Signature)) {
55
return ReadSignature ();
56
} else if (type == typeof (object)) {
57
return ReadVariant ();
58
} else if (type == typeof (string)) {
60
} else if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (IDictionary<,>)) {
61
Type[] genArgs = type.GetGenericArguments ();
62
//Type dictType = typeof (Dictionary<,>).MakeGenericType (genArgs);
63
//workaround for Mono bug #81035 (memory leak)
64
Type dictType = Mapper.GetGenericType (typeof (Dictionary<,>), genArgs);
65
System.Collections.IDictionary idict = (System.Collections.IDictionary)Activator.CreateInstance(dictType, new object[0]);
66
GetValueToDict (genArgs[0], genArgs[1], idict);
68
} else if (Mapper.IsPublic (type)) {
69
return GetObject (type);
70
} else if (!type.IsPrimitive && !type.IsEnum) {
71
return ReadStruct (type);
74
DType dtype = Signature.TypeToDType (type);
75
val = ReadValue (dtype);
78
val = Enum.ToObject (type, val);
84
//helper method, should not be used generally
85
public object ReadValue (DType dtype)
93
return ReadBoolean ();
105
return ReadUInt32 ();
111
return ReadUInt64 ();
115
return ReadSingle ();
119
return ReadDouble ();
122
return ReadString ();
124
case DType.ObjectPath:
125
return ReadObjectPath ();
127
case DType.Signature:
128
return ReadSignature ();
131
return ReadVariant ();
134
throw new Exception ("Unhandled D-Bus type: " + dtype);
138
public object GetObject (Type type)
140
ObjectPath path = ReadObjectPath ();
142
return message.Connection.GetObject (type, (string)message.Header[FieldCode.Sender], path);
145
public byte ReadByte ()
150
public bool ReadBoolean ()
152
uint intval = ReadUInt32 ();
160
throw new Exception ("Read value " + intval + " at position " + pos + " while expecting boolean (0/1)");
164
unsafe protected void MarshalUShort (void* dstPtr)
168
if (data.Length < pos + 2)
169
throw new Exception ("Cannot read beyond end of data");
171
if (endianness == Connection.NativeEndianness) {
172
fixed (byte* p = &data[pos])
173
*((ushort*)dstPtr) = *((ushort*)p);
175
byte* dst = (byte*)dstPtr;
176
dst[0] = data[pos + 1];
177
dst[1] = data[pos + 0];
183
unsafe public short ReadInt16 ()
187
MarshalUShort (&val);
192
unsafe public ushort ReadUInt16 ()
196
MarshalUShort (&val);
201
unsafe protected void MarshalUInt (void* dstPtr)
205
if (data.Length < pos + 4)
206
throw new Exception ("Cannot read beyond end of data");
208
if (endianness == Connection.NativeEndianness) {
209
fixed (byte* p = &data[pos])
210
*((uint*)dstPtr) = *((uint*)p);
212
byte* dst = (byte*)dstPtr;
213
dst[0] = data[pos + 3];
214
dst[1] = data[pos + 2];
215
dst[2] = data[pos + 1];
216
dst[3] = data[pos + 0];
222
unsafe public int ReadInt32 ()
231
unsafe public uint ReadUInt32 ()
240
unsafe protected void MarshalULong (void* dstPtr)
244
if (data.Length < pos + 8)
245
throw new Exception ("Cannot read beyond end of data");
247
if (endianness == Connection.NativeEndianness) {
248
fixed (byte* p = &data[pos])
249
*((ulong*)dstPtr) = *((ulong*)p);
251
byte* dst = (byte*)dstPtr;
252
for (int i = 0; i < 8; ++i)
253
dst[i] = data[pos + (7 - i)];
259
unsafe public long ReadInt64 ()
268
unsafe public ulong ReadUInt64 ()
278
unsafe public float ReadSingle ()
288
unsafe public double ReadDouble ()
297
public string ReadString ()
299
uint ln = ReadUInt32 ();
301
string val = Encoding.UTF8.GetString (data, pos, (int)ln);
308
public ObjectPath ReadObjectPath ()
310
//exactly the same as string
311
return new ObjectPath (ReadString ());
314
public Signature ReadSignature ()
316
byte ln = ReadByte ();
318
// Avoid an array allocation for small signatures
320
DType dtype = (DType)ReadByte ();
322
return new Signature (dtype);
325
if (ln > Protocol.MaxSignatureLength)
326
throw new Exception ("Signature length " + ln + " exceeds maximum allowed " + Protocol.MaxSignatureLength + " bytes");
328
byte[] sigData = new byte[ln];
329
Array.Copy (data, pos, sigData, 0, (int)ln);
333
return Signature.Take (sigData);
336
public object ReadVariant ()
338
return ReadValue (ReadSignature ());
341
// Used primarily for reading variant values
342
object ReadValue (Signature sig)
345
return ReadValue (sig[0]);
350
} catch (NotSupportedException e) {
351
// We don't catch other exceptions as they indicate a malformed signature
352
if (Protocol.Verbose)
353
Console.Error.WriteLine (e.Message);
364
ReadPad (sig.Alignment);
367
int ln = pos - startPos;
369
DValue dv = new DValue();
370
dv.endianness = endianness;
372
dv.data = new byte[ln];
373
Array.Copy (data, startPos, dv.data, 0, ln);
377
return ReadValue (t);
380
//not pretty or efficient but works
381
public void GetValueToDict (Type keyType, Type valType, System.Collections.IDictionary val)
383
uint ln = ReadUInt32 ();
385
if (ln > Protocol.MaxArrayLength)
386
throw new Exception ("Dict length " + ln + " exceeds maximum allowed " + Protocol.MaxArrayLength + " bytes");
388
//advance to the alignment of the element
389
//ReadPad (Protocol.GetAlignment (Signature.TypeToDType (type)));
392
int endPos = pos + (int)ln;
394
//while (stream.Position != endPos)
399
val.Add (ReadValue (keyType), ReadValue (valType));
403
throw new Exception ("Read pos " + pos + " != ep " + endPos);
406
//this could be made generic to avoid boxing
407
public Array ReadArray (Type elemType)
409
uint ln = ReadUInt32 ();
411
if (ln > Protocol.MaxArrayLength)
412
throw new Exception ("Array length " + ln + " exceeds maximum allowed " + Protocol.MaxArrayLength + " bytes");
414
//TODO: more fast paths for primitive arrays
415
if (elemType == typeof (byte)) {
416
byte[] valb = new byte[ln];
417
Array.Copy (data, pos, valb, 0, (int)ln);
422
//advance to the alignment of the element
423
ReadPad (Protocol.GetAlignment (Signature.TypeToDType (elemType)));
425
int endPos = pos + (int)ln;
427
//List<T> vals = new List<T> ();
428
System.Collections.ArrayList vals = new System.Collections.ArrayList ();
430
//while (stream.Position != endPos)
432
vals.Add (ReadValue (elemType));
435
throw new Exception ("Read pos " + pos + " != ep " + endPos);
437
return vals.ToArray (elemType);
441
//probably the wrong place for this
442
//there might be more elegant solutions
443
public object ReadStruct (Type type)
447
object val = Activator.CreateInstance (type);
450
if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (KeyValuePair<,>)) {
453
System.Reflection.PropertyInfo key_prop = type.GetProperty ("Key");
454
GetValue (key_prop.PropertyType, out elem);
455
key_prop.SetValue (val, elem, null);
457
System.Reflection.PropertyInfo val_prop = type.GetProperty ("Value");
458
GetValue (val_prop.PropertyType, out elem);
459
val_prop.SetValue (val, elem, null);
465
FieldInfo[] fis = type.GetFields (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
467
foreach (System.Reflection.FieldInfo fi in fis)
468
fi.SetValue (val, ReadValue (fi.FieldType));
473
public void ReadNull ()
476
throw new Exception ("Read non-zero byte at position " + pos + " while expecting null terminator");
481
public void ReadPad (int alignment)
483
pos = Protocol.Padded (pos, alignment);
487
public void ReadPad (int alignment)
489
for (int endPos = Protocol.Padded (pos, alignment) ; pos != endPos ; pos++)
491
throw new Exception ("Read non-zero byte at position " + pos + " while expecting padding");