blob: 54cbc209d51d37627456fcf41da6f59c9f68af7d [file] [log] [blame]
Jon Skeetee835a32015-06-30 17:22:26 +01001#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
Jon Skeet8a0312b2015-07-16 17:03:06 +010032
Jon Skeetfb248822015-09-04 12:41:14 +010033using Google.Protobuf.WellKnownTypes;
Jon Skeetee835a32015-06-30 17:22:26 +010034using System;
Jon Skeet0d684d32015-06-24 17:21:55 +010035using System.Collections.Generic;
36
37namespace Google.Protobuf
38{
39 /// <summary>
40 /// Factory methods for <see cref="FieldCodec{T}"/>.
41 /// </summary>
42 public static class FieldCodec
43 {
Jon Skeetf34d37a2015-06-30 13:16:20 +010044 // TODO: Avoid the "dual hit" of lambda expressions: create open delegates instead. (At least test...)
Jon Skeet811fc892015-08-04 15:58:39 +010045
46 /// <summary>
47 /// Retrieves a codec suitable for a string field with the given tag.
48 /// </summary>
49 /// <param name="tag">The tag.</param>
50 /// <returns>A codec for the given tag.</returns>
Jon Skeet0d684d32015-06-24 17:21:55 +010051 public static FieldCodec<string> ForString(uint tag)
52 {
Jon Skeet8a0312b2015-07-16 17:03:06 +010053 return new FieldCodec<string>(input => input.ReadString(), (output, value) => output.WriteString(value), CodedOutputStream.ComputeStringSize, tag);
Jon Skeet0d684d32015-06-24 17:21:55 +010054 }
55
Jon Skeet811fc892015-08-04 15:58:39 +010056 /// <summary>
57 /// Retrieves a codec suitable for a bytes field with the given tag.
58 /// </summary>
59 /// <param name="tag">The tag.</param>
60 /// <returns>A codec for the given tag.</returns>
Jon Skeet0d684d32015-06-24 17:21:55 +010061 public static FieldCodec<ByteString> ForBytes(uint tag)
62 {
63 return new FieldCodec<ByteString>(input => input.ReadBytes(), (output, value) => output.WriteBytes(value), CodedOutputStream.ComputeBytesSize, tag);
64 }
65
Jon Skeet811fc892015-08-04 15:58:39 +010066 /// <summary>
67 /// Retrieves a codec suitable for a bool field with the given tag.
68 /// </summary>
69 /// <param name="tag">The tag.</param>
70 /// <returns>A codec for the given tag.</returns>
Jon Skeet0d684d32015-06-24 17:21:55 +010071 public static FieldCodec<bool> ForBool(uint tag)
72 {
73 return new FieldCodec<bool>(input => input.ReadBool(), (output, value) => output.WriteBool(value), CodedOutputStream.ComputeBoolSize, tag);
74 }
75
Jon Skeet811fc892015-08-04 15:58:39 +010076 /// <summary>
77 /// Retrieves a codec suitable for an int32 field with the given tag.
78 /// </summary>
79 /// <param name="tag">The tag.</param>
80 /// <returns>A codec for the given tag.</returns>
Jon Skeet0d684d32015-06-24 17:21:55 +010081 public static FieldCodec<int> ForInt32(uint tag)
82 {
83 return new FieldCodec<int>(input => input.ReadInt32(), (output, value) => output.WriteInt32(value), CodedOutputStream.ComputeInt32Size, tag);
84 }
85
Jon Skeet811fc892015-08-04 15:58:39 +010086 /// <summary>
87 /// Retrieves a codec suitable for an sint32 field with the given tag.
88 /// </summary>
89 /// <param name="tag">The tag.</param>
90 /// <returns>A codec for the given tag.</returns>
Jon Skeet0d684d32015-06-24 17:21:55 +010091 public static FieldCodec<int> ForSInt32(uint tag)
92 {
93 return new FieldCodec<int>(input => input.ReadSInt32(), (output, value) => output.WriteSInt32(value), CodedOutputStream.ComputeSInt32Size, tag);
94 }
95
Jon Skeet811fc892015-08-04 15:58:39 +010096 /// <summary>
97 /// Retrieves a codec suitable for a fixed32 field with the given tag.
98 /// </summary>
99 /// <param name="tag">The tag.</param>
100 /// <returns>A codec for the given tag.</returns>
Jon Skeetc1283312015-06-26 10:32:23 +0100101 public static FieldCodec<uint> ForFixed32(uint tag)
Jon Skeet0d684d32015-06-24 17:21:55 +0100102 {
Jon Skeet14f22222015-07-03 11:41:37 +0100103 return new FieldCodec<uint>(input => input.ReadFixed32(), (output, value) => output.WriteFixed32(value), 4, tag);
Jon Skeet0d684d32015-06-24 17:21:55 +0100104 }
105
Jon Skeet811fc892015-08-04 15:58:39 +0100106 /// <summary>
107 /// Retrieves a codec suitable for an sfixed32 field with the given tag.
108 /// </summary>
109 /// <param name="tag">The tag.</param>
110 /// <returns>A codec for the given tag.</returns>
Jon Skeetc1283312015-06-26 10:32:23 +0100111 public static FieldCodec<int> ForSFixed32(uint tag)
112 {
Jon Skeet14f22222015-07-03 11:41:37 +0100113 return new FieldCodec<int>(input => input.ReadSFixed32(), (output, value) => output.WriteSFixed32(value), 4, tag);
Jon Skeetc1283312015-06-26 10:32:23 +0100114 }
115
Jon Skeet811fc892015-08-04 15:58:39 +0100116 /// <summary>
117 /// Retrieves a codec suitable for a uint32 field with the given tag.
118 /// </summary>
119 /// <param name="tag">The tag.</param>
120 /// <returns>A codec for the given tag.</returns>
Jon Skeet0d684d32015-06-24 17:21:55 +0100121 public static FieldCodec<uint> ForUInt32(uint tag)
122 {
123 return new FieldCodec<uint>(input => input.ReadUInt32(), (output, value) => output.WriteUInt32(value), CodedOutputStream.ComputeUInt32Size, tag);
124 }
125
Jon Skeet811fc892015-08-04 15:58:39 +0100126 /// <summary>
127 /// Retrieves a codec suitable for an int64 field with the given tag.
128 /// </summary>
129 /// <param name="tag">The tag.</param>
130 /// <returns>A codec for the given tag.</returns>
Jon Skeet0d684d32015-06-24 17:21:55 +0100131 public static FieldCodec<long> ForInt64(uint tag)
132 {
133 return new FieldCodec<long>(input => input.ReadInt64(), (output, value) => output.WriteInt64(value), CodedOutputStream.ComputeInt64Size, tag);
134 }
135
Jon Skeet811fc892015-08-04 15:58:39 +0100136 /// <summary>
137 /// Retrieves a codec suitable for an sint64 field with the given tag.
138 /// </summary>
139 /// <param name="tag">The tag.</param>
140 /// <returns>A codec for the given tag.</returns>
Jon Skeet0d684d32015-06-24 17:21:55 +0100141 public static FieldCodec<long> ForSInt64(uint tag)
142 {
143 return new FieldCodec<long>(input => input.ReadSInt64(), (output, value) => output.WriteSInt64(value), CodedOutputStream.ComputeSInt64Size, tag);
144 }
145
Jon Skeet811fc892015-08-04 15:58:39 +0100146 /// <summary>
147 /// Retrieves a codec suitable for a fixed64 field with the given tag.
148 /// </summary>
149 /// <param name="tag">The tag.</param>
150 /// <returns>A codec for the given tag.</returns>
Jon Skeetc1283312015-06-26 10:32:23 +0100151 public static FieldCodec<ulong> ForFixed64(uint tag)
Jon Skeet0d684d32015-06-24 17:21:55 +0100152 {
Jon Skeet14f22222015-07-03 11:41:37 +0100153 return new FieldCodec<ulong>(input => input.ReadFixed64(), (output, value) => output.WriteFixed64(value), 8, tag);
Jon Skeet0d684d32015-06-24 17:21:55 +0100154 }
155
Jon Skeet811fc892015-08-04 15:58:39 +0100156 /// <summary>
157 /// Retrieves a codec suitable for an sfixed64 field with the given tag.
158 /// </summary>
159 /// <param name="tag">The tag.</param>
160 /// <returns>A codec for the given tag.</returns>
Jon Skeetc1283312015-06-26 10:32:23 +0100161 public static FieldCodec<long> ForSFixed64(uint tag)
162 {
Jon Skeet14f22222015-07-03 11:41:37 +0100163 return new FieldCodec<long>(input => input.ReadSFixed64(), (output, value) => output.WriteSFixed64(value), 8, tag);
Jon Skeetc1283312015-06-26 10:32:23 +0100164 }
165
Jon Skeet811fc892015-08-04 15:58:39 +0100166 /// <summary>
167 /// Retrieves a codec suitable for a uint64 field with the given tag.
168 /// </summary>
169 /// <param name="tag">The tag.</param>
170 /// <returns>A codec for the given tag.</returns>
Jon Skeet0d684d32015-06-24 17:21:55 +0100171 public static FieldCodec<ulong> ForUInt64(uint tag)
172 {
173 return new FieldCodec<ulong>(input => input.ReadUInt64(), (output, value) => output.WriteUInt64(value), CodedOutputStream.ComputeUInt64Size, tag);
174 }
175
Jon Skeet811fc892015-08-04 15:58:39 +0100176 /// <summary>
177 /// Retrieves a codec suitable for a float field with the given tag.
178 /// </summary>
179 /// <param name="tag">The tag.</param>
180 /// <returns>A codec for the given tag.</returns>
Jon Skeet0d684d32015-06-24 17:21:55 +0100181 public static FieldCodec<float> ForFloat(uint tag)
182 {
183 return new FieldCodec<float>(input => input.ReadFloat(), (output, value) => output.WriteFloat(value), CodedOutputStream.ComputeFloatSize, tag);
184 }
185
Jon Skeet811fc892015-08-04 15:58:39 +0100186 /// <summary>
187 /// Retrieves a codec suitable for a double field with the given tag.
188 /// </summary>
189 /// <param name="tag">The tag.</param>
190 /// <returns>A codec for the given tag.</returns>
Jon Skeet0d684d32015-06-24 17:21:55 +0100191 public static FieldCodec<double> ForDouble(uint tag)
192 {
Jon Skeet286edc02015-06-26 20:11:34 +0100193 return new FieldCodec<double>(input => input.ReadDouble(), (output, value) => output.WriteDouble(value), CodedOutputStream.ComputeDoubleSize, tag);
Jon Skeet0d684d32015-06-24 17:21:55 +0100194 }
195
196 // Enums are tricky. We can probably use expression trees to build these delegates automatically,
Jon Skeetf34d37a2015-06-30 13:16:20 +0100197 // but it's easy to generate the code for it.
Jon Skeet811fc892015-08-04 15:58:39 +0100198
199 /// <summary>
200 /// Retrieves a codec suitable for an enum field with the given tag.
201 /// </summary>
202 /// <param name="tag">The tag.</param>
203 /// <param name="toInt32">A conversion function from <see cref="Int32"/> to the enum type.</param>
204 /// <param name="fromInt32">A conversion function from the enum type to <see cref="Int32"/>.</param>
205 /// <returns>A codec for the given tag.</returns>
Jon Skeet0d684d32015-06-24 17:21:55 +0100206 public static FieldCodec<T> ForEnum<T>(uint tag, Func<T, int> toInt32, Func<int, T> fromInt32)
207 {
208 return new FieldCodec<T>(input => fromInt32(
209 input.ReadEnum()),
210 (output, value) => output.WriteEnum(toInt32(value)),
211 value => CodedOutputStream.ComputeEnumSize(toInt32(value)), tag);
212 }
213
Jon Skeet811fc892015-08-04 15:58:39 +0100214 /// <summary>
215 /// Retrieves a codec suitable for a message field with the given tag.
216 /// </summary>
217 /// <param name="tag">The tag.</param>
218 /// <param name="parser">A parser to use for the message type.</param>
219 /// <returns>A codec for the given tag.</returns>
Jon Skeet0d684d32015-06-24 17:21:55 +0100220 public static FieldCodec<T> ForMessage<T>(uint tag, MessageParser<T> parser) where T : IMessage<T>
221 {
222 return new FieldCodec<T>(input => { T message = parser.CreateTemplate(); input.ReadMessage(message); return message; },
223 (output, value) => output.WriteMessage(value), message => CodedOutputStream.ComputeMessageSize(message), tag);
224 }
Jon Skeet8a0312b2015-07-16 17:03:06 +0100225
226 /// <summary>
227 /// Creates a codec for a wrapper type of a class - which must be string or ByteString.
228 /// </summary>
229 public static FieldCodec<T> ForClassWrapper<T>(uint tag) where T : class
230 {
231 var nestedCodec = WrapperCodecs.GetCodec<T>();
232 return new FieldCodec<T>(
233 input => WrapperCodecs.Read<T>(input, nestedCodec),
234 (output, value) => WrapperCodecs.Write<T>(output, value, nestedCodec),
235 value => WrapperCodecs.CalculateSize<T>(value, nestedCodec),
236 tag,
237 null); // Default value for the wrapper
238 }
239
240 /// <summary>
241 /// Creates a codec for a wrapper type of a struct - which must be Int32, Int64, UInt32, UInt64,
242 /// Bool, Single or Double.
243 /// </summary>
244 public static FieldCodec<T?> ForStructWrapper<T>(uint tag) where T : struct
245 {
246 var nestedCodec = WrapperCodecs.GetCodec<T>();
247 return new FieldCodec<T?>(
248 input => WrapperCodecs.Read<T>(input, nestedCodec),
249 (output, value) => WrapperCodecs.Write<T>(output, value.Value, nestedCodec),
250 value => value == null ? 0 : WrapperCodecs.CalculateSize<T>(value.Value, nestedCodec),
251 tag,
252 null); // Default value for the wrapper
253 }
254
Jon Skeet34878cb2015-07-17 06:41:46 +0100255 /// <summary>
256 /// Helper code to create codecs for wrapper types.
257 /// </summary>
258 /// <remarks>
259 /// Somewhat ugly with all the static methods, but the conversions involved to/from nullable types make it
260 /// slightly tricky to improve. So long as we keep the public API (ForClassWrapper, ForStructWrapper) in place,
261 /// we can refactor later if we come up with something cleaner.
262 /// </remarks>
Jon Skeet8a0312b2015-07-16 17:03:06 +0100263 private static class WrapperCodecs
264 {
Jon Skeetfb248822015-09-04 12:41:14 +0100265 private static readonly Dictionary<System.Type, object> Codecs = new Dictionary<System.Type, object>
Jon Skeet8a0312b2015-07-16 17:03:06 +0100266 {
Jon Skeetfb248822015-09-04 12:41:14 +0100267 { typeof(bool), ForBool(WireFormat.MakeTag(Wrappers.WrapperValueFieldNumber, WireFormat.WireType.Varint)) },
268 { typeof(int), ForInt32(WireFormat.MakeTag(Wrappers.WrapperValueFieldNumber, WireFormat.WireType.Varint)) },
269 { typeof(long), ForInt64(WireFormat.MakeTag(Wrappers.WrapperValueFieldNumber, WireFormat.WireType.Varint)) },
270 { typeof(uint), ForUInt32(WireFormat.MakeTag(Wrappers.WrapperValueFieldNumber, WireFormat.WireType.Varint)) },
271 { typeof(ulong), ForUInt64(WireFormat.MakeTag(Wrappers.WrapperValueFieldNumber, WireFormat.WireType.Varint)) },
272 { typeof(float), ForFloat(WireFormat.MakeTag(Wrappers.WrapperValueFieldNumber, WireFormat.WireType.Fixed32)) },
273 { typeof(double), ForDouble(WireFormat.MakeTag(Wrappers.WrapperValueFieldNumber, WireFormat.WireType.Fixed64)) },
274 { typeof(string), ForString(WireFormat.MakeTag(Wrappers.WrapperValueFieldNumber, WireFormat.WireType.LengthDelimited)) },
275 { typeof(ByteString), ForBytes(WireFormat.MakeTag(Wrappers.WrapperValueFieldNumber, WireFormat.WireType.LengthDelimited)) }
Jon Skeet8a0312b2015-07-16 17:03:06 +0100276 };
277
278 /// <summary>
279 /// Returns a field codec which effectively wraps a value of type T in a message.
280 ///
281 /// </summary>
282 internal static FieldCodec<T> GetCodec<T>()
283 {
284 object value;
285 if (!Codecs.TryGetValue(typeof(T), out value))
286 {
287 throw new InvalidOperationException("Invalid type argument requested for wrapper codec: " + typeof(T));
288 }
289 return (FieldCodec<T>) value;
290 }
291
292 internal static T Read<T>(CodedInputStream input, FieldCodec<T> codec)
293 {
294 int length = input.ReadLength();
295 int oldLimit = input.PushLimit(length);
296
297 uint tag;
298 T value = codec.DefaultValue;
Jon Skeetff334a62015-08-05 11:23:38 +0100299 while ((tag = input.ReadTag()) != 0)
Jon Skeet8a0312b2015-07-16 17:03:06 +0100300 {
Jon Skeet8a0312b2015-07-16 17:03:06 +0100301 if (tag == codec.Tag)
302 {
303 value = codec.Read(input);
304 }
Jon Skeete7f88ff2015-08-06 11:40:32 +0100305 else
Jon Skeet8a0312b2015-07-16 17:03:06 +0100306 {
Jon Skeete7f88ff2015-08-06 11:40:32 +0100307 input.SkipLastField();
Jon Skeet8a0312b2015-07-16 17:03:06 +0100308 }
Jon Skeete7f88ff2015-08-06 11:40:32 +0100309
Jon Skeet8a0312b2015-07-16 17:03:06 +0100310 }
Jon Skeete7f88ff2015-08-06 11:40:32 +0100311 input.CheckReadEndOfStreamTag();
Jon Skeet8a0312b2015-07-16 17:03:06 +0100312 input.PopLimit(oldLimit);
313
314 return value;
315 }
316
317 internal static void Write<T>(CodedOutputStream output, T value, FieldCodec<T> codec)
318 {
319 output.WriteLength(codec.CalculateSizeWithTag(value));
320 codec.WriteTagAndValue(output, value);
321 }
322
323 internal static int CalculateSize<T>(T value, FieldCodec<T> codec)
324 {
325 int fieldLength = codec.CalculateSizeWithTag(value);
326 return CodedOutputStream.ComputeLengthSize(fieldLength) + fieldLength;
327 }
328 }
Jon Skeet0d684d32015-06-24 17:21:55 +0100329 }
330
331 /// <summary>
332 /// An encode/decode pair for a single field. This effectively encapsulates
333 /// all the information needed to read or write the field value from/to a coded
334 /// stream.
335 /// </summary>
336 /// <remarks>
337 /// This never writes default values to the stream, and is not currently designed
338 /// to play well with packed arrays.
339 /// </remarks>
340 public sealed class FieldCodec<T>
341 {
Jon Skeet8a0312b2015-07-16 17:03:06 +0100342 private static readonly T DefaultDefault;
Jon Skeet0d684d32015-06-24 17:21:55 +0100343
344 static FieldCodec()
345 {
346 if (typeof(T) == typeof(string))
347 {
Jon Skeet8a0312b2015-07-16 17:03:06 +0100348 DefaultDefault = (T)(object)"";
Jon Skeet0d684d32015-06-24 17:21:55 +0100349 }
350 else if (typeof(T) == typeof(ByteString))
351 {
Jon Skeet8a0312b2015-07-16 17:03:06 +0100352 DefaultDefault = (T)(object)ByteString.Empty;
Jon Skeet0d684d32015-06-24 17:21:55 +0100353 }
Jon Skeet8a0312b2015-07-16 17:03:06 +0100354 // Otherwise it's the default value of the CLR type
Jon Skeet0d684d32015-06-24 17:21:55 +0100355 }
356
357 private static Func<T, bool> CreateDefaultValueCheck<TTmp>(Func<TTmp, bool> check)
358 {
359 return (Func<T, bool>)(object)check;
360 }
361
362 private readonly Func<CodedInputStream, T> reader;
363 private readonly Action<CodedOutputStream, T> writer;
Jon Skeetf2a27cc2015-06-26 17:37:14 +0100364 private readonly Func<T, int> sizeCalculator;
Jon Skeet0d684d32015-06-24 17:21:55 +0100365 private readonly uint tag;
366 private readonly int tagSize;
Jon Skeetf2a27cc2015-06-26 17:37:14 +0100367 private readonly int fixedSize;
Jon Skeet8a0312b2015-07-16 17:03:06 +0100368 // Default value for this codec. Usually the same for every instance of the same type, but
369 // for string/ByteString wrapper fields the codec's default value is null, whereas for
370 // other string/ByteString fields it's "" or ByteString.Empty.
371 private readonly T defaultValue;
Jon Skeet0d684d32015-06-24 17:21:55 +0100372
373 internal FieldCodec(
374 Func<CodedInputStream, T> reader,
375 Action<CodedOutputStream, T> writer,
Jon Skeetf2a27cc2015-06-26 17:37:14 +0100376 Func<T, int> sizeCalculator,
Jon Skeet8a0312b2015-07-16 17:03:06 +0100377 uint tag) : this(reader, writer, sizeCalculator, tag, DefaultDefault)
378 {
379 }
380
381 internal FieldCodec(
382 Func<CodedInputStream, T> reader,
383 Action<CodedOutputStream, T> writer,
384 Func<T, int> sizeCalculator,
385 uint tag,
386 T defaultValue)
Jon Skeet0d684d32015-06-24 17:21:55 +0100387 {
388 this.reader = reader;
389 this.writer = writer;
Jon Skeetf2a27cc2015-06-26 17:37:14 +0100390 this.sizeCalculator = sizeCalculator;
391 this.fixedSize = 0;
Jon Skeet0d684d32015-06-24 17:21:55 +0100392 this.tag = tag;
Jon Skeet8a0312b2015-07-16 17:03:06 +0100393 this.defaultValue = defaultValue;
Jon Skeet0d684d32015-06-24 17:21:55 +0100394 tagSize = CodedOutputStream.ComputeRawVarint32Size(tag);
395 }
396
Jon Skeetf2a27cc2015-06-26 17:37:14 +0100397 internal FieldCodec(
398 Func<CodedInputStream, T> reader,
399 Action<CodedOutputStream, T> writer,
400 int fixedSize,
401 uint tag)
402 {
403 this.reader = reader;
404 this.writer = writer;
405 this.sizeCalculator = _ => fixedSize;
406 this.fixedSize = fixedSize;
407 this.tag = tag;
408 tagSize = CodedOutputStream.ComputeRawVarint32Size(tag);
409 }
410
411 /// <summary>
412 /// Returns the size calculator for just a value.
413 /// </summary>
414 internal Func<T, int> ValueSizeCalculator { get { return sizeCalculator; } }
415
416 /// <summary>
417 /// Returns a delegate to write a value (unconditionally) to a coded output stream.
418 /// </summary>
419 internal Action<CodedOutputStream, T> ValueWriter { get { return writer; } }
420
421 /// <summary>
422 /// Returns a delegate to read a value from a coded input stream. It is assumed that
423 /// the stream is already positioned on the appropriate tag.
424 /// </summary>
425 internal Func<CodedInputStream, T> ValueReader { get { return reader; } }
426
427 /// <summary>
428 /// Returns the fixed size for an entry, or 0 if sizes vary.
429 /// </summary>
430 internal int FixedSize { get { return fixedSize; } }
431
Jon Skeet811fc892015-08-04 15:58:39 +0100432 /// <summary>
433 /// Gets the tag of the codec.
434 /// </summary>
435 /// <value>
436 /// The tag of the codec.
437 /// </value>
Jon Skeet0d684d32015-06-24 17:21:55 +0100438 public uint Tag { get { return tag; } }
439
Jon Skeet811fc892015-08-04 15:58:39 +0100440 /// <summary>
441 /// Gets the default value of the codec's type.
442 /// </summary>
443 /// <value>
444 /// The default value of the codec's type.
445 /// </value>
Jon Skeet8a0312b2015-07-16 17:03:06 +0100446 public T DefaultValue { get { return defaultValue; } }
Jon Skeet0d684d32015-06-24 17:21:55 +0100447
Jon Skeetf2a27cc2015-06-26 17:37:14 +0100448 /// <summary>
449 /// Write a tag and the given value, *if* the value is not the default.
450 /// </summary>
451 public void WriteTagAndValue(CodedOutputStream output, T value)
Jon Skeet0d684d32015-06-24 17:21:55 +0100452 {
453 if (!IsDefault(value))
454 {
455 output.WriteTag(tag);
456 writer(output, value);
457 }
458 }
459
Jon Skeet811fc892015-08-04 15:58:39 +0100460 /// <summary>
461 /// Reads a value of the codec type from the given <see cref="CodedInputStream"/>.
462 /// </summary>
463 /// <param name="input">The input stream to read from.</param>
464 /// <returns>The value read from the stream.</returns>
Jon Skeet0d684d32015-06-24 17:21:55 +0100465 public T Read(CodedInputStream input)
466 {
467 return reader(input);
468 }
469
Jon Skeetf2a27cc2015-06-26 17:37:14 +0100470 /// <summary>
471 /// Calculates the size required to write the given value, with a tag,
472 /// if the value is not the default.
473 /// </summary>
474 public int CalculateSizeWithTag(T value)
Jon Skeet0d684d32015-06-24 17:21:55 +0100475 {
Jon Skeetf2a27cc2015-06-26 17:37:14 +0100476 return IsDefault(value) ? 0 : sizeCalculator(value) + tagSize;
Jon Skeet8a0312b2015-07-16 17:03:06 +0100477 }
478
479 private bool IsDefault(T value)
480 {
481 return EqualityComparer<T>.Default.Equals(value, defaultValue);
482 }
Jon Skeet0d684d32015-06-24 17:21:55 +0100483 }
484}