394 lines
14 KiB
C#
394 lines
14 KiB
C#
#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 System.Security;
|
|
using LC.Google.Protobuf.Reflection;
|
|
|
|
namespace LC.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)
|
|
{
|
|
WriteContext.Initialize(output, out WriteContext ctx);
|
|
try
|
|
{
|
|
WriteTo(ref ctx);
|
|
}
|
|
finally
|
|
{
|
|
ctx.CopyStateTo(output);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serializes the set and writes it to <paramref name="ctx"/>.
|
|
/// </summary>
|
|
[SecuritySafeCritical]
|
|
public void WriteTo(ref WriteContext ctx)
|
|
{
|
|
foreach (KeyValuePair<int, UnknownField> entry in fields)
|
|
{
|
|
entry.Value.WriteTo(entry.Key, ref ctx);
|
|
}
|
|
}
|
|
|
|
/// <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)
|
|
{
|
|
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>
|
|
[SecuritySafeCritical]
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|