TypeModel.cs 73 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658
  1. using System;
  2. using System.IO;
  3. using System.Collections;
  4. #if FEAT_IKVM
  5. using Type = IKVM.Reflection.Type;
  6. using IKVM.Reflection;
  7. #else
  8. using System.Reflection;
  9. #endif
  10. namespace ProtoBuf.Meta
  11. {
  12. /// <summary>
  13. /// Provides protobuf serialization support for a number of types
  14. /// </summary>
  15. public abstract class TypeModel
  16. {
  17. #if WINRT || COREFX
  18. internal TypeInfo MapType(TypeInfo type)
  19. {
  20. return type;
  21. }
  22. #endif
  23. /// <summary>
  24. /// Should the <c>Kind</c> be included on date/time values?
  25. /// </summary>
  26. protected internal virtual bool SerializeDateTimeKind() { return false; }
  27. /// <summary>
  28. /// Resolve a System.Type to the compiler-specific type
  29. /// </summary>
  30. protected internal Type MapType(System.Type type)
  31. {
  32. return MapType(type, true);
  33. }
  34. /// <summary>
  35. /// Resolve a System.Type to the compiler-specific type
  36. /// </summary>
  37. protected internal virtual Type MapType(System.Type type, bool demand)
  38. {
  39. #if FEAT_IKVM
  40. throw new NotImplementedException(); // this should come from RuntimeTypeModel!
  41. #else
  42. return type;
  43. #endif
  44. }
  45. private WireType GetWireType(ProtoTypeCode code, DataFormat format, ref Type type, out int modelKey)
  46. {
  47. modelKey = -1;
  48. if (Helpers.IsEnum(type))
  49. {
  50. modelKey = GetKey(ref type);
  51. return WireType.Variant;
  52. }
  53. switch (code)
  54. {
  55. case ProtoTypeCode.Int64:
  56. case ProtoTypeCode.UInt64:
  57. return format == DataFormat.FixedSize ? WireType.Fixed64 : WireType.Variant;
  58. case ProtoTypeCode.Int16:
  59. case ProtoTypeCode.Int32:
  60. case ProtoTypeCode.UInt16:
  61. case ProtoTypeCode.UInt32:
  62. case ProtoTypeCode.Boolean:
  63. case ProtoTypeCode.SByte:
  64. case ProtoTypeCode.Byte:
  65. case ProtoTypeCode.Char:
  66. return format == DataFormat.FixedSize ? WireType.Fixed32 : WireType.Variant;
  67. case ProtoTypeCode.Double:
  68. return WireType.Fixed64;
  69. case ProtoTypeCode.Single:
  70. return WireType.Fixed32;
  71. case ProtoTypeCode.String:
  72. case ProtoTypeCode.DateTime:
  73. case ProtoTypeCode.Decimal:
  74. case ProtoTypeCode.ByteArray:
  75. case ProtoTypeCode.TimeSpan:
  76. case ProtoTypeCode.Guid:
  77. case ProtoTypeCode.Uri:
  78. return WireType.String;
  79. }
  80. if ((modelKey = GetKey(ref type)) >= 0)
  81. {
  82. return WireType.String;
  83. }
  84. return WireType.None;
  85. }
  86. #if !FEAT_IKVM
  87. /// <summary>
  88. /// This is the more "complete" version of Serialize, which handles single instances of mapped types.
  89. /// The value is written as a complete field, including field-header and (for sub-objects) a
  90. /// length-prefix
  91. /// In addition to that, this provides support for:
  92. /// - basic values; individual int / string / Guid / etc
  93. /// - IEnumerable sequences of any type handled by TrySerializeAuxiliaryType
  94. ///
  95. /// </summary>
  96. internal bool TrySerializeAuxiliaryType(ProtoWriter writer, Type type, DataFormat format, int tag, object value, bool isInsideList)
  97. {
  98. if (type == null) { type = value.GetType(); }
  99. ProtoTypeCode typecode = Helpers.GetTypeCode(type);
  100. int modelKey;
  101. // note the "ref type" here normalizes against proxies
  102. WireType wireType = GetWireType(typecode, format, ref type, out modelKey);
  103. if (modelKey >= 0)
  104. { // write the header, but defer to the model
  105. if (Helpers.IsEnum(type))
  106. { // no header
  107. Serialize(modelKey, value, writer);
  108. return true;
  109. }
  110. else
  111. {
  112. ProtoWriter.WriteFieldHeader(tag, wireType, writer);
  113. switch (wireType)
  114. {
  115. case WireType.None:
  116. throw ProtoWriter.CreateException(writer);
  117. case WireType.StartGroup:
  118. case WireType.String:
  119. // needs a wrapping length etc
  120. SubItemToken token = ProtoWriter.StartSubItem(value, writer);
  121. Serialize(modelKey, value, writer);
  122. ProtoWriter.EndSubItem(token, writer);
  123. return true;
  124. default:
  125. Serialize(modelKey, value, writer);
  126. return true;
  127. }
  128. }
  129. }
  130. if(wireType != WireType.None) {
  131. ProtoWriter.WriteFieldHeader(tag, wireType, writer);
  132. }
  133. switch(typecode) {
  134. case ProtoTypeCode.Int16: ProtoWriter.WriteInt16((short)value, writer); return true;
  135. case ProtoTypeCode.Int32: ProtoWriter.WriteInt32((int)value, writer); return true;
  136. case ProtoTypeCode.Int64: ProtoWriter.WriteInt64((long)value, writer); return true;
  137. case ProtoTypeCode.UInt16: ProtoWriter.WriteUInt16((ushort)value, writer); return true;
  138. case ProtoTypeCode.UInt32: ProtoWriter.WriteUInt32((uint)value, writer); return true;
  139. case ProtoTypeCode.UInt64: ProtoWriter.WriteUInt64((ulong)value, writer); return true;
  140. case ProtoTypeCode.Boolean: ProtoWriter.WriteBoolean((bool)value, writer); return true;
  141. case ProtoTypeCode.SByte: ProtoWriter.WriteSByte((sbyte)value, writer); return true;
  142. case ProtoTypeCode.Byte: ProtoWriter.WriteByte((byte)value, writer); return true;
  143. case ProtoTypeCode.Char: ProtoWriter.WriteUInt16((ushort)(char)value, writer); return true;
  144. case ProtoTypeCode.Double: ProtoWriter.WriteDouble((double)value, writer); return true;
  145. case ProtoTypeCode.Single: ProtoWriter.WriteSingle((float)value, writer); return true;
  146. case ProtoTypeCode.DateTime:
  147. if (SerializeDateTimeKind())
  148. BclHelpers.WriteDateTimeWithKind((DateTime)value, writer);
  149. else
  150. BclHelpers.WriteDateTime((DateTime)value, writer);
  151. return true;
  152. case ProtoTypeCode.Decimal: BclHelpers.WriteDecimal((decimal)value, writer); return true;
  153. case ProtoTypeCode.String: ProtoWriter.WriteString((string)value, writer); return true;
  154. case ProtoTypeCode.ByteArray: ProtoWriter.WriteBytes((byte[])value, writer); return true;
  155. case ProtoTypeCode.TimeSpan: BclHelpers.WriteTimeSpan((TimeSpan)value, writer); return true;
  156. case ProtoTypeCode.Guid: BclHelpers.WriteGuid((Guid)value, writer); return true;
  157. case ProtoTypeCode.Uri: ProtoWriter.WriteString(((Uri)value).AbsoluteUri, writer); return true;
  158. }
  159. // by now, we should have covered all the simple cases; if we wrote a field-header, we have
  160. // forgotten something!
  161. Helpers.DebugAssert(wireType == WireType.None);
  162. // now attempt to handle sequences (including arrays and lists)
  163. IEnumerable sequence = value as IEnumerable;
  164. if (sequence != null)
  165. {
  166. if (isInsideList) throw CreateNestedListsNotSupported();
  167. foreach (object item in sequence) {
  168. if (item == null) { throw new NullReferenceException(); }
  169. if (!TrySerializeAuxiliaryType(writer, null, format, tag, item, true))
  170. {
  171. ThrowUnexpectedType(item.GetType());
  172. }
  173. }
  174. return true;
  175. }
  176. return false;
  177. }
  178. private void SerializeCore(ProtoWriter writer, object value)
  179. {
  180. if (value == null) throw new ArgumentNullException("value");
  181. Type type = value.GetType();
  182. int key = GetKey(ref type);
  183. if (key >= 0)
  184. {
  185. Serialize(key, value, writer);
  186. }
  187. else if (!TrySerializeAuxiliaryType(writer, type, DataFormat.Default, Serializer.ListItemTag, value, false))
  188. {
  189. ThrowUnexpectedType(type);
  190. }
  191. }
  192. #endif
  193. /// <summary>
  194. /// Writes a protocol-buffer representation of the given instance to the supplied stream.
  195. /// </summary>
  196. /// <param name="value">The existing instance to be serialized (cannot be null).</param>
  197. /// <param name="dest">The destination stream to write to.</param>
  198. public void Serialize(Stream dest, object value)
  199. {
  200. Serialize(dest, value, null);
  201. }
  202. /// <summary>
  203. /// Writes a protocol-buffer representation of the given instance to the supplied stream.
  204. /// </summary>
  205. /// <param name="value">The existing instance to be serialized (cannot be null).</param>
  206. /// <param name="dest">The destination stream to write to.</param>
  207. /// <param name="context">Additional information about this serialization operation.</param>
  208. public void Serialize(Stream dest, object value, SerializationContext context)
  209. {
  210. #if FEAT_IKVM
  211. throw new NotSupportedException();
  212. #else
  213. using (ProtoWriter writer = new ProtoWriter(dest, this, context))
  214. {
  215. writer.SetRootObject(value);
  216. SerializeCore(writer, value);
  217. writer.Close();
  218. }
  219. #endif
  220. }
  221. /// <summary>
  222. /// Writes a protocol-buffer representation of the given instance to the supplied writer.
  223. /// </summary>
  224. /// <param name="value">The existing instance to be serialized (cannot be null).</param>
  225. /// <param name="dest">The destination writer to write to.</param>
  226. public void Serialize(ProtoWriter dest, object value)
  227. {
  228. #if FEAT_IKVM
  229. throw new NotSupportedException();
  230. #else
  231. if (dest == null) throw new ArgumentNullException("dest");
  232. dest.CheckDepthFlushlock();
  233. dest.SetRootObject(value);
  234. SerializeCore(dest, value);
  235. dest.CheckDepthFlushlock();
  236. ProtoWriter.Flush(dest);
  237. #endif
  238. }
  239. /// <summary>
  240. /// Applies a protocol-buffer stream to an existing instance (or null), using length-prefixed
  241. /// data - useful with network IO.
  242. /// </summary>
  243. /// <param name="type">The type being merged.</param>
  244. /// <param name="value">The existing instance to be modified (can be null).</param>
  245. /// <param name="source">The binary stream to apply to the instance (cannot be null).</param>
  246. /// <param name="style">How to encode the length prefix.</param>
  247. /// <param name="fieldNumber">The tag used as a prefix to each record (only used with base-128 style prefixes).</param>
  248. /// <returns>The updated instance; this may be different to the instance argument if
  249. /// either the original instance was null, or the stream defines a known sub-type of the
  250. /// original instance.</returns>
  251. public object DeserializeWithLengthPrefix(Stream source, object value, Type type, PrefixStyle style, int fieldNumber)
  252. {
  253. int bytesRead;
  254. return DeserializeWithLengthPrefix(source, value, type, style, fieldNumber, null, out bytesRead);
  255. }
  256. /// <summary>
  257. /// Applies a protocol-buffer stream to an existing instance (or null), using length-prefixed
  258. /// data - useful with network IO.
  259. /// </summary>
  260. /// <param name="type">The type being merged.</param>
  261. /// <param name="value">The existing instance to be modified (can be null).</param>
  262. /// <param name="source">The binary stream to apply to the instance (cannot be null).</param>
  263. /// <param name="style">How to encode the length prefix.</param>
  264. /// <param name="expectedField">The tag used as a prefix to each record (only used with base-128 style prefixes).</param>
  265. /// <param name="resolver">Used to resolve types on a per-field basis.</param>
  266. /// <returns>The updated instance; this may be different to the instance argument if
  267. /// either the original instance was null, or the stream defines a known sub-type of the
  268. /// original instance.</returns>
  269. public object DeserializeWithLengthPrefix(Stream source, object value, Type type, PrefixStyle style, int expectedField, Serializer.TypeResolver resolver)
  270. {
  271. int bytesRead;
  272. return DeserializeWithLengthPrefix(source, value, type, style, expectedField, resolver, out bytesRead);
  273. }
  274. /// <summary>
  275. /// Applies a protocol-buffer stream to an existing instance (or null), using length-prefixed
  276. /// data - useful with network IO.
  277. /// </summary>
  278. /// <param name="type">The type being merged.</param>
  279. /// <param name="value">The existing instance to be modified (can be null).</param>
  280. /// <param name="source">The binary stream to apply to the instance (cannot be null).</param>
  281. /// <param name="style">How to encode the length prefix.</param>
  282. /// <param name="expectedField">The tag used as a prefix to each record (only used with base-128 style prefixes).</param>
  283. /// <param name="resolver">Used to resolve types on a per-field basis.</param>
  284. /// <param name="bytesRead">Returns the number of bytes consumed by this operation (includes length-prefix overheads and any skipped data).</param>
  285. /// <returns>The updated instance; this may be different to the instance argument if
  286. /// either the original instance was null, or the stream defines a known sub-type of the
  287. /// original instance.</returns>
  288. public object DeserializeWithLengthPrefix(Stream source, object value, Type type, PrefixStyle style, int expectedField, Serializer.TypeResolver resolver, out int bytesRead)
  289. {
  290. bool haveObject;
  291. return DeserializeWithLengthPrefix(source, value, type, style, expectedField, resolver, out bytesRead, out haveObject, null);
  292. }
  293. private object DeserializeWithLengthPrefix(Stream source, object value, Type type, PrefixStyle style, int expectedField, Serializer.TypeResolver resolver, out int bytesRead, out bool haveObject, SerializationContext context)
  294. {
  295. #if FEAT_IKVM
  296. throw new NotSupportedException();
  297. #else
  298. haveObject = false;
  299. bool skip;
  300. int len;
  301. int tmpBytesRead;
  302. bytesRead = 0;
  303. if (type == null && (style != PrefixStyle.Base128 || resolver == null))
  304. {
  305. throw new InvalidOperationException("A type must be provided unless base-128 prefixing is being used in combination with a resolver");
  306. }
  307. int actualField;
  308. do
  309. {
  310. bool expectPrefix = expectedField > 0 || resolver != null;
  311. len = ProtoReader.ReadLengthPrefix(source, expectPrefix, style, out actualField, out tmpBytesRead);
  312. if (tmpBytesRead == 0) return value;
  313. bytesRead += tmpBytesRead;
  314. if (len < 0) return value;
  315. switch (style)
  316. {
  317. case PrefixStyle.Base128:
  318. if (expectPrefix && expectedField == 0 && type == null && resolver != null)
  319. {
  320. type = resolver(actualField);
  321. skip = type == null;
  322. }
  323. else { skip = expectedField != actualField; }
  324. break;
  325. default:
  326. skip = false;
  327. break;
  328. }
  329. if (skip)
  330. {
  331. if (len == int.MaxValue) throw new InvalidOperationException();
  332. ProtoReader.Seek(source, len, null);
  333. bytesRead += len;
  334. }
  335. } while (skip);
  336. ProtoReader reader = null;
  337. try
  338. {
  339. reader = ProtoReader.Create(source, this, context, len);
  340. int key = GetKey(ref type);
  341. if (key >= 0 && !Helpers.IsEnum(type))
  342. {
  343. value = Deserialize(key, value, reader);
  344. }
  345. else
  346. {
  347. if (!(TryDeserializeAuxiliaryType(reader, DataFormat.Default, Serializer.ListItemTag, type, ref value, true, false, true, false) || len == 0))
  348. {
  349. TypeModel.ThrowUnexpectedType(type); // throws
  350. }
  351. }
  352. bytesRead += reader.Position;
  353. haveObject = true;
  354. return value;
  355. }
  356. finally
  357. {
  358. ProtoReader.Recycle(reader);
  359. }
  360. #endif
  361. }
  362. /// <summary>
  363. /// Reads a sequence of consecutive length-prefixed items from a stream, using
  364. /// either base-128 or fixed-length prefixes. Base-128 prefixes with a tag
  365. /// are directly comparable to serializing multiple items in succession
  366. /// (use the <see cref="Serializer.ListItemTag"/> tag to emulate the implicit behavior
  367. /// when serializing a list/array). When a tag is
  368. /// specified, any records with different tags are silently omitted. The
  369. /// tag is ignored. The tag is ignores for fixed-length prefixes.
  370. /// </summary>
  371. /// <param name="source">The binary stream containing the serialized records.</param>
  372. /// <param name="style">The prefix style used in the data.</param>
  373. /// <param name="expectedField">The tag of records to return (if non-positive, then no tag is
  374. /// expected and all records are returned).</param>
  375. /// <param name="resolver">On a field-by-field basis, the type of object to deserialize (can be null if "type" is specified). </param>
  376. /// <param name="type">The type of object to deserialize (can be null if "resolver" is specified).</param>
  377. /// <returns>The sequence of deserialized objects.</returns>
  378. public System.Collections.IEnumerable DeserializeItems(System.IO.Stream source, Type type, PrefixStyle style, int expectedField, Serializer.TypeResolver resolver)
  379. {
  380. return DeserializeItems(source, type, style, expectedField, resolver, null);
  381. }
  382. /// <summary>
  383. /// Reads a sequence of consecutive length-prefixed items from a stream, using
  384. /// either base-128 or fixed-length prefixes. Base-128 prefixes with a tag
  385. /// are directly comparable to serializing multiple items in succession
  386. /// (use the <see cref="Serializer.ListItemTag"/> tag to emulate the implicit behavior
  387. /// when serializing a list/array). When a tag is
  388. /// specified, any records with different tags are silently omitted. The
  389. /// tag is ignored. The tag is ignores for fixed-length prefixes.
  390. /// </summary>
  391. /// <param name="source">The binary stream containing the serialized records.</param>
  392. /// <param name="style">The prefix style used in the data.</param>
  393. /// <param name="expectedField">The tag of records to return (if non-positive, then no tag is
  394. /// expected and all records are returned).</param>
  395. /// <param name="resolver">On a field-by-field basis, the type of object to deserialize (can be null if "type" is specified). </param>
  396. /// <param name="type">The type of object to deserialize (can be null if "resolver" is specified).</param>
  397. /// <returns>The sequence of deserialized objects.</returns>
  398. /// <param name="context">Additional information about this serialization operation.</param>
  399. public System.Collections.IEnumerable DeserializeItems(System.IO.Stream source, Type type, PrefixStyle style, int expectedField, Serializer.TypeResolver resolver, SerializationContext context)
  400. {
  401. return new DeserializeItemsIterator(this, source, type, style, expectedField, resolver, context);
  402. }
  403. #if !NO_GENERICS
  404. /// <summary>
  405. /// Reads a sequence of consecutive length-prefixed items from a stream, using
  406. /// either base-128 or fixed-length prefixes. Base-128 prefixes with a tag
  407. /// are directly comparable to serializing multiple items in succession
  408. /// (use the <see cref="Serializer.ListItemTag"/> tag to emulate the implicit behavior
  409. /// when serializing a list/array). When a tag is
  410. /// specified, any records with different tags are silently omitted. The
  411. /// tag is ignored. The tag is ignores for fixed-length prefixes.
  412. /// </summary>
  413. /// <typeparam name="T">The type of object to deserialize.</typeparam>
  414. /// <param name="source">The binary stream containing the serialized records.</param>
  415. /// <param name="style">The prefix style used in the data.</param>
  416. /// <param name="expectedField">The tag of records to return (if non-positive, then no tag is
  417. /// expected and all records are returned).</param>
  418. /// <returns>The sequence of deserialized objects.</returns>
  419. public System.Collections.Generic.IEnumerable<T> DeserializeItems<T>(Stream source, PrefixStyle style, int expectedField)
  420. {
  421. return DeserializeItems<T>(source, style, expectedField, null);
  422. }
  423. /// <summary>
  424. /// Reads a sequence of consecutive length-prefixed items from a stream, using
  425. /// either base-128 or fixed-length prefixes. Base-128 prefixes with a tag
  426. /// are directly comparable to serializing multiple items in succession
  427. /// (use the <see cref="Serializer.ListItemTag"/> tag to emulate the implicit behavior
  428. /// when serializing a list/array). When a tag is
  429. /// specified, any records with different tags are silently omitted. The
  430. /// tag is ignored. The tag is ignores for fixed-length prefixes.
  431. /// </summary>
  432. /// <typeparam name="T">The type of object to deserialize.</typeparam>
  433. /// <param name="source">The binary stream containing the serialized records.</param>
  434. /// <param name="style">The prefix style used in the data.</param>
  435. /// <param name="expectedField">The tag of records to return (if non-positive, then no tag is
  436. /// expected and all records are returned).</param>
  437. /// <returns>The sequence of deserialized objects.</returns>
  438. /// <param name="context">Additional information about this serialization operation.</param>
  439. public System.Collections.Generic.IEnumerable<T> DeserializeItems<T>(Stream source, PrefixStyle style, int expectedField, SerializationContext context)
  440. {
  441. return new DeserializeItemsIterator<T>(this, source, style, expectedField, context);
  442. }
  443. private sealed class DeserializeItemsIterator<T> : DeserializeItemsIterator,
  444. System.Collections.Generic.IEnumerator<T>,
  445. System.Collections.Generic.IEnumerable<T>
  446. {
  447. System.Collections.Generic.IEnumerator<T> System.Collections.Generic.IEnumerable<T>.GetEnumerator() { return this; }
  448. public new T Current { get { return (T)base.Current; } }
  449. void IDisposable.Dispose() { }
  450. public DeserializeItemsIterator(TypeModel model, Stream source, PrefixStyle style, int expectedField, SerializationContext context)
  451. : base(model, source, model.MapType(typeof(T)), style, expectedField, null, context) { }
  452. }
  453. #endif
  454. private class DeserializeItemsIterator : IEnumerator, IEnumerable
  455. {
  456. IEnumerator IEnumerable.GetEnumerator() { return this; }
  457. private bool haveObject;
  458. private object current;
  459. public bool MoveNext()
  460. {
  461. if (haveObject)
  462. {
  463. int bytesRead;
  464. current = model.DeserializeWithLengthPrefix(source, null, type, style, expectedField, resolver, out bytesRead, out haveObject, context);
  465. }
  466. return haveObject;
  467. }
  468. void IEnumerator.Reset() { throw new NotSupportedException(); }
  469. public object Current { get { return current; } }
  470. private readonly Stream source;
  471. private readonly Type type;
  472. private readonly PrefixStyle style;
  473. private readonly int expectedField;
  474. private readonly Serializer.TypeResolver resolver;
  475. private readonly TypeModel model;
  476. private readonly SerializationContext context;
  477. public DeserializeItemsIterator(TypeModel model, Stream source, Type type, PrefixStyle style, int expectedField, Serializer.TypeResolver resolver, SerializationContext context)
  478. {
  479. haveObject = true;
  480. this.source = source;
  481. this.type = type;
  482. this.style = style;
  483. this.expectedField = expectedField;
  484. this.resolver = resolver;
  485. this.model = model;
  486. this.context = context;
  487. }
  488. }
  489. /// <summary>
  490. /// Writes a protocol-buffer representation of the given instance to the supplied stream,
  491. /// with a length-prefix. This is useful for socket programming,
  492. /// as DeserializeWithLengthPrefix can be used to read the single object back
  493. /// from an ongoing stream.
  494. /// </summary>
  495. /// <param name="type">The type being serialized.</param>
  496. /// <param name="value">The existing instance to be serialized (cannot be null).</param>
  497. /// <param name="style">How to encode the length prefix.</param>
  498. /// <param name="dest">The destination stream to write to.</param>
  499. /// <param name="fieldNumber">The tag used as a prefix to each record (only used with base-128 style prefixes).</param>
  500. public void SerializeWithLengthPrefix(Stream dest, object value, Type type, PrefixStyle style, int fieldNumber)
  501. {
  502. SerializeWithLengthPrefix(dest, value, type, style, fieldNumber, null);
  503. }
  504. /// <summary>
  505. /// Writes a protocol-buffer representation of the given instance to the supplied stream,
  506. /// with a length-prefix. This is useful for socket programming,
  507. /// as DeserializeWithLengthPrefix can be used to read the single object back
  508. /// from an ongoing stream.
  509. /// </summary>
  510. /// <param name="type">The type being serialized.</param>
  511. /// <param name="value">The existing instance to be serialized (cannot be null).</param>
  512. /// <param name="style">How to encode the length prefix.</param>
  513. /// <param name="dest">The destination stream to write to.</param>
  514. /// <param name="fieldNumber">The tag used as a prefix to each record (only used with base-128 style prefixes).</param>
  515. /// <param name="context">Additional information about this serialization operation.</param>
  516. public void SerializeWithLengthPrefix(Stream dest, object value, Type type, PrefixStyle style, int fieldNumber, SerializationContext context)
  517. {
  518. if (type == null)
  519. {
  520. if(value == null) throw new ArgumentNullException("value");
  521. type = MapType(value.GetType());
  522. }
  523. int key = GetKey(ref type);
  524. using (ProtoWriter writer = new ProtoWriter(dest, this, context))
  525. {
  526. switch (style)
  527. {
  528. case PrefixStyle.None:
  529. Serialize(key, value, writer);
  530. break;
  531. case PrefixStyle.Base128:
  532. case PrefixStyle.Fixed32:
  533. case PrefixStyle.Fixed32BigEndian:
  534. ProtoWriter.WriteObject(value, key, writer, style, fieldNumber);
  535. break;
  536. default:
  537. throw new ArgumentOutOfRangeException("style");
  538. }
  539. writer.Close();
  540. }
  541. }
  542. /// <summary>
  543. /// Applies a protocol-buffer stream to an existing instance (which may be null).
  544. /// </summary>
  545. /// <param name="type">The type (including inheritance) to consider.</param>
  546. /// <param name="value">The existing instance to be modified (can be null).</param>
  547. /// <param name="source">The binary stream to apply to the instance (cannot be null).</param>
  548. /// <returns>The updated instance; this may be different to the instance argument if
  549. /// either the original instance was null, or the stream defines a known sub-type of the
  550. /// original instance.</returns>
  551. public object Deserialize(Stream source, object value, System.Type type)
  552. {
  553. return Deserialize(source, value, type, null);
  554. }
  555. /// <summary>
  556. /// Applies a protocol-buffer stream to an existing instance (which may be null).
  557. /// </summary>
  558. /// <param name="type">The type (including inheritance) to consider.</param>
  559. /// <param name="value">The existing instance to be modified (can be null).</param>
  560. /// <param name="source">The binary stream to apply to the instance (cannot be null).</param>
  561. /// <returns>The updated instance; this may be different to the instance argument if
  562. /// either the original instance was null, or the stream defines a known sub-type of the
  563. /// original instance.</returns>
  564. /// <param name="context">Additional information about this serialization operation.</param>
  565. public object Deserialize(Stream source, object value, System.Type type, SerializationContext context)
  566. {
  567. #if FEAT_IKVM
  568. throw new NotSupportedException();
  569. #else
  570. bool autoCreate = PrepareDeserialize(value, ref type);
  571. ProtoReader reader = null;
  572. try
  573. {
  574. reader = ProtoReader.Create(source, this, context, ProtoReader.TO_EOF);
  575. if (value != null) reader.SetRootObject(value);
  576. object obj = DeserializeCore(reader, type, value, autoCreate);
  577. reader.CheckFullyConsumed();
  578. return obj;
  579. }
  580. finally
  581. {
  582. ProtoReader.Recycle(reader);
  583. }
  584. #endif
  585. }
  586. private bool PrepareDeserialize(object value, ref Type type)
  587. {
  588. if (type == null)
  589. {
  590. if (value == null)
  591. {
  592. throw new ArgumentNullException("type");
  593. }
  594. else
  595. {
  596. type = MapType(value.GetType());
  597. }
  598. }
  599. bool autoCreate = true;
  600. #if !NO_GENERICS
  601. Type underlyingType = Helpers.GetUnderlyingType(type);
  602. if (underlyingType != null)
  603. {
  604. type = underlyingType;
  605. autoCreate = false;
  606. }
  607. #endif
  608. return autoCreate;
  609. }
  610. /// <summary>
  611. /// Applies a protocol-buffer stream to an existing instance (which may be null).
  612. /// </summary>
  613. /// <param name="type">The type (including inheritance) to consider.</param>
  614. /// <param name="value">The existing instance to be modified (can be null).</param>
  615. /// <param name="source">The binary stream to apply to the instance (cannot be null).</param>
  616. /// <param name="length">The number of bytes to consume.</param>
  617. /// <returns>The updated instance; this may be different to the instance argument if
  618. /// either the original instance was null, or the stream defines a known sub-type of the
  619. /// original instance.</returns>
  620. public object Deserialize(Stream source, object value, System.Type type, int length)
  621. {
  622. return Deserialize(source, value, type, length, null);
  623. }
  624. /// <summary>
  625. /// Applies a protocol-buffer stream to an existing instance (which may be null).
  626. /// </summary>
  627. /// <param name="type">The type (including inheritance) to consider.</param>
  628. /// <param name="value">The existing instance to be modified (can be null).</param>
  629. /// <param name="source">The binary stream to apply to the instance (cannot be null).</param>
  630. /// <param name="length">The number of bytes to consume (or -1 to read to the end of the stream).</param>
  631. /// <returns>The updated instance; this may be different to the instance argument if
  632. /// either the original instance was null, or the stream defines a known sub-type of the
  633. /// original instance.</returns>
  634. /// <param name="context">Additional information about this serialization operation.</param>
  635. public object Deserialize(Stream source, object value, System.Type type, int length, SerializationContext context)
  636. {
  637. #if FEAT_IKVM
  638. throw new NotSupportedException();
  639. #else
  640. bool autoCreate = PrepareDeserialize(value, ref type);
  641. ProtoReader reader = null;
  642. try
  643. {
  644. reader = ProtoReader.Create(source, this, context, length);
  645. if (value != null) reader.SetRootObject(value);
  646. object obj = DeserializeCore(reader, type, value, autoCreate);
  647. reader.CheckFullyConsumed();
  648. return obj;
  649. }
  650. finally
  651. {
  652. ProtoReader.Recycle(reader);
  653. }
  654. #endif
  655. }
  656. /// <summary>
  657. /// Applies a protocol-buffer reader to an existing instance (which may be null).
  658. /// </summary>
  659. /// <param name="type">The type (including inheritance) to consider.</param>
  660. /// <param name="value">The existing instance to be modified (can be null).</param>
  661. /// <param name="source">The reader to apply to the instance (cannot be null).</param>
  662. /// <returns>The updated instance; this may be different to the instance argument if
  663. /// either the original instance was null, or the stream defines a known sub-type of the
  664. /// original instance.</returns>
  665. public object Deserialize(ProtoReader source, object value, System.Type type)
  666. {
  667. #if FEAT_IKVM
  668. throw new NotSupportedException();
  669. #else
  670. if (source == null) throw new ArgumentNullException("source");
  671. bool autoCreate = PrepareDeserialize(value, ref type);
  672. if (value != null) source.SetRootObject(value);
  673. object obj = DeserializeCore(source, type, value, autoCreate);
  674. source.CheckFullyConsumed();
  675. return obj;
  676. #endif
  677. }
  678. #if !FEAT_IKVM
  679. private object DeserializeCore(ProtoReader reader, Type type, object value, bool noAutoCreate)
  680. {
  681. int key = GetKey(ref type);
  682. if (key >= 0 && !Helpers.IsEnum(type))
  683. {
  684. return Deserialize(key, value, reader);
  685. }
  686. // this returns true to say we actively found something, but a value is assigned either way (or throws)
  687. TryDeserializeAuxiliaryType(reader, DataFormat.Default, Serializer.ListItemTag, type, ref value, true, false, noAutoCreate, false);
  688. return value;
  689. }
  690. #endif
  691. #if WINRT || COREFX
  692. private static readonly System.Reflection.TypeInfo ilist = typeof(IList).GetTypeInfo();
  693. #else
  694. private static readonly System.Type ilist = typeof(IList);
  695. #endif
  696. internal static MethodInfo ResolveListAdd(TypeModel model, Type listType, Type itemType, out bool isList)
  697. {
  698. #if WINRT || COREFX
  699. TypeInfo listTypeInfo = listType.GetTypeInfo();
  700. #else
  701. Type listTypeInfo = listType;
  702. #endif
  703. isList = model.MapType(ilist).IsAssignableFrom(listTypeInfo);
  704. Type[] types = { itemType };
  705. MethodInfo add = Helpers.GetInstanceMethod(listTypeInfo, "Add", types);
  706. #if !NO_GENERICS
  707. if (add == null)
  708. { // fallback: look for ICollection<T>'s Add(typedObject) method
  709. bool forceList = listTypeInfo.IsInterface &&
  710. listTypeInfo == model.MapType(typeof(System.Collections.Generic.IEnumerable<>)).MakeGenericType(types)
  711. #if WINRT || COREFX
  712. .GetTypeInfo()
  713. #endif
  714. ;
  715. #if WINRT || COREFX
  716. TypeInfo constuctedListType = typeof(System.Collections.Generic.ICollection<>).MakeGenericType(types).GetTypeInfo();
  717. #else
  718. Type constuctedListType = model.MapType(typeof(System.Collections.Generic.ICollection<>)).MakeGenericType(types);
  719. #endif
  720. if (forceList || constuctedListType.IsAssignableFrom(listTypeInfo))
  721. {
  722. add = Helpers.GetInstanceMethod(constuctedListType, "Add", types);
  723. }
  724. }
  725. if (add == null)
  726. {
  727. #if WINRT || COREFX
  728. foreach (Type tmpType in listTypeInfo.ImplementedInterfaces)
  729. #else
  730. foreach (Type interfaceType in listTypeInfo.GetInterfaces())
  731. #endif
  732. {
  733. #if WINRT || COREFX
  734. TypeInfo interfaceType = tmpType.GetTypeInfo();
  735. #endif
  736. if (interfaceType.Name == "IProducerConsumerCollection`1" && interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition().FullName == "System.Collections.Concurrent.IProducerConsumerCollection`1")
  737. {
  738. add = Helpers.GetInstanceMethod(interfaceType, "TryAdd", types);
  739. if (add != null) break;
  740. }
  741. }
  742. }
  743. #endif
  744. if (add == null)
  745. { // fallback: look for a public list.Add(object) method
  746. types[0] = model.MapType(typeof(object));
  747. add = Helpers.GetInstanceMethod(listTypeInfo, "Add", types);
  748. }
  749. if (add == null && isList)
  750. { // fallback: look for IList's Add(object) method
  751. add = Helpers.GetInstanceMethod(model.MapType(ilist), "Add", types);
  752. }
  753. return add;
  754. }
  755. internal static Type GetListItemType(TypeModel model, Type listType)
  756. {
  757. Helpers.DebugAssert(listType != null);
  758. #if WINRT
  759. TypeInfo listTypeInfo = listType.GetTypeInfo();
  760. if (listType == typeof(string) || listType.IsArray
  761. || !typeof(IEnumerable).GetTypeInfo().IsAssignableFrom(listTypeInfo)) return null;
  762. #else
  763. if (listType == model.MapType(typeof(string)) || listType.IsArray
  764. || !model.MapType(typeof(IEnumerable)).IsAssignableFrom(listType)) return null;
  765. #endif
  766. BasicList candidates = new BasicList();
  767. #if WINRT
  768. foreach (MethodInfo method in listType.GetRuntimeMethods())
  769. #else
  770. foreach (MethodInfo method in listType.GetMethods())
  771. #endif
  772. {
  773. if (method.IsStatic || method.Name != "Add") continue;
  774. ParameterInfo[] parameters = method.GetParameters();
  775. Type paramType;
  776. if (parameters.Length == 1 && !candidates.Contains(paramType = parameters[0].ParameterType))
  777. {
  778. candidates.Add(paramType);
  779. }
  780. }
  781. string name = listType.Name;
  782. bool isQueueStack = name != null && (name.IndexOf("Queue") >= 0 || name.IndexOf("Stack") >= 0);
  783. #if !NO_GENERICS
  784. if(!isQueueStack)
  785. {
  786. TestEnumerableListPatterns(model, candidates, listType);
  787. #if WINRT
  788. foreach (Type iType in listTypeInfo.ImplementedInterfaces)
  789. {
  790. TestEnumerableListPatterns(model, candidates, iType);
  791. }
  792. #else
  793. foreach (Type iType in listType.GetInterfaces())
  794. {
  795. TestEnumerableListPatterns(model, candidates, iType);
  796. }
  797. #endif
  798. }
  799. #endif
  800. #if WINRT
  801. // more convenient GetProperty overload not supported on all platforms
  802. foreach (PropertyInfo indexer in listType.GetRuntimeProperties())
  803. {
  804. if (indexer.Name != "Item" || candidates.Contains(indexer.PropertyType)) continue;
  805. ParameterInfo[] args = indexer.GetIndexParameters();
  806. if (args.Length != 1 || args[0].ParameterType != typeof(int)) continue;
  807. MethodInfo getter = indexer.GetMethod;
  808. if (getter == null || getter.IsStatic) continue;
  809. candidates.Add(indexer.PropertyType);
  810. }
  811. #else
  812. // more convenient GetProperty overload not supported on all platforms
  813. foreach (PropertyInfo indexer in listType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
  814. {
  815. if (indexer.Name != "Item" || candidates.Contains(indexer.PropertyType)) continue;
  816. ParameterInfo[] args = indexer.GetIndexParameters();
  817. if (args.Length != 1 || args[0].ParameterType != model.MapType(typeof(int))) continue;
  818. candidates.Add(indexer.PropertyType);
  819. }
  820. #endif
  821. switch (candidates.Count)
  822. {
  823. case 0:
  824. return null;
  825. case 1:
  826. if ((Type)candidates[0] == listType) return null; // recursive
  827. return (Type)candidates[0];
  828. case 2:
  829. if ((Type)candidates[0] != listType && CheckDictionaryAccessors(model, (Type)candidates[0], (Type)candidates[1])) return (Type)candidates[0];
  830. if ((Type)candidates[1] != listType && CheckDictionaryAccessors(model, (Type)candidates[1], (Type)candidates[0])) return (Type)candidates[1];
  831. break;
  832. }
  833. return null;
  834. }
  835. private static void TestEnumerableListPatterns(TypeModel model, BasicList candidates, Type iType)
  836. {
  837. #if WINRT || COREFX
  838. TypeInfo iTypeInfo = iType.GetTypeInfo();
  839. if (iTypeInfo.IsGenericType)
  840. {
  841. Type typeDef = iTypeInfo.GetGenericTypeDefinition();
  842. if(
  843. typeDef == model.MapType(typeof(System.Collections.Generic.IEnumerable<>))
  844. || typeDef == model.MapType(typeof(System.Collections.Generic.ICollection<>))
  845. || typeDef.GetTypeInfo().FullName == "System.Collections.Concurrent.IProducerConsumerCollection`1")
  846. {
  847. Type[] iTypeArgs = iTypeInfo.GenericTypeArguments;
  848. if (!candidates.Contains(iTypeArgs[0]))
  849. {
  850. candidates.Add(iTypeArgs[0]);
  851. }
  852. }
  853. }
  854. #elif !NO_GENERICS
  855. if (iType.IsGenericType)
  856. {
  857. Type typeDef = iType.GetGenericTypeDefinition();
  858. if (typeDef == model.MapType(typeof(System.Collections.Generic.IEnumerable<>))
  859. || typeDef == model.MapType(typeof(System.Collections.Generic.ICollection<>))
  860. || typeDef.FullName == "System.Collections.Concurrent.IProducerConsumerCollection`1")
  861. {
  862. Type[] iTypeArgs = iType.GetGenericArguments();
  863. if (!candidates.Contains(iTypeArgs[0]))
  864. {
  865. candidates.Add(iTypeArgs[0]);
  866. }
  867. }
  868. }
  869. #endif
  870. }
  871. private static bool CheckDictionaryAccessors(TypeModel model, Type pair, Type value)
  872. {
  873. #if NO_GENERICS
  874. return false;
  875. #elif WINRT || COREFX
  876. TypeInfo finalType = pair.GetTypeInfo();
  877. return finalType.IsGenericType && finalType.GetGenericTypeDefinition() == typeof(System.Collections.Generic.KeyValuePair<,>)
  878. && finalType.GenericTypeArguments[1] == value;
  879. #else
  880. return pair.IsGenericType && pair.GetGenericTypeDefinition() == model.MapType(typeof(System.Collections.Generic.KeyValuePair<,>))
  881. && pair.GetGenericArguments()[1] == value;
  882. #endif
  883. }
  884. #if !FEAT_IKVM
  885. private bool TryDeserializeList(TypeModel model, ProtoReader reader, DataFormat format, int tag, Type listType, Type itemType, ref object value)
  886. {
  887. bool isList;
  888. MethodInfo addMethod = TypeModel.ResolveListAdd(model, listType, itemType, out isList);
  889. if (addMethod == null) throw new NotSupportedException("Unknown list variant: " + listType.FullName);
  890. bool found = false;
  891. object nextItem = null;
  892. IList list = value as IList;
  893. object[] args = isList ? null : new object[1];
  894. BasicList arraySurrogate = listType.IsArray ? new BasicList() : null;
  895. while (TryDeserializeAuxiliaryType(reader, format, tag, itemType, ref nextItem, true, true, true, true))
  896. {
  897. found = true;
  898. if (value == null && arraySurrogate == null)
  899. {
  900. value = CreateListInstance(listType, itemType);
  901. list = value as IList;
  902. }
  903. if (list != null)
  904. {
  905. list.Add(nextItem);
  906. }
  907. else if (arraySurrogate != null)
  908. {
  909. arraySurrogate.Add(nextItem);
  910. }
  911. else
  912. {
  913. args[0] = nextItem;
  914. addMethod.Invoke(value, args);
  915. }
  916. nextItem = null;
  917. }
  918. if (arraySurrogate != null)
  919. {
  920. Array newArray;
  921. if (value != null)
  922. {
  923. if (arraySurrogate.Count == 0)
  924. { // we'll stay with what we had, thanks
  925. }
  926. else
  927. {
  928. Array existing = (Array)value;
  929. newArray = Array.CreateInstance(itemType, existing.Length + arraySurrogate.Count);
  930. Array.Copy(existing, newArray, existing.Length);
  931. arraySurrogate.CopyTo(newArray, existing.Length);
  932. value = newArray;
  933. }
  934. }
  935. else
  936. {
  937. newArray = Array.CreateInstance(itemType, arraySurrogate.Count);
  938. arraySurrogate.CopyTo(newArray, 0);
  939. value = newArray;
  940. }
  941. }
  942. return found;
  943. }
  944. private static object CreateListInstance(Type listType, Type itemType)
  945. {
  946. Type concreteListType = listType;
  947. if (listType.IsArray)
  948. {
  949. return Array.CreateInstance(itemType, 0);
  950. }
  951. #if WINRT || COREFX
  952. TypeInfo listTypeInfo = listType.GetTypeInfo();
  953. if (!listTypeInfo.IsClass || listTypeInfo.IsAbstract ||
  954. Helpers.GetConstructor(listTypeInfo, Helpers.EmptyTypes, true) == null)
  955. #else
  956. if (!listType.IsClass || listType.IsAbstract ||
  957. Helpers.GetConstructor(listType, Helpers.EmptyTypes, true) == null)
  958. #endif
  959. {
  960. string fullName;
  961. bool handled = false;
  962. #if WINRT || COREFX
  963. if (listTypeInfo.IsInterface &&
  964. #else
  965. if (listType.IsInterface &&
  966. #endif
  967. (fullName = listType.FullName) != null && fullName.IndexOf("Dictionary") >= 0) // have to try to be frugal here...
  968. {
  969. #if !NO_GENERICS
  970. #if WINRT || COREFX
  971. TypeInfo finalType = listType.GetTypeInfo();
  972. if (finalType.IsGenericType && finalType.GetGenericTypeDefinition() == typeof(System.Collections.Generic.IDictionary<,>))
  973. {
  974. Type[] genericTypes = listType.GenericTypeArguments;
  975. concreteListType = typeof(System.Collections.Generic.Dictionary<,>).MakeGenericType(genericTypes);
  976. handled = true;
  977. }
  978. #else
  979. if (listType.IsGenericType && listType.GetGenericTypeDefinition() == typeof(System.Collections.Generic.IDictionary<,>))
  980. {
  981. Type[] genericTypes = listType.GetGenericArguments();
  982. concreteListType = typeof(System.Collections.Generic.Dictionary<,>).MakeGenericType(genericTypes);
  983. handled = true;
  984. }
  985. #endif
  986. #endif
  987. #if !SILVERLIGHT && !WINRT && !PORTABLE && ! COREFX
  988. if (!handled && listType == typeof(IDictionary))
  989. {
  990. concreteListType = typeof(Hashtable);
  991. handled = true;
  992. }
  993. #endif
  994. }
  995. #if !NO_GENERICS
  996. if (!handled)
  997. {
  998. concreteListType = typeof(System.Collections.Generic.List<>).MakeGenericType(itemType);
  999. handled = true;
  1000. }
  1001. #endif
  1002. #if !SILVERLIGHT && !WINRT && !PORTABLE && ! COREFX
  1003. if (!handled)
  1004. {
  1005. concreteListType = typeof(ArrayList);
  1006. handled = true;
  1007. }
  1008. #endif
  1009. }
  1010. return Activator.CreateInstance(concreteListType);
  1011. }
  1012. /// <summary>
  1013. /// This is the more "complete" version of Deserialize, which handles single instances of mapped types.
  1014. /// The value is read as a complete field, including field-header and (for sub-objects) a
  1015. /// length-prefix..kmc
  1016. ///
  1017. /// In addition to that, this provides support for:
  1018. /// - basic values; individual int / string / Guid / etc
  1019. /// - IList sets of any type handled by TryDeserializeAuxiliaryType
  1020. /// </summary>
  1021. internal bool TryDeserializeAuxiliaryType(ProtoReader reader, DataFormat format, int tag, Type type, ref object value, bool skipOtherFields, bool asListItem, bool autoCreate, bool insideList)
  1022. {
  1023. if (type == null) throw new ArgumentNullException("type");
  1024. Type itemType = null;
  1025. ProtoTypeCode typecode = Helpers.GetTypeCode(type);
  1026. int modelKey;
  1027. WireType wiretype = GetWireType(typecode, format, ref type, out modelKey);
  1028. bool found = false;
  1029. if (wiretype == WireType.None)
  1030. {
  1031. itemType = GetListItemType(this, type);
  1032. if (itemType == null && type.IsArray && type.GetArrayRank() == 1 && type != typeof(byte[]))
  1033. {
  1034. itemType = type.GetElementType();
  1035. }
  1036. if (itemType != null)
  1037. {
  1038. if (insideList) throw TypeModel.CreateNestedListsNotSupported();
  1039. found = TryDeserializeList(this, reader, format, tag, type, itemType, ref value);
  1040. if (!found && autoCreate)
  1041. {
  1042. value = CreateListInstance(type, itemType);
  1043. }
  1044. return found;
  1045. }
  1046. // otherwise, not a happy bunny...
  1047. ThrowUnexpectedType(type);
  1048. }
  1049. // to treat correctly, should read all values
  1050. while (true)
  1051. {
  1052. // for convenience (re complex exit conditions), additional exit test here:
  1053. // if we've got the value, are only looking for one, and we aren't a list - then exit
  1054. if (found && asListItem) break;
  1055. // read the next item
  1056. int fieldNumber = reader.ReadFieldHeader();
  1057. if (fieldNumber <= 0) break;
  1058. if (fieldNumber != tag)
  1059. {
  1060. if (skipOtherFields)
  1061. {
  1062. reader.SkipField();
  1063. continue;
  1064. }
  1065. throw ProtoReader.AddErrorData(new InvalidOperationException(
  1066. "Expected field " + tag.ToString() + ", but found " + fieldNumber.ToString()), reader);
  1067. }
  1068. found = true;
  1069. reader.Hint(wiretype); // handle signed data etc
  1070. if (modelKey >= 0)
  1071. {
  1072. switch (wiretype)
  1073. {
  1074. case WireType.String:
  1075. case WireType.StartGroup:
  1076. SubItemToken token = ProtoReader.StartSubItem(reader);
  1077. value = Deserialize(modelKey, value, reader);
  1078. ProtoReader.EndSubItem(token, reader);
  1079. continue;
  1080. default:
  1081. value = Deserialize(modelKey, value, reader);
  1082. continue;
  1083. }
  1084. }
  1085. switch (typecode)
  1086. {
  1087. case ProtoTypeCode.Int16: value = reader.ReadInt16(); continue;
  1088. case ProtoTypeCode.Int32: value = reader.ReadInt32(); continue;
  1089. case ProtoTypeCode.Int64: value = reader.ReadInt64(); continue;
  1090. case ProtoTypeCode.UInt16: value = reader.ReadUInt16(); continue;
  1091. case ProtoTypeCode.UInt32: value = reader.ReadUInt32(); continue;
  1092. case ProtoTypeCode.UInt64: value = reader.ReadUInt64(); continue;
  1093. case ProtoTypeCode.Boolean: value = reader.ReadBoolean(); continue;
  1094. case ProtoTypeCode.SByte: value = reader.ReadSByte(); continue;
  1095. case ProtoTypeCode.Byte: value = reader.ReadByte(); continue;
  1096. case ProtoTypeCode.Char: value = (char)reader.ReadUInt16(); continue;
  1097. case ProtoTypeCode.Double: value = reader.ReadDouble(); continue;
  1098. case ProtoTypeCode.Single: value = reader.ReadSingle(); continue;
  1099. case ProtoTypeCode.DateTime: value = BclHelpers.ReadDateTime(reader); continue;
  1100. case ProtoTypeCode.Decimal: value = BclHelpers.ReadDecimal(reader); continue;
  1101. case ProtoTypeCode.String: value = reader.ReadString(); continue;
  1102. case ProtoTypeCode.ByteArray: value = ProtoReader.AppendBytes((byte[])value, reader); continue;
  1103. case ProtoTypeCode.TimeSpan: value = BclHelpers.ReadTimeSpan(reader); continue;
  1104. case ProtoTypeCode.Guid: value = BclHelpers.ReadGuid(reader); continue;
  1105. case ProtoTypeCode.Uri: value = new Uri(reader.ReadString()); continue;
  1106. }
  1107. }
  1108. if (!found && !asListItem && autoCreate)
  1109. {
  1110. if (type != typeof(string))
  1111. {
  1112. value = Activator.CreateInstance(type);
  1113. }
  1114. }
  1115. return found;
  1116. }
  1117. #endif
  1118. #if !NO_RUNTIME
  1119. /// <summary>
  1120. /// Creates a new runtime model, to which the caller
  1121. /// can add support for a range of types. A model
  1122. /// can be used "as is", or can be compiled for
  1123. /// optimal performance.
  1124. /// </summary>
  1125. public static RuntimeTypeModel Create()
  1126. {
  1127. return new RuntimeTypeModel(false);
  1128. }
  1129. #endif
  1130. /// <summary>
  1131. /// Applies common proxy scenarios, resolving the actual type to consider
  1132. /// </summary>
  1133. protected internal static Type ResolveProxies(Type type)
  1134. {
  1135. if (type == null) return null;
  1136. #if !NO_GENERICS
  1137. if (type.IsGenericParameter) return null;
  1138. // Nullable<T>
  1139. Type tmp = Helpers.GetUnderlyingType(type);
  1140. if (tmp != null) return tmp;
  1141. #endif
  1142. #if !(WINRT || CF)
  1143. // EF POCO
  1144. string fullName = type.FullName;
  1145. if (fullName != null && fullName.StartsWith("System.Data.Entity.DynamicProxies."))
  1146. {
  1147. #if COREFX
  1148. return type.GetTypeInfo().BaseType;
  1149. #else
  1150. return type.BaseType;
  1151. #endif
  1152. }
  1153. // NHibernate
  1154. Type[] interfaces = type.GetInterfaces();
  1155. for(int i = 0 ; i < interfaces.Length ; i++)
  1156. {
  1157. switch(interfaces[i].FullName)
  1158. {
  1159. case "NHibernate.Proxy.INHibernateProxy":
  1160. case "NHibernate.Proxy.DynamicProxy.IProxy":
  1161. case "NHibernate.Intercept.IFieldInterceptorAccessor":
  1162. #if COREFX
  1163. return type.GetTypeInfo().BaseType;
  1164. #else
  1165. return type.BaseType;
  1166. #endif
  1167. }
  1168. }
  1169. #endif
  1170. return null;
  1171. }
  1172. /// <summary>
  1173. /// Indicates whether the supplied type is explicitly modelled by the model
  1174. /// </summary>
  1175. public bool IsDefined(Type type)
  1176. {
  1177. return GetKey(ref type) >= 0;
  1178. }
  1179. /// <summary>
  1180. /// Provides the key that represents a given type in the current model.
  1181. /// The type is also normalized for proxies at the same time.
  1182. /// </summary>
  1183. protected internal int GetKey(ref Type type)
  1184. {
  1185. if (type == null) return -1;
  1186. int key = GetKeyImpl(type);
  1187. if (key < 0)
  1188. {
  1189. Type normalized = ResolveProxies(type);
  1190. if (normalized != null) {
  1191. type = normalized; // hence ref
  1192. key = GetKeyImpl(type);
  1193. }
  1194. }
  1195. return key;
  1196. }
  1197. /// <summary>
  1198. /// Provides the key that represents a given type in the current model.
  1199. /// </summary>
  1200. protected abstract int GetKeyImpl(Type type);
  1201. /// <summary>
  1202. /// Writes a protocol-buffer representation of the given instance to the supplied stream.
  1203. /// </summary>
  1204. /// <param name="key">Represents the type (including inheritance) to consider.</param>
  1205. /// <param name="value">The existing instance to be serialized (cannot be null).</param>
  1206. /// <param name="dest">The destination stream to write to.</param>
  1207. protected internal abstract void Serialize(int key, object value, ProtoWriter dest);
  1208. /// <summary>
  1209. /// Applies a protocol-buffer stream to an existing instance (which may be null).
  1210. /// </summary>
  1211. /// <param name="key">Represents the type (including inheritance) to consider.</param>
  1212. /// <param name="value">The existing instance to be modified (can be null).</param>
  1213. /// <param name="source">The binary stream to apply to the instance (cannot be null).</param>
  1214. /// <returns>The updated instance; this may be different to the instance argument if
  1215. /// either the original instance was null, or the stream defines a known sub-type of the
  1216. /// original instance.</returns>
  1217. protected internal abstract object Deserialize(int key, object value, ProtoReader source);
  1218. //internal ProtoSerializer Create(IProtoSerializer head)
  1219. //{
  1220. // return new RuntimeSerializer(head, this);
  1221. //}
  1222. //internal ProtoSerializer Compile
  1223. /// <summary>
  1224. /// Indicates the type of callback to be used
  1225. /// </summary>
  1226. protected internal enum CallbackType
  1227. {
  1228. /// <summary>
  1229. /// Invoked before an object is serialized
  1230. /// </summary>
  1231. BeforeSerialize,
  1232. /// <summary>
  1233. /// Invoked after an object is serialized
  1234. /// </summary>
  1235. AfterSerialize,
  1236. /// <summary>
  1237. /// Invoked before an object is deserialized (or when a new instance is created)
  1238. /// </summary>
  1239. BeforeDeserialize,
  1240. /// <summary>
  1241. /// Invoked after an object is deserialized
  1242. /// </summary>
  1243. AfterDeserialize
  1244. }
  1245. /// <summary>
  1246. /// Create a deep clone of the supplied instance; any sub-items are also cloned.
  1247. /// </summary>
  1248. public object DeepClone(object value)
  1249. {
  1250. #if FEAT_IKVM
  1251. throw new NotSupportedException();
  1252. #else
  1253. if (value == null) return null;
  1254. Type type = value.GetType();
  1255. int key = GetKey(ref type);
  1256. if (key >= 0 && !Helpers.IsEnum(type))
  1257. {
  1258. using (MemoryStream ms = new MemoryStream())
  1259. {
  1260. using(ProtoWriter writer = new ProtoWriter(ms, this, null))
  1261. {
  1262. writer.SetRootObject(value);
  1263. Serialize(key, value, writer);
  1264. writer.Close();
  1265. }
  1266. ms.Position = 0;
  1267. ProtoReader reader = null;
  1268. try
  1269. {
  1270. reader = ProtoReader.Create(ms, this, null, ProtoReader.TO_EOF);
  1271. return Deserialize(key, null, reader);
  1272. }
  1273. finally
  1274. {
  1275. ProtoReader.Recycle(reader);
  1276. }
  1277. }
  1278. }
  1279. int modelKey;
  1280. if (type == typeof(byte[])) {
  1281. byte[] orig = (byte[])value, clone = new byte[orig.Length];
  1282. Helpers.BlockCopy(orig, 0, clone, 0, orig.Length);
  1283. return clone;
  1284. }
  1285. else if (GetWireType(Helpers.GetTypeCode(type), DataFormat.Default, ref type, out modelKey) != WireType.None && modelKey < 0)
  1286. { // immutable; just return the original value
  1287. return value;
  1288. }
  1289. using (MemoryStream ms = new MemoryStream())
  1290. {
  1291. using (ProtoWriter writer = new ProtoWriter(ms, this, null))
  1292. {
  1293. if (!TrySerializeAuxiliaryType(writer, type, DataFormat.Default, Serializer.ListItemTag, value, false)) ThrowUnexpectedType(type);
  1294. writer.Close();
  1295. }
  1296. ms.Position = 0;
  1297. ProtoReader reader = null;
  1298. try
  1299. {
  1300. reader = ProtoReader.Create(ms, this, null, ProtoReader.TO_EOF);
  1301. value = null; // start from scratch!
  1302. TryDeserializeAuxiliaryType(reader, DataFormat.Default, Serializer.ListItemTag, type, ref value, true, false, true, false);
  1303. return value;
  1304. }
  1305. finally
  1306. {
  1307. ProtoReader.Recycle(reader);
  1308. }
  1309. }
  1310. #endif
  1311. }
  1312. /// <summary>
  1313. /// Indicates that while an inheritance tree exists, the exact type encountered was not
  1314. /// specified in that hierarchy and cannot be processed.
  1315. /// </summary>
  1316. protected internal static void ThrowUnexpectedSubtype(Type expected, Type actual)
  1317. {
  1318. if (expected != TypeModel.ResolveProxies(actual))
  1319. {
  1320. throw new InvalidOperationException("Unexpected sub-type: " + actual.FullName);
  1321. }
  1322. }
  1323. /// <summary>
  1324. /// Indicates that the given type was not expected, and cannot be processed.
  1325. /// </summary>
  1326. protected internal static void ThrowUnexpectedType(Type type)
  1327. {
  1328. string fullName = type == null ? "(unknown)" : type.FullName;
  1329. #if !NO_GENERICS && !WINRT
  1330. if (type != null)
  1331. {
  1332. Type baseType = type
  1333. #if COREFX
  1334. .GetTypeInfo()
  1335. #endif
  1336. .BaseType;
  1337. if (baseType != null && baseType
  1338. #if COREFX
  1339. .GetTypeInfo()
  1340. #endif
  1341. .IsGenericType && baseType.GetGenericTypeDefinition().Name == "GeneratedMessage`2")
  1342. {
  1343. throw new InvalidOperationException(
  1344. "Are you mixing protobuf-net and protobuf-csharp-port? See http://stackoverflow.com/q/11564914; type: " + fullName);
  1345. }
  1346. }
  1347. #endif
  1348. throw new InvalidOperationException("Type is not expected, and no contract can be inferred: " + fullName);
  1349. }
  1350. internal static Exception CreateNestedListsNotSupported()
  1351. {
  1352. return new NotSupportedException("Nested or jagged lists and arrays are not supported");
  1353. }
  1354. /// <summary>
  1355. /// Indicates that the given type cannot be constructed; it may still be possible to
  1356. /// deserialize into existing instances.
  1357. /// </summary>
  1358. public static void ThrowCannotCreateInstance(Type type)
  1359. {
  1360. throw new ProtoException("No parameterless constructor found for " + (type == null ? "(null)" : type.Name));
  1361. }
  1362. internal static string SerializeType(TypeModel model, System.Type type)
  1363. {
  1364. if (model != null)
  1365. {
  1366. TypeFormatEventHandler handler = model.DynamicTypeFormatting;
  1367. if (handler != null)
  1368. {
  1369. TypeFormatEventArgs args = new TypeFormatEventArgs(type);
  1370. handler(model, args);
  1371. if (!Helpers.IsNullOrEmpty(args.FormattedName)) return args.FormattedName;
  1372. }
  1373. }
  1374. return type.AssemblyQualifiedName;
  1375. }
  1376. internal static System.Type DeserializeType(TypeModel model, string value)
  1377. {
  1378. if (model != null)
  1379. {
  1380. TypeFormatEventHandler handler = model.DynamicTypeFormatting;
  1381. if (handler != null)
  1382. {
  1383. TypeFormatEventArgs args = new TypeFormatEventArgs(value);
  1384. handler(model, args);
  1385. if (args.Type != null) return args.Type;
  1386. }
  1387. }
  1388. return System.Type.GetType(value);
  1389. }
  1390. /// <summary>
  1391. /// Returns true if the type supplied is either a recognised contract type,
  1392. /// or a *list* of a recognised contract type.
  1393. /// </summary>
  1394. /// <remarks>Note that primitives always return false, even though the engine
  1395. /// will, if forced, try to serialize such</remarks>
  1396. /// <returns>True if this type is recognised as a serializable entity, else false</returns>
  1397. public bool CanSerializeContractType(Type type)
  1398. {
  1399. return CanSerialize(type, false, true, true);
  1400. }
  1401. /// <summary>
  1402. /// Returns true if the type supplied is a basic type with inbuilt handling,
  1403. /// a recognised contract type, or a *list* of a basic / contract type.
  1404. /// </summary>
  1405. public bool CanSerialize(Type type)
  1406. {
  1407. return CanSerialize(type, true, true, true);
  1408. }
  1409. /// <summary>
  1410. /// Returns true if the type supplied is a basic type with inbuilt handling,
  1411. /// or a *list* of a basic type with inbuilt handling
  1412. /// </summary>
  1413. public bool CanSerializeBasicType(Type type)
  1414. {
  1415. return CanSerialize(type, true, false, true);
  1416. }
  1417. private bool CanSerialize(Type type, bool allowBasic, bool allowContract, bool allowLists)
  1418. {
  1419. if (type == null) throw new ArgumentNullException("type");
  1420. Type tmp = Helpers.GetUnderlyingType(type);
  1421. if (tmp != null) type = tmp;
  1422. // is it a basic type?
  1423. ProtoTypeCode typeCode = Helpers.GetTypeCode(type);
  1424. switch(typeCode)
  1425. {
  1426. case ProtoTypeCode.Empty:
  1427. case ProtoTypeCode.Unknown:
  1428. break;
  1429. default:
  1430. return allowBasic; // well-known basic type
  1431. }
  1432. int modelKey = GetKey(ref type);
  1433. if (modelKey >= 0) return allowContract; // known contract type
  1434. // is it a list?
  1435. if (allowLists)
  1436. {
  1437. Type itemType = null;
  1438. if (type.IsArray)
  1439. { // note we don't need to exclude byte[], as that is handled by GetTypeCode already
  1440. if (type.GetArrayRank() == 1) itemType = type.GetElementType();
  1441. }
  1442. else
  1443. {
  1444. itemType = GetListItemType(this, type);
  1445. }
  1446. if (itemType != null) return CanSerialize(itemType, allowBasic, allowContract, false);
  1447. }
  1448. return false;
  1449. }
  1450. /// <summary>
  1451. /// Suggest a .proto definition for the given type
  1452. /// </summary>
  1453. /// <param name="type">The type to generate a .proto definition for, or <c>null</c> to generate a .proto that represents the entire model</param>
  1454. /// <returns>The .proto definition as a string</returns>
  1455. public virtual string GetSchema(Type type)
  1456. {
  1457. throw new NotSupportedException();
  1458. }
  1459. /// <summary>
  1460. /// Used to provide custom services for writing and parsing type names when using dynamic types. Both parsing and formatting
  1461. /// are provided on a single API as it is essential that both are mapped identically at all times.
  1462. /// </summary>
  1463. public event TypeFormatEventHandler DynamicTypeFormatting;
  1464. #if PLAT_BINARYFORMATTER && !(WINRT || PHONE8 || COREFX)
  1465. /// <summary>
  1466. /// Creates a new IFormatter that uses protocol-buffer [de]serialization.
  1467. /// </summary>
  1468. /// <returns>A new IFormatter to be used during [de]serialization.</returns>
  1469. /// <param name="type">The type of object to be [de]deserialized by the formatter.</param>
  1470. public System.Runtime.Serialization.IFormatter CreateFormatter(Type type)
  1471. {
  1472. return new Formatter(this, type);
  1473. }
  1474. internal sealed class Formatter : System.Runtime.Serialization.IFormatter
  1475. {
  1476. private readonly TypeModel model;
  1477. private readonly Type type;
  1478. internal Formatter(TypeModel model, Type type)
  1479. {
  1480. if (model == null) throw new ArgumentNullException("model");
  1481. if (type == null) throw new ArgumentNullException("type");
  1482. this.model = model;
  1483. this.type = type;
  1484. }
  1485. private System.Runtime.Serialization.SerializationBinder binder;
  1486. public System.Runtime.Serialization.SerializationBinder Binder
  1487. {
  1488. get { return binder; }
  1489. set { binder = value; }
  1490. }
  1491. private System.Runtime.Serialization.StreamingContext context;
  1492. public System.Runtime.Serialization.StreamingContext Context
  1493. {
  1494. get { return context; }
  1495. set { context = value; }
  1496. }
  1497. public object Deserialize(Stream source)
  1498. {
  1499. #if FEAT_IKVM
  1500. throw new NotSupportedException();
  1501. #else
  1502. return model.Deserialize(source, null, type, -1, Context);
  1503. #endif
  1504. }
  1505. public void Serialize(Stream destination, object graph)
  1506. {
  1507. model.Serialize(destination, graph, Context);
  1508. }
  1509. private System.Runtime.Serialization.ISurrogateSelector surrogateSelector;
  1510. public System.Runtime.Serialization.ISurrogateSelector SurrogateSelector
  1511. {
  1512. get { return surrogateSelector; }
  1513. set { surrogateSelector = value; }
  1514. }
  1515. }
  1516. #endif
  1517. #if DEBUG // this is used by some unit tests only, to ensure no buffering when buffering is disabled
  1518. private bool forwardsOnly;
  1519. /// <summary>
  1520. /// If true, buffering of nested objects is disabled
  1521. /// </summary>
  1522. public bool ForwardsOnly
  1523. {
  1524. get { return forwardsOnly; }
  1525. set { forwardsOnly = value; }
  1526. }
  1527. #endif
  1528. internal virtual Type GetType(string fullName, Assembly context)
  1529. {
  1530. #if FEAT_IKVM
  1531. throw new NotImplementedException();
  1532. #else
  1533. return ResolveKnownType(fullName, this, context);
  1534. #endif
  1535. }
  1536. [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
  1537. internal static Type ResolveKnownType(string name, TypeModel model, Assembly assembly)
  1538. {
  1539. if (Helpers.IsNullOrEmpty(name)) return null;
  1540. try
  1541. {
  1542. #if FEAT_IKVM
  1543. // looks like a NullReferenceException, but this should call into RuntimeTypeModel's version
  1544. Type type = model == null ? null : model.GetType(name, assembly);
  1545. #else
  1546. Type type = Type.GetType(name);
  1547. #endif
  1548. if (type != null) return type;
  1549. }
  1550. catch { }
  1551. try
  1552. {
  1553. int i = name.IndexOf(',');
  1554. string fullName = (i > 0 ? name.Substring(0, i) : name).Trim();
  1555. #if !(WINRT || FEAT_IKVM || COREFX)
  1556. if (assembly == null) assembly = Assembly.GetCallingAssembly();
  1557. #endif
  1558. Type type = assembly == null ? null : assembly.GetType(fullName);
  1559. if (type != null) return type;
  1560. }
  1561. catch { }
  1562. return null;
  1563. }
  1564. }
  1565. }