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