Get rid of ObjectPath objects and use a string instead.
[versaplex.git] / dbus-sharp / MessageWriter.cs
blob8c9361850c64f79d021feb9cfdcad9956fd3ffcb
1 // Copyright 2006 Alp Toker <alp@atoker.com>
2 // This software is made available under the MIT License
3 // See COPYING for details
4 //
5 using System;
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.Text;
9 using System.IO;
10 using System.Reflection;
12 namespace Wv
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)
49 WritePad(2);
50 byte[] dst = new byte[2];
52 if (endianness == Connection.NativeEndianness) {
53 dst[0] = data[0];
54 dst[1] = data[1];
56 else {
57 dst[0] = data[1];
58 dst[1] = data[0];
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)
76 WritePad(4);
77 byte[] dst = new byte[4];
79 if (endianness == Connection.NativeEndianness) {
80 dst[0] = data[0];
81 dst[1] = data[1];
82 dst[2] = data[2];
83 dst[3] = data[3];
85 else {
86 dst[0] = data[3];
87 dst[1] = data[2];
88 dst[2] = data[1];
89 dst[3] = data[0];
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)
107 WritePad (8);
108 byte[] dst = new byte[8];
110 if (endianness == Connection.NativeEndianness) {
111 for (int i = 0; i < 8; ++i)
112 dst[i] = data[i];
114 else {
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);
147 WriteNull ();
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);
159 WriteNull ();
162 public void WriteComplex(object val, Type type)
164 if (type == typeof(void))
165 return;
167 if (type.IsArray) {
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);
181 else {
182 throw new Exception ("Can't write");
186 public void Write(Type type, object val)
188 if (type == typeof(void))
189 return;
191 if (type.IsArray) {
192 xWriteArray(val, type.GetElementType());
194 else if (type == typeof(Signature)) {
195 Write((Signature)val);
197 else if (type == typeof(object)) {
198 Write(val);
200 else if (type == typeof(string)) {
201 Write((string)val);
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);
218 else {
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)
226 switch (dtype)
228 case DType.Byte:
229 Write((byte)val);
230 break;
231 case DType.Boolean:
232 Write((bool)val);
233 break;
234 case DType.Int16:
235 Write((short)val);
236 break;
237 case DType.UInt16:
238 Write((ushort)val);
239 break;
240 case DType.Int32:
241 Write((int)val);
242 break;
243 case DType.UInt32:
244 Write((uint)val);
245 break;
246 case DType.Int64:
247 Write((long)val);
248 break;
249 case DType.UInt64:
250 Write((ulong)val);
251 break;
252 case DType.Single:
253 Write((float)val);
254 break;
255 case DType.Double:
256 Write((double)val);
257 break;
258 case DType.String:
259 Write((string)val);
260 break;
261 case DType.ObjectPath:
262 Write((string)val);
263 break;
264 case DType.Signature:
265 Write((Signature)val);
266 break;
267 case DType.Variant:
268 Write((object)val);
269 break;
270 default:
271 throw new Exception ("Unhandled D-Bus type: " + dtype);
275 public void WriteObject(Type type, object val)
277 string path;
279 BusObject bobj = val as BusObject;
281 if (bobj == null && val is MarshalByRefObject) {
282 bobj = ((MarshalByRefObject)val).GetLifetimeService () as BusObject;
285 if (bobj == null)
286 throw new Exception ("No object reference to write");
288 path = bobj.Path;
290 Write (path);
293 //variant
294 public void Write(object val)
296 //TODO: maybe support sending null variants
298 if (val == null)
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);
309 Write(sig);
310 Write(type, val);
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);
325 return;
328 long origPos = stream.Position;
329 Write ((uint)0);
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");
346 Write (ln);
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,
352 IEnumerable<T> list,
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.
359 WritePad(4);
361 int startpad = (int)(stream.Position+4) % 8;
362 tmp.stream.Write(zeroes, 0, startpad);
364 int first = -1;
365 foreach (T i in list)
367 tmp.WritePad(align);
368 if (first < 0)
369 first = (int)tmp.stream.Position;
370 doelement(tmp, i);
373 byte[] a = tmp.ToArray();
374 Write((uint)(a.Length - first));
375 stream.Write(a, startpad, a.Length - startpad);
378 // not used!
379 public void WriteDict<K,V>(IDictionary<K,V> dict)
381 WriteArray(8, dict, (w2, i) => {
382 w2.Write(i.Key);
383 w2.Write(i.Value);
387 public void WriteFromDict(Type keyType, Type valType, IDictionary val)
389 long origPos = stream.Position;
390 Write ((uint)0);
392 //advance to the alignment of the element
393 //WritePad (Protocol.GetAlignment (Signature.TypeToDType (type)));
394 WritePad (8);
396 long startPos = stream.Position;
398 foreach (System.Collections.DictionaryEntry entry in val)
400 WritePad (8);
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");
413 Write (ln);
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()
425 stream.WriteByte(0);
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)
438 WritePad(4);
440 long sizepos = stream.Position;
442 Write((int)0);
444 WritePad(alignment);
446 long counter = stream.Position;
448 wd(this);
450 long endpos = stream.Position;
452 stream.Position = sizepos;
453 Write((int)(endpos-counter));
455 stream.Position = endpos;