1 // Copyright 2006 Alp Toker <alp@atoker.com>
2 // This software is made available under the MIT License
3 // See COPYING for details
6 using System
.Collections
;
7 using System
.Collections
.Generic
;
10 using System
.Reflection
;
14 public class MessageWriter
16 EndianFlag endianness
;
17 internal MemoryStream stream
;
19 public Connection connection
;
21 // a default constructor is a bad idea for now as we want to make
22 // sure the header and content-type match
23 public MessageWriter() : this (Connection
.NativeEndianness
) {}
25 public MessageWriter(EndianFlag endianness
)
27 this.endianness
= endianness
;
28 stream
= new MemoryStream ();
31 public byte[] ToArray()
33 //TODO: mark the writer locked or something here
34 return stream
.ToArray();
37 public void Write(byte val
)
39 stream
.WriteByte(val
);
42 public void Write(bool val
)
44 Write ((uint) (val
? 1 : 0));
47 unsafe protected void MarshalUShort(byte *data
)
50 byte[] dst
= new byte[2];
52 if (endianness
== Connection
.NativeEndianness
) {
61 stream
.Write(dst
, 0, 2);
64 unsafe public void Write(short val
)
66 MarshalUShort((byte*)&val
);
69 unsafe public void Write(ushort val
)
71 MarshalUShort((byte*)&val
);
74 unsafe protected void MarshalUInt(byte *data
)
77 byte[] dst
= new byte[4];
79 if (endianness
== Connection
.NativeEndianness
) {
92 stream
.Write(dst
, 0, 4);
95 unsafe public void Write(int val
)
97 MarshalUInt((byte*)&val
);
100 unsafe public void Write(uint val
)
102 MarshalUInt((byte*)&val
);
105 unsafe protected void MarshalULong(byte *data
)
108 byte[] dst
= new byte[8];
110 if (endianness
== Connection
.NativeEndianness
) {
111 for (int i
= 0; i
< 8; ++i
)
115 for (int i
= 0; i
< 8; ++i
)
116 dst
[i
] = data
[7 - i
];
119 stream
.Write(dst
, 0, 8);
122 unsafe public void Write(long val
)
124 MarshalULong((byte*)&val
);
127 unsafe public void Write (ulong val
)
129 MarshalULong((byte*)&val
);
132 unsafe public void Write (float val
)
134 MarshalUInt((byte*)&val
);
137 unsafe public void Write (double val
)
139 MarshalULong((byte*)&val
);
142 public void Write(string val
)
144 byte[] utf8_data
= Encoding
.UTF8
.GetBytes (val
);
145 Write ((uint)utf8_data
.Length
);
146 stream
.Write (utf8_data
, 0, utf8_data
.Length
);
150 public void Write(Signature val
)
152 byte[] ascii_data
= val
.GetBuffer ();
154 if (ascii_data
.Length
> Protocol
.MaxSignatureLength
)
155 throw new Exception ("Signature length " + ascii_data
.Length
+ " exceeds maximum allowed " + Protocol
.MaxSignatureLength
+ " bytes");
157 Write ((byte)ascii_data
.Length
);
158 stream
.Write (ascii_data
, 0, ascii_data
.Length
);
162 public void WriteComplex(object val
, Type type
)
164 if (type
== typeof(void))
168 xWriteArray(val
, type
.GetElementType());
170 else if (type
.IsGenericType
&& (type
.GetGenericTypeDefinition () == typeof (IDictionary
<,>) || type
.GetGenericTypeDefinition () == typeof (Dictionary
<,>))) {
171 Type
[] genArgs
= type
.GetGenericArguments ();
172 System
.Collections
.IDictionary idict
= (System
.Collections
.IDictionary
)val
;
173 WriteFromDict (genArgs
[0], genArgs
[1], idict
);
175 else if (Mapper
.IsPublic (type
)) {
176 WriteObject (type
, val
);
178 else if (!type
.IsPrimitive
&& !type
.IsEnum
) {
179 WriteValueType (val
, type
);
182 throw new Exception ("Can't write");
186 public void Write(Type type
, object val
)
188 if (type
== typeof(void))
192 xWriteArray(val
, type
.GetElementType());
194 else if (type
== typeof(Signature
)) {
195 Write((Signature
)val
);
197 else if (type
== typeof(object)) {
200 else if (type
== typeof(string)) {
203 else if (type
.IsGenericType
204 && (type
.GetGenericTypeDefinition()
205 == typeof(IDictionary
<,>)
206 || type
.GetGenericTypeDefinition()
207 == typeof(Dictionary
<,>))) {
208 Type
[] genArgs
= type
.GetGenericArguments();
209 IDictionary idict
= (IDictionary
)val
;
210 WriteFromDict(genArgs
[0], genArgs
[1], idict
);
212 else if (Mapper
.IsPublic(type
)) {
213 WriteObject(type
, val
);
215 else if (!type
.IsPrimitive
&& !type
.IsEnum
) {
216 WriteValueType(val
, type
);
219 Write(Signature
.TypeToDType(type
), val
);
223 //helper method, should not be used as it boxes needlessly
224 public void Write (DType dtype
, object val
)
261 case DType
.ObjectPath
:
264 case DType
.Signature
:
265 Write((Signature
)val
);
271 throw new Exception ("Unhandled D-Bus type: " + dtype
);
275 public void WriteObject(Type type
, object val
)
279 BusObject bobj
= val
as BusObject
;
281 if (bobj
== null && val
is MarshalByRefObject
) {
282 bobj
= ((MarshalByRefObject
)val
).GetLifetimeService () as BusObject
;
286 throw new Exception ("No object reference to write");
294 public void Write(object val
)
296 //TODO: maybe support sending null variants
299 throw new NotSupportedException ("Cannot send null variant");
301 Type type
= val
.GetType();
303 WriteVariant(type
, val
);
306 public void WriteVariant(Type type
, object val
)
308 Signature sig
= Signature
.GetSig(type
);
313 //this requires a seekable stream for now
314 public void xWriteArray(object obj
, Type elemType
)
316 Array val
= (Array
)obj
;
318 //TODO: more fast paths for primitive arrays
319 if (elemType
== typeof (byte)) {
320 if (val
.Length
> Protocol
.MaxArrayLength
)
321 throw new Exception ("Array length " + val
.Length
+ " exceeds maximum allowed " + Protocol
.MaxArrayLength
+ " bytes");
323 Write ((uint)val
.Length
);
324 stream
.Write ((byte[])val
, 0, val
.Length
);
328 long origPos
= stream
.Position
;
331 //advance to the alignment of the element
332 WritePad(Protocol
.GetAlignment (Signature
.TypeToDType (elemType
)));
334 long startPos
= stream
.Position
;
336 foreach (object elem
in val
)
337 Write (elemType
, elem
);
339 long endPos
= stream
.Position
;
340 uint ln
= (uint)(endPos
- startPos
);
341 stream
.Position
= origPos
;
343 if (ln
> Protocol
.MaxArrayLength
)
344 throw new Exception ("Array length " + ln
+ " exceeds maximum allowed " + Protocol
.MaxArrayLength
+ " bytes");
347 stream
.Position
= endPos
;
350 static byte[] zeroes
= new byte[8] { 0,0,0,0,0,0,0,0 }
;
351 public void WriteArray
<T
>(int align
,
353 Action
<MessageWriter
,T
> doelement
)
355 var tmp
= new MessageWriter(endianness
);
357 // after the arraylength, we'll be aligned to size 4, but that
358 // might not be enough, so maybe we need to fix it up.
361 int startpad
= (int)(stream
.Position
+4) % 8;
362 tmp
.stream
.Write(zeroes
, 0, startpad
);
365 foreach (T i
in list
)
369 first
= (int)tmp
.stream
.Position
;
373 byte[] a
= tmp
.ToArray();
374 Write((uint)(a
.Length
- first
));
375 stream
.Write(a
, startpad
, a
.Length
- startpad
);
379 public void WriteDict
<K
,V
>(IDictionary
<K
,V
> dict
)
381 WriteArray(8, dict
, (w2
, i
) => {
387 public void WriteFromDict(Type keyType
, Type valType
, IDictionary val
)
389 long origPos
= stream
.Position
;
392 //advance to the alignment of the element
393 //WritePad (Protocol.GetAlignment (Signature.TypeToDType (type)));
396 long startPos
= stream
.Position
;
398 foreach (System
.Collections
.DictionaryEntry entry
in val
)
402 Write (keyType
, entry
.Key
);
403 Write (valType
, entry
.Value
);
406 long endPos
= stream
.Position
;
407 uint ln
= (uint)(endPos
- startPos
);
408 stream
.Position
= origPos
;
410 if (ln
> Protocol
.MaxArrayLength
)
411 throw new Exception ("Dict length " + ln
+ " exceeds maximum allowed " + Protocol
.MaxArrayLength
+ " bytes");
414 stream
.Position
= endPos
;
417 public void WriteValueType(object val
, Type type
)
419 MethodInfo mi
= TypeImplementer
.GetWriteMethod (type
);
420 mi
.Invoke (null, new object[] {this, val}
);
423 public void WriteNull()
428 public void WritePad(int alignment
)
430 int needed
= Protocol
.PadNeeded ((int)stream
.Position
, alignment
);
431 for (int i
= 0 ; i
!= needed
; i
++)
432 stream
.WriteByte (0);
435 public delegate void WriterDelegate(MessageWriter w
);
436 public void WriteDelegatePrependSize(WriterDelegate wd
, int alignment
)
440 long sizepos
= stream
.Position
;
446 long counter
= stream
.Position
;
450 long endpos
= stream
.Position
;
452 stream
.Position
= sizepos
;
453 Write((int)(endpos
-counter
));
455 stream
.Position
= endpos
;