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