ValueMember.cs 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855
  1. #if !NO_RUNTIME
  2. using System;
  3. using ProtoBuf.Serializers;
  4. using System.Globalization;
  5. using System.Collections.Generic;
  6. #if PROFILE259
  7. using System.Reflection;
  8. using System.Linq;
  9. #else
  10. using System.Reflection;
  11. #endif
  12. namespace ProtoBuf.Meta
  13. {
  14. /// <summary>
  15. /// Represents a member (property/field) that is mapped to a protobuf field
  16. /// </summary>
  17. public class ValueMember
  18. {
  19. private int _fieldNumber;
  20. /// <summary>
  21. /// The number that identifies this member in a protobuf stream
  22. /// </summary>
  23. public int FieldNumber
  24. {
  25. get => _fieldNumber;
  26. internal set
  27. {
  28. if (_fieldNumber != value)
  29. {
  30. MetaType.AssertValidFieldNumber(value);
  31. ThrowIfFrozen();
  32. _fieldNumber = value;
  33. }
  34. }
  35. }
  36. private readonly MemberInfo originalMember;
  37. private MemberInfo backingMember;
  38. /// <summary>
  39. /// Gets the member (field/property) which this member relates to.
  40. /// </summary>
  41. public MemberInfo Member { get { return originalMember; } }
  42. /// <summary>
  43. /// Gets the backing member (field/property) which this member relates to
  44. /// </summary>
  45. public MemberInfo BackingMember
  46. {
  47. get { return backingMember; }
  48. set
  49. {
  50. if (backingMember != value)
  51. {
  52. ThrowIfFrozen();
  53. backingMember = value;
  54. }
  55. }
  56. }
  57. private readonly Type parentType, itemType, defaultType, memberType;
  58. private object defaultValue;
  59. /// <summary>
  60. /// Within a list / array / etc, the type of object for each item in the list (especially useful with ArrayList)
  61. /// </summary>
  62. public Type ItemType => itemType;
  63. /// <summary>
  64. /// The underlying type of the member
  65. /// </summary>
  66. public Type MemberType => memberType;
  67. /// <summary>
  68. /// For abstract types (IList etc), the type of concrete object to create (if required)
  69. /// </summary>
  70. public Type DefaultType => defaultType;
  71. /// <summary>
  72. /// The type the defines the member
  73. /// </summary>
  74. public Type ParentType => parentType;
  75. /// <summary>
  76. /// The default value of the item (members with this value will not be serialized)
  77. /// </summary>
  78. public object DefaultValue
  79. {
  80. get { return defaultValue; }
  81. set
  82. {
  83. if (defaultValue != value)
  84. {
  85. ThrowIfFrozen();
  86. defaultValue = value;
  87. }
  88. }
  89. }
  90. private readonly RuntimeTypeModel model;
  91. /// <summary>
  92. /// Creates a new ValueMember instance
  93. /// </summary>
  94. public ValueMember(RuntimeTypeModel model, Type parentType, int fieldNumber, MemberInfo member, Type memberType, Type itemType, Type defaultType, DataFormat dataFormat, object defaultValue)
  95. : this(model, fieldNumber, memberType, itemType, defaultType, dataFormat)
  96. {
  97. if (parentType == null) throw new ArgumentNullException("parentType");
  98. if (fieldNumber < 1 && !Helpers.IsEnum(parentType)) throw new ArgumentOutOfRangeException("fieldNumber");
  99. this.originalMember = member ?? throw new ArgumentNullException("member");
  100. this.parentType = parentType;
  101. if (fieldNumber < 1 && !Helpers.IsEnum(parentType)) throw new ArgumentOutOfRangeException("fieldNumber");
  102. //#if WINRT
  103. if (defaultValue != null && model.MapType(defaultValue.GetType()) != memberType)
  104. //#else
  105. // if (defaultValue != null && !memberType.IsInstanceOfType(defaultValue))
  106. //#endif
  107. {
  108. defaultValue = ParseDefaultValue(memberType, defaultValue);
  109. }
  110. this.defaultValue = defaultValue;
  111. MetaType type = model.FindWithoutAdd(memberType);
  112. if (type != null)
  113. {
  114. AsReference = type.AsReferenceDefault;
  115. }
  116. else
  117. { // we need to scan the hard way; can't risk recursion by fully walking it
  118. AsReference = MetaType.GetAsReferenceDefault(model, memberType);
  119. }
  120. }
  121. /// <summary>
  122. /// Creates a new ValueMember instance
  123. /// </summary>
  124. internal ValueMember(RuntimeTypeModel model, int fieldNumber, Type memberType, Type itemType, Type defaultType, DataFormat dataFormat)
  125. {
  126. _fieldNumber = fieldNumber;
  127. this.memberType = memberType ?? throw new ArgumentNullException(nameof(memberType));
  128. this.itemType = itemType;
  129. this.defaultType = defaultType;
  130. this.model = model ?? throw new ArgumentNullException(nameof(model));
  131. this.dataFormat = dataFormat;
  132. }
  133. internal object GetRawEnumValue()
  134. {
  135. #if PORTABLE || CF || COREFX || PROFILE259
  136. object value = ((FieldInfo)originalMember).GetValue(null);
  137. switch(Helpers.GetTypeCode(Enum.GetUnderlyingType(((FieldInfo)originalMember).FieldType)))
  138. {
  139. case ProtoTypeCode.SByte: return (sbyte)value;
  140. case ProtoTypeCode.Byte: return (byte)value;
  141. case ProtoTypeCode.Int16: return (short)value;
  142. case ProtoTypeCode.UInt16: return (ushort)value;
  143. case ProtoTypeCode.Int32: return (int)value;
  144. case ProtoTypeCode.UInt32: return (uint)value;
  145. case ProtoTypeCode.Int64: return (long)value;
  146. case ProtoTypeCode.UInt64: return (ulong)value;
  147. default:
  148. throw new InvalidOperationException();
  149. }
  150. #else
  151. return ((FieldInfo)originalMember).GetRawConstantValue();
  152. #endif
  153. }
  154. private static object ParseDefaultValue(Type type, object value)
  155. {
  156. {
  157. Type tmp = Helpers.GetUnderlyingType(type);
  158. if (tmp != null) type = tmp;
  159. }
  160. if (value is string s)
  161. {
  162. if (Helpers.IsEnum(type)) return Helpers.ParseEnum(type, s);
  163. switch (Helpers.GetTypeCode(type))
  164. {
  165. case ProtoTypeCode.Boolean: return bool.Parse(s);
  166. case ProtoTypeCode.Byte: return byte.Parse(s, NumberStyles.Integer, CultureInfo.InvariantCulture);
  167. case ProtoTypeCode.Char: // char.Parse missing on CF/phone7
  168. if (s.Length == 1) return s[0];
  169. throw new FormatException("Single character expected: \"" + s + "\"");
  170. case ProtoTypeCode.DateTime: return DateTime.Parse(s, CultureInfo.InvariantCulture);
  171. case ProtoTypeCode.Decimal: return decimal.Parse(s, NumberStyles.Any, CultureInfo.InvariantCulture);
  172. case ProtoTypeCode.Double: return double.Parse(s, NumberStyles.Any, CultureInfo.InvariantCulture);
  173. case ProtoTypeCode.Int16: return short.Parse(s, NumberStyles.Any, CultureInfo.InvariantCulture);
  174. case ProtoTypeCode.Int32: return int.Parse(s, NumberStyles.Any, CultureInfo.InvariantCulture);
  175. case ProtoTypeCode.Int64: return long.Parse(s, NumberStyles.Any, CultureInfo.InvariantCulture);
  176. case ProtoTypeCode.SByte: return sbyte.Parse(s, NumberStyles.Integer, CultureInfo.InvariantCulture);
  177. case ProtoTypeCode.Single: return float.Parse(s, NumberStyles.Any, CultureInfo.InvariantCulture);
  178. case ProtoTypeCode.String: return s;
  179. case ProtoTypeCode.UInt16: return ushort.Parse(s, NumberStyles.Any, CultureInfo.InvariantCulture);
  180. case ProtoTypeCode.UInt32: return uint.Parse(s, NumberStyles.Any, CultureInfo.InvariantCulture);
  181. case ProtoTypeCode.UInt64: return ulong.Parse(s, NumberStyles.Any, CultureInfo.InvariantCulture);
  182. case ProtoTypeCode.TimeSpan: return TimeSpan.Parse(s);
  183. case ProtoTypeCode.Uri: return s; // Uri is decorated as string
  184. case ProtoTypeCode.Guid: return new Guid(s);
  185. }
  186. }
  187. if (Helpers.IsEnum(type)) return Enum.ToObject(type, value);
  188. return Convert.ChangeType(value, type, CultureInfo.InvariantCulture);
  189. }
  190. private IProtoSerializer serializer;
  191. internal IProtoSerializer Serializer
  192. {
  193. get
  194. {
  195. return serializer ?? (serializer = BuildSerializer());
  196. }
  197. }
  198. private DataFormat dataFormat;
  199. /// <summary>
  200. /// Specifies the rules used to process the field; this is used to determine the most appropriate
  201. /// wite-type, but also to describe subtypes <i>within</i> that wire-type (such as SignedVariant)
  202. /// </summary>
  203. public DataFormat DataFormat
  204. {
  205. get { return dataFormat; }
  206. set
  207. {
  208. if (value != dataFormat)
  209. {
  210. ThrowIfFrozen();
  211. this.dataFormat = value;
  212. }
  213. }
  214. }
  215. /// <summary>
  216. /// Indicates whether this field should follow strict encoding rules; this means (for example) that if a "fixed32"
  217. /// is encountered when "variant" is defined, then it will fail (throw an exception) when parsing. Note that
  218. /// when serializing the defined type is always used.
  219. /// </summary>
  220. public bool IsStrict
  221. {
  222. get { return HasFlag(OPTIONS_IsStrict); }
  223. set { SetFlag(OPTIONS_IsStrict, value, true); }
  224. }
  225. /// <summary>
  226. /// Indicates whether this field should use packed encoding (which can save lots of space for repeated primitive values).
  227. /// This option only applies to list/array data of primitive types (int, double, etc).
  228. /// </summary>
  229. public bool IsPacked
  230. {
  231. get { return HasFlag(OPTIONS_IsPacked); }
  232. set { SetFlag(OPTIONS_IsPacked, value, true); }
  233. }
  234. /// <summary>
  235. /// Indicates whether this field should *repace* existing values (the default is false, meaning *append*).
  236. /// This option only applies to list/array data.
  237. /// </summary>
  238. public bool OverwriteList
  239. {
  240. get { return HasFlag(OPTIONS_OverwriteList); }
  241. set { SetFlag(OPTIONS_OverwriteList, value, true); }
  242. }
  243. /// <summary>
  244. /// Indicates whether this field is mandatory.
  245. /// </summary>
  246. public bool IsRequired
  247. {
  248. get { return HasFlag(OPTIONS_IsRequired); }
  249. set { SetFlag(OPTIONS_IsRequired, value, true); }
  250. }
  251. /// <summary>
  252. /// Enables full object-tracking/full-graph support.
  253. /// </summary>
  254. public bool AsReference
  255. {
  256. get { return HasFlag(OPTIONS_AsReference); }
  257. set { SetFlag(OPTIONS_AsReference, value, true); }
  258. }
  259. /// <summary>
  260. /// Embeds the type information into the stream, allowing usage with types not known in advance.
  261. /// </summary>
  262. public bool DynamicType
  263. {
  264. get { return HasFlag(OPTIONS_DynamicType); }
  265. set { SetFlag(OPTIONS_DynamicType, value, true); }
  266. }
  267. /// <summary>
  268. /// Indicates that the member should be treated as a protobuf Map
  269. /// </summary>
  270. public bool IsMap
  271. {
  272. get { return HasFlag(OPTIONS_IsMap); }
  273. set { SetFlag(OPTIONS_IsMap, value, true); }
  274. }
  275. private DataFormat mapKeyFormat, mapValueFormat;
  276. /// <summary>
  277. /// Specifies the data-format that should be used for the key, when IsMap is enabled
  278. /// </summary>
  279. public DataFormat MapKeyFormat
  280. {
  281. get { return mapKeyFormat; }
  282. set
  283. {
  284. if (mapKeyFormat != value)
  285. {
  286. ThrowIfFrozen();
  287. mapKeyFormat = value;
  288. }
  289. }
  290. }
  291. /// <summary>
  292. /// Specifies the data-format that should be used for the value, when IsMap is enabled
  293. /// </summary>
  294. public DataFormat MapValueFormat
  295. {
  296. get { return mapValueFormat; }
  297. set
  298. {
  299. if (mapValueFormat != value)
  300. {
  301. ThrowIfFrozen();
  302. mapValueFormat = value;
  303. }
  304. }
  305. }
  306. private MethodInfo getSpecified, setSpecified;
  307. /// <summary>
  308. /// Specifies methods for working with optional data members.
  309. /// </summary>
  310. /// <param name="getSpecified">Provides a method (null for none) to query whether this member should
  311. /// be serialized; it must be of the form "bool {Method}()". The member is only serialized if the
  312. /// method returns true.</param>
  313. /// <param name="setSpecified">Provides a method (null for none) to indicate that a member was
  314. /// deserialized; it must be of the form "void {Method}(bool)", and will be called with "true"
  315. /// when data is found.</param>
  316. public void SetSpecified(MethodInfo getSpecified, MethodInfo setSpecified)
  317. {
  318. if (this.getSpecified != getSpecified || this.setSpecified != setSpecified)
  319. {
  320. if (getSpecified != null)
  321. {
  322. if (getSpecified.ReturnType != model.MapType(typeof(bool))
  323. || getSpecified.IsStatic
  324. || getSpecified.GetParameters().Length != 0)
  325. {
  326. throw new ArgumentException("Invalid pattern for checking member-specified", "getSpecified");
  327. }
  328. }
  329. if (setSpecified != null)
  330. {
  331. ParameterInfo[] args;
  332. if (setSpecified.ReturnType != model.MapType(typeof(void))
  333. || setSpecified.IsStatic
  334. || (args = setSpecified.GetParameters()).Length != 1
  335. || args[0].ParameterType != model.MapType(typeof(bool)))
  336. {
  337. throw new ArgumentException("Invalid pattern for setting member-specified", "setSpecified");
  338. }
  339. }
  340. ThrowIfFrozen();
  341. this.getSpecified = getSpecified;
  342. this.setSpecified = setSpecified;
  343. }
  344. }
  345. private void ThrowIfFrozen()
  346. {
  347. if (serializer != null) throw new InvalidOperationException("The type cannot be changed once a serializer has been generated");
  348. }
  349. internal bool ResolveMapTypes(out Type dictionaryType, out Type keyType, out Type valueType)
  350. {
  351. dictionaryType = keyType = valueType = null;
  352. try
  353. {
  354. #if COREFX || PROFILE259
  355. var info = memberType.GetTypeInfo();
  356. #else
  357. var info = memberType;
  358. #endif
  359. if (ImmutableCollectionDecorator.IdentifyImmutable(model, MemberType, out _, out _, out _, out _, out _, out _))
  360. {
  361. return false;
  362. }
  363. if (info.IsInterface && info.IsGenericType && info.GetGenericTypeDefinition() == typeof(IDictionary<,>))
  364. {
  365. #if PROFILE259
  366. var typeArgs = memberType.GetGenericTypeDefinition().GenericTypeArguments;
  367. #else
  368. var typeArgs = memberType.GetGenericArguments();
  369. #endif
  370. if (IsValidMapKeyType(typeArgs[0]))
  371. {
  372. keyType = typeArgs[0];
  373. valueType = typeArgs[1];
  374. dictionaryType = memberType;
  375. }
  376. return false;
  377. }
  378. #if PROFILE259
  379. foreach (var iType in memberType.GetTypeInfo().ImplementedInterfaces)
  380. #else
  381. foreach (var iType in memberType.GetInterfaces())
  382. #endif
  383. {
  384. #if COREFX || PROFILE259
  385. info = iType.GetTypeInfo();
  386. #else
  387. info = iType;
  388. #endif
  389. if (info.IsGenericType && info.GetGenericTypeDefinition() == typeof(IDictionary<,>))
  390. {
  391. if (dictionaryType != null) throw new InvalidOperationException("Multiple dictionary interfaces implemented by type: " + memberType.FullName);
  392. #if PROFILE259
  393. var typeArgs = iType.GetGenericTypeDefinition().GenericTypeArguments;
  394. #else
  395. var typeArgs = iType.GetGenericArguments();
  396. #endif
  397. if (IsValidMapKeyType(typeArgs[0]))
  398. {
  399. keyType = typeArgs[0];
  400. valueType = typeArgs[1];
  401. dictionaryType = memberType;
  402. }
  403. }
  404. }
  405. if (dictionaryType == null) return false;
  406. // (note we checked the key type already)
  407. // not a map if value is repeated
  408. Type itemType = null, defaultType = null;
  409. model.ResolveListTypes(valueType, ref itemType, ref defaultType);
  410. if (itemType != null) return false;
  411. return dictionaryType != null;
  412. }
  413. catch
  414. {
  415. // if it isn't a good fit; don't use "map"
  416. return false;
  417. }
  418. }
  419. static bool IsValidMapKeyType(Type type)
  420. {
  421. if (type == null || Helpers.IsEnum(type)) return false;
  422. switch (Helpers.GetTypeCode(type))
  423. {
  424. case ProtoTypeCode.Boolean:
  425. case ProtoTypeCode.Byte:
  426. case ProtoTypeCode.Char:
  427. case ProtoTypeCode.Int16:
  428. case ProtoTypeCode.Int32:
  429. case ProtoTypeCode.Int64:
  430. case ProtoTypeCode.String:
  431. case ProtoTypeCode.SByte:
  432. case ProtoTypeCode.UInt16:
  433. case ProtoTypeCode.UInt32:
  434. case ProtoTypeCode.UInt64:
  435. return true;
  436. }
  437. return false;
  438. }
  439. private IProtoSerializer BuildSerializer()
  440. {
  441. int opaqueToken = 0;
  442. try
  443. {
  444. model.TakeLock(ref opaqueToken);// check nobody is still adding this type
  445. var member = backingMember ?? originalMember;
  446. IProtoSerializer ser;
  447. if (IsMap)
  448. {
  449. ResolveMapTypes(out var dictionaryType, out var keyType, out var valueType);
  450. if (dictionaryType == null)
  451. {
  452. throw new InvalidOperationException("Unable to resolve map type for type: " + memberType.FullName);
  453. }
  454. var concreteType = defaultType;
  455. if (concreteType == null && Helpers.IsClass(memberType))
  456. {
  457. concreteType = memberType;
  458. }
  459. var keySer = TryGetCoreSerializer(model, MapKeyFormat, keyType, out var keyWireType, false, false, false, false);
  460. if (!AsReference)
  461. {
  462. AsReference = MetaType.GetAsReferenceDefault(model, valueType);
  463. }
  464. var valueSer = TryGetCoreSerializer(model, MapValueFormat, valueType, out var valueWireType, AsReference, DynamicType, false, true);
  465. #if PROFILE259
  466. IEnumerable<ConstructorInfo> ctors = typeof(MapDecorator<,,>).MakeGenericType(new Type[] { dictionaryType, keyType, valueType }).GetTypeInfo().DeclaredConstructors;
  467. if (ctors.Count() != 1)
  468. {
  469. throw new InvalidOperationException("Unable to resolve MapDecorator constructor");
  470. }
  471. ser = (IProtoSerializer)ctors.First().Invoke(new object[] {model, concreteType, keySer, valueSer, _fieldNumber,
  472. DataFormat == DataFormat.Group ? WireType.StartGroup : WireType.String, keyWireType, valueWireType, OverwriteList });
  473. #else
  474. var ctors = typeof(MapDecorator<,,>).MakeGenericType(new Type[] { dictionaryType, keyType, valueType }).GetConstructors(
  475. BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
  476. if (ctors.Length != 1) throw new InvalidOperationException("Unable to resolve MapDecorator constructor");
  477. ser = (IProtoSerializer)ctors[0].Invoke(new object[] {model, concreteType, keySer, valueSer, _fieldNumber,
  478. DataFormat == DataFormat.Group ? WireType.StartGroup : WireType.String, keyWireType, valueWireType, OverwriteList });
  479. #endif
  480. }
  481. else
  482. {
  483. Type finalType = itemType ?? memberType;
  484. ser = TryGetCoreSerializer(model, dataFormat, finalType, out WireType wireType, AsReference, DynamicType, OverwriteList, true);
  485. if (ser == null)
  486. {
  487. throw new InvalidOperationException("No serializer defined for type: " + finalType.FullName);
  488. }
  489. // apply tags
  490. if (itemType != null && SupportNull)
  491. {
  492. if (IsPacked)
  493. {
  494. throw new NotSupportedException("Packed encodings cannot support null values");
  495. }
  496. ser = new TagDecorator(NullDecorator.Tag, wireType, IsStrict, ser);
  497. ser = new NullDecorator(model, ser);
  498. ser = new TagDecorator(_fieldNumber, WireType.StartGroup, false, ser);
  499. }
  500. else
  501. {
  502. ser = new TagDecorator(_fieldNumber, wireType, IsStrict, ser);
  503. }
  504. // apply lists if appropriate
  505. if (itemType != null)
  506. {
  507. Type underlyingItemType = SupportNull ? itemType : Helpers.GetUnderlyingType(itemType) ?? itemType;
  508. Helpers.DebugAssert(underlyingItemType == ser.ExpectedType
  509. || (ser.ExpectedType == model.MapType(typeof(object)) && !Helpers.IsValueType(underlyingItemType))
  510. , "Wrong type in the tail; expected {0}, received {1}", ser.ExpectedType, underlyingItemType);
  511. if (memberType.IsArray)
  512. {
  513. ser = new ArrayDecorator(model, ser, _fieldNumber, IsPacked, wireType, memberType, OverwriteList, SupportNull);
  514. }
  515. else
  516. {
  517. ser = ListDecorator.Create(model, memberType, defaultType, ser, _fieldNumber, IsPacked, wireType, member != null && PropertyDecorator.CanWrite(model, member), OverwriteList, SupportNull);
  518. }
  519. }
  520. else if (defaultValue != null && !IsRequired && getSpecified == null)
  521. { // note: "ShouldSerialize*" / "*Specified" / etc ^^^^ take precedence over defaultValue,
  522. // as does "IsRequired"
  523. ser = new DefaultValueDecorator(model, defaultValue, ser);
  524. }
  525. if (memberType == model.MapType(typeof(Uri)))
  526. {
  527. ser = new UriDecorator(model, ser);
  528. }
  529. #if PORTABLE
  530. else if(memberType.FullName == typeof(Uri).FullName)
  531. {
  532. // In PCLs, the Uri type may not match (WinRT uses Internal/Uri, .Net uses System/Uri)
  533. ser = new ReflectedUriDecorator(memberType, model, ser);
  534. }
  535. #endif
  536. }
  537. if (member != null)
  538. {
  539. if (member is PropertyInfo prop)
  540. {
  541. ser = new PropertyDecorator(model, parentType, prop, ser);
  542. }
  543. else if (member is FieldInfo fld)
  544. {
  545. ser = new FieldDecorator(parentType, fld, ser);
  546. }
  547. else
  548. {
  549. throw new InvalidOperationException();
  550. }
  551. if (getSpecified != null || setSpecified != null)
  552. {
  553. ser = new MemberSpecifiedDecorator(getSpecified, setSpecified, ser);
  554. }
  555. }
  556. return ser;
  557. }
  558. finally
  559. {
  560. model.ReleaseLock(opaqueToken);
  561. }
  562. }
  563. private static WireType GetIntWireType(DataFormat format, int width)
  564. {
  565. switch (format)
  566. {
  567. case DataFormat.ZigZag: return WireType.SignedVariant;
  568. case DataFormat.FixedSize: return width == 32 ? WireType.Fixed32 : WireType.Fixed64;
  569. case DataFormat.TwosComplement:
  570. case DataFormat.Default: return WireType.Variant;
  571. default: throw new InvalidOperationException();
  572. }
  573. }
  574. private static WireType GetDateTimeWireType(DataFormat format)
  575. {
  576. switch (format)
  577. {
  578. case DataFormat.Group: return WireType.StartGroup;
  579. case DataFormat.FixedSize: return WireType.Fixed64;
  580. case DataFormat.WellKnown:
  581. case DataFormat.Default:
  582. return WireType.String;
  583. default: throw new InvalidOperationException();
  584. }
  585. }
  586. internal static IProtoSerializer TryGetCoreSerializer(RuntimeTypeModel model, DataFormat dataFormat, Type type, out WireType defaultWireType,
  587. bool asReference, bool dynamicType, bool overwriteList, bool allowComplexTypes)
  588. {
  589. {
  590. Type tmp = Helpers.GetUnderlyingType(type);
  591. if (tmp != null) type = tmp;
  592. }
  593. if (Helpers.IsEnum(type))
  594. {
  595. if (allowComplexTypes && model != null)
  596. {
  597. // need to do this before checking the typecode; an int enum will report Int32 etc
  598. defaultWireType = WireType.Variant;
  599. return new EnumSerializer(type, model.GetEnumMap(type));
  600. }
  601. else
  602. { // enum is fine for adding as a meta-type
  603. defaultWireType = WireType.None;
  604. return null;
  605. }
  606. }
  607. ProtoTypeCode code = Helpers.GetTypeCode(type);
  608. switch (code)
  609. {
  610. case ProtoTypeCode.Int32:
  611. defaultWireType = GetIntWireType(dataFormat, 32);
  612. return new Int32Serializer(model);
  613. case ProtoTypeCode.UInt32:
  614. defaultWireType = GetIntWireType(dataFormat, 32);
  615. return new UInt32Serializer(model);
  616. case ProtoTypeCode.Int64:
  617. defaultWireType = GetIntWireType(dataFormat, 64);
  618. return new Int64Serializer(model);
  619. case ProtoTypeCode.UInt64:
  620. defaultWireType = GetIntWireType(dataFormat, 64);
  621. return new UInt64Serializer(model);
  622. case ProtoTypeCode.String:
  623. defaultWireType = WireType.String;
  624. if (asReference)
  625. {
  626. return new NetObjectSerializer(model, model.MapType(typeof(string)), 0, BclHelpers.NetObjectOptions.AsReference);
  627. }
  628. return new StringSerializer(model);
  629. case ProtoTypeCode.Single:
  630. defaultWireType = WireType.Fixed32;
  631. return new SingleSerializer(model);
  632. case ProtoTypeCode.Double:
  633. defaultWireType = WireType.Fixed64;
  634. return new DoubleSerializer(model);
  635. case ProtoTypeCode.Boolean:
  636. defaultWireType = WireType.Variant;
  637. return new BooleanSerializer(model);
  638. case ProtoTypeCode.DateTime:
  639. defaultWireType = GetDateTimeWireType(dataFormat);
  640. return new DateTimeSerializer(dataFormat, model);
  641. case ProtoTypeCode.Decimal:
  642. defaultWireType = WireType.String;
  643. return new DecimalSerializer(model);
  644. case ProtoTypeCode.Byte:
  645. defaultWireType = GetIntWireType(dataFormat, 32);
  646. return new ByteSerializer(model);
  647. case ProtoTypeCode.SByte:
  648. defaultWireType = GetIntWireType(dataFormat, 32);
  649. return new SByteSerializer(model);
  650. case ProtoTypeCode.Char:
  651. defaultWireType = WireType.Variant;
  652. return new CharSerializer(model);
  653. case ProtoTypeCode.Int16:
  654. defaultWireType = GetIntWireType(dataFormat, 32);
  655. return new Int16Serializer(model);
  656. case ProtoTypeCode.UInt16:
  657. defaultWireType = GetIntWireType(dataFormat, 32);
  658. return new UInt16Serializer(model);
  659. case ProtoTypeCode.TimeSpan:
  660. defaultWireType = GetDateTimeWireType(dataFormat);
  661. return new TimeSpanSerializer(dataFormat, model);
  662. case ProtoTypeCode.Guid:
  663. defaultWireType = dataFormat == DataFormat.Group ? WireType.StartGroup : WireType.String;
  664. return new GuidSerializer(model);
  665. case ProtoTypeCode.Uri:
  666. defaultWireType = WireType.String;
  667. return new StringSerializer(model);
  668. case ProtoTypeCode.ByteArray:
  669. defaultWireType = WireType.String;
  670. return new BlobSerializer(model, overwriteList);
  671. case ProtoTypeCode.Type:
  672. defaultWireType = WireType.String;
  673. return new SystemTypeSerializer(model);
  674. }
  675. IProtoSerializer parseable = model.AllowParseableTypes ? ParseableSerializer.TryCreate(type, model) : null;
  676. if (parseable != null)
  677. {
  678. defaultWireType = WireType.String;
  679. return parseable;
  680. }
  681. if (allowComplexTypes && model != null)
  682. {
  683. int key = model.GetKey(type, false, true);
  684. MetaType meta = null;
  685. if (key >= 0)
  686. {
  687. meta = model[type];
  688. if (dataFormat == DataFormat.Default && meta.IsGroup)
  689. {
  690. dataFormat = DataFormat.Group;
  691. }
  692. }
  693. if (asReference || dynamicType)
  694. {
  695. BclHelpers.NetObjectOptions options = BclHelpers.NetObjectOptions.None;
  696. if (asReference) options |= BclHelpers.NetObjectOptions.AsReference;
  697. if (dynamicType) options |= BclHelpers.NetObjectOptions.DynamicType;
  698. if (meta != null)
  699. { // exists
  700. if (asReference && Helpers.IsValueType(type))
  701. {
  702. string message = "AsReference cannot be used with value-types";
  703. if (type.Name == "KeyValuePair`2")
  704. {
  705. message += "; please see https://stackoverflow.com/q/14436606/23354";
  706. }
  707. else
  708. {
  709. message += ": " + type.FullName;
  710. }
  711. throw new InvalidOperationException(message);
  712. }
  713. if (asReference && meta.IsAutoTuple) options |= BclHelpers.NetObjectOptions.LateSet;
  714. if (meta.UseConstructor) options |= BclHelpers.NetObjectOptions.UseConstructor;
  715. }
  716. defaultWireType = dataFormat == DataFormat.Group ? WireType.StartGroup : WireType.String;
  717. return new NetObjectSerializer(model, type, key, options);
  718. }
  719. if (key >= 0)
  720. {
  721. defaultWireType = dataFormat == DataFormat.Group ? WireType.StartGroup : WireType.String;
  722. return new SubItemSerializer(type, key, meta, true);
  723. }
  724. }
  725. defaultWireType = WireType.None;
  726. return null;
  727. }
  728. private string name;
  729. internal void SetName(string name)
  730. {
  731. if (name != this.name)
  732. {
  733. ThrowIfFrozen();
  734. this.name = name;
  735. }
  736. }
  737. /// <summary>
  738. /// Gets the logical name for this member in the schema (this is not critical for binary serialization, but may be used
  739. /// when inferring a schema).
  740. /// </summary>
  741. public string Name
  742. {
  743. get { return string.IsNullOrEmpty(name) ? originalMember.Name : name; }
  744. set { SetName(value); }
  745. }
  746. private const byte
  747. OPTIONS_IsStrict = 1,
  748. OPTIONS_IsPacked = 2,
  749. OPTIONS_IsRequired = 4,
  750. OPTIONS_OverwriteList = 8,
  751. OPTIONS_SupportNull = 16,
  752. OPTIONS_AsReference = 32,
  753. OPTIONS_IsMap = 64,
  754. OPTIONS_DynamicType = 128;
  755. private byte flags;
  756. private bool HasFlag(byte flag) { return (flags & flag) == flag; }
  757. private void SetFlag(byte flag, bool value, bool throwIfFrozen)
  758. {
  759. if (throwIfFrozen && HasFlag(flag) != value)
  760. {
  761. ThrowIfFrozen();
  762. }
  763. if (value)
  764. flags |= flag;
  765. else
  766. flags = (byte)(flags & ~flag);
  767. }
  768. /// <summary>
  769. /// Should lists have extended support for null values? Note this makes the serialization less efficient.
  770. /// </summary>
  771. public bool SupportNull
  772. {
  773. get { return HasFlag(OPTIONS_SupportNull); }
  774. set { SetFlag(OPTIONS_SupportNull, value, true); }
  775. }
  776. internal string GetSchemaTypeName(bool applyNetObjectProxy, ref RuntimeTypeModel.CommonImports imports)
  777. {
  778. Type effectiveType = ItemType;
  779. if (effectiveType == null) effectiveType = MemberType;
  780. return model.GetSchemaTypeName(effectiveType, DataFormat, applyNetObjectProxy && AsReference, applyNetObjectProxy && DynamicType, ref imports);
  781. }
  782. internal sealed class Comparer : System.Collections.IComparer, IComparer<ValueMember>
  783. {
  784. public static readonly Comparer Default = new Comparer();
  785. public int Compare(object x, object y)
  786. {
  787. return Compare(x as ValueMember, y as ValueMember);
  788. }
  789. public int Compare(ValueMember x, ValueMember y)
  790. {
  791. if (ReferenceEquals(x, y)) return 0;
  792. if (x == null) return -1;
  793. if (y == null) return 1;
  794. return x.FieldNumber.CompareTo(y.FieldNumber);
  795. }
  796. }
  797. }
  798. }
  799. #endif