FieldCodec.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773
  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);
  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);
  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);
  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);
  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);
  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);
  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);
  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);
  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);
  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);
  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);
  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);
  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);
  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);
  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);
  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);
  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>(input => { T message = parser.CreateTemplate(); input.ReadMessage(message); return message; },
  369. (output, value) => output.WriteMessage(value), (CodedInputStream i, ref T v) =>
  370. {
  371. if (v == null)
  372. {
  373. v = parser.CreateTemplate();
  374. }
  375. i.ReadMessage(v);
  376. },
  377. (ref T v, T v2) =>
  378. {
  379. if (v2 == null)
  380. {
  381. return false;
  382. }
  383. else if (v == null)
  384. {
  385. v = v2.Clone();
  386. }
  387. else
  388. {
  389. v.MergeFrom(v2);
  390. }
  391. return true;
  392. }, message => CodedOutputStream.ComputeMessageSize(message), tag);
  393. }
  394. /// <summary>
  395. /// Retrieves a codec suitable for a group field with the given tag.
  396. /// </summary>
  397. /// <param name="startTag">The start group tag.</param>
  398. /// <param name="endTag">The end group tag.</param>
  399. /// <param name="parser">A parser to use for the group message type.</param>
  400. /// <returns>A codec for given tag</returns>
  401. public static FieldCodec<T> ForGroup<T>(uint startTag, uint endTag, MessageParser<T> parser) where T : class, IMessage<T>
  402. {
  403. return new FieldCodec<T>(input => { T message = parser.CreateTemplate(); input.ReadGroup(message); return message; },
  404. (output, value) => output.WriteGroup(value), (CodedInputStream i, ref T v) => {
  405. if (v == null)
  406. {
  407. v = parser.CreateTemplate();
  408. }
  409. i.ReadGroup(v);
  410. },
  411. (ref T v, T v2) =>
  412. {
  413. if (v2 == null)
  414. {
  415. return v == null;
  416. }
  417. else if (v == null)
  418. {
  419. v = v2.Clone();
  420. }
  421. else
  422. {
  423. v.MergeFrom(v2);
  424. }
  425. return true;
  426. }, message => CodedOutputStream.ComputeGroupSize(message), startTag, endTag);
  427. }
  428. /// <summary>
  429. /// Creates a codec for a wrapper type of a class - which must be string or ByteString.
  430. /// </summary>
  431. public static FieldCodec<T> ForClassWrapper<T>(uint tag) where T : class
  432. {
  433. var nestedCodec = WrapperCodecs.GetCodec<T>();
  434. return new FieldCodec<T>(
  435. input => WrapperCodecs.Read<T>(input, nestedCodec),
  436. (output, value) => WrapperCodecs.Write<T>(output, value, nestedCodec),
  437. (CodedInputStream i, ref T v) => v = WrapperCodecs.Read<T>(i, nestedCodec),
  438. (ref T v, T v2) => { v = v2; return v == null; },
  439. value => WrapperCodecs.CalculateSize<T>(value, nestedCodec),
  440. tag, 0,
  441. null); // Default value for the wrapper
  442. }
  443. /// <summary>
  444. /// Creates a codec for a wrapper type of a struct - which must be Int32, Int64, UInt32, UInt64,
  445. /// Bool, Single or Double.
  446. /// </summary>
  447. public static FieldCodec<T?> ForStructWrapper<T>(uint tag) where T : struct
  448. {
  449. var nestedCodec = WrapperCodecs.GetCodec<T>();
  450. return new FieldCodec<T?>(
  451. input => WrapperCodecs.Read<T>(input, nestedCodec),
  452. (output, value) => WrapperCodecs.Write<T>(output, value.Value, nestedCodec),
  453. (CodedInputStream i, ref T? v) => v = WrapperCodecs.Read<T>(i, nestedCodec),
  454. (ref T? v, T? v2) => { if (v2.HasValue) { v = v2; } return v.HasValue; },
  455. value => value == null ? 0 : WrapperCodecs.CalculateSize<T>(value.Value, nestedCodec),
  456. tag, 0,
  457. null); // Default value for the wrapper
  458. }
  459. /// <summary>
  460. /// Helper code to create codecs for wrapper types.
  461. /// </summary>
  462. /// <remarks>
  463. /// Somewhat ugly with all the static methods, but the conversions involved to/from nullable types make it
  464. /// slightly tricky to improve. So long as we keep the public API (ForClassWrapper, ForStructWrapper) in place,
  465. /// we can refactor later if we come up with something cleaner.
  466. /// </remarks>
  467. private static class WrapperCodecs
  468. {
  469. private static readonly Dictionary<System.Type, object> Codecs = new Dictionary<System.Type, object>
  470. {
  471. { typeof(bool), ForBool(WireFormat.MakeTag(WrappersReflection.WrapperValueFieldNumber, WireFormat.WireType.Varint)) },
  472. { typeof(int), ForInt32(WireFormat.MakeTag(WrappersReflection.WrapperValueFieldNumber, WireFormat.WireType.Varint)) },
  473. { typeof(long), ForInt64(WireFormat.MakeTag(WrappersReflection.WrapperValueFieldNumber, WireFormat.WireType.Varint)) },
  474. { typeof(uint), ForUInt32(WireFormat.MakeTag(WrappersReflection.WrapperValueFieldNumber, WireFormat.WireType.Varint)) },
  475. { typeof(ulong), ForUInt64(WireFormat.MakeTag(WrappersReflection.WrapperValueFieldNumber, WireFormat.WireType.Varint)) },
  476. { typeof(float), ForFloat(WireFormat.MakeTag(WrappersReflection.WrapperValueFieldNumber, WireFormat.WireType.Fixed32)) },
  477. { typeof(double), ForDouble(WireFormat.MakeTag(WrappersReflection.WrapperValueFieldNumber, WireFormat.WireType.Fixed64)) },
  478. { typeof(string), ForString(WireFormat.MakeTag(WrappersReflection.WrapperValueFieldNumber, WireFormat.WireType.LengthDelimited)) },
  479. { typeof(ByteString), ForBytes(WireFormat.MakeTag(WrappersReflection.WrapperValueFieldNumber, WireFormat.WireType.LengthDelimited)) }
  480. };
  481. /// <summary>
  482. /// Returns a field codec which effectively wraps a value of type T in a message.
  483. ///
  484. /// </summary>
  485. internal static FieldCodec<T> GetCodec<T>()
  486. {
  487. object value;
  488. if (!Codecs.TryGetValue(typeof(T), out value))
  489. {
  490. throw new InvalidOperationException("Invalid type argument requested for wrapper codec: " + typeof(T));
  491. }
  492. return (FieldCodec<T>) value;
  493. }
  494. internal static T Read<T>(CodedInputStream input, FieldCodec<T> codec)
  495. {
  496. int length = input.ReadLength();
  497. int oldLimit = input.PushLimit(length);
  498. uint tag;
  499. T value = codec.DefaultValue;
  500. while ((tag = input.ReadTag()) != 0)
  501. {
  502. if (tag == codec.Tag)
  503. {
  504. value = codec.Read(input);
  505. }
  506. else
  507. {
  508. input.SkipLastField();
  509. }
  510. }
  511. input.CheckReadEndOfStreamTag();
  512. input.PopLimit(oldLimit);
  513. return value;
  514. }
  515. internal static void Write<T>(CodedOutputStream output, T value, FieldCodec<T> codec)
  516. {
  517. output.WriteLength(codec.CalculateSizeWithTag(value));
  518. codec.WriteTagAndValue(output, value);
  519. }
  520. internal static int CalculateSize<T>(T value, FieldCodec<T> codec)
  521. {
  522. int fieldLength = codec.CalculateSizeWithTag(value);
  523. return CodedOutputStream.ComputeLengthSize(fieldLength) + fieldLength;
  524. }
  525. }
  526. }
  527. /// <summary>
  528. /// <para>
  529. /// An encode/decode pair for a single field. This effectively encapsulates
  530. /// all the information needed to read or write the field value from/to a coded
  531. /// stream.
  532. /// </para>
  533. /// <para>
  534. /// This class is public and has to be as it is used by generated code, but its public
  535. /// API is very limited - just what the generated code needs to call directly.
  536. /// </para>
  537. /// </summary>
  538. /// <remarks>
  539. /// This never writes default values to the stream, and does not address "packedness"
  540. /// in repeated fields itself, other than to know whether or not the field *should* be packed.
  541. /// </remarks>
  542. public sealed class FieldCodec<T>
  543. {
  544. private static readonly EqualityComparer<T> EqualityComparer = ProtobufEqualityComparers.GetEqualityComparer<T>();
  545. private static readonly T DefaultDefault;
  546. // Only non-nullable value types support packing. This is the simplest way of detecting that.
  547. private static readonly bool TypeSupportsPacking = default(T) != null;
  548. /// <summary>
  549. /// Merges an input stream into a value
  550. /// </summary>
  551. internal delegate void InputMerger(CodedInputStream input, ref T value);
  552. /// <summary>
  553. /// Merges a value into a reference to another value, returning a boolean if the value was set
  554. /// </summary>
  555. internal delegate bool ValuesMerger(ref T value, T other);
  556. static FieldCodec()
  557. {
  558. if (typeof(T) == typeof(string))
  559. {
  560. DefaultDefault = (T)(object)"";
  561. }
  562. else if (typeof(T) == typeof(ByteString))
  563. {
  564. DefaultDefault = (T)(object)ByteString.Empty;
  565. }
  566. // Otherwise it's the default value of the CLR type
  567. }
  568. internal static bool IsPackedRepeatedField(uint tag) =>
  569. TypeSupportsPacking && WireFormat.GetTagWireType(tag) == WireFormat.WireType.LengthDelimited;
  570. internal bool PackedRepeatedField { get; }
  571. /// <summary>
  572. /// Returns a delegate to write a value (unconditionally) to a coded output stream.
  573. /// </summary>
  574. internal Action<CodedOutputStream, T> ValueWriter { get; }
  575. /// <summary>
  576. /// Returns the size calculator for just a value.
  577. /// </summary>
  578. internal Func<T, int> ValueSizeCalculator { get; }
  579. /// <summary>
  580. /// Returns a delegate to read a value from a coded input stream. It is assumed that
  581. /// the stream is already positioned on the appropriate tag.
  582. /// </summary>
  583. internal Func<CodedInputStream, T> ValueReader { get; }
  584. /// <summary>
  585. /// Returns a delegate to merge a value from a coded input stream.
  586. /// It is assumed that the stream is already positioned on the appropriate tag
  587. /// </summary>
  588. internal InputMerger ValueMerger { get; }
  589. /// <summary>
  590. /// Returns a delegate to merge two values together.
  591. /// </summary>
  592. internal ValuesMerger FieldMerger { get; }
  593. /// <summary>
  594. /// Returns the fixed size for an entry, or 0 if sizes vary.
  595. /// </summary>
  596. internal int FixedSize { get; }
  597. /// <summary>
  598. /// Gets the tag of the codec.
  599. /// </summary>
  600. /// <value>
  601. /// The tag of the codec.
  602. /// </value>
  603. internal uint Tag { get; }
  604. /// <summary>
  605. /// Gets the end tag of the codec or 0 if there is no end tag
  606. /// </summary>
  607. /// <value>
  608. /// The end tag of the codec.
  609. /// </value>
  610. internal uint EndTag { get; }
  611. /// <summary>
  612. /// Default value for this codec. Usually the same for every instance of the same type, but
  613. /// for string/ByteString wrapper fields the codec's default value is null, whereas for
  614. /// other string/ByteString fields it's "" or ByteString.Empty.
  615. /// </summary>
  616. /// <value>
  617. /// The default value of the codec's type.
  618. /// </value>
  619. internal T DefaultValue { get; }
  620. private readonly int tagSize;
  621. internal FieldCodec(
  622. Func<CodedInputStream, T> reader,
  623. Action<CodedOutputStream, T> writer,
  624. int fixedSize,
  625. uint tag) : this(reader, writer, _ => fixedSize, tag)
  626. {
  627. FixedSize = fixedSize;
  628. }
  629. internal FieldCodec(
  630. Func<CodedInputStream, T> reader,
  631. Action<CodedOutputStream, T> writer,
  632. Func<T, int> sizeCalculator,
  633. uint tag,
  634. uint endTag = 0) : this(reader, writer, (CodedInputStream i, ref T v) => v = reader(i), (ref T v, T v2) => { v = v2; return true; }, sizeCalculator, tag, endTag, DefaultDefault)
  635. {
  636. }
  637. internal FieldCodec(
  638. Func<CodedInputStream, T> reader,
  639. Action<CodedOutputStream, T> writer,
  640. InputMerger inputMerger,
  641. ValuesMerger valuesMerger,
  642. Func<T, int> sizeCalculator,
  643. uint tag,
  644. uint endTag = 0) : this(reader, writer, inputMerger, valuesMerger, sizeCalculator, tag, endTag, DefaultDefault)
  645. {
  646. }
  647. internal FieldCodec(
  648. Func<CodedInputStream, T> reader,
  649. Action<CodedOutputStream, T> writer,
  650. InputMerger inputMerger,
  651. ValuesMerger valuesMerger,
  652. Func<T, int> sizeCalculator,
  653. uint tag,
  654. uint endTag,
  655. T defaultValue)
  656. {
  657. ValueReader = reader;
  658. ValueWriter = writer;
  659. ValueMerger = inputMerger;
  660. FieldMerger = valuesMerger;
  661. ValueSizeCalculator = sizeCalculator;
  662. FixedSize = 0;
  663. Tag = tag;
  664. DefaultValue = defaultValue;
  665. tagSize = CodedOutputStream.ComputeRawVarint32Size(tag);
  666. if (endTag != 0)
  667. tagSize += CodedOutputStream.ComputeRawVarint32Size(endTag);
  668. // Detect packed-ness once, so we can check for it within RepeatedField<T>.
  669. PackedRepeatedField = IsPackedRepeatedField(tag);
  670. }
  671. /// <summary>
  672. /// Write a tag and the given value, *if* the value is not the default.
  673. /// </summary>
  674. public void WriteTagAndValue(CodedOutputStream output, T value)
  675. {
  676. if (!IsDefault(value))
  677. {
  678. output.WriteTag(Tag);
  679. ValueWriter(output, value);
  680. if (EndTag != 0)
  681. {
  682. output.WriteTag(EndTag);
  683. }
  684. }
  685. }
  686. /// <summary>
  687. /// Reads a value of the codec type from the given <see cref="CodedInputStream"/>.
  688. /// </summary>
  689. /// <param name="input">The input stream to read from.</param>
  690. /// <returns>The value read from the stream.</returns>
  691. public T Read(CodedInputStream input) => ValueReader(input);
  692. /// <summary>
  693. /// Calculates the size required to write the given value, with a tag,
  694. /// if the value is not the default.
  695. /// </summary>
  696. public int CalculateSizeWithTag(T value) => IsDefault(value) ? 0 : ValueSizeCalculator(value) + tagSize;
  697. private bool IsDefault(T value) => EqualityComparer.Equals(value, DefaultValue);
  698. }
  699. }