Protocol Buffers - Google's data interchange format (grpc依赖)
https://developers.google.com/protocol-buffers/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
375 lines
14 KiB
375 lines
14 KiB
#region Copyright notice and license |
|
// Protocol Buffers - Google's data interchange format |
|
// Copyright 2015 Google Inc. All rights reserved. |
|
// https://developers.google.com/protocol-buffers/ |
|
// |
|
// Redistribution and use in source and binary forms, with or without |
|
// modification, are permitted provided that the following conditions are |
|
// met: |
|
// |
|
// * Redistributions of source code must retain the above copyright |
|
// notice, this list of conditions and the following disclaimer. |
|
// * Redistributions in binary form must reproduce the above |
|
// copyright notice, this list of conditions and the following disclaimer |
|
// in the documentation and/or other materials provided with the |
|
// distribution. |
|
// * Neither the name of Google Inc. nor the names of its |
|
// contributors may be used to endorse or promote products derived from |
|
// this software without specific prior written permission. |
|
// |
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
#endregion |
|
|
|
using System; |
|
using System.Collections.Generic; |
|
using System.IO; |
|
using Google.Protobuf.Reflection; |
|
|
|
namespace Google.Protobuf |
|
{ |
|
/// <summary> |
|
/// Used to keep track of fields which were seen when parsing a protocol message |
|
/// but whose field numbers or types are unrecognized. This most frequently |
|
/// occurs when new fields are added to a message type and then messages containing |
|
/// those fields are read by old software that was built before the new types were |
|
/// added. |
|
/// |
|
/// Most users will never need to use this class directly. |
|
/// </summary> |
|
public sealed partial class UnknownFieldSet |
|
{ |
|
private readonly IDictionary<int, UnknownField> fields; |
|
|
|
/// <summary> |
|
/// Creates a new UnknownFieldSet. |
|
/// </summary> |
|
internal UnknownFieldSet() |
|
{ |
|
this.fields = new Dictionary<int, UnknownField>(); |
|
} |
|
|
|
/// <summary> |
|
/// Checks whether or not the given field number is present in the set. |
|
/// </summary> |
|
internal bool HasField(int field) |
|
{ |
|
return fields.ContainsKey(field); |
|
} |
|
|
|
/// <summary> |
|
/// Serializes the set and writes it to <paramref name="output"/>. |
|
/// </summary> |
|
public void WriteTo(CodedOutputStream output) |
|
{ |
|
foreach (KeyValuePair<int, UnknownField> entry in fields) |
|
{ |
|
entry.Value.WriteTo(entry.Key, output); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets the number of bytes required to encode this set. |
|
/// </summary> |
|
public int CalculateSize() |
|
{ |
|
int result = 0; |
|
foreach (KeyValuePair<int, UnknownField> entry in fields) |
|
{ |
|
result += entry.Value.GetSerializedSize(entry.Key); |
|
} |
|
return result; |
|
} |
|
|
|
/// <summary> |
|
/// Checks if two unknown field sets are equal. |
|
/// </summary> |
|
public override bool Equals(object other) |
|
{ |
|
if (ReferenceEquals(this, other)) |
|
{ |
|
return true; |
|
} |
|
UnknownFieldSet otherSet = other as UnknownFieldSet; |
|
IDictionary<int, UnknownField> otherFields = otherSet.fields; |
|
if (fields.Count != otherFields.Count) |
|
{ |
|
return false; |
|
} |
|
foreach (KeyValuePair<int, UnknownField> leftEntry in fields) |
|
{ |
|
UnknownField rightValue; |
|
if (!otherFields.TryGetValue(leftEntry.Key, out rightValue)) |
|
{ |
|
return false; |
|
} |
|
if (!leftEntry.Value.Equals(rightValue)) |
|
{ |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
/// <summary> |
|
/// Gets the unknown field set's hash code. |
|
/// </summary> |
|
public override int GetHashCode() |
|
{ |
|
int ret = 1; |
|
foreach (KeyValuePair<int, UnknownField> field in fields) |
|
{ |
|
// Use ^ here to make the field order irrelevant. |
|
int hash = field.Key.GetHashCode() ^ field.Value.GetHashCode(); |
|
ret ^= hash; |
|
} |
|
return ret; |
|
} |
|
|
|
// Optimization: We keep around the last field that was |
|
// modified so that we can efficiently add to it multiple times in a |
|
// row (important when parsing an unknown repeated field). |
|
private int lastFieldNumber; |
|
private UnknownField lastField; |
|
|
|
private UnknownField GetOrAddField(int number) |
|
{ |
|
if (lastField != null && number == lastFieldNumber) |
|
{ |
|
return lastField; |
|
} |
|
if (number == 0) |
|
{ |
|
return null; |
|
} |
|
|
|
UnknownField existing; |
|
if (fields.TryGetValue(number, out existing)) |
|
{ |
|
return existing; |
|
} |
|
lastField = new UnknownField(); |
|
AddOrReplaceField(number, lastField); |
|
lastFieldNumber = number; |
|
return lastField; |
|
} |
|
|
|
/// <summary> |
|
/// Adds a field to the set. If a field with the same number already exists, it |
|
/// is replaced. |
|
/// </summary> |
|
internal UnknownFieldSet AddOrReplaceField(int number, UnknownField field) |
|
{ |
|
if (number == 0) |
|
{ |
|
throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number."); |
|
} |
|
fields[number] = field; |
|
return this; |
|
} |
|
|
|
/// <summary> |
|
/// Parse a single field from <paramref name="ctx"/> and merge it |
|
/// into this set. |
|
/// </summary> |
|
/// <param name="ctx">The parse context from which to read the field</param> |
|
/// <returns>false if the tag is an "end group" tag, true otherwise</returns> |
|
private bool MergeFieldFrom(ref ParseContext ctx) |
|
{ |
|
// TODO: deduplicate MergeFieldFrom implementations |
|
uint tag = ctx.LastTag; |
|
int number = WireFormat.GetTagFieldNumber(tag); |
|
switch (WireFormat.GetTagWireType(tag)) |
|
{ |
|
case WireFormat.WireType.Varint: |
|
{ |
|
ulong uint64 = ctx.ReadUInt64(); |
|
GetOrAddField(number).AddVarint(uint64); |
|
return true; |
|
} |
|
case WireFormat.WireType.Fixed32: |
|
{ |
|
uint uint32 = ctx.ReadFixed32(); |
|
GetOrAddField(number).AddFixed32(uint32); |
|
return true; |
|
} |
|
case WireFormat.WireType.Fixed64: |
|
{ |
|
ulong uint64 = ctx.ReadFixed64(); |
|
GetOrAddField(number).AddFixed64(uint64); |
|
return true; |
|
} |
|
case WireFormat.WireType.LengthDelimited: |
|
{ |
|
ByteString bytes = ctx.ReadBytes(); |
|
GetOrAddField(number).AddLengthDelimited(bytes); |
|
return true; |
|
} |
|
case WireFormat.WireType.StartGroup: |
|
{ |
|
UnknownFieldSet set = new UnknownFieldSet(); |
|
ParsingPrimitivesMessages.ReadGroup(ref ctx, number, set); |
|
GetOrAddField(number).AddGroup(set); |
|
return true; |
|
} |
|
case WireFormat.WireType.EndGroup: |
|
{ |
|
return false; |
|
} |
|
default: |
|
throw InvalidProtocolBufferException.InvalidWireType(); |
|
} |
|
} |
|
|
|
internal void MergeGroupFrom(ref ParseContext ctx) |
|
{ |
|
while (true) |
|
{ |
|
uint tag = ctx.ReadTag(); |
|
if (tag == 0) |
|
{ |
|
break; |
|
} |
|
if (!MergeFieldFrom(ref ctx)) |
|
{ |
|
break; |
|
} |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Create a new UnknownFieldSet if unknownFields is null. |
|
/// Parse a single field from <paramref name="input"/> and merge it |
|
/// into unknownFields. If <paramref name="input"/> is configured to discard unknown fields, |
|
/// <paramref name="unknownFields"/> will be returned as-is and the field will be skipped. |
|
/// </summary> |
|
/// <param name="unknownFields">The UnknownFieldSet which need to be merged</param> |
|
/// <param name="input">The coded input stream containing the field</param> |
|
/// <returns>The merged UnknownFieldSet</returns> |
|
public static UnknownFieldSet MergeFieldFrom(UnknownFieldSet unknownFields, |
|
CodedInputStream input) |
|
{ |
|
ParseContext.Initialize(input, out ParseContext ctx); |
|
try |
|
{ |
|
return MergeFieldFrom(unknownFields, ref ctx); |
|
} |
|
finally |
|
{ |
|
ctx.CopyStateTo(input); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Create a new UnknownFieldSet if unknownFields is null. |
|
/// Parse a single field from <paramref name="ctx"/> and merge it |
|
/// into unknownFields. If <paramref name="ctx"/> is configured to discard unknown fields, |
|
/// <paramref name="unknownFields"/> will be returned as-is and the field will be skipped. |
|
/// </summary> |
|
/// <param name="unknownFields">The UnknownFieldSet which need to be merged</param> |
|
/// <param name="ctx">The parse context from which to read the field</param> |
|
/// <returns>The merged UnknownFieldSet</returns> |
|
public static UnknownFieldSet MergeFieldFrom(UnknownFieldSet unknownFields, |
|
ref ParseContext ctx) |
|
{ |
|
if (ctx.DiscardUnknownFields) |
|
{ |
|
ParsingPrimitivesMessages.SkipLastField(ref ctx.buffer, ref ctx.state); |
|
return unknownFields; |
|
} |
|
if (unknownFields == null) |
|
{ |
|
unknownFields = new UnknownFieldSet(); |
|
} |
|
if (!unknownFields.MergeFieldFrom(ref ctx)) |
|
{ |
|
throw new InvalidProtocolBufferException("Merge an unknown field of end-group tag, indicating that the corresponding start-group was missing."); // match the old code-gen |
|
} |
|
return unknownFields; |
|
} |
|
|
|
/// <summary> |
|
/// Merges the fields from <paramref name="other"/> into this set. |
|
/// If a field number exists in both sets, the values in <paramref name="other"/> |
|
/// will be appended to the values in this set. |
|
/// </summary> |
|
private UnknownFieldSet MergeFrom(UnknownFieldSet other) |
|
{ |
|
if (other != null) |
|
{ |
|
foreach (KeyValuePair<int, UnknownField> entry in other.fields) |
|
{ |
|
MergeField(entry.Key, entry.Value); |
|
} |
|
} |
|
return this; |
|
} |
|
|
|
/// <summary> |
|
/// Created a new UnknownFieldSet to <paramref name="unknownFields"/> if |
|
/// needed and merges the fields from <paramref name="other"/> into the first set. |
|
/// If a field number exists in both sets, the values in <paramref name="other"/> |
|
/// will be appended to the values in this set. |
|
/// </summary> |
|
public static UnknownFieldSet MergeFrom(UnknownFieldSet unknownFields, |
|
UnknownFieldSet other) |
|
{ |
|
if (other == null) |
|
{ |
|
return unknownFields; |
|
} |
|
if (unknownFields == null) |
|
{ |
|
unknownFields = new UnknownFieldSet(); |
|
} |
|
unknownFields.MergeFrom(other); |
|
return unknownFields; |
|
} |
|
|
|
|
|
/// <summary> |
|
/// Adds a field to the unknown field set. If a field with the same |
|
/// number already exists, the two are merged. |
|
/// </summary> |
|
private UnknownFieldSet MergeField(int number, UnknownField field) |
|
{ |
|
if (number == 0) |
|
{ |
|
throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number."); |
|
} |
|
if (HasField(number)) |
|
{ |
|
GetOrAddField(number).MergeFrom(field); |
|
} |
|
else |
|
{ |
|
AddOrReplaceField(number, field); |
|
} |
|
return this; |
|
} |
|
|
|
/// <summary> |
|
/// Clone an unknown field set from <paramref name="other"/>. |
|
/// </summary> |
|
public static UnknownFieldSet Clone(UnknownFieldSet other) |
|
{ |
|
if (other == null) |
|
{ |
|
return null; |
|
} |
|
UnknownFieldSet unknownFields = new UnknownFieldSet(); |
|
unknownFields.MergeFrom(other); |
|
return unknownFields; |
|
} |
|
} |
|
} |
|
|
|
|