OLD | NEW |
1 #region Copyright notice and license | 1 #region Copyright notice and license |
2 // Protocol Buffers - Google's data interchange format | 2 // Protocol Buffers - Google's data interchange format |
3 // Copyright 2015 Google Inc. All rights reserved. | 3 // Copyright 2015 Google Inc. All rights reserved. |
4 // https://developers.google.com/protocol-buffers/ | 4 // https://developers.google.com/protocol-buffers/ |
5 // | 5 // |
6 // Redistribution and use in source and binary forms, with or without | 6 // Redistribution and use in source and binary forms, with or without |
7 // modification, are permitted provided that the following conditions are | 7 // modification, are permitted provided that the following conditions are |
8 // met: | 8 // met: |
9 // | 9 // |
10 // * Redistributions of source code must retain the above copyright | 10 // * Redistributions of source code must retain the above copyright |
(...skipping 12 matching lines...) Expand all Loading... |
23 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 23 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
24 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 24 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
27 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 27 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
28 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 28 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
29 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 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. | 30 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
31 #endregion | 31 #endregion |
32 | 32 |
| 33 using Google.Protobuf.Compatibility; |
33 using Google.Protobuf.WellKnownTypes; | 34 using Google.Protobuf.WellKnownTypes; |
34 using System; | 35 using System; |
35 using System.Collections.Generic; | 36 using System.Collections.Generic; |
36 | 37 |
37 namespace Google.Protobuf | 38 namespace Google.Protobuf |
38 { | 39 { |
39 /// <summary> | 40 /// <summary> |
40 /// Factory methods for <see cref="FieldCodec{T}"/>. | 41 /// Factory methods for <see cref="FieldCodec{T}"/>. |
41 /// </summary> | 42 /// </summary> |
42 public static class FieldCodec | 43 public static class FieldCodec |
(...skipping 279 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
322 | 323 |
323 internal static int CalculateSize<T>(T value, FieldCodec<T> codec) | 324 internal static int CalculateSize<T>(T value, FieldCodec<T> codec) |
324 { | 325 { |
325 int fieldLength = codec.CalculateSizeWithTag(value); | 326 int fieldLength = codec.CalculateSizeWithTag(value); |
326 return CodedOutputStream.ComputeLengthSize(fieldLength) + fieldL
ength; | 327 return CodedOutputStream.ComputeLengthSize(fieldLength) + fieldL
ength; |
327 } | 328 } |
328 } | 329 } |
329 } | 330 } |
330 | 331 |
331 /// <summary> | 332 /// <summary> |
| 333 /// <para> |
332 /// An encode/decode pair for a single field. This effectively encapsulates | 334 /// 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 co
ded | 335 /// all the information needed to read or write the field value from/to a co
ded |
334 /// stream. | 336 /// stream. |
| 337 /// </para> |
| 338 /// <para> |
| 339 /// This class is public and has to be as it is used by generated code, but
its public |
| 340 /// API is very limited - just what the generated code needs to call directl
y. |
| 341 /// </para> |
335 /// </summary> | 342 /// </summary> |
336 /// <remarks> | 343 /// <remarks> |
337 /// This never writes default values to the stream, and is not currently des
igned | 344 /// This never writes default values to the stream, and does not address "pa
ckedness" |
338 /// to play well with packed arrays. | 345 /// in repeated fields itself, other than to know whether or not the field *
should* be packed. |
339 /// </remarks> | 346 /// </remarks> |
340 public sealed class FieldCodec<T> | 347 public sealed class FieldCodec<T> |
341 { | 348 { |
342 private static readonly T DefaultDefault; | 349 private static readonly T DefaultDefault; |
| 350 private static readonly bool TypeSupportsPacking = typeof(T).IsValueType
() && Nullable.GetUnderlyingType(typeof(T)) == null; |
343 | 351 |
344 static FieldCodec() | 352 static FieldCodec() |
345 { | 353 { |
346 if (typeof(T) == typeof(string)) | 354 if (typeof(T) == typeof(string)) |
347 { | 355 { |
348 DefaultDefault = (T)(object)""; | 356 DefaultDefault = (T)(object)""; |
349 } | 357 } |
350 else if (typeof(T) == typeof(ByteString)) | 358 else if (typeof(T) == typeof(ByteString)) |
351 { | 359 { |
352 DefaultDefault = (T)(object)ByteString.Empty; | 360 DefaultDefault = (T)(object)ByteString.Empty; |
353 } | 361 } |
354 // Otherwise it's the default value of the CLR type | 362 // Otherwise it's the default value of the CLR type |
355 } | 363 } |
356 | 364 |
357 private static Func<T, bool> CreateDefaultValueCheck<TTmp>(Func<TTmp, bo
ol> check) | 365 internal static bool IsPackedRepeatedField(uint tag) => |
358 { | 366 TypeSupportsPacking && WireFormat.GetTagWireType(tag) == WireFormat.
WireType.LengthDelimited; |
359 return (Func<T, bool>)(object)check; | |
360 } | |
361 | 367 |
362 private readonly Func<CodedInputStream, T> reader; | 368 internal bool PackedRepeatedField { get; } |
363 private readonly Action<CodedOutputStream, T> writer; | |
364 private readonly Func<T, int> sizeCalculator; | |
365 private readonly uint tag; | |
366 private readonly int tagSize; | |
367 private readonly int fixedSize; | |
368 // 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 nul
l, whereas for | |
370 // other string/ByteString fields it's "" or ByteString.Empty. | |
371 private readonly T defaultValue; | |
372 | 369 |
373 internal FieldCodec( | 370 /// <summary> |
374 Func<CodedInputStream, T> reader, | 371 /// Returns a delegate to write a value (unconditionally) to a coded out
put stream. |
375 Action<CodedOutputStream, T> writer, | 372 /// </summary> |
376 Func<T, int> sizeCalculator, | 373 internal Action<CodedOutputStream, T> ValueWriter { get; } |
377 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) | |
387 { | |
388 this.reader = reader; | |
389 this.writer = writer; | |
390 this.sizeCalculator = sizeCalculator; | |
391 this.fixedSize = 0; | |
392 this.tag = tag; | |
393 this.defaultValue = defaultValue; | |
394 tagSize = CodedOutputStream.ComputeRawVarint32Size(tag); | |
395 } | |
396 | |
397 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 | 374 |
411 /// <summary> | 375 /// <summary> |
412 /// Returns the size calculator for just a value. | 376 /// Returns the size calculator for just a value. |
413 /// </summary> | 377 /// </summary> |
414 internal Func<T, int> ValueSizeCalculator { get { return sizeCalculator;
} } | 378 internal Func<T, int> ValueSizeCalculator { get; } |
415 | |
416 /// <summary> | |
417 /// Returns a delegate to write a value (unconditionally) to a coded out
put stream. | |
418 /// </summary> | |
419 internal Action<CodedOutputStream, T> ValueWriter { get { return writer;
} } | |
420 | 379 |
421 /// <summary> | 380 /// <summary> |
422 /// Returns a delegate to read a value from a coded input stream. It is
assumed that | 381 /// 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. | 382 /// the stream is already positioned on the appropriate tag. |
424 /// </summary> | 383 /// </summary> |
425 internal Func<CodedInputStream, T> ValueReader { get { return reader; }
} | 384 internal Func<CodedInputStream, T> ValueReader { get; } |
426 | 385 |
427 /// <summary> | 386 /// <summary> |
428 /// Returns the fixed size for an entry, or 0 if sizes vary. | 387 /// Returns the fixed size for an entry, or 0 if sizes vary. |
429 /// </summary> | 388 /// </summary> |
430 internal int FixedSize { get { return fixedSize; } } | 389 internal int FixedSize { get; } |
431 | 390 |
432 /// <summary> | 391 /// <summary> |
433 /// Gets the tag of the codec. | 392 /// Gets the tag of the codec. |
434 /// </summary> | 393 /// </summary> |
435 /// <value> | 394 /// <value> |
436 /// The tag of the codec. | 395 /// The tag of the codec. |
437 /// </value> | 396 /// </value> |
438 public uint Tag { get { return tag; } } | 397 internal uint Tag { get; } |
439 | 398 |
440 /// <summary> | 399 /// <summary> |
441 /// Gets the default value of the codec's type. | 400 /// Default value for this codec. Usually the same for every instance of
the same type, but |
| 401 /// for string/ByteString wrapper fields the codec's default value is nu
ll, whereas for |
| 402 /// other string/ByteString fields it's "" or ByteString.Empty. |
442 /// </summary> | 403 /// </summary> |
443 /// <value> | 404 /// <value> |
444 /// The default value of the codec's type. | 405 /// The default value of the codec's type. |
445 /// </value> | 406 /// </value> |
446 public T DefaultValue { get { return defaultValue; } } | 407 internal T DefaultValue { get; } |
| 408 |
| 409 private readonly int tagSize; |
| 410 |
| 411 internal FieldCodec( |
| 412 Func<CodedInputStream, T> reader, |
| 413 Action<CodedOutputStream, T> writer, |
| 414 int fixedSize, |
| 415 uint tag) : this(reader, writer, _ => fixedSize, tag) |
| 416 { |
| 417 FixedSize = fixedSize; |
| 418 } |
| 419 |
| 420 internal FieldCodec( |
| 421 Func<CodedInputStream, T> reader, |
| 422 Action<CodedOutputStream, T> writer, |
| 423 Func<T, int> sizeCalculator, |
| 424 uint tag) : this(reader, writer, sizeCalculator, tag, DefaultDefault
) |
| 425 { |
| 426 } |
| 427 |
| 428 internal FieldCodec( |
| 429 Func<CodedInputStream, T> reader, |
| 430 Action<CodedOutputStream, T> writer, |
| 431 Func<T, int> sizeCalculator, |
| 432 uint tag, |
| 433 T defaultValue) |
| 434 { |
| 435 ValueReader = reader; |
| 436 ValueWriter = writer; |
| 437 ValueSizeCalculator = sizeCalculator; |
| 438 FixedSize = 0; |
| 439 Tag = tag; |
| 440 DefaultValue = defaultValue; |
| 441 tagSize = CodedOutputStream.ComputeRawVarint32Size(tag); |
| 442 // Detect packed-ness once, so we can check for it within RepeatedFi
eld<T>. |
| 443 PackedRepeatedField = IsPackedRepeatedField(tag); |
| 444 } |
447 | 445 |
448 /// <summary> | 446 /// <summary> |
449 /// Write a tag and the given value, *if* the value is not the default. | 447 /// Write a tag and the given value, *if* the value is not the default. |
450 /// </summary> | 448 /// </summary> |
451 public void WriteTagAndValue(CodedOutputStream output, T value) | 449 public void WriteTagAndValue(CodedOutputStream output, T value) |
452 { | 450 { |
453 if (!IsDefault(value)) | 451 if (!IsDefault(value)) |
454 { | 452 { |
455 output.WriteTag(tag); | 453 output.WriteTag(Tag); |
456 writer(output, value); | 454 ValueWriter(output, value); |
457 } | 455 } |
458 } | 456 } |
459 | 457 |
460 /// <summary> | 458 /// <summary> |
461 /// Reads a value of the codec type from the given <see cref="CodedInput
Stream"/>. | 459 /// Reads a value of the codec type from the given <see cref="CodedInput
Stream"/>. |
462 /// </summary> | 460 /// </summary> |
463 /// <param name="input">The input stream to read from.</param> | 461 /// <param name="input">The input stream to read from.</param> |
464 /// <returns>The value read from the stream.</returns> | 462 /// <returns>The value read from the stream.</returns> |
465 public T Read(CodedInputStream input) | 463 public T Read(CodedInputStream input) => ValueReader(input); |
466 { | |
467 return reader(input); | |
468 } | |
469 | 464 |
470 /// <summary> | 465 /// <summary> |
471 /// Calculates the size required to write the given value, with a tag, | 466 /// Calculates the size required to write the given value, with a tag, |
472 /// if the value is not the default. | 467 /// if the value is not the default. |
473 /// </summary> | 468 /// </summary> |
474 public int CalculateSizeWithTag(T value) | 469 public int CalculateSizeWithTag(T value) => IsDefault(value) ? 0 : Value
SizeCalculator(value) + tagSize; |
475 { | |
476 return IsDefault(value) ? 0 : sizeCalculator(value) + tagSize; | |
477 } | |
478 | 470 |
479 private bool IsDefault(T value) | 471 private bool IsDefault(T value) => EqualityComparer<T>.Default.Equals(va
lue, DefaultValue); |
480 { | |
481 return EqualityComparer<T>.Default.Equals(value, defaultValue); | |
482 } | |
483 } | 472 } |
484 } | 473 } |
OLD | NEW |