PageRenderTime 47ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/Rider/Utils/SilverlightSerializer.cs

https://bitbucket.org/lduparc/berider
C# | 1333 lines | 908 code | 134 blank | 291 comment | 241 complexity | 7edcf2e6f9c5bfd2896b9ba244465a87 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. // SilverlightSerializer by Mike Talbot
  2. // http://whydoidoit.com
  3. // email: mike.talbot@alterian.com
  4. // twitter: mike_talbot
  5. //
  6. // This code is free to use, no warranty is offered or implied.
  7. // If you redistribute, please retain this header.
  8. #region
  9. using System;
  10. using System.Collections;
  11. using System.Collections.Generic;
  12. using System.Diagnostics;
  13. using System.IO;
  14. using System.Linq;
  15. using System.Reflection;
  16. #endregion
  17. namespace Rider.Utils
  18. {
  19. /// <summary>
  20. /// Indicates that a property or field should not be serialized
  21. /// </summary>
  22. [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
  23. public class DoNotSerialize : Attribute
  24. {
  25. }
  26. /// <summary>
  27. /// Used in checksum mode to flag a property as not being part
  28. /// of the "meaning" of an object - i.e. two objects with the
  29. /// same checksum "mean" the same thing, even if some of the
  30. /// properties are different, those properties would not be
  31. /// relevant to the purpose of the object
  32. /// </summary>
  33. [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
  34. public class DoNotChecksum : Attribute
  35. {
  36. }
  37. /// <summary>
  38. /// Attribute used to flag IDs this can be useful for check object
  39. /// consistence when the serializer is in a mode that does not
  40. /// serialize identifiers
  41. /// </summary>
  42. [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
  43. public class SerializerId : Attribute
  44. {
  45. }
  46. public interface ISerializeObject
  47. {
  48. object[] Serialize(object target);
  49. object Deserialize(object[] data);
  50. }
  51. [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
  52. public class SerializerAttribute : Attribute
  53. {
  54. internal Type SerializesType;
  55. public SerializerAttribute(Type serializesType)
  56. {
  57. SerializesType = serializesType;
  58. }
  59. }
  60. /// <summary>
  61. /// Silverlight/.NET compatible binary serializer with suppression support
  62. /// produces compact representations, suitable for further compression
  63. /// </summary>
  64. public static class SilverlightSerializer
  65. {
  66. private static readonly Dictionary<Type, IEnumerable<FieldInfo>> FieldLists = new Dictionary<Type, IEnumerable<FieldInfo>>();
  67. private static readonly Dictionary<string, IEnumerable<PropertyInfo>> PropertyLists = new Dictionary<string, IEnumerable<PropertyInfo>>();
  68. private static readonly Dictionary<string, IEnumerable<PropertyInfo>> ChecksumLists = new Dictionary<string, IEnumerable<PropertyInfo>>();
  69. [ThreadStatic]
  70. private static List<Type> _knownTypes;
  71. [ThreadStatic]
  72. private static Dictionary<object, int> _seenObjects;
  73. [ThreadStatic]
  74. private static List<object> _loadedObjects;
  75. [ThreadStatic]
  76. private static List<string> _propertyIds;
  77. [ThreadStatic]
  78. private static Stack<List<object>> _loStack;
  79. [ThreadStatic]
  80. private static Stack<Dictionary<object, int>> _soStack;
  81. [ThreadStatic]
  82. private static Stack<List<Type>> _ktStack;
  83. [ThreadStatic]
  84. private static Stack<List<string>> _piStack;
  85. [ThreadStatic]
  86. private static bool _isChecksum;
  87. [ThreadStatic]
  88. public static bool IgnoreIds;
  89. /// <summary>
  90. /// Arguments for a missing type event
  91. /// </summary>
  92. public class TypeMappingEventArgs : EventArgs
  93. {
  94. /// <summary>
  95. /// The missing types name
  96. /// </summary>
  97. public string TypeName = string.Empty;
  98. /// <summary>
  99. /// Supply a type to use instead
  100. /// </summary>
  101. public Type UseType = null;
  102. }
  103. /// <summary>
  104. /// Event that is fired if a particular type cannot be found
  105. /// </summary>
  106. public static event EventHandler<TypeMappingEventArgs> MapMissingType;
  107. private static void InvokeMapMissingType(TypeMappingEventArgs e)
  108. {
  109. EventHandler<TypeMappingEventArgs> handler = MapMissingType;
  110. if (handler != null)
  111. handler(null, e);
  112. }
  113. /// <summary>
  114. /// Put the serializer into Checksum mode
  115. /// </summary>
  116. public static bool IsChecksum
  117. {
  118. get
  119. {
  120. return _isChecksum;
  121. }
  122. set
  123. {
  124. _isChecksum = value;
  125. }
  126. }
  127. /// <summary>
  128. /// Deserialize to a type
  129. /// </summary>
  130. /// <param name="array"></param>
  131. /// <returns></returns>
  132. public static T Deserialize<T>(byte[] array) where T : class
  133. {
  134. return Deserialize(array) as T;
  135. }
  136. /// <summary>
  137. /// Deserialize from a stream to a type
  138. /// </summary>
  139. /// <param name="stream"></param>
  140. /// <returns></returns>
  141. public static T Deserialize<T>(Stream stream) where T : class
  142. {
  143. return Deserialize(stream) as T;
  144. }
  145. /// <summary>
  146. /// Get a checksum for an item. Checksums "should" be different
  147. /// for every object that has a different "meaning". You can
  148. /// flag properties as DoNotChecksum if that helps to keep decorative
  149. /// properties away from the checksum whilst including meaningful ones
  150. /// </summary>
  151. /// <param name="item">The object to checksum</param>
  152. /// <returns>A checksum string, this includes no illegal characters and can be used as a file name</returns>
  153. public static string GetChecksum(object item)
  154. {
  155. if (item == null)
  156. return "";
  157. byte[] checksum = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
  158. var isChecksum = SilverlightSerializer.IsChecksum;
  159. SilverlightSerializer.IsChecksum = true;
  160. var toBytes = SilverlightSerializer.Serialize(item);
  161. SilverlightSerializer.IsChecksum = isChecksum;
  162. for (var i = 0; i < toBytes.Length; i++)
  163. {
  164. checksum[i & 15] ^= toBytes[i];
  165. }
  166. return toBytes.Count().ToString() + Encode(checksum);
  167. }
  168. private static string Encode(byte[] checksum)
  169. {
  170. var s = Convert.ToBase64String(checksum);
  171. return s.Aggregate("", (current, c) => current + (Char.IsLetterOrDigit(c)
  172. ? c
  173. : Char.GetNumericValue(c)));
  174. }
  175. //Holds a reference to the custom serializers
  176. private static readonly Dictionary<Type, ISerializeObject> Serializers = new Dictionary<Type, ISerializeObject>();
  177. //Dictionary to ensure we only scan an assembly once
  178. private static readonly Dictionary<Assembly, bool> Assemblies = new Dictionary<Assembly, bool>();
  179. /// <summary>
  180. /// Register all of the custom serializers in an assembly
  181. /// </summary>
  182. /// <param name="assembly">Leave blank to register the assembly that the method is called from, or pass an assembly</param>
  183. public static void RegisterSerializationAssembly(Assembly assembly = null)
  184. {
  185. if (assembly == null)
  186. assembly = Assembly.GetCallingAssembly();
  187. if (Assemblies.ContainsKey(assembly))
  188. return;
  189. Assemblies[assembly] = true;
  190. ScanAllTypesForAttribute((tp, attr) =>
  191. {
  192. Serializers[((SerializerAttribute)attr).SerializesType] = Activator.CreateInstance(tp) as ISerializeObject;
  193. }, assembly, typeof(SerializerAttribute));
  194. }
  195. //Function to be called when scanning types
  196. internal delegate void ScanTypeFunction(Type type, Attribute attribute);
  197. /// <summary>
  198. /// Scan all of the types in an assembly for a particular attribute
  199. /// </summary>
  200. /// <param name="function">The function to call</param>
  201. /// <param name="assembly">The assembly to scan</param>
  202. /// <param name="attribute">The attribute to look for</param>
  203. internal static void ScanAllTypesForAttribute(ScanTypeFunction function, Assembly assembly, Type attribute = null)
  204. {
  205. try
  206. {
  207. foreach (var tp in assembly.GetTypes())
  208. {
  209. if (attribute != null)
  210. {
  211. var attrs = Attribute.GetCustomAttributes(tp, attribute, false);
  212. if (attrs != null)
  213. {
  214. foreach (var attr in attrs)
  215. function(tp, attr);
  216. }
  217. }
  218. else
  219. function(tp, null);
  220. }
  221. }
  222. catch (Exception)
  223. {
  224. }
  225. }
  226. /// <summary>
  227. /// Dictionary of all the used objects to check if properties are different
  228. /// to those set during construction
  229. /// </summary>
  230. private static readonly Dictionary<Type, object> Vanilla = new Dictionary<Type, object>();
  231. /// <summary>
  232. /// Write persistence debugging information to the debug output window
  233. /// often used with Verbose
  234. /// </summary>
  235. public static bool IsLoud;
  236. /// <summary>
  237. /// Write all types, even if they are known, often used with Loud mode
  238. /// </summary>
  239. public static bool Verbose;
  240. /// <summary>
  241. /// Caches and returns property info for a type
  242. /// </summary>
  243. /// <param name = "itm">The type that should have its property info returned</param>
  244. /// <returns>An enumeration of PropertyInfo objects</returns>
  245. /// <remarks>
  246. /// It should be noted that the implementation converts the enumeration returned from reflection to an array as this more than double the speed of subsequent reads
  247. /// </remarks>
  248. private static IEnumerable<PropertyInfo> GetPropertyInfo(Type itm)
  249. {
  250. lock (PropertyLists)
  251. {
  252. IEnumerable<PropertyInfo> ret = null;
  253. Debug.Assert(itm.AssemblyQualifiedName != null);
  254. if (!IsChecksum)
  255. {
  256. if (!PropertyLists.TryGetValue(itm.AssemblyQualifiedName, out ret))
  257. {
  258. ret = itm.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.GetCustomAttributes(typeof(DoNotSerialize), false).Count() == 0 && !(p.GetIndexParameters().Count() > 0) && (p.GetSetMethod() != null)).ToArray();
  259. PropertyLists[itm.AssemblyQualifiedName] = ret;
  260. }
  261. }
  262. else
  263. {
  264. if (!ChecksumLists.TryGetValue(itm.AssemblyQualifiedName, out ret))
  265. {
  266. ret = itm.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.GetCustomAttributes(typeof(DoNotSerialize), false).Count() == 0 && p.GetCustomAttributes(typeof(DoNotChecksum), true).Count() == 0 && !(p.GetIndexParameters().Count() > 0) && (p.GetSetMethod() != null)).ToArray();
  267. ChecksumLists[itm.AssemblyQualifiedName] = ret;
  268. }
  269. }
  270. return IgnoreIds && ret != null
  271. ? ret.Where(p => p.GetCustomAttributes(typeof(SerializerId), true).Count() == 0)
  272. : ret;
  273. }
  274. }
  275. /// <summary>
  276. /// Caches and returns field info for a type
  277. /// </summary>
  278. /// <param name = "itm">The type that should have its field info returned</param>
  279. /// <returns>An enumeration of FieldInfo objects</returns>
  280. /// <remarks>
  281. /// It should be noted that the implementation converts the enumeration returned from reflection to an array as this more than double the speed of subsequent reads
  282. /// </remarks>
  283. private static IEnumerable<FieldInfo> GetFieldInfo(Type itm)
  284. {
  285. lock (FieldLists)
  286. {
  287. IEnumerable<FieldInfo> ret = null;
  288. if (FieldLists.ContainsKey(itm))
  289. ret = FieldLists[itm];
  290. else
  291. {
  292. ret = FieldLists[itm] = itm.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetField).Where(p => p.GetCustomAttributes(typeof(DoNotSerialize), false).Count() == 0).ToArray();
  293. }
  294. return IsChecksum ? ret.Where(p => p.GetCustomAttributes(typeof(DoNotChecksum), true).Count() == 0) : ret;
  295. }
  296. }
  297. /// <summary>
  298. /// Returns a token that represents the name of the property
  299. /// </summary>
  300. /// <param name = "name">The name for which to return a token</param>
  301. /// <returns>A 2 byte token representing the name</returns>
  302. private static ushort GetPropertyDefinitionId(string name)
  303. {
  304. lock (_propertyIds)
  305. {
  306. var ret = _propertyIds.IndexOf(name);
  307. if (ret >= 0)
  308. return (ushort)ret;
  309. _propertyIds.Add(name);
  310. return (ushort)(_propertyIds.Count - 1);
  311. }
  312. }
  313. /// <summary>
  314. /// Deserializes from a stream, potentially into an existing instance
  315. /// </summary>
  316. /// <param name="inputStream">Stream to deserialize from</param>
  317. /// <param name="instance">Instance to use</param>
  318. /// <returns></returns>
  319. public static object Deserialize(Stream inputStream, object instance = null)
  320. {
  321. var v = Verbose;
  322. CreateStacks();
  323. try
  324. {
  325. _ktStack.Push(_knownTypes);
  326. _piStack.Push(_propertyIds);
  327. _loStack.Push(_loadedObjects);
  328. var rw = new BinaryReader(inputStream);
  329. var version = rw.ReadString();
  330. var count = rw.ReadInt32();
  331. if (version == "SerV3")
  332. Verbose = rw.ReadBoolean();
  333. _propertyIds = new List<string>();
  334. _knownTypes = new List<Type>();
  335. _loadedObjects = new List<object>();
  336. for (var i = 0; i < count; i++)
  337. {
  338. var typeName = rw.ReadString();
  339. var tp = Type.GetType(typeName);
  340. if (tp == null)
  341. {
  342. var map = new TypeMappingEventArgs
  343. {
  344. TypeName = typeName
  345. };
  346. InvokeMapMissingType(map);
  347. tp = map.UseType;
  348. }
  349. if (!Verbose)
  350. if (tp == null)
  351. throw new ArgumentException(string.Format("Cannot reference type {0} in this context", typeName));
  352. _knownTypes.Add(tp);
  353. }
  354. count = rw.ReadInt32();
  355. for (var i = 0; i < count; i++)
  356. {
  357. _propertyIds.Add(rw.ReadString());
  358. }
  359. return DeserializeObject(rw, null, instance);
  360. }
  361. finally
  362. {
  363. _knownTypes = _ktStack.Pop();
  364. _propertyIds = _piStack.Pop();
  365. _loadedObjects = _loStack.Pop();
  366. Verbose = v;
  367. }
  368. }
  369. /// <summary>
  370. /// Convert a previously serialized object from a byte array
  371. /// back into a .NET object
  372. /// </summary>
  373. /// <param name = "bytes">The data stream for the object</param>
  374. /// <returns>The rehydrated object represented by the data supplied</returns>
  375. public static object Deserialize(byte[] bytes)
  376. {
  377. using (MemoryStream inputStream = new MemoryStream(bytes))
  378. {
  379. return Deserialize(inputStream);
  380. }
  381. }
  382. /// <summary>
  383. /// Convert a previously serialized object from a byte array
  384. /// back into a .NET object
  385. /// </summary>
  386. /// <param name = "bytes">The data stream for the object</param>
  387. /// <returns>The rehydrated object represented by the data supplied</returns>
  388. public static void DeserializeInto(byte[] bytes, object instance)
  389. {
  390. using (MemoryStream inputStream = new MemoryStream(bytes))
  391. {
  392. Deserialize(inputStream, instance);
  393. }
  394. }
  395. /// <summary>
  396. /// Creates a set of stacks on the current thread
  397. /// </summary>
  398. private static void CreateStacks()
  399. {
  400. if (_piStack == null)
  401. _piStack = new Stack<List<string>>();
  402. if (_ktStack == null)
  403. _ktStack = new Stack<List<Type>>();
  404. if (_loStack == null)
  405. _loStack = new Stack<List<object>>();
  406. if (_soStack == null)
  407. _soStack = new Stack<Dictionary<object, int>>();
  408. }
  409. /// <summary>
  410. /// Deserializes an object or primitive from the stream
  411. /// </summary>
  412. /// <param name = "reader">The reader of the binary file</param>
  413. /// <param name = "itemType">The expected type of the item being read (supports compact format)</param>
  414. /// <returns>The value read from the file</returns>
  415. /// <remarks>
  416. /// The function is supplied with the type of the property that the object was stored in (if known) this enables
  417. /// a compact format where types only have to be specified if they differ from the expected one
  418. /// </remarks>
  419. private static object DeserializeObject(BinaryReader reader, Type itemType = null, object instance = null)
  420. {
  421. var tpId = (ushort)reader.ReadUInt16();
  422. if (tpId == 0xFFFE)
  423. return null;
  424. //Lookup the value type if necessary
  425. if (tpId != 0xffff || itemType == null)
  426. itemType = _knownTypes[tpId];
  427. object obj = null;
  428. if (itemType != null)
  429. {
  430. //Check for custom serialization
  431. if (Serializers.ContainsKey(itemType))
  432. {
  433. //Read the serializer and its data
  434. var serializer = Serializers[itemType];
  435. object[] data = DeserializeObject(reader, typeof(object[])) as object[];
  436. return serializer.Deserialize(data);
  437. }
  438. //Check if this is a simple value and read it if so
  439. if (IsSimpleType(itemType))
  440. {
  441. if (itemType.IsEnum)
  442. {
  443. return Enum.Parse(itemType, ReadValue(reader, typeof(int)).ToString(), true);
  444. }
  445. return ReadValue(reader, itemType);
  446. }
  447. }
  448. //See if we should lookup this object or create a new one
  449. var found = reader.ReadChar();
  450. if (found == 'S') //S is for Seen
  451. return _loadedObjects[reader.ReadInt32()];
  452. if (itemType != null)
  453. {
  454. //Otherwise create the object
  455. if (itemType.IsArray)
  456. {
  457. int baseCount = reader.ReadInt32();
  458. if (baseCount == -1)
  459. {
  460. return DeserializeMultiDimensionArray(itemType, reader, baseCount);
  461. }
  462. else
  463. {
  464. return DeserializeArray(itemType, reader, baseCount);
  465. }
  466. }
  467. obj = instance ?? CreateObject(itemType);
  468. _loadedObjects.Add(obj);
  469. }
  470. //Check for collection types)
  471. if (obj is IDictionary)
  472. return DeserializeDictionary(obj as IDictionary, itemType, reader);
  473. if (obj is IList)
  474. return DeserializeList(obj as IList, itemType, reader);
  475. //Otherwise we are serializing an object
  476. return DeserializeObjectAndProperties(obj, itemType, reader);
  477. }
  478. /// <summary>
  479. /// Deserializes an array of values
  480. /// </summary>
  481. /// <param name = "itemType">The type of the array</param>
  482. /// <param name = "reader">The reader of the stream</param>
  483. /// <returns>The deserialized array</returns>
  484. /// <remarks>
  485. /// This routine optimizes for arrays of primitives and bytes
  486. /// </remarks>
  487. private static object DeserializeArray(Type itemType, BinaryReader reader, int count)
  488. {
  489. // If the count is -1 at this point, then it is being called from the
  490. // deserialization of a multi-dimensional array - so we need
  491. // to read the size of the array
  492. if (count == -1)
  493. {
  494. count = reader.ReadInt32();
  495. }
  496. //Get the expected element type
  497. var elementType = itemType.GetElementType();
  498. //Optimize for byte arrays
  499. if (elementType == typeof(byte))
  500. {
  501. var ret = reader.ReadBytes(count);
  502. _loadedObjects.Add(ret);
  503. return ret;
  504. }
  505. //Create an array of the correct type
  506. var array = Array.CreateInstance(elementType, count);
  507. _loadedObjects.Add(array);
  508. //Check whether the array contains primitives, if it does we don't
  509. //need to store the type of each member
  510. if (IsSimpleType(elementType))
  511. for (var l = 0; l < count; l++)
  512. {
  513. array.SetValue(ReadValue(reader, elementType), l);
  514. }
  515. else
  516. for (var l = 0; l < count; l++)
  517. {
  518. array.SetValue(DeserializeObject(reader, elementType), l);
  519. }
  520. return array;
  521. }
  522. /// <summary>
  523. /// Deserializes a multi-dimensional array of values
  524. /// </summary>
  525. /// <param name = "itemType">The type of the array</param>
  526. /// <param name = "reader">The reader of the stream</param>
  527. /// <param name="count">The base size of the multi-dimensional array</param>
  528. /// <returns>The deserialized array</returns>
  529. /// <remarks>
  530. /// This routine deserializes values serialized on a 'row by row' basis, and
  531. /// calls into DeserializeArray to do this
  532. /// </remarks>
  533. private static object DeserializeMultiDimensionArray(Type itemType, BinaryReader reader, int count)
  534. {
  535. //Read the number of dimensions the array has
  536. var dimensions = reader.ReadInt32();
  537. var totalLength = reader.ReadInt32();
  538. int rowLength = 0;
  539. // Establish the length of each array element
  540. // and get the total 'row size'
  541. int[] lengths = new int[dimensions];
  542. int[] indices = new int[dimensions];
  543. for (int item = 0; item < dimensions; item++)
  544. {
  545. lengths[item] = reader.ReadInt32();
  546. rowLength += lengths[item];
  547. indices[item] = 0;
  548. }
  549. int cols = lengths[lengths.Length - 1];
  550. //int cols = dimensions == 1 ? 1 : lengths[lengths.Length - 1];
  551. //Get the expected element type
  552. var elementType = itemType.GetElementType();
  553. Array sourceArrays = Array.CreateInstance(elementType, lengths);
  554. DeserializeArrayPart(sourceArrays, 0, indices, itemType, reader);
  555. return sourceArrays;
  556. }
  557. private static void DeserializeArrayPart(Array sourceArrays, int i, int[] indices, Type itemType, BinaryReader binaryReader)
  558. {
  559. int length = sourceArrays.GetLength(i);
  560. for (var l = 0; l < length; l++)
  561. {
  562. indices[i] = l;
  563. if (i != sourceArrays.Rank - 2)
  564. DeserializeArrayPart(sourceArrays, i + 1, indices, itemType, binaryReader);
  565. else
  566. {
  567. Array sourceArray = (Array)DeserializeArray(itemType, binaryReader, -1);
  568. int cols = sourceArrays.GetLength(i + 1);
  569. for (int arrayStartIndex = 0; arrayStartIndex < cols; arrayStartIndex++)
  570. {
  571. indices[i + 1] = arrayStartIndex;
  572. sourceArrays.SetValue(sourceArray.GetValue(arrayStartIndex), indices);
  573. }
  574. }
  575. }
  576. }
  577. /// <summary>
  578. /// Deserializes a dictionary from storage, handles generic types with storage optimization
  579. /// </summary>
  580. /// <param name = "o">The newly created dictionary</param>
  581. /// <param name = "itemType">The type of the dictionary</param>
  582. /// <param name = "reader">The binary reader for the current bytes</param>
  583. /// <returns>The dictionary object updated with the values from storage</returns>
  584. private static object DeserializeDictionary(IDictionary o, Type itemType, BinaryReader reader)
  585. {
  586. Type keyType = null;
  587. Type valueType = null;
  588. if (itemType.IsGenericType)
  589. {
  590. var types = itemType.GetGenericArguments();
  591. keyType = types[0];
  592. valueType = types[1];
  593. }
  594. var count = reader.ReadInt32();
  595. var list = new List<object>();
  596. for (var i = 0; i < count; i++)
  597. {
  598. list.Add(DeserializeObject(reader, keyType));
  599. }
  600. for (var i = 0; i < count; i++)
  601. {
  602. o[list[i]] = DeserializeObject(reader, valueType);
  603. }
  604. return o;
  605. }
  606. /// <summary>
  607. /// Deserialize a list from the data stream
  608. /// </summary>
  609. /// <param name = "o">The newly created list</param>
  610. /// <param name = "itemType">The type of the list</param>
  611. /// <param name = "reader">The reader for the current bytes</param>
  612. /// <returns>The list updated with values from the stream</returns>
  613. private static object DeserializeList(IList o, Type itemType, BinaryReader reader)
  614. {
  615. Type valueType = null;
  616. if (itemType.IsGenericType)
  617. {
  618. var types = itemType.GetGenericArguments();
  619. valueType = types[0];
  620. }
  621. var count = reader.ReadInt32();
  622. var list = new List<object>();
  623. for (var i = 0; i < count; i++)
  624. {
  625. o.Add(DeserializeObject(reader, valueType));
  626. }
  627. return o;
  628. }
  629. /// <summary>
  630. /// Deserializes a class based object that is not a collection, looks for both public properties and fields
  631. /// </summary>
  632. /// <param name = "o">The object being deserialized</param>
  633. /// <param name = "itemType">The type of the object</param>
  634. /// <param name = "reader">The reader for the current stream of bytes</param>
  635. /// <returns>The object updated with values from the stream</returns>
  636. private static object DeserializeObjectAndProperties(object o, Type itemType, BinaryReader reader)
  637. {
  638. DeserializeProperties(reader, itemType, o);
  639. DeserializeFields(reader, itemType, o);
  640. return o;
  641. }
  642. /// <summary>
  643. /// Deserializes the properties of an object from the stream
  644. /// </summary>
  645. /// <param name = "reader">The reader of the bytes in the stream</param>
  646. /// <param name = "itemType">The type of the object</param>
  647. /// <param name = "o">The object to deserialize</param>
  648. private static void DeserializeProperties(BinaryReader reader, Type itemType, object o)
  649. {
  650. //Get the number of properties
  651. var propCount = reader.ReadByte();
  652. int length = 0;
  653. if (Verbose)
  654. length = reader.ReadInt32();
  655. if (o == null)
  656. {
  657. reader.BaseStream.Seek(length, SeekOrigin.Current);
  658. return;
  659. }
  660. for (var i = 0; i < propCount; i++)
  661. {
  662. //Get a property name identifier
  663. var propId = reader.ReadUInt16();
  664. //Lookup the name
  665. var propName = _propertyIds[propId];
  666. //Use the name to find the type
  667. var propType = itemType.GetProperty(propName);
  668. //Deserialize the value
  669. var value = DeserializeObject(reader, propType != null ? propType.PropertyType : null);
  670. if (propType != null && value != null)
  671. {
  672. try
  673. {
  674. propType.SetValue(o, value, null);
  675. }
  676. catch (Exception)
  677. {
  678. //Suppress cases where the old value is no longer compatible with the new property type
  679. }
  680. }
  681. }
  682. }
  683. /// <summary>
  684. /// Deserializes the fields of an object from the stream
  685. /// </summary>
  686. /// <param name = "reader">The reader of the bytes in the stream</param>
  687. /// <param name = "itemType">The type of the object</param>
  688. /// <param name = "o">The object to deserialize</param>
  689. private static void DeserializeFields(BinaryReader reader, Type itemType, object o)
  690. {
  691. var fieldCount = reader.ReadByte();
  692. int length = 0;
  693. if (Verbose)
  694. length = reader.ReadInt32();
  695. if (o == null)
  696. {
  697. reader.BaseStream.Seek(length, SeekOrigin.Current);
  698. return;
  699. }
  700. for (var i = 0; i < fieldCount; i++)
  701. {
  702. var fieldId = reader.ReadUInt16();
  703. var fieldName = _propertyIds[fieldId];
  704. var fieldType = itemType.GetField(fieldName);
  705. var value = DeserializeObject(reader, fieldType != null ? fieldType.FieldType : null);
  706. if (fieldType != null && value != null)
  707. {
  708. try
  709. {
  710. fieldType.SetValue(o, value);
  711. }
  712. catch (Exception)
  713. {
  714. //Suppress cases where the old value is no longer compatible with the new property type
  715. }
  716. }
  717. }
  718. }
  719. public static void Serialize(object item, Stream outputStream)
  720. {
  721. CreateStacks();
  722. try
  723. {
  724. _ktStack.Push(_knownTypes);
  725. _piStack.Push(_propertyIds);
  726. _soStack.Push(_seenObjects);
  727. _propertyIds = new List<string>();
  728. _knownTypes = new List<Type>();
  729. _seenObjects = new Dictionary<object, int>();
  730. var strm = new MemoryStream();
  731. var wr = new BinaryWriter(strm);
  732. SerializeObject(item, wr);
  733. var outputWr = new BinaryWriter(outputStream);
  734. outputWr.Write("SerV3");
  735. outputWr.Write(_knownTypes.Count);
  736. //New, store the verbose property
  737. outputWr.Write(Verbose);
  738. foreach (var kt in _knownTypes)
  739. {
  740. outputWr.Write(kt.AssemblyQualifiedName);
  741. }
  742. outputWr.Write(_propertyIds.Count);
  743. foreach (var pi in _propertyIds)
  744. {
  745. outputWr.Write(pi);
  746. }
  747. strm.WriteTo(outputStream);
  748. }
  749. finally
  750. {
  751. _knownTypes = _ktStack.Pop();
  752. _propertyIds = _piStack.Pop();
  753. _seenObjects = _soStack.Pop();
  754. }
  755. }
  756. /// <summary>
  757. /// Serialize an object into an array of bytes
  758. /// </summary>
  759. /// <param name = "item">The object to serialize</param>
  760. /// <returns>A byte array representation of the item</returns>
  761. public static byte[] Serialize(object item)
  762. {
  763. using (MemoryStream outputStream = new MemoryStream())
  764. {
  765. Serialize(item, outputStream);
  766. //Reset the verbose mode
  767. return outputStream.ToArray();
  768. }
  769. }
  770. /// <summary>
  771. /// Serialize an object into an array of bytes
  772. /// </summary>
  773. /// <param name = "item">The object to serialize</param>
  774. /// <param name="makeVerbose">Whether the object should be serialized for forwards compatibility</param>
  775. /// <returns>A byte array representation of the item</returns>
  776. public static byte[] Serialize(object item, bool makeVerbose)
  777. {
  778. using (MemoryStream outputStream = new MemoryStream())
  779. {
  780. var v = Verbose;
  781. Verbose = makeVerbose;
  782. Serialize(item, outputStream);
  783. Verbose = v;
  784. //Reset the verbose mode
  785. return outputStream.ToArray();
  786. }
  787. }
  788. private static void SerializeObject(object item, BinaryWriter writer, Type propertyType = null)
  789. {
  790. if (item == null)
  791. {
  792. writer.Write((ushort)0xFFFE);
  793. return;
  794. }
  795. var itemType = item.GetType();
  796. Debug.Assert(itemType != null);
  797. //If this isn't a simple type, then this might be a subclass so we need to
  798. //store the type
  799. if (propertyType != itemType || Verbose)
  800. {
  801. //Write the type identifier
  802. var tpId = GetTypeId(itemType);
  803. writer.Write(tpId);
  804. }
  805. else
  806. //Write a dummy identifier
  807. writer.Write((ushort)0xFFFF);
  808. //Check for custom serialization
  809. if (Serializers.ContainsKey(itemType))
  810. {
  811. //If we have a custom serializer then use it!
  812. var serializer = Serializers[itemType];
  813. var data = serializer.Serialize(item);
  814. SerializeObject(data, writer, typeof(object[]));
  815. return;
  816. }
  817. //Check for simple types again
  818. if (IsSimpleType(itemType))
  819. {
  820. if (itemType.IsEnum)
  821. WriteValue(writer, (int)item);
  822. else
  823. WriteValue(writer, item);
  824. return;
  825. }
  826. //Check whether this object has been seen
  827. if (_seenObjects.ContainsKey(item))
  828. {
  829. writer.Write('S');
  830. writer.Write(_seenObjects[item]);
  831. return;
  832. }
  833. //We are going to serialize an object
  834. writer.Write('O');
  835. _seenObjects[item] = _seenObjects.Count;
  836. //Check for collection types)
  837. if (item is Array)
  838. {
  839. if (((Array)item).Rank == 1)
  840. {
  841. SerializeArray(item as Array, itemType, writer);
  842. }
  843. else
  844. {
  845. SerializeMultiDimensionArray(item as Array, itemType, writer);
  846. }
  847. return;
  848. }
  849. if (item is IDictionary)
  850. {
  851. SerializeDictionary(item as IDictionary, itemType, writer);
  852. return;
  853. }
  854. if (item is IList)
  855. {
  856. SerializeList(item as IList, itemType, writer);
  857. return;
  858. }
  859. //Otherwise we are serializing an object
  860. SerializeObjectAndProperties(item, itemType, writer);
  861. }
  862. private static void SerializeList(IList item, Type tp, BinaryWriter writer)
  863. {
  864. Type valueType = null;
  865. //Try to optimize the storage of types based on the type of list
  866. if (tp.IsGenericType)
  867. {
  868. var types = tp.GetGenericArguments();
  869. valueType = types[0];
  870. }
  871. writer.Write(item.Count);
  872. foreach (var val in item)
  873. {
  874. SerializeObject(val, writer, valueType);
  875. }
  876. }
  877. private static void SerializeDictionary(IDictionary item, Type tp, BinaryWriter writer)
  878. {
  879. Type keyType = null;
  880. Type valueType = null;
  881. //Try to optimise storage based on the type of dictionary
  882. if (tp.IsGenericType)
  883. {
  884. var types = tp.GetGenericArguments();
  885. keyType = types[0];
  886. valueType = types[1];
  887. }
  888. //Write out the size
  889. writer.Write(item.Count);
  890. //Serialize the pairs
  891. foreach (var key in item.Keys)
  892. {
  893. SerializeObject(key, writer, keyType);
  894. }
  895. foreach (var val in item.Values)
  896. {
  897. SerializeObject(val, writer, valueType);
  898. }
  899. }
  900. private static void SerializeArray(Array item, Type tp, BinaryWriter writer)
  901. {
  902. var length = item.Length;
  903. writer.Write(length);
  904. var propertyType = tp.GetElementType();
  905. //Special optimization for arrays of byte
  906. if (propertyType == typeof(byte))
  907. writer.Write((byte[])item, 0, length);
  908. //Special optimization for arrays of simple types
  909. //which don't need to have the entry type stored
  910. //for each item
  911. else if (IsSimpleType(propertyType))
  912. for (var l = 0; l < length; l++)
  913. {
  914. WriteValue(writer, item.GetValue(l));
  915. }
  916. else
  917. for (var l = 0; l < length; l++)
  918. {
  919. SerializeObject(item.GetValue(l), writer, propertyType);
  920. }
  921. }
  922. private static void SerializeMultiDimensionArray(Array item, Type tp, BinaryWriter writer)
  923. {
  924. // Multi-dimension serializer data is:
  925. // Int32: Ranks
  926. // Int32 (x number of ranks): length of array dimension
  927. int dimensions = item.Rank;
  928. var length = item.GetLength(0);
  929. // Determine the number of cols being populated
  930. var cols = item.GetLength(item.Rank - 1);
  931. // Explicitly write this value, to denote that this is a multi-dimensional array
  932. // so it doesn't break the deserializer when reading values for existing arrays
  933. writer.Write((int)-1);
  934. writer.Write(dimensions);
  935. writer.Write(item.Length);
  936. var propertyType = tp.GetElementType();
  937. var indicies = new int[dimensions];
  938. // Write out the length of each array, if we are dealing with the first array
  939. for (int arrayStartIndex = 0; arrayStartIndex < dimensions; arrayStartIndex++)
  940. {
  941. indicies[arrayStartIndex] = 0;
  942. writer.Write(item.GetLength(arrayStartIndex));
  943. }
  944. SerializeArrayPart(item, 0, indicies, writer);
  945. }
  946. private static void SerializeArrayPart(Array item, int i, int[] indices, BinaryWriter writer)
  947. {
  948. var length = item.GetLength(i);
  949. for (var l = 0; l < length; l++)
  950. {
  951. indices[i] = l;
  952. if (i != item.Rank - 2)
  953. SerializeArrayPart(item, i + 1, indices, writer);
  954. else
  955. {
  956. Type arrayType = item.GetType().GetElementType();
  957. var cols = item.GetLength(i + 1);
  958. var baseArray = Array.CreateInstance(arrayType, cols);
  959. // Convert the whole multi-dimensional array to be 'row' based
  960. // and serialize using the existing code
  961. for (int arrayStartIndex = 0; arrayStartIndex < cols; arrayStartIndex++)
  962. {
  963. indices[i + 1] = arrayStartIndex;
  964. baseArray.SetValue(item.GetValue(indices), arrayStartIndex);
  965. }
  966. SerializeArray(baseArray, baseArray.GetType(), writer);
  967. }
  968. }
  969. }
  970. /// <summary>
  971. /// Return whether the type specified is a simple type that can be serialized fast
  972. /// </summary>
  973. /// <param name = "tp">The type to check</param>
  974. /// <returns>True if the type is a simple one and can be serialized directly</returns>
  975. private static bool IsSimpleType(Type tp)
  976. {
  977. return tp.IsPrimitive || tp == typeof(DateTime) || tp == typeof(TimeSpan) || tp == typeof(string) || tp.IsEnum || tp == typeof(Guid) || tp == typeof(decimal);
  978. }
  979. private static void SerializeObjectAndProperties(object item, Type itemType, BinaryWriter writer)
  980. {
  981. lock (Vanilla)
  982. {
  983. if (Vanilla.ContainsKey(itemType) == false)
  984. {
  985. Vanilla[itemType] = CreateObject(itemType);
  986. }
  987. }
  988. WriteProperties(itemType, item, writer);
  989. WriteFields(itemType, item, writer);
  990. }
  991. private static object CreateObject(Type itemType)
  992. {
  993. try
  994. {
  995. return Activator.CreateInstance(itemType);
  996. }
  997. catch (Exception)
  998. {
  999. return itemType.GetConstructor(new Type[] { }).Invoke(new object[] { });
  1000. }
  1001. }
  1002. private static void WriteProperties(Type itemType, object item, BinaryWriter writer)
  1003. {
  1004. var propertyStream = new MemoryStream();
  1005. var pw = new BinaryWriter(propertyStream);
  1006. byte propCount = 0;
  1007. //Get the properties of the object
  1008. var properties = GetPropertyInfo(itemType);
  1009. foreach (var property in properties)
  1010. {
  1011. if (IsChecksum && IsLoud)
  1012. {
  1013. Debug.WriteLine(string.Format(" ----> {0} on {1}", property.Name, item.ToString()));
  1014. }
  1015. var value = property.GetValue(item, null);
  1016. //Don't store null values
  1017. if (value == null)
  1018. continue;
  1019. //Don't store empty collections
  1020. if (value is ICollection)
  1021. if ((value as ICollection).Count == 0)
  1022. continue;
  1023. //Don't store empty arrays
  1024. if (value is Array)
  1025. if ((value as Array).Length == 0)
  1026. continue;
  1027. //Check whether the value differs from the default
  1028. lock (Vanilla)
  1029. {
  1030. if (value.Equals(property.GetValue(Vanilla[itemType], null)))
  1031. continue;
  1032. }
  1033. //If we get here then we need to store the property
  1034. propCount++;
  1035. pw.Write(GetPropertyDefinitionId(property.Name));
  1036. SerializeObject(value, pw, property.PropertyType);
  1037. }
  1038. writer.Write(propCount);
  1039. if (Verbose)
  1040. writer.Write((int)propertyStream.Length);
  1041. propertyStream.WriteTo(writer.BaseStream);
  1042. }
  1043. private static void WriteFields(Type itemType, object item, BinaryWriter writer)
  1044. {
  1045. var fieldStream = new MemoryStream();
  1046. var fw = new BinaryWriter(fieldStream);
  1047. byte fieldCount = 0;
  1048. //Get the public fields of the object
  1049. var fields = GetFieldInfo(itemType);
  1050. foreach (var field in fields)
  1051. {
  1052. var value = field.GetValue(item);
  1053. //Don't store null values
  1054. if (value == null)
  1055. continue;
  1056. //Don't store empty collections
  1057. if (value is ICollection)
  1058. if ((value as ICollection).Count == 0)
  1059. continue;
  1060. //Don't store empty arrays
  1061. if (value is Array)
  1062. if ((value as Array).Length == 0)
  1063. continue;
  1064. //Check whether the value differs from the default
  1065. lock (Vanilla)
  1066. {
  1067. if (value.Equals(field.GetValue(Vanilla[itemType])))
  1068. continue;
  1069. }
  1070. //if we get here then we need to store the field
  1071. fieldCount++;
  1072. fw.Write(GetPropertyDefinitionId(field.Name));
  1073. SerializeObject(value, fw, field.FieldType);
  1074. }
  1075. writer.Write(fieldCount);
  1076. if (Verbose)
  1077. writer.Write((int)fieldStream.Length);
  1078. fieldStream.WriteTo(writer.BaseStream);
  1079. }
  1080. /// <summary>
  1081. /// Write a basic untyped value
  1082. /// </summary>
  1083. /// <param name = "writer">The writer to commit byte to</param>
  1084. /// <param name = "value">The value to write</param>
  1085. private static void WriteValue(BinaryWriter writer, object value)
  1086. {
  1087. if (value is string)
  1088. writer.Write((string)value);
  1089. else if (value == null)
  1090. writer.Write("~~NULL~~");
  1091. else if (value is decimal)
  1092. {
  1093. int[] array = Decimal.GetBits((Decimal)value);
  1094. SerializeObject(array, writer, typeof(int[]));
  1095. }
  1096. else if (value is float)
  1097. writer.Write((float)value);
  1098. else if (value is bool)
  1099. writer.Write((bool)value
  1100. ? 'Y'
  1101. : 'N');
  1102. else if (value is Guid)
  1103. writer.Write(value.ToString());
  1104. else if (value is DateTime)
  1105. writer.Write(((DateTime)value).Ticks);
  1106. else if (value is TimeSpan)
  1107. writer.Write(((TimeSpan)value).Ticks);
  1108. else if (value is char)
  1109. writer.Write((char)value);
  1110. else if (value is ushort)
  1111. writer.Write((ushort)value);
  1112. else if (value is double)
  1113. writer.Write((double)value);
  1114. else if (value is ulong)
  1115. writer.Write((ulong)value);
  1116. else if (value is int)
  1117. writer.Write((int)value);
  1118. else if (value is uint)
  1119. writer.Write((uint)value);
  1120. else if (value is byte)
  1121. writer.Write((byte)value);
  1122. else if (value is long)
  1123. writer.Write((long)value);
  1124. else if (value is short)
  1125. writer.Write((short)value);
  1126. else if (value is sbyte)
  1127. writer.Write((sbyte)value);
  1128. else
  1129. writer.Write((int)value);
  1130. }
  1131. /// <summary>
  1132. /// Read a basic value from the stream
  1133. /// </summary>
  1134. /// <param name = "reader">The reader with the stream</param>
  1135. /// <param name = "tp">The type to read</param>
  1136. /// <returns>The hydrated value</returns>
  1137. private static object ReadValue(BinaryReader reader, Type tp)
  1138. {
  1139. if (tp == typeof(string))
  1140. {
  1141. var retString = reader.ReadString();
  1142. return retString == "~~NULL~~"
  1143. ? null
  1144. : retString;
  1145. }
  1146. if (tp == typeof(bool))
  1147. return reader.ReadChar() == 'Y';
  1148. if (tp == typeof(decimal))
  1149. {
  1150. var array = DeserializeObject(reader, typeof(int[])) as int[];
  1151. return new Decimal(array);
  1152. }
  1153. if (tp == typeof(DateTime))
  1154. return new DateTime(reader.ReadInt64());
  1155. if (tp == typeof(TimeSpan))
  1156. return new TimeSpan(reader.ReadInt64());
  1157. if (tp == typeof(float))
  1158. return reader.ReadSingle();
  1159. if (tp == typeof(char))
  1160. return reader.ReadChar();
  1161. if (tp == typeof(ushort))
  1162. return reader.ReadUInt16();
  1163. if (tp == typeof(double))
  1164. return reader.ReadDouble();
  1165. if (tp == typeof(ulong))
  1166. return reader.ReadUInt64();
  1167. if (tp == typeof(int))
  1168. return reader.ReadInt32();
  1169. if (tp == typeof(uint))
  1170. return reader.ReadUInt32();
  1171. if (tp == typeof(byte))
  1172. return reader.ReadByte();
  1173. if (tp == typeof(long))
  1174. return reader.ReadInt64();
  1175. if (tp == typeof(short))
  1176. return reader.ReadInt16

Large files files are truncated, but you can click here to view the full file