PageRenderTime 44ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/source/library/Interlace/Amf/AmfWriter.cs

https://bitbucket.org/VahidN/interlace
C# | 416 lines | 307 code | 82 blank | 27 comment | 30 complexity | 8cde3d34bc0edc59e4259ce14f23090e MD5 | raw file
  1. #region Using Directives and Copyright Notice
  2. // Copyright (c) 2007-2010, Computer Consultancy Pty Ltd
  3. // All rights reserved.
  4. //
  5. // Redistribution and use in source and binary forms, with or without
  6. // modification, are permitted provided that the following conditions are met:
  7. // * Redistributions of source code must retain the above copyright
  8. // notice, this list of conditions and the following disclaimer.
  9. // * Redistributions in binary form must reproduce the above copyright
  10. // notice, this list of conditions and the following disclaimer in the
  11. // documentation and/or other materials provided with the distribution.
  12. // * Neither the name of the Computer Consultancy Pty Ltd nor the
  13. // names of its contributors may be used to endorse or promote products
  14. // derived from this software without specific prior written permission.
  15. //
  16. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  17. // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  18. // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  19. // ARE DISCLAIMED. IN NO EVENT SHALL COMPUTER CONSULTANCY PTY LTD BE LIABLE
  20. // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  21. // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  22. // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  23. // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  24. // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  25. // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  26. // DAMAGE.
  27. using System;
  28. using System.Collections.Generic;
  29. using System.IO;
  30. using System.Net;
  31. using System.Runtime.Serialization;
  32. using System.Text;
  33. using System.Xml;
  34. #endregion
  35. namespace Interlace.Amf
  36. {
  37. public class AmfWriter : IDisposable
  38. {
  39. BinaryWriter _writer;
  40. AmfRegistry _registry;
  41. Dictionary<string, int> _stringTable;
  42. // The object and date time table are actually the same table, but couldn't be stored
  43. // in the same dictionary (values are boxed and never compare reference-equal):
  44. ObjectIDGenerator _idGenerator;
  45. Dictionary<DateTime, int> _dateTimeTable;
  46. Dictionary<long, int> _objectTable;
  47. Dictionary<long, int> _traitsTable;
  48. static Dictionary<Type, int> _typeMap;
  49. static AmfWriter()
  50. {
  51. _typeMap = new Dictionary<Type, int>();
  52. _typeMap[typeof(bool)] = AmfMarker.True;
  53. _typeMap[typeof(int)] = AmfMarker.Integer;
  54. _typeMap[typeof(double)] = AmfMarker.Double;
  55. _typeMap[typeof(string)] = AmfMarker.String;
  56. _typeMap[typeof(DateTime)] = AmfMarker.Date;
  57. _typeMap[typeof(AmfArray)] = AmfMarker.Array;
  58. _typeMap[typeof(XmlDocument)] = AmfMarker.Xml;
  59. _typeMap[typeof(byte[])] = AmfMarker.ByteArray;
  60. }
  61. static readonly DateTime _amfEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
  62. public AmfWriter(BinaryWriter writer, AmfRegistry registry)
  63. {
  64. _writer = writer;
  65. _registry = registry;
  66. _stringTable = new Dictionary<string, int>();
  67. _idGenerator = new ObjectIDGenerator();
  68. _dateTimeTable = new Dictionary<DateTime, int>();
  69. _objectTable = new Dictionary<long, int>();
  70. _traitsTable = new Dictionary<long, int>();
  71. }
  72. public static byte[] Write(AmfRegistry registry, object value)
  73. {
  74. using (MemoryStream stream = new MemoryStream())
  75. {
  76. using (AmfWriter writer = new AmfWriter(new BinaryWriter(stream), registry))
  77. {
  78. writer.Write(value);
  79. return stream.ToArray();
  80. }
  81. }
  82. }
  83. void WritePackedInteger(int value)
  84. {
  85. if (value < 0) throw new AmfException("An attempt was made to serialize a negative integer.");
  86. if (value < 0x80)
  87. {
  88. _writer.Write((byte)value);
  89. }
  90. else if (value < 0x4000)
  91. {
  92. _writer.Write((byte)(0x80 | (value >> 7) & 0x7f));
  93. _writer.Write((byte)(value & 0x7f));
  94. }
  95. else if (value < 0x200000)
  96. {
  97. _writer.Write((byte)(0x80 | (value >> 14) & 0x7f));
  98. _writer.Write((byte)(0x80 | (value >> 7) & 0x7f));
  99. _writer.Write((byte)(value & 0x7f));
  100. }
  101. else if (value < 0x40000000)
  102. {
  103. _writer.Write((byte)(0x80 | (value >> 22) & 0x7f));
  104. _writer.Write((byte)(0x80 | (value >> 15) & 0x7f));
  105. _writer.Write((byte)(0x80 | (value >> 8) & 0x7f));
  106. _writer.Write((byte)(value & 0xff));
  107. }
  108. else
  109. {
  110. throw new AmfException("An integer too large to serialize was serialized.");
  111. }
  112. }
  113. void WriteFlaggedInteger(int value, bool flag)
  114. {
  115. WritePackedInteger((value << 1) | (flag ? 1 : 0));
  116. }
  117. void WriteNetworkDouble(double value)
  118. {
  119. _writer.Write((long)IPAddress.HostToNetworkOrder(BitConverter.DoubleToInt64Bits(value)));
  120. }
  121. void WriteNull()
  122. {
  123. _writer.Write((byte)AmfMarker.Null);
  124. }
  125. void Write(bool value)
  126. {
  127. if (value)
  128. {
  129. _writer.Write((byte)AmfMarker.True);
  130. }
  131. else
  132. {
  133. _writer.Write((byte)AmfMarker.False);
  134. }
  135. }
  136. void Write(int value)
  137. {
  138. _writer.Write((byte)AmfMarker.Integer);
  139. WritePackedInteger(value);
  140. }
  141. void Write(double value)
  142. {
  143. _writer.Write((byte)AmfMarker.Double);
  144. WriteNetworkDouble(value);
  145. }
  146. void WriteBareString(string value)
  147. {
  148. if (_stringTable.ContainsKey(value))
  149. {
  150. int index = _stringTable[value];
  151. WriteFlaggedInteger(index, false);
  152. }
  153. else
  154. {
  155. if (value != "") _stringTable[value] = _stringTable.Count;
  156. byte[] data = Encoding.UTF8.GetBytes(value);
  157. WriteFlaggedInteger(data.Length, true);
  158. _writer.Write(data);
  159. }
  160. }
  161. void Write(string value)
  162. {
  163. _writer.Write((byte)AmfMarker.String);
  164. WriteBareString(value);
  165. }
  166. void Write(DateTime value)
  167. {
  168. _writer.Write((byte)AmfMarker.Date);
  169. if (_dateTimeTable.ContainsKey(value))
  170. {
  171. int index = _dateTimeTable[value];
  172. WriteFlaggedInteger(index, false);
  173. }
  174. else
  175. {
  176. _dateTimeTable[value] = _dateTimeTable.Count + _objectTable.Count;
  177. double doubleValue = (value - _amfEpoch).TotalMilliseconds;
  178. WriteFlaggedInteger(0, true);
  179. WriteNetworkDouble(doubleValue);
  180. }
  181. }
  182. void Write(AmfArray value)
  183. {
  184. _writer.Write((byte)AmfMarker.Array);
  185. bool firstTime;
  186. long uniqueValue = _idGenerator.GetId(value, out firstTime);
  187. if (!firstTime)
  188. {
  189. WriteFlaggedInteger(_objectTable[uniqueValue], false);
  190. }
  191. else
  192. {
  193. _objectTable[uniqueValue] = _objectTable.Count + _dateTimeTable.Count;
  194. WriteFlaggedInteger(value.DenseElements.Count, true);
  195. foreach (KeyValuePair<string, object> pair in value.AssociativeElements)
  196. {
  197. WriteBareString(pair.Key);
  198. Write(pair.Value);
  199. }
  200. WriteBareString("");
  201. foreach (object denseElement in value.DenseElements)
  202. {
  203. Write(denseElement);
  204. }
  205. }
  206. }
  207. void Write(byte[] value)
  208. {
  209. _writer.Write((byte)AmfMarker.ByteArray);
  210. bool firstTime;
  211. long uniqueValue = _idGenerator.GetId(value, out firstTime);
  212. if (!firstTime)
  213. {
  214. WriteFlaggedInteger(_objectTable[uniqueValue], false);
  215. }
  216. else
  217. {
  218. _objectTable[uniqueValue] = _objectTable.Count + _dateTimeTable.Count;
  219. WriteFlaggedInteger(value.Length, true);
  220. _writer.Write(value);
  221. }
  222. }
  223. void WriteObjectTraits(AmfTraits traits)
  224. {
  225. bool firstTime;
  226. long uniqueValue = _idGenerator.GetId(traits, out firstTime);
  227. if (!firstTime)
  228. {
  229. int argument = (_traitsTable[uniqueValue] << 2) | 0x01;
  230. WritePackedInteger(argument);
  231. }
  232. else
  233. {
  234. _traitsTable[uniqueValue] = _traitsTable.Count;
  235. if (traits.Kind == AmfTraitsKind.Externalizable) throw new NotImplementedException();
  236. int argument = (traits.MemberNames.Length << 4) | 0x03;
  237. if (traits.Kind == AmfTraitsKind.Dynamic) argument |= 0x08;
  238. WritePackedInteger(argument);
  239. WriteBareString(traits.ClassName);
  240. foreach (string memberName in traits.MemberNames)
  241. {
  242. WriteBareString(memberName);
  243. }
  244. }
  245. }
  246. void WriteObject(Type valueType, object value)
  247. {
  248. _writer.Write((byte)AmfMarker.Object);
  249. bool firstTime;
  250. long uniqueValue = _idGenerator.GetId(value, out firstTime);
  251. if (!firstTime)
  252. {
  253. WriteFlaggedInteger(_objectTable[uniqueValue], false);
  254. }
  255. else
  256. {
  257. _objectTable[uniqueValue] = _objectTable.Count + _dateTimeTable.Count;
  258. IAmfClassDescriptor descriptor = _registry.GetByType(valueType);
  259. AmfTraits traits;
  260. IDictionary<string, object> staticMembers;
  261. IDictionary<string, object> dynamicMembers;
  262. descriptor.SerializeObject(value, out traits, out staticMembers, out dynamicMembers);
  263. WriteObjectTraits(traits);
  264. foreach (string memberName in traits.MemberNames)
  265. {
  266. Write(staticMembers[memberName]);
  267. }
  268. if (traits.Kind == AmfTraitsKind.Dynamic)
  269. {
  270. foreach (KeyValuePair<string, object> pair in dynamicMembers)
  271. {
  272. WriteBareString(pair.Key);
  273. Write(pair.Value);
  274. }
  275. WriteBareString("");
  276. }
  277. }
  278. }
  279. public const byte Object = 0x0a;
  280. public void Write(object value)
  281. {
  282. if (value == null)
  283. {
  284. WriteNull();
  285. }
  286. else
  287. {
  288. Type type = value.GetType();
  289. int simpleKind;
  290. if (_typeMap.TryGetValue(type, out simpleKind))
  291. {
  292. switch (simpleKind)
  293. {
  294. case AmfMarker.True:
  295. Write((bool)value);
  296. break;
  297. case AmfMarker.Integer:
  298. Write((int)value);
  299. break;
  300. case AmfMarker.Double:
  301. Write((double)value);
  302. break;
  303. case AmfMarker.String:
  304. Write((string)value);
  305. break;
  306. case AmfMarker.Date:
  307. Write((DateTime)value);
  308. break;
  309. case AmfMarker.Array:
  310. Write((AmfArray)value);
  311. break;
  312. case AmfMarker.ByteArray:
  313. Write((byte[])value);
  314. break;
  315. default:
  316. throw new NotImplementedException();
  317. }
  318. }
  319. else
  320. {
  321. WriteObject(type, value);
  322. }
  323. }
  324. }
  325. public void Dispose()
  326. {
  327. if (_writer != null)
  328. {
  329. _writer.Close();
  330. _writer = null;
  331. }
  332. }
  333. }
  334. }