FieldCodec.cs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. using System;
  2. using System.Collections.Generic;
  3. namespace Google.Protobuf
  4. {
  5. /// <summary>
  6. /// Factory methods for <see cref="FieldCodec{T}"/>.
  7. /// </summary>
  8. public static class FieldCodec
  9. {
  10. public static FieldCodec<string> ForString(uint tag)
  11. {
  12. return new FieldCodec<string>(input => input.ReadString(), (output, value) => output.WriteString(value), CodedOutputStream.ComputeStringSize, tag);
  13. }
  14. public static FieldCodec<ByteString> ForBytes(uint tag)
  15. {
  16. return new FieldCodec<ByteString>(input => input.ReadBytes(), (output, value) => output.WriteBytes(value), CodedOutputStream.ComputeBytesSize, tag);
  17. }
  18. public static FieldCodec<bool> ForBool(uint tag)
  19. {
  20. return new FieldCodec<bool>(input => input.ReadBool(), (output, value) => output.WriteBool(value), CodedOutputStream.ComputeBoolSize, tag);
  21. }
  22. public static FieldCodec<int> ForInt32(uint tag)
  23. {
  24. return new FieldCodec<int>(input => input.ReadInt32(), (output, value) => output.WriteInt32(value), CodedOutputStream.ComputeInt32Size, tag);
  25. }
  26. public static FieldCodec<int> ForSInt32(uint tag)
  27. {
  28. return new FieldCodec<int>(input => input.ReadSInt32(), (output, value) => output.WriteSInt32(value), CodedOutputStream.ComputeSInt32Size, tag);
  29. }
  30. public static FieldCodec<uint> ForFixed32(uint tag)
  31. {
  32. return new FieldCodec<uint>(input => input.ReadFixed32(), (output, value) => output.WriteFixed32(value), CodedOutputStream.ComputeFixed32Size, tag);
  33. }
  34. public static FieldCodec<int> ForSFixed32(uint tag)
  35. {
  36. return new FieldCodec<int>(input => input.ReadSFixed32(), (output, value) => output.WriteSFixed32(value), CodedOutputStream.ComputeSFixed32Size, tag);
  37. }
  38. public static FieldCodec<uint> ForUInt32(uint tag)
  39. {
  40. return new FieldCodec<uint>(input => input.ReadUInt32(), (output, value) => output.WriteUInt32(value), CodedOutputStream.ComputeUInt32Size, tag);
  41. }
  42. public static FieldCodec<long> ForInt64(uint tag)
  43. {
  44. return new FieldCodec<long>(input => input.ReadInt64(), (output, value) => output.WriteInt64(value), CodedOutputStream.ComputeInt64Size, tag);
  45. }
  46. public static FieldCodec<long> ForSInt64(uint tag)
  47. {
  48. return new FieldCodec<long>(input => input.ReadSInt64(), (output, value) => output.WriteSInt64(value), CodedOutputStream.ComputeSInt64Size, tag);
  49. }
  50. public static FieldCodec<ulong> ForFixed64(uint tag)
  51. {
  52. return new FieldCodec<ulong>(input => input.ReadFixed64(), (output, value) => output.WriteFixed64(value), CodedOutputStream.ComputeFixed64Size, tag);
  53. }
  54. public static FieldCodec<long> ForSFixed64(uint tag)
  55. {
  56. return new FieldCodec<long>(input => input.ReadSFixed64(), (output, value) => output.WriteSFixed64(value), CodedOutputStream.ComputeSFixed64Size, tag);
  57. }
  58. public static FieldCodec<ulong> ForUInt64(uint tag)
  59. {
  60. return new FieldCodec<ulong>(input => input.ReadUInt64(), (output, value) => output.WriteUInt64(value), CodedOutputStream.ComputeUInt64Size, tag);
  61. }
  62. public static FieldCodec<float> ForFloat(uint tag)
  63. {
  64. return new FieldCodec<float>(input => input.ReadFloat(), (output, value) => output.WriteFloat(value), CodedOutputStream.ComputeFloatSize, tag);
  65. }
  66. public static FieldCodec<double> ForDouble(uint tag)
  67. {
  68. return new FieldCodec<double>(input => input.ReadFloat(), (output, value) => output.WriteDouble(value), CodedOutputStream.ComputeDoubleSize, tag);
  69. }
  70. // Enums are tricky. We can probably use expression trees to build these delegates automatically,
  71. // but it's easy to generate the code fdor it.
  72. public static FieldCodec<T> ForEnum<T>(uint tag, Func<T, int> toInt32, Func<int, T> fromInt32)
  73. {
  74. return new FieldCodec<T>(input => fromInt32(
  75. input.ReadEnum()),
  76. (output, value) => output.WriteEnum(toInt32(value)),
  77. value => CodedOutputStream.ComputeEnumSize(toInt32(value)), tag);
  78. }
  79. public static FieldCodec<T> ForMessage<T>(uint tag, MessageParser<T> parser) where T : IMessage<T>
  80. {
  81. return new FieldCodec<T>(input => { T message = parser.CreateTemplate(); input.ReadMessage(message); return message; },
  82. (output, value) => output.WriteMessage(value), message => CodedOutputStream.ComputeMessageSize(message), tag);
  83. }
  84. }
  85. /// <summary>
  86. /// An encode/decode pair for a single field. This effectively encapsulates
  87. /// all the information needed to read or write the field value from/to a coded
  88. /// stream.
  89. /// </summary>
  90. /// <remarks>
  91. /// This never writes default values to the stream, and is not currently designed
  92. /// to play well with packed arrays.
  93. /// </remarks>
  94. public sealed class FieldCodec<T>
  95. {
  96. private static readonly Func<T, bool> IsDefault;
  97. private static readonly T Default;
  98. static FieldCodec()
  99. {
  100. if (typeof(T) == typeof(string))
  101. {
  102. Default = (T)(object)"";
  103. IsDefault = CreateDefaultValueCheck<string>(x => x.Length == 0);
  104. }
  105. else if (typeof(T) == typeof(ByteString))
  106. {
  107. Default = (T)(object)ByteString.Empty;
  108. IsDefault = CreateDefaultValueCheck<ByteString>(x => x.Length == 0);
  109. }
  110. else if (!typeof(T).IsValueType)
  111. {
  112. // Default default
  113. IsDefault = CreateDefaultValueCheck<T>(x => x == null);
  114. }
  115. else
  116. {
  117. // Default default
  118. IsDefault = CreateDefaultValueCheck<T>(x => EqualityComparer<T>.Default.Equals(x, default(T)));
  119. }
  120. }
  121. private static Func<T, bool> CreateDefaultValueCheck<TTmp>(Func<TTmp, bool> check)
  122. {
  123. return (Func<T, bool>)(object)check;
  124. }
  125. private readonly Func<CodedInputStream, T> reader;
  126. private readonly Action<CodedOutputStream, T> writer;
  127. private readonly Func<T, int> sizeComputer;
  128. private readonly uint tag;
  129. private readonly int tagSize;
  130. internal FieldCodec(
  131. Func<CodedInputStream, T> reader,
  132. Action<CodedOutputStream, T> writer,
  133. Func<T, int> sizeComputer,
  134. uint tag)
  135. {
  136. this.reader = reader;
  137. this.writer = writer;
  138. this.sizeComputer = sizeComputer;
  139. this.tag = tag;
  140. tagSize = CodedOutputStream.ComputeRawVarint32Size(tag);
  141. }
  142. public uint Tag { get { return tag; } }
  143. public T DefaultValue { get { return Default; } }
  144. public void Write(CodedOutputStream output, T value)
  145. {
  146. if (!IsDefault(value))
  147. {
  148. output.WriteTag(tag);
  149. writer(output, value);
  150. }
  151. }
  152. public T Read(CodedInputStream input)
  153. {
  154. return reader(input);
  155. }
  156. public int CalculateSize(T value)
  157. {
  158. return IsDefault(value) ? 0 : sizeComputer(value) + CodedOutputStream.ComputeRawVarint32Size(tag);
  159. }
  160. }
  161. }