FieldCodec.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792
  1. #region Copyright notice and license
  2. // Protocol Buffers - Google's data interchange format
  3. // Copyright 2015 Google Inc. All rights reserved.
  4. // https://developers.google.com/protocol-buffers/
  5. //
  6. // Redistribution and use in source and binary forms, with or without
  7. // modification, are permitted provided that the following conditions are
  8. // met:
  9. //
  10. // * Redistributions of source code must retain the above copyright
  11. // notice, this list of conditions and the following disclaimer.
  12. // * Redistributions in binary form must reproduce the above
  13. // copyright notice, this list of conditions and the following disclaimer
  14. // in the documentation and/or other materials provided with the
  15. // distribution.
  16. // * Neither the name of Google Inc. nor the names of its
  17. // contributors may be used to endorse or promote products derived from
  18. // this software without specific prior written permission.
  19. //
  20. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. #endregion
  32. using Google.Protobuf.Collections;
  33. using Google.Protobuf.Compatibility;
  34. using Google.Protobuf.WellKnownTypes;
  35. using System;
  36. using System.Collections.Generic;
  37. namespace Google.Protobuf
  38. {
  39. /// <summary>
  40. /// Factory methods for <see cref="FieldCodec{T}"/>.
  41. /// </summary>
  42. public static class FieldCodec
  43. {
  44. // TODO: Avoid the "dual hit" of lambda expressions: create open delegates instead. (At least test...)
  45. /// <summary>
  46. /// Retrieves a codec suitable for a string field with the given tag.
  47. /// </summary>
  48. /// <param name="tag">The tag.</param>
  49. /// <returns>A codec for the given tag.</returns>
  50. public static FieldCodec<string> ForString(uint tag)
  51. {
  52. return FieldCodec.ForString(tag, "");
  53. }
  54. /// <summary>
  55. /// Retrieves a codec suitable for a bytes field with the given tag.
  56. /// </summary>
  57. /// <param name="tag">The tag.</param>
  58. /// <returns>A codec for the given tag.</returns>
  59. public static FieldCodec<ByteString> ForBytes(uint tag)
  60. {
  61. return FieldCodec.ForBytes(tag, ByteString.Empty);
  62. }
  63. /// <summary>
  64. /// Retrieves a codec suitable for a bool field with the given tag.
  65. /// </summary>
  66. /// <param name="tag">The tag.</param>
  67. /// <returns>A codec for the given tag.</returns>
  68. public static FieldCodec<bool> ForBool(uint tag)
  69. {
  70. return FieldCodec.ForBool(tag, false);
  71. }
  72. /// <summary>
  73. /// Retrieves a codec suitable for an int32 field with the given tag.
  74. /// </summary>
  75. /// <param name="tag">The tag.</param>
  76. /// <returns>A codec for the given tag.</returns>
  77. public static FieldCodec<int> ForInt32(uint tag)
  78. {
  79. return FieldCodec.ForInt32(tag, 0);
  80. }
  81. /// <summary>
  82. /// Retrieves a codec suitable for an sint32 field with the given tag.
  83. /// </summary>
  84. /// <param name="tag">The tag.</param>
  85. /// <returns>A codec for the given tag.</returns>
  86. public static FieldCodec<int> ForSInt32(uint tag)
  87. {
  88. return FieldCodec.ForSInt32(tag, 0);
  89. }
  90. /// <summary>
  91. /// Retrieves a codec suitable for a fixed32 field with the given tag.
  92. /// </summary>
  93. /// <param name="tag">The tag.</param>
  94. /// <returns>A codec for the given tag.</returns>
  95. public static FieldCodec<uint> ForFixed32(uint tag)
  96. {
  97. return FieldCodec.ForFixed32(tag, 0);
  98. }
  99. /// <summary>
  100. /// Retrieves a codec suitable for an sfixed32 field with the given tag.
  101. /// </summary>
  102. /// <param name="tag">The tag.</param>
  103. /// <returns>A codec for the given tag.</returns>
  104. public static FieldCodec<int> ForSFixed32(uint tag)
  105. {
  106. return FieldCodec.ForSFixed32(tag, 0);
  107. }
  108. /// <summary>
  109. /// Retrieves a codec suitable for a uint32 field with the given tag.
  110. /// </summary>
  111. /// <param name="tag">The tag.</param>
  112. /// <returns>A codec for the given tag.</returns>
  113. public static FieldCodec<uint> ForUInt32(uint tag)
  114. {
  115. return FieldCodec.ForUInt32(tag, 0);
  116. }
  117. /// <summary>
  118. /// Retrieves a codec suitable for an int64 field with the given tag.
  119. /// </summary>
  120. /// <param name="tag">The tag.</param>
  121. /// <returns>A codec for the given tag.</returns>
  122. public static FieldCodec<long> ForInt64(uint tag)
  123. {
  124. return FieldCodec.ForInt64(tag, 0);
  125. }
  126. /// <summary>
  127. /// Retrieves a codec suitable for an sint64 field with the given tag.
  128. /// </summary>
  129. /// <param name="tag">The tag.</param>
  130. /// <returns>A codec for the given tag.</returns>
  131. public static FieldCodec<long> ForSInt64(uint tag)
  132. {
  133. return FieldCodec.ForSInt64(tag, 0);
  134. }
  135. /// <summary>
  136. /// Retrieves a codec suitable for a fixed64 field with the given tag.
  137. /// </summary>
  138. /// <param name="tag">The tag.</param>
  139. /// <returns>A codec for the given tag.</returns>
  140. public static FieldCodec<ulong> ForFixed64(uint tag)
  141. {
  142. return FieldCodec.ForFixed64(tag, 0);
  143. }
  144. /// <summary>
  145. /// Retrieves a codec suitable for an sfixed64 field with the given tag.
  146. /// </summary>
  147. /// <param name="tag">The tag.</param>
  148. /// <returns>A codec for the given tag.</returns>
  149. public static FieldCodec<long> ForSFixed64(uint tag)
  150. {
  151. return FieldCodec.ForSFixed64(tag, 0);
  152. }
  153. /// <summary>
  154. /// Retrieves a codec suitable for a uint64 field with the given tag.
  155. /// </summary>
  156. /// <param name="tag">The tag.</param>
  157. /// <returns>A codec for the given tag.</returns>
  158. public static FieldCodec<ulong> ForUInt64(uint tag)
  159. {
  160. return FieldCodec.ForUInt64(tag, 0);
  161. }
  162. /// <summary>
  163. /// Retrieves a codec suitable for a float field with the given tag.
  164. /// </summary>
  165. /// <param name="tag">The tag.</param>
  166. /// <returns>A codec for the given tag.</returns>
  167. public static FieldCodec<float> ForFloat(uint tag)
  168. {
  169. return FieldCodec.ForFloat(tag, 0);
  170. }
  171. /// <summary>
  172. /// Retrieves a codec suitable for a double field with the given tag.
  173. /// </summary>
  174. /// <param name="tag">The tag.</param>
  175. /// <returns>A codec for the given tag.</returns>
  176. public static FieldCodec<double> ForDouble(uint tag)
  177. {
  178. return FieldCodec.ForDouble(tag, 0);
  179. }
  180. // Enums are tricky. We can probably use expression trees to build these delegates automatically,
  181. // but it's easy to generate the code for it.
  182. /// <summary>
  183. /// Retrieves a codec suitable for an enum field with the given tag.
  184. /// </summary>
  185. /// <param name="tag">The tag.</param>
  186. /// <param name="toInt32">A conversion function from <see cref="Int32"/> to the enum type.</param>
  187. /// <param name="fromInt32">A conversion function from the enum type to <see cref="Int32"/>.</param>
  188. /// <returns>A codec for the given tag.</returns>
  189. public static FieldCodec<T> ForEnum<T>(uint tag, Func<T, int> toInt32, Func<int, T> fromInt32)
  190. {
  191. return FieldCodec.ForEnum(tag, toInt32, fromInt32, default(T));
  192. }
  193. /// <summary>
  194. /// Retrieves a codec suitable for a string field with the given tag.
  195. /// </summary>
  196. /// <param name="tag">The tag.</param>
  197. /// <param name="defaultValue">The default value.</param>
  198. /// <returns>A codec for the given tag.</returns>
  199. public static FieldCodec<string> ForString(uint tag, string defaultValue)
  200. {
  201. return new FieldCodec<string>(input => input.ReadString(), (output, value) => output.WriteString(value), CodedOutputStream.ComputeStringSize, tag, defaultValue);
  202. }
  203. /// <summary>
  204. /// Retrieves a codec suitable for a bytes field with the given tag.
  205. /// </summary>
  206. /// <param name="tag">The tag.</param>
  207. /// <param name="defaultValue">The default value.</param>
  208. /// <returns>A codec for the given tag.</returns>
  209. public static FieldCodec<ByteString> ForBytes(uint tag, ByteString defaultValue)
  210. {
  211. return new FieldCodec<ByteString>(input => input.ReadBytes(), (output, value) => output.WriteBytes(value), CodedOutputStream.ComputeBytesSize, tag, defaultValue);
  212. }
  213. /// <summary>
  214. /// Retrieves a codec suitable for a bool field with the given tag.
  215. /// </summary>
  216. /// <param name="tag">The tag.</param>
  217. /// <param name="defaultValue">The default value.</param>
  218. /// <returns>A codec for the given tag.</returns>
  219. public static FieldCodec<bool> ForBool(uint tag, bool defaultValue)
  220. {
  221. return new FieldCodec<bool>(input => input.ReadBool(), (output, value) => output.WriteBool(value), CodedOutputStream.BoolSize, tag, defaultValue);
  222. }
  223. /// <summary>
  224. /// Retrieves a codec suitable for an int32 field with the given tag.
  225. /// </summary>
  226. /// <param name="tag">The tag.</param>
  227. /// <param name="defaultValue">The default value.</param>
  228. /// <returns>A codec for the given tag.</returns>
  229. public static FieldCodec<int> ForInt32(uint tag, int defaultValue)
  230. {
  231. return new FieldCodec<int>(input => input.ReadInt32(), (output, value) => output.WriteInt32(value), CodedOutputStream.ComputeInt32Size, tag, defaultValue);
  232. }
  233. /// <summary>
  234. /// Retrieves a codec suitable for an sint32 field with the given tag.
  235. /// </summary>
  236. /// <param name="tag">The tag.</param>
  237. /// <param name="defaultValue">The default value.</param>
  238. /// <returns>A codec for the given tag.</returns>
  239. public static FieldCodec<int> ForSInt32(uint tag, int defaultValue)
  240. {
  241. return new FieldCodec<int>(input => input.ReadSInt32(), (output, value) => output.WriteSInt32(value), CodedOutputStream.ComputeSInt32Size, tag, defaultValue);
  242. }
  243. /// <summary>
  244. /// Retrieves a codec suitable for a fixed32 field with the given tag.
  245. /// </summary>
  246. /// <param name="tag">The tag.</param>
  247. /// <param name="defaultValue">The default value.</param>
  248. /// <returns>A codec for the given tag.</returns>
  249. public static FieldCodec<uint> ForFixed32(uint tag, uint defaultValue)
  250. {
  251. return new FieldCodec<uint>(input => input.ReadFixed32(), (output, value) => output.WriteFixed32(value), 4, tag, defaultValue);
  252. }
  253. /// <summary>
  254. /// Retrieves a codec suitable for an sfixed32 field with the given tag.
  255. /// </summary>
  256. /// <param name="tag">The tag.</param>
  257. /// <param name="defaultValue">The default value.</param>
  258. /// <returns>A codec for the given tag.</returns>
  259. public static FieldCodec<int> ForSFixed32(uint tag, int defaultValue)
  260. {
  261. return new FieldCodec<int>(input => input.ReadSFixed32(), (output, value) => output.WriteSFixed32(value), 4, tag, defaultValue);
  262. }
  263. /// <summary>
  264. /// Retrieves a codec suitable for a uint32 field with the given tag.
  265. /// </summary>
  266. /// <param name="tag">The tag.</param>
  267. /// <param name="defaultValue">The default value.</param>
  268. /// <returns>A codec for the given tag.</returns>
  269. public static FieldCodec<uint> ForUInt32(uint tag, uint defaultValue)
  270. {
  271. return new FieldCodec<uint>(input => input.ReadUInt32(), (output, value) => output.WriteUInt32(value), CodedOutputStream.ComputeUInt32Size, tag, defaultValue);
  272. }
  273. /// <summary>
  274. /// Retrieves a codec suitable for an int64 field with the given tag.
  275. /// </summary>
  276. /// <param name="tag">The tag.</param>
  277. /// <param name="defaultValue">The default value.</param>
  278. /// <returns>A codec for the given tag.</returns>
  279. public static FieldCodec<long> ForInt64(uint tag, long defaultValue)
  280. {
  281. return new FieldCodec<long>(input => input.ReadInt64(), (output, value) => output.WriteInt64(value), CodedOutputStream.ComputeInt64Size, tag, defaultValue);
  282. }
  283. /// <summary>
  284. /// Retrieves a codec suitable for an sint64 field with the given tag.
  285. /// </summary>
  286. /// <param name="tag">The tag.</param>
  287. /// <param name="defaultValue">The default value.</param>
  288. /// <returns>A codec for the given tag.</returns>
  289. public static FieldCodec<long> ForSInt64(uint tag, long defaultValue)
  290. {
  291. return new FieldCodec<long>(input => input.ReadSInt64(), (output, value) => output.WriteSInt64(value), CodedOutputStream.ComputeSInt64Size, tag, defaultValue);
  292. }
  293. /// <summary>
  294. /// Retrieves a codec suitable for a fixed64 field with the given tag.
  295. /// </summary>
  296. /// <param name="tag">The tag.</param>
  297. /// <param name="defaultValue">The default value.</param>
  298. /// <returns>A codec for the given tag.</returns>
  299. public static FieldCodec<ulong> ForFixed64(uint tag, ulong defaultValue)
  300. {
  301. return new FieldCodec<ulong>(input => input.ReadFixed64(), (output, value) => output.WriteFixed64(value), 8, tag, defaultValue);
  302. }
  303. /// <summary>
  304. /// Retrieves a codec suitable for an sfixed64 field with the given tag.
  305. /// </summary>
  306. /// <param name="tag">The tag.</param>
  307. /// <param name="defaultValue">The default value.</param>
  308. /// <returns>A codec for the given tag.</returns>
  309. public static FieldCodec<long> ForSFixed64(uint tag, long defaultValue)
  310. {
  311. return new FieldCodec<long>(input => input.ReadSFixed64(), (output, value) => output.WriteSFixed64(value), 8, tag, defaultValue);
  312. }
  313. /// <summary>
  314. /// Retrieves a codec suitable for a uint64 field with the given tag.
  315. /// </summary>
  316. /// <param name="tag">The tag.</param>
  317. /// <param name="defaultValue">The default value.</param>
  318. /// <returns>A codec for the given tag.</returns>
  319. public static FieldCodec<ulong> ForUInt64(uint tag, ulong defaultValue)
  320. {
  321. return new FieldCodec<ulong>(input => input.ReadUInt64(), (output, value) => output.WriteUInt64(value), CodedOutputStream.ComputeUInt64Size, tag, defaultValue);
  322. }
  323. /// <summary>
  324. /// Retrieves a codec suitable for a float field with the given tag.
  325. /// </summary>
  326. /// <param name="tag">The tag.</param>
  327. /// <param name="defaultValue">The default value.</param>
  328. /// <returns>A codec for the given tag.</returns>
  329. public static FieldCodec<float> ForFloat(uint tag, float defaultValue)
  330. {
  331. return new FieldCodec<float>(input => input.ReadFloat(), (output, value) => output.WriteFloat(value), CodedOutputStream.FloatSize, tag, defaultValue);
  332. }
  333. /// <summary>
  334. /// Retrieves a codec suitable for a double field with the given tag.
  335. /// </summary>
  336. /// <param name="tag">The tag.</param>
  337. /// <param name="defaultValue">The default value.</param>
  338. /// <returns>A codec for the given tag.</returns>
  339. public static FieldCodec<double> ForDouble(uint tag, double defaultValue)
  340. {
  341. return new FieldCodec<double>(input => input.ReadDouble(), (output, value) => output.WriteDouble(value), CodedOutputStream.DoubleSize, tag, defaultValue);
  342. }
  343. // Enums are tricky. We can probably use expression trees to build these delegates automatically,
  344. // but it's easy to generate the code for it.
  345. /// <summary>
  346. /// Retrieves a codec suitable for an enum field with the given tag.
  347. /// </summary>
  348. /// <param name="tag">The tag.</param>
  349. /// <param name="toInt32">A conversion function from <see cref="Int32"/> to the enum type.</param>
  350. /// <param name="fromInt32">A conversion function from the enum type to <see cref="Int32"/>.</param>
  351. /// <param name="defaultValue">The default value.</param>
  352. /// <returns>A codec for the given tag.</returns>
  353. public static FieldCodec<T> ForEnum<T>(uint tag, Func<T, int> toInt32, Func<int, T> fromInt32, T defaultValue)
  354. {
  355. return new FieldCodec<T>(input => fromInt32(
  356. input.ReadEnum()),
  357. (output, value) => output.WriteEnum(toInt32(value)),
  358. value => CodedOutputStream.ComputeEnumSize(toInt32(value)), tag, defaultValue);
  359. }
  360. /// <summary>
  361. /// Retrieves a codec suitable for a message field with the given tag.
  362. /// </summary>
  363. /// <param name="tag">The tag.</param>
  364. /// <param name="parser">A parser to use for the message type.</param>
  365. /// <returns>A codec for the given tag.</returns>
  366. public static FieldCodec<T> ForMessage<T>(uint tag, MessageParser<T> parser) where T : class, IMessage<T>
  367. {
  368. return new FieldCodec<T>(
  369. input =>
  370. {
  371. T message = parser.CreateTemplate();
  372. input.ReadMessage(message);
  373. return message;
  374. },
  375. (output, value) => output.WriteMessage(value),
  376. (CodedInputStream i, ref T v) =>
  377. {
  378. if (v == null)
  379. {
  380. v = parser.CreateTemplate();
  381. }
  382. i.ReadMessage(v);
  383. },
  384. (ref T v, T v2) =>
  385. {
  386. if (v2 == null)
  387. {
  388. return false;
  389. }
  390. else if (v == null)
  391. {
  392. v = v2.Clone();
  393. }
  394. else
  395. {
  396. v.MergeFrom(v2);
  397. }
  398. return true;
  399. },
  400. message => CodedOutputStream.ComputeMessageSize(message), tag);
  401. }
  402. /// <summary>
  403. /// Retrieves a codec suitable for a group field with the given tag.
  404. /// </summary>
  405. /// <param name="startTag">The start group tag.</param>
  406. /// <param name="endTag">The end group tag.</param>
  407. /// <param name="parser">A parser to use for the group message type.</param>
  408. /// <returns>A codec for given tag</returns>
  409. public static FieldCodec<T> ForGroup<T>(uint startTag, uint endTag, MessageParser<T> parser) where T : class, IMessage<T>
  410. {
  411. return new FieldCodec<T>(
  412. input =>
  413. {
  414. T message = parser.CreateTemplate();
  415. input.ReadGroup(message);
  416. return message;
  417. },
  418. (output, value) => output.WriteGroup(value),
  419. (CodedInputStream i, ref T v) =>
  420. {
  421. if (v == null)
  422. {
  423. v = parser.CreateTemplate();
  424. }
  425. i.ReadGroup(v);
  426. },
  427. (ref T v, T v2) =>
  428. {
  429. if (v2 == null)
  430. {
  431. return v == null;
  432. }
  433. else if (v == null)
  434. {
  435. v = v2.Clone();
  436. }
  437. else
  438. {
  439. v.MergeFrom(v2);
  440. }
  441. return true;
  442. },
  443. message => CodedOutputStream.ComputeGroupSize(message), startTag, endTag);
  444. }
  445. /// <summary>
  446. /// Creates a codec for a wrapper type of a class - which must be string or ByteString.
  447. /// </summary>
  448. public static FieldCodec<T> ForClassWrapper<T>(uint tag) where T : class
  449. {
  450. var nestedCodec = WrapperCodecs.GetCodec<T>();
  451. return new FieldCodec<T>(
  452. input => WrapperCodecs.Read<T>(input, nestedCodec),
  453. (output, value) => WrapperCodecs.Write<T>(output, value, nestedCodec),
  454. (CodedInputStream i, ref T v) => v = WrapperCodecs.Read<T>(i, nestedCodec),
  455. (ref T v, T v2) => { v = v2; return v == null; },
  456. value => WrapperCodecs.CalculateSize<T>(value, nestedCodec),
  457. tag, 0,
  458. null); // Default value for the wrapper
  459. }
  460. /// <summary>
  461. /// Creates a codec for a wrapper type of a struct - which must be Int32, Int64, UInt32, UInt64,
  462. /// Bool, Single or Double.
  463. /// </summary>
  464. public static FieldCodec<T?> ForStructWrapper<T>(uint tag) where T : struct
  465. {
  466. var nestedCodec = WrapperCodecs.GetCodec<T>();
  467. return new FieldCodec<T?>(
  468. input => WrapperCodecs.Read<T>(input, nestedCodec),
  469. (output, value) => WrapperCodecs.Write<T>(output, value.Value, nestedCodec),
  470. (CodedInputStream i, ref T? v) => v = WrapperCodecs.Read<T>(i, nestedCodec),
  471. (ref T? v, T? v2) => { if (v2.HasValue) { v = v2; } return v.HasValue; },
  472. value => value == null ? 0 : WrapperCodecs.CalculateSize<T>(value.Value, nestedCodec),
  473. tag, 0,
  474. null); // Default value for the wrapper
  475. }
  476. /// <summary>
  477. /// Helper code to create codecs for wrapper types.
  478. /// </summary>
  479. /// <remarks>
  480. /// Somewhat ugly with all the static methods, but the conversions involved to/from nullable types make it
  481. /// slightly tricky to improve. So long as we keep the public API (ForClassWrapper, ForStructWrapper) in place,
  482. /// we can refactor later if we come up with something cleaner.
  483. /// </remarks>
  484. private static class WrapperCodecs
  485. {
  486. private static readonly Dictionary<System.Type, object> Codecs = new Dictionary<System.Type, object>
  487. {
  488. { typeof(bool), ForBool(WireFormat.MakeTag(WrappersReflection.WrapperValueFieldNumber, WireFormat.WireType.Varint)) },
  489. { typeof(int), ForInt32(WireFormat.MakeTag(WrappersReflection.WrapperValueFieldNumber, WireFormat.WireType.Varint)) },
  490. { typeof(long), ForInt64(WireFormat.MakeTag(WrappersReflection.WrapperValueFieldNumber, WireFormat.WireType.Varint)) },
  491. { typeof(uint), ForUInt32(WireFormat.MakeTag(WrappersReflection.WrapperValueFieldNumber, WireFormat.WireType.Varint)) },
  492. { typeof(ulong), ForUInt64(WireFormat.MakeTag(WrappersReflection.WrapperValueFieldNumber, WireFormat.WireType.Varint)) },
  493. { typeof(float), ForFloat(WireFormat.MakeTag(WrappersReflection.WrapperValueFieldNumber, WireFormat.WireType.Fixed32)) },
  494. { typeof(double), ForDouble(WireFormat.MakeTag(WrappersReflection.WrapperValueFieldNumber, WireFormat.WireType.Fixed64)) },
  495. { typeof(string), ForString(WireFormat.MakeTag(WrappersReflection.WrapperValueFieldNumber, WireFormat.WireType.LengthDelimited)) },
  496. { typeof(ByteString), ForBytes(WireFormat.MakeTag(WrappersReflection.WrapperValueFieldNumber, WireFormat.WireType.LengthDelimited)) }
  497. };
  498. /// <summary>
  499. /// Returns a field codec which effectively wraps a value of type T in a message.
  500. ///
  501. /// </summary>
  502. internal static FieldCodec<T> GetCodec<T>()
  503. {
  504. object value;
  505. if (!Codecs.TryGetValue(typeof(T), out value))
  506. {
  507. throw new InvalidOperationException("Invalid type argument requested for wrapper codec: " + typeof(T));
  508. }
  509. return (FieldCodec<T>) value;
  510. }
  511. internal static T Read<T>(CodedInputStream input, FieldCodec<T> codec)
  512. {
  513. int length = input.ReadLength();
  514. int oldLimit = input.PushLimit(length);
  515. uint tag;
  516. T value = codec.DefaultValue;
  517. while ((tag = input.ReadTag()) != 0)
  518. {
  519. if (tag == codec.Tag)
  520. {
  521. value = codec.Read(input);
  522. }
  523. else
  524. {
  525. input.SkipLastField();
  526. }
  527. }
  528. input.CheckReadEndOfStreamTag();
  529. input.PopLimit(oldLimit);
  530. return value;
  531. }
  532. internal static void Write<T>(CodedOutputStream output, T value, FieldCodec<T> codec)
  533. {
  534. output.WriteLength(codec.CalculateSizeWithTag(value));
  535. codec.WriteTagAndValue(output, value);
  536. }
  537. internal static int CalculateSize<T>(T value, FieldCodec<T> codec)
  538. {
  539. int fieldLength = codec.CalculateSizeWithTag(value);
  540. return CodedOutputStream.ComputeLengthSize(fieldLength) + fieldLength;
  541. }
  542. }
  543. }
  544. /// <summary>
  545. /// <para>
  546. /// An encode/decode pair for a single field. This effectively encapsulates
  547. /// all the information needed to read or write the field value from/to a coded
  548. /// stream.
  549. /// </para>
  550. /// <para>
  551. /// This class is public and has to be as it is used by generated code, but its public
  552. /// API is very limited - just what the generated code needs to call directly.
  553. /// </para>
  554. /// </summary>
  555. /// <remarks>
  556. /// This never writes default values to the stream, and does not address "packedness"
  557. /// in repeated fields itself, other than to know whether or not the field *should* be packed.
  558. /// </remarks>
  559. public sealed class FieldCodec<T>
  560. {
  561. private static readonly EqualityComparer<T> EqualityComparer = ProtobufEqualityComparers.GetEqualityComparer<T>();
  562. private static readonly T DefaultDefault;
  563. // Only non-nullable value types support packing. This is the simplest way of detecting that.
  564. private static readonly bool TypeSupportsPacking = default(T) != null;
  565. /// <summary>
  566. /// Merges an input stream into a value
  567. /// </summary>
  568. internal delegate void InputMerger(CodedInputStream input, ref T value);
  569. /// <summary>
  570. /// Merges a value into a reference to another value, returning a boolean if the value was set
  571. /// </summary>
  572. internal delegate bool ValuesMerger(ref T value, T other);
  573. static FieldCodec()
  574. {
  575. if (typeof(T) == typeof(string))
  576. {
  577. DefaultDefault = (T)(object)"";
  578. }
  579. else if (typeof(T) == typeof(ByteString))
  580. {
  581. DefaultDefault = (T)(object)ByteString.Empty;
  582. }
  583. // Otherwise it's the default value of the CLR type
  584. }
  585. internal static bool IsPackedRepeatedField(uint tag) =>
  586. TypeSupportsPacking && WireFormat.GetTagWireType(tag) == WireFormat.WireType.LengthDelimited;
  587. internal bool PackedRepeatedField { get; }
  588. /// <summary>
  589. /// Returns a delegate to write a value (unconditionally) to a coded output stream.
  590. /// </summary>
  591. internal Action<CodedOutputStream, T> ValueWriter { get; }
  592. /// <summary>
  593. /// Returns the size calculator for just a value.
  594. /// </summary>
  595. internal Func<T, int> ValueSizeCalculator { get; }
  596. /// <summary>
  597. /// Returns a delegate to read a value from a coded input stream. It is assumed that
  598. /// the stream is already positioned on the appropriate tag.
  599. /// </summary>
  600. internal Func<CodedInputStream, T> ValueReader { get; }
  601. /// <summary>
  602. /// Returns a delegate to merge a value from a coded input stream.
  603. /// It is assumed that the stream is already positioned on the appropriate tag
  604. /// </summary>
  605. internal InputMerger ValueMerger { get; }
  606. /// <summary>
  607. /// Returns a delegate to merge two values together.
  608. /// </summary>
  609. internal ValuesMerger FieldMerger { get; }
  610. /// <summary>
  611. /// Returns the fixed size for an entry, or 0 if sizes vary.
  612. /// </summary>
  613. internal int FixedSize { get; }
  614. /// <summary>
  615. /// Gets the tag of the codec.
  616. /// </summary>
  617. /// <value>
  618. /// The tag of the codec.
  619. /// </value>
  620. internal uint Tag { get; }
  621. /// <summary>
  622. /// Gets the end tag of the codec or 0 if there is no end tag
  623. /// </summary>
  624. /// <value>
  625. /// The end tag of the codec.
  626. /// </value>
  627. internal uint EndTag { get; }
  628. /// <summary>
  629. /// Default value for this codec. Usually the same for every instance of the same type, but
  630. /// for string/ByteString wrapper fields the codec's default value is null, whereas for
  631. /// other string/ByteString fields it's "" or ByteString.Empty.
  632. /// </summary>
  633. /// <value>
  634. /// The default value of the codec's type.
  635. /// </value>
  636. internal T DefaultValue { get; }
  637. private readonly int tagSize;
  638. internal FieldCodec(
  639. Func<CodedInputStream, T> reader,
  640. Action<CodedOutputStream, T> writer,
  641. int fixedSize,
  642. uint tag,
  643. T defaultValue) : this(reader, writer, _ => fixedSize, tag, defaultValue)
  644. {
  645. FixedSize = fixedSize;
  646. }
  647. internal FieldCodec(
  648. Func<CodedInputStream, T> reader,
  649. Action<CodedOutputStream, T> writer,
  650. Func<T, int> sizeCalculator,
  651. uint tag,
  652. T defaultValue) : this(reader, writer, (CodedInputStream i, ref T v) => v = reader(i), (ref T v, T v2) => { v = v2; return true; }, sizeCalculator, tag, 0, defaultValue)
  653. {
  654. }
  655. internal FieldCodec(
  656. Func<CodedInputStream, T> reader,
  657. Action<CodedOutputStream, T> writer,
  658. InputMerger inputMerger,
  659. ValuesMerger valuesMerger,
  660. Func<T, int> sizeCalculator,
  661. uint tag,
  662. uint endTag = 0) : this(reader, writer, inputMerger, valuesMerger, sizeCalculator, tag, endTag, DefaultDefault)
  663. {
  664. }
  665. internal FieldCodec(
  666. Func<CodedInputStream, T> reader,
  667. Action<CodedOutputStream, T> writer,
  668. InputMerger inputMerger,
  669. ValuesMerger valuesMerger,
  670. Func<T, int> sizeCalculator,
  671. uint tag,
  672. uint endTag,
  673. T defaultValue)
  674. {
  675. ValueReader = reader;
  676. ValueWriter = writer;
  677. ValueMerger = inputMerger;
  678. FieldMerger = valuesMerger;
  679. ValueSizeCalculator = sizeCalculator;
  680. FixedSize = 0;
  681. Tag = tag;
  682. EndTag = endTag;
  683. DefaultValue = defaultValue;
  684. tagSize = CodedOutputStream.ComputeRawVarint32Size(tag);
  685. if (endTag != 0)
  686. tagSize += CodedOutputStream.ComputeRawVarint32Size(endTag);
  687. // Detect packed-ness once, so we can check for it within RepeatedField<T>.
  688. PackedRepeatedField = IsPackedRepeatedField(tag);
  689. }
  690. /// <summary>
  691. /// Write a tag and the given value, *if* the value is not the default.
  692. /// </summary>
  693. public void WriteTagAndValue(CodedOutputStream output, T value)
  694. {
  695. if (!IsDefault(value))
  696. {
  697. output.WriteTag(Tag);
  698. ValueWriter(output, value);
  699. if (EndTag != 0)
  700. {
  701. output.WriteTag(EndTag);
  702. }
  703. }
  704. }
  705. /// <summary>
  706. /// Reads a value of the codec type from the given <see cref="CodedInputStream"/>.
  707. /// </summary>
  708. /// <param name="input">The input stream to read from.</param>
  709. /// <returns>The value read from the stream.</returns>
  710. public T Read(CodedInputStream input) => ValueReader(input);
  711. /// <summary>
  712. /// Calculates the size required to write the given value, with a tag,
  713. /// if the value is not the default.
  714. /// </summary>
  715. public int CalculateSizeWithTag(T value) => IsDefault(value) ? 0 : ValueSizeCalculator(value) + tagSize;
  716. private bool IsDefault(T value) => EqualityComparer.Equals(value, DefaultValue);
  717. }
  718. }