Index: third_party/protobuf/csharp/src/Google.Protobuf/FieldCodec.cs |
diff --git a/third_party/protobuf/csharp/src/Google.Protobuf/FieldCodec.cs b/third_party/protobuf/csharp/src/Google.Protobuf/FieldCodec.cs |
index 6ee8e2f974c107f819b65c3912d3255b7188340d..9831308882b08e9fdd7b67bdd38b3c48c06d3dc2 100644 |
--- a/third_party/protobuf/csharp/src/Google.Protobuf/FieldCodec.cs |
+++ b/third_party/protobuf/csharp/src/Google.Protobuf/FieldCodec.cs |
@@ -30,6 +30,7 @@ |
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
#endregion |
+using Google.Protobuf.Compatibility; |
using Google.Protobuf.WellKnownTypes; |
using System; |
using System.Collections.Generic; |
@@ -329,17 +330,24 @@ namespace Google.Protobuf |
} |
/// <summary> |
+ /// <para> |
/// An encode/decode pair for a single field. This effectively encapsulates |
/// all the information needed to read or write the field value from/to a coded |
/// stream. |
+ /// </para> |
+ /// <para> |
+ /// This class is public and has to be as it is used by generated code, but its public |
+ /// API is very limited - just what the generated code needs to call directly. |
+ /// </para> |
/// </summary> |
/// <remarks> |
- /// This never writes default values to the stream, and is not currently designed |
- /// to play well with packed arrays. |
+ /// This never writes default values to the stream, and does not address "packedness" |
+ /// in repeated fields itself, other than to know whether or not the field *should* be packed. |
/// </remarks> |
public sealed class FieldCodec<T> |
{ |
private static readonly T DefaultDefault; |
+ private static readonly bool TypeSupportsPacking = typeof(T).IsValueType() && Nullable.GetUnderlyingType(typeof(T)) == null; |
static FieldCodec() |
{ |
@@ -354,80 +362,31 @@ namespace Google.Protobuf |
// Otherwise it's the default value of the CLR type |
} |
- private static Func<T, bool> CreateDefaultValueCheck<TTmp>(Func<TTmp, bool> check) |
- { |
- return (Func<T, bool>)(object)check; |
- } |
- |
- private readonly Func<CodedInputStream, T> reader; |
- private readonly Action<CodedOutputStream, T> writer; |
- private readonly Func<T, int> sizeCalculator; |
- private readonly uint tag; |
- private readonly int tagSize; |
- private readonly int fixedSize; |
- // Default value for this codec. Usually the same for every instance of the same type, but |
- // for string/ByteString wrapper fields the codec's default value is null, whereas for |
- // other string/ByteString fields it's "" or ByteString.Empty. |
- private readonly T defaultValue; |
+ internal static bool IsPackedRepeatedField(uint tag) => |
+ TypeSupportsPacking && WireFormat.GetTagWireType(tag) == WireFormat.WireType.LengthDelimited; |
- internal FieldCodec( |
- Func<CodedInputStream, T> reader, |
- Action<CodedOutputStream, T> writer, |
- Func<T, int> sizeCalculator, |
- uint tag) : this(reader, writer, sizeCalculator, tag, DefaultDefault) |
- { |
- } |
- |
- internal FieldCodec( |
- Func<CodedInputStream, T> reader, |
- Action<CodedOutputStream, T> writer, |
- Func<T, int> sizeCalculator, |
- uint tag, |
- T defaultValue) |
- { |
- this.reader = reader; |
- this.writer = writer; |
- this.sizeCalculator = sizeCalculator; |
- this.fixedSize = 0; |
- this.tag = tag; |
- this.defaultValue = defaultValue; |
- tagSize = CodedOutputStream.ComputeRawVarint32Size(tag); |
- } |
- |
- internal FieldCodec( |
- Func<CodedInputStream, T> reader, |
- Action<CodedOutputStream, T> writer, |
- int fixedSize, |
- uint tag) |
- { |
- this.reader = reader; |
- this.writer = writer; |
- this.sizeCalculator = _ => fixedSize; |
- this.fixedSize = fixedSize; |
- this.tag = tag; |
- tagSize = CodedOutputStream.ComputeRawVarint32Size(tag); |
- } |
+ internal bool PackedRepeatedField { get; } |
/// <summary> |
- /// Returns the size calculator for just a value. |
+ /// Returns a delegate to write a value (unconditionally) to a coded output stream. |
/// </summary> |
- internal Func<T, int> ValueSizeCalculator { get { return sizeCalculator; } } |
+ internal Action<CodedOutputStream, T> ValueWriter { get; } |
/// <summary> |
- /// Returns a delegate to write a value (unconditionally) to a coded output stream. |
+ /// Returns the size calculator for just a value. |
/// </summary> |
- internal Action<CodedOutputStream, T> ValueWriter { get { return writer; } } |
+ internal Func<T, int> ValueSizeCalculator { get; } |
/// <summary> |
/// Returns a delegate to read a value from a coded input stream. It is assumed that |
/// the stream is already positioned on the appropriate tag. |
/// </summary> |
- internal Func<CodedInputStream, T> ValueReader { get { return reader; } } |
+ internal Func<CodedInputStream, T> ValueReader { get; } |
/// <summary> |
/// Returns the fixed size for an entry, or 0 if sizes vary. |
/// </summary> |
- internal int FixedSize { get { return fixedSize; } } |
+ internal int FixedSize { get; } |
/// <summary> |
/// Gets the tag of the codec. |
@@ -435,15 +394,54 @@ namespace Google.Protobuf |
/// <value> |
/// The tag of the codec. |
/// </value> |
- public uint Tag { get { return tag; } } |
+ internal uint Tag { get; } |
/// <summary> |
- /// Gets the default value of the codec's type. |
+ /// Default value for this codec. Usually the same for every instance of the same type, but |
+ /// for string/ByteString wrapper fields the codec's default value is null, whereas for |
+ /// other string/ByteString fields it's "" or ByteString.Empty. |
/// </summary> |
/// <value> |
/// The default value of the codec's type. |
/// </value> |
- public T DefaultValue { get { return defaultValue; } } |
+ internal T DefaultValue { get; } |
+ |
+ private readonly int tagSize; |
+ |
+ internal FieldCodec( |
+ Func<CodedInputStream, T> reader, |
+ Action<CodedOutputStream, T> writer, |
+ int fixedSize, |
+ uint tag) : this(reader, writer, _ => fixedSize, tag) |
+ { |
+ FixedSize = fixedSize; |
+ } |
+ |
+ internal FieldCodec( |
+ Func<CodedInputStream, T> reader, |
+ Action<CodedOutputStream, T> writer, |
+ Func<T, int> sizeCalculator, |
+ uint tag) : this(reader, writer, sizeCalculator, tag, DefaultDefault) |
+ { |
+ } |
+ |
+ internal FieldCodec( |
+ Func<CodedInputStream, T> reader, |
+ Action<CodedOutputStream, T> writer, |
+ Func<T, int> sizeCalculator, |
+ uint tag, |
+ T defaultValue) |
+ { |
+ ValueReader = reader; |
+ ValueWriter = writer; |
+ ValueSizeCalculator = sizeCalculator; |
+ FixedSize = 0; |
+ Tag = tag; |
+ DefaultValue = defaultValue; |
+ tagSize = CodedOutputStream.ComputeRawVarint32Size(tag); |
+ // Detect packed-ness once, so we can check for it within RepeatedField<T>. |
+ PackedRepeatedField = IsPackedRepeatedField(tag); |
+ } |
/// <summary> |
/// Write a tag and the given value, *if* the value is not the default. |
@@ -452,8 +450,8 @@ namespace Google.Protobuf |
{ |
if (!IsDefault(value)) |
{ |
- output.WriteTag(tag); |
- writer(output, value); |
+ output.WriteTag(Tag); |
+ ValueWriter(output, value); |
} |
} |
@@ -462,23 +460,14 @@ namespace Google.Protobuf |
/// </summary> |
/// <param name="input">The input stream to read from.</param> |
/// <returns>The value read from the stream.</returns> |
- public T Read(CodedInputStream input) |
- { |
- return reader(input); |
- } |
+ public T Read(CodedInputStream input) => ValueReader(input); |
/// <summary> |
/// Calculates the size required to write the given value, with a tag, |
/// if the value is not the default. |
/// </summary> |
- public int CalculateSizeWithTag(T value) |
- { |
- return IsDefault(value) ? 0 : sizeCalculator(value) + tagSize; |
- } |
+ public int CalculateSizeWithTag(T value) => IsDefault(value) ? 0 : ValueSizeCalculator(value) + tagSize; |
- private bool IsDefault(T value) |
- { |
- return EqualityComparer<T>.Default.Equals(value, defaultValue); |
- } |
+ private bool IsDefault(T value) => EqualityComparer<T>.Default.Equals(value, DefaultValue); |
} |
} |