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