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