// Copyright 2006 Alp Toker // This software is made available under the MIT License // See COPYING for details using System; using System.Text; using System.Collections.Generic; using System.IO; using System.Reflection; namespace NDesk.DBus { class MessageReader { protected EndianFlag endianness; protected byte[] data; //TODO: this should be uint or long to handle long messages protected int pos = 0; protected Message message; public MessageReader (EndianFlag endianness, byte[] data) { if (data == null) throw new ArgumentNullException ("data"); this.endianness = endianness; this.data = data; } public MessageReader (Message message) : this (message.Header.Endianness, message.Body) { if (message == null) throw new ArgumentNullException ("message"); this.message = message; } public object ReadValue (Type type) { if (type == typeof (void)) return null; if (type.IsArray) { return ReadArray (type.GetElementType ()); } else if (type == typeof (ObjectPath)) { return ReadObjectPath (); } else if (type == typeof (Signature)) { return ReadSignature (); } else if (type == typeof (object)) { return ReadVariant (); } else if (type == typeof (string)) { return ReadString (); } else if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (IDictionary<,>)) { Type[] genArgs = type.GetGenericArguments (); //Type dictType = typeof (Dictionary<,>).MakeGenericType (genArgs); //workaround for Mono bug #81035 (memory leak) Type dictType = Mapper.GetGenericType (typeof (Dictionary<,>), genArgs); System.Collections.IDictionary idict = (System.Collections.IDictionary)Activator.CreateInstance(dictType, new object[0]); GetValueToDict (genArgs[0], genArgs[1], idict); return idict; } else if (Mapper.IsPublic (type)) { return GetObject (type); } else if (!type.IsPrimitive && !type.IsEnum) { return ReadStruct (type); } else { object val; DType dtype = Signature.TypeToDType (type); val = ReadValue (dtype); if (type.IsEnum) val = Enum.ToObject (type, val); return val; } } //helper method, should not be used generally public object ReadValue (DType dtype) { switch (dtype) { case DType.Byte: return ReadByte (); case DType.Boolean: return ReadBoolean (); case DType.Int16: return ReadInt16 (); case DType.UInt16: return ReadUInt16 (); case DType.Int32: return ReadInt32 (); case DType.UInt32: return ReadUInt32 (); case DType.Int64: return ReadInt64 (); case DType.UInt64: return ReadUInt64 (); #if !DISABLE_SINGLE case DType.Single: return ReadSingle (); #endif case DType.Double: return ReadDouble (); case DType.String: return ReadString (); case DType.ObjectPath: return ReadObjectPath (); case DType.Signature: return ReadSignature (); case DType.Variant: return ReadVariant (); default: throw new Exception ("Unhandled D-Bus type: " + dtype); } } public object GetObject (Type type) { ObjectPath path = ReadObjectPath (); return message.Connection.GetObject (type, (string)message.Header.Fields[FieldCode.Sender], path); } public byte ReadByte () { return data[pos++]; } public bool ReadBoolean () { uint intval = ReadUInt32 (); switch (intval) { case 0: return false; case 1: return true; default: throw new Exception ("Read value " + intval + " at position " + pos + " while expecting boolean (0/1)"); } } unsafe protected void MarshalUShort (byte *dst) { ReadPad (2); if (endianness == Connection.NativeEndianness) { dst[0] = data[pos + 0]; dst[1] = data[pos + 1]; } else { dst[0] = data[pos + 1]; dst[1] = data[pos + 0]; } pos += 2; } unsafe public short ReadInt16 () { short val; MarshalUShort ((byte*)&val); return val; } unsafe public ushort ReadUInt16 () { ushort val; MarshalUShort ((byte*)&val); return val; } unsafe protected void MarshalUInt (byte *dst) { ReadPad (4); if (endianness == Connection.NativeEndianness) { dst[0] = data[pos + 0]; dst[1] = data[pos + 1]; dst[2] = data[pos + 2]; dst[3] = data[pos + 3]; } else { dst[0] = data[pos + 3]; dst[1] = data[pos + 2]; dst[2] = data[pos + 1]; dst[3] = data[pos + 0]; } pos += 4; } unsafe public int ReadInt32 () { int val; MarshalUInt ((byte*)&val); return val; } unsafe public uint ReadUInt32 () { uint val; MarshalUInt ((byte*)&val); return val; } unsafe protected void MarshalULong (byte *dst) { ReadPad (8); if (endianness == Connection.NativeEndianness) { for (int i = 0; i < 8; ++i) dst[i] = data[pos + i]; } else { for (int i = 0; i < 8; ++i) dst[i] = data[pos + (7 - i)]; } pos += 8; } unsafe public long ReadInt64 () { long val; MarshalULong ((byte*)&val); return val; } unsafe public ulong ReadUInt64 () { ulong val; MarshalULong ((byte*)&val); return val; } #if !DISABLE_SINGLE unsafe public float ReadSingle () { float val; MarshalUInt ((byte*)&val); return val; } #endif unsafe public double ReadDouble () { double val; MarshalULong ((byte*)&val); return val; } public string ReadString () { uint ln = ReadUInt32 (); string val = Encoding.UTF8.GetString (data, pos, (int)ln); pos += (int)ln; ReadNull (); return val; } public ObjectPath ReadObjectPath () { //exactly the same as string return new ObjectPath (ReadString ()); } public Signature ReadSignature () { byte ln = ReadByte (); if (ln > Protocol.MaxSignatureLength) throw new Exception ("Signature length " + ln + " exceeds maximum allowed " + Protocol.MaxSignatureLength + " bytes"); byte[] sigData = new byte[ln]; Array.Copy (data, pos, sigData, 0, (int)ln); pos += (int)ln; ReadNull (); return new Signature (sigData); } public object ReadVariant () { return ReadVariant (ReadSignature ()); } object ReadVariant (Signature sig) { return ReadValue (sig.ToType ()); } //not pretty or efficient but works public void GetValueToDict (Type keyType, Type valType, System.Collections.IDictionary val) { uint ln = ReadUInt32 (); if (ln > Protocol.MaxArrayLength) throw new Exception ("Dict length " + ln + " exceeds maximum allowed " + Protocol.MaxArrayLength + " bytes"); //advance to the alignment of the element //ReadPad (Protocol.GetAlignment (Signature.TypeToDType (type))); ReadPad (8); int endPos = pos + (int)ln; //while (stream.Position != endPos) while (pos < endPos) { ReadPad (8); val.Add (ReadValue (keyType), ReadValue (valType)); } if (pos != endPos) throw new Exception ("Read pos " + pos + " != ep " + endPos); } //this could be made generic to avoid boxing public Array ReadArray (Type elemType) { uint ln = ReadUInt32 (); if (ln > Protocol.MaxArrayLength) throw new Exception ("Array length " + ln + " exceeds maximum allowed " + Protocol.MaxArrayLength + " bytes"); //TODO: more fast paths for primitive arrays if (elemType == typeof (byte)) { byte[] valb = new byte[ln]; Array.Copy (data, pos, valb, 0, (int)ln); pos += (int)ln; return valb; } //advance to the alignment of the element ReadPad (Protocol.GetAlignment (Signature.TypeToDType (elemType))); int endPos = pos + (int)ln; //List vals = new List (); System.Collections.ArrayList vals = new System.Collections.ArrayList (); //while (stream.Position != endPos) while (pos < endPos) vals.Add (ReadValue (elemType)); if (pos != endPos) throw new Exception ("Read pos " + pos + " != ep " + endPos); return vals.ToArray (elemType); } //struct //probably the wrong place for this //there might be more elegant solutions public object ReadStruct (Type type) { ReadPad (8); object val = Activator.CreateInstance (type); /* if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (KeyValuePair<,>)) { object elem; System.Reflection.PropertyInfo key_prop = type.GetProperty ("Key"); GetValue (key_prop.PropertyType, out elem); key_prop.SetValue (val, elem, null); System.Reflection.PropertyInfo val_prop = type.GetProperty ("Value"); GetValue (val_prop.PropertyType, out elem); val_prop.SetValue (val, elem, null); return; } */ FieldInfo[] fis = type.GetFields (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); foreach (System.Reflection.FieldInfo fi in fis) fi.SetValue (val, ReadValue (fi.FieldType)); return val; } public void ReadNull () { if (data[pos] != 0) throw new Exception ("Read non-zero byte at position " + pos + " while expecting null terminator"); pos++; } /* public void ReadPad (int alignment) { pos = Protocol.Padded (pos, alignment); } */ public void ReadPad (int alignment) { for (int endPos = Protocol.Padded (pos, alignment) ; pos != endPos ; pos++) if (data[pos] != 0) throw new Exception ("Read non-zero byte at position " + pos + " while expecting padding"); } } }