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;
10
using System.Runtime.InteropServices;
14
sealed 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 sure the header and content-type match
22
public MessageWriter () : this (Connection.NativeEndianness) {}
24
public MessageWriter (EndianFlag endianness)
26
this.endianness = endianness;
27
stream = new MemoryStream ();
30
public byte[] ToArray ()
32
//TODO: mark the writer locked or something here
33
return stream.ToArray ();
36
public void ToStream (Stream dest)
38
stream.WriteTo (dest);
41
public void CloseWrite ()
46
public void Write (byte val)
48
stream.WriteByte (val);
51
public void Write (bool val)
53
Write ((uint) (val ? 1 : 0));
56
// Buffer for integer marshaling
57
byte[] dst = new byte[8];
58
unsafe void MarshalUShort (void* dataPtr)
62
if (endianness == Connection.NativeEndianness) {
63
fixed (byte* p = &dst[0])
64
*((ushort*)p) = *((ushort*)dataPtr);
66
byte* data = (byte*)dataPtr;
71
stream.Write (dst, 0, 2);
74
unsafe public void Write (short val)
79
unsafe public void Write (ushort val)
84
unsafe void MarshalUInt (void* dataPtr)
88
if (endianness == Connection.NativeEndianness) {
89
fixed (byte* p = &dst[0])
90
*((uint*)p) = *((uint*)dataPtr);
92
byte* data = (byte*)dataPtr;
99
stream.Write (dst, 0, 4);
102
unsafe public void Write (int val)
107
unsafe public void Write (uint val)
112
unsafe void MarshalULong (void* dataPtr)
116
if (endianness == Connection.NativeEndianness) {
117
fixed (byte* p = &dst[0])
118
*((ulong*)p) = *((ulong*)dataPtr);
120
byte* data = (byte*)dataPtr;
121
for (int i = 0; i < 8; ++i)
122
dst[i] = data[7 - i];
125
stream.Write (dst, 0, 8);
128
unsafe public void Write (long val)
133
unsafe public void Write (ulong val)
139
unsafe public void Write (float val)
145
unsafe public void Write (double val)
150
public void Write (string val)
152
byte[] utf8_data = Encoding.UTF8.GetBytes (val);
153
Write ((uint)utf8_data.Length);
154
stream.Write (utf8_data, 0, utf8_data.Length);
158
public void Write (ObjectPath val)
163
public void Write (Signature val)
165
byte[] ascii_data = val.GetBuffer ();
167
if (ascii_data.Length > Protocol.MaxSignatureLength)
168
throw new Exception ("Signature length " + ascii_data.Length + " exceeds maximum allowed " + Protocol.MaxSignatureLength + " bytes");
170
Write ((byte)ascii_data.Length);
171
stream.Write (ascii_data, 0, ascii_data.Length);
176
public void WriteComplex (object val, Type type)
178
if (type == typeof (void))
182
MethodInfo miDict = typeof (MessageWriter).GetMethod ("WriteArray");
183
MethodInfo mi = miDict.MakeGenericMethod (type.GetElementType ());
184
mi.Invoke (this, new object[] {val});
185
} else if (type.IsGenericType && (type.GetGenericTypeDefinition () == typeof (IDictionary<,>) || type.GetGenericTypeDefinition () == typeof (Dictionary<,>))) {
186
Type[] genArgs = type.GetGenericArguments ();
187
MethodInfo miDict = typeof (MessageWriter).GetMethod ("WriteFromDict");
188
MethodInfo mi = miDict.MakeGenericMethod (genArgs);
189
mi.Invoke (this, new object[] {val});
190
} else if (Mapper.IsPublic (type)) {
191
WriteObject (type, val);
192
} else if (!type.IsPrimitive && !type.IsEnum) {
193
WriteValueType (val, type);
195
} else if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>)) {
196
//is it possible to support nullable types?
197
Type[] genArgs = type.GetGenericArguments ();
198
WriteVariant (genArgs[0], val);
201
throw new Exception ("Can't write");
206
public void Write (Type type, object val)
208
if (type == typeof (void))
212
MethodInfo miDict = typeof (MessageWriter).GetMethod ("WriteArray");
213
MethodInfo mi = miDict.MakeGenericMethod (type.GetElementType ());
214
mi.Invoke (this, new object[] {val});
215
} else if (type == typeof (ObjectPath)) {
216
Write ((ObjectPath)val);
217
} else if (type == typeof (Signature)) {
218
Write ((Signature)val);
219
} else if (type == typeof (object)) {
221
} else if (type == typeof (string)) {
223
} else if (type.IsGenericType && (type.GetGenericTypeDefinition () == typeof (IDictionary<,>) || type.GetGenericTypeDefinition () == typeof (Dictionary<,>))) {
224
Type[] genArgs = type.GetGenericArguments ();
225
MethodInfo miDict = typeof (MessageWriter).GetMethod ("WriteFromDict");
226
MethodInfo mi = miDict.MakeGenericMethod (genArgs);
227
mi.Invoke (this, new object[] {val});
228
} else if (Mapper.IsPublic (type)) {
229
WriteObject (type, val);
230
} else if (!type.IsPrimitive && !type.IsEnum) {
231
WriteValueType (val, type);
233
Write (Signature.TypeToDType (type), val);
237
//helper method, should not be used as it boxes needlessly
238
public void Write (DType dtype, object val)
299
case DType.ObjectPath:
301
Write ((ObjectPath)val);
304
case DType.Signature:
306
Write ((Signature)val);
315
throw new Exception ("Unhandled D-Bus type: " + dtype);
319
public void WriteObject (Type type, object val)
323
BusObject bobj = val as BusObject;
325
if (bobj == null && val is MarshalByRefObject) {
326
bobj = ((MarshalByRefObject)val).GetLifetimeService () as BusObject;
330
throw new Exception ("No object reference to write");
338
public void Write (object val)
341
throw new NotSupportedException ("Cannot send null variant");
344
DValue dv = (DValue)val;
346
if (dv.endianness != endianness)
347
throw new NotImplementedException ("Writing opposite endian DValues not yet implemented.");
349
Write (dv.signature);
350
WritePad (dv.signature.Alignment);
351
stream.Write (dv.data, 0, dv.data.Length);
355
Type type = val.GetType ();
357
WriteVariant (type, val);
360
public void WriteVariant (Type type, object val)
362
Signature sig = Signature.GetSig (type);
368
//this requires a seekable stream for now
369
public unsafe void WriteArray<T> (T[] val)
371
Type elemType = typeof (T);
373
if (elemType == typeof (byte)) {
374
if (val.Length > Protocol.MaxArrayLength)
375
throw new Exception ("Array length " + val.Length + " exceeds maximum allowed " + Protocol.MaxArrayLength + " bytes");
377
Write ((uint)val.Length);
378
stream.Write ((byte[])(object)val, 0, val.Length);
383
elemType = Enum.GetUnderlyingType (elemType);
385
Signature sigElem = Signature.GetSig (elemType);
387
if (endianness == Connection.NativeEndianness && elemType.IsValueType && !sigElem.IsStruct && sigElem.GetFixedSize (ref fixedSize)) {
388
int byteLength = fixedSize * val.Length;
389
if (byteLength > Protocol.MaxArrayLength)
390
throw new Exception ("Array length " + byteLength + " exceeds maximum allowed " + Protocol.MaxArrayLength + " bytes");
391
Write ((uint)byteLength);
392
WritePad (sigElem.Alignment);
394
GCHandle valHandle = GCHandle.Alloc (val, GCHandleType.Pinned);
395
IntPtr p = valHandle.AddrOfPinnedObject ();
396
byte[] data = new byte[byteLength];
398
for (int i = 0 ; i != byteLength ; i++)
400
stream.Write (data, 0, data.Length);
405
long origPos = stream.Position;
408
//advance to the alignment of the element
409
WritePad (sigElem.Alignment);
411
long startPos = stream.Position;
413
TypeWriter<T> tWriter = TypeImplementer.GetTypeWriter<T> ();
415
foreach (T elem in val)
416
tWriter (this, elem);
418
long endPos = stream.Position;
419
uint ln = (uint)(endPos - startPos);
420
stream.Position = origPos;
422
if (ln > Protocol.MaxArrayLength)
423
throw new Exception ("Array length " + ln + " exceeds maximum allowed " + Protocol.MaxArrayLength + " bytes");
426
stream.Position = endPos;
429
public void WriteValueType (object val, Type type)
431
MethodInfo mi = TypeImplementer.GetWriteMethod (type);
432
mi.Invoke (null, new object[] {this, val});
435
public void WriteStructure<T> (T value)
437
TypeWriter<T> tWriter = TypeImplementer.GetTypeWriter<T> ();
438
tWriter (this, value);
441
public void WriteFromDict<TKey,TValue> (IDictionary<TKey,TValue> val)
443
long origPos = stream.Position;
448
long startPos = stream.Position;
450
TypeWriter<TKey> keyWriter = TypeImplementer.GetTypeWriter<TKey> ();
451
TypeWriter<TValue> valueWriter = TypeImplementer.GetTypeWriter<TValue> ();
453
foreach (KeyValuePair<TKey,TValue> entry in val)
456
keyWriter (this, entry.Key);
457
valueWriter (this, entry.Value);
460
long endPos = stream.Position;
461
uint ln = (uint)(endPos - startPos);
462
stream.Position = origPos;
464
if (ln > Protocol.MaxArrayLength)
465
throw new Exception ("Dict length " + ln + " exceeds maximum allowed " + Protocol.MaxArrayLength + " bytes");
468
stream.Position = endPos;
472
public void Write (IDictionary<FieldCode, object> val)
474
WriteHeaderDict(val);
477
public void Write (Dictionary<FieldCode, object> val)
479
WriteHeaderDict (val);
484
public void Write (Dictionary<byte, object> val)
486
long origPos = stream.Position;
491
long startPos = stream.Position;
493
foreach (KeyValuePair<byte, object> entry in val) {
499
long endPos = stream.Position;
500
uint ln = (uint)(endPos - startPos);
501
stream.Position = origPos;
503
if (ln > Protocol.MaxArrayLength)
504
throw new Exception ("Dict length " + ln + " exceeds maximum allowed " + Protocol.MaxArrayLength + " bytes");
507
stream.Position = endPos;
511
internal void WriteHeaderFields (Dictionary<byte, object> val)
513
long origPos = stream.Position;
518
long startPos = stream.Position;
520
foreach (KeyValuePair<byte, object> entry in val) {
523
switch ((FieldCode)entry.Key) {
524
case FieldCode.Destination:
525
case FieldCode.ErrorName:
526
case FieldCode.Interface:
527
case FieldCode.Member:
528
case FieldCode.Sender:
529
Write (Signature.StringSig);
530
Write ((string)entry.Value);
533
Write (Signature.ObjectPathSig);
534
Write ((ObjectPath)entry.Value);
536
case FieldCode.ReplySerial:
537
Write (Signature.UInt32Sig);
538
Write ((uint)entry.Value);
546
long endPos = stream.Position;
547
uint ln = (uint)(endPos - startPos);
548
stream.Position = origPos;
550
if (ln > Protocol.MaxArrayLength)
551
throw new Exception ("Dict length " + ln + " exceeds maximum allowed " + Protocol.MaxArrayLength + " bytes");
554
stream.Position = endPos;
557
public void WriteNull ()
559
stream.WriteByte (0);
562
// Source buffer for zero-padding
563
static readonly byte[] nullBytes = new byte[8];
564
public void WritePad (int alignment)
566
int needed = Protocol.PadNeeded ((int)stream.Position, alignment);
567
stream.Write (nullBytes, 0, needed);