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
33using Google.Protobuf.Collections;
34using Google.Protobuf.Compatibility;
35using Google.Protobuf.WellKnownTypes;
36using System;
37using System.Collections.Generic;
38using System.Security;
39
40namespace 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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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();
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();
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