/Cloudy.Protobuf/Serializer.cs

https://github.com/eigenein/cloudy · C# · 290 lines · 262 code · 15 blank · 13 comment · 27 complexity · 201f10da5c8710a49d0b1683dd6e8abc MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Reflection;
  6. using System.Runtime.Serialization;
  7. using Cloudy.Protobuf.Attributes;
  8. using Cloudy.Protobuf.Encoding;
  9. using Cloudy.Protobuf.Enums;
  10. using Cloudy.Protobuf.Exceptions;
  11. using Cloudy.Protobuf.Helpers;
  12. using Cloudy.Protobuf.Interfaces;
  13. using Cloudy.Protobuf.Serializers;
  14. using Cloudy.Protobuf.Structures;
  15. using Cloudy.Protobuf.ValueBuilders;
  16. namespace Cloudy.Protobuf
  17. {
  18. /// <summary>
  19. /// Used to serialize to and deserialize from the Protobuf format.
  20. /// </summary>
  21. public class Serializer : AbstractSerializer
  22. {
  23. private readonly Type expectedType;
  24. private readonly Dictionary<uint, BuildingProperty> properties;
  25. private Serializer(Type expectedType, Dictionary<uint, BuildingProperty> properties)
  26. {
  27. this.expectedType = expectedType;
  28. this.properties = properties;
  29. }
  30. private static readonly Dictionary<Type, Serializer> SerializerCache =
  31. new Dictionary<Type, Serializer>();
  32. /// <summary>
  33. /// Creates a serializer of objects of the specified type.
  34. /// </summary>
  35. public static Serializer CreateSerializer(Type type)
  36. {
  37. lock (SerializerCache)
  38. {
  39. return CreateSerializerThreadUnsafe(type);
  40. }
  41. }
  42. private static Serializer CreateSerializerThreadUnsafe(Type type)
  43. {
  44. Serializer serializer;
  45. if (SerializerCache.TryGetValue(type, out serializer))
  46. {
  47. return serializer;
  48. }
  49. ProtobufSerializableAttribute serializableAttribute =
  50. (ProtobufSerializableAttribute)Attribute.GetCustomAttribute(
  51. type, typeof(ProtobufSerializableAttribute));
  52. if (serializableAttribute == null)
  53. {
  54. throw new NotSerializableException(type);
  55. }
  56. Dictionary<uint, BuildingProperty> properties =
  57. new Dictionary<uint, BuildingProperty>();
  58. foreach (PropertyInfo property in type.GetProperties())
  59. {
  60. ProtobufFieldAttribute fieldAttribute =
  61. (ProtobufFieldAttribute)Attribute.GetCustomAttribute(
  62. property, typeof(ProtobufFieldAttribute));
  63. if (fieldAttribute == null)
  64. {
  65. continue;
  66. }
  67. if (properties.ContainsKey(fieldAttribute.FieldNumber))
  68. {
  69. throw new DuplicateFieldNumberException(fieldAttribute.FieldNumber);
  70. }
  71. BuildingProperty buildingProperty = new BuildingProperty(
  72. property, CreateBuildingSerializer(property.PropertyType, fieldAttribute, true));
  73. buildingProperty.BuildingSerializer.Serializer =
  74. new NullProxySerializer(buildingProperty.BuildingSerializer.Serializer,
  75. !fieldAttribute.Required);
  76. if (fieldAttribute.Required)
  77. {
  78. buildingProperty.BuildingSerializer.Builder =
  79. new RequiredValueBuilder(buildingProperty.BuildingSerializer.Builder);
  80. }
  81. properties[fieldAttribute.FieldNumber] = buildingProperty;
  82. }
  83. return SerializerCache[type] = new Serializer(type, properties);
  84. }
  85. private static BuildingSerializer CreateBuildingSerializer(Type propertyType,
  86. ProtobufFieldAttribute attribute, bool examineForCollection)
  87. {
  88. BuildingSerializer buildingSerializer;
  89. if (TrySerializeAsPrimitiveValue(propertyType, attribute, out buildingSerializer))
  90. {
  91. return buildingSerializer;
  92. }
  93. if (TrySerializeAsEnum(propertyType, out buildingSerializer))
  94. {
  95. return buildingSerializer;
  96. }
  97. if (TrySerializeAsNullable(propertyType, attribute, out buildingSerializer))
  98. {
  99. return buildingSerializer;
  100. }
  101. if (TrySerializeAsCollection(propertyType, attribute, examineForCollection,
  102. out buildingSerializer))
  103. {
  104. return buildingSerializer;
  105. }
  106. return new BuildingSerializer(new EmbeddedMessageSerializer(
  107. CreateSerializer(propertyType)),
  108. new SingleValueBuilder(propertyType));
  109. }
  110. private static bool TrySerializeAsNullable(Type propertyType,
  111. ProtobufFieldAttribute attribute, out BuildingSerializer buildingSerializer)
  112. {
  113. buildingSerializer = null;
  114. if (propertyType.IsGenericType &&
  115. propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
  116. {
  117. Type underlyingType = propertyType.GetGenericArguments()[0];
  118. SerializerWithWireType underlyingSerializer = CreateBuildingSerializer(
  119. underlyingType, attribute, false).Serializer;
  120. buildingSerializer = new BuildingSerializer(
  121. underlyingSerializer, new NullableValueBuilder(underlyingType));
  122. return true;
  123. }
  124. return false;
  125. }
  126. private static bool TrySerializeAsEnum(Type propertyType,
  127. out BuildingSerializer buildingSerializer)
  128. {
  129. buildingSerializer = null;
  130. if (propertyType.IsSubclassOf(typeof(Enum)))
  131. {
  132. buildingSerializer = new BuildingSerializer(
  133. new EnumSerializer(propertyType),
  134. new SingleValueBuilder(propertyType));
  135. return true;
  136. }
  137. return false;
  138. }
  139. private static bool TrySerializeAsCollection(Type propertyType,
  140. ProtobufFieldAttribute attribute, bool examineForCollection,
  141. out BuildingSerializer buildingSerializer)
  142. {
  143. buildingSerializer = null;
  144. if (examineForCollection &&
  145. propertyType.IsGenericType &&
  146. propertyType.GetGenericTypeDefinition() == typeof(ICollection<>))
  147. {
  148. Type underlyingType = propertyType.GetGenericArguments()[0];
  149. SerializerWithWireType underlyingSerializer = CreateBuildingSerializer(
  150. underlyingType, attribute, false).Serializer;
  151. {
  152. buildingSerializer = new BuildingSerializer(attribute.Packed
  153. ? (SerializerWithWireType)new PackedRepeatedSerializer(underlyingSerializer)
  154. : new RepeatedSerializer(attribute.FieldNumber, underlyingSerializer),
  155. new RepeatedValueBuilder(underlyingType, !attribute.Packed));
  156. return true;
  157. }
  158. }
  159. return false;
  160. }
  161. private static bool TrySerializeAsPrimitiveValue(Type propertyType,
  162. ProtobufFieldAttribute attribute,
  163. out BuildingSerializer buildingSerializer)
  164. {
  165. buildingSerializer = null;
  166. SerializerWithWireType serializer;
  167. if ((attribute.DataType != DataType.Default &&
  168. DataTypeToSerializerCache.TryGetSerializer(attribute.DataType, out serializer))
  169. || DefaultSerializersCache.TryGetSerializer(propertyType, out serializer))
  170. {
  171. {
  172. buildingSerializer = new BuildingSerializer(serializer,
  173. new SingleValueBuilder(propertyType));
  174. return true;
  175. }
  176. }
  177. return false;
  178. }
  179. public override void Serialize(Stream stream, object o)
  180. {
  181. try
  182. {
  183. if (o.GetType() != expectedType)
  184. {
  185. throw new InvalidOperationException(String.Format(
  186. "Expected type: {0}, but was: {1}", expectedType, o.GetType()));
  187. }
  188. foreach (KeyValuePair<uint, BuildingProperty> entry in properties)
  189. {
  190. SerializerWithWireType serializer = entry.Value.BuildingSerializer.Serializer;
  191. object value = entry.Value.Property.GetValue(o, null);
  192. if (!serializer.ShouldBeSkipped(value))
  193. {
  194. ProtobufWriter.WriteKey(stream, entry.Key, serializer.WireType);
  195. entry.Value.BuildingSerializer.Serializer.Serialize(stream, value);
  196. }
  197. }
  198. }
  199. catch (Exception ex)
  200. {
  201. throw new SerializationException("Serialization has failed.", ex);
  202. }
  203. }
  204. /// <summary>
  205. /// Serializes the object.
  206. /// </summary>
  207. /// <param name="stream">The target stream.</param>
  208. /// <param name="o">The object to serialize.</param>
  209. /// <param name="streamingMode">Tells whether the object should be
  210. /// serialized as an embedded message.</param>
  211. public void Serialize(Stream stream, object o, bool streamingMode)
  212. {
  213. if (!streamingMode)
  214. {
  215. Serialize(stream, o);
  216. }
  217. else
  218. {
  219. new EmbeddedMessageSerializer(this).Serialize(stream, o);
  220. }
  221. }
  222. public override object Deserialize(Stream stream)
  223. {
  224. try
  225. {
  226. Dictionary<uint, IValueBuilder> buildingProperties = properties.ToDictionary(
  227. entry => entry.Key,
  228. entry => entry.Value.BuildingSerializer.Builder.CreateInstance());
  229. while (true)
  230. {
  231. WireType wireType;
  232. uint fieldNumber;
  233. try
  234. {
  235. ProtobufReader.ReadKey(stream, out fieldNumber, out wireType);
  236. }
  237. catch (EndOfStreamException)
  238. {
  239. break;
  240. }
  241. IValueBuilder valueBuilder;
  242. if (buildingProperties.TryGetValue(fieldNumber, out valueBuilder))
  243. {
  244. valueBuilder.UpdateValue(properties[fieldNumber]
  245. .BuildingSerializer.Serializer.Deserialize(stream));
  246. }
  247. else
  248. {
  249. UnknownFieldSkipHelper.Skip(stream, wireType);
  250. }
  251. }
  252. object o = Activator.CreateInstance(expectedType);
  253. foreach (KeyValuePair<uint, IValueBuilder> property in buildingProperties)
  254. {
  255. object value = property.Value.BuildObject();
  256. if (value != null)
  257. {
  258. properties[property.Key].Property.SetValue(o,
  259. value, null);
  260. }
  261. }
  262. return o;
  263. }
  264. catch (Exception ex)
  265. {
  266. throw new SerializationException("Deserialization has failed.", ex);
  267. }
  268. }
  269. public object Deserialize(Stream stream, bool streamingMode)
  270. {
  271. return streamingMode ? new EmbeddedMessageSerializer(this).Deserialize(stream)
  272. : Deserialize(stream);
  273. }
  274. }
  275. }